Home | History | Annotate | Line # | Download | only in src
      1 /***********************************************************
      2 
      3 Copyright 1987, 1988, 1994, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24 
     25 
     26 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
     27 
     28                         All Rights Reserved
     29 
     30 Permission to use, copy, modify, and distribute this software and its
     31 documentation for any purpose and without fee is hereby granted,
     32 provided that the above copyright notice appear in all copies and that
     33 both that copyright notice and this permission notice appear in
     34 supporting documentation, and that the name of Digital not be
     35 used in advertising or publicity pertaining to distribution of the
     36 software without specific, written prior permission.
     37 
     38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     44 SOFTWARE.
     45 
     46 ******************************************************************/
     47 
     48 #ifdef HAVE_CONFIG_H
     49 #include <config.h>
     50 #endif
     51 #include <X11/IntrinsicP.h>
     52 #include <X11/StringDefs.h>
     53 #include <X11/Xmu/Misc.h>
     54 #include <X11/Xaw/Scrollbar.h>
     55 #include <X11/Xaw/ViewportP.h>
     56 #include <X11/Xaw/XawInit.h>
     57 #include "Private.h"
     58 
     59 /*
     60  * Class Methods
     61  */
     62 static Boolean Layout(FormWidget, unsigned int, unsigned int, Bool);
     63 static void XawViewportChangeManaged(Widget);
     64 static void XawViewportInitialize(Widget, Widget, ArgList, Cardinal*);
     65 static void
     66 XawViewportConstraintInitialize(Widget, Widget, ArgList, Cardinal*);
     67 static XtGeometryResult XawViewportGeometryManager(Widget, XtWidgetGeometry*,
     68 						   XtWidgetGeometry*);
     69 static XtGeometryResult XawViewportQueryGeometry(Widget,
     70 						 XtWidgetGeometry*,
     71 						 XtWidgetGeometry*);
     72 static void XawViewportRealize(Widget, XtValueMask*, XSetWindowAttributes*);
     73 static void XawViewportResize(Widget);
     74 static Boolean XawViewportSetValues(Widget, Widget, Widget,
     75 				    ArgList, Cardinal*);
     76 
     77 /*
     78  * Prototypes
     79  */
     80 static void ComputeLayout(Widget, Bool, Bool);
     81 static void ComputeWithForceBars(Widget, Bool, XtWidgetGeometry*,
     82 				 int*, int*);
     83 static Widget CreateScrollbar(ViewportWidget, Bool);
     84 static XtGeometryResult GeometryRequestPlusScrollbar(ViewportWidget, Bool,
     85 						     XtWidgetGeometry*,
     86 						     XtWidgetGeometry*);
     87 static Bool GetGeometry(Widget, unsigned int, unsigned int);
     88 static void MoveChild(ViewportWidget, int, int);
     89 static XtGeometryResult QueryGeometry(ViewportWidget, XtWidgetGeometry*,
     90 				      XtWidgetGeometry*);
     91 static void RedrawThumbs(ViewportWidget);
     92 static void ScrollUpDownProc(Widget, XtPointer, XtPointer);
     93 static void SendReport(ViewportWidget, unsigned int);
     94 static void SetBar(Widget, int, unsigned int, unsigned int);
     95 static XtGeometryResult TestSmaller(ViewportWidget, XtWidgetGeometry*,
     96 				    XtWidgetGeometry*);
     97 static void ThumbProc(Widget, XtPointer, XtPointer);
     98 
     99 /*
    100  * Initialization
    101  */
    102 #define offset(field) XtOffsetOf(ViewportRec, viewport.field)
    103 static XtResource resources[] = {
    104   {
    105     XtNforceBars,
    106     XtCBoolean,
    107     XtRBoolean,
    108     sizeof(Boolean),
    109     offset(forcebars),
    110     XtRImmediate,
    111     (XtPointer)False
    112   },
    113   {
    114     XtNallowHoriz,
    115     XtCBoolean,
    116     XtRBoolean,
    117     sizeof(Boolean),
    118     offset(allowhoriz),
    119     XtRImmediate,
    120     (XtPointer)False
    121   },
    122   {
    123     XtNallowVert,
    124     XtCBoolean,
    125     XtRBoolean,
    126     sizeof(Boolean),
    127     offset(allowvert),
    128     XtRImmediate,
    129     (XtPointer)False
    130   },
    131   {
    132     XtNuseBottom,
    133     XtCBoolean,
    134     XtRBoolean,
    135     sizeof(Boolean),
    136     offset(usebottom),
    137     XtRImmediate,
    138     (XtPointer)False
    139   },
    140   {
    141     XtNuseRight,
    142     XtCBoolean,
    143     XtRBoolean,
    144     sizeof(Boolean),
    145     offset(useright),
    146     XtRImmediate,
    147     (XtPointer)False
    148   },
    149   {
    150     XtNreportCallback,
    151     XtCReportCallback,
    152     XtRCallback,
    153     sizeof(XtPointer),
    154     offset(report_callbacks),
    155     XtRImmediate,
    156     NULL
    157   },
    158 };
    159 #undef offset
    160 
    161 #define Superclass	(&formClassRec)
    162 ViewportClassRec viewportClassRec = {
    163   /* core */
    164   {
    165     (WidgetClass)Superclass,		/* superclass */
    166     "Viewport",				/* class_name */
    167     sizeof(ViewportRec),		/* widget_size */
    168     XawInitializeWidgetSet,		/* class_initialize */
    169     NULL,				/* class_part_init */
    170     False,				/* class_inited */
    171     XawViewportInitialize,		/* initialize */
    172     NULL,				/* initialize_hook */
    173     XawViewportRealize,			/* realize */
    174     NULL,				/* actions */
    175     0,					/* num_actions */
    176     resources,				/* resources */
    177     XtNumber(resources),		/* num_resources */
    178     NULLQUARK,				/* xrm_class */
    179     True,				/* compress_motion */
    180     True,				/* compress_exposure */
    181     True,				/* compress_enterleave */
    182     False,				/* visible_interest */
    183     NULL,				/* destroy */
    184     XawViewportResize,			/* resize */
    185     XtInheritExpose,			/* expose */
    186     XawViewportSetValues,		/* set_values */
    187     NULL,				/* set_values_hook */
    188     XtInheritSetValuesAlmost,		/* set_values_almost */
    189     NULL,				/* get_values_hook */
    190     NULL,				/* accept_focus */
    191     XtVersion,				/* version */
    192     NULL,				/* callback_private */
    193     NULL,				/* tm_table */
    194     XawViewportQueryGeometry,		/* query_geometry */
    195     XtInheritDisplayAccelerator,	/* display_accelerator */
    196     NULL,				/* extension */
    197   },
    198   /* composite */
    199   {
    200     XawViewportGeometryManager,		/* geometry_manager */
    201     XawViewportChangeManaged,		/* change_managed */
    202     XtInheritInsertChild,		/* insert_child */
    203     XtInheritDeleteChild,		/* delete_child */
    204     NULL,				/* extension */
    205   },
    206   /* constraint */
    207   {
    208     NULL,				/* subresourses */
    209     0,					/* subresource_count */
    210     sizeof(ViewportConstraintsRec),	/* constraint_size */
    211     XawViewportConstraintInitialize,	/* initialize */
    212     NULL,				/* destroy */
    213     NULL,				/* set_values */
    214     NULL,				/* extension */
    215   },
    216   /* form */
    217   {
    218     Layout,				/* layout */
    219 #ifndef OLDXAW
    220     NULL,
    221 #endif
    222   },
    223   /* viewport */
    224   {
    225     NULL,				/* extension */
    226   },
    227 };
    228 
    229 WidgetClass viewportWidgetClass = (WidgetClass)&viewportClassRec;
    230 
    231 /*
    232  * Implementation
    233  */
    234 static Widget
    235 CreateScrollbar(ViewportWidget w, Bool horizontal)
    236 {
    237     static Arg barArgs[] = {
    238 	{XtNorientation,	    0},
    239 	{XtNlength,		    0},
    240 	{XtNleft,		    0},
    241 	{XtNright,		    0},
    242 	{XtNtop,		    0},
    243 	{XtNbottom,		    0},
    244 	{XtNmappedWhenManaged,	    False},
    245     };
    246     Widget clip = w->viewport.clip;
    247     ViewportConstraints constraints =
    248 	(ViewportConstraints)clip->core.constraints;
    249     Widget bar;
    250 
    251     XtSetArg(barArgs[0], XtNorientation,
    252 	   horizontal ? XtorientHorizontal : XtorientVertical);
    253     XtSetArg(barArgs[1], XtNlength,
    254 	   horizontal ? XtWidth(clip) : XtHeight(clip));
    255     XtSetArg(barArgs[2], XtNleft,
    256 	   !horizontal && w->viewport.useright ? XtChainRight : XtChainLeft);
    257     XtSetArg(barArgs[3], XtNright,
    258 	   !horizontal && !w->viewport.useright ? XtChainLeft : XtChainRight);
    259     XtSetArg(barArgs[4], XtNtop,
    260 	   horizontal && w->viewport.usebottom ? XtChainBottom: XtChainTop);
    261     XtSetArg(barArgs[5], XtNbottom,
    262 	   horizontal && !w->viewport.usebottom ? XtChainTop: XtChainBottom);
    263 
    264     bar = XtCreateWidget(horizontal ? "horizontal" : "vertical",
    265 			 scrollbarWidgetClass, (Widget)w,
    266 			 barArgs, XtNumber(barArgs));
    267     XtAddCallback(bar, XtNscrollProc, ScrollUpDownProc, (XtPointer)w);
    268     XtAddCallback(bar, XtNjumpProc, ThumbProc, (XtPointer)w);
    269 
    270     if (horizontal) {
    271 	w->viewport.horiz_bar = bar;
    272 	constraints->form.vert_base = bar;
    273     }
    274     else {
    275 	w->viewport.vert_bar = bar;
    276 	constraints->form.horiz_base = bar;
    277     }
    278 
    279     XtManageChild(bar);
    280 
    281     return (bar);
    282 }
    283 
    284 /*ARGSUSED*/
    285 static void
    286 XawViewportInitialize(Widget request _X_UNUSED, Widget cnew,
    287 		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    288 {
    289     ViewportWidget w = (ViewportWidget)cnew;
    290     static Arg clip_args[8];
    291     Cardinal arg_cnt;
    292     Widget h_bar, v_bar;
    293     Dimension clip_height, clip_width;
    294 
    295     w->form.default_spacing = 0; /* Reset the default spacing to 0 pixels */
    296 
    297     /*
    298      * Initialize all widget pointers to NULL
    299      */
    300     w->viewport.child = NULL;
    301     w->viewport.horiz_bar = w->viewport.vert_bar = NULL;
    302 
    303     /*
    304      * Create Clip Widget
    305      */
    306     arg_cnt = 0;
    307     XtSetArg(clip_args[arg_cnt], XtNbackgroundPixmap, None);	arg_cnt++;
    308     XtSetArg(clip_args[arg_cnt], XtNborderWidth, 0);		arg_cnt++;
    309     XtSetArg(clip_args[arg_cnt], XtNleft, XtChainLeft);		arg_cnt++;
    310     XtSetArg(clip_args[arg_cnt], XtNright, XtChainRight);	arg_cnt++;
    311     XtSetArg(clip_args[arg_cnt], XtNtop, XtChainTop);		arg_cnt++;
    312     XtSetArg(clip_args[arg_cnt], XtNbottom, XtChainBottom);	arg_cnt++;
    313     XtSetArg(clip_args[arg_cnt], XtNwidth, XtWidth(w));		arg_cnt++;
    314     XtSetArg(clip_args[arg_cnt], XtNheight, XtHeight(w));	arg_cnt++;
    315 
    316     w->viewport.clip = XtCreateManagedWidget("clip", widgetClass, cnew,
    317 					     clip_args, arg_cnt);
    318 
    319     if (!w->viewport.forcebars)
    320 	return;		 /* If we are not forcing the bars then we are done */
    321 
    322     if (w->viewport.allowhoriz)
    323 	(void)CreateScrollbar(w, True);
    324     if (w->viewport.allowvert)
    325 	(void)CreateScrollbar(w, False);
    326 
    327     h_bar = w->viewport.horiz_bar;
    328     v_bar = w->viewport.vert_bar;
    329 
    330     /*
    331      * Set the clip widget to the correct height
    332      */
    333     clip_width = XtWidth(w);
    334     clip_height = XtHeight(w);
    335 
    336     if (h_bar != NULL &&  XtWidth(w) > XtWidth(h_bar) + XtBorderWidth(h_bar))
    337 	clip_width = (Dimension)(clip_width - (XtWidth(h_bar) + XtBorderWidth(h_bar)));
    338 
    339     if (v_bar != NULL && XtHeight(w) > XtHeight(v_bar) + XtBorderWidth(v_bar))
    340 	clip_height = (Dimension)(clip_height - (XtHeight(v_bar) + XtBorderWidth(v_bar)));
    341 
    342     arg_cnt = 0;
    343     XtSetArg(clip_args[arg_cnt], XtNwidth, clip_width); arg_cnt++;
    344     XtSetArg(clip_args[arg_cnt], XtNheight, clip_height); arg_cnt++;
    345     XtSetValues(w->viewport.clip, clip_args, arg_cnt);
    346 }
    347 
    348 /*ARGSUSED*/
    349 static void
    350 XawViewportConstraintInitialize(Widget request _X_UNUSED, Widget cnew,
    351 				ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    352 {
    353     ((ViewportConstraints)cnew->core.constraints)->viewport.reparented = False;
    354 }
    355 
    356 static void
    357 XawViewportRealize(Widget widget, XtValueMask *value_mask,
    358 		   XSetWindowAttributes *attributes)
    359 {
    360     ViewportWidget w = (ViewportWidget)widget;
    361     Widget child = w->viewport.child;
    362     Widget clip = w->viewport.clip;
    363 
    364     *value_mask |= CWBitGravity;
    365     attributes->bit_gravity = NorthWestGravity;
    366     (*Superclass->core_class.realize)(widget, value_mask, attributes);
    367 
    368     (*w->core.widget_class->core_class.resize)(widget);	/* turn on bars */
    369 
    370     if (child != NULL) {
    371 	XtMoveWidget(child, 0, 0);
    372 	XtRealizeWidget(clip);
    373 	XtRealizeWidget(child);
    374 	XReparentWindow(XtDisplay(w), XtWindow(child), XtWindow(clip), 0, 0);
    375 	XtMapWidget(child);
    376     }
    377 }
    378 
    379 /*ARGSUSED*/
    380 static Boolean
    381 XawViewportSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
    382 		     ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    383 {
    384     ViewportWidget w = (ViewportWidget)cnew;
    385     ViewportWidget cw = (ViewportWidget)current;
    386 
    387     if (w->viewport.forcebars != cw->viewport.forcebars
    388 	|| w->viewport.allowvert != cw->viewport.allowvert
    389 	|| w->viewport.allowhoriz != cw->viewport.allowhoriz
    390 	|| w->viewport.useright != cw->viewport.useright
    391 	|| w->viewport.usebottom != cw->viewport.usebottom)
    392 	(*w->core.widget_class->core_class.resize)(cnew); /* Recompute layout */
    393 
    394     return (False);
    395 }
    396 
    397 static void
    398 XawViewportChangeManaged(Widget widget)
    399 {
    400     ViewportWidget w = (ViewportWidget)widget;
    401     int num_children = (int)w->composite.num_children;
    402     Widget child, *childP;
    403     int i;
    404 
    405     child = NULL;
    406     for (childP = w->composite.children,
    407 	 i = 0; i < num_children;
    408 	 childP++, i++) {
    409 	if (XtIsManaged(*childP)
    410 	    && *childP != w->viewport.clip
    411 	    && *childP != w->viewport.horiz_bar
    412 	    && *childP != w->viewport.vert_bar)	{
    413 	    child = *childP;
    414 	    break;
    415 	}
    416     }
    417 
    418     if (child != w->viewport.child) {
    419 	w->viewport.child = child;
    420 	if (child != NULL) {
    421 	    XtResizeWidget(child, XtWidth(child), XtHeight(child), 0);
    422 	    if (XtIsRealized(widget)) {
    423 		ViewportConstraints constraints =
    424 		    (ViewportConstraints)child->core.constraints;
    425 		if (!XtIsRealized(child)) {
    426 		    Window window = XtWindow(w);
    427 
    428 		    XtMoveWidget(child, 0, 0);
    429 		    w->core.window = XtWindow(w->viewport.clip);
    430 		    XtRealizeWidget(child);
    431 		    w->core.window = window;
    432 		    constraints->viewport.reparented = True;
    433 		}
    434 		else if (!constraints->viewport.reparented) {
    435 		    XReparentWindow(XtDisplay(w), XtWindow(child),
    436 				    XtWindow(w->viewport.clip), 0, 0);
    437 		    constraints->viewport.reparented = True;
    438 		    if (child->core.mapped_when_managed)
    439 		    XtMapWidget(child);
    440 		}
    441 	    }
    442 	    GetGeometry(widget, XtWidth(child), XtHeight(child));
    443 	    (*((ViewportWidgetClass)w->core.widget_class)->form_class.layout)
    444 	    ((FormWidget)w, XtWidth(w), XtHeight(w), True /* True? */);
    445 	}
    446     }
    447 }
    448 
    449 static void
    450 SetBar(Widget w, int top, unsigned int length, unsigned int total)
    451 {
    452     XawScrollbarSetThumb(w, (float)top / (float)total,
    453 			 (float)length / (float)total);
    454 }
    455 
    456 static void
    457 RedrawThumbs(ViewportWidget w)
    458 {
    459     Widget child = w->viewport.child;
    460     Widget clip = w->viewport.clip;
    461 
    462     if (w->viewport.horiz_bar != NULL)
    463 	SetBar(w->viewport.horiz_bar, -(int)XtX(child),
    464 	       XtWidth(clip), XtWidth(child));
    465 
    466     if (w->viewport.vert_bar != NULL)
    467 	SetBar(w->viewport.vert_bar, -(int)XtY(child),
    468 	       XtHeight(clip), XtHeight(child));
    469 }
    470 
    471 static void
    472 SendReport(ViewportWidget w, unsigned int changed)
    473 {
    474     if (w->viewport.report_callbacks) {
    475 	Widget child = w->viewport.child;
    476 	Widget clip = w->viewport.clip;
    477 	XawPannerReport rep = {
    478 	    .changed = changed,
    479 	    .slider_x = (Position) -XtX(child),	/* child is canvas */
    480 	    .slider_y = (Position) -XtY(child),	/* clip is slider */
    481 	    .slider_width = XtWidth(clip),
    482 	    .slider_height = XtHeight(clip),
    483 	    .canvas_width = XtWidth(child),
    484 	    .canvas_height = XtHeight(child)
    485 	};
    486 	XtCallCallbackList((Widget)w, w->viewport.report_callbacks,
    487 			   (XtPointer)&rep);
    488     }
    489 }
    490 
    491 static void
    492 MoveChild(ViewportWidget w, int x, int y)
    493 {
    494     Widget child = w->viewport.child;
    495     Widget clip = w->viewport.clip;
    496 
    497     /* make sure we never move past right/bottom borders */
    498     if (-x + (int)XtWidth(clip) > XtWidth(child))
    499 	x = -(int)(XtWidth(child) - XtWidth(clip));
    500 
    501     if (-y + (int)XtHeight(clip) > XtHeight(child))
    502 	y = -(int)(XtHeight(child) - XtHeight(clip));
    503 
    504     /* make sure we never move past left/top borders */
    505     if (x >= 0)
    506 	x = 0;
    507     if (y >= 0)
    508 	y = 0;
    509 
    510     XtMoveWidget(child, (Position) x, (Position) y);
    511     SendReport(w, (XawPRSliderX | XawPRSliderY));
    512 
    513     RedrawThumbs(w);
    514 }
    515 
    516 static void
    517 ComputeLayout(Widget widget, Bool query, Bool destroy_scrollbars)
    518 {
    519     ViewportWidget w = (ViewportWidget)widget;
    520     Widget child = w->viewport.child;
    521     Widget clip = w->viewport.clip;
    522     ViewportConstraints constraints =
    523 	(ViewportConstraints)clip->core.constraints;
    524     Bool needshoriz, needsvert;
    525     int clip_width, clip_height;
    526     XtWidgetGeometry intended;
    527 
    528     if (child == NULL)
    529 	return;
    530 
    531     clip_width = XtWidth(w);
    532     clip_height = XtHeight(w);
    533     intended.request_mode = CWBorderWidth;
    534     intended.border_width = 0;
    535 
    536     if (w->viewport.forcebars) {
    537 	needsvert = w->viewport.allowvert;
    538 	needshoriz = w->viewport.allowhoriz;
    539 	ComputeWithForceBars(widget, query, &intended,
    540 			     &clip_width, &clip_height);
    541     }
    542     else {
    543 	Dimension prev_width, prev_height;
    544 	XtGeometryMask prev_mode;
    545 	XtWidgetGeometry preferred;
    546 
    547 	needshoriz = needsvert = False;
    548 
    549 	/*
    550 	 * intended.{width,height} caches the eventual child dimensions,
    551 	 * but we don't set the mode bits until after we decide that the
    552 	 * child's preferences are not acceptable
    553 	 */
    554 	if (!w->viewport.allowhoriz)
    555 	    intended.request_mode |= CWWidth;
    556 
    557 	if (XtWidth(child) < clip_width)
    558 	    intended.width = (Dimension)clip_width;
    559 	else
    560 	    intended.width = XtWidth(child);
    561 
    562 	if (XtHeight(child) < clip_height)
    563 	    intended.height = (Dimension)clip_height;
    564 	else
    565 	    intended.height = XtHeight(child);
    566 
    567 	if (!w->viewport.allowvert)
    568 	    intended.request_mode |= CWHeight;
    569 
    570 	if (!query) {
    571 	    preferred.width = XtWidth(child);
    572 	    preferred.height = XtHeight(child);
    573 	}
    574 	do { /* while intended != prev  */
    575 	    if (query) {
    576 		(void)XtQueryGeometry(child, &intended, &preferred);
    577 		if (!(preferred.request_mode & CWWidth))
    578 		    preferred.width = intended.width;
    579 		if (!(preferred.request_mode & CWHeight))
    580 		    preferred.height = intended.height;
    581 	    }
    582 	    prev_width = intended.width;
    583 	    prev_height = intended.height;
    584 	    prev_mode = intended.request_mode;
    585 	    /*
    586 	     * note that having once decided to turn on either bar
    587 	     * we'll not change our mind until we're next resized,
    588 	     * thus avoiding potential oscillations
    589 	     */
    590 #define CheckHoriz() \
    591 	    if (w->viewport.allowhoriz &&				\
    592 		preferred.width > clip_width) {				\
    593 		if (!needshoriz) {					\
    594 		    Widget bar2;					\
    595 									\
    596 		    needshoriz = True;					\
    597 		    if ((bar2 = w->viewport.horiz_bar) == NULL)		\
    598 			bar2 = CreateScrollbar(w, True);		\
    599 		    clip_height -= XtHeight(bar2) + XtBorderWidth(bar2);\
    600 		    if (clip_height < 1)				\
    601 			clip_height = 1;				\
    602 		}							\
    603 		intended.width = preferred.width;			\
    604 	    }
    605 
    606 	    CheckHoriz();
    607 	    if (w->viewport.allowvert && preferred.height > clip_height) {
    608 		if (!needsvert) {
    609 		    Widget bar;
    610 		    needsvert = True;
    611 		    if ((bar = w->viewport.vert_bar) == NULL)
    612 			bar = CreateScrollbar(w, False);
    613 		    clip_width -= XtWidth(bar) + XtBorderWidth(bar);
    614 		    if (clip_width < 1)
    615 			clip_width = 1;
    616 		    CheckHoriz();
    617 		}
    618 		intended.height = preferred.height;
    619 	    }
    620 	    if (!w->viewport.allowhoriz || preferred.width < clip_width) {
    621 		intended.width = (Dimension)clip_width;
    622 		intended.request_mode |= CWWidth;
    623 	    }
    624 	    if (!w->viewport.allowvert || preferred.height < clip_height) {
    625 		intended.height = (Dimension)clip_height;
    626 		intended.request_mode |= CWHeight;
    627 	    }
    628 	} while (intended.request_mode != prev_mode
    629 		 || (intended.request_mode & CWWidth
    630 		     && intended.width != prev_width)
    631 		 || (intended.request_mode & CWHeight
    632 		     && intended.height != prev_height));
    633     }
    634 
    635     if (XtIsRealized(clip))
    636 	XRaiseWindow(XtDisplay(clip), XtWindow(clip));
    637 
    638     XtMoveWidget(clip,
    639 		 (Position)(needsvert ? w->viewport.useright ? 0 :
    640 					XtWidth(w->viewport.vert_bar)
    641 			    + XtBorderWidth(w->viewport.vert_bar) : 0),
    642 		 (Position)(needshoriz ? w->viewport.usebottom ? 0 :
    643 					 XtHeight(w->viewport.horiz_bar)
    644 			    + XtBorderWidth(w->viewport.horiz_bar) : 0));
    645     XtResizeWidget(clip, (Dimension)clip_width, (Dimension)clip_height, 0);
    646 
    647     if (w->viewport.horiz_bar != NULL) {
    648 	Widget bar = w->viewport.horiz_bar;
    649 
    650 	if (!needshoriz) {
    651 	    constraints->form.vert_base = NULL;
    652 	    if (destroy_scrollbars) {
    653 		XtDestroyWidget(bar);
    654 		w->viewport.horiz_bar = NULL;
    655 	    }
    656 	}
    657 	else {
    658 	    int bw = XtBorderWidth(bar);
    659 
    660 	    XtResizeWidget(bar, (Dimension)clip_width, (Dimension)XtHeight(bar), (Dimension)bw);
    661 	    XtMoveWidget(bar,
    662 			 (Position)(needsvert && !w->viewport.useright
    663 				    ? XtWidth(w->viewport.vert_bar) : -bw),
    664 			 (Position)(w->viewport.usebottom
    665 				    ? XtHeight(w) - XtHeight(bar) - bw : -bw));
    666 	    XtSetMappedWhenManaged(bar, True);
    667 	}
    668     }
    669 
    670     if (w->viewport.vert_bar != NULL) {
    671 	Widget bar = w->viewport.vert_bar;
    672 
    673 	if (!needsvert)	{
    674 	    constraints->form.horiz_base = NULL;
    675 	    if (destroy_scrollbars) {
    676 		XtDestroyWidget(bar);
    677 		w->viewport.vert_bar = NULL;
    678 	    }
    679 	}
    680 	else {
    681 	    int bw = bar->core.border_width;
    682 
    683 	    XtResizeWidget(bar, (Dimension)XtWidth(bar), (Dimension)clip_height, (Dimension)bw);
    684 	    XtMoveWidget(bar,
    685 			(Position)(w->viewport.useright
    686 				   ? XtWidth(w) - XtWidth(bar) - bw : -bw),
    687 			(Position)(needshoriz && !w->viewport.usebottom
    688 				   ? XtHeight(w->viewport.horiz_bar) : -bw));
    689 	   XtSetMappedWhenManaged(bar, True);
    690 	}
    691     }
    692 
    693     if (child != NULL) {
    694 	XtResizeWidget(child, intended.width, intended.height, 0);
    695 	MoveChild(w, needshoriz ? XtX(child) : 0,	needsvert ? XtY(child) : 0);
    696     }
    697 
    698     SendReport (w, XawPRAll);
    699 }
    700 
    701 /*
    702  * Function:
    703  *	ComputeWithForceBars
    704  *
    705  * Parameters:
    706  *	widget	    - viewport widget
    707  *	query	    - whether or not to query the child
    708  *	intended    - cache of the child's height is stored here
    709  *		      (used and returned)
    710  *	clip_width  - size of clip window (used and returned)
    711  *	clip_height - ""
    712  *
    713  * Description:
    714  *	Computes the layout give forcebars is set.
    715  */
    716 static void
    717 ComputeWithForceBars(Widget widget, Bool query, XtWidgetGeometry *intended,
    718 		     int *clip_width, int *clip_height)
    719 {
    720     ViewportWidget w = (ViewportWidget)widget;
    721     Widget child = w->viewport.child;
    722     XtWidgetGeometry preferred;
    723 
    724     /*
    725      * If forcebars then needs = allows = has
    726      * Thus if needsvert is set it MUST have a scrollbar
    727      */
    728     if (w->viewport.allowvert) {
    729 	if (w->viewport.vert_bar == NULL)
    730 	    w->viewport.vert_bar = CreateScrollbar(w, False);
    731 
    732 	*clip_width -= XtWidth(w->viewport.vert_bar) +
    733 		       XtBorderWidth(w->viewport.vert_bar);
    734     }
    735 
    736     if (w->viewport.allowhoriz) {
    737 	if (w->viewport.horiz_bar == NULL)
    738 	    w->viewport.horiz_bar = CreateScrollbar(w, True);
    739 
    740 	*clip_height -= XtHeight(w->viewport.horiz_bar) +
    741 			XtBorderWidth(w->viewport.horiz_bar);
    742     }
    743 
    744     AssignMax(*clip_width, 1);
    745     AssignMax(*clip_height, 1);
    746 
    747     if (!w->viewport.allowvert) {
    748 	intended->height = (Dimension)*clip_height;
    749 	intended->request_mode = CWHeight;
    750     }
    751     if (!w->viewport.allowhoriz) {
    752 	intended->width = (Dimension)*clip_width;
    753 	intended->request_mode = CWWidth;
    754     }
    755 
    756     if (query) {
    757 	if (w->viewport.allowvert || w->viewport.allowhoriz) {
    758 	    XtQueryGeometry(child, intended, &preferred);
    759 
    760 	    if (!(intended->request_mode & CWWidth)) {
    761 		if (preferred.request_mode & CWWidth)
    762 		    intended->width = preferred.width;
    763 		else
    764 		    intended->width = XtWidth(child);
    765 	    }
    766 
    767 	    if (!(intended->request_mode & CWHeight)) {
    768 		if (preferred.request_mode & CWHeight)
    769 		    intended->height = preferred.height;
    770 		else
    771 		    intended->height = XtHeight(child);
    772 	    }
    773 	}
    774     }
    775     else {
    776 	if (w->viewport.allowvert)
    777 	    intended->height = XtHeight(child);
    778 	if (w->viewport.allowhoriz)
    779 	    intended->width = XtWidth(child);
    780     }
    781 
    782     if (*clip_width > (int)intended->width)
    783 	intended->width = (Dimension)*clip_width;
    784     if (*clip_height > (int)intended->height)
    785 	intended->height = (Dimension)*clip_height;
    786 }
    787 
    788 static void
    789 XawViewportResize(Widget widget)
    790 {
    791     ComputeLayout(widget, True, True);
    792 }
    793 
    794 /*ARGSUSED*/
    795 static Boolean
    796 Layout(FormWidget w, unsigned int width _X_UNUSED, unsigned int height _X_UNUSED, Bool force _X_UNUSED)
    797 {
    798     ComputeLayout((Widget)w, True, True);
    799     w->form.preferred_width = XtWidth(w);
    800     w->form.preferred_height = XtHeight(w);
    801 
    802     return (False);
    803 }
    804 
    805 static void
    806 ScrollUpDownProc(Widget widget, XtPointer closure, XtPointer call_data)
    807 {
    808     ViewportWidget w = (ViewportWidget)closure;
    809     Widget child = w->viewport.child;
    810     int pix = (int)(long)call_data;
    811     int x, y;
    812 
    813     if (child == NULL)
    814 	return;
    815 
    816     x = XtX(child) - (widget == w->viewport.horiz_bar ? pix : 0);
    817     y = XtY(child) - (widget == w->viewport.vert_bar ? pix : 0);
    818     MoveChild(w, x, y);
    819 }
    820 
    821 /*ARGSUSED*/
    822 static void
    823 ThumbProc(Widget widget, XtPointer closure, XtPointer call_data)
    824 {
    825     ViewportWidget w = (ViewportWidget)closure;
    826     Widget child = w->viewport.child;
    827     float percent = *(float *)call_data;
    828     int x, y;
    829 
    830     if (child == NULL)
    831 	return;
    832 
    833     if (widget == w->viewport.horiz_bar)
    834 	x = (int)(-percent * XtWidth(child));
    835     else
    836 	x = XtX(child);
    837 
    838     if (widget == w->viewport.vert_bar)
    839 	y = (int)(-percent * XtHeight(child));
    840     else
    841 	y = XtY(child);
    842 
    843     MoveChild(w, x, y);
    844 }
    845 
    846 static XtGeometryResult
    847 TestSmaller(ViewportWidget w, XtWidgetGeometry *request,
    848 	    XtWidgetGeometry *reply_return)
    849 {
    850     if (request->width < XtWidth(w) || request->height < XtHeight(w))
    851 	return (XtMakeGeometryRequest((Widget)w, request, reply_return));
    852 
    853     return (XtGeometryYes);
    854 }
    855 
    856 static XtGeometryResult
    857 GeometryRequestPlusScrollbar(ViewportWidget w, Bool horizontal,
    858 			     XtWidgetGeometry *request,
    859 			     XtWidgetGeometry *reply_return)
    860 {
    861     Widget sb;
    862     XtWidgetGeometry plusScrollbars;
    863 
    864     plusScrollbars = *request;
    865     if ((sb = w->viewport.horiz_bar) == NULL)
    866 	sb = CreateScrollbar(w, horizontal);
    867     request->width = (Dimension)(request->width + XtWidth(sb));
    868     request->height = (Dimension)(request->height + XtHeight(sb));
    869     XtDestroyWidget(sb);
    870     return (XtMakeGeometryRequest((Widget)w, &plusScrollbars, reply_return));
    871 }
    872 
    873 #define WidthChange()	(request->width != XtWidth(w))
    874 #define HeightChange()	(request->height != XtHeight(w))
    875 static XtGeometryResult
    876 QueryGeometry(ViewportWidget w, XtWidgetGeometry *request,
    877 	      XtWidgetGeometry *reply_return)
    878 {
    879     if (w->viewport.allowhoriz && w->viewport.allowvert)
    880 	return (TestSmaller(w, request, reply_return));
    881 
    882     else if (w->viewport.allowhoriz && !w->viewport.allowvert) {
    883 	if (WidthChange() && !HeightChange())
    884 	    return (TestSmaller(w, request, reply_return));
    885 	else if (!WidthChange() && HeightChange())
    886 	    return (XtMakeGeometryRequest((Widget)w, request, reply_return));
    887 	else if (WidthChange() && HeightChange())
    888 	    return (GeometryRequestPlusScrollbar(w, True, request, reply_return));
    889 	else /* !WidthChange() && !HeightChange() */
    890 	    return (XtGeometryYes);
    891     }
    892     else if (!w->viewport.allowhoriz && w->viewport.allowvert) {
    893 	if (!WidthChange() && HeightChange())
    894 	    return (TestSmaller(w, request, reply_return));
    895 	else if (WidthChange() && !HeightChange())
    896 	    return (XtMakeGeometryRequest((Widget)w, request, reply_return));
    897 	else if (WidthChange() && HeightChange())
    898 	    return (GeometryRequestPlusScrollbar(w, False, request, reply_return));
    899 	else /* !WidthChange() && !HeightChange() */
    900 	    return (XtGeometryYes);
    901     }
    902     else /* (!w->viewport.allowhoriz && !w->viewport.allowvert) */
    903 	return (XtMakeGeometryRequest((Widget)w, request, reply_return));
    904 }
    905 #undef WidthChange
    906 #undef HeightChange
    907 
    908 static XtGeometryResult
    909 XawViewportGeometryManager(Widget child, XtWidgetGeometry *request,
    910 			   XtWidgetGeometry *reply)
    911 {
    912     ViewportWidget w = (ViewportWidget)child->core.parent;
    913     Bool rWidth = (request->request_mode & CWWidth) != 0;
    914     Bool rHeight = (request->request_mode & CWHeight) != 0;
    915     XtWidgetGeometry allowed;
    916     XtGeometryResult result;
    917     Bool reconfigured;
    918     Bool child_changed_size;
    919     unsigned int height_remaining;
    920 
    921     if (request->request_mode & XtCWQueryOnly)
    922 	return (QueryGeometry(w, request, reply));
    923 
    924     if (child != w->viewport.child
    925 	|| request->request_mode & (XtGeometryMask)(~(CWWidth | CWHeight | CWBorderWidth))
    926 	|| ((request->request_mode & CWBorderWidth)
    927 	    && request->border_width > 0))
    928 	return (XtGeometryNo);
    929 
    930     allowed = *request;
    931 
    932     reconfigured = GetGeometry((Widget)w,
    933 				rWidth ? request->width : XtWidth(w),
    934 				rHeight ? request->height : XtHeight(w));
    935 
    936     child_changed_size = (rWidth && XtWidth(child) != request->width) ||
    937 			 (rHeight && XtHeight(child) != request->height);
    938 
    939     height_remaining = XtHeight(w);
    940     if (rWidth && XtWidth(w) != request->width) {
    941 	if (w->viewport.allowhoriz && request->width > XtWidth(w)) {
    942 	    /* horizontal scrollbar will be needed so possibly reduce height */
    943 	    Widget bar;
    944 
    945 	    if ((bar = w->viewport.horiz_bar) == NULL)
    946 		bar = CreateScrollbar(w, True);
    947 	    height_remaining = (height_remaining - (unsigned)(XtHeight(bar) + XtBorderWidth(bar)));
    948 	    reconfigured = True;
    949 	}
    950 	else
    951 	    allowed.width = XtWidth(w);
    952     }
    953     if (rHeight && height_remaining != request->height) {
    954 	if (w->viewport.allowvert && request->height > height_remaining) {
    955 	    /* vertical scrollbar will be needed, so possibly reduce width */
    956 	    if (!w->viewport.allowhoriz || request->width < XtWidth(w)) {
    957 		Widget bar;
    958 
    959 		if ((bar = w->viewport.vert_bar) == NULL)
    960 		    bar = CreateScrollbar(w, False);
    961 		if (!rWidth) {
    962 		    allowed.width = XtWidth(w);
    963 		    allowed.request_mode |= CWWidth;
    964 		}
    965 		if (allowed.width  >  XtWidth(bar) + XtBorderWidth(bar))
    966 		    allowed.width = (Dimension)(allowed.width - (XtWidth(bar) + XtBorderWidth(bar)));
    967 		else
    968 		    allowed.width = 1;
    969 		reconfigured = True;
    970 	    }
    971 	}
    972 	else
    973 	    allowed.height = (Dimension)height_remaining;
    974     }
    975 
    976     if (allowed.width != request->width || allowed.height != request->height) {
    977 	*reply = allowed;
    978 	result = XtGeometryAlmost;
    979     }
    980     else {
    981 	if (rWidth)
    982 	    XtWidth(child) = request->width;
    983 	if (rHeight)
    984 	    XtHeight(child) = request->height;
    985 	result = XtGeometryYes;
    986     }
    987 
    988     if (reconfigured || child_changed_size)
    989 	ComputeLayout((Widget)w, False, result == XtGeometryYes);
    990 
    991     return (result);
    992 }
    993 
    994 static Bool
    995 GetGeometry(Widget w, unsigned int width, unsigned int height)
    996 {
    997     XtWidgetGeometry geometry, return_geom;
    998     XtGeometryResult result;
    999 
   1000     if (width == XtWidth(w) && height == XtHeight(w))
   1001 	return (False);
   1002 
   1003     geometry.request_mode = CWWidth | CWHeight;
   1004     geometry.width = (Dimension)width;
   1005     geometry.height = (Dimension)height;
   1006 
   1007     if (XtIsRealized(w)) {
   1008 	if (((ViewportWidget)w)->viewport.allowhoriz && width > XtWidth(w))
   1009 	    geometry.width = XtWidth(w);
   1010 	if (((ViewportWidget)w)->viewport.allowvert && height > XtHeight(w))
   1011 	    geometry.height = XtHeight(w);
   1012     }
   1013     else {
   1014 	/* This is the Realize call; we'll inherit a w&h iff none currently */
   1015 	if (XtWidth(w) != 0) {
   1016 	    if (XtHeight(w) != 0)
   1017 		return (False);
   1018 	    geometry.width = XtWidth(w);
   1019 	}
   1020 	if (XtHeight(w) != 0)
   1021 	    geometry.height = XtHeight(w);
   1022     }
   1023 
   1024     result = XtMakeGeometryRequest(w, &geometry, &return_geom);
   1025     if (result == XtGeometryAlmost)
   1026 	result = XtMakeGeometryRequest(w, &return_geom, NULL);
   1027 
   1028     return (result == XtGeometryYes);
   1029 }
   1030 
   1031 static XtGeometryResult
   1032 XawViewportQueryGeometry(Widget w, XtWidgetGeometry *constraints,
   1033 			 XtWidgetGeometry *reply)
   1034 {
   1035     if (((ViewportWidget)w)->viewport.child != NULL)
   1036 	return (XtQueryGeometry(((ViewportWidget)w)->viewport.child,
   1037 				constraints, reply));
   1038 
   1039     return (XtGeometryYes);
   1040 }
   1041 
   1042 void
   1043 XawViewportSetLocation
   1044 (
   1045  Widget gw,
   1046 #if NeedWidePrototypes
   1047  double xoff, double yoff
   1048 #else
   1049  float xoff, float yoff
   1050 #endif
   1051  )
   1052 {
   1053     ViewportWidget w = (ViewportWidget)gw;
   1054     Widget child = w->viewport.child;
   1055     int x, y;
   1056 
   1057     if (xoff > 1.0)			/* scroll to right */
   1058 	x = XtWidth(child);
   1059     else if (xoff < 0.0)		/* if the offset is < 0.0 nothing */
   1060 	x = XtX(child);
   1061     else
   1062 	x = (int)((float)XtWidth(child) * xoff);
   1063 
   1064     if (yoff > 1.0)
   1065 	y = XtHeight(child);
   1066     else if (yoff < 0.0)
   1067 	y = XtY(child);
   1068     else
   1069 	y = (int)((float)XtHeight(child) * yoff);
   1070 
   1071     MoveChild (w, -x, -y);
   1072 }
   1073 
   1074 void
   1075 XawViewportSetCoordinates(Widget gw,
   1076 #if NeedWidePrototypes
   1077 	int x, int y
   1078 #else
   1079 	Position x, Position y
   1080 #endif
   1081 )
   1082 {
   1083     ViewportWidget w = (ViewportWidget)gw;
   1084     Widget child = w->viewport.child;
   1085 
   1086     if (x > XtWidth(child))
   1087 	x = (Position)XtWidth(child);
   1088     else if (x < 0)
   1089 	x = (Position)XtX(child);
   1090 
   1091     if (y > XtHeight(child))
   1092 	y = (Position)XtHeight(child);
   1093     else if (y < 0)
   1094 	y = (Position)XtY(child);
   1095 
   1096     MoveChild (w, -x, -y);
   1097 }
   1098