Annotation of micropolis/src/tk/tktxdisp.c, revision 1.1

1.1     ! root        1: /* 
        !             2:  * tkTextDisp.c --
        !             3:  *
        !             4:  *     This module provides facilities to display text widgets.  It is
        !             5:  *     the only place where information is kept about the screen layout
        !             6:  *     of text widgets.
        !             7:  *
        !             8:  * Copyright 1992 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/tkTextDisp.c,v 1.20 92/08/24 09:24:18 ouster Exp $ SPRITE (Berkeley)";
        !            20: #endif
        !            21: 
        !            22: #include "tkconfig.h"
        !            23: #include "tkint.h"
        !            24: #include "tktext.h"
        !            25: 
        !            26: #include <assert.h>
        !            27: 
        !            28: /*
        !            29:  * The following structure describes how to display a range of characters.
        !            30:  * The information is generated by scanning all of the tags associated
        !            31:  * with the characters and combining that with default information for
        !            32:  * the overall widget.  These structures form the hash keys for
        !            33:  * dInfoPtr->styleTable.
        !            34:  */
        !            35: 
        !            36: typedef struct StyleValues {
        !            37:     Tk_3DBorder border;                /* Used for drawing background under text.
        !            38:                                 * NULL means use widget background. */
        !            39:     int borderWidth;           /* Width of 3-D border for background. */
        !            40:     int relief;                        /* 3-D relief for background. */
        !            41:     Pixmap bgStipple;          /* Stipple bitmap for background.  None
        !            42:                                 * means draw solid. */
        !            43:     XColor *fgColor;           /* Foreground color for text. */
        !            44:     XFontStruct *fontPtr;      /* Font for displaying text. */
        !            45:     Pixmap fgStipple;          /* Stipple bitmap for text and other
        !            46:                                 * foreground stuff.   None means draw
        !            47:                                 * solid.*/
        !            48:     int underline;             /* Non-zero means draw underline underneath
        !            49:                                 * text. */
        !            50: } StyleValues;
        !            51: 
        !            52: /*
        !            53:  * The following structure extends the StyleValues structure above with
        !            54:  * graphics contexts used to actually draw the characters.  The entries
        !            55:  * in dInfoPtr->styleTable point to structures of this type.
        !            56:  */
        !            57: 
        !            58: typedef struct Style {
        !            59:     int refCount;              /* Number of times this structure is
        !            60:                                 * referenced in Chunks. */
        !            61:     GC bgGC;                   /* Graphics context for background.  None
        !            62:                                 * unless background is stippled. */
        !            63:     GC fgGC;                   /* Graphics context for foreground. */
        !            64:     StyleValues *sValuePtr;    /* Raw information from which GCs were
        !            65:                                 * derived. */
        !            66:     Tcl_HashEntry *hPtr;       /* Pointer to entry in styleTable.  Used
        !            67:                                 * to delete entry. */
        !            68: } Style;
        !            69: 
        !            70: /*
        !            71:  * The following structure describes a range of characters, all on the
        !            72:  * same line of the display (which also means the same line of the text
        !            73:  * widget) and all having the same display attributes.
        !            74:  */
        !            75: 
        !            76: typedef struct Chunk {
        !            77:     char *text;                        /* Characters to display. */
        !            78:     int numChars;              /* Number of characters to display. */
        !            79:     Style *stylePtr;           /* Style information used to display
        !            80:                                 * characters. */
        !            81:     int x;                     /* X-coordinate of pixel at which to display
        !            82:                                 * the characters. */
        !            83:     struct Chunk *nextPtr;     /* Next in list of all chunks displayed on the
        !            84:                                 * same display line. */
        !            85: } Chunk;
        !            86: 
        !            87: /*
        !            88:  * The following structure describes one line of the display, which may
        !            89:  * be either part or all of one line of the text.
        !            90:  */
        !            91: 
        !            92: typedef struct DLine {
        !            93:     TkTextLine *linePtr;       /* Pointer to structure in B-tree that
        !            94:                                 * contains characters displayed in this
        !            95:                                 * line. */
        !            96:     int y;                     /* Y-position at which line is supposed to
        !            97:                                 * be drawn (topmost pixel of rectangular
        !            98:                                 * area occupied by line). */
        !            99:     int oldY;                  /* Y-position at which line currently
        !           100:                                 * appears on display.  -1 means line isn't
        !           101:                                 * currently visible on display.  This is
        !           102:                                 * used to move lines by scrolling rather
        !           103:                                 * than re-drawing. */
        !           104:     int height;                        /* Height of line, in pixels. */
        !           105:     int baseline;              /* Offset of text baseline from y. */
        !           106:     Chunk *chunkPtr;           /* Pointer to first chunk in list of all
        !           107:                                 * of those that are displayed on this
        !           108:                                 * line of the screen. */
        !           109:     struct DLine *nextPtr;     /* Next in list of all display lines for
        !           110:                                 * this window.   The list is sorted in
        !           111:                                 * order from top to bottom.  Note:  the
        !           112:                                 * next DLine doesn't always correspond
        !           113:                                 * to the next line of text:  (a) can have
        !           114:                                 * multiple DLines for one text line, and
        !           115:                                 * (b) can have gaps where DLine's have been
        !           116:                                 * deleted because they're out of date. */
        !           117: } DLine;
        !           118: 
        !           119: /*
        !           120:  * Overall display information for a text widget:
        !           121:  */
        !           122: 
        !           123: typedef struct DInfo {
        !           124:     Tcl_HashTable styleTable;  /* Hash table that maps from StyleValues to
        !           125:                                 * Styles for this widget. */
        !           126:     DLine *dLinePtr;           /* First in list of all display lines for
        !           127:                                 * this widget, in order from top to bottom. */
        !           128:     GC copyGC;                 /* Graphics context for copying from off-
        !           129:                                 * screen pixmaps onto screen. */
        !           130:     GC scrollGC;               /* Graphics context for copying from one place
        !           131:                                 * in the window to another (scrolling):
        !           132:                                 * differs from copyGC in that we need to get
        !           133:                                 * GraphicsExpose events. */
        !           134:     int x;                     /* First x-coordinate that may be used for
        !           135:                                 * actually displaying line information.
        !           136:                                 * Leaves space for border, etc. */
        !           137:     int y;                     /* First y-coordinate that may be used for
        !           138:                                 * actually displaying line information.
        !           139:                                 * Leaves space for border, etc. */
        !           140:     int maxX;                  /* First x-coordinate to right of available
        !           141:                                 * space for displaying lines. */
        !           142:     int maxY;                  /* First y-coordinate to bottom of available
        !           143:                                 * space for displaying lines. */
        !           144:     int topOfEof;              /* Top-most pixel (lowest y-value) that has
        !           145:                                 * been drawn in the appropriate fashion for
        !           146:                                 * the portion of the window after the last
        !           147:                                 * line of the text.  This field is used to
        !           148:                                 * figure out when to redraw part or all of
        !           149:                                 * the eof field. */
        !           150:     int flags;                 /* Various flag values:  see below for
        !           151:                                 * definitions. */
        !           152: } DInfo;
        !           153: 
        !           154: /*
        !           155:  * Flag values for DInfo structures:
        !           156:  *
        !           157:  * DINFO_OUT_OF_DATE:          Non-zero means that the DLine structures
        !           158:  *                             for this window are partially or completely
        !           159:  *                             out of date and need to be recomputed.
        !           160:  * REDRAW_PENDING:             Means that a when-idle handler has been
        !           161:  *                             scheduled to update the display.
        !           162:  * REDRAW_BORDERS:             Means window border or pad area has
        !           163:  *                             potentially been damaged and must be redrawn.
        !           164:  * REPICK_NEEDED:              1 means that the widget has been modified
        !           165:  *                             in a way that could change the current
        !           166:  *                             character (a different character might be
        !           167:  *                             under the mouse cursor now).  Need to
        !           168:  *                             recompute the current character before
        !           169:  *                             the next redisplay.
        !           170:  */
        !           171: 
        !           172: #define DINFO_OUT_OF_DATE      1
        !           173: #define REDRAW_PENDING         2
        !           174: #define REDRAW_BORDERS         4
        !           175: #define REPICK_NEEDED          8
        !           176: 
        !           177: /*
        !           178:  * Structures of the type defined below are used to keep track of
        !           179:  * tags while scanning through the text to create DLine structures.
        !           180:  */
        !           181: 
        !           182: typedef struct TagInfo {
        !           183:     int numTags;               /* Number of tags currently active (the first
        !           184:                                 * entries at *tagPtr). */
        !           185:     int arraySize;             /* Total number of entries at *tagPtr.  We
        !           186:                                 * over-allocate the array to avoid continual
        !           187:                                 * reallocations. */
        !           188:     TkTextTag **tagPtrs;       /* Pointer to array of pointers to active tags.
        !           189:                                 * Array has space for arraySize tags, and
        !           190:                                 * the first numTags are slots identify the
        !           191:                                 * active tags. Malloc'ed (but may be NULL). */
        !           192:     TkTextSearch search;       /* Used to scan for tag transitions.  Current
        !           193:                                 * state identifies next tag transition. */
        !           194: } TagInfo;
        !           195: 
        !           196: /*
        !           197:  * The following counters keep statistics about redisplay that can be
        !           198:  * checked to see how clever this code is at reducing redisplays.
        !           199:  */
        !           200: 
        !           201: static int numRedisplays;      /* Number of calls to DisplayText. */
        !           202: static int linesRedrawn;       /* Number of calls to DisplayDLine. */
        !           203: static int numCopies;          /* Number of calls to XCopyArea to copy part
        !           204:                                 * of the screen. */
        !           205: static int damagedCopies;      /* Number of times that XCopyAreas didn't
        !           206:                                 * completely work because some of the source
        !           207:                                 * information was damaged. */
        !           208: static int TextUpdateTime = 100; // Added by Don.
        !           209: 
        !           210: /*
        !           211:  * Forward declarations for procedures defined later in this file:
        !           212:  */
        !           213: 
        !           214: static void            ComputeStyleValues _ANSI_ARGS_((TkText *textPtr,
        !           215:                            int numTags, TkTextTag **tagPtr,
        !           216:                            StyleValues *sValuePtr));
        !           217: static void            DisplayDLine _ANSI_ARGS_((TkText *textPtr,
        !           218:                            DLine *dlPtr, Pixmap pixmap));
        !           219: static void            DisplayText _ANSI_ARGS_((ClientData clientData));
        !           220: static DLine *         FindDLine _ANSI_ARGS_((DLine *dlPtr, int line));
        !           221: static void            FreeDLines _ANSI_ARGS_((TkText *textPtr,
        !           222:                            DLine *firstPtr, DLine *lastPtr, int unlink));
        !           223: static void            FreeStyle _ANSI_ARGS_((Style *stylePtr));
        !           224: static Style *         GetStyle _ANSI_ARGS_((TkText *textPtr,
        !           225:                            StyleValues *sValuePtr));
        !           226: static DLine *         LayoutLine _ANSI_ARGS_((TkText *textPtr, int line,
        !           227:                            TkTextLine *linePtr, TagInfo *tInfoPtr));
        !           228: static void            ToggleTag _ANSI_ARGS_((TagInfo *tInfoPtr,
        !           229:                            TkTextTag *tagPtr));
        !           230: static void            UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
        !           231: 
        !           232: /*
        !           233:  *----------------------------------------------------------------------
        !           234:  *
        !           235:  * TkTextCreateDInfo --
        !           236:  *
        !           237:  *     This procedure is called when a new text widget is created.
        !           238:  *     Its job is to set up display-related information for the widget.
        !           239:  *
        !           240:  * Results:
        !           241:  *     None.
        !           242:  *
        !           243:  * Side effects:
        !           244:  *     A DInfo data structure is allocated and initialized and attached
        !           245:  *     to textPtr.
        !           246:  *
        !           247:  *----------------------------------------------------------------------
        !           248:  */
        !           249: 
        !           250: void
        !           251: TkTextCreateDInfo(textPtr)
        !           252:     TkText *textPtr;           /* Overall information for text widget. */
        !           253: {
        !           254:     register DInfo *dInfoPtr;
        !           255:     XGCValues gcValues;
        !           256: 
        !           257:     dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
        !           258:     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
        !           259:     dInfoPtr->dLinePtr = NULL;
        !           260:     gcValues.graphics_exposures = False;
        !           261:     dInfoPtr->copyGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
        !           262:     gcValues.graphics_exposures = True;
        !           263:     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
        !           264:            &gcValues);
        !           265:     dInfoPtr->topOfEof = 0;
        !           266:     dInfoPtr->flags = DINFO_OUT_OF_DATE;
        !           267:     textPtr->dInfoPtr = dInfoPtr;
        !           268: }
        !           269: 
        !           270: /*
        !           271:  *----------------------------------------------------------------------
        !           272:  *
        !           273:  * TkTextFreeDInfo --
        !           274:  *
        !           275:  *     This procedure is called to free up all of the private display
        !           276:  *     information kept by this file for a text widget.
        !           277:  *
        !           278:  * Results:
        !           279:  *     None.
        !           280:  *
        !           281:  * Side effects:
        !           282:  *     Lots of resources get freed.
        !           283:  *
        !           284:  *----------------------------------------------------------------------
        !           285:  */
        !           286: 
        !           287: void
        !           288: TkTextFreeDInfo(textPtr)
        !           289:     TkText *textPtr;           /* Overall information for text widget. */
        !           290: {
        !           291:     register DInfo *dInfoPtr = textPtr->dInfoPtr;
        !           292: 
        !           293:     /*
        !           294:      * Be careful to free up styleTable *after* freeing up all the
        !           295:      * DLines, so that the hash table is still intact to free up the
        !           296:      * style-related information from the lines.  Once the lines are
        !           297:      * all free then styleTable will be empty.
        !           298:      */
        !           299: 
        !           300:     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
        !           301:     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
        !           302:     Tk_FreeGC(dInfoPtr->copyGC);
        !           303:     Tk_FreeGC(dInfoPtr->scrollGC);
        !           304:     if (dInfoPtr->flags & REDRAW_PENDING) {
        !           305: //     Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
        !           306:         assert(textPtr->updateTimerToken != NULL);
        !           307:        if (textPtr->updateTimerToken != NULL) {
        !           308:            Tk_DeleteTimerHandler(textPtr->updateTimerToken);
        !           309:            textPtr->updateTimerToken = NULL;
        !           310:        }
        !           311:     }
        !           312:     ckfree((char *) dInfoPtr);
        !           313: }
        !           314: 
        !           315: /*
        !           316:  *----------------------------------------------------------------------
        !           317:  *
        !           318:  * GetStyle --
        !           319:  *
        !           320:  *     This procedure creates graphics contexts needed to display
        !           321:  *     text in a particular style, determined by "sValuePtr".  It
        !           322:  *     attempts to share style information as much as possible.
        !           323:  *
        !           324:  * Results:
        !           325:  *     The return value is a pointer to a Style structure that
        !           326:  *     corresponds to *sValuePtr.
        !           327:  *
        !           328:  * Side effects:
        !           329:  *     A new entry may be created in the style table for the widget.
        !           330:  *
        !           331:  *----------------------------------------------------------------------
        !           332:  */
        !           333: 
        !           334: static Style *
        !           335: GetStyle(textPtr, sValuePtr)
        !           336:     TkText *textPtr;           /* Overall information about text widget. */
        !           337:     StyleValues *sValuePtr;    /* Information about desired style. */
        !           338: {
        !           339:     Style *stylePtr;
        !           340:     Tcl_HashEntry *hPtr;
        !           341:     int new;
        !           342:     XGCValues gcValues;
        !           343:     unsigned long mask;
        !           344: 
        !           345:     /*
        !           346:      * Use an existing style if there's one around that matches.
        !           347:      */
        !           348: 
        !           349:     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
        !           350:            (char *) sValuePtr, &new);
        !           351:     if (!new) {
        !           352:        stylePtr = (Style *) Tcl_GetHashValue(hPtr);
        !           353:        stylePtr->refCount++;
        !           354:        return stylePtr;
        !           355:     }
        !           356: 
        !           357:     /*
        !           358:      * No existing style matched.  Make a new one.
        !           359:      */
        !           360: 
        !           361:     stylePtr = (Style *) ckalloc(sizeof(Style));
        !           362:     stylePtr->refCount = 1;
        !           363:     if ((sValuePtr->border != NULL) && (sValuePtr->bgStipple != None)) {
        !           364:        gcValues.foreground = Tk_3DBorderColor(sValuePtr->border)->pixel;
        !           365:        gcValues.stipple = sValuePtr->bgStipple;
        !           366:        gcValues.fill_style = FillStippled;
        !           367:        stylePtr->bgGC = Tk_GetGC(textPtr->tkwin,
        !           368:                GCForeground|GCStipple|GCFillStyle, &gcValues);
        !           369:     } else {
        !           370:        stylePtr->bgGC = None;
        !           371:     }
        !           372:     mask = GCForeground|GCFont;
        !           373:     gcValues.foreground = sValuePtr->fgColor->pixel;
        !           374:     gcValues.font = sValuePtr->fontPtr->fid;
        !           375:     if (sValuePtr->fgStipple != None) {
        !           376:        gcValues.stipple = sValuePtr->fgStipple;
        !           377:        gcValues.fill_style = FillStippled;
        !           378:        mask |= GCStipple|GCFillStyle;
        !           379:     }
        !           380:     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
        !           381:     stylePtr->sValuePtr = (StyleValues *)
        !           382:            Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
        !           383:     stylePtr->hPtr = hPtr;
        !           384:     Tcl_SetHashValue(hPtr, stylePtr);
        !           385:     return stylePtr;
        !           386: }
        !           387: 
        !           388: /*
        !           389:  *----------------------------------------------------------------------
        !           390:  *
        !           391:  * FreeStyle --
        !           392:  *
        !           393:  *     This procedure is called when a Style structure is no longer
        !           394:  *     needed.  It decrements the reference count and frees up the
        !           395:  *     space for the style structure if the reference count is 0.
        !           396:  *
        !           397:  * Results:
        !           398:  *     None.
        !           399:  *
        !           400:  * Side effects:
        !           401:  *     The storage and other resources associated with the style
        !           402:  *     are freed up if no-one's still using it.
        !           403:  *
        !           404:  *----------------------------------------------------------------------
        !           405:  */
        !           406: 
        !           407: static void
        !           408: FreeStyle(stylePtr)
        !           409:     register Style *stylePtr;  /* Information about style to be freed. */
        !           410: 
        !           411: {
        !           412:     stylePtr->refCount--;
        !           413:     if (stylePtr->refCount == 0) {
        !           414:        if (stylePtr->bgGC != None) {
        !           415:            Tk_FreeGC(stylePtr->bgGC);
        !           416:        }
        !           417:        Tk_FreeGC(stylePtr->fgGC);
        !           418:        Tcl_DeleteHashEntry(stylePtr->hPtr);
        !           419:        ckfree((char *) stylePtr);
        !           420:     }
        !           421: }
        !           422: 
        !           423: /*
        !           424:  *----------------------------------------------------------------------
        !           425:  *
        !           426:  * ComputeStyleValues --
        !           427:  *
        !           428:  *     Given a list of tags that apply at a particular point, compute
        !           429:  *     the StyleValues that correspond to that set of tags.
        !           430:  *
        !           431:  * Results:
        !           432:  *     All of the fields of *sValuePtr get filled in to hold the
        !           433:  *     appropriate display information for the given set of tags
        !           434:  *     in the given widget.
        !           435:  *
        !           436:  * Side effects:
        !           437:  *     None.
        !           438:  *
        !           439:  *----------------------------------------------------------------------
        !           440:  */
        !           441: 
        !           442: static void
        !           443: ComputeStyleValues(textPtr, numTags, tagPtrPtr, sValuePtr)
        !           444:     TkText *textPtr;                   /* Overall information for widget. */
        !           445:     int numTags;                       /* Number of tags at *tagPtr. */
        !           446:     register TkTextTag **tagPtrPtr;    /* Pointer to array of tag pointers. */
        !           447:     register StyleValues *sValuePtr;   /* Pointer to structure to fill in. */
        !           448: {
        !           449:     register TkTextTag *tagPtr;
        !           450: 
        !           451:     /*
        !           452:      * The variables below keep track of the highest-priority specification
        !           453:      * that has occurred for each of the various fields of the StyleValues.
        !           454:      */
        !           455: 
        !           456:     int borderPrio, bgStipplePrio;
        !           457:     int fgPrio, fontPrio, fgStipplePrio;
        !           458: 
        !           459:     borderPrio = bgStipplePrio = -1;
        !           460:     fgPrio = fontPrio = fgStipplePrio = -1;
        !           461:     memset((VOID *) sValuePtr, 0, sizeof(StyleValues));
        !           462:     sValuePtr->fgColor = textPtr->fgColor;
        !           463:     sValuePtr->fontPtr = textPtr->fontPtr;
        !           464: 
        !           465:     /*
        !           466:      * Scan through all of the tags, updating the StyleValues to hold
        !           467:      * the highest-priority information.
        !           468:      */
        !           469: 
        !           470:     for ( ; numTags > 0; tagPtrPtr++, numTags--) {
        !           471:        tagPtr = *tagPtrPtr;
        !           472:        if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
        !           473:            sValuePtr->border = tagPtr->border;
        !           474:            sValuePtr->borderWidth = tagPtr->borderWidth;
        !           475:            sValuePtr->relief = tagPtr->relief;
        !           476:            borderPrio = tagPtr->priority;
        !           477:        }
        !           478:        if ((tagPtr->bgStipple != None)
        !           479:                && (tagPtr->priority > bgStipplePrio)) {
        !           480:            sValuePtr->bgStipple = tagPtr->bgStipple;
        !           481:            bgStipplePrio = tagPtr->priority;
        !           482:        }
        !           483:        if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
        !           484:            sValuePtr->fgColor = tagPtr->fgColor;
        !           485:            fgPrio = tagPtr->priority;
        !           486:        }
        !           487:        if ((tagPtr->fontPtr != None) && (tagPtr->priority > fontPrio)) {
        !           488:            sValuePtr->fontPtr = tagPtr->fontPtr;
        !           489:            fontPrio = tagPtr->priority;
        !           490:        }
        !           491:        if ((tagPtr->fgStipple != None)
        !           492:                && (tagPtr->priority > fgStipplePrio)) {
        !           493:            sValuePtr->fgStipple = tagPtr->fgStipple;
        !           494:            fgStipplePrio = tagPtr->priority;
        !           495:        }
        !           496:        if (tagPtr->underline) {
        !           497:            sValuePtr->underline = 1;
        !           498:        }
        !           499:     }
        !           500: }
        !           501: 
        !           502: /*
        !           503:  *----------------------------------------------------------------------
        !           504:  *
        !           505:  * LayoutLine --
        !           506:  *
        !           507:  *     This procedure generates a linked list of one or more DLine
        !           508:  *     structures, which describe how to display everything in one
        !           509:  *     line of the text.
        !           510:  *
        !           511:  * Results:
        !           512:  *     The return value is a pointer to one or more DLine structures
        !           513:  *     linked into a linked list.  The structures are completely filled
        !           514:  *     in except for the y field, which the caller must supply.  Also,
        !           515:  *     the information at *tInfoPtr gets updated to refer to the state
        !           516:  *     just after the last character of the line.
        !           517:  *
        !           518:  * Side effects:
        !           519:  *     None.
        !           520:  *
        !           521:  *----------------------------------------------------------------------
        !           522:  */
        !           523: 
        !           524: static DLine *
        !           525: LayoutLine(textPtr, line, linePtr, tInfoPtr)
        !           526:     TkText *textPtr;           /* Overall information about text widget. */
        !           527:     int line;                  /* Index of line to layout. */
        !           528:     TkTextLine *linePtr;       /* Line to layout (corresponds to line). */
        !           529:     TagInfo *tInfoPtr;         /* Information to help keep track of tags.
        !           530:                                 * Caller must have initialized to correspond
        !           531:                                 * to state just before start of line. */
        !           532: {
        !           533:     DLine *firstLinePtr;
        !           534:     DLine *lastLinePtr = NULL; /* Initializations needed only to stop */
        !           535:     Chunk *lastChunkPtr = NULL;        /* compiler warnings. */
        !           536:     register DLine *dlPtr;
        !           537:     register Chunk *chunkPtr;
        !           538:     StyleValues styleValues;
        !           539:     int ch, charsThatFit, ascent, descent, x, maxX;
        !           540: 
        !           541:     firstLinePtr = NULL;
        !           542: 
        !           543:     /*
        !           544:      * Each iteration of the loop below creates one DLine structure.
        !           545:      */
        !           546: 
        !           547:     ch = 0;
        !           548:     while (1) {
        !           549: 
        !           550:        /*
        !           551:         * Create and initialize a new DLine structure.
        !           552:         */
        !           553: 
        !           554:        dlPtr = (DLine *) ckalloc(sizeof(DLine));
        !           555:        dlPtr->linePtr = linePtr;
        !           556:        dlPtr->y = 0;
        !           557:        dlPtr->oldY = -1;
        !           558:        dlPtr->chunkPtr = NULL;
        !           559:        dlPtr->nextPtr = NULL;
        !           560:        if (firstLinePtr == NULL) {
        !           561:            firstLinePtr = dlPtr;
        !           562:        } else {
        !           563:            lastLinePtr->nextPtr = dlPtr;
        !           564:        }
        !           565:        lastLinePtr = dlPtr;
        !           566: 
        !           567:        /*
        !           568:         * Each iteration of the loop below creates one Chunk for the
        !           569:         * new display line.
        !           570:         */
        !           571: 
        !           572:        x = textPtr->dInfoPtr->x;
        !           573:        maxX = textPtr->dInfoPtr->maxX;
        !           574:        ascent = descent = 0;
        !           575:        while (x < maxX) {
        !           576:            chunkPtr = (Chunk *) ckalloc(sizeof(Chunk));
        !           577:            chunkPtr->numChars = linePtr->numBytes - ch;
        !           578:            chunkPtr->text = linePtr->bytes + ch;
        !           579:            chunkPtr->x = x;
        !           580:            chunkPtr->nextPtr = NULL;
        !           581:            if (dlPtr->chunkPtr == NULL) {
        !           582:                dlPtr->chunkPtr = chunkPtr;
        !           583:            } else {
        !           584:                lastChunkPtr->nextPtr = chunkPtr;
        !           585:            }
        !           586:            lastChunkPtr = chunkPtr;
        !           587: 
        !           588:            /*
        !           589:             * Update the tag array to include any tag transitions up
        !           590:             * through the current position, then find the next position
        !           591:             * with a transition on a tag that impacts the way things are
        !           592:             * displayed.
        !           593:             */
        !           594: 
        !           595:            while (1) {
        !           596:                int affectsDisplay;
        !           597:                TkTextTag *tagPtr;
        !           598: 
        !           599:                if ((tInfoPtr->search.linePtr == NULL)
        !           600:                        || (tInfoPtr->search.line1 > line)) {
        !           601:                    break;
        !           602:                }
        !           603:                tagPtr = tInfoPtr->search.tagPtr;
        !           604:                affectsDisplay = TK_TAG_AFFECTS_DISPLAY(tagPtr);
        !           605:                if ((tInfoPtr->search.line1 < line)
        !           606:                        || (tInfoPtr->search.ch1 <= ch)) {
        !           607:                    if (affectsDisplay) {
        !           608:                        ToggleTag(tInfoPtr, tagPtr);
        !           609:                    }
        !           610:                } else {
        !           611:                    if (affectsDisplay) {
        !           612:                        chunkPtr->numChars = tInfoPtr->search.ch1 - ch;
        !           613:                        break;
        !           614:                    }
        !           615:                }
        !           616:                (void) TkBTreeNextTag(&tInfoPtr->search);
        !           617:            }
        !           618: 
        !           619:            /*
        !           620:             * Create style information for this chunk.
        !           621:             */
        !           622: 
        !           623:            ComputeStyleValues(textPtr, tInfoPtr->numTags, tInfoPtr->tagPtrs,
        !           624:                    &styleValues);
        !           625:            chunkPtr->stylePtr = GetStyle(textPtr, &styleValues);
        !           626: 
        !           627:            /*
        !           628:             * See how many characters will fit on the line.  If they don't
        !           629:             * all fit, then a number of compensations may have to be made.
        !           630:             *
        !           631:             * 1. Make sure that at least one character is displayed on
        !           632:             *    each line.
        !           633:             * 2. In wrap mode "none", allow a partial character to be
        !           634:             *    displayed at the end of an incomplete line.
        !           635:             * 3. In wrap mode "word", search back to find the last space
        !           636:             *    character, and terminate the line just after that space
        !           637:             *    character.  This involves a couple of extra complexities:
        !           638:             *        - the last space may be several chunks back;  in this
        !           639:             *          case, delete all the chunks that are after the
        !           640:             *          space.
        !           641:             *        - if no words fit at all, then use character-wrap for
        !           642:             *          this DLine.
        !           643:             *        - have to reinitialize the tag search information, since
        !           644:             *          we may back up over tag toggles (they'll need to be
        !           645:             *          reconsidered on the next DLine).
        !           646:             */
        !           647: 
        !           648:            charsThatFit = TkMeasureChars(styleValues.fontPtr,
        !           649:                    chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
        !           650:                    maxX, 0, &x);
        !           651:            if ((charsThatFit < chunkPtr->numChars) || (x >= maxX)) {
        !           652:                x = maxX;
        !           653:                chunkPtr->numChars = charsThatFit;
        !           654:                ch += charsThatFit;
        !           655:                if (ch < (linePtr->numBytes - 1)) {
        !           656:                    if ((charsThatFit == 0) && (chunkPtr == dlPtr->chunkPtr)) {
        !           657:                        chunkPtr->numChars = 1;
        !           658:                        ch++;
        !           659:                    } else if (textPtr->wrapMode == tkTextWordUid) {
        !           660:                        if (isspace(chunkPtr->text[charsThatFit])) {
        !           661:                            ch += 1;    /* Include space on this line. */
        !           662:                        } else {
        !           663:                            register Chunk *chunkPtr2;
        !           664:                            register char *p;
        !           665:                            Chunk *spaceChunkPtr;
        !           666:                            int count, space;
        !           667: 
        !           668:                            spaceChunkPtr = NULL;
        !           669:                            space = 0;
        !           670:                            for (chunkPtr2 = dlPtr->chunkPtr;
        !           671:                                    chunkPtr2 != NULL;
        !           672:                                    chunkPtr2 = chunkPtr2->nextPtr) {
        !           673:                                for (count = chunkPtr2->numChars - 1,
        !           674:                                        p = chunkPtr2->text + count;
        !           675:                                        count >= 0; count--, p--) {
        !           676:                                    if (isspace(*p)) {
        !           677:                                        spaceChunkPtr = chunkPtr2;
        !           678:                                        space = count;
        !           679:                                        break;
        !           680:                                    }
        !           681:                                }
        !           682:                            }
        !           683:                            if (spaceChunkPtr != NULL) {
        !           684:                                spaceChunkPtr->numChars = space;
        !           685:                                ch = (spaceChunkPtr->text + space + 1)
        !           686:                                        - linePtr->bytes;
        !           687:                                if (chunkPtr != spaceChunkPtr) {
        !           688:                                    chunkPtr = spaceChunkPtr;
        !           689:                                    if (tInfoPtr->tagPtrs != NULL) {
        !           690:                                        ckfree((char *) tInfoPtr->tagPtrs);
        !           691:                                    }
        !           692:                                    tInfoPtr->tagPtrs = TkBTreeGetTags(
        !           693:                                            textPtr->tree, dlPtr->linePtr, ch,
        !           694:                                            &tInfoPtr->numTags);
        !           695:                                    TkBTreeStartSearch(textPtr->tree, line,
        !           696:                                            ch+1,
        !           697:                                            TkBTreeNumLines(textPtr->tree), 0,
        !           698:                                            (TkTextTag *) NULL,
        !           699:                                            &tInfoPtr->search);
        !           700:                                    (void) TkBTreeNextTag(&tInfoPtr->search);
        !           701:                                    tInfoPtr->arraySize = tInfoPtr->numTags;
        !           702:                                    while (chunkPtr->nextPtr != NULL) {
        !           703:                                        chunkPtr2 = chunkPtr->nextPtr;
        !           704:                                        chunkPtr->nextPtr = chunkPtr2->nextPtr;
        !           705:                                        FreeStyle(chunkPtr2->stylePtr);
        !           706:                                        ckfree((char *) chunkPtr2);
        !           707:                                    }
        !           708:                                }
        !           709:                            }
        !           710:                        }
        !           711:                    } else if (textPtr->wrapMode == tkTextNoneUid) {
        !           712:                        chunkPtr->numChars++;
        !           713:                        ch++;
        !           714:                    }
        !           715:                }
        !           716:            } else {
        !           717:                ch += chunkPtr->numChars;
        !           718:            }
        !           719: 
        !           720:            /*
        !           721:             * Update height information for use later in computing
        !           722:             * line's overall height and baseline.
        !           723:             */
        !           724: 
        !           725:            if (styleValues.fontPtr->ascent > ascent) {
        !           726:                ascent = styleValues.fontPtr->ascent;
        !           727:            }
        !           728:            if (styleValues.fontPtr->descent > descent) {
        !           729:                descent = styleValues.fontPtr->descent;
        !           730:            }
        !           731:        }
        !           732: 
        !           733:        dlPtr->height = ascent + descent;
        !           734:        dlPtr->baseline = ascent;
        !           735: 
        !           736:        /*
        !           737:         * Quit when every character but the last character (the newline)
        !           738:         * has been accounted for.  Also quit if the wrap mode is "none":
        !           739:         * this ignores all the characters that don't fit on the first
        !           740:         * line.
        !           741:         */
        !           742: 
        !           743:        if ((ch >= (linePtr->numBytes-1))
        !           744:                || (textPtr->wrapMode == tkTextNoneUid)) {
        !           745:            break;
        !           746:        }
        !           747:     }
        !           748:     return firstLinePtr;
        !           749: }
        !           750: 
        !           751: /*
        !           752:  *----------------------------------------------------------------------
        !           753:  *
        !           754:  * ToggleTag --
        !           755:  *
        !           756:  *     Update information about tags to reflect a transition on a
        !           757:  *     particular tag.
        !           758:  *
        !           759:  * Results:
        !           760:  *     The array at *tInfoPtr is modified to include tagPtr if it
        !           761:  *     didn't already or to exclude it if it used to include it.
        !           762:  *     The array will be reallocated to a larger size if needed.
        !           763:  *
        !           764:  * Side effects:
        !           765:  *     None.
        !           766:  *
        !           767:  *----------------------------------------------------------------------
        !           768:  */
        !           769: 
        !           770: static void
        !           771: ToggleTag(tInfoPtr, tagPtr)
        !           772:     register TagInfo *tInfoPtr;                /* Tag information to be updated. */
        !           773:     TkTextTag *tagPtr;                 /* Tag to be toggled into or out of
        !           774:                                         * *tInfoPtr. */
        !           775: {
        !           776:     register TkTextTag **tagPtrPtr;
        !           777:     int i;
        !           778: 
        !           779:     for (i = tInfoPtr->numTags, tagPtrPtr = tInfoPtr->tagPtrs;
        !           780:            i > 0; i--, tagPtrPtr++) {
        !           781:        if (*tagPtrPtr == tagPtr) {
        !           782:            tInfoPtr->numTags--;
        !           783:            *tagPtrPtr = tInfoPtr->tagPtrs[tInfoPtr->numTags];
        !           784:            return;
        !           785:        }
        !           786:     }
        !           787: 
        !           788:     /*
        !           789:      * Tag not currently in array.  Grow the array if necessary, then
        !           790:      * add the tag to it.
        !           791:      */
        !           792: 
        !           793:     if (tInfoPtr->numTags == tInfoPtr->arraySize) {
        !           794:        TkTextTag **newPtrs;
        !           795: 
        !           796:        newPtrs = (TkTextTag **) ckalloc((unsigned)
        !           797:                ((tInfoPtr->arraySize+10) * sizeof(TkTextTag *)));
        !           798:        if (tInfoPtr->tagPtrs != NULL) {
        !           799:            memcpy((VOID *) newPtrs, (VOID *) tInfoPtr->tagPtrs,
        !           800:                    tInfoPtr->arraySize * sizeof(TkTextTag *));
        !           801:            ckfree((char *) tInfoPtr->tagPtrs);
        !           802:        }
        !           803:        tInfoPtr->tagPtrs = newPtrs;
        !           804:        tInfoPtr->arraySize += 10;
        !           805:     }
        !           806:     tInfoPtr->tagPtrs[tInfoPtr->numTags] = tagPtr;
        !           807:     tInfoPtr->numTags++;
        !           808: }
        !           809: 
        !           810: /*
        !           811:  *----------------------------------------------------------------------
        !           812:  *
        !           813:  * UpdateDisplayInfo --
        !           814:  *
        !           815:  *     This procedure is invoked to recompute some or all of the
        !           816:  *     DLine structures for a text widget.  At the time it is called
        !           817:  *     the DLine structures still left in the widget are guaranteed
        !           818:  *     to be correct (except for their y-coordinates), but there may
        !           819:  *     be missing structures (the DLine structures get removed as
        !           820:  *     soon as they are potentially out-of-date).
        !           821:  *
        !           822:  * Results:
        !           823:  *     None.
        !           824:  *
        !           825:  * Side effects:
        !           826:  *     Upon return, the DLine information for textPtr correctly reflects
        !           827:  *     the positions where characters will be displayed.  However, this
        !           828:  *     procedure doesn't actually bring the display up-to-date.
        !           829:  *
        !           830:  *----------------------------------------------------------------------
        !           831:  */
        !           832: 
        !           833: static void
        !           834: UpdateDisplayInfo(textPtr)
        !           835:     TkText *textPtr;           /* Text widget to update. */
        !           836: {
        !           837:     register DInfo *dInfoPtr = textPtr->dInfoPtr;
        !           838:     register DLine *dlPtr, *prevPtr, *dlPtr2;
        !           839:     TkTextLine *linePtr;
        !           840:     TagInfo tagInfo;
        !           841:     int line, y, maxY;
        !           842: 
        !           843:     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
        !           844:        return;
        !           845:     }
        !           846:     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
        !           847: 
        !           848:     linePtr = textPtr->topLinePtr;
        !           849:     dlPtr = dInfoPtr->dLinePtr;
        !           850:     tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
        !           851:            &tagInfo.numTags);
        !           852:     tagInfo.arraySize = tagInfo.numTags;
        !           853: 
        !           854:     /*
        !           855:      * Tricky point:  initialize the tag search just *after* the first
        !           856:      * character in the line, since the tagInfo structure already has all
        !           857:      * the tags for the first character.
        !           858:      */
        !           859: 
        !           860:     line = TkBTreeLineIndex(linePtr);
        !           861:     TkBTreeStartSearch(textPtr->tree, line, 1, TkBTreeNumLines(textPtr->tree),
        !           862:            0, (TkTextTag *) NULL, &tagInfo.search);
        !           863:     TkBTreeNextTag(&tagInfo.search);
        !           864:     prevPtr = NULL;
        !           865:     y = dInfoPtr->y;
        !           866:     maxY = dInfoPtr->maxY;
        !           867:     while ((linePtr != NULL) && (y < maxY)) {
        !           868:        register DLine *newPtr;
        !           869:        /*
        !           870:         * See if the next DLine matches the next line we want to
        !           871:         * appear on the screen.  If so then we can just use its
        !           872:         * information.  If not then create new DLine structures
        !           873:         * for the desired line and insert them into the list.
        !           874:         */
        !           875: 
        !           876:        if ((dlPtr == NULL) || (dlPtr->linePtr != linePtr)) {
        !           877:            newPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
        !           878:            if (prevPtr == NULL) {
        !           879:                dInfoPtr->dLinePtr = newPtr;
        !           880:            } else {
        !           881:                prevPtr->nextPtr = newPtr;
        !           882:            }
        !           883:            for (dlPtr2 = newPtr; dlPtr2->nextPtr != NULL;
        !           884:                    dlPtr2 = dlPtr2->nextPtr) {
        !           885:                /* Empty loop body. */
        !           886:            }
        !           887:            dlPtr2->nextPtr = dlPtr;
        !           888:            dlPtr = newPtr;
        !           889:        }
        !           890: 
        !           891:        /*
        !           892:         * Skip to the next line, and update the y-position while
        !           893:         * skipping.
        !           894:         */
        !           895: 
        !           896:        do {
        !           897:            dlPtr->y = y;
        !           898:            y += dlPtr->height;
        !           899:            prevPtr = dlPtr;
        !           900:            dlPtr = dlPtr->nextPtr;
        !           901:        } while ((dlPtr != NULL) && (dlPtr->linePtr == linePtr));
        !           902:        linePtr = TkBTreeNextLine(linePtr);
        !           903:        line++;
        !           904:     }
        !           905: 
        !           906:     /*
        !           907:      * Delete any DLine structures that don't fit on the screen and free
        !           908:      * up the tag array.
        !           909:      */
        !           910: 
        !           911:     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
        !           912:     if (tagInfo.tagPtrs != NULL) {
        !           913:        ckfree((char *) tagInfo.tagPtrs);
        !           914:     }
        !           915: 
        !           916:     /*
        !           917:      * Update the vertical scrollbar, if there is one.
        !           918:      */
        !           919: 
        !           920:     if (textPtr->yScrollCmd != NULL) {
        !           921:        int numLines, first, result, maxY, height;
        !           922:        char string[60];
        !           923: 
        !           924:        /*
        !           925:         * Count the number of text lines on the screen.
        !           926:         */
        !           927: 
        !           928:        maxY = 0;
        !           929:        for (numLines = 0, linePtr = NULL, dlPtr = dInfoPtr->dLinePtr;
        !           930:                dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
        !           931:            if (dlPtr->linePtr != linePtr) {
        !           932:                numLines++;
        !           933:                linePtr = dlPtr->linePtr;
        !           934:            }
        !           935:            maxY = dlPtr->y + dlPtr->height;
        !           936:        }
        !           937: 
        !           938:        /*
        !           939:         * If the screen isn't completely full, then estimate the number of
        !           940:         * lines that would fit on it if it were full.
        !           941:         */
        !           942: 
        !           943:        height = dInfoPtr->maxY - dInfoPtr->y;
        !           944:        if (numLines == 0) {
        !           945:            numLines = height /
        !           946:                    (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
        !           947:        } else if (maxY < height) {
        !           948:            numLines = (numLines * height)/maxY;
        !           949:        }
        !           950:         /* DEH: be reasonable if dLinePtr is null */
        !           951:        if (dInfoPtr->dLinePtr == NULL) {
        !           952:          sprintf(string, " 0 0 0 0");
        !           953:        } else {
        !           954:          first = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
        !           955:          sprintf(string, " %d %d %d %d", TkBTreeNumLines(textPtr->tree),
        !           956:                  numLines, first, first+numLines-1);
        !           957:        }
        !           958:        result = Tcl_VarEval(textPtr->interp, textPtr->yScrollCmd, string,
        !           959:                (char *) NULL);
        !           960:        if (result != TCL_OK) {
        !           961:            TkBindError(textPtr->interp);
        !           962:        }
        !           963:     }
        !           964: }
        !           965: 
        !           966: /*
        !           967:  *----------------------------------------------------------------------
        !           968:  *
        !           969:  * FreeDLines --
        !           970:  *
        !           971:  *     This procedure is called to free up all of the resources
        !           972:  *     associated with one or more DLine structures.
        !           973:  *
        !           974:  * Results:
        !           975:  *     None.
        !           976:  *
        !           977:  * Side effects:
        !           978:  *     Memory gets freed and various other resources are released.
        !           979:  *
        !           980:  *----------------------------------------------------------------------
        !           981:  */
        !           982: 
        !           983: static void
        !           984: FreeDLines(textPtr, firstPtr, lastPtr, unlink)
        !           985:     TkText *textPtr;                   /* Information about overall text
        !           986:                                         * widget. */
        !           987:     register DLine *firstPtr;          /* Pointer to first DLine to free up. */
        !           988:     DLine *lastPtr;                    /* Pointer to DLine just after last
        !           989:                                         * one to free (NULL means everything
        !           990:                                         * starting with firstPtr). */
        !           991:     int unlink;                                /* 1 means DLines are currently linked
        !           992:                                         * into the list rooted at
        !           993:                                         * textPtr->dInfoPtr->dLinePtr and
        !           994:                                         * they have to be unlinked.  0 means
        !           995:                                         * just free without unlinking. */
        !           996: {
        !           997:     register Chunk *chunkPtr, *nextChunkPtr;
        !           998:     register DLine *nextDLinePtr;
        !           999: 
        !          1000:     if (unlink) {
        !          1001:        if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
        !          1002:            textPtr->dInfoPtr->dLinePtr = lastPtr;
        !          1003:        } else {
        !          1004:            register DLine *prevPtr;
        !          1005:            for (prevPtr = textPtr->dInfoPtr->dLinePtr;
        !          1006:                    prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
        !          1007:                /* Empty loop body. */
        !          1008:            }
        !          1009:            prevPtr->nextPtr = lastPtr;
        !          1010:        }
        !          1011:     }
        !          1012:     while (firstPtr != lastPtr) {
        !          1013:        nextDLinePtr = firstPtr->nextPtr;
        !          1014:        for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
        !          1015:                chunkPtr = nextChunkPtr) {
        !          1016:            FreeStyle(chunkPtr->stylePtr);
        !          1017:            nextChunkPtr = chunkPtr->nextPtr;
        !          1018:            ckfree((char *) chunkPtr);
        !          1019:        }
        !          1020:        ckfree((char *) firstPtr);
        !          1021:        firstPtr = nextDLinePtr;
        !          1022:     }
        !          1023: }
        !          1024: 
        !          1025: /*
        !          1026:  *----------------------------------------------------------------------
        !          1027:  *
        !          1028:  * DisplayDLine --
        !          1029:  *
        !          1030:  *     This procedure is invoked to draw a single line on the
        !          1031:  *     screen.
        !          1032:  *
        !          1033:  * Results:
        !          1034:  *     None.
        !          1035:  *
        !          1036:  * Side effects:
        !          1037:  *     The line given by dlPtr is drawn at its correct position in
        !          1038:  *     textPtr's window.  Note that this is one *display* line, not
        !          1039:  *     one *text* line.
        !          1040:  *
        !          1041:  *----------------------------------------------------------------------
        !          1042:  */
        !          1043: 
        !          1044: static void
        !          1045: DisplayDLine(textPtr, dlPtr, pixmap)
        !          1046:     TkText *textPtr;           /* Text widget in which to draw line. */
        !          1047:     register DLine *dlPtr;     /* Information about line to draw. */
        !          1048:     Pixmap pixmap;             /* Pixmap to use for double-buffering.
        !          1049:                                 * Caller must make sure it's large enough
        !          1050:                                 * to hold line. */
        !          1051: {
        !          1052:     register Style *stylePtr;
        !          1053:     register StyleValues *sValuePtr;
        !          1054:     register Chunk *chunkPtr;
        !          1055:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1056:     Display *display;
        !          1057:     int width, height, count, x;
        !          1058:     XFontStruct *fontPtr;
        !          1059: 
        !          1060:     /*
        !          1061:      * First, clear the area of the line to the background color for the
        !          1062:      * text widget.
        !          1063:      */
        !          1064: 
        !          1065:     display = Tk_Display(textPtr->tkwin);
        !          1066:     Tk_Fill3DRectangle(display, pixmap, textPtr->border, 0, 0,
        !          1067:            Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
        !          1068: 
        !          1069:     /*
        !          1070:      * Next, cycle through all of the chunks in the line displaying
        !          1071:      * backgrounds.  We need to do two passes, one for the backgrounds
        !          1072:      * and one for the characters, because some characters (e.g. italics
        !          1073:      * with heavy slants) may cross background boundaries.  If some
        !          1074:      * backgrounds are drawn after some text, the later backgrounds may
        !          1075:      * obliterate parts of earlier characters.
        !          1076:      */
        !          1077: 
        !          1078:     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
        !          1079:            chunkPtr = chunkPtr->nextPtr) {
        !          1080: 
        !          1081:        /*
        !          1082:         * Draw a special background for this chunk if one is specified
        !          1083:         * in its style.  Two tricks here:
        !          1084:         * 1. if this is the last chunk in the line then extend the
        !          1085:         *    background across to the end of the line.
        !          1086:         * 2. if the background is stippled, then we have to draw the
        !          1087:         *    stippled part specially, since Tk_Fill3DRectangle doesn't
        !          1088:         *    do stipples.
        !          1089:         */
        !          1090: 
        !          1091:        stylePtr = chunkPtr->stylePtr;
        !          1092:        sValuePtr = stylePtr->sValuePtr;
        !          1093:        if (sValuePtr->border != NULL) {
        !          1094:            if (chunkPtr->nextPtr != NULL) {
        !          1095:                width = chunkPtr->nextPtr->x - chunkPtr->x;
        !          1096:            } else {
        !          1097:                width = Tk_Width(textPtr->tkwin) - chunkPtr->x;
        !          1098:            }
        !          1099:            if (stylePtr->bgGC != NULL) {
        !          1100:                XFillRectangle(display, pixmap, stylePtr->bgGC, chunkPtr->x,
        !          1101:                        0, (unsigned int) width, (unsigned int) dlPtr->height);
        !          1102:                Tk_Draw3DRectangle(display, pixmap, sValuePtr->border,
        !          1103:                        chunkPtr->x, 0, width, dlPtr->height,
        !          1104:                        sValuePtr->borderWidth, sValuePtr->relief);
        !          1105:            } else {
        !          1106:                Tk_Fill3DRectangle(display, pixmap, sValuePtr->border,
        !          1107:                        chunkPtr->x, 0, width, dlPtr->height,
        !          1108:                        sValuePtr->borderWidth, sValuePtr->relief);
        !          1109:            }
        !          1110:        }
        !          1111:     }
        !          1112: 
        !          1113:     /*
        !          1114:      * If the insertion cursor is displayed on this line, then draw it
        !          1115:      * now, on top of the background but before the text.  As a special
        !          1116:      * workaround to keep the cursor visible on mono displays, write the default
        !          1117:      * background in the cursor area (instead of nothing) when the cursor
        !          1118:      * isn't on.  Otherwise the selection would hide the cursor.
        !          1119:      */
        !          1120: 
        !          1121:     if ((textPtr->insertAnnotPtr->linePtr == dlPtr->linePtr)
        !          1122:            && (textPtr->state == tkTextNormalUid)
        !          1123:            && (textPtr->flags & GOT_FOCUS)) {
        !          1124:        for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
        !          1125:                chunkPtr = chunkPtr->nextPtr) {
        !          1126:            count = textPtr->insertAnnotPtr->ch
        !          1127:                    - (chunkPtr->text - dlPtr->linePtr->bytes);
        !          1128:            if (count < 0) {
        !          1129:                break;
        !          1130:            }
        !          1131:            if (count > chunkPtr->numChars) {
        !          1132:                continue;
        !          1133:            }
        !          1134: 
        !          1135:            /*
        !          1136:             * Deciding whether to display the cursor just after the last
        !          1137:             * character in a line is tricky because of various wrap
        !          1138:             * modes.  Do it unless we're in character wrap mode and
        !          1139:             * this line wraps, in which case it's better to display the
        !          1140:             * cursor on the next line.  For word wrap, there's an
        !          1141:             * undisplayed space character that the user must be able to
        !          1142:             * position the cursor in front of.  For no wrap, there's no
        !          1143:             * next line on which to display the cursor.
        !          1144:             */
        !          1145:            if ((count == chunkPtr->numChars)
        !          1146:                    && (textPtr->wrapMode == tkTextCharUid)
        !          1147:                    && (chunkPtr->text[count] != '\n')) {
        !          1148:                continue;
        !          1149:            }
        !          1150:            fontPtr = chunkPtr->stylePtr->sValuePtr->fontPtr;
        !          1151:            TkMeasureChars(fontPtr, chunkPtr->text, count, chunkPtr->x,
        !          1152:                    (int) 1000000, 0, &x);
        !          1153:            if (textPtr->flags & INSERT_ON) {
        !          1154:                Tk_Fill3DRectangle(display, pixmap, textPtr->insertBorder,
        !          1155:                        x - textPtr->insertWidth/2,
        !          1156:                        dlPtr->baseline - fontPtr->ascent,
        !          1157:                        textPtr->insertWidth,
        !          1158:                        fontPtr->ascent + fontPtr->descent,
        !          1159:                        textPtr->insertBorderWidth, TK_RELIEF_RAISED);
        !          1160:            } else if (Tk_DefaultDepth(Tk_Screen(textPtr->tkwin)) == 1) {
        !          1161:                Tk_Fill3DRectangle(display, pixmap, textPtr->border,
        !          1162:                        x - textPtr->insertWidth/2,
        !          1163:                        dlPtr->baseline - fontPtr->ascent,
        !          1164:                        textPtr->insertWidth,
        !          1165:                        fontPtr->ascent + fontPtr->descent,
        !          1166:                        0, TK_RELIEF_FLAT);
        !          1167:            }
        !          1168: 
        !          1169:        }
        !          1170:     }
        !          1171: 
        !          1172:     /*
        !          1173:      * Make another pass through all of the chunks to redraw all of
        !          1174:      * the text (and underlines, etc., if they're wanted).
        !          1175:      */
        !          1176: 
        !          1177:     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
        !          1178:            chunkPtr = chunkPtr->nextPtr) {
        !          1179:        stylePtr = chunkPtr->stylePtr;
        !          1180:        sValuePtr = stylePtr->sValuePtr;
        !          1181:        if (chunkPtr->numChars > 0) {
        !          1182:            TkDisplayChars(display, pixmap, stylePtr->fgGC, sValuePtr->fontPtr,
        !          1183:                    chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
        !          1184:                    dlPtr->baseline, 0);
        !          1185:            if (sValuePtr->underline) {
        !          1186:                TkUnderlineChars(display, pixmap, stylePtr->fgGC,
        !          1187:                        sValuePtr->fontPtr, chunkPtr->text, chunkPtr->x,
        !          1188:                        dlPtr->baseline, 0, 0, chunkPtr->numChars-1);
        !          1189:            }
        !          1190:        }
        !          1191:     }
        !          1192: 
        !          1193:     /*
        !          1194:      * Copy the pixmap onto the screen.  If this is the last line on
        !          1195:      * the screen, only copy a piece of the line, so that it doesn't
        !          1196:      * overflow into the border area.  Another special trick:  copy the
        !          1197:      * padding area to the left of the line;  this is because the
        !          1198:      * insertion cursor sometimes overflows onto that area and we want
        !          1199:      * to get as much of the cursor as possible.
        !          1200:      */
        !          1201: 
        !          1202:     height = dlPtr->height;
        !          1203:     if ((height + dlPtr->y) > dInfoPtr->maxY) {
        !          1204:        height = dInfoPtr->maxY - dlPtr->y;
        !          1205:     }
        !          1206:     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin),
        !          1207:            dInfoPtr->copyGC, dInfoPtr->x - textPtr->padX, 0,
        !          1208:            dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
        !          1209:            height, dInfoPtr->x - textPtr->padX, dlPtr->y);
        !          1210:     linesRedrawn++;
        !          1211: }
        !          1212: 
        !          1213: /*
        !          1214:  *----------------------------------------------------------------------
        !          1215:  *
        !          1216:  * DisplayText --
        !          1217:  *
        !          1218:  *     This procedure is invoked as a when-idle handler to update the
        !          1219:  *     display.  It only redisplays the parts of the text widget that
        !          1220:  *     are out of date.
        !          1221:  *
        !          1222:  * Results:
        !          1223:  *     None.
        !          1224:  *
        !          1225:  * Side effects:
        !          1226:  *     Information is redrawn on the screen.
        !          1227:  *
        !          1228:  *----------------------------------------------------------------------
        !          1229:  */
        !          1230: 
        !          1231: static void
        !          1232: DisplayText(clientData)
        !          1233:     ClientData clientData;     /* Information about widget. */
        !          1234: {
        !          1235:     register TkText *textPtr = (TkText *) clientData;
        !          1236:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1237:     Tk_Window tkwin;
        !          1238:     register DLine *dlPtr;
        !          1239:     Pixmap pixmap;
        !          1240:     int maxHeight;
        !          1241:     int bottomY = 0;           /* Initialization needed only to stop
        !          1242:                                 * compiler warnings. */
        !          1243: 
        !          1244:     assert(textPtr->updateTimerToken != NULL);
        !          1245:     
        !          1246:     textPtr->updateTimerToken = 0;
        !          1247: 
        !          1248:     if ((textPtr->tkwin == NULL) || !Tk_IsMapped(textPtr->tkwin)
        !          1249:            || (dInfoPtr->maxX <= dInfoPtr->x)
        !          1250:            || (dInfoPtr->maxY <= dInfoPtr->y)) {
        !          1251:        goto done;
        !          1252:     }
        !          1253:     numRedisplays++;
        !          1254: 
        !          1255:     /*
        !          1256:      * Choose a new current item if that is needed (this could cause
        !          1257:      * event handlers to be invoked, hence the preserve/release calls
        !          1258:      * and the loop, since the handlers could conceivably necessitate
        !          1259:      * yet another current item calculation).  The tkwin check is because
        !          1260:      * the whole window could go away in the Tk_Release call.
        !          1261:      */
        !          1262: 
        !          1263:     while (dInfoPtr->flags & REPICK_NEEDED) {
        !          1264:        Tk_Preserve((ClientData) textPtr);
        !          1265:        dInfoPtr->flags &= ~REPICK_NEEDED;
        !          1266:        TkTextPickCurrent(textPtr, &textPtr->pickEvent);
        !          1267:        tkwin = textPtr->tkwin;
        !          1268:        Tk_Release((ClientData) textPtr);
        !          1269:        if (tkwin == NULL) {
        !          1270:            return;
        !          1271:        }
        !          1272:     }
        !          1273: 
        !          1274:     /*
        !          1275:      * First recompute what's supposed to be displayed.
        !          1276:      */
        !          1277: 
        !          1278:     UpdateDisplayInfo(textPtr);
        !          1279: 
        !          1280:     /*
        !          1281:      * Redraw the borders if that's needed.
        !          1282:      */
        !          1283: 
        !          1284:     if (dInfoPtr->flags & REDRAW_BORDERS) {
        !          1285:        Tk_Draw3DRectangle(Tk_Display(textPtr->tkwin),
        !          1286:                Tk_WindowId(textPtr->tkwin), textPtr->border,
        !          1287:                0, 0, Tk_Width(textPtr->tkwin), Tk_Height(textPtr->tkwin),
        !          1288:                textPtr->borderWidth, textPtr->relief);
        !          1289:     }
        !          1290: 
        !          1291:     /*
        !          1292:      * See if it's possible to bring some parts of the screen up-to-date
        !          1293:      * by scrolling (copying from other parts of the screen).
        !          1294:      */
        !          1295: 
        !          1296:     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
        !          1297:        register DLine *dlPtr2;
        !          1298:        int offset, height;
        !          1299: 
        !          1300:        if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
        !          1301:                || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
        !          1302:            continue;
        !          1303:        }
        !          1304: 
        !          1305:        /*
        !          1306:         * This line is already drawn somewhere in the window so it only
        !          1307:         * needs to be copied to its new location.  See if there's a group
        !          1308:         * of lines that can all be copied together.
        !          1309:         */
        !          1310: 
        !          1311:        offset = dlPtr->y - dlPtr->oldY;
        !          1312:        height = dlPtr->height;
        !          1313:        for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
        !          1314:                dlPtr2 = dlPtr2->nextPtr) {
        !          1315:            if ((dlPtr2->oldY == -1)
        !          1316:                    || ((dlPtr2->oldY + offset) != dlPtr2->y)
        !          1317:                    || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
        !          1318:                break;
        !          1319:            }
        !          1320:            height += dlPtr2->height;
        !          1321:        }
        !          1322: 
        !          1323:        /*
        !          1324:         * Copy the information and update the lines to show that they've
        !          1325:         * been copied.  Reduce the height of the area being copied if
        !          1326:         * necessary to avoid overwriting the border area.
        !          1327:         */
        !          1328: 
        !          1329:        if ((dlPtr->y + height) > dInfoPtr->maxY) {
        !          1330:            height = dInfoPtr->maxY - dlPtr->y;
        !          1331:        }
        !          1332:        XCopyArea(Tk_Display(textPtr->tkwin), Tk_WindowId(textPtr->tkwin),
        !          1333:                Tk_WindowId(textPtr->tkwin), dInfoPtr->scrollGC,
        !          1334:                dInfoPtr->x - textPtr->padX, dlPtr->oldY,
        !          1335:                dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
        !          1336:                height, dInfoPtr->x - textPtr->padX, dlPtr->y);
        !          1337:        numCopies++;
        !          1338:        while (1) {
        !          1339:            dlPtr->oldY = dlPtr->y;
        !          1340:            if (dlPtr->nextPtr == dlPtr2) {
        !          1341:                break;
        !          1342:            }
        !          1343:            dlPtr = dlPtr->nextPtr;
        !          1344:        }
        !          1345: 
        !          1346:        /*
        !          1347:         * It's possible that part of the area copied above was obscured.
        !          1348:         * To handle this situation, read expose-related events generated
        !          1349:         * during the XCopyArea operation.
        !          1350:         */
        !          1351: 
        !          1352:        while (1) {
        !          1353:            XEvent event;
        !          1354: 
        !          1355:            XWindowEvent(Tk_Display(textPtr->tkwin),
        !          1356:                    Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
        !          1357:            if (event.type == NoExpose) {
        !          1358:                break;
        !          1359:            } else if (event.type == GraphicsExpose) {
        !          1360:                TkTextRedrawRegion(textPtr, event.xgraphicsexpose.x,
        !          1361:                        event.xgraphicsexpose.y, event.xgraphicsexpose.width,
        !          1362:                        event.xgraphicsexpose.height);
        !          1363:                if (event.xgraphicsexpose.count == 0) {
        !          1364:                    damagedCopies++;
        !          1365:                    break;
        !          1366:                }
        !          1367:            } else if (event.type == Expose) {
        !          1368:                /*
        !          1369:                 * A tricky situation.  This event must already have been
        !          1370:                 * queued up before the XCopyArea was issued.  If the area
        !          1371:                 * in this event overlaps the area copied, then some of the
        !          1372:                 * bits that were copied were bogus.  The easiest way to
        !          1373:                 * handle this is to issue two redisplays:  one for the
        !          1374:                 * original area and one for the area shifted as if it was
        !          1375:                 * in the copied area.
        !          1376:                 */
        !          1377: 
        !          1378:                TkTextRedrawRegion(textPtr, event.xexpose.x,
        !          1379:                        event.xexpose.y, event.xexpose.width,
        !          1380:                        event.xexpose.height);
        !          1381:                TkTextRedrawRegion(textPtr, event.xexpose.x,
        !          1382:                        event.xexpose.y + offset, event.xexpose.width,
        !          1383:                        event.xexpose.height);
        !          1384:            } else {
        !          1385:                panic("DisplayText received unknown exposure event");
        !          1386:            }
        !          1387:        }
        !          1388:     }
        !          1389: 
        !          1390:     /*
        !          1391:      * Now we have to redraw the lines that couldn't be updated by
        !          1392:      * scrolling.  First, compute the height of the largest line and
        !          1393:      * allocate an off-screen pixmap to use for double-buffered
        !          1394:      * displays.
        !          1395:      */
        !          1396: 
        !          1397:     maxHeight = -1;
        !          1398:     for (dlPtr = textPtr->dInfoPtr->dLinePtr; dlPtr != NULL;
        !          1399:            dlPtr = dlPtr->nextPtr) {
        !          1400:        if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
        !          1401:            maxHeight = dlPtr->height;
        !          1402:        }
        !          1403:        bottomY = dlPtr->y + dlPtr->height;
        !          1404:     }
        !          1405:     if (maxHeight >= 0) {
        !          1406:        pixmap = XCreatePixmap(Tk_Display(textPtr->tkwin),
        !          1407:                Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
        !          1408:                maxHeight, Tk_DefaultDepth(Tk_Screen(textPtr->tkwin)));
        !          1409:        for (dlPtr = textPtr->dInfoPtr->dLinePtr; dlPtr != NULL;
        !          1410:                dlPtr = dlPtr->nextPtr) {
        !          1411:            if (dlPtr->oldY != dlPtr->y) {
        !          1412:                DisplayDLine(textPtr, dlPtr, pixmap);
        !          1413:                dlPtr->oldY = dlPtr->y;
        !          1414:            }
        !          1415:        }
        !          1416:        XFreePixmap(Tk_Display(textPtr->tkwin), pixmap);
        !          1417:     }
        !          1418: 
        !          1419:     /*
        !          1420:      * Lastly, see if we need to refresh the part of the window below
        !          1421:      * the last line of text (if there is any such area).
        !          1422:      */
        !          1423: 
        !          1424:     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
        !          1425:        dInfoPtr->topOfEof = dInfoPtr->maxY;
        !          1426:     }
        !          1427:     if (bottomY < dInfoPtr->topOfEof) {
        !          1428:        Tk_Fill3DRectangle(Tk_Display(textPtr->tkwin),
        !          1429:                Tk_WindowId(textPtr->tkwin), textPtr->border,
        !          1430:                dInfoPtr->x, bottomY, dInfoPtr->maxX - dInfoPtr->x,
        !          1431:                dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
        !          1432:     }
        !          1433:     dInfoPtr->topOfEof = bottomY;
        !          1434:     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
        !          1435:        dInfoPtr->topOfEof = dInfoPtr->maxY;
        !          1436:     }
        !          1437: 
        !          1438:     done:
        !          1439:     dInfoPtr->flags &= ~(REDRAW_PENDING|REDRAW_BORDERS);
        !          1440: }
        !          1441: 
        !          1442: /*
        !          1443:  *----------------------------------------------------------------------
        !          1444:  *
        !          1445:  * TkTextRedrawRegion --
        !          1446:  *
        !          1447:  *     This procedure is invoked to schedule a redisplay for a given
        !          1448:  *     region of a text widget.  The redisplay itself may not occur
        !          1449:  *     immediately:  it's scheduled as a when-idle handler.
        !          1450:  *
        !          1451:  * Results:
        !          1452:  *     None.
        !          1453:  *
        !          1454:  * Side effects:
        !          1455:  *     Information will eventually be redrawn on the screen.
        !          1456:  *
        !          1457:  *----------------------------------------------------------------------
        !          1458:  */
        !          1459: 
        !          1460:        /* ARGSUSED */
        !          1461: void
        !          1462: TkTextRedrawRegion(textPtr, x, y, width, height)
        !          1463:     TkText *textPtr;           /* Widget record for text widget. */
        !          1464:     int x, y;                  /* Coordinates of upper-left corner of area
        !          1465:                                 * to be redrawn, in pixels relative to
        !          1466:                                 * textPtr's window. */
        !          1467:     int width, height;         /* Width and height of area to be redrawn. */
        !          1468: {
        !          1469:     register DLine *dlPtr;
        !          1470:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1471:     int maxY;
        !          1472: 
        !          1473:     /*
        !          1474:      * Find all lines that overlap the given region and mark them for
        !          1475:      * redisplay.
        !          1476:      */
        !          1477: 
        !          1478:     maxY = y + height;
        !          1479:     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
        !          1480:            dlPtr = dlPtr->nextPtr) {
        !          1481:        if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
        !          1482:            dlPtr->oldY = -1;
        !          1483:        }
        !          1484:     }
        !          1485:     if (dInfoPtr->topOfEof < maxY) {
        !          1486:        dInfoPtr->topOfEof = maxY;
        !          1487:     }
        !          1488: 
        !          1489:     /*
        !          1490:      * Schedule the redisplay operation if there isn't one already
        !          1491:      * scheduled.
        !          1492:      */
        !          1493: 
        !          1494:     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
        !          1495:        dInfoPtr->flags |= REDRAW_PENDING;
        !          1496: //     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
        !          1497:        assert(textPtr->updateTimerToken == NULL);
        !          1498:        if (textPtr->updateTimerToken == NULL) {
        !          1499:            textPtr->updateTimerToken =
        !          1500:              Tk_CreateTimerHandler(
        !          1501:                TextUpdateTime,
        !          1502:                DisplayText,
        !          1503:                (ClientData) textPtr);
        !          1504:        }
        !          1505:     }
        !          1506:     if ((x < dInfoPtr->x) || (y < dInfoPtr->y)
        !          1507:            || ((x + width) > dInfoPtr->maxX) || (maxY > dInfoPtr->maxY)) {
        !          1508:        dInfoPtr->flags |= REDRAW_BORDERS;
        !          1509:     }
        !          1510: }
        !          1511: 
        !          1512: /*
        !          1513:  *----------------------------------------------------------------------
        !          1514:  *
        !          1515:  * TkTextLinesChanged --
        !          1516:  *
        !          1517:  *     This procedure is invoked when lines in a text widget are about
        !          1518:  *     to be modified in a way that changes how they are displayed (e.g.
        !          1519:  *     characters were inserted, the line was deleted, or tag information
        !          1520:  *     was changed).  This procedure must be called *before* a change is
        !          1521:  *     made, so that pointers to TkTextLines in the display information
        !          1522:  *     are still valid.
        !          1523:  *
        !          1524:  * Results:
        !          1525:  *     None.
        !          1526:  *
        !          1527:  * Side effects:
        !          1528:  *     The indicated lines will be redisplayed at some point in the
        !          1529:  *     future (the actual redisplay is scheduled as a when-idle handler).
        !          1530:  *
        !          1531:  *----------------------------------------------------------------------
        !          1532:  */
        !          1533: 
        !          1534: void
        !          1535: TkTextLinesChanged(textPtr, first, last)
        !          1536:     TkText *textPtr;           /* Widget record for text widget. */
        !          1537:     int first;                 /* Index of first line that must be
        !          1538:                                 * redisplayed. */
        !          1539:     int last;                  /* Index of last line to redisplay. */
        !          1540: {
        !          1541:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1542:     DLine *firstPtr, *lastPtr;
        !          1543: 
        !          1544:     /*
        !          1545:      * Find the DLines corresponding to first and last+1.
        !          1546:      */
        !          1547: 
        !          1548:     firstPtr = FindDLine(dInfoPtr->dLinePtr, first);
        !          1549:     if (firstPtr == NULL) {
        !          1550:        return;
        !          1551:     }
        !          1552:     lastPtr = FindDLine(dInfoPtr->dLinePtr, last+1);
        !          1553:     if (firstPtr == lastPtr) {
        !          1554:        return;
        !          1555:     }
        !          1556: 
        !          1557:     /*
        !          1558:      * Delete all the DLines from first up through last (but not including
        !          1559:      * lastPtr, which points to the first line *outside* the range).
        !          1560:      */
        !          1561: 
        !          1562:     FreeDLines(textPtr, firstPtr, lastPtr, 1);
        !          1563: 
        !          1564:     /*
        !          1565:      * Schedule both a redisplay and a recomputation of display information.
        !          1566:      */
        !          1567: 
        !          1568:     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
        !          1569: //     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
        !          1570:         assert(textPtr->updateTimerToken == NULL);
        !          1571:        if (textPtr->updateTimerToken == NULL) {
        !          1572:            textPtr->updateTimerToken =
        !          1573:              Tk_CreateTimerHandler(
        !          1574:                TextUpdateTime,
        !          1575:                DisplayText,
        !          1576:                (ClientData) textPtr);
        !          1577:        }
        !          1578:     }
        !          1579:     dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
        !          1580: }
        !          1581: 
        !          1582: /*
        !          1583:  *----------------------------------------------------------------------
        !          1584:  *
        !          1585:  * TkTextRedrawTag --
        !          1586:  *
        !          1587:  *     This procedure is invoked to request a redraw of all characters
        !          1588:  *     in a given range of characters that have a particular tag on or
        !          1589:  *     off.  It's called, for example, when characters are tagged or
        !          1590:  *     untagged, or when tag options change.
        !          1591:  *
        !          1592:  * Results:
        !          1593:  *     None.
        !          1594:  *
        !          1595:  * Side effects:
        !          1596:  *     Information on the screen may be redrawn, and the layout of
        !          1597:  *     the screen may change.
        !          1598:  *
        !          1599:  *----------------------------------------------------------------------
        !          1600:  */
        !          1601: 
        !          1602: void
        !          1603: TkTextRedrawTag(textPtr, line1, ch1, line2, ch2, tagPtr, withTag)
        !          1604:     TkText *textPtr;           /* Widget record for text widget. */
        !          1605:     int line1, ch1;            /* Index of first character in range of
        !          1606:                                 * interest. */
        !          1607:     int line2, ch2;            /* Index of character just after last one
        !          1608:                                 * in range of interest. */
        !          1609:     TkTextTag *tagPtr;         /* Information about tag. */
        !          1610:     int withTag;               /* 1 means redraw characters that have the
        !          1611:                                 * tag, 0 means redraw those without. */
        !          1612: {
        !          1613:     register DLine *dlPtr;
        !          1614:     DLine *endPtr;
        !          1615:     int topLine, tagOn;
        !          1616:     TkTextSearch search;
        !          1617:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1618: 
        !          1619:     /*
        !          1620:      * Round up the starting position if it's before the first line
        !          1621:      * visible on the screen (we only care about what's on the screen).
        !          1622:      */
        !          1623: 
        !          1624:     dlPtr = dInfoPtr->dLinePtr;
        !          1625:     if (dlPtr == NULL) {
        !          1626:        return;
        !          1627:     }
        !          1628:     topLine = TkBTreeLineIndex(dlPtr->linePtr);
        !          1629:     if (topLine > line1) {
        !          1630:        line1 = topLine;
        !          1631:        ch1 = 0;
        !          1632:     }
        !          1633: 
        !          1634:     /* 
        !          1635:      * Initialize a search through all transitions on the tag, starting
        !          1636:      * with the first transition where the tag's current state is different
        !          1637:      * from what it will eventually be.
        !          1638:      */
        !          1639: 
        !          1640:     TkBTreeStartSearch(textPtr->tree, line1, ch1+1, line2, ch2,
        !          1641:            tagPtr, &search);
        !          1642:     tagOn = TkBTreeCharTagged(search.linePtr, ch1, tagPtr);
        !          1643:     if (tagOn != withTag) {
        !          1644:        if (!TkBTreeNextTag(&search)) {
        !          1645:            return;
        !          1646:        }
        !          1647:     }
        !          1648: 
        !          1649:     /*
        !          1650:      * Each loop through the loop below is for one range of characters
        !          1651:      * where the tag's current state is different than its eventual
        !          1652:      * state.  At the top of the loop, search contains information about
        !          1653:      * the first character in the range.
        !          1654:      */
        !          1655: 
        !          1656:     while (1) {
        !          1657:        /*
        !          1658:         * Find the first DLine structure in the range.
        !          1659:         */
        !          1660: 
        !          1661:        dlPtr = FindDLine(dlPtr, search.line1);
        !          1662:        if (dlPtr == NULL) {
        !          1663:            break;
        !          1664:        }
        !          1665: 
        !          1666:        /*
        !          1667:         * Find the first DLine structure that's past the end of the range.
        !          1668:         */
        !          1669: 
        !          1670:        if (TkBTreeNextTag(&search)) {
        !          1671:            endPtr = FindDLine(dlPtr,
        !          1672:                    (search.ch1 > 0) ? (search.line1 + 1) : search.line1);
        !          1673:        } else {
        !          1674:            endPtr = FindDLine(dlPtr,
        !          1675:                    (ch2 > 0) ? (search.line2 + 1) : search.line2);
        !          1676:        }
        !          1677: 
        !          1678:        /*
        !          1679:         * Delete all of the display lines in the range, so that they'll
        !          1680:         * be re-layed out and redrawn.
        !          1681:         */
        !          1682: 
        !          1683:        FreeDLines(textPtr, dlPtr, endPtr, 1);
        !          1684:        dlPtr = endPtr;
        !          1685: 
        !          1686:        /*
        !          1687:         * Find the first text line in the next range.
        !          1688:         */
        !          1689: 
        !          1690:        if (!TkBTreeNextTag(&search)) {
        !          1691:            break;
        !          1692:        }
        !          1693:     }
        !          1694: 
        !          1695:     /*
        !          1696:      * Lastly, schedule a redisplay and layout recalculation if they
        !          1697:      * aren't already pending.
        !          1698:      */
        !          1699: 
        !          1700:     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
        !          1701: //     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
        !          1702:         assert(textPtr->updateTimerToken == NULL);
        !          1703:         if (textPtr->updateTimerToken == NULL) {
        !          1704:            textPtr->updateTimerToken =
        !          1705:              Tk_CreateTimerHandler(
        !          1706:                TextUpdateTime,
        !          1707:                DisplayText,
        !          1708:                (ClientData) textPtr);
        !          1709:        }
        !          1710:     }
        !          1711:     dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
        !          1712: }
        !          1713: 
        !          1714: /*
        !          1715:  *----------------------------------------------------------------------
        !          1716:  *
        !          1717:  * TkTextRelayoutWindow --
        !          1718:  *
        !          1719:  *     This procedure is called when something has happened that
        !          1720:  *     invalidates the whole layout of characters on the screen, such
        !          1721:  *     as a change in a configuration option for the overall text
        !          1722:  *     widget or a change in the window size.  It causes all display
        !          1723:  *     information to be recomputed and the window to be redrawn.
        !          1724:  *
        !          1725:  * Results:
        !          1726:  *     None.
        !          1727:  *
        !          1728:  * Side effects:
        !          1729:  *     All the display information will be recomputed for the window
        !          1730:  *     and the window will be redrawn.
        !          1731:  *
        !          1732:  *----------------------------------------------------------------------
        !          1733:  */
        !          1734: 
        !          1735: void
        !          1736: TkTextRelayoutWindow(textPtr)
        !          1737:     TkText *textPtr;           /* Widget record for text widget. */
        !          1738: {
        !          1739:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1740: 
        !          1741:     /*
        !          1742:      * Throw away all the current layout information.
        !          1743:      */
        !          1744: 
        !          1745:     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
        !          1746:     dInfoPtr->dLinePtr = NULL;
        !          1747: 
        !          1748:     /*
        !          1749:      * Recompute some overall things for the layout.
        !          1750:      */
        !          1751: 
        !          1752:     dInfoPtr->x = textPtr->borderWidth + textPtr->padX;
        !          1753:     dInfoPtr->y = textPtr->borderWidth + textPtr->padY;
        !          1754:     dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - dInfoPtr->x;
        !          1755:     dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - dInfoPtr->y;
        !          1756:     dInfoPtr->topOfEof = dInfoPtr->maxY;
        !          1757: 
        !          1758:     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
        !          1759: //     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
        !          1760:         assert(textPtr->updateTimerToken == NULL);
        !          1761:        if (textPtr->updateTimerToken == NULL) {
        !          1762:            textPtr->updateTimerToken =
        !          1763:              Tk_CreateTimerHandler(
        !          1764:                TextUpdateTime,
        !          1765:                DisplayText,
        !          1766:                (ClientData) textPtr);
        !          1767:        }
        !          1768:     }
        !          1769:     dInfoPtr->flags |= REDRAW_PENDING | REDRAW_BORDERS | DINFO_OUT_OF_DATE | REPICK_NEEDED;
        !          1770: }
        !          1771: 
        !          1772: /*
        !          1773:  *----------------------------------------------------------------------
        !          1774:  *
        !          1775:  * TkTextSetView --
        !          1776:  *
        !          1777:  *     This procedure is called to specify what lines are to be
        !          1778:  *     displayed in a text widget.
        !          1779:  *
        !          1780:  * Results:
        !          1781:  *     None.
        !          1782:  *
        !          1783:  * Side effects:
        !          1784:  *     The display will (eventually) be updated so that the line
        !          1785:  *     given by "line" is visible on the screen at the position
        !          1786:  *     determined by "pickPlace".
        !          1787:  *
        !          1788:  *----------------------------------------------------------------------
        !          1789:  */
        !          1790: 
        !          1791: void
        !          1792: TkTextSetView(textPtr, line, pickPlace)
        !          1793:     TkText *textPtr;           /* Widget record for text widget. */
        !          1794:     int line;                  /* Number of line that is to appear somewhere
        !          1795:                                 * in the window.  This line number must
        !          1796:                                 * be a valid one in the file. */
        !          1797:     int pickPlace;             /* 0 means topLine must appear at top of
        !          1798:                                 * screen.  1 means we get to pick where it
        !          1799:                                 * appears:  minimize screen motion or else
        !          1800:                                 * display line at center of screen. */
        !          1801: {
        !          1802:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          1803:     register DLine *dlPtr, *dlPtr2;
        !          1804:     TkTextLine *linePtr;
        !          1805:     int curTopLine, curBotLine;
        !          1806:     int bottomY;
        !          1807:     TagInfo tagInfo;
        !          1808: #define CLOSE_LINES 5
        !          1809: 
        !          1810:     if (!pickPlace) {
        !          1811:        /*
        !          1812:         * The line must go at the top of the screen.  See if the new
        !          1813:         * topmost line is already somewhere on the screen.  If so then
        !          1814:         * delete all the DLine structures ahead of it.  Otherwise just
        !          1815:         * leave all the DLine's alone (if the new topmost line is above
        !          1816:         * the top of the current window, i.e. we're scrolling back towards
        !          1817:         * the beginning of the file we may be able to reuse some of the
        !          1818:         * information that's currently on the screen without redisplaying
        !          1819:         * it all.
        !          1820:         */
        !          1821:     
        !          1822:        dlPtr = FindDLine(dInfoPtr->dLinePtr, line);
        !          1823:        if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
        !          1824:            FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
        !          1825:        }
        !          1826:     
        !          1827:        textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
        !          1828:        goto scheduleUpdate;
        !          1829:     }
        !          1830: 
        !          1831:     /*
        !          1832:      * We have to pick where to display the given line.  First, bring
        !          1833:      * the display information up to date and see if the line will be
        !          1834:      * completely visible in the current screen configuration.  If so
        !          1835:      * then there's nothing to do.
        !          1836:      */
        !          1837: 
        !          1838:     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
        !          1839:        UpdateDisplayInfo(textPtr);
        !          1840:     }
        !          1841:     linePtr = TkBTreeFindLine(textPtr->tree, line);
        !          1842:     /* DEH: return if dlPtr is null */
        !          1843:     if ((dlPtr = dInfoPtr->dLinePtr) == NULL)
        !          1844:         return;
        !          1845:     for (; ; dlPtr = dlPtr->nextPtr) {
        !          1846:        if (dlPtr->nextPtr == NULL) {
        !          1847:            break;
        !          1848:        }
        !          1849:        if ((dlPtr->linePtr == linePtr)
        !          1850:                && (dlPtr->nextPtr->linePtr != linePtr)) {
        !          1851:            break;
        !          1852:        }
        !          1853:     }
        !          1854:     if ((dlPtr->linePtr == linePtr)
        !          1855:            && ((dlPtr->y + dlPtr->height) <= dInfoPtr->maxY)) {
        !          1856:        return;
        !          1857:     }
        !          1858: 
        !          1859:     /*
        !          1860:      * The desired line isn't already on-screen.  See if it is within
        !          1861:      * a few lines of the top of the window.  If so then just make it
        !          1862:      * the top line on the screen.
        !          1863:      */
        !          1864: 
        !          1865:     bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
        !          1866:     curTopLine = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
        !          1867:     if (line < curTopLine) {
        !          1868:        if (line >= (curTopLine-CLOSE_LINES)) {
        !          1869:            textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
        !          1870:            goto scheduleUpdate;
        !          1871:        }
        !          1872:     } else {
        !          1873:        /*
        !          1874:         * The desired line is below the bottom of the screen.  If it is
        !          1875:         * within a few lines of the bottom of the screen then position
        !          1876:         * it at the bottom of the screen. (At this point dlPtr points to
        !          1877:         * the last line on the screen)
        !          1878:         */
        !          1879:     
        !          1880:        curBotLine = TkBTreeLineIndex(dlPtr->linePtr);
        !          1881:        if (line <= (curBotLine+5)) {
        !          1882:            bottomY = dInfoPtr->maxY;
        !          1883:        }
        !          1884:     }
        !          1885: 
        !          1886:     /*
        !          1887:      * Our job now is arrange the display so that "line" appears as
        !          1888:      * low on the screen as possible but with its bottom no lower
        !          1889:      * than bottomY (bottomY is the bottom of the window if the
        !          1890:      * desired line is just below the current screen, otherwise it
        !          1891:      * is the center of the window.  Work upwards (through smaller
        !          1892:      * line numbers) computing how much space lines take, until we
        !          1893:      * fine the line that should be at the top of the screen.
        !          1894:      */
        !          1895: 
        !          1896:     for (textPtr->topLinePtr = linePtr = TkBTreeFindLine(textPtr->tree, line);
        !          1897:            ; line--, textPtr->topLinePtr = linePtr,
        !          1898:            linePtr = TkBTreeFindLine(textPtr->tree, line)) {
        !          1899:        tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
        !          1900:                &tagInfo.numTags);
        !          1901:        tagInfo.arraySize = tagInfo.numTags;
        !          1902:        TkBTreeStartSearch(textPtr->tree, line, 1, line+1, 0,
        !          1903:                (TkTextTag *) NULL, &tagInfo.search);
        !          1904:        TkBTreeNextTag(&tagInfo.search);
        !          1905:        dlPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
        !          1906:        for (dlPtr2 = dlPtr; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
        !          1907:            bottomY -= dlPtr2->height;
        !          1908:        }
        !          1909:        FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
        !          1910:        if (tagInfo.tagPtrs != NULL) {
        !          1911:            ckfree((char *) tagInfo.tagPtrs);
        !          1912:        }
        !          1913:        if ((bottomY <= 0) || (line <= 0)) {
        !          1914:            break;
        !          1915:        }
        !          1916:     }
        !          1917: 
        !          1918:     scheduleUpdate:
        !          1919:     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
        !          1920: //     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
        !          1921:         assert(textPtr->updateTimerToken == NULL);
        !          1922:        if (textPtr->updateTimerToken == NULL) {
        !          1923:            textPtr->updateTimerToken =
        !          1924:              Tk_CreateTimerHandler(
        !          1925:                TextUpdateTime,
        !          1926:                DisplayText,
        !          1927:                (ClientData) textPtr);
        !          1928:        }
        !          1929:     }
        !          1930:     dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
        !          1931: }
        !          1932: 
        !          1933: /*
        !          1934:  *----------------------------------------------------------------------
        !          1935:  *
        !          1936:  * FindDLine --
        !          1937:  *
        !          1938:  *     This procedure is called to find the DLine corresponding to a
        !          1939:  *     given text line.
        !          1940:  *
        !          1941:  * Results:
        !          1942:  *     The return value is a pointer to the first DLine found in the
        !          1943:  *     list headed by dlPtr whose line number is greater or equal to
        !          1944:  *     line.  If there is no such line in the list then NULL is returned.
        !          1945:  *
        !          1946:  * Side effects:
        !          1947:  *     None.
        !          1948:  *
        !          1949:  *----------------------------------------------------------------------
        !          1950:  */
        !          1951: 
        !          1952: static DLine *
        !          1953: FindDLine(dlPtr, line)
        !          1954:     register DLine *dlPtr;     /* Pointer to first in list of DLines
        !          1955:                                 * to search. */
        !          1956:     int line;                  /* Line number in text that is desired. */
        !          1957: {
        !          1958:     TkTextLine *linePtr;
        !          1959:     int thisLine;
        !          1960: 
        !          1961:     if (dlPtr == NULL) {
        !          1962:        return NULL;
        !          1963:     }
        !          1964:     thisLine = TkBTreeLineIndex(dlPtr->linePtr);
        !          1965:     while (thisLine < line) {
        !          1966:        /*
        !          1967:         * This DLine isn't the right one.  Go on to the next DLine
        !          1968:         * (skipping multiple DLine's for the same text line).
        !          1969:         */
        !          1970: 
        !          1971:        linePtr = dlPtr->linePtr;
        !          1972:        do {
        !          1973:            dlPtr = dlPtr->nextPtr;
        !          1974:            if (dlPtr == NULL) {
        !          1975:                return NULL;
        !          1976:            }
        !          1977:        } while (dlPtr->linePtr == linePtr);
        !          1978: 
        !          1979:        /*
        !          1980:         * Step through text lines, keeping track of the line number
        !          1981:         * we're on, until we catch up to dlPtr (remember, there could
        !          1982:         * be gaps in the DLine list where DLine's have been deleted).
        !          1983:         */
        !          1984: 
        !          1985:        do {
        !          1986:            linePtr = TkBTreeNextLine(linePtr);
        !          1987:            thisLine++;
        !          1988:            if (linePtr == NULL) {
        !          1989:                panic("FindDLine reached end of text");
        !          1990:            }
        !          1991:        } while (linePtr != dlPtr->linePtr);
        !          1992:     }
        !          1993:     return dlPtr;
        !          1994: }
        !          1995: 
        !          1996: /*
        !          1997:  *----------------------------------------------------------------------
        !          1998:  *
        !          1999:  * TkTextCharAtLoc --
        !          2000:  *
        !          2001:  *     Given an (x,y) coordinate on the screen, find the location of
        !          2002:  *     the closest character to that location.
        !          2003:  *
        !          2004:  * Results:
        !          2005:  *     The return value is a pointer to the text line containing the
        !          2006:  *     character displayed closest to (x,y).  The value at *chPtr is
        !          2007:  *     overwritten with the index with that line of the closest
        !          2008:  *     character.
        !          2009:  *
        !          2010:  * Side effects:
        !          2011:  *     None.
        !          2012:  *
        !          2013:  *----------------------------------------------------------------------
        !          2014:  */
        !          2015: 
        !          2016: TkTextLine *
        !          2017: TkTextCharAtLoc(textPtr, x, y, chPtr)
        !          2018:     TkText *textPtr;           /* Widget record for text widget. */
        !          2019:     int x, y;                  /* Pixel coordinates of point in widget's
        !          2020:                                 * window. */
        !          2021:     int *chPtr;                        /* Place to store index-within-line of
        !          2022:                                 * closest character. */
        !          2023: {
        !          2024:     DInfo *dInfoPtr = textPtr->dInfoPtr;
        !          2025:     register DLine *dlPtr;
        !          2026:     register Chunk *chunkPtr;
        !          2027:     int count;
        !          2028:     int endX;
        !          2029: 
        !          2030:     /*
        !          2031:      * Make sure that all of the layout information about what's
        !          2032:      * displayed where on the screen is up-to-date.
        !          2033:      */
        !          2034: 
        !          2035:     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
        !          2036:        UpdateDisplayInfo(textPtr);
        !          2037:     }
        !          2038: 
        !          2039:     /*
        !          2040:      * If the coordinates are above the top of the window, then adjust
        !          2041:      * them to refer to the upper-right corner of the window.
        !          2042:      */
        !          2043: 
        !          2044:     if (y < dInfoPtr->y) {
        !          2045:        y = dInfoPtr->y;
        !          2046:        x = dInfoPtr->x;
        !          2047:     } else if (y >= dInfoPtr->topOfEof) {
        !          2048:        y = dInfoPtr->topOfEof;
        !          2049:        x = dInfoPtr->maxX;
        !          2050:     }
        !          2051:     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
        !          2052:        if (y > (dlPtr->y + dlPtr->height)) {
        !          2053:            if (dlPtr->nextPtr != NULL) {
        !          2054:                continue;
        !          2055:            }
        !          2056: 
        !          2057:            /*
        !          2058:             * The coordinates are off the bottom of the window.  Adjust
        !          2059:             * them to refer to the lower-right character on the window.
        !          2060:             */
        !          2061: 
        !          2062:            y = dlPtr->y;
        !          2063:            x = dInfoPtr->maxX;
        !          2064:        }
        !          2065:        for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
        !          2066:            if ((chunkPtr->nextPtr == NULL) || (chunkPtr->nextPtr->x > x)) {
        !          2067:                break;
        !          2068:            }
        !          2069:        }
        !          2070:        count = TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
        !          2071:            chunkPtr->text, chunkPtr->numChars, chunkPtr->x, x, 0, &endX);
        !          2072:        if (count >= chunkPtr->numChars) {
        !          2073:            /*
        !          2074:             * The point is off the end of the line.  Return the character
        !          2075:             * after the last one that fit, unless that character appears
        !          2076:             * as the first character on the next DLine or unless the last
        !          2077:             * one that fit extends beyond the edge of the window.
        !          2078:             */
        !          2079: 
        !          2080:            if ((dlPtr->nextPtr != NULL)
        !          2081:                    && (dlPtr->nextPtr->chunkPtr->text
        !          2082:                    == (chunkPtr->text + chunkPtr->numChars))) {
        !          2083:                count = chunkPtr->numChars-1;
        !          2084:            }
        !          2085:            if (endX >= dInfoPtr->maxX) {
        !          2086:                count = chunkPtr->numChars-1;
        !          2087:            }
        !          2088:        }
        !          2089:        *chPtr = count + (chunkPtr->text - dlPtr->linePtr->bytes);
        !          2090:        return dlPtr->linePtr;
        !          2091:     }
        !          2092:     panic("TkTextCharAtLoc ran out of lines");
        !          2093:     return (TkTextLine *) NULL;
        !          2094: }

unix.superglobalmegacorp.com

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