[comp.windows.x] Xaw/Form.c bug - another int/unsigned int complication

csvsj@garnet.berkeley.edu (Steve Jacobson) (10/01/88)

The following bug report exposes another problem associated with the unsigned
int Dimension typecast:

VERSION:
	X11 release 2

CLIENT MACHINE:
	Sun 3/50

CLIENT OPERATING SYSTEM:
	Sun OS 3.2

DISPLAY:
	Sun monochrome

SYNOPSIS:
	Form widget behaves improperly when downsized to a point where
	a child's geometry constraints causes it not to be visible. A
	subsequent upsize making the form large enough for the child to be
	visible again reveals that the geometry constraints have been violated.

DESCRIPTION:
	The Resize() procedure in Form.c goes through some careful calculations
	to adhere to a child's geometry constraint. The resulting width and
	height results may be negative given a child's constraints and the
	form's new size. The current code does a dirty fix; if height or width
	is less than 1, it is reset to one before the child is moved and
	resized.

	There are similar problems with x and y.

	This action causes the form to "forget" the original constraint
	geometry, so when the form is resized large enough to restore the
	original layout, the results are wrong.

REPEAT-BY:
	Compile and run this program:


#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Command.h>
#include <X11/Form.h>
#include <X11/Cardinals.h>

main(argc, argv)
   int argc;
   char **argv;
{
   Arg args[15];
   Widget top_level, form, button_1, button_2, button_3;
   
   top_level = XtInitialize(NULL, "bug",
               "BUg", 0, &argc, argv);
   form = XtCreateManagedWidget("Form", formWidgetClass,
                           top_level, NULL, ZERO);
   XtSetArg(args[0], XtNbottom, XtChainTop);
   XtSetArg(args[1], XtNtop, XtChainTop);
   XtSetArg(args[2], XtNleft, XtChainLeft);
   XtSetArg(args[3], XtNright, XtChainLeft);
   button_1 = XtCreateManagedWidget("this is button_1", commandWidgetClass,
                              form, args, FOUR);
   XtSetArg(args[4], XtNfromHoriz, button_1);
   button_2 = XtCreateManagedWidget("this is button_2", commandWidgetClass,
                              form, args, FIVE);
   XtSetArg(args[3], XtNright, XtChainRight);
   XtSetArg(args[4], XtNfromHoriz, button_2);
   button_3 = XtCreateManagedWidget("this is button_3", commandWidgetClass,
                              form, args, FIVE);
   XtRealizeWidget(top_level);
   XtMainLoop();
}

   Resize the window larger and see how button_3 tracks the window's
   right edge while button_1 and button_2 stay the same size.

   Now resize the window smaller, but not so small that there is no room
   for button_3. Notice that button_3 shrinks correctly, preserving the
   space between its right border and the window's right edge.

   If you resize the window larger again, button_3 again tracks the window's
   right edge correctly.

   Now resize the window smaller, so small that there is no space for button_3.
   Notice that the window correctly clips button_2.

   Now resize the window large enough to show button_3. Notice that button_2
   is redisplayed correctly, but button_3 is apparently clipped by the
   window; its right border and the space between it and the window's
   right edge is not visible.

   The form has altered button_3's geometry constraint behavior. No matter
   what you do now, the window will clip button_3's right edge when it is
   visible.

FIX:
   Here is diff output comparing a fixed Form.c with the original:

220c220
<     return (loc);
---
>     return (loc > 0) ? loc : 0;
252a253,254
>    if (width < 1) width = 1;
>    if (height < 1) height = 1;

   This fix removes the damaging resetting actions.

   Note that this fix is dependent on non-portable int/unsigned int type
   conversions. The core width and height are unsigned ints, so a negative
   width or height is treated as a very large number, so the child widget's
   window is clipped by the form widget, as it is under the original code.
   However, when the form is upsized, the value is then treated as a
   negative number in the coordinate calculations. While this will work
   on most architectures, it is not guarenteed to be portable.

   To fix it right would require, at the minimum, two additional integer
   variables in the core private data structure to hold negative
   dimension values in a portable manner. The resize calculations
   would use these instead of the unsigned ints.