1 /*
2  * Copyright 1989 O'Reilly and Associates, Inc.
3 
4      The X Consortium, and any party obtaining a copy of these files from
5      the X Consortium, directly or indirectly, is granted, free of charge, a
6      full and unrestricted irrevocable, world-wide, paid up, royalty-free,
7      nonexclusive right and license to deal in this software and
8      documentation files (the "Software"), including without limitation the
9      rights to use, copy, modify, merge, publish, distribute, sublicense,
10      and/or sell copies of the Software, and to permit persons who receive
11      copies from any such party to do so.  This license includes without
12      limitation a license to do the foregoing actions under any patents of
13      the party supplying this software to the X Consortium.
14  */
15 
16 /* ScrollBox.c - scrollBox composite widget */
17 
18 #include <X11/IntrinsicP.h>
19 #include <X11/StringDefs.h>
20 #include <X11/Shell.h>
21 
22 #include "x11/ScrollBoxP.h"
23 
24 #include <stdio.h>
25 
26 #define INITIAL_WIDTH 300
27 #define INITIAL_HEIGHT 300
28 
29 /************************************************************************
30  *                                                                      *
31  * scrollBox Resources                                                  *
32  *                                                                      *
33  ************************************************************************/
34 
35 static XtResource resources[] =
36 {
37     { XtNhSpace, XtCHSpace, XtRDimension, sizeof(Dimension),
38         XtOffset(ScrollBoxWidget, scrollBox.h_space),
39         XtRImmediate, (XtPointer)4 },
40     { XtNvSpace, XtCVSpace, XtRDimension, sizeof(Dimension),
41         XtOffset(ScrollBoxWidget, scrollBox.v_space),
42         XtRImmediate, (XtPointer)4 },
43     { XtNheightInc, XtCHeightInc, XtRDimension, sizeof(Dimension),
44         XtOffset(ScrollBoxWidget, scrollBox.increment_height),
45         XtRImmediate, (XtPointer)13 },
46     { XtNwidthInc, XtCWidthInc, XtRDimension, sizeof(Dimension),
47         XtOffset(ScrollBoxWidget, scrollBox.increment_width),
48         XtRImmediate, (XtPointer)7 },
49 };
50 
51 /************************************************************************
52  *                                                                      *
53  * Full class record constant                                           *
54  *                                                                      *
55  ************************************************************************/
56 
57 static void Initialize(Widget, Widget, ArgList, Cardinal *);
58 static void Resize(Widget);
59 static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);
60 static void ChangeManaged(Widget);
61 static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry *,
62                                       XtWidgetGeometry *);
63 static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry *,
64                                         XtWidgetGeometry *);
65 static void RefigureLocations(Widget);
66 
67 ScrollBoxClassRec scrollBoxClassRec = {
68   {
69     /* core_class fields        */
70         /* superclass           */  (WidgetClass) &compositeClassRec,
71         /* class_name           */  "scrollBox",
72         /* widget_size          */  sizeof(ScrollBoxRec),
73         /* class_initialize     */  NULL,
74         /* class_part_init      */  NULL,
75         /* class_inited         */  FALSE,
76         /* initialize           */  Initialize,
77         /* initialize_hook      */  NULL,
78         /* realize              */  XtInheritRealize,
79         /* actions              */  NULL,
80         /* num_actions          */  0,
81         /* resources            */  resources,
82         /* num_resources        */  XtNumber(resources),
83         /* xrm_class            */  NULLQUARK,
84         /* compress_motion      */  TRUE,
85         /* compress_exposure    */  TRUE,
86         /* compress_enterleave  */  TRUE,
87         /* visible_interest     */  FALSE,
88         /* destroy              */  NULL,
89         /* resize               */  Resize,
90         /* expose               */  NULL,
91         /* set_values           */  SetValues,
92         /* set_values_hook      */  NULL,
93         /* set_values_almost    */  XtInheritSetValuesAlmost,
94         /* get_values_hook      */  NULL,
95         /* accept_focus         */  NULL,
96         /* version              */  XtVersion,
97         /* callback_private     */  NULL,
98         /* tm_table             */  NULL,
99         /* query_geometry       */  QueryGeometry,
100         /* display_accelerator  */  XtInheritDisplayAccelerator,
101         /* extension            */  NULL
102   },{
103     /* composite_class fields   */
104         /* geometry_manager     */  GeometryManager,
105         /* change_managed       */  ChangeManaged,
106         /* insert_child         */  XtInheritInsertChild,
107         /* delete_child         */  XtInheritDeleteChild,
108         /* extension            */  NULL
109   },{
110     /* scrollBox class fields   */
111         /* empty                */  0,
112   }
113 };
114 
115 WidgetClass scrollBoxWidgetClass = (WidgetClass)&scrollBoxClassRec;
116 
117 
118 /************************************************************************
119  *                                                                      *
120  * Private Routines                                                     *
121  *                                                                      *
122  ************************************************************************/
123 
124 /* Do a layout, either actually assigning positions, or just
125    calculating size. */
126 
DoLayout(Widget w,Boolean doit)127 static void DoLayout(Widget w, Boolean doit)
128 {
129     ScrollBoxWidget sbw = (ScrollBoxWidget)w;
130     Widget wmain, vscroll, hscroll, child;
131     Dimension mw, mh;   /* main window */
132     Dimension vh;   /* vertical scrollbar length (height) */
133     Dimension hw;   /* horizontal scrollbar length (width) */
134     Position vx;
135     Position hy;
136     Cardinal i;
137 
138     if (sbw->composite.num_children != 3)
139         XtAppError(XtWidgetToApplicationContext(w),
140             "ScrollBox: must manage exactly three widgets.");
141 
142     for (i = 0; i < sbw->composite.num_children; i++)
143     {
144         child = sbw->composite.children[i];
145 
146         if (!XtIsManaged(child))
147             XtAppError(XtWidgetToApplicationContext(w),
148                 "ScrollBox: all three widgets must be managed.");
149     }
150 
151     /* Child one is the main window, two is the vertical scrollbar,
152        and three is the horizontal scrollbar. */
153 
154     wmain = sbw->composite.children[0];
155     vscroll = sbw->composite.children[1];
156     hscroll = sbw->composite.children[2];
157 
158     /* Size all three widgets so that space is fully utilized. */
159 
160     mw = sbw->core.width - (2 * sbw->scrollBox.h_space) -
161         vscroll->core.width - (2 * vscroll->core.border_width) -
162         (2 * wmain->core.border_width);
163 
164     mh = sbw->core.height - (2 * sbw->scrollBox.v_space) -
165         hscroll->core.height - (2 * hscroll->core.border_width) -
166         (2 * wmain->core.border_width);
167 
168     /* Force the main window to be sized to the appropriate increment. */
169 
170     mw = (mw / sbw->scrollBox.increment_width) *
171         sbw->scrollBox.increment_width;
172 
173     mh = ((mh / sbw->scrollBox.increment_height) *
174         sbw->scrollBox.increment_height) +
175         sbw->scrollBox.increment_height;
176 
177     vx = wmain->core.x + mw + sbw->scrollBox.h_space +
178         wmain->core.border_width + vscroll->core.border_width;
179 
180     hy = wmain->core.y + mh + sbw->scrollBox.v_space +
181         wmain->core.border_width + hscroll->core.border_width;
182 
183     vh = mh;   /* scrollbars are always same length as main window */
184     hw = mw;
185 
186     if (doit)
187     {
188         XtResizeWidget(wmain, mw, mh, 1);
189 
190         XtResizeWidget(vscroll, vscroll->core.width, vh, 1);
191         XtMoveWidget(vscroll, vx, vscroll->core.y);
192 
193         XtResizeWidget(hscroll, hw, hscroll->core.height, 1);
194         XtMoveWidget(hscroll, hscroll->core.x, hy);
195     }
196 }
197 
GeometryManager(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply)198 static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request,
199                                         XtWidgetGeometry *reply)
200 {
201     XtWidgetGeometry allowed;
202 
203     if (request->request_mode & ~(XtCWQueryOnly | CWWidth | CWHeight))
204         return XtGeometryNo;
205 
206     if (request->request_mode & CWWidth)
207         allowed.width = request->width;
208     else
209         allowed.width = w->core.width;
210 
211     if (request->request_mode & CWHeight)
212         allowed.height = request->height;
213     else
214         allowed.height = w->core.height;
215 
216     if (allowed.width == w->core.width && allowed.height == w->core.height)
217         return XtGeometryNo;
218 
219     if (!(request->request_mode & XtCWQueryOnly))
220         RefigureLocations(w);
221 
222     return XtGeometryYes;
223 }
224 
RefigureLocations(Widget w)225 static void RefigureLocations(Widget w)
226 {
227     DoLayout(w, False);
228 }
229 
230 /* Calculate preferred size.  We can't just use the current sizes
231    of the children, because that calculation would always end up with
232    our current size.  Could query each child, and use that size to
233    recalculate a size for us, then if it ends up being larger than width
234    and height passed in, accept bounding box. However, we know our
235    children and they don't have any particular preferred geometry,
236    except the bigger the better. Therefore, if the parent suggested a
237    size, we'll take it. */
238 
QueryGeometry(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply_return)239 static XtGeometryResult QueryGeometry(Widget w, XtWidgetGeometry *request,
240                                       XtWidgetGeometry *reply_return)
241 {
242     XtGeometryResult result=XtGeometryNo;
243 
244     request->request_mode &= CWWidth | CWHeight;
245 
246     /* parent isn't going to change w or h, so nothing to re-compute */
247 
248     if (request->request_mode == 0)
249         return XtGeometryYes;
250 
251     /* if proposed size is large enough, accept it.  Otherwise, suggest
252        our arbitrary initial size. */
253 
254     if (request->request_mode & CWHeight)
255     {
256         if (request->height < INITIAL_HEIGHT)
257         {
258             result = XtGeometryAlmost;
259             reply_return->height = INITIAL_HEIGHT;
260             reply_return->request_mode &= CWHeight;
261         }
262         else
263             result = XtGeometryYes;
264     }
265 
266     if (request->request_mode & CWWidth)
267     {
268         if (request->width < INITIAL_WIDTH)
269         {
270             result = XtGeometryAlmost;
271             reply_return->width = INITIAL_WIDTH;
272             reply_return->request_mode &= CWWidth;
273         }
274         else
275             result = XtGeometryYes;
276     }
277 
278     return result;
279 }
280 
281 /* Actually layout the scrollBox  */
282 
Resize(Widget w)283 static void Resize(Widget w)
284 {
285     DoLayout(w, True);
286 }
287 
ChangeManaged(Widget w)288 static void ChangeManaged(Widget w)
289 {
290     DoLayout(w, True);
291 }
292 
Initialize(Widget request,Widget new,ArgList args,Cardinal * num_args)293 static void Initialize(Widget request, Widget new,
294                        ArgList args, Cardinal *num_args)
295 {
296     ScrollBoxWidget newsbw = (ScrollBoxWidget)new;
297 
298     if (newsbw->core.width == 0)
299         newsbw->core.width = INITIAL_WIDTH;
300 
301     if (newsbw->core.height == 0)
302         newsbw->core.height = INITIAL_HEIGHT;
303 
304 }
305 
SetValues(Widget current,Widget request,Widget new,ArgList args,Cardinal * num_args)306 static Boolean SetValues(Widget current, Widget request, Widget new,
307                          ArgList args, Cardinal *num_args)
308 {
309     ScrollBoxWidget sbwcurrent = (ScrollBoxWidget)current;
310     ScrollBoxWidget sbwnew = (ScrollBoxWidget)new;
311 
312     /* need to relayout if h_space or v_space change */
313 
314     if ((sbwnew->scrollBox.h_space != sbwcurrent->scrollBox.h_space) ||
315         (sbwnew->scrollBox.v_space != sbwcurrent->scrollBox.v_space))
316         DoLayout(new, True);
317 
318     return False;
319 }
320