Annotation of micropolis/src/tk/tkpack.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tkPack.c --
                      3:  *
                      4:  *     This file contains code to implement the "packer"
                      5:  *     geometry manager for Tk.
                      6:  *
                      7:  * Copyright 1990 Regents of the University of California.
                      8:  * Permission to use, copy, modify, and distribute this
                      9:  * software and its documentation for any purpose and without
                     10:  * fee is hereby granted, provided that the above copyright
                     11:  * notice appear in all copies.  The University of California
                     12:  * makes no representations about the suitability of this
                     13:  * software for any purpose.  It is provided "as is" without
                     14:  * express or implied warranty.
                     15:  */
                     16: 
                     17: #ifndef lint
                     18: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPack.c,v 1.27 92/01/04 15:16:41 ouster Exp $ SPRITE (Berkeley)";
                     19: #endif
                     20: 
                     21: #include "tkconfig.h"
                     22: #include "tkint.h"
                     23: 
                     24: typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
                     25: 
                     26: /* For each window that the packer cares about (either because
                     27:  * the window is managed by the packer or because the window
                     28:  * has children that are managed by the packer), there is a
                     29:  * structure of the following type:
                     30:  */
                     31: 
                     32: typedef struct Packer {
                     33:     Tk_Window tkwin;           /* Tk token for window.  NULL means that
                     34:                                 * the window has been deleted, but the
                     35:                                 * packet hasn't had a chance to clean up
                     36:                                 * yet because the structure is still in
                     37:                                 * use. */
                     38:     struct Packer *parentPtr;  /* Parent within which this window
                     39:                                 * is packed (NULL means this window
                     40:                                 * isn't managed by the packer). */
                     41:     struct Packer *nextPtr;    /* Next window packed within same
                     42:                                 * parent.  List is priority-ordered:
                     43:                                 * first on list gets packed first. */
                     44:     struct Packer *childPtr;   /* First in list of children packed
                     45:                                 * inside this window (NULL means
                     46:                                 * no packed children). */
                     47:     Side side;                 /* Side of parent against which
                     48:                                 * this window is packed. */
                     49:     Tk_Anchor anchorPoint;     /* If frame allocated for window is larger
                     50:                                 * than window needs, this indicates how
                     51:                                 * where to position window in frame. */
                     52:     int padX, padY;            /* Additional amounts of space to give window
                     53:                                 * besides what it asked for. */
                     54:     int doubleBw;              /* Twice the window's last known border
                     55:                                 * width.  If this changes, the window
                     56:                                 * must be repacked within its parent. */
                     57:     int *abortPtr;             /* If non-NULL, it means that there is a nested
                     58:                                 * call to ArrangePacking already working on
                     59:                                 * this window.  *abortPtr may be set to 1 to
                     60:                                 * abort that nested call.  This happens, for
                     61:                                 * example, if tkwin or any of its children
                     62:                                 * is deleted. */
                     63:     int flags;                 /* Miscellaneous flags;  see below
                     64:                                 * for definitions. */
                     65: } Packer;
                     66: 
                     67: /*
                     68:  * Flag values for Packer structures:
                     69:  *
                     70:  * REQUESTED_REPACK:           1 means a Tk_DoWhenIdle request
                     71:  *                             has already been made to repack
                     72:  *                             all the children of this window.
                     73:  * FILLX:                      1 means if frame allocated for window
                     74:  *                             is wider than window needs, expand window
                     75:  *                             to fill frame.  0 means don't make window
                     76:  *                             any larger than needed.
                     77:  * FILLY:                      Same as FILLX, except for height.
                     78:  * EXPAND:                     1 means this window's frame will absorb any
                     79:  *                             extra space in the parent window.
                     80:  */
                     81: 
                     82: #define REQUESTED_REPACK       1
                     83: #define FILLX                  2
                     84: #define FILLY                  4
                     85: #define EXPAND                 8
                     86: 
                     87: /*
                     88:  * Hash table used to map from Tk_Window tokens to corresponding
                     89:  * Packer structures:
                     90:  */
                     91: 
                     92: static Tcl_HashTable packerHashTable;
                     93: 
                     94: /*
                     95:  * Have statics in this module been initialized?
                     96:  */
                     97: 
                     98: static initialized = 0;
                     99: 
                    100: /*
                    101:  * Forward declarations for procedures defined later in this file:
                    102:  */
                    103: 
                    104: static void            ArrangePacking _ANSI_ARGS_((ClientData clientData));
                    105: static Packer *                GetPacker _ANSI_ARGS_((Tk_Window tkwin));
                    106: static int             PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
                    107:                            Packer *prevPtr, Packer *parentPtr, int argc,
                    108:                            char **argv));
                    109: static void            PackReqProc _ANSI_ARGS_((ClientData clientData,
                    110:                            Tk_Window tkwin));
                    111: static void            PackStructureProc _ANSI_ARGS_((ClientData clientData,
                    112:                            XEvent *eventPtr));
                    113: static void            Unlink _ANSI_ARGS_((Packer *packPtr));
                    114: 
                    115: /*
                    116:  *--------------------------------------------------------------
                    117:  *
                    118:  * Tk_PackCmd --
                    119:  *
                    120:  *     This procedure is invoked to process the "pack" Tcl command.
                    121:  *     See the user documentation for details on what it does.
                    122:  *
                    123:  * Results:
                    124:  *     A standard Tcl result.
                    125:  *
                    126:  * Side effects:
                    127:  *     See the user documentation.
                    128:  *
                    129:  *--------------------------------------------------------------
                    130:  */
                    131: 
                    132: int
                    133: Tk_PackCmd(clientData, interp, argc, argv)
                    134:     ClientData clientData;     /* Main window associated with
                    135:                                 * interpreter. */
                    136:     Tcl_Interp *interp;                /* Current interpreter. */
                    137:     int argc;                  /* Number of arguments. */
                    138:     char **argv;               /* Argument strings. */
                    139: {
                    140:     Tk_Window tkwin = (Tk_Window) clientData;
                    141:     int length;
                    142:     char c;
                    143: 
                    144:     if (argc < 3) {
                    145:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    146:                argv[0], " option arg ?arg ...?\"", (char *) NULL);
                    147:        return TCL_ERROR;
                    148:     }
                    149:     c = argv[1][0];
                    150:     length = strlen(argv[1]);
                    151:     if ((c == 'a') && (length >= 2)
                    152:            && (strncmp(argv[1], "after", length) == 0)) {
                    153:        Packer *prevPtr;
                    154:        Tk_Window tkwin2;
                    155: 
                    156:        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
                    157:        if (tkwin2 == NULL) {
                    158:            return TCL_ERROR;
                    159:        }
                    160:        prevPtr = GetPacker(tkwin2);
                    161:        if (prevPtr->parentPtr == NULL) {
                    162:            Tcl_AppendResult(interp, "window \"", argv[2],
                    163:                    "\" isn't packed", (char *) NULL);
                    164:            return TCL_ERROR;
                    165:        }
                    166:        return PackAfter(interp, prevPtr, prevPtr->parentPtr, argc-3, argv+3);
                    167:     } else if ((c == 'a') && (length >= 2)
                    168:            && (strncmp(argv[1], "append", length) == 0)) {
                    169:        Packer *parentPtr;
                    170:        register Packer *prevPtr;
                    171:        Tk_Window tkwin2;
                    172: 
                    173:        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
                    174:        if (tkwin2 == NULL) {
                    175:            return TCL_ERROR;
                    176:        }
                    177:        parentPtr = GetPacker(tkwin2);
                    178:        prevPtr = parentPtr->childPtr;
                    179:        if (prevPtr != NULL) {
                    180:            while (prevPtr->nextPtr != NULL) {
                    181:                prevPtr = prevPtr->nextPtr;
                    182:            }
                    183:        }
                    184:        return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
                    185:     } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
                    186:        Packer *packPtr, *parentPtr;
                    187:        register Packer *prevPtr;
                    188:        Tk_Window tkwin2;
                    189: 
                    190:        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
                    191:        if (tkwin2 == NULL) {
                    192:            return TCL_ERROR;
                    193:        }
                    194:        packPtr = GetPacker(tkwin2);
                    195:        if (packPtr->parentPtr == NULL) {
                    196:            Tcl_AppendResult(interp, "window \"", argv[2],
                    197:                    "\" isn't packed", (char *) NULL);
                    198:            return TCL_ERROR;
                    199:        }
                    200:        parentPtr = packPtr->parentPtr;
                    201:        prevPtr = parentPtr->childPtr;
                    202:        if (prevPtr == packPtr) {
                    203:            prevPtr = NULL;
                    204:        } else {
                    205:            for ( ; ; prevPtr = prevPtr->nextPtr) {
                    206:                if (prevPtr == NULL) {
                    207:                    panic("\"pack before\" couldn't find predecessor");
                    208:                }
                    209:                if (prevPtr->nextPtr == packPtr) {
                    210:                    break;
                    211:                }
                    212:            }
                    213:        }
                    214:        return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3);
                    215:     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
                    216:        char *prefix;
                    217:        register Packer *packPtr;
                    218:        Tk_Window tkwin2;
                    219:        char tmp[20];
                    220:        static char *sideNames[] = {"top", "bottom", "left", "right"};
                    221: 
                    222:        if (argc != 3) {
                    223:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    224:                    argv[0], " info window\"", (char *) NULL);
                    225:            return TCL_ERROR;
                    226:        }
                    227:        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
                    228:        if (tkwin2 == NULL) {
                    229:            return TCL_ERROR;
                    230:        }
                    231:        packPtr = GetPacker(tkwin2);
                    232:        prefix = "";
                    233:        for (packPtr = packPtr->childPtr; packPtr != NULL;
                    234:                packPtr = packPtr->nextPtr) {
                    235:            Tcl_AppendResult(interp, prefix, Tk_PathName(packPtr->tkwin),
                    236:                    " {", sideNames[(int) packPtr->side],
                    237:                    " frame ", Tk_NameOfAnchor(packPtr->anchorPoint),
                    238:                    (char *) NULL);
                    239:            if (packPtr->padX != 0) {
                    240:                sprintf(tmp, "%d", packPtr->padX);
                    241:                Tcl_AppendResult(interp, " padx ", tmp, (char *) NULL);
                    242:            }
                    243:            if (packPtr->padY != 0) {
                    244:                sprintf(tmp, "%d", packPtr->padY);
                    245:                Tcl_AppendResult(interp, " pady ", tmp, (char *) NULL);
                    246:            }
                    247:            if (packPtr->flags & EXPAND) {
                    248:                Tcl_AppendResult(interp, " expand", (char *) NULL);
                    249:            }
                    250:            if ((packPtr->flags & (FILLX|FILLY)) == (FILLX|FILLY)) {
                    251:                Tcl_AppendResult(interp, " fill", (char *) NULL);
                    252:            } else if (packPtr->flags & FILLX) {
                    253:                Tcl_AppendResult(interp, " fillx", (char *) NULL);
                    254:            } else if (packPtr->flags & FILLY) {
                    255:                Tcl_AppendResult(interp, " filly", (char *) NULL);
                    256:            }
                    257:            Tcl_AppendResult(interp, "}", (char *) NULL);
                    258:            prefix = " ";
                    259:        }
                    260:        return TCL_OK;
                    261:     } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
                    262:        Tk_Window tkwin2;
                    263:        Packer *packPtr;
                    264: 
                    265:        if (argc != 3) {
                    266:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    267:                    argv[0], " unpack window\"", (char *) NULL);
                    268:            return TCL_ERROR;
                    269:        }
                    270:        tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
                    271:        if (tkwin2 == NULL) {
                    272:            return TCL_ERROR;
                    273:        }
                    274:        packPtr = GetPacker(tkwin2);
                    275:        if ((packPtr != NULL) && (packPtr->parentPtr != NULL)) {
                    276:            Tk_ManageGeometry(tkwin2, (Tk_GeometryProc *) NULL,
                    277:                    (ClientData) NULL);
                    278:            Unlink(packPtr);
                    279:            Tk_UnmapWindow(packPtr->tkwin);
                    280:        }
                    281:     } else {
                    282:        Tcl_AppendResult(interp, "bad option \"", argv[1],
                    283:                "\":  must be after, append, before, or info", (char *) NULL);
                    284:        return TCL_ERROR;
                    285:     }
                    286:     return TCL_OK;
                    287: }
                    288: 
                    289: /*
                    290:  *--------------------------------------------------------------
                    291:  *
                    292:  * PackReqProc --
                    293:  *
                    294:  *     This procedure is invoked by Tk_GeometryRequest for
                    295:  *     windows managed by the packer.
                    296:  *
                    297:  * Results:
                    298:  *     None.
                    299:  *
                    300:  * Side effects:
                    301:  *     Arranges for tkwin, and all its managed siblings, to
                    302:  *     be re-packed at the next idle point.
                    303:  *
                    304:  *--------------------------------------------------------------
                    305:  */
                    306: 
                    307:        /* ARGSUSED */
                    308: static void
                    309: PackReqProc(clientData, tkwin)
                    310:     ClientData clientData;     /* Packer's information about
                    311:                                 * window that got new preferred
                    312:                                 * geometry.  */
                    313:     Tk_Window tkwin;           /* Other Tk-related information
                    314:                                 * about the window. */
                    315: {
                    316:     register Packer *packPtr = (Packer *) clientData;
                    317: 
                    318:     packPtr = packPtr->parentPtr;
                    319:     if (!(packPtr->flags & REQUESTED_REPACK)) {
                    320:        packPtr->flags |= REQUESTED_REPACK;
                    321:        Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
                    322:     }
                    323: }
                    324: 
                    325: /*
                    326:  *--------------------------------------------------------------
                    327:  *
                    328:  * ArrangePacking --
                    329:  *
                    330:  *     This procedure is invoked (using the Tk_DoWhenIdle
                    331:  *     mechanism) to re-layout a set of windows managed by
                    332:  *     the packer.  It is invoked at idle time so that a
                    333:  *     series of packer requests can be merged into a single
                    334:  *     layout operation.
                    335:  *
                    336:  * Results:
                    337:  *     None.
                    338:  *
                    339:  * Side effects:
                    340:  *     The packed children of parentPtr may get resized or
                    341:  *     moved.
                    342:  *
                    343:  *--------------------------------------------------------------
                    344:  */
                    345: 
                    346: static void
                    347: ArrangePacking(clientData)
                    348:     ClientData clientData;     /* Structure describing parent
                    349:                                 * whose children are to be
                    350:                                 * re-layed out. */
                    351: {
                    352:     register Packer *parentPtr = (Packer *) clientData;
                    353:     register Packer *childPtr; 
                    354:     int numExpX, numExpY;      /* # of windows that are expandable in
                    355:                                 * each direction. */
                    356:     int spareX, spareY;                /* Amount of extra space to give to each
                    357:                                 * expandable window. */
                    358:     int leftOverX, leftOverY;  /* Extra chunk of space to give to last
                    359:                                 * expandable window. */
                    360:     int cavityX, cavityY, cavityWidth, cavityHeight;
                    361:                                /* These variables keep track of the
                    362:                                 * as-yet-unallocated space remaining in
                    363:                                 * the middle of the parent window. */
                    364:     int frameX, frameY, frameWidth, frameHeight;
                    365:                                /* These variables keep track of the frame
                    366:                                 * allocated to the current window. */
                    367:     int x, y, width, height;   /* These variables are used to hold the
                    368:                                 * actual geometry of the current window. */
                    369:     int intBWidth;             /* Width of internal border in parent window,
                    370:                                 * if any. */
                    371:     int abort;                 /* May get set to non-zero to abort this
                    372:                                 * repacking operation. */
                    373:     int maxWidth, maxHeight, tmp;
                    374: 
                    375:     parentPtr->flags &= ~REQUESTED_REPACK;
                    376: 
                    377:     /*
                    378:      * If the parent has no children anymore, then don't do anything
                    379:      * at all:  just leave the parent's size as-is.
                    380:      */
                    381: 
                    382:     if (parentPtr->childPtr == NULL) {
                    383:        return;
                    384:     }
                    385: 
                    386:     /*
                    387:      * Abort any nested call to ArrangePacking for this window, since
                    388:      * we'll do everything necessary here, and set up so this call
                    389:      * can be aborted if necessary.  
                    390:      */
                    391: 
                    392:     if (parentPtr->abortPtr != NULL) {
                    393:        *parentPtr->abortPtr = 1;
                    394:     }
                    395:     parentPtr->abortPtr = &abort;
                    396:     abort = 0;
                    397:     Tk_Preserve((ClientData) parentPtr);
                    398: 
                    399:     /*
                    400:      * Pass #1: scan all the children to figure out the total amount
                    401:      * of space needed.  Two separate widths and heights are computed.
                    402:      *
                    403:      * "Width" and "height" compute the minimum parent size to meet
                    404:      * the needs of each window in the direction "where there is
                    405:      * flexibility".  For example, if a child is packed TOP, then
                    406:      * y is the flexible direction:  the child's requested height
                    407:      * will determine its size.  For this window x is the inflexible
                    408:      * direction:  the window's width will be determined by the amount
                    409:      * of space left in the parent's cavity, not by the window's
                    410:      * requested width.  "Width" and "height" are needed in order to
                    411:      * compute how much extra space there is, so that it can be divided
                    412:      * among the windows that have the EXPAND flag.
                    413:      *
                    414:      * "MaxWidth" and "maxHeight" compute the minimum parent size to
                    415:      * meet all the needs of every window in both directions, flexible
                    416:      * or inflexible.  These values are needed to make geometry requests
                    417:      * of the parent's parent.
                    418:      */
                    419: 
                    420:     intBWidth = Tk_InternalBorderWidth(parentPtr->tkwin);
                    421:     width = height = maxWidth = maxHeight = 2*intBWidth;
                    422:     numExpX = numExpY = 0;
                    423:     for (childPtr = parentPtr->childPtr; childPtr != NULL;
                    424:            childPtr = childPtr->nextPtr) {
                    425:        if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
                    426:            tmp = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
                    427:                    + childPtr->padX + width;
                    428:            if (tmp > maxWidth) {
                    429:                maxWidth = tmp;
                    430:            }
                    431:            height += Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
                    432:                    + childPtr->padY;
                    433:            if (childPtr->flags & EXPAND) {
                    434:                numExpY++;
                    435:            }
                    436:        } else {
                    437:            tmp = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw
                    438:                    + childPtr->padY + height;
                    439:            if (tmp > maxHeight) {
                    440:                maxHeight = tmp;
                    441:            }
                    442:            width += Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw
                    443:                    + childPtr->padX;
                    444:            if (childPtr->flags & EXPAND) {
                    445:                numExpX++;
                    446:            }
                    447:        }
                    448:     }
                    449:     if (width > maxWidth) {
                    450:        maxWidth = width;
                    451:     }
                    452:     if (height > maxHeight) {
                    453:        maxHeight = height;
                    454:     }
                    455: 
                    456:     /*
                    457:      * If the total amount of space needed in the parent window has
                    458:      * changed, then notify the next geometry manager up and requeue
                    459:      * ourselves to start again after the parent has had a chance to
                    460:      * resize us.
                    461:      */
                    462: 
                    463:     if ((maxWidth != Tk_ReqWidth(parentPtr->tkwin))
                    464:            || (maxHeight != Tk_ReqHeight(parentPtr->tkwin))) {
                    465:        Tk_GeometryRequest(parentPtr->tkwin, maxWidth, maxHeight);
                    466:        parentPtr->flags |= REQUESTED_REPACK;
                    467:        Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
                    468:        goto done;
                    469:     }
                    470: 
                    471:     /*
                    472:      * If there is spare space, figure out how much of it goes to
                    473:      * each of the windows that is expandable.
                    474:      */
                    475: 
                    476:     spareX = Tk_Width(parentPtr->tkwin) - width;
                    477:     spareY = Tk_Height(parentPtr->tkwin) - height;
                    478:     if ((spareX <= 0) || (numExpX == 0)) {
                    479:        leftOverX = 0;
                    480:        spareX = 0;
                    481:     } else {
                    482:        leftOverX = spareX % numExpX;
                    483:        spareX /= numExpX;
                    484:     }
                    485:     if ((spareY <= 0) || (numExpY == 0)) {
                    486:        leftOverY = spareY;
                    487:        spareY = 0;
                    488:     } else {
                    489:        leftOverY = spareY % numExpY;
                    490:        spareY /= numExpY;
                    491:     }
                    492: 
                    493:     /*
                    494:      * Pass #2: scan the children a second time assigning
                    495:      * new sizes.  The "cavity" variables keep track of the
                    496:      * unclaimed space in the cavity of the window;  this
                    497:      * shrinks inward as we allocate windows around the
                    498:      * edges.  The "frame" variables keep track of the space
                    499:      * allocated to the current window and its frame.  The
                    500:      * current window is then placed somewhere inside the
                    501:      * frame, depending on anchorPoint.
                    502:      */
                    503: 
                    504:     cavityX = cavityY = x = y = intBWidth;
                    505:     cavityWidth = Tk_Width(parentPtr->tkwin) - 2*intBWidth;
                    506:     cavityHeight = Tk_Height(parentPtr->tkwin) - 2*intBWidth;
                    507:     for (childPtr = parentPtr->childPtr; childPtr != NULL;
                    508:            childPtr = childPtr->nextPtr) {
                    509:        if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) {
                    510:            frameWidth = cavityWidth;
                    511:            frameHeight = Tk_ReqHeight(childPtr->tkwin) + childPtr->padY
                    512:                    + childPtr->doubleBw;
                    513:            if (childPtr->flags & EXPAND) {
                    514:                frameHeight += spareY;
                    515:                numExpY--;
                    516:                if (numExpY == 0) {
                    517:                    frameHeight += leftOverY;
                    518:                }
                    519:            }
                    520:            cavityHeight -= frameHeight;
                    521:            if (cavityHeight < 0) {
                    522:                frameHeight += cavityHeight;
                    523:                cavityHeight = 0;
                    524:            }
                    525:            frameX = cavityX;
                    526:            if (childPtr->side == TOP) {
                    527:                frameY = cavityY;
                    528:                cavityY += frameHeight;
                    529:            } else {
                    530:                frameY = cavityY + cavityHeight;
                    531:            }
                    532:        } else {
                    533:            frameHeight = cavityHeight;
                    534:            frameWidth = Tk_ReqWidth(childPtr->tkwin) + childPtr->padX
                    535:                    + childPtr->doubleBw;
                    536:            if (childPtr->flags & EXPAND) {
                    537:                frameWidth += spareX;
                    538:                numExpX--;
                    539:                if (numExpX == 0) {
                    540:                    frameWidth += leftOverX;
                    541:                }
                    542:            }
                    543:            cavityWidth -= frameWidth;
                    544:            if (cavityWidth < 0) {
                    545:                frameWidth += cavityWidth;
                    546:                cavityWidth = 0;
                    547:            }
                    548:            frameY = cavityY;
                    549:            if (childPtr->side == LEFT) {
                    550:                frameX = cavityX;
                    551:                cavityX += frameWidth;
                    552:            } else {
                    553:                frameX = cavityX + cavityWidth;
                    554:            }
                    555:        }
                    556: 
                    557:        /*
                    558:         * Now that we've got the size of the frame for the window,
                    559:         * compute the window's actual size and location using the
                    560:         * fill and frame factors.
                    561:         */
                    562: 
                    563:        width = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw;
                    564:        if ((childPtr->flags & FILLX) || (width > frameWidth)) {
                    565:            width = frameWidth;
                    566:        }
                    567:        height = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw;
                    568:        if ((childPtr->flags & FILLY) || (height > frameHeight)) {
                    569:            height = frameHeight;
                    570:        }
                    571:        switch (childPtr->anchorPoint) {
                    572:            case TK_ANCHOR_N:
                    573:                x = frameX + (frameWidth - width)/2;
                    574:                y = frameY;
                    575:                break;
                    576:            case TK_ANCHOR_NE:
                    577:                x = frameX + frameWidth - width;
                    578:                y = frameY;
                    579:                break;
                    580:            case TK_ANCHOR_E:
                    581:                x = frameX + frameWidth - width;
                    582:                y = frameY + (frameHeight - height)/2;
                    583:                break;
                    584:            case TK_ANCHOR_SE:
                    585:                x = frameX + frameWidth - width;
                    586:                y = frameY + frameHeight - height;
                    587:                break;
                    588:            case TK_ANCHOR_S:
                    589:                x = frameX + (frameWidth - width)/2;
                    590:                y = frameY + frameHeight - height;
                    591:                break;
                    592:            case TK_ANCHOR_SW:
                    593:                x = frameX;
                    594:                y = frameY + frameHeight - height;
                    595:                break;
                    596:            case TK_ANCHOR_W:
                    597:                x = frameX;
                    598:                y = frameY + (frameHeight - height)/2;
                    599:                break;
                    600:            case TK_ANCHOR_NW:
                    601:                x = frameX;
                    602:                y = frameY;
                    603:                break;
                    604:            case TK_ANCHOR_CENTER:
                    605:                x = frameX + (frameWidth - width)/2;
                    606:                y = frameY + (frameHeight - height)/2;
                    607:                break;
                    608:            default:
                    609:                panic("bad frame factor in ArrangePacking");
                    610:        }
                    611:        width -= childPtr->doubleBw;
                    612:        height -= childPtr->doubleBw;
                    613: 
                    614:        /*
                    615:         * If the window is too small to be interesting then
                    616:         * unmap it.  Otherwise configure it and then make sure
                    617:         * it's mapped.
                    618:         */
                    619: 
                    620:        if ((width <= 0) || (height <= 0)) {
                    621:            Tk_UnmapWindow(childPtr->tkwin);
                    622:        } else {
                    623:            if ((x != Tk_X(childPtr->tkwin))
                    624:                    || (y != Tk_Y(childPtr->tkwin))
                    625:                    || (width != Tk_Width(childPtr->tkwin))
                    626:                    || (height != Tk_Height(childPtr->tkwin))) {
                    627:                Tk_MoveResizeWindow(childPtr->tkwin, x, y,
                    628:                        (unsigned int) width, (unsigned int) height);
                    629:            }
                    630:            if (abort) {
                    631:                goto done;
                    632:            }
                    633:            Tk_MapWindow(childPtr->tkwin);
                    634:        }
                    635: 
                    636:        /*
                    637:         * Changes to the window's structure could cause almost anything
                    638:         * to happen, including deleting the parent or child.  If this
                    639:         * happens, we'll be told to abort.
                    640:         */
                    641: 
                    642:        if (abort) {
                    643:            goto done;
                    644:        }
                    645:     }
                    646: 
                    647:     done:
                    648:     parentPtr->abortPtr = NULL;
                    649:     Tk_Release((ClientData) parentPtr);
                    650: }
                    651: 
                    652: /*
                    653:  *--------------------------------------------------------------
                    654:  *
                    655:  * GetPacker --
                    656:  *
                    657:  *     This internal procedure is used to locate a Packer
                    658:  *     structure for a given window, creating one if one
                    659:  *     doesn't exist already.
                    660:  *
                    661:  * Results:
                    662:  *     The return value is a pointer to the Packer structure
                    663:  *     corresponding to tkwin.
                    664:  *
                    665:  * Side effects:
                    666:  *     A new packer structure may be created.  If so, then
                    667:  *     a callback is set up to clean things up when the
                    668:  *     window is deleted.
                    669:  *
                    670:  *--------------------------------------------------------------
                    671:  */
                    672: 
                    673: static Packer *
                    674: GetPacker(tkwin)
                    675:     Tk_Window tkwin;           /* Token for window for which
                    676:                                 * packer structure is desired. */
                    677: {
                    678:     register Packer *packPtr;
                    679:     Tcl_HashEntry *hPtr;
                    680:     int new;
                    681: 
                    682:     if (!initialized) {
                    683:        initialized = 1;
                    684:        Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
                    685:     }
                    686: 
                    687:     /*
                    688:      * See if there's already packer for this window.  If not,
                    689:      * then create a new one.
                    690:      */
                    691: 
                    692:     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
                    693:     if (!new) {
                    694:        return (Packer *) Tcl_GetHashValue(hPtr);
                    695:     }
                    696:     packPtr = (Packer *) ckalloc(sizeof(Packer));
                    697:     packPtr->tkwin = tkwin;
                    698:     packPtr->parentPtr = NULL;
                    699:     packPtr->nextPtr = NULL;
                    700:     packPtr->childPtr = NULL;
                    701:     packPtr->side = TOP;
                    702:     packPtr->anchorPoint = TK_ANCHOR_CENTER;
                    703:     packPtr->padX = packPtr->padY = 0;
                    704:     packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
                    705:     packPtr->abortPtr = NULL;
                    706:     packPtr->flags = 0;
                    707:     Tcl_SetHashValue(hPtr, packPtr);
                    708:     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
                    709:            PackStructureProc, (ClientData) packPtr);
                    710:     return packPtr;
                    711: }
                    712: 
                    713: /*
                    714:  *--------------------------------------------------------------
                    715:  *
                    716:  * PackAfter --
                    717:  *
                    718:  *     This procedure does most of the real work of adding
                    719:  *     one or more windows into the packing order for its parent.
                    720:  *
                    721:  * Results:
                    722:  *     A standard Tcl return value.
                    723:  *
                    724:  * Side effects:
                    725:  *     The geometry of the specified windows may change, both now and
                    726:  *     again in the future.
                    727:  *
                    728:  *--------------------------------------------------------------
                    729:  */
                    730: 
                    731: static int
                    732: PackAfter(interp, prevPtr, parentPtr, argc, argv)
                    733:     Tcl_Interp *interp;                /* Interpreter for error reporting. */
                    734:     Packer *prevPtr;           /* Pack windows in argv just after this
                    735:                                 * window;  NULL means pack as first
                    736:                                 * child of parentPtr. */
                    737:     Packer *parentPtr;         /* Parent in which to pack windows. */
                    738:     int argc;                  /* Number of elements in argv. */
                    739:     char **argv;               /* Array of lists, each containing 2
                    740:                                 * elements:  window name and side
                    741:                                 * against which to pack. */
                    742: {
                    743:     register Packer *packPtr;
                    744:     Tk_Window tkwin;
                    745:     int length, optionCount;
                    746:     char **options;
                    747:     int index;
                    748:     char c;
                    749: 
                    750:     /*
                    751:      * Iterate over all of the window specifiers, each consisting of
                    752:      * two arguments.  The first argument contains the window name and
                    753:      * the additional arguments contain options such as "top" or
                    754:      * "padx 20".
                    755:      */
                    756: 
                    757:     for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
                    758:        if (argc < 2) {
                    759:            Tcl_AppendResult(interp, "wrong # args: window \"",
                    760:                    argv[0], "\" should be followed by options",
                    761:                    (char *) NULL);
                    762:            return TCL_ERROR;
                    763:        }
                    764: 
                    765:        /*
                    766:         * Find the packer for the window to be packed, and make sure
                    767:         * that the window in which it will be packed is its parent.
                    768:         */
                    769: 
                    770:        tkwin = Tk_NameToWindow(interp, argv[0], parentPtr->tkwin);
                    771:        if (tkwin == NULL) {
                    772:            return TCL_ERROR;
                    773:        }
                    774:        if (Tk_Parent(tkwin) != parentPtr->tkwin) {
                    775:            Tcl_AppendResult(interp, "tried to pack \"",
                    776:                    argv[0], "\" in window that isn't its parent",
                    777:                    (char *) NULL);
                    778:            return TCL_ERROR;
                    779:        }
                    780:        packPtr = GetPacker(tkwin);
                    781: 
                    782:        /*
                    783:         * Process options for this window.
                    784:         */
                    785: 
                    786:        if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
                    787:            return TCL_ERROR;
                    788:        }
                    789:        packPtr->side = TOP;
                    790:        packPtr->anchorPoint = TK_ANCHOR_CENTER;
                    791:        packPtr->padX = packPtr->padY = 0;
                    792:        packPtr->flags &= ~(FILLX|FILLY|EXPAND);
                    793:        for (index = 0 ; index < optionCount; index++) {
                    794:            char *curOpt = options[index];
                    795: 
                    796:            c = curOpt[0];
                    797:            length = strlen(curOpt);
                    798: 
                    799:            if ((c == 't')
                    800:                    && (strncmp(curOpt, "top", length)) == 0) {
                    801:                packPtr->side = TOP;
                    802:            } else if ((c == 'b')
                    803:                    && (strncmp(curOpt, "bottom", length)) == 0) {
                    804:                packPtr->side = BOTTOM;
                    805:            } else if ((c == 'l')
                    806:                    && (strncmp(curOpt, "left", length)) == 0) {
                    807:                packPtr->side = LEFT;
                    808:            } else if ((c == 'r')
                    809:                    && (strncmp(curOpt, "right", length)) == 0) {
                    810:                packPtr->side = RIGHT;
                    811:            } else if ((c == 'e')
                    812:                    && (strncmp(curOpt, "expand", length)) == 0) {
                    813:                packPtr->flags |= EXPAND;
                    814:            } else if ((c == 'f')
                    815:                    && (strcmp(curOpt, "fill")) == 0) {
                    816:                packPtr->flags |= FILLX|FILLY;
                    817:            } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
                    818:                packPtr->flags |= FILLX;
                    819:            } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
                    820:                packPtr->flags |= FILLY;
                    821:            } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
                    822:                if (optionCount < (index+2)) {
                    823:                    missingPad:
                    824:                    Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
                    825:                            "\" option must be followed by count",
                    826:                            (char *) NULL);
                    827:                    goto error;
                    828:                }
                    829:                if ((Tcl_GetInt(interp, options[index+1], &packPtr->padX)
                    830:                        != TCL_OK) || (packPtr->padX < 0)) {
                    831:                    badPad:
                    832:                    Tcl_AppendResult(interp, "bad pad value \"",
                    833:                            options[index+1], "\":  must be positive integer",
                    834:                            (char *) NULL);
                    835:                    goto error;
                    836:                }
                    837:                index++;
                    838:            } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
                    839:                if (optionCount < (index+2)) {
                    840:                    goto missingPad;
                    841:                }
                    842:                if ((Tcl_GetInt(interp, options[index+1], &packPtr->padY)
                    843:                        != TCL_OK) || (packPtr->padY < 0)) {
                    844:                    goto badPad;
                    845:                }
                    846:                index++;
                    847:            } else if ((c == 'f') && (length > 1)
                    848:                    && (strncmp(curOpt, "frame", length) == 0)) {
                    849:                if (optionCount < (index+2)) {
                    850:                    Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
                    851:                            "option must be followed by anchor point",
                    852:                            (char *) NULL);
                    853:                    goto error;
                    854:                }
                    855:                if (Tk_GetAnchor(interp, options[index+1],
                    856:                        &packPtr->anchorPoint) != TCL_OK) {
                    857:                    goto error;
                    858:                }
                    859:                index++;
                    860:            } else {
                    861:                Tcl_AppendResult(interp, "bad option \"", curOpt,
                    862:                        "\":  should be top, bottom, left, right, ",
                    863:                        "expand, fill, fillx, filly, padx, pady, or frame",
                    864:                        (char *) NULL);
                    865:                goto error;
                    866:            }
                    867:        }
                    868: 
                    869:        if (packPtr != prevPtr) {
                    870: 
                    871:            /*
                    872:             * Unpack this window if it's currently packed.
                    873:             */
                    874:        
                    875:            if (packPtr->parentPtr != NULL) {
                    876:                Unlink(packPtr);
                    877:            }
                    878:        
                    879:            /*
                    880:             * Add the window in the correct place in its parent's
                    881:             * packing order, then make sure that the window is
                    882:             * managed by us.
                    883:             */
                    884: 
                    885:            packPtr->parentPtr = parentPtr;
                    886:            if (prevPtr == NULL) {
                    887:                packPtr->nextPtr = parentPtr->childPtr;
                    888:                parentPtr->childPtr = packPtr;
                    889:            } else {
                    890:                packPtr->nextPtr = prevPtr->nextPtr;
                    891:                prevPtr->nextPtr = packPtr;
                    892:            }
                    893:            Tk_ManageGeometry(tkwin, PackReqProc, (ClientData) packPtr);
                    894:        }
                    895:        ckfree((char *) options);
                    896:     }
                    897: 
                    898:     /*
                    899:      * Arrange for the parent to be re-packed at the first
                    900:      * idle moment.
                    901:      */
                    902: 
                    903:     if (parentPtr->abortPtr != NULL) {
                    904:        *parentPtr->abortPtr = 1;
                    905:     }
                    906:     if (!(parentPtr->flags & REQUESTED_REPACK)) {
                    907:        parentPtr->flags |= REQUESTED_REPACK;
                    908:        Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
                    909:     }
                    910:     return TCL_OK;
                    911: 
                    912:     error:
                    913:     ckfree((char *) options);
                    914:     return TCL_ERROR;
                    915: }
                    916: 
                    917: /*
                    918:  *----------------------------------------------------------------------
                    919:  *
                    920:  * Unlink --
                    921:  *
                    922:  *     Remove a packer from its parent's list of children.
                    923:  *
                    924:  * Results:
                    925:  *     None.
                    926:  *
                    927:  * Side effects:
                    928:  *     The parent will be scheduled for repacking.
                    929:  *
                    930:  *----------------------------------------------------------------------
                    931:  */
                    932: 
                    933: static void
                    934: Unlink(packPtr)
                    935:     register Packer *packPtr;          /* Window to unlink. */
                    936: {
                    937:     register Packer *parentPtr, *packPtr2;
                    938: 
                    939:     parentPtr = packPtr->parentPtr;
                    940:     if (parentPtr == NULL) {
                    941:        return;
                    942:     }
                    943:     if (parentPtr->childPtr == packPtr) {
                    944:        parentPtr->childPtr = packPtr->nextPtr;
                    945:     } else {
                    946:        for (packPtr2 = parentPtr->childPtr; ; packPtr2 = packPtr2->nextPtr) {
                    947:            if (packPtr2 == NULL) {
                    948:                panic("Unlink couldn't find previous window");
                    949:            }
                    950:            if (packPtr2->nextPtr == packPtr) {
                    951:                packPtr2->nextPtr = packPtr->nextPtr;
                    952:                break;
                    953:            }
                    954:        }
                    955:     }
                    956:     if (!(parentPtr->flags & REQUESTED_REPACK)) {
                    957:        parentPtr->flags |= REQUESTED_REPACK;
                    958:        Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr);
                    959:     }
                    960:     if (parentPtr->abortPtr != NULL) {
                    961:        *parentPtr->abortPtr = 1;
                    962:     }
                    963: 
                    964:     packPtr->parentPtr = NULL;
                    965: }
                    966: 
                    967: /*
                    968:  *----------------------------------------------------------------------
                    969:  *
                    970:  * DestroyPacker --
                    971:  *
                    972:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    973:  *     to clean up the internal structure of a packer at a safe time
                    974:  *     (when no-one is using it anymore).
                    975:  *
                    976:  * Results:
                    977:  *     None.
                    978:  *
                    979:  * Side effects:
                    980:  *     Everything associated with the packer is freed up.
                    981:  *
                    982:  *----------------------------------------------------------------------
                    983:  */
                    984: 
                    985: static void
                    986: DestroyPacker(clientData)
                    987:     ClientData clientData;             /* Info about packed window that
                    988:                                         * is now dead. */
                    989: {
                    990:     register Packer *packPtr = (Packer *) clientData;
                    991:     ckfree((char *) packPtr);
                    992: }
                    993: 
                    994: /*
                    995:  *----------------------------------------------------------------------
                    996:  *
                    997:  * PackStructureProc --
                    998:  *
                    999:  *     This procedure is invoked by the Tk event dispatcher in response
                   1000:  *     to StructureNotify events.
                   1001:  *
                   1002:  * Results:
                   1003:  *     None.
                   1004:  *
                   1005:  * Side effects:
                   1006:  *     If a window was just deleted, clean up all its packer-related
                   1007:  *     information.  If it was just resized, repack its children, if
                   1008:  *     any.
                   1009:  *
                   1010:  *----------------------------------------------------------------------
                   1011:  */
                   1012: 
                   1013: static void
                   1014: PackStructureProc(clientData, eventPtr)
                   1015:     ClientData clientData;             /* Our information about window
                   1016:                                         * referred to by eventPtr. */
                   1017:     XEvent *eventPtr;                  /* Describes what just happened. */
                   1018: {
                   1019:     register Packer *packPtr = (Packer *) clientData;
                   1020:     if (eventPtr->type == ConfigureNotify) {
                   1021:        if ((packPtr->childPtr != NULL)
                   1022:                && !(packPtr->flags & REQUESTED_REPACK)) {
                   1023:            packPtr->flags |= REQUESTED_REPACK;
                   1024:            Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
                   1025:        }
                   1026:        if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
                   1027:            if ((packPtr->parentPtr != NULL)
                   1028:                    && !(packPtr->parentPtr->flags & REQUESTED_REPACK)) {
                   1029:                packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
                   1030:                packPtr->parentPtr->flags |= REQUESTED_REPACK;
                   1031:                Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->parentPtr);
                   1032:            }
                   1033:        }
                   1034:     } else if (eventPtr->type == DestroyNotify) {
                   1035:        register Packer *packPtr2;
                   1036: 
                   1037:        if (packPtr->parentPtr != NULL) {
                   1038:            Unlink(packPtr);
                   1039:        }
                   1040:        for (packPtr2 = packPtr->childPtr; packPtr2 != NULL;
                   1041:                packPtr2 = packPtr2->nextPtr) {
                   1042:            packPtr2->parentPtr = NULL;
                   1043:            packPtr2->nextPtr = NULL;
                   1044:        }
                   1045:        Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
                   1046:                (char *) packPtr->tkwin));
                   1047:        if (packPtr->flags & REQUESTED_REPACK) {
                   1048:            Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
                   1049:        }
                   1050:        packPtr->tkwin = NULL;
                   1051:        Tk_EventuallyFree((ClientData) packPtr, DestroyPacker);
                   1052:     }
                   1053: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.