1 /***********************************************************
3 Copyright 1987, 1988, 1994, 1998 The Open Group
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
26 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 ******************************************************************/
51 #include <X11/IntrinsicP.h>
52 #include <X11/StringDefs.h>
53 #include <X11/Xmu/Misc.h>
54 #include <X11/Xaw/BoxP.h>
55 #include <X11/Xaw/XawInit.h>
61 static void XawBoxChangeManaged(Widget);
62 static void XawBoxClassInitialize(void);
64 static void XawBoxExpose(Widget, XEvent*, Region);
66 static XtGeometryResult XawBoxGeometryManager(Widget, XtWidgetGeometry*,
68 static void XawBoxInitialize(Widget, Widget, ArgList, Cardinal*);
69 static XtGeometryResult XawBoxQueryGeometry(Widget, XtWidgetGeometry*,
71 static void XawBoxRealize(Widget, Mask*, XSetWindowAttributes*);
72 static void XawBoxResize(Widget);
73 static Boolean XawBoxSetValues(Widget, Widget, Widget,
79 static void DoLayout(BoxWidget, unsigned int, unsigned int,
80 Dimension*, Dimension*, Bool);
81 static Bool TryNewLayout(BoxWidget);
87 static XtActionsRec actions[] = {
88 {"set-values", XawSetValuesAction},
89 {"get-values", XawGetValuesAction},
90 {"declare", XawDeclareAction},
91 {"call-proc", XawCallProcAction},
95 static XtResource resources[] = {
101 XtOffsetOf(BoxRec, box.h_space),
110 XtOffsetOf(BoxRec, box.v_space),
118 sizeof(XtOrientation),
119 XtOffsetOf(BoxRec, box.orientation),
121 (XtPointer)XtorientVertical
128 sizeof(XawDisplayList*),
129 XtOffsetOf(BoxRec, box.display_list),
136 BoxClassRec boxClassRec = {
139 (WidgetClass)&compositeClassRec, /* superclass */
140 "Box", /* class_name */
141 sizeof(BoxRec), /* widget_size */
142 XawBoxClassInitialize, /* class_initialize */
143 NULL, /* class_part_init */
144 False, /* class_inited */
145 XawBoxInitialize, /* initialize */
146 NULL, /* initialize_hook */
147 XawBoxRealize, /* realize */
149 actions, /* actions */
150 XtNumber(actions), /* num_actions */
155 resources, /* resources */
156 XtNumber(resources), /* num_resources */
157 NULLQUARK, /* xrm_class */
158 True, /* compress_motion */
159 True, /* compress_exposure */
160 True, /* compress_enterleave */
161 False, /* visible_interest */
163 XawBoxResize, /* resize */
165 XawBoxExpose, /* expose */
169 XawBoxSetValues, /* set_values */
170 NULL, /* set_values_hook */
171 XtInheritSetValuesAlmost, /* set_values_almost */
172 NULL, /* get_values_hook */
173 NULL, /* accept_focus */
174 XtVersion, /* version */
175 NULL, /* callback_private */
177 XawBoxQueryGeometry, /* query_geometry */
178 XtInheritDisplayAccelerator, /* display_accelerator */
179 NULL, /* extension */
183 XawBoxGeometryManager, /* geometry_manager */
184 XawBoxChangeManaged, /* change_managed */
185 XtInheritInsertChild, /* insert_child */
186 XtInheritDeleteChild, /* delete_child */
187 NULL, /* extension */
191 NULL, /* extension */
195 WidgetClass boxWidgetClass = (WidgetClass)&boxClassRec;
198 * Do a layout, either actually assigning positions, or just calculating size.
199 * Returns minimum width and height that will preserve the same layout.
202 DoLayout(BoxWidget bbw, unsigned int width, unsigned int height,
203 Dimension *reply_width, Dimension *reply_height, Bool position)
205 Boolean vbox = (bbw->box.orientation == XtorientVertical);
207 Dimension w, h; /* Width and height needed for box */
208 Dimension lw, lh; /* Width and height needed for current line */
209 Dimension bw, bh; /* Width and height needed for current widget */
210 Dimension h_space; /* Local copy of bbw->box.h_space */
211 Widget widget; /* Current widget */
212 unsigned int num_mapped_children = 0;
214 /* Box width and height */
215 h_space = bbw->box.h_space;
218 for (i = 0; i < bbw->composite.num_children; i++) {
219 if (XtIsManaged(bbw->composite.children[i])
220 && bbw->composite.children[i]->core.width > w)
221 w = bbw->composite.children[i]->core.width;
226 h = bbw->box.v_space;
228 /* Line width and height */
232 for (i = 0; i < bbw->composite.num_children; i++) {
233 widget = bbw->composite.children[i];
234 if (widget->core.managed) {
235 if (widget->core.mapped_when_managed)
236 num_mapped_children++;
237 /* Compute widget width */
238 bw = XtWidth(widget) + (XtBorderWidth(widget)<<1) + h_space;
239 if ((Dimension)(lw + bw) > width) {
241 /* At least one widget on this line, and
242 * can't fit any more. Start new line if vbox
246 h += lh + bbw->box.v_space;
251 else if (!position) {
252 /* too narrow for this widget; we'll assume we can grow */
253 DoLayout(bbw, (unsigned)(lw + bw), height, reply_width,
254 reply_height, position);
258 if (position && (lw != XtX(widget) || h != XtY(widget))) {
259 /* It would be nice to use window gravity, but there isn't
260 * sufficient fine-grain control to nicely handle all
261 * situations (e.g. when only the height changes --
262 * a common case). Explicit unmapping is a cheap hack
263 * to speed things up & avoid the visual jitter as
264 * things slide around.
266 * %%% perhaps there should be a client resource to
267 * control this. If so, we'll have to optimize to
268 * perform the moves from the correct end so we don't
269 * force extra exposures as children occlude each other.
271 if (XtIsRealized(widget) && widget->core.mapped_when_managed)
272 XUnmapWindow( XtDisplay(widget), XtWindow(widget));
273 XtMoveWidget(widget, (int)lw, (int)h);
276 bh = XtHeight(widget) + (XtBorderWidth(widget) << 1);
281 if (!vbox && width && lw > width && lh < height) {
282 /* reduce width if too wide and height not filled */
283 Dimension sw = lw, sh = lh;
284 Dimension width_needed = width;
285 XtOrientation orientation = bbw->box.orientation;
287 bbw->box.orientation = XtorientVertical;
288 while (sh < height && sw > width) {
290 DoLayout(bbw, (unsigned)(sw-1), height, &sw, &sh, False);
294 if (width_needed != lw) {
295 DoLayout(bbw, width_needed, height,
296 reply_width, reply_height, position);
297 bbw->box.orientation = orientation;
300 bbw->box.orientation = orientation;
302 if (vbox && (width < w || width < lw)) {
304 DoLayout(bbw, w, height, reply_width, reply_height, position);
307 if (position && XtIsRealized((Widget)bbw)) {
308 if (bbw->composite.num_children == num_mapped_children)
309 XMapSubwindows(XtDisplay((Widget)bbw), XtWindow((Widget)bbw));
311 int ii = bbw->composite.num_children;
312 Widget *childP = bbw->composite.children;
314 for (; ii > 0; childP++, ii--)
315 if (XtIsRealized(*childP) && XtIsManaged(*childP)
316 && (*childP)->core.mapped_when_managed)
317 XtMapWidget(*childP);
321 /* Finish off last line */
324 h += lh + bbw->box.v_space;
327 *reply_width = Max(w, 1);
328 *reply_height = Max(h, 1);
332 * Calculate preferred size, given constraining box, caching it in the widget
334 static XtGeometryResult
335 XawBoxQueryGeometry(Widget widget, XtWidgetGeometry *constraint,
336 XtWidgetGeometry *preferred)
338 BoxWidget w = (BoxWidget)widget;
340 Dimension preferred_width = w->box.preferred_width;
341 Dimension preferred_height = w->box.preferred_height;
343 constraint->request_mode &= CWWidth | CWHeight;
345 if (constraint->request_mode == 0)
346 /* parent isn't going to change w or h, so nothing to re-compute */
347 return (XtGeometryYes);
349 if (constraint->request_mode == w->box.last_query_mode
350 && (!(constraint->request_mode & CWWidth)
351 || constraint->width == w->box.last_query_width)
352 && (!(constraint->request_mode & CWHeight)
353 || constraint->height == w->box.last_query_height)) {
354 /* same query; current preferences are still valid */
355 preferred->request_mode = CWWidth | CWHeight;
356 preferred->width = preferred_width;
357 preferred->height = preferred_height;
358 if (constraint->request_mode == (CWWidth | CWHeight)
359 && constraint->width == preferred_width
360 && constraint->height == preferred_height)
361 return (XtGeometryYes);
363 return (XtGeometryAlmost);
366 /* else gotta do it the long way...
367 I have a preference for tall and narrow, so if my width is
368 constrained, I'll accept it; otherwise, I'll compute the minimum
369 width that will fit me within the height constraint */
371 w->box.last_query_mode = constraint->request_mode;
372 w->box.last_query_width = constraint->width;
373 w->box.last_query_height= constraint->height;
375 if (constraint->request_mode & CWWidth)
376 width = constraint->width;
377 else { /* if (constraint->request_mode & CWHeight) */
378 /* let's see if I can become any narrower */
380 constraint->width = 65535;
383 /* height is currently ignored by DoLayout.
384 height = (constraint->request_mode & CWHeight) ? constraint->height
387 DoLayout(w, width, 0, &preferred_width, &preferred_height, False);
389 if (constraint->request_mode & CWHeight
390 && preferred_height > constraint->height) {
391 /* find minimum width for this height */
392 if (preferred_width <= constraint->width) {
393 width = preferred_width;
394 do { /* find some width big enough to stay within this height */
396 if (width > constraint->width)
397 width = constraint->width;
398 DoLayout(w, width, 0, &preferred_width, &preferred_height, False);
399 } while (preferred_height > constraint->height
400 && width < constraint->width);
401 if (width != constraint->width) {
402 do { /* find minimum width */
403 width = preferred_width;
404 DoLayout(w, (unsigned)(preferred_width - 1), 0,
405 &preferred_width, &preferred_height, False);
406 } while (preferred_height < constraint->height);
408 DoLayout(w, width, 0, &preferred_width, &preferred_height, False);
413 preferred->request_mode = CWWidth | CWHeight;
414 preferred->width = w->box.preferred_width = preferred_width;
415 preferred->height = w->box.preferred_height = preferred_height;
417 if (constraint->request_mode == (CWWidth|CWHeight)
418 && constraint->width == preferred_width
419 && constraint->height == preferred_height)
420 return (XtGeometryYes);
422 return (XtGeometryAlmost);
426 * Actually layout the box
429 XawBoxResize(Widget w)
433 DoLayout((BoxWidget)w, XtWidth(w), XtHeight(w), &tmp, &tmp, True);
437 * Try to do a new layout within the current width and height;
438 * if that fails try to resize and do it within the box returne
439 * by XawBoxQueryGeometry
441 * TryNewLayout just says if it's possible, and doesn't actually move the kids
444 TryNewLayout(BoxWidget bbw)
446 Dimension preferred_width, preferred_height;
447 Dimension proposed_width, proposed_height;
450 DoLayout(bbw, bbw->core.width, bbw->core.height,
451 &preferred_width, &preferred_height, False);
453 /* at this point, preferred_width is guaranteed to not be greater
454 than bbw->core.width unless some child is larger, so there's no
455 point in re-computing another layout */
457 if (XtWidth(bbw) == preferred_width && XtHeight(bbw) == preferred_height)
460 /* let's see if our parent will go for a new size */
462 proposed_width = preferred_width;
463 proposed_height = preferred_height;
465 switch (XtMakeResizeRequest((Widget)bbw,proposed_width,proposed_height,
466 &proposed_width, &proposed_height)) {
471 /* protect from malicious parents who change their minds */
472 DoLayout(bbw, bbw->core.width, bbw->core.height,
473 &preferred_width, &preferred_height, False);
474 if (preferred_width <= XtWidth(bbw)
475 && preferred_height <= XtHeight(bbw))
479 case XtGeometryAlmost:
480 if (proposed_height >= preferred_height &&
481 proposed_width >= preferred_width) {
483 * Take it, and assume the parent knows what it is doing.
485 * The parent must accept this since it was returned in
488 (void)XtMakeResizeRequest((Widget)bbw,
489 proposed_width, proposed_height,
490 &proposed_width, &proposed_height);
493 else if (proposed_width != preferred_width) {
494 /* recalc bounding box; height might change */
495 DoLayout(bbw, proposed_width, 0,
496 &preferred_width, &preferred_height, False);
497 proposed_height = preferred_height;
499 else { /* proposed_height != preferred_height */
500 XtWidgetGeometry constraints, reply;
502 constraints.request_mode = CWHeight;
503 constraints.height = proposed_height;
504 (void)XawBoxQueryGeometry((Widget)bbw, &constraints, &reply);
505 proposed_width = preferred_width;
512 } while (iterations < 10);
520 * 'reply' is unused; we say only yeay or nay, never almost.
523 static XtGeometryResult
524 XawBoxGeometryManager(Widget w, XtWidgetGeometry *request,
525 XtWidgetGeometry *reply)
527 Dimension width, height, borderWidth;
530 /* Position request always denied */
531 if (((request->request_mode & CWX) && request->x != XtX(w))
532 || ((request->request_mode & CWY) && request->y != XtY(w)))
533 return (XtGeometryNo);
535 /* Size changes must see if the new size can be accomodated */
536 if (request->request_mode & (CWWidth | CWHeight | CWBorderWidth)) {
537 /* Make all three fields in the request valid */
538 if ((request->request_mode & CWWidth) == 0)
539 request->width = XtWidth(w);
540 if ((request->request_mode & CWHeight) == 0)
541 request->height = XtHeight(w);
542 if ((request->request_mode & CWBorderWidth) == 0)
543 request->border_width = XtBorderWidth(w);
545 /* Save current size and set to new size */
547 height = XtHeight(w);
548 borderWidth = XtBorderWidth(w);
549 XtWidth(w) = request->width;
550 XtHeight(w) = request->height;
551 XtBorderWidth(w) = request->border_width;
553 /* Decide if new layout works:
554 (1) new widget is smaller,
555 (2) new widget fits in existing Box,
556 (3) Box can be expanded to allow new widget to fit
559 bbw = (BoxWidget) w->core.parent;
561 if (TryNewLayout(bbw)) {
562 /* Fits in existing or new space, relayout */
563 (*XtClass((Widget)bbw)->core_class.resize)((Widget)bbw);
564 return (XtGeometryYes);
567 /* Cannot satisfy request, change back to original geometry */
569 XtHeight(w) = height;
570 XtBorderWidth(w) = borderWidth;
571 return (XtGeometryNo);
575 /* Any stacking changes don't make a difference, so allow if that's all */
576 return (XtGeometryYes);
580 XawBoxChangeManaged(Widget w)
582 /* Reconfigure the box */
583 (void)TryNewLayout((BoxWidget)w);
588 XawBoxClassInitialize(void)
590 XawInitializeWidgetSet();
591 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
593 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString,
594 NULL, 0, XtCacheNone, NULL);
599 XawBoxInitialize(Widget request, Widget cnew,
600 ArgList args, Cardinal *num_args)
602 BoxWidget newbbw = (BoxWidget)cnew;
604 newbbw->box.last_query_mode = CWWidth | CWHeight;
605 newbbw->box.last_query_width = newbbw->box.last_query_height = 0;
606 newbbw->box.preferred_width = Max(newbbw->box.h_space, 1);
607 newbbw->box.preferred_height = Max(newbbw->box.v_space, 1);
609 if (XtWidth(newbbw) == 0)
610 XtWidth(newbbw) = newbbw->box.preferred_width;
612 if (XtHeight(newbbw) == 0)
613 XtHeight(newbbw) = newbbw->box.preferred_height;
617 XawBoxRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
623 XtCreateWindow(w, InputOutput, (Visual *)CopyFromParent,
624 *valueMask, attributes);
627 if (w->core.background_pixmap > XtUnspecifiedPixmap) {
628 pixmap = XawPixmapFromXPixmap(w->core.background_pixmap, XtScreen(w),
629 w->core.colormap, w->core.depth);
630 if (pixmap && pixmap->mask)
631 XawReshapeWidget(w, pixmap);
638 XawBoxSetValues(Widget current, Widget request, Widget cnew,
639 ArgList args, Cardinal *num_args)
641 /* need to relayout if h_space or v_space change */
643 BoxWidget b_old = (BoxWidget)current;
644 BoxWidget b_new = (BoxWidget)cnew;
646 if (b_old->core.background_pixmap != b_new->core.background_pixmap) {
647 XawPixmap *opix, *npix;
649 opix = XawPixmapFromXPixmap(b_old->core.background_pixmap,
650 XtScreen(b_old), b_old->core.colormap,
652 npix = XawPixmapFromXPixmap(b_new->core.background_pixmap,
653 XtScreen(b_new), b_new->core.colormap,
655 if ((npix && npix->mask) || (opix && opix->mask))
656 XawReshapeWidget(cnew, npix);
665 XawBoxExpose(Widget w, XEvent *event, Region region)
667 BoxWidget xaw = (BoxWidget)w;
669 if (xaw->box.display_list)
670 XawRunDisplayList(w, xaw->box.display_list, event, region);