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

1.1       root        1: /* 
                      2:  * tkMessage.c --
                      3:  *
                      4:  *     This module implements a message widgets for the Tk
                      5:  *     toolkit.  A message widget displays a multi-line string
                      6:  *     in a window according to a particular aspect ratio.
                      7:  *
                      8:  * Copyright 1990 Regents of the University of California.
                      9:  * Permission to use, copy, modify, and distribute this
                     10:  * software and its documentation for any purpose and without
                     11:  * fee is hereby granted, provided that the above copyright
                     12:  * notice appear in all copies.  The University of California
                     13:  * makes no representations about the suitability of this
                     14:  * software for any purpose.  It is provided "as is" without
                     15:  * express or implied warranty.
                     16:  */
                     17: 
                     18: #ifndef lint
                     19: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMessage.c,v 1.36 92/06/08 11:06:05 ouster Exp $ SPRITE (Berkeley)";
                     20: #endif
                     21: 
                     22: #include "tkconfig.h"
                     23: #include "default.h"
                     24: #include "tkint.h"
                     25: 
                     26: /*
                     27:  * A data structure of the following type is kept for each message
                     28:  * widget managed by this file:
                     29:  */
                     30: 
                     31: typedef struct {
                     32:     Tk_Window tkwin;           /* Window that embodies the message.  NULL
                     33:                                 * means that the window has been destroyed
                     34:                                 * but the data structures haven't yet been
                     35:                                 * cleaned up.*/
                     36:     Tcl_Interp *interp;                /* Interpreter associated with message. */
                     37:     Tk_Uid string;             /* String displayed in message. */
                     38:     int numChars;              /* Number of characters in string, not
                     39:                                 * including terminating NULL character. */
                     40:     char *textVarName;         /* Name of variable (malloc'ed) or NULL.
                     41:                                 * If non-NULL, message displays the contents
                     42:                                 * of this variable. */
                     43: 
                     44:     /*
                     45:      * Information used when displaying widget:
                     46:      */
                     47: 
                     48:     Tk_3DBorder border;                /* Structure used to draw 3-D border and
                     49:                                 * background.  NULL means a border hasn't
                     50:                                 * been created yet. */
                     51:     int borderWidth;           /* Width of border. */
                     52:     int relief;                        /* 3-D effect: TK_RELIEF_RAISED, etc. */
                     53:     XFontStruct *fontPtr;      /* Information about text font, or NULL. */
                     54:     XColor *fgColorPtr;                /* Foreground color in normal mode. */
                     55:     GC textGC;                 /* GC for drawing text in normal mode. */
                     56:     int padX, padY;            /* User-requested extra space around text. */
                     57:     Tk_Anchor anchor;          /* Where to position text within window region
                     58:                                 * if window is larger or smaller than
                     59:                                 * needed. */
                     60:     int width;                 /* User-requested width, in pixels.  0 means
                     61:                                 * compute width using aspect ratio below. */
                     62:     int aspect;                        /* Desired aspect ratio for window
                     63:                                 * (100*width/height). */
                     64:     int lineLength;            /* Length of each line, in pixels.  Computed
                     65:                                 * from width and/or aspect. */
                     66:     int msgHeight;             /* Total number of pixels in vertical direction
                     67:                                 * needed to display message. */
                     68:     Tk_Justify justify;                /* Justification for text. */
                     69: 
                     70:     /*
                     71:      * Miscellaneous information:
                     72:      */
                     73: 
                     74:     Cursor cursor;             /* Current cursor for window, or None. */
                     75:     int flags;                 /* Various flags;  see below for
                     76:                                 * definitions. */
                     77: } Message;
                     78: 
                     79: /*
                     80:  * Flag bits for messages:
                     81:  *
                     82:  * REDRAW_PENDING:             Non-zero means a DoWhenIdle handler
                     83:  *                             has already been queued to redraw
                     84:  *                             this window.
                     85:  * CLEAR_NEEDED;               Need to clear the window when redrawing.
                     86:  */
                     87: 
                     88: #define REDRAW_PENDING         1
                     89: #define CLEAR_NEEDED           2
                     90: 
                     91: /*
                     92:  * Information used for argv parsing.
                     93:  */
                     94: 
                     95: 
                     96: static Tk_ConfigSpec configSpecs[] = {
                     97:     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
                     98:        DEF_MESSAGE_ANCHOR, Tk_Offset(Message, anchor), 0},
                     99:     {TK_CONFIG_INT, "-aspect", "aspect", "Aspect",
                    100:        DEF_MESSAGE_ASPECT, Tk_Offset(Message, aspect), 0},
                    101:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    102:        DEF_MESSAGE_BG_COLOR, Tk_Offset(Message, border),
                    103:        TK_CONFIG_COLOR_ONLY},
                    104:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    105:        DEF_MESSAGE_BG_MONO, Tk_Offset(Message, border),
                    106:        TK_CONFIG_MONO_ONLY},
                    107:     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
                    108:        (char *) NULL, 0, 0},
                    109:     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
                    110:        (char *) NULL, 0, 0},
                    111:     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
                    112:        DEF_MESSAGE_BORDER_WIDTH, Tk_Offset(Message, borderWidth), 0},
                    113:     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
                    114:        DEF_MESSAGE_CURSOR, Tk_Offset(Message, cursor), TK_CONFIG_NULL_OK},
                    115:     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
                    116:        (char *) NULL, 0, 0},
                    117:     {TK_CONFIG_FONT, "-font", "font", "Font",
                    118:        DEF_MESSAGE_FONT, Tk_Offset(Message, fontPtr), 0},
                    119:     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
                    120:        DEF_MESSAGE_FG, Tk_Offset(Message, fgColorPtr), 0},
                    121:     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
                    122:        DEF_MESSAGE_JUSTIFY, Tk_Offset(Message, justify), 0},
                    123:     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
                    124:        DEF_MESSAGE_PADX, Tk_Offset(Message, padX), 0},
                    125:     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
                    126:        DEF_MESSAGE_PADY, Tk_Offset(Message, padY), 0},
                    127:     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
                    128:        DEF_MESSAGE_RELIEF, Tk_Offset(Message, relief), 0},
                    129:     {TK_CONFIG_STRING, "-text", "text", "Text",
                    130:        DEF_MESSAGE_TEXT, Tk_Offset(Message, string), 0},
                    131:     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
                    132:        DEF_MESSAGE_TEXT_VARIABLE, Tk_Offset(Message, textVarName),
                    133:        TK_CONFIG_NULL_OK},
                    134:     {TK_CONFIG_PIXELS, "-width", "width", "Width",
                    135:        DEF_MESSAGE_WIDTH, Tk_Offset(Message, width), 0},
                    136:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    137:        (char *) NULL, 0, 0}
                    138: };
                    139: 
                    140: /*
                    141:  * Forward declarations for procedures defined later in this file:
                    142:  */
                    143: 
                    144: static void            MessageEventProc _ANSI_ARGS_((ClientData clientData,
                    145:                            XEvent *eventPtr));
                    146: static char *          MessageTextVarProc _ANSI_ARGS_((ClientData clientData,
                    147:                            Tcl_Interp *interp, char *name1, char *name2,
                    148:                            int flags));
                    149: static int             MessageWidgetCmd _ANSI_ARGS_((ClientData clientData,
                    150:                            Tcl_Interp *interp, int argc, char **argv));
                    151: static void            ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr));
                    152: static int             ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp,
                    153:                            Message *msgPtr, int argc, char **argv,
                    154:                            int flags));
                    155: static void            DestroyMessage _ANSI_ARGS_((ClientData clientData));
                    156: static void            DisplayMessage _ANSI_ARGS_((ClientData clientData));
                    157: 
                    158: /*
                    159:  *--------------------------------------------------------------
                    160:  *
                    161:  * Tk_MessageCmd --
                    162:  *
                    163:  *     This procedure is invoked to process the "message" Tcl
                    164:  *     command.  See the user documentation for details on what
                    165:  *     it does.
                    166:  *
                    167:  * Results:
                    168:  *     A standard Tcl result.
                    169:  *
                    170:  * Side effects:
                    171:  *     See the user documentation.
                    172:  *
                    173:  *--------------------------------------------------------------
                    174:  */
                    175: 
                    176: int
                    177: Tk_MessageCmd(clientData, interp, argc, argv)
                    178:     ClientData clientData;     /* Main window associated with
                    179:                                 * interpreter. */
                    180:     Tcl_Interp *interp;                /* Current interpreter. */
                    181:     int argc;                  /* Number of arguments. */
                    182:     char **argv;               /* Argument strings. */
                    183: {
                    184:     register Message *msgPtr;
                    185:     Tk_Window new;
                    186:     Tk_Window tkwin = (Tk_Window) clientData;
                    187: 
                    188:     if (argc < 2) {
                    189:        Tcl_AppendResult(interp, "wrong # args:  should be \"",
                    190:                argv[0], " pathName ?options?\"", (char *) NULL);
                    191:        return TCL_ERROR;
                    192:     }
                    193: 
                    194:     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
                    195:     if (new == NULL) {
                    196:        return TCL_ERROR;
                    197:     }
                    198: 
                    199:     msgPtr = (Message *) ckalloc(sizeof(Message));
                    200:     msgPtr->tkwin = new;
                    201:     msgPtr->interp = interp;
                    202:     msgPtr->string = NULL;
                    203:     msgPtr->textVarName = NULL;
                    204:     msgPtr->border = NULL;
                    205:     msgPtr->borderWidth = 0;
                    206:     msgPtr->relief = TK_RELIEF_FLAT;
                    207:     msgPtr->fontPtr = NULL;
                    208:     msgPtr->fgColorPtr = NULL;
                    209:     msgPtr->textGC = NULL;
                    210:     msgPtr->padX = 0;
                    211:     msgPtr->padY = 0;
                    212:     msgPtr->width = 0;
                    213:     msgPtr->aspect = 150;
                    214:     msgPtr->justify = TK_JUSTIFY_LEFT;
                    215:     msgPtr->cursor = None;
                    216:     msgPtr->flags = 0;
                    217: 
                    218:     Tk_SetClass(msgPtr->tkwin, "Message");
                    219:     Tk_CreateEventHandler(msgPtr->tkwin, ExposureMask|StructureNotifyMask,
                    220:            MessageEventProc, (ClientData) msgPtr);
                    221:     Tcl_CreateCommand(interp, Tk_PathName(msgPtr->tkwin), MessageWidgetCmd,
                    222:            (ClientData) msgPtr, (void (*)()) NULL);
                    223:     if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) {
                    224:        goto error;
                    225:     }
                    226: 
                    227:     interp->result = Tk_PathName(msgPtr->tkwin);
                    228:     return TCL_OK;
                    229: 
                    230:     error:
                    231:     Tk_DestroyWindow(msgPtr->tkwin);
                    232:     return TCL_ERROR;
                    233: }
                    234: 
                    235: /*
                    236:  *--------------------------------------------------------------
                    237:  *
                    238:  * MessageWidgetCmd --
                    239:  *
                    240:  *     This procedure is invoked to process the Tcl command
                    241:  *     that corresponds to a widget managed by this module.
                    242:  *     See the user documentation for details on what it does.
                    243:  *
                    244:  * Results:
                    245:  *     A standard Tcl result.
                    246:  *
                    247:  * Side effects:
                    248:  *     See the user documentation.
                    249:  *
                    250:  *--------------------------------------------------------------
                    251:  */
                    252: 
                    253: static int
                    254: MessageWidgetCmd(clientData, interp, argc, argv)
                    255:     ClientData clientData;     /* Information about message widget. */
                    256:     Tcl_Interp *interp;                /* Current interpreter. */
                    257:     int argc;                  /* Number of arguments. */
                    258:     char **argv;               /* Argument strings. */
                    259: {
                    260:     register Message *msgPtr = (Message *) clientData;
                    261:     int length;
                    262:     char c;
                    263: 
                    264:     if (argc < 2) {
                    265:        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    266:                " option ?arg arg ...?\"", (char *) NULL);
                    267:        return TCL_ERROR;
                    268:     }
                    269:     c = argv[1][0];
                    270:     length = strlen(argv[1]);
                    271:     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
                    272:        if (argc == 2) {
                    273:            return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
                    274:                    (char *) msgPtr, (char *) NULL, 0);
                    275:        } else if (argc == 3) {
                    276:            return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
                    277:                    (char *) msgPtr, argv[2], 0);
                    278:        } else {
                    279:            return ConfigureMessage(interp, msgPtr, argc-2, argv+2,
                    280:                    TK_CONFIG_ARGV_ONLY);
                    281:        }
                    282:     } else {
                    283:        Tcl_AppendResult(interp, "bad option \"", argv[1],
                    284:                "\":  must be configure", (char *) NULL);
                    285:        return TCL_ERROR;
                    286:     }
                    287: }
                    288: 
                    289: /*
                    290:  *----------------------------------------------------------------------
                    291:  *
                    292:  * DestroyMessage --
                    293:  *
                    294:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    295:  *     to clean up the internal structure of a message at a safe time
                    296:  *     (when no-one is using it anymore).
                    297:  *
                    298:  * Results:
                    299:  *     None.
                    300:  *
                    301:  * Side effects:
                    302:  *     Everything associated with the message is freed up.
                    303:  *
                    304:  *----------------------------------------------------------------------
                    305:  */
                    306: 
                    307: static void
                    308: DestroyMessage(clientData)
                    309:     ClientData clientData;     /* Info about message widget. */
                    310: {
                    311:     register Message *msgPtr = (Message *) clientData;
                    312: 
                    313:     if (msgPtr->string != NULL) {
                    314:        ckfree(msgPtr->string);
                    315:     }
                    316:     if (msgPtr->textVarName != NULL) {
                    317:        Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName,
                    318:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                    319:                MessageTextVarProc, (ClientData) msgPtr);
                    320:        ckfree(msgPtr->textVarName);
                    321:     }
                    322:     if (msgPtr->border != NULL) {
                    323:        Tk_Free3DBorder(msgPtr->border);
                    324:     }
                    325:     if (msgPtr->fontPtr != NULL) {
                    326:        Tk_FreeFontStruct(msgPtr->fontPtr);
                    327:     }
                    328:     if (msgPtr->fgColorPtr != NULL) {
                    329:        Tk_FreeColor(msgPtr->fgColorPtr);
                    330:     }
                    331:     if (msgPtr->textGC != None) {
                    332:        Tk_FreeGC(msgPtr->textGC);
                    333:     }
                    334:     if (msgPtr->cursor != None) {
                    335:        Tk_FreeCursor(msgPtr->cursor);
                    336:     }
                    337:     ckfree((char *) msgPtr);
                    338: }
                    339: 
                    340: /*
                    341:  *----------------------------------------------------------------------
                    342:  *
                    343:  * ConfigureMessage --
                    344:  *
                    345:  *     This procedure is called to process an argv/argc list, plus
                    346:  *     the Tk option database, in order to configure (or
                    347:  *     reconfigure) a message widget.
                    348:  *
                    349:  * Results:
                    350:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                    351:  *     returned, then interp->result contains an error message.
                    352:  *
                    353:  * Side effects:
                    354:  *     Configuration information, such as text string, colors, font,
                    355:  *     etc. get set for msgPtr;  old resources get freed, if there
                    356:  *     were any.
                    357:  *
                    358:  *----------------------------------------------------------------------
                    359:  */
                    360: 
                    361: static int
                    362: ConfigureMessage(interp, msgPtr, argc, argv, flags)
                    363:     Tcl_Interp *interp;                /* Used for error reporting. */
                    364:     register Message *msgPtr;  /* Information about widget;  may or may
                    365:                                 * not already have values for some fields. */
                    366:     int argc;                  /* Number of valid entries in argv. */
                    367:     char **argv;               /* Arguments. */
                    368:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
                    369: {
                    370:     XGCValues gcValues;
                    371:     GC newGC;
                    372: 
                    373:     /*
                    374:      * Eliminate any existing trace on a variable monitored by the message.
                    375:      */
                    376: 
                    377:     if (msgPtr->textVarName != NULL) {
                    378:        Tcl_UntraceVar(interp, msgPtr->textVarName, 
                    379:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                    380:                MessageTextVarProc, (ClientData) msgPtr);
                    381:     }
                    382: 
                    383:     if (Tk_ConfigureWidget(interp, msgPtr->tkwin, configSpecs,
                    384:            argc, argv, (char *) msgPtr, flags) != TCL_OK) {
                    385:        return TCL_ERROR;
                    386:     }
                    387: 
                    388:     /*
                    389:      * If the message is to display the value of a variable, then set up
                    390:      * a trace on the variable's value, create the variable if it doesn't
                    391:      * exist, and fetch its current value.
                    392:      */
                    393: 
                    394:     if (msgPtr->textVarName != NULL) {
                    395:        char *value;
                    396: 
                    397:        value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
                    398:        if (value == NULL) {
                    399:            Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
                    400:                    TCL_GLOBAL_ONLY);
                    401:        } else {
                    402:            if (msgPtr->string != NULL) {
                    403:                ckfree(msgPtr->string);
                    404:            }
                    405:            msgPtr->string = ckalloc((unsigned) (strlen(value) + 1));
                    406:            strcpy(msgPtr->string, value);
                    407:        }
                    408:        Tcl_TraceVar(interp, msgPtr->textVarName,
                    409:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                    410:                MessageTextVarProc, (ClientData) msgPtr);
                    411:     }
                    412: 
                    413:     /*
                    414:      * A few other options need special processing, such as setting
                    415:      * the background from a 3-D border or handling special defaults
                    416:      * that couldn't be specified to Tk_ConfigureWidget.
                    417:      */
                    418: 
                    419:     msgPtr->numChars = strlen(msgPtr->string);
                    420: 
                    421:     Tk_SetBackgroundFromBorder(msgPtr->tkwin, msgPtr->border);
                    422: 
                    423:     gcValues.font = msgPtr->fontPtr->fid;
                    424:     gcValues.foreground = msgPtr->fgColorPtr->pixel;
                    425:     newGC = Tk_GetGC(msgPtr->tkwin, GCForeground|GCFont,
                    426:            &gcValues);
                    427:     if (msgPtr->textGC != None) {
                    428:        Tk_FreeGC(msgPtr->textGC);
                    429:     }
                    430:     msgPtr->textGC = newGC;
                    431: 
                    432:     if (msgPtr->padX == -1) {
                    433:        msgPtr->padX = msgPtr->fontPtr->ascent/2;
                    434:     }
                    435: 
                    436:     if (msgPtr->padY == -1) {
                    437:        msgPtr->padY = msgPtr->fontPtr->ascent/4;
                    438:     }
                    439: 
                    440:     if (msgPtr->justify == TK_JUSTIFY_FILL) {
                    441:        interp->result = "can't use \"fill\" justify style in messages";
                    442:        return TCL_ERROR;
                    443:     }
                    444: 
                    445:     /*
                    446:      * Recompute the desired geometry for the window, and arrange for
                    447:      * the window to be redisplayed.
                    448:      */
                    449: 
                    450:     ComputeMessageGeometry(msgPtr);
                    451:     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
                    452:            && !(msgPtr->flags & REDRAW_PENDING)) {
                    453:        Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
                    454:        msgPtr->flags |= REDRAW_PENDING|CLEAR_NEEDED;
                    455:     }
                    456: 
                    457:     return TCL_OK;
                    458: }
                    459: 
                    460: /*
                    461:  *--------------------------------------------------------------
                    462:  *
                    463:  * ComputeMessageGeometry --
                    464:  *
                    465:  *     Compute the desired geometry for a message window,
                    466:  *     taking into account the desired aspect ratio for the
                    467:  *     window.
                    468:  *
                    469:  * Results:
                    470:  *     None.
                    471:  *
                    472:  * Side effects:
                    473:  *     Tk_GeometryRequest is called to inform the geometry
                    474:  *     manager of the desired geometry for this window.
                    475:  *
                    476:  *--------------------------------------------------------------
                    477:  */
                    478: 
                    479: static void
                    480: ComputeMessageGeometry(msgPtr)
                    481:     register Message *msgPtr;  /* Information about window. */
                    482: {
                    483:     char *p;
                    484:     int width, inc, height, numLines;
                    485:     int thisWidth, maxWidth;
                    486:     int aspect, lowerBound, upperBound;
                    487: 
                    488:     /*
                    489:      * Compute acceptable bounds for the final aspect ratio.
                    490:      */
                    491:     aspect = msgPtr->aspect/10;
                    492:     if (aspect < 5) {
                    493:        aspect = 5;
                    494:     }
                    495:     lowerBound = msgPtr->aspect - aspect;
                    496:     upperBound = msgPtr->aspect + aspect;
                    497: 
                    498:     /*
                    499:      * Do the computation in multiple passes:  start off with
                    500:      * a very wide window, and compute its height.  Then change
                    501:      * the width and try again.  Reduce the size of the change
                    502:      * and iterate until dimensions are found that approximate
                    503:      * the desired aspect ratio.  Or, if the user gave an explicit
                    504:      * width then just use that.
                    505:      */
                    506: 
                    507:     if (msgPtr->width > 0) {
                    508:        width = msgPtr->width;
                    509:        inc = 0;
                    510:     } else {
                    511:        width = WidthOfScreen(Tk_Screen(msgPtr->tkwin))/2;
                    512:        inc = width/2;
                    513:     }
                    514:     for ( ; ; inc /= 2) {
                    515:        maxWidth = 0;
                    516:        for (numLines = 1, p = msgPtr->string; ; numLines++)  {
                    517:            if (*p == '\n') {
                    518:                p++;
                    519:                continue;
                    520:            }
                    521:            p += TkMeasureChars(msgPtr->fontPtr, p,
                    522:                    msgPtr->numChars - (p - msgPtr->string), 0, width,
                    523:                    TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
                    524:            if (thisWidth > maxWidth) {
                    525:                maxWidth = thisWidth;
                    526:            }
                    527:            if (*p == 0) {
                    528:                break;
                    529:            }
                    530: 
                    531:            /*
                    532:             * Skip spaces and tabs at the beginning of a line, unless
                    533:             * they follow a user-requested newline.
                    534:             */
                    535: 
                    536:            while (isspace(*p)) {
                    537:                if (*p == '\n') {
                    538:                    p++;
                    539:                    break;
                    540:                }
                    541:                p++;
                    542:            }
                    543:        }
                    544: 
                    545:        height = numLines * (msgPtr->fontPtr->ascent
                    546:                + msgPtr->fontPtr->descent) + 2*msgPtr->borderWidth
                    547:                + 2*msgPtr->padY;
                    548:        if (inc <= 2) {
                    549:            break;
                    550:        }
                    551:        aspect = (100*(maxWidth + 2*msgPtr->borderWidth
                    552:                + 2*msgPtr->padX))/height;
                    553:        if (aspect < lowerBound) {
                    554:            width += inc;
                    555:        } else if (aspect > upperBound) {
                    556:            width -= inc;
                    557:        } else {
                    558:            break;
                    559:        }
                    560:     }
                    561:     msgPtr->lineLength = maxWidth;
                    562:     msgPtr->msgHeight = numLines * (msgPtr->fontPtr->ascent
                    563:                + msgPtr->fontPtr->descent);
                    564:     Tk_GeometryRequest(msgPtr->tkwin,
                    565:            maxWidth + 2*msgPtr->borderWidth + 2*msgPtr->padX, height);
                    566:     Tk_SetInternalBorder(msgPtr->tkwin, msgPtr->borderWidth);
                    567: }
                    568: 
                    569: /*
                    570:  *--------------------------------------------------------------
                    571:  *
                    572:  * DisplayMessage --
                    573:  *
                    574:  *     This procedure redraws the contents of a message window.
                    575:  *
                    576:  * Results:
                    577:  *     None.
                    578:  *
                    579:  * Side effects:
                    580:  *     Information appears on the screen.
                    581:  *
                    582:  *--------------------------------------------------------------
                    583:  */
                    584: 
                    585: static void
                    586: DisplayMessage(clientData)
                    587:     ClientData clientData;     /* Information about window. */
                    588: {
                    589:     register Message *msgPtr = (Message *) clientData;
                    590:     register Tk_Window tkwin = msgPtr->tkwin;
                    591:     char *p;
                    592:     int x, y, lineLength, numChars, charsLeft;
                    593: 
                    594:     msgPtr->flags &= ~REDRAW_PENDING;
                    595:     if ((msgPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
                    596:        return;
                    597:     }
                    598:     if (msgPtr->flags & CLEAR_NEEDED) {
                    599:        XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
                    600:        msgPtr->flags &= ~CLEAR_NEEDED;
                    601:     }
                    602: 
                    603:     /*
                    604:      * Compute starting y-location for message based on message size
                    605:      * and anchor option.
                    606:      */
                    607: 
                    608:     switch (msgPtr->anchor) {
                    609:        case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
                    610:            y = msgPtr->borderWidth + msgPtr->padY;
                    611:            break;
                    612:        case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
                    613:            y = (Tk_Height(tkwin) - msgPtr->msgHeight)/2;
                    614:            break;
                    615:        default:
                    616:            y = Tk_Height(tkwin) - msgPtr->borderWidth - msgPtr->padY
                    617:                    - msgPtr->msgHeight;
                    618:            break;
                    619:     }
                    620:     y += msgPtr->fontPtr->ascent;
                    621: 
                    622:     /*
                    623:      * Work through the string to display one line at a time.
                    624:      * Display each line in three steps.  First compute the
                    625:      * line's width, then figure out where to display the
                    626:      * line to justify it properly, then display the line.
                    627:      */
                    628: 
                    629:     for (p = msgPtr->string, charsLeft = msgPtr->numChars; *p != 0;
                    630:            y += msgPtr->fontPtr->ascent + msgPtr->fontPtr->descent) {
                    631:        if (*p == '\n') {
                    632:            p++;
                    633:            charsLeft--;
                    634:            continue;
                    635:        }
                    636:        numChars = TkMeasureChars(msgPtr->fontPtr, p, charsLeft, 0,
                    637:                msgPtr->lineLength, TK_WHOLE_WORDS|TK_AT_LEAST_ONE,
                    638:                &lineLength);
                    639:        switch (msgPtr->anchor) {
                    640:            case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
                    641:                x = msgPtr->borderWidth + msgPtr->padX;
                    642:                break;
                    643:            case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
                    644:                x = (Tk_Width(tkwin) - msgPtr->lineLength)/2;
                    645:                break;
                    646:            default:
                    647:                x = Tk_Width(tkwin) - msgPtr->borderWidth - msgPtr->padX
                    648:                        - msgPtr->lineLength;
                    649:                break;
                    650:        }
                    651:        if (msgPtr->justify == TK_JUSTIFY_CENTER) {
                    652:            x += (msgPtr->lineLength - lineLength)/2;
                    653:        } else if (msgPtr->justify == TK_JUSTIFY_RIGHT) {
                    654:            x += msgPtr->lineLength - lineLength;
                    655:        }
                    656:        TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin),
                    657:                msgPtr->textGC, msgPtr->fontPtr, p, numChars, x, y, 0);
                    658:        p += numChars;
                    659:        charsLeft -= numChars;
                    660: 
                    661:        /*
                    662:         * Skip blanks at the beginning of a line, unless they follow
                    663:         * a user-requested newline.
                    664:         */
                    665: 
                    666:        while (isspace(*p)) {
                    667:            charsLeft--;
                    668:            if (*p == '\n') {
                    669:                p++;
                    670:                break;
                    671:            }
                    672:            p++;
                    673:        }
                    674:     }
                    675: 
                    676:     if (msgPtr->relief != TK_RELIEF_FLAT) {
                    677:        Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                    678:                msgPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
                    679:                msgPtr->borderWidth, msgPtr->relief);
                    680:     }
                    681: }
                    682: 
                    683: /*
                    684:  *--------------------------------------------------------------
                    685:  *
                    686:  * MessageEventProc --
                    687:  *
                    688:  *     This procedure is invoked by the Tk dispatcher for various
                    689:  *     events on messages.
                    690:  *
                    691:  * Results:
                    692:  *     None.
                    693:  *
                    694:  * Side effects:
                    695:  *     When the window gets deleted, internal structures get
                    696:  *     cleaned up.  When it gets exposed, it is redisplayed.
                    697:  *
                    698:  *--------------------------------------------------------------
                    699:  */
                    700: 
                    701: static void
                    702: MessageEventProc(clientData, eventPtr)
                    703:     ClientData clientData;     /* Information about window. */
                    704:     XEvent *eventPtr;          /* Information about event. */
                    705: {
                    706:     Message *msgPtr = (Message *) clientData;
                    707: 
                    708:     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
                    709:        if ((msgPtr->tkwin != NULL) && !(msgPtr->flags & REDRAW_PENDING)) {
                    710:            Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
                    711:            msgPtr->flags |= REDRAW_PENDING;
                    712:        }
                    713:     } else if (eventPtr->type == DestroyNotify) {
                    714:        Tcl_DeleteCommand(msgPtr->interp, Tk_PathName(msgPtr->tkwin));
                    715:        msgPtr->tkwin = NULL;
                    716:        if (msgPtr->flags & REDRAW_PENDING) {
                    717:            Tk_CancelIdleCall(DisplayMessage, (ClientData) msgPtr);
                    718:        }
                    719:        Tk_EventuallyFree((ClientData) msgPtr, DestroyMessage);
                    720:     }
                    721: }
                    722: 
                    723: /*
                    724:  *--------------------------------------------------------------
                    725:  *
                    726:  * MessageTextVarProc --
                    727:  *
                    728:  *     This procedure is invoked when someone changes the variable
                    729:  *     whose contents are to be displayed in a message.
                    730:  *
                    731:  * Results:
                    732:  *     NULL is always returned.
                    733:  *
                    734:  * Side effects:
                    735:  *     The text displayed in the message will change to match the
                    736:  *     variable.
                    737:  *
                    738:  *--------------------------------------------------------------
                    739:  */
                    740: 
                    741:        /* ARGSUSED */
                    742: static char *
                    743: MessageTextVarProc(clientData, interp, name1, name2, flags)
                    744:     ClientData clientData;     /* Information about message. */
                    745:     Tcl_Interp *interp;                /* Interpreter containing variable. */
                    746:     char *name1;               /* Name of variable. */
                    747:     char *name2;               /* Second part of variable name. */
                    748:     int flags;                 /* Information about what happened. */
                    749: {
                    750:     register Message *msgPtr = (Message *) clientData;
                    751:     char *value;
                    752: 
                    753:     /*
                    754:      * If the variable is unset, then immediately recreate it unless
                    755:      * the whole interpreter is going away.
                    756:      */
                    757: 
                    758:     if (flags & TCL_TRACE_UNSETS) {
                    759:        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
                    760:            Tcl_SetVar2(interp, name1, name2, msgPtr->string,
                    761:                    flags & TCL_GLOBAL_ONLY);
                    762:            Tcl_TraceVar2(interp, name1, name2,
                    763:                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                    764:                    MessageTextVarProc, clientData);
                    765:        }
                    766:        return (char *) NULL;
                    767:     }
                    768: 
                    769:     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
                    770:     if (value == NULL) {
                    771:        value = "";
                    772:     }
                    773:     if (msgPtr->string != NULL) {
                    774:        ckfree(msgPtr->string);
                    775:     }
                    776:     msgPtr->numChars = strlen(value);
                    777:     msgPtr->string = ckalloc((unsigned) (msgPtr->numChars + 1));
                    778:     strcpy(msgPtr->string, value);
                    779:     ComputeMessageGeometry(msgPtr);
                    780: 
                    781:     msgPtr->flags |= CLEAR_NEEDED;
                    782:     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
                    783:            && !(msgPtr->flags & REDRAW_PENDING)) {
                    784:        Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
                    785:        msgPtr->flags |= REDRAW_PENDING;
                    786:     }
                    787:     return (char *) NULL;
                    788: }

unix.superglobalmegacorp.com

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