Annotation of micropolis/src/tk/tktxdisp.c, revision 1.1.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.