|
|
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.