Box.c

Go to the documentation of this file.
00001 /* $XConsortium: Box.c,v 1.49 94/04/17 20:11:54 kaleb Exp $ */
00002 
00003 /***********************************************************
00004 
00005 Copyright (c) 1987, 1988, 1994  X Consortium
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a copy
00008 of this software and associated documentation files (the "Software"), to deal
00009 in the Software without restriction, including without limitation the rights
00010 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011 copies of the Software, and to permit persons to whom the Software is
00012 furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00020 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00021 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00022 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00023 
00024 Except as contained in this notice, the name of the X Consortium shall not be
00025 used in advertising or otherwise to promote the sale, use or other dealings
00026 in this Software without prior written authorization from the X Consortium.
00027 
00028 
00029 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
00030 
00031                         All Rights Reserved
00032 
00033 Permission to use, copy, modify, and distribute this software and its 
00034 documentation for any purpose and without fee is hereby granted, 
00035 provided that the above copyright notice appear in all copies and that
00036 both that copyright notice and this permission notice appear in 
00037 supporting documentation, and that the name of Digital not be
00038 used in advertising or publicity pertaining to distribution of the
00039 software without specific, written prior permission.  
00040 
00041 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00042 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00043 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00044 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00045 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00046 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00047 SOFTWARE.
00048 
00049 ******************************************************************/
00050 
00051 
00052 /* 
00053  * Box.c - Box composite widget
00054  * 
00055  */
00056 
00057 #include        <X11/IntrinsicP.h>
00058 #include        <X11/StringDefs.h>
00059 #include        <X11/Xmu/Misc.h>
00060 #include        <X11/Xaw3d/XawInit.h>
00061 #include        <X11/Xaw3d/BoxP.h>
00062 
00063 /****************************************************************
00064  *
00065  * Box Resources
00066  *
00067  ****************************************************************/
00068 
00069 static XtResource resources[] = {
00070     { XtNhSpace, XtCHSpace, XtRDimension, sizeof(Dimension),
00071                 XtOffsetOf(BoxRec, box.h_space),
00072                 XtRImmediate, (XtPointer)4 },
00073     { XtNvSpace, XtCVSpace, XtRDimension, sizeof(Dimension),
00074                 XtOffsetOf(BoxRec, box.v_space),
00075                 XtRImmediate, (XtPointer)4 },
00076     { XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
00077                 XtOffsetOf(BoxRec, box.orientation),
00078                 XtRImmediate, (XtPointer)XtorientVertical },
00079 };
00080 
00081 /****************************************************************
00082  *
00083  * Full class record constant
00084  *
00085  ****************************************************************/
00086 
00087 static void ClassInitialize();
00088 static void Initialize();
00089 static void Realize();
00090 static void Resize();
00091 static Boolean SetValues();
00092 static XtGeometryResult GeometryManager();
00093 static void ChangeManaged();
00094 static XtGeometryResult PreferredSize();
00095 
00096 BoxClassRec boxClassRec = {
00097   {
00098 /* core_class fields      */
00099     /* superclass         */    (WidgetClass) &compositeClassRec,
00100     /* class_name         */    "Box",
00101     /* widget_size        */    sizeof(BoxRec),
00102     /* class_initialize   */    ClassInitialize,
00103     /* class_part_init    */    NULL,
00104     /* class_inited       */    FALSE,
00105     /* initialize         */    Initialize,
00106     /* initialize_hook    */    NULL,
00107     /* realize            */    Realize,
00108     /* actions            */    NULL,
00109     /* num_actions        */    0,
00110     /* resources          */    resources,
00111     /* num_resources      */    XtNumber(resources),
00112     /* xrm_class          */    NULLQUARK,
00113     /* compress_motion    */    TRUE,
00114     /* compress_exposure  */    TRUE,
00115     /* compress_enterleave*/    TRUE,
00116     /* visible_interest   */    FALSE,
00117     /* destroy            */    NULL,
00118     /* resize             */    Resize,
00119     /* expose             */    NULL,
00120     /* set_values         */    SetValues,
00121     /* set_values_hook    */    NULL,
00122     /* set_values_almost  */    XtInheritSetValuesAlmost,
00123     /* get_values_hook    */    NULL,
00124     /* accept_focus       */    NULL,
00125     /* version            */    XtVersion,
00126     /* callback_private   */    NULL,
00127     /* tm_table           */    NULL,
00128     /* query_geometry     */    PreferredSize,
00129     /* display_accelerator*/    XtInheritDisplayAccelerator,
00130     /* extension          */    NULL
00131   },{
00132 /* composite_class fields */
00133     /* geometry_manager   */    GeometryManager,
00134     /* change_managed     */    ChangeManaged,
00135     /* insert_child       */    XtInheritInsertChild,
00136     /* delete_child       */    XtInheritDeleteChild,
00137     /* extension          */    NULL
00138   },{
00139 /* Box class fields */
00140     /* empty              */    0,
00141   }
00142 };
00143 
00144 WidgetClass boxWidgetClass = (WidgetClass)&boxClassRec;
00145 
00146 
00147 /****************************************************************
00148  *
00149  * Private Routines
00150  *
00151  ****************************************************************/
00152 
00153 /*
00154  *
00155  * Do a layout, either actually assigning positions, or just calculating size.
00156  * Returns minimum width and height that will preserve the same layout.
00157  *
00158  */
00159 
00160 static void DoLayout(bbw, width, height, reply_width, reply_height, position)
00161     BoxWidget   bbw;
00162     Dimension   width, height;
00163     Dimension   *reply_width, *reply_height; /* bounding box */
00164     Boolean     position;       /* actually reposition the windows? */
00165 {
00166     Boolean vbox = (bbw->box.orientation == XtorientVertical);
00167     Cardinal  i;
00168     Dimension w, h;     /* Width and height needed for box              */
00169     Dimension lw, lh;   /* Width and height needed for current line     */
00170     Dimension bw, bh;   /* Width and height needed for current widget   */
00171     Dimension h_space;  /* Local copy of bbw->box.h_space               */
00172     Widget widget;      /* Current widget                               */
00173     int num_mapped_children = 0;
00174  
00175     /* Box width and height */
00176     h_space = bbw->box.h_space;
00177 
00178     w = 0;
00179     for (i = 0; i < bbw->composite.num_children; i++) {
00180         if ( bbw->composite.children[i]->core.width > w )
00181             w = bbw->composite.children[i]->core.width;
00182     }
00183     w += h_space;
00184     if ( w > width ) width = w;
00185     h = bbw->box.v_space;
00186    
00187     /* Line width and height */
00188     lh = 0;
00189     lw = h_space;
00190   
00191     for (i = 0; i < bbw->composite.num_children; i++) {
00192         widget = bbw->composite.children[i];
00193         if (widget->core.managed) {
00194             if (widget->core.mapped_when_managed) num_mapped_children++;
00195             /* Compute widget width */
00196             bw = widget->core.width + 2*widget->core.border_width + h_space;
00197             if ((Dimension)(lw + bw) > width) {
00198                 if (lw > h_space) {
00199                     /* At least one widget on this line, and
00200                      * can't fit any more.  Start new line if vbox.
00201                      */
00202                     AssignMax(w, lw);
00203                     if (vbox) {
00204                         h += lh + bbw->box.v_space;
00205                         lh = 0;
00206                         lw = h_space;
00207                     }
00208                 }
00209                 else if (!position) {
00210                     /* too narrow for this widget; we'll assume we can grow */
00211                     DoLayout(bbw, lw + bw, height, reply_width,
00212                              reply_height, position);
00213                     return;
00214                 }
00215             }
00216             if (position && (lw != (Dimension)widget->core.x || h != (Dimension)widget->core.y)) {
00217                 /* It would be nice to use window gravity, but there isn't
00218                  * sufficient fine-grain control to nicely handle all
00219                  * situations (e.g. when only the height changes --
00220                  * a common case).  Explicit unmapping is a cheap hack
00221                  * to speed things up & avoid the visual jitter as
00222                  * things slide around.
00223                  *
00224                  * %%% perhaps there should be a client resource to
00225                  * control this.  If so, we'll have to optimize to
00226                  * perform the moves from the correct end so we don't
00227                  * force extra exposures as children occlude each other.
00228                  */
00229                 if (XtIsRealized(widget) && widget->core.mapped_when_managed)
00230                     XUnmapWindow( XtDisplay(widget), XtWindow(widget) );
00231                 XtMoveWidget(widget, (int)lw, (int)h);
00232             }
00233             lw += bw;
00234             bh = widget->core.height + 2*widget->core.border_width;
00235             AssignMax(lh, bh);
00236         } /* if managed */
00237     } /* for */
00238 
00239     if (!vbox && width && lw > width && lh < height) {
00240         /* reduce width if too wide and height not filled */
00241         Dimension sw = lw, sh = lh;
00242         Dimension width_needed = 0;
00243         XtOrientation orientation = bbw->box.orientation;
00244         bbw->box.orientation = XtorientVertical;
00245         while (sh < height && sw > width) {
00246             width_needed = sw;
00247             DoLayout(bbw, sw-1, height, &sw, &sh, False);
00248         }
00249         if (sh < height) width_needed = sw;
00250         if (width_needed != lw) {
00251             DoLayout(bbw,width_needed,height,reply_width,reply_height,position);
00252             bbw->box.orientation = orientation;
00253             return;
00254         }
00255         bbw->box.orientation = orientation;
00256     }
00257    if ( vbox && ( ( width < w ) || ( width < lw ) ) ) {
00258         AssignMax(w, lw);
00259         DoLayout( bbw, w, height, reply_width, reply_height, position );
00260         return;
00261     }
00262     if (position && XtIsRealized((Widget)bbw)) {
00263         if (bbw->composite.num_children == num_mapped_children)
00264             XMapSubwindows( XtDisplay((Widget)bbw), XtWindow((Widget)bbw) );
00265         else {
00266             int i = bbw->composite.num_children;
00267             Widget *childP = bbw->composite.children;
00268             for (; i > 0; childP++, i--)
00269                 if (XtIsRealized(*childP) && XtIsManaged(*childP) &&
00270                     (*childP)->core.mapped_when_managed)
00271                     XtMapWidget(*childP);
00272         }
00273     }
00274 
00275     /* Finish off last line */
00276     if (lw > h_space) {
00277         AssignMax(w, lw);
00278         h += lh + bbw->box.v_space;
00279     }
00280 
00281     *reply_width = Max(w, 1);
00282     *reply_height = Max(h, 1);
00283 }
00284 
00285 /*
00286  *
00287  * Calculate preferred size, given constraining box, caching it in the widget.
00288  *
00289  */
00290 
00291 static XtGeometryResult PreferredSize(widget, constraint, preferred)
00292     Widget widget;
00293     XtWidgetGeometry *constraint, *preferred;
00294 {
00295     BoxWidget w = (BoxWidget)widget;
00296     Dimension width /*, height */;
00297     Dimension preferred_width = w->box.preferred_width;
00298     Dimension preferred_height = w->box.preferred_height;
00299 
00300     constraint->request_mode &= CWWidth | CWHeight;
00301 
00302     if (constraint->request_mode == 0)
00303         /* parent isn't going to change w or h, so nothing to re-compute */
00304         return XtGeometryYes;
00305 
00306     if (constraint->request_mode == w->box.last_query_mode &&
00307         (!(constraint->request_mode & CWWidth) ||
00308          constraint->width == w->box.last_query_width) &&
00309         (!(constraint->request_mode & CWHeight) ||
00310          constraint->height == w->box.last_query_height)) {
00311         /* same query; current preferences are still valid */
00312         preferred->request_mode = CWWidth | CWHeight;
00313         preferred->width = preferred_width;
00314         preferred->height = preferred_height;
00315         if (constraint->request_mode == (CWWidth | CWHeight) &&
00316             constraint->width == preferred_width &&
00317             constraint->height == preferred_height)
00318             return XtGeometryYes;
00319         else
00320             return XtGeometryAlmost;
00321     }
00322         
00323     /* else gotta do it the long way...
00324        I have a preference for tall and narrow, so if my width is
00325        constrained, I'll accept it; otherwise, I'll compute the minimum
00326        width that will fit me within the height constraint */
00327 
00328     w->box.last_query_mode = constraint->request_mode;
00329     w->box.last_query_width = constraint->width;
00330     w->box.last_query_height= constraint->height;
00331 
00332     if (constraint->request_mode & CWWidth)
00333         width = constraint->width;
00334     else /* if (constraint->request_mode & CWHeight) */ {
00335          /* let's see if I can become any narrower */
00336         width = 0;
00337         constraint->width = 65535;
00338     }
00339 
00340     /* height is currently ignored by DoLayout.
00341        height = (constraint->request_mode & CWHeight) ? constraint->height
00342                        : *preferred_height;
00343      */
00344     DoLayout(w, width, (Dimension)0,
00345              &preferred_width, &preferred_height, FALSE);
00346 
00347     if (constraint->request_mode & CWHeight &&
00348         preferred_height > constraint->height) {
00349         /* find minimum width for this height */
00350         if (preferred_width > constraint->width) {
00351             /* punt; over-constrained */
00352         }
00353         else {
00354             width = preferred_width;
00355             do { /* find some width big enough to stay within this height */
00356                 width *= 2;
00357                 if (width > constraint->width) width = constraint->width;
00358                 DoLayout(w, width, 0, &preferred_width, &preferred_height, FALSE);
00359             } while (preferred_height > constraint->height &&
00360                      width < constraint->width);
00361             if (width != constraint->width) {
00362                 do { /* find minimum width */
00363                     width = preferred_width;
00364                     DoLayout(w, preferred_width-1, 0,
00365                              &preferred_width, &preferred_height, FALSE);
00366                 } while (preferred_height < constraint->height);
00367                 /* one last time */
00368                 DoLayout(w, width, 0, &preferred_width, &preferred_height, FALSE);
00369             }
00370         }
00371     }
00372 
00373     preferred->request_mode = CWWidth | CWHeight;
00374     preferred->width = w->box.preferred_width = preferred_width;
00375     preferred->height = w->box.preferred_height = preferred_height;
00376 
00377     if (constraint->request_mode == (CWWidth|CWHeight)
00378         && constraint->width == preferred_width
00379         && constraint->height == preferred_height)
00380         return XtGeometryYes;
00381     else
00382         return XtGeometryAlmost;
00383 
00384 }
00385 
00386 /*
00387  *
00388  * Actually layout the box
00389  *
00390  */
00391 
00392 static void Resize(w)
00393     Widget      w;
00394 {
00395     Dimension junk;
00396 
00397     DoLayout((BoxWidget)w, w->core.width, w->core.height, &junk, &junk, TRUE);
00398 
00399 } /* Resize */
00400 
00401 /*
00402  *
00403  * Try to do a new layout within the current width and height;
00404  * if that fails try to resize and do it within the box returne
00405  * by PreferredSize.
00406  *
00407  * TryNewLayout just says if it's possible, and doesn't actually move the kids
00408  */
00409 
00410 static Boolean TryNewLayout(bbw)
00411     BoxWidget   bbw;
00412 {
00413     Dimension   preferred_width, preferred_height;
00414     Dimension   proposed_width, proposed_height;
00415     int         iterations;
00416 
00417     DoLayout( bbw, bbw->core.width, bbw->core.height,
00418               &preferred_width, &preferred_height, FALSE );
00419 
00420     /* at this point, preferred_width is guaranteed to not be greater
00421        than bbw->core.width unless some child is larger, so there's no
00422        point in re-computing another layout */
00423 
00424     if ((bbw->core.width == preferred_width) &&
00425         (bbw->core.height == preferred_height)) {
00426         /* Same size */
00427         return (TRUE);
00428     }
00429 
00430     /* let's see if our parent will go for a new size. */
00431     iterations = 0;
00432     proposed_width = preferred_width;
00433     proposed_height = preferred_height;
00434     do {
00435         switch (XtMakeResizeRequest((Widget)bbw,proposed_width,proposed_height,
00436                                      &proposed_width, &proposed_height))
00437         {
00438             case XtGeometryYes:
00439                 return (TRUE);
00440 
00441             case XtGeometryNo:
00442                 if (iterations > 0)
00443                     /* protect from malicious parents who change their minds */
00444                     DoLayout( bbw, bbw->core.width, bbw->core.height,
00445                               &preferred_width, &preferred_height, FALSE );
00446                 if ((preferred_width <= bbw->core.width) &&
00447                     (preferred_height <= bbw->core.height))
00448                     return (TRUE);
00449                 else
00450                     return (FALSE);
00451 
00452             case XtGeometryAlmost:
00453                 if (proposed_height >= preferred_height &&
00454                     proposed_width >= preferred_width) {
00455 
00456                     /*
00457                      * Take it, and assume the parent knows what it is doing.
00458                      *
00459                      * The parent must accept this since it was returned in
00460                      * almost.
00461                      *
00462                      */
00463                     (void) XtMakeResizeRequest( (Widget)bbw,
00464                                        proposed_width, proposed_height,
00465                                        &proposed_width, &proposed_height);
00466                     return(TRUE);
00467                 }
00468                 else if (proposed_width != preferred_width) {
00469                     /* recalc bounding box; height might change */
00470                     DoLayout(bbw, proposed_width, 0,
00471                              &preferred_width, &preferred_height, FALSE);
00472                     proposed_height = preferred_height;
00473                 }
00474                 else { /* proposed_height != preferred_height */
00475                     XtWidgetGeometry constraints, reply;
00476                     constraints.request_mode = CWHeight;
00477                     constraints.height = proposed_height;
00478                     (void)PreferredSize((Widget)bbw, &constraints, &reply);
00479                     proposed_width = preferred_width;
00480                 }
00481                 break;
00482 
00483             case XtGeometryDone: /* ??? */
00484             default:
00485                 break;
00486         }
00487         iterations++;
00488     } while (iterations < 10);
00489     return (FALSE);
00490 }
00491 
00492 /*
00493  *
00494  * Geometry Manager
00495  *
00496  * 'reply' is unused; we say only yeay or nay, never almost.
00497  *
00498  */
00499 
00500 /*ARGSUSED*/
00501 static XtGeometryResult GeometryManager(w, request, reply)
00502     Widget              w;
00503     XtWidgetGeometry    *request;
00504     XtWidgetGeometry    *reply; /* RETURN */
00505 
00506 {
00507     Dimension   width, height, borderWidth;
00508     BoxWidget bbw;
00509 
00510     /* Position request always denied */
00511     if ((request->request_mode & CWX && request->x != w->core.x) ||
00512         (request->request_mode & CWY && request->y != w->core.y))
00513         return (XtGeometryNo);
00514 
00515     /* Size changes must see if the new size can be accomodated */
00516     if (request->request_mode & (CWWidth | CWHeight | CWBorderWidth)) {
00517 
00518         /* Make all three fields in the request valid */
00519         if ((request->request_mode & CWWidth) == 0)
00520             request->width = w->core.width;
00521         if ((request->request_mode & CWHeight) == 0)
00522             request->height = w->core.height;
00523         if ((request->request_mode & CWBorderWidth) == 0)
00524             request->border_width = w->core.border_width;
00525 
00526         /* Save current size and set to new size */
00527         width = w->core.width;
00528         height = w->core.height;
00529         borderWidth = w->core.border_width;
00530         w->core.width = request->width;
00531         w->core.height = request->height;
00532         w->core.border_width = request->border_width;
00533 
00534         /* Decide if new layout works: (1) new widget is smaller,
00535            (2) new widget fits in existing Box, (3) Box can be
00536            expanded to allow new widget to fit */
00537 
00538         bbw = (BoxWidget) w->core.parent;
00539 
00540 /* whenever a child changes his geometry, we attempt to
00541  * change ours to be the minimum enclosing size...
00542         if (((request->width + request->border_width <= width + borderWidth) &&
00543             (request->height + request->border_width <= height + borderWidth))
00544         || bbw->box.preferred_width < bbw->core.width
00545         || bbw->box.preferred_height < bbw->core.height
00546         || TryNewLayout(bbw)) {
00547  */
00548         if (TryNewLayout(bbw)) {
00549             /* Fits in existing or new space, relayout */
00550             (*XtClass((Widget)bbw)->core_class.resize)((Widget)bbw);
00551             return (XtGeometryYes);
00552         } else {
00553             /* Cannot satisfy request, change back to original geometry */
00554             w->core.width = width;
00555             w->core.height = height;
00556             w->core.border_width = borderWidth;
00557             return (XtGeometryNo);
00558         }
00559     }; /* if any size changes requested */
00560 
00561     /* Any stacking changes don't make a difference, so allow if that's all */
00562     return (XtGeometryYes);
00563 }
00564 
00565 static void ChangeManaged(w)
00566     Widget w;
00567 {
00568     /* Reconfigure the box */
00569     (void) TryNewLayout((BoxWidget)w);
00570     Resize(w);
00571 }
00572 
00573 static void ClassInitialize()
00574 {
00575     XawInitializeWidgetSet();
00576     XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
00577                     (XtConvertArgList)NULL, (Cardinal)0 );
00578 }
00579 
00580 /* ARGSUSED */
00581 static void Initialize(request, new, args, num_args)
00582     Widget request, new;
00583     ArgList args;
00584     Cardinal *num_args;
00585 {
00586     BoxWidget newbbw = (BoxWidget)new;
00587 
00588     newbbw->box.last_query_mode = CWWidth | CWHeight;
00589     newbbw->box.last_query_width = newbbw->box.last_query_height = 0;
00590     newbbw->box.preferred_width = Max(newbbw->box.h_space, 1);
00591     newbbw->box.preferred_height = Max(newbbw->box.v_space, 1);
00592 
00593     if (newbbw->core.width == 0)
00594         newbbw->core.width = newbbw->box.preferred_width;
00595 
00596     if (newbbw->core.height == 0)
00597         newbbw->core.height = newbbw->box.preferred_height;
00598 
00599 } /* Initialize */
00600 
00601 static void Realize(w, valueMask, attributes)
00602     Widget w;
00603     Mask *valueMask;
00604     XSetWindowAttributes *attributes;
00605 {
00606     attributes->bit_gravity = NorthWestGravity;
00607     *valueMask |= CWBitGravity;
00608 
00609     XtCreateWindow( w, (unsigned)InputOutput, (Visual *)CopyFromParent,
00610                     *valueMask, attributes);
00611 } /* Realize */
00612 
00613 /* ARGSUSED */
00614 static Boolean SetValues(current, request, new, args, num_args)
00615     Widget current, request, new;
00616     ArgList args;
00617     Cardinal *num_args;
00618 {
00619    /* need to relayout if h_space or v_space change */
00620 
00621     return False;
00622 }

Generated on Sun Mar 4 15:03:55 2007 for Scilab [trunk] by  doxygen 1.5.1