1/***********************************************************
2
3Copyright 1987, 1988, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
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 */
62static Boolean Layout(FormWidget, unsigned int, unsigned int, Bool);
63static void XawViewportChangeManaged(Widget);
64static void XawViewportInitialize(Widget, Widget, ArgList, Cardinal*);
65static void
66XawViewportConstraintInitialize(Widget, Widget, ArgList, Cardinal*);
67static XtGeometryResult XawViewportGeometryManager(Widget, XtWidgetGeometry*,
68						   XtWidgetGeometry*);
69static XtGeometryResult XawViewportQueryGeometry(Widget,
70						 XtWidgetGeometry*,
71						 XtWidgetGeometry*);
72static void XawViewportRealize(Widget, XtValueMask*, XSetWindowAttributes*);
73static void XawViewportResize(Widget);
74static Boolean XawViewportSetValues(Widget, Widget, Widget,
75				    ArgList, Cardinal*);
76
77/*
78 * Prototypes
79 */
80static void ComputeLayout(Widget, Bool, Bool);
81static void ComputeWithForceBars(Widget, Bool, XtWidgetGeometry*,
82				 int*, int*);
83static Widget CreateScrollbar(ViewportWidget, Bool);
84static XtGeometryResult GeometryRequestPlusScrollbar(ViewportWidget, Bool,
85						     XtWidgetGeometry*,
86						     XtWidgetGeometry*);
87static Bool GetGeometry(Widget, unsigned int, unsigned int);
88static void MoveChild(ViewportWidget, int, int);
89static XtGeometryResult QueryGeometry(ViewportWidget, XtWidgetGeometry*,
90				      XtWidgetGeometry*);
91static void RedrawThumbs(ViewportWidget);
92static void ScrollUpDownProc(Widget, XtPointer, XtPointer);
93static void SendReport(ViewportWidget, unsigned int);
94static void SetBar(Widget, int, unsigned int, unsigned int);
95static XtGeometryResult TestSmaller(ViewportWidget, XtWidgetGeometry*,
96				    XtWidgetGeometry*);
97static void ThumbProc(Widget, XtPointer, XtPointer);
98
99/*
100 * Initialization
101 */
102#define offset(field) XtOffsetOf(ViewportRec, viewport.field)
103static 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)
162ViewportClassRec 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
229WidgetClass viewportWidgetClass = (WidgetClass)&viewportClassRec;
230
231/*
232 * Implementation
233 */
234static Widget
235CreateScrollbar(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*/
285static void
286XawViewportInitialize(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*/
349static void
350XawViewportConstraintInitialize(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
356static void
357XawViewportRealize(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*/
380static Boolean
381XawViewportSetValues(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
397static void
398XawViewportChangeManaged(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
449static void
450SetBar(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
456static void
457RedrawThumbs(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
471static void
472SendReport(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
491static void
492MoveChild(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
516static void
517ComputeLayout(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 */
716static void
717ComputeWithForceBars(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
788static void
789XawViewportResize(Widget widget)
790{
791    ComputeLayout(widget, True, True);
792}
793
794/*ARGSUSED*/
795static Boolean
796Layout(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
805static void
806ScrollUpDownProc(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*/
822static void
823ThumbProc(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
846static XtGeometryResult
847TestSmaller(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
856static XtGeometryResult
857GeometryRequestPlusScrollbar(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))
875static XtGeometryResult
876QueryGeometry(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
908static XtGeometryResult
909XawViewportGeometryManager(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
994static Bool
995GetGeometry(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
1031static XtGeometryResult
1032XawViewportQueryGeometry(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
1042void
1043XawViewportSetLocation
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
1074void
1075XawViewportSetCoordinates(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