
UserWidget Design Notes:
------------------------

Implementing the ability to allow cells to contain widgets is
surprisingly difficult.  The following is a series of design 
comments that poped up while I was trying to get this to work.

Method 1:  
---------
Following Andrew Lister's original work for cell
widgets, one simply puts the user's cell widget where-ever 
the Xbae text widget might have gone, and that's that. 

This seems to work at first, but runs into problems as soon
as one tries to scroll:  the user widgets are not properly
clipped to the scrolling region.  This is because the parent
of the user widget is the matrix itself, and not the scrolling
window.

Method 2: 
---------
The clip problem can be obviously solved using the
same trick that is used for the matrix text widget:  Reparent
the widget window to one of the clip windows (either the main 
scrolling clip, or the left/right/bottom/top clip windows).

This works, sort of.  The widgets are indeed properly drawn and 
properly clipped.  But ... reparenting the window of a widget 
tends to confuse the Intrinsics, and exposes Intrinsics and/or
Motif design flaws and/or bugs.  Basically, the Intrinsics were
designed to assume that the widget heirarchy is identical to
the window heirarchy.  For example, XtTranslateCoords 
fundamentally relies on this, and neither the Intrinsics nor 
Motif provide a way of adjusting the location after a window 
reparent.  

The important reprecussion of this is that mouse button press 
events may not by properly translated into widget callbacks.
For example, I experimented with an arrow widget.  When the 
window of the arrow widget is reparented, my copy of Motif
seems to properly issue an XmCR_ARM callback when the left
mouse button is pressed down on the arrow.  But it never issues 
XmCR_ACTIVATE when the nmouse buutton is released.  A purist
might argue that the ARM should never have been issued; 
I would argue that the ACTIVATE should have been.  My guess 
is that Motif calculates the ACTIVATE based on various widget 
geometries, rather than on the actual window geometries.  
Oddly enough, registering an event handler for the arrow
button does correctly deliver button presses *and* button 
releases over the arrow. Bizarre!

Since I figure that it would be safe to assume that large parts 
of Motif & the Intrinsics is broken in the same way, it is
clear that this is a bad solution.  Thus we are lead to 

Method 3:
---------
User cell widgets should have one of the clip windows as thier
parent.  This would provide the ideal solution:  the user 
widgets would be properly clipped in the scrolling windows, 
and we wouldn't be palying any dirty tricks on the Intrinsics 
with regard to geometry.

This solution sounds nice until you realize that in the current 
implementation of the clip widgets derive from XmPrimitive, and 
thus cannot have children.  Argh! Of course, the solution is
to modify the clip widgets so that they derive from Composite,
or, maybe better still, from XmBullitenBoard.  But this is a
lot of work that is potentially error prone and very tedious
to debug. The clip widgets are used all over the place, and to
verify that a fundamental modification of the clip widget won't
have unexpected side effects will take a lot of work.

-----------
Conclusion:
-----------

For this implementation, I fell back to Method 2, because
my app can deal with the reparenting issues (I had to hack
XmCR_ACTIVATE in my app).  I have run out of time to do
method 3, so, my apologies to y'all whose widgets don't work.

Suggestions if you have problems:
a) If you are not getting the proper callbacks, try mocking 
them up by using XtAddEventHandler on the widget that isn't 
handling the callbacks properly.

b) If you want to put your widget into a fixed cell, you'll
have to hack XbaeMatrix to handle this generality.  At this point,
I just have run out of steam.  Look for #if CELL_WIDGETS that
occur near the string "clip_window", and replace clip_window
by left_clip, right_clip, bottom_clip or top_clip as appropriate.

c) If you want to implement method 3, go for it.  Be sure to 
remove the XReparentWindow for the cell widgets.

-- linas
Linas Vepstas
October 1997


Appendix:
---------
Alternate, simplified explanation:


With Xt & X11, the window position is stored in *two* locations.
The X11 server stores it -- and it can be fetched with
XGetWindowPosition() or something like that.  The server is 
always right.

The Xt toolkit noticed that querying the server for a window position
is a CPU-cyle waster, and caches what it thinks is the correct window
position.  This is just fine, as long as one uses Xt consistently
to reposition windows.

Normally, when you use Xt to create a widget, one declares a parent
widget.  The parent widget always knows where its children are, and
thus, Xt always knows as well, and the value that the server stores,
and the value that Xt caches, both agree.

One serious limitation of Xt/Motif (a limitation that GTK does not have)
is that only *some*, not all, widgets can have children.  Xbae uses
a special "clip" widget for the scrolling region (and four others,
for the top/bottom/left/right fixed regions).  To correctly handle
a user widget in a scrolling xbae cell, it should be a child of the
scrolling/left/right/bottom/top clip widget.  Problem: the clip widget
is not allowed to have children.  Bummer.

My solution was to cheat, and use XReparentWindow() to make sure
that cell widgets scroll & get clipped appropriately when placed 
in the scrolling region.  This works just fine for many widgets,
and works almost perfectly for the rest.  However, some widgets
have problems. In particular, the Motif ArrowButton widget.  Seems
to compute button down events just fine, but lisses the button up
events. Beats me why.  ArrowButton is the only one that I know of 
that has this problem.  Arguably, its a bug, but given my cheating,
its a hard argument to make.


