|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.