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