2 static char Xrcsid
[] = "$XConsortium: Box.c,v 1.43 89/12/07 20:14:26 kit Exp $";
6 /***********************************************************
7 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
8 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
12 Permission to use, copy, modify, and distribute this software and its
13 documentation for any purpose and without fee is hereby granted,
14 provided that the above copyright notice appear in all copies and that
15 both that copyright notice and this permission notice appear in
16 supporting documentation, and that the names of Digital or MIT not be
17 used in advertising or publicity pertaining to distribution of the
18 software without specific, written prior permission.
20 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 ******************************************************************/
32 * Box.c - Box composite widget
36 #include <X11/IntrinsicP.h>
37 #include <X11/StringDefs.h>
38 #include <X11/Xmu/Misc.h>
39 #include <./Xaw3_1XawInit.h>
40 #include <./Xaw3_1BoxP.h>
42 /****************************************************************
46 ****************************************************************/
48 static XtResource resources
[] = {
49 { XtNhSpace
, XtCHSpace
, XtRDimension
, sizeof(Dimension
),
50 XtOffsetOf(BoxRec
, box
.h_space
),
51 XtRImmediate
, (XtPointer
)4 },
52 { XtNvSpace
, XtCVSpace
, XtRDimension
, sizeof(Dimension
),
53 XtOffsetOf(BoxRec
, box
.v_space
),
54 XtRImmediate
, (XtPointer
)4 },
55 { XtNorientation
, XtCOrientation
, XtROrientation
, sizeof(XtOrientation
),
56 XtOffsetOf(BoxRec
, box
.orientation
),
57 XtRImmediate
, (XtPointer
)XtorientVertical
},
60 /****************************************************************
62 * Full class record constant
64 ****************************************************************/
66 static void ClassInitialize();
67 static void Initialize();
68 static void Realize();
70 static Boolean
SetValues();
71 static XtGeometryResult
GeometryManager();
72 static void ChangeManaged();
73 static XtGeometryResult
PreferredSize();
75 BoxClassRec boxClassRec
= {
77 /* core_class fields */
78 /* superclass */ (WidgetClass
) &compositeClassRec
,
79 /* class_name */ "Box",
80 /* widget_size */ sizeof(BoxRec
),
81 /* class_initialize */ ClassInitialize
,
82 /* class_part_init */ NULL
,
83 /* class_inited */ FALSE
,
84 /* initialize */ Initialize
,
85 /* initialize_hook */ NULL
,
86 /* realize */ Realize
,
89 /* resources */ resources
,
90 /* num_resources */ XtNumber(resources
),
91 /* xrm_class */ NULLQUARK
,
92 /* compress_motion */ TRUE
,
93 /* compress_exposure */ TRUE
,
94 /* compress_enterleave*/ TRUE
,
95 /* visible_interest */ FALSE
,
99 /* set_values */ SetValues
,
100 /* set_values_hook */ NULL
,
101 /* set_values_almost */ XtInheritSetValuesAlmost
,
102 /* get_values_hook */ NULL
,
103 /* accept_focus */ NULL
,
104 /* version */ XtVersion
,
105 /* callback_private */ NULL
,
107 /* query_geometry */ PreferredSize
,
108 /* display_accelerator*/ XtInheritDisplayAccelerator
,
111 /* composite_class fields */
112 /* geometry_manager */ GeometryManager
,
113 /* change_managed */ ChangeManaged
,
114 /* insert_child */ XtInheritInsertChild
,
115 /* delete_child */ XtInheritDeleteChild
,
118 /* Box class fields */
123 WidgetClass boxWidgetClass
= (WidgetClass
)&boxClassRec
;
126 /****************************************************************
130 ****************************************************************/
134 * Do a layout, either actually assigning positions, or just calculating size.
135 * Returns minimum width and height that will preserve the same layout.
139 static DoLayout(bbw
, width
, height
, reply_width
, reply_height
, position
)
141 Dimension width
, height
;
142 Dimension
*reply_width
, *reply_height
; /* bounding box */
143 Boolean position
; /* actually reposition the windows? */
145 Boolean vbox
= (bbw
->box
.orientation
== XtorientVertical
);
147 Dimension w
, h
; /* Width and height needed for box */
148 Dimension lw
, lh
; /* Width and height needed for current line */
149 Dimension bw
, bh
; /* Width and height needed for current widget */
150 Dimension h_space
; /* Local copy of bbw->box.h_space */
151 register Widget widget
; /* Current widget */
152 int num_mapped_children
= 0;
154 /* Box width and height */
155 h_space
= bbw
->box
.h_space
;
157 h
= bbw
->box
.v_space
;
159 /* Line width and height */
163 for (i
= 0; i
< bbw
->composite
.num_children
; i
++) {
164 widget
= bbw
->composite
.children
[i
];
165 if (widget
->core
.managed
) {
166 if (widget
->core
.mapped_when_managed
) num_mapped_children
++;
167 /* Compute widget width */
168 bw
= widget
->core
.width
+ 2*widget
->core
.border_width
+ h_space
;
169 if (lw
+ bw
> width
) {
171 /* At least one widget on this line, and
172 * can't fit any more. Start new line if vbox.
176 h
+= lh
+ bbw
->box
.v_space
;
181 else if (!position
) {
182 /* too narrow for this widget; we'll assume we can grow */
183 DoLayout(bbw
, lw
+ bw
, height
, reply_width
,
184 reply_height
, position
);
188 if (position
&& (lw
!= widget
->core
.x
|| h
!= widget
->core
.y
)) {
189 /* It would be nice to use window gravity, but there isn't
190 * sufficient fine-grain control to nicely handle all
191 * situations (e.g. when only the height changes --
192 * a common case). Explicit unmapping is a cheap hack
193 * to speed things up & avoid the visual jitter as
194 * things slide around.
196 * %%% perhaps there should be a client resource to
197 * control this. If so, we'll have to optimize to
198 * perform the moves from the correct end so we don't
199 * force extra exposures as children occlude each other.
201 if (XtIsRealized(widget
))
202 XUnmapWindow( XtDisplay(widget
), XtWindow(widget
) );
203 XtMoveWidget(bbw
->composite
.children
[i
], (int)lw
, (int)h
);
206 bh
= widget
->core
.height
+ 2*widget
->core
.border_width
;
211 if (!vbox
&& width
&& lw
> width
&& lh
< height
) {
212 /* reduce width if too wide and height not filled */
213 Dimension sw
= lw
, sh
= lh
;
214 Dimension width_needed
;
215 XtOrientation orientation
= bbw
->box
.orientation
;
216 bbw
->box
.orientation
= XtorientVertical
;
217 while (sh
< height
&& sw
> width
) {
219 DoLayout(bbw
, sw
-1, height
, &sw
, &sh
, False
);
221 if (sh
< height
) width_needed
= sw
;
222 if (width_needed
!= lw
) {
223 DoLayout(bbw
,width_needed
,height
,reply_width
,reply_height
,position
);
224 bbw
->box
.orientation
= orientation
;
227 bbw
->box
.orientation
= orientation
;
230 if (position
&& XtIsRealized((Widget
)bbw
)) {
231 if (bbw
->composite
.num_children
== num_mapped_children
)
232 XMapSubwindows( XtDisplay((Widget
)bbw
), XtWindow((Widget
)bbw
) );
234 int i
= num_mapped_children
;
235 register Widget
*childP
= bbw
->composite
.children
;
236 for (; i
> 0; childP
++) {
237 if (XtIsManaged(*childP
) &&
238 (*childP
)->core
.mapped_when_managed
) {
239 XtMapWidget(*childP
);
246 /* Finish off last line */
249 h
+= lh
+ bbw
->box
.v_space
;
252 *reply_width
= Max(w
, 1);
253 *reply_height
= Max(h
, 1);
258 * Calculate preferred size, given constraining box, caching it in the widget.
262 static XtGeometryResult
PreferredSize(widget
, constraint
, preferred
)
264 XtWidgetGeometry
*constraint
, *preferred
;
266 BoxWidget w
= (BoxWidget
)widget
;
267 Dimension width
/*, height */;
268 Dimension preferred_width
= w
->box
.preferred_width
;
269 Dimension preferred_height
= w
->box
.preferred_height
;
271 constraint
->request_mode
&= CWWidth
| CWHeight
;
273 if (constraint
->request_mode
== 0)
274 /* parent isn't going to change w or h, so nothing to re-compute */
275 return XtGeometryYes
;
277 if (constraint
->request_mode
== w
->box
.last_query_mode
&&
278 (!(constraint
->request_mode
& CWWidth
) ||
279 constraint
->width
== w
->box
.last_query_width
) &&
280 (!(constraint
->request_mode
& CWHeight
) ||
281 constraint
->height
== w
->box
.last_query_height
)) {
282 /* same query; current preferences are still valid */
283 preferred
->request_mode
= CWWidth
| CWHeight
;
284 preferred
->width
= preferred_width
;
285 preferred
->height
= preferred_height
;
286 if (constraint
->request_mode
== (CWWidth
| CWHeight
) &&
287 constraint
->width
== preferred_width
&&
288 constraint
->height
== preferred_height
)
289 return XtGeometryYes
;
291 return XtGeometryAlmost
;
294 /* else gotta do it the long way...
295 I have a preference for tall and narrow, so if my width is
296 constrained, I'll accept it; otherwise, I'll compute the minimum
297 width that will fit me within the height constraint */
299 w
->box
.last_query_mode
= constraint
->request_mode
;
300 w
->box
.last_query_width
= constraint
->width
;
301 w
->box
.last_query_height
= constraint
->height
;
303 if (constraint
->request_mode
& CWWidth
)
304 width
= constraint
->width
;
305 else /* if (constraint->request_mode & CWHeight) */ {
306 /* let's see if I can become any narrower */
308 constraint
->width
= 65535;
311 /* height is currently ignored by DoLayout.
312 height = (constraint->request_mode & CWHeight) ? constraint->height
315 DoLayout(w
, width
, (Dimension
)0,
316 &preferred_width
, &preferred_height
, FALSE
);
318 if (constraint
->request_mode
& CWHeight
&&
319 preferred_height
> constraint
->height
) {
320 /* find minimum width for this height */
321 if (preferred_width
> constraint
->width
) {
322 /* punt; over-constrained */
325 width
= preferred_width
;
326 do { /* find some width big enough to stay within this height */
328 if (width
> constraint
->width
) width
= constraint
->width
;
329 DoLayout(w
, width
, 0, &preferred_width
, &preferred_height
, FALSE
);
330 } while (preferred_height
> constraint
->height
&&
331 width
< constraint
->width
);
332 if (width
!= constraint
->width
) {
333 do { /* find minimum width */
334 width
= preferred_width
;
335 DoLayout(w
, preferred_width
-1, 0,
336 &preferred_width
, &preferred_height
, FALSE
);
337 } while (preferred_height
< constraint
->height
);
339 DoLayout(w
, width
, 0, &preferred_width
, &preferred_height
, FALSE
);
344 preferred
->request_mode
= CWWidth
| CWHeight
;
345 preferred
->width
= w
->box
.preferred_width
= preferred_width
;
346 preferred
->height
= w
->box
.preferred_height
= preferred_height
;
348 if (constraint
->request_mode
== (CWWidth
|CWHeight
)
349 && constraint
->width
== preferred_width
350 && constraint
->height
== preferred_height
)
351 return XtGeometryYes
;
353 return XtGeometryAlmost
;
359 * Actually layout the box
363 static void Resize(w
)
368 DoLayout((BoxWidget
)w
, w
->core
.width
, w
->core
.height
, &junk
, &junk
, TRUE
);
374 * Try to do a new layout within the current width and height;
375 * if that fails try to resize and do it within the box returne
378 * TryNewLayout just says if it's possible, and doesn't actually move the kids
381 static Boolean
TryNewLayout(bbw
)
384 Dimension preferred_width
, preferred_height
;
385 Dimension proposed_width
, proposed_height
;
388 DoLayout( bbw
, bbw
->core
.width
, bbw
->core
.height
,
389 &preferred_width
, &preferred_height
, FALSE
);
391 /* at this point, preferred_width is guaranteed to not be greater
392 than bbw->core.width unless some child is larger, so there's no
393 point in re-computing another layout */
395 if ((bbw
->core
.width
== preferred_width
) &&
396 (bbw
->core
.height
== preferred_height
)) {
401 /* let's see if our parent will go for a new size. */
403 proposed_width
= preferred_width
;
404 proposed_height
= preferred_height
;
406 switch (XtMakeResizeRequest((Widget
)bbw
,proposed_width
,proposed_height
,
407 &proposed_width
, &proposed_height
))
414 /* protect from malicious parents who change their minds */
415 DoLayout( bbw
, bbw
->core
.width
, bbw
->core
.height
,
416 &preferred_width
, &preferred_height
, FALSE
);
417 if ((preferred_width
<= bbw
->core
.width
) &&
418 (preferred_height
<= bbw
->core
.height
))
423 case XtGeometryAlmost
:
424 if (proposed_height
>= preferred_height
&&
425 proposed_width
>= preferred_width
) {
428 * Take it, and assume the parent knows what it is doing.
430 * The parent must accept this since it was returned in
434 (void) XtMakeResizeRequest( (Widget
)bbw
,
435 proposed_width
, proposed_height
,
436 &proposed_width
, &proposed_height
);
439 else if (proposed_width
!= preferred_width
) {
440 /* recalc bounding box; height might change */
441 DoLayout(bbw
, proposed_width
, 0,
442 &preferred_width
, &preferred_height
, FALSE
);
443 proposed_height
= preferred_height
;
445 else { /* proposed_height != preferred_height */
446 XtWidgetGeometry constraints
, reply
;
447 constraints
.request_mode
= CWHeight
;
448 constraints
.height
= proposed_height
;
449 (void)PreferredSize((Widget
)bbw
, &constraints
, &reply
);
450 proposed_width
= preferred_width
;
454 } while (iterations
< 10);
462 * 'reply' is unused; we say only yeay or nay, never almost.
467 static XtGeometryResult
GeometryManager(w
, request
, reply
)
469 XtWidgetGeometry
*request
;
470 XtWidgetGeometry
*reply
; /* RETURN */
473 Dimension width
, height
, borderWidth
;
476 /* Position request always denied */
477 if ((request
->request_mode
& CWX
&& request
->x
!= w
->core
.x
) ||
478 (request
->request_mode
& CWY
&& request
->y
!= w
->core
.y
))
479 return (XtGeometryNo
);
481 /* Size changes must see if the new size can be accomodated */
482 if (request
->request_mode
& (CWWidth
| CWHeight
| CWBorderWidth
)) {
484 /* Make all three fields in the request valid */
485 if ((request
->request_mode
& CWWidth
) == 0)
486 request
->width
= w
->core
.width
;
487 if ((request
->request_mode
& CWHeight
) == 0)
488 request
->height
= w
->core
.height
;
489 if ((request
->request_mode
& CWBorderWidth
) == 0)
490 request
->border_width
= w
->core
.border_width
;
492 /* Save current size and set to new size */
493 width
= w
->core
.width
;
494 height
= w
->core
.height
;
495 borderWidth
= w
->core
.border_width
;
496 w
->core
.width
= request
->width
;
497 w
->core
.height
= request
->height
;
498 w
->core
.border_width
= request
->border_width
;
500 /* Decide if new layout works: (1) new widget is smaller,
501 (2) new widget fits in existing Box, (3) Box can be
502 expanded to allow new widget to fit */
504 bbw
= (BoxWidget
) w
->core
.parent
;
506 /* whenever a child changes his geometry, we attempt to
507 * change ours to be the minimum enclosing size...
508 if (((request->width + request->border_width <= width + borderWidth) &&
509 (request->height + request->border_width <= height + borderWidth))
510 || bbw->box.preferred_width < bbw->core.width
511 || bbw->box.preferred_height < bbw->core.height
512 || TryNewLayout(bbw)) {
514 if (TryNewLayout(bbw
)) {
515 /* Fits in existing or new space, relayout */
516 (*XtClass((Widget
)bbw
)->core_class
.resize
)((Widget
)bbw
);
517 return (XtGeometryYes
);
519 /* Cannot satisfy request, change back to original geometry */
520 w
->core
.width
= width
;
521 w
->core
.height
= height
;
522 w
->core
.border_width
= borderWidth
;
523 return (XtGeometryNo
);
525 }; /* if any size changes requested */
527 /* Any stacking changes don't make a difference, so allow if that's all */
528 return (XtGeometryYes
);
531 static void ChangeManaged(w
)
534 /* Reconfigure the box */
535 (void) TryNewLayout((BoxWidget
)w
);
539 static void ClassInitialize()
541 XawInitializeWidgetSet();
542 XtAddConverter( XtRString
, XtROrientation
, XmuCvtStringToOrientation
,
547 static void Initialize(request
, new)
550 BoxWidget newbbw
= (BoxWidget
)new;
552 newbbw
->box
.last_query_mode
= CWWidth
| CWHeight
;
553 newbbw
->box
.last_query_width
= newbbw
->box
.last_query_height
= 0;
554 newbbw
->box
.preferred_width
= Max(newbbw
->box
.h_space
, 1);
555 newbbw
->box
.preferred_height
= Max(newbbw
->box
.v_space
, 1);
557 if (newbbw
->core
.width
== 0)
558 newbbw
->core
.width
= newbbw
->box
.preferred_width
;
560 if (newbbw
->core
.height
== 0)
561 newbbw
->core
.height
= newbbw
->box
.preferred_height
;
565 static void Realize(w
, valueMask
, attributes
)
568 XSetWindowAttributes
*attributes
;
570 attributes
->bit_gravity
= NorthWestGravity
;
571 *valueMask
|= CWBitGravity
;
573 XtCreateWindow( w
, (unsigned)InputOutput
, (Visual
*)CopyFromParent
,
574 *valueMask
, attributes
);
578 static Boolean
SetValues(current
, request
, new)
579 Widget current
, request
, new;
581 /* need to relayout if h_space or v_space change */