Now we get to the interesting part of the implementation. The geo_matrix_create() method in Trivial.c is not boilerplate, but it does show you what you need to do (well, actually, one small portion is boilerplate). Instead of repeating the code section here you can look up the method in the listing on the preceding pages - but take note that the method in Trivial.c is called trivial_matrix_create().
Note that the function has essentially three sections. In the first section, you need to loop through your children (or evaluate instance variables, as is done in SelectionBox), deciding on how many rows of children that need to be controlled. Basically, what you are doing is evaluating how many MajorLayout structures you are going to need. You can also choose to count the number of managed children you have (this may or may not be the same as the number of children you have); this is optional, as the wasted space is not very large, and it eventually gets deallocated anyway.
In the second section, we have a small piece of boilerplate: it is very important to duplicate this code exactly. While the _pref and _from fields are often NULL, they are not when this method is called from _XmHandleGeometryManager(). Make sure you copy this right.
geoSpec = _XmGeoMatrixAlloc(nrows, numKids, 0); geoSpec->composite = (Widget)_w; geoSpec->instigator = (Widget)_from; if (_pref) geoSpec->instig_request = *_pref; geoSpec->margin_w = BB_MarginWidth(_w) + MGR_ShadowThickness(_w); geoSpec->margin_h = BB_MarginHeight(_w) + MGR_ShadowThickness(_w); geoSpec->no_geo_request = trivial_NoGeoRequest;
You can be a little creative when you calculate the margin_w and margin_h variables. Also, make sure that you hook up the no_geo_request() method as shown in the last line of the code excerpt above.
The third section of code is basically where the subclass needs to setup the MajorLayout structures with the desired information for controlling the layout, and setting the KidGeometry structures to point at the widget children that should appear.
XmTrivial's implementation of this method is very simplistic. Now for a little more demanding example. The following code is SelectionBox's version - look for the boilerplate above to find the separation between the sections:
XmGeoMatrix _XmSelectionBoxGeoMatrixCreate(Widget _w, Widget _from, XtWidgetGeometry *_pref) { XmGeoMatrix geoSpec; register XmGeoRowLayout layoutPtr; register XmKidGeometry boxPtr; Cardinal numKids; Boolean newRow; int nrows, i, nextras; Widget *extras; numKids = MGR_NumChildren(_w); nextras = 0; extras = NULL; for (i = 0; i < numKids; i++) { if (XtIsManaged(MGR_Children(_w)[i]) && MGR_Children(_w)[i] !!= SB_ListLabel(_w) && (SB_List(_w) ? MGR_Children(_w)[i] !!= XtParent(SB_List(_w)) : True) && MGR_Children(_w)[i] !!= SB_SelectionLabel(_w) && MGR_Children(_w)[i] !!= SB_Text(_w) && MGR_Children(_w)[i] !!= SB_Separator(_w) && MGR_Children(_w)[i] !!= SB_OkButton(_w) && MGR_Children(_w)[i] !!= SB_ApplyButton(_w) && MGR_Children(_w)[i] !!= SB_HelpButton(_w) && MGR_Children(_w)[i] !!= BB_CancelButton(_w)) { nextras++; } } if (nextras) extras = (Widget *)XtMalloc(sizeof(Widget) * nextras); nextras = 0; for (i = 0; i < numKids; i++) { if (XtIsManaged(MGR_Children(_w)[i]) && MGR_Children(_w)[i] !!= SB_ListLabel(_w) && (SB_List(_w) ? MGR_Children(_w)[i] !!= XtParent(SB_List(_w)) : True) && MGR_Children(_w)[i] !!= SB_SelectionLabel(_w) && MGR_Children(_w)[i] !!= SB_Text(_w) && MGR_Children(_w)[i] !!= SB_Separator(_w) && MGR_Children(_w)[i] !!= SB_OkButton(_w) && MGR_Children(_w)[i] !!= SB_ApplyButton(_w) && MGR_Children(_w)[i] !!= SB_HelpButton(_w) && MGR_Children(_w)[i] !!= BB_CancelButton(_w)) { extras[nextras] = MGR_Children(_w)[i]; nextras++; } } nrows = 0; /* note the starting from one. The zero'th child is the "work area" */ if (nextras > 0) { for (i = 1; i < nextras; i++) { if (XmIsMenuBar(extras[i]) && XtIsManaged(extras[i])) nrows++; } if (extras[0] && XtIsManaged(extras[0])) nrows++; } if (SB_ListLabel(_w) && XtIsManaged(SB_ListLabel(_w))) nrows++; if (SB_List(_w) && XtIsManaged(SB_List(_w))) nrows++; if (SB_SelectionLabel(_w) && XtIsManaged(SB_SelectionLabel(_w))) nrows++; if (SB_Text(_w) && XtIsManaged(SB_Text(_w))) nrows++; if (SB_Separator(_w) && XtIsManaged(SB_Separator(_w))) nrows++; if ((BB_CancelButton(_w) && XtIsManaged(BB_CancelButton(_w))) || (SB_OkButton(_w) && XtIsManaged(SB_OkButton(_w))) || (SB_ApplyButton(_w) && XtIsManaged(SB_ApplyButton(_w))) || (SB_HelpButton(_w) && XtIsManaged(SB_HelpButton(_w)))) nrows++; else { for (i = i; i < nextras; i++) { if (extras[i] && XtIsManaged(extras[i]) && (XmIsPushButton(extras[i]) || XmIsPushButtonGadget(extras[i]))) { nrows++; break; } } } geoSpec = _XmGeoMatrixAlloc(nrows, numKids, 0); geoSpec->composite = (Widget)_w; geoSpec->instigator = (Widget)_from; if (_pref) geoSpec->instig_request = *_pref; geoSpec->margin_w = BB_MarginWidth(_w) + MGR_ShadowThickness(_w); geoSpec->margin_h = BB_MarginHeight(_w) + MGR_ShadowThickness(_w); geoSpec->no_geo_request = _XmSelectionBoxNoGeoRequest; layoutPtr = &(geoSpec->layouts->row); boxPtr = geoSpec->boxes; for (i = 1; i < nextras; i++) { if (XmIsMenuBar(extras[i]) && XtIsManaged(extras[i])) { layoutPtr->fix_up = _XmMenuBarFix; layoutPtr->space_above = 0; boxPtr += 2; layoutPtr++; } } if (SB_ChildPlacement(_w) == XmPLACE_TOP && nextras && extras[0] && XtIsManaged(extras[0]) && _XmGeoSetupKid(boxPtr, extras[0])) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; nrows++; } if (SB_DialogType(_w) == XmDIALOG_PROMPT && SB_ChildPlacement(_w) == XmPLACE_ABOVE_SELECTION && nextras && extras[0] && XtIsManaged(extras[0]) && _XmGeoSetupKid(boxPtr, extras[0])) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; nrows++; } newRow = False; if (SB_ListLabel(_w) && XtIsManaged(SB_ListLabel(_w)) && _XmGeoSetupKid(boxPtr, SB_ListLabel(_w))) { layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->fit_mode = XmGEO_PROPORTIONAL; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr->space_between = BB_MarginWidth(_w); newRow = TRUE; boxPtr++; } if (newRow) { layoutPtr++; boxPtr++; } if (SB_DialogType(_w) == XmDIALOG_COMMAND && SB_ChildPlacement(_w) == XmPLACE_ABOVE_SELECTION && nextras && extras[0] && XtIsManaged(extras[0]) && _XmGeoSetupKid(boxPtr, extras[0])) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; nrows++; } newRow = FALSE; if (SB_List(_w) && XtIsManaged(SB_List(_w)) && _XmGeoSetupKid(boxPtr, XtParent(SB_List(_w)))) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->fit_mode = XmGEO_PROPORTIONAL; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = 0; /* BB_MarginHeight(_w); */ layoutPtr->space_between = BB_MarginWidth(_w); newRow = TRUE; boxPtr++; } if (newRow) { layoutPtr++; boxPtr++; } if (SB_DialogType(_w) !!= XmDIALOG_COMMAND && SB_DialogType(_w) !!= XmDIALOG_PROMPT && SB_ChildPlacement(_w) == XmPLACE_ABOVE_SELECTION && nextras && extras[0] && XtIsManaged(extras[0]) && _XmGeoSetupKid(boxPtr, extras[0])) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; nrows++; } if (SB_SelectionLabel(_w) && XtIsManaged(SB_SelectionLabel(_w)) && _XmGeoSetupKid(boxPtr, SB_SelectionLabel(_w))) { layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 0; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; } if (SB_Text(_w) && XtIsManaged(SB_Text(_w)) && _XmGeoSetupKid(boxPtr, SB_Text(_w))) { layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->stretch_height = 0; layoutPtr->even_height = 1; layoutPtr->even_width = 0; layoutPtr->space_above = 0; /* BB_MarginHeight(_w); */ boxPtr += 2; layoutPtr++; } if (SB_ChildPlacement(_w) == XmPLACE_BELOW_SELECTION && nextras && extras[0] && XtIsManaged(extras[0]) && _XmGeoSetupKid(boxPtr, extras[0])) { layoutPtr->stretch_height = 1; layoutPtr->fill_mode = XmGEO_EXPAND; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); layoutPtr++; boxPtr += 2; nrows++; } if (SB_Separator(_w) && XtIsManaged(SB_Separator(_w)) && _XmGeoSetupKid( boxPtr, SB_Separator(_w))) { layoutPtr->fix_up = _XmSeparatorFix; layoutPtr->space_above = BB_MarginHeight(_w); boxPtr += 2; layoutPtr++; } newRow = False; if (SB_OkButton(_w) && XtIsManaged(SB_OkButton(_w)) && _XmGeoSetupKid(boxPtr++, SB_OkButton(_w))) { layoutPtr->fill_mode = XmGEO_CENTER; layoutPtr->fit_mode = XmGEO_WRAP; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); newRow = True; } for (i = 1; i < nextras; i++) { if (extras[i] && XtIsManaged(extras[i]) && (XmIsPushButton(extras[i]) || XmIsPushButtonGadget(extras[i])) && _XmGeoSetupKid(boxPtr++, extras[i])) { layoutPtr->fill_mode = XmGEO_CENTER; layoutPtr->fit_mode = XmGEO_WRAP; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); newRow = True; } } if (SB_ApplyButton(_w) && XtIsManaged(SB_ApplyButton(_w)) && _XmGeoSetupKid(boxPtr++, SB_ApplyButton(_w))) { layoutPtr->fill_mode = XmGEO_CENTER; layoutPtr->fit_mode = XmGEO_WRAP; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); newRow = True; } if (BB_CancelButton(_w) && XtIsManaged(BB_CancelButton(_w)) && _XmGeoSetupKid(boxPtr++, BB_CancelButton(_w))) { layoutPtr->fill_mode = XmGEO_CENTER; layoutPtr->fit_mode = XmGEO_WRAP; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); newRow = True; } if (SB_HelpButton(_w) && XtIsManaged(SB_HelpButton(_w)) && _XmGeoSetupKid(boxPtr++, SB_HelpButton(_w))) { layoutPtr->fill_mode = XmGEO_CENTER; layoutPtr->fit_mode = XmGEO_WRAP; layoutPtr->even_width = 1; layoutPtr->even_height = 1; layoutPtr->space_above = BB_MarginHeight(_w); newRow = True; } if (newRow) { layoutPtr++; boxPtr++; } layoutPtr->space_above = 0; /* BB_MarginHeight(_w); */ layoutPtr->end = TRUE; if (nextras) XtFree((char *)extras); return(geoSpec); }
While it may look scary, once you understand what it is doing, it really isn't. You can see the advantage of using the GeoUtils in SelectionBox: other than the code above, there really isn't any trace of geometry management in SelectionBox - it's all taken care of automagically.
Another point to note is SelectionBox's no_geo_request() method - it's slightly different, as the Command widget class doesn't even have a geo_matrix_create() method - instead, it inherits SelectionBox's.
Boolean _XmSelectionBoxNoGeoRequest(XmGeoMatrix _geoSpec) { if (BB_InSetValues(_geoSpec->composite) && (XtClass(_geoSpec->composite) == xmSelectionBoxWidgetClass || XtClass(_geoSpec->composite) == xmCommandWidgetClass)) return TRUE; return FALSE; }