4 * This module provides facilities to display text widgets. It is
5 * the only place where information is kept about the screen layout
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 1995 Christian Werner
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 * The following structure describes how to display a range of characters.
22 * The information is generated by scanning all of the tags associated
23 * with the characters and combining that with default information for
24 * the overall widget. These structures form the hash keys for
25 * dInfoPtr->styleTable.
28 typedef struct StyleValues {
30 int justify; /* Justification style for text. */
31 int lMargin1; /* Left margin, in pixels, for first display
32 * line of each text line. */
33 int lMargin2; /* Left margin, in pixels, for second and
34 * later display lines of each text line. */
35 int rMargin; /* Right margin, in pixels. */
36 CkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
38 Ck_Uid wrapMode; /* How to handle wrap-around for this tag.
39 * One of ckTextCharUid, ckTextNoneUid,
40 * or ckTextWordUid. */
44 * The following structure extends the StyleValues structure above with
45 * graphics contexts used to actually draw the characters. The entries
46 * in dInfoPtr->styleTable point to structures of this type.
49 typedef struct Style {
50 int refCount; /* Number of times this structure is
51 * referenced in Chunks. */
52 StyleValues *sValuePtr; /* Raw information from which GCs were
54 Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable. Used
59 * The following macro determines whether two styles have the same
60 * background so that, for example, no beveled border should be drawn
64 #define SAME_BACKGROUND(s1, s2) \
65 (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
66 && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
67 && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
68 && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
71 * The following structure describes one line of the display, which may
72 * be either part or all of one line of the text.
75 typedef struct DLine {
76 CkTextIndex index; /* Identifies first character in text
77 * that is displayed on this line. */
78 int count; /* Number of characters accounted for by this
79 * display line, including a trailing space
80 * or newline that isn't actually displayed. */
81 int y; /* Y-position at which line is supposed to
82 * be drawn (topmost pixel of rectangular
83 * area occupied by line). */
84 int oldY; /* Y-position at which line currently
85 * appears on display. -1 means line isn't
86 * currently visible on display and must be
87 * redrawn. This is used to move lines by
88 * scrolling rather than re-drawing. */
89 int height; /* Height of line, in chars. */
90 int length; /* Total length of line, in chars. */
91 CkTextDispChunk *chunkPtr; /* Pointer to first chunk in list of all
92 * of those that are displayed on this
93 * line of the screen. */
94 struct DLine *nextPtr; /* Next in list of all display lines for
95 * this window. The list is sorted in
96 * order from top to bottom. Note: the
97 * next DLine doesn't always correspond
98 * to the next line of text: (a) can have
99 * multiple DLines for one text line, and
100 * (b) can have gaps where DLine's have been
101 * deleted because they're out of date. */
102 int flags; /* Various flag bits: see below for values. */
106 * Flag bits for DLine structures:
108 * NEW_LAYOUT - Non-zero means that the line has been
109 * re-layed out since the last time the
110 * display was updated.
111 * TOP_LINE - Non-zero means that this was the top line
112 * in the window the last time that the window
113 * was laid out. This is important because
114 * a line may be displayed differently if its
115 * at the top or bottom than if it's in the
116 * middle (e.g. beveled edges aren't displayed
117 * for middle lines if the adjacent line has
118 * a similar background).
119 * BOTTOM_LINE - Non-zero means that this was the bottom line
120 * in the window the last time that the window
126 #define BOTTOM_LINE 8
129 * Overall display information for a text widget:
132 typedef struct DInfo {
133 Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
134 * to Styles for this widget. */
135 DLine *dLinePtr; /* First in list of all display lines for
136 * this widget, in order from top to bottom. */
137 int x; /* First x-coordinate that may be used for
138 * actually displaying line information.
139 * Leaves space for border, etc. */
140 int y; /* First y-coordinate that may be used for
141 * actually displaying line information.
142 * Leaves space for border, etc. */
143 int maxX; /* First x-coordinate to right of available
144 * space for displaying lines. */
145 int maxY; /* First y-coordinate below available
146 * space for displaying lines. */
147 int topOfEof; /* Top-most pixel (lowest y-value) that has
148 * been drawn in the appropriate fashion for
149 * the portion of the window after the last
150 * line of the text. This field is used to
151 * figure out when to redraw part or all of
155 * Information used for scrolling:
158 int newCharOffset; /* Desired x scroll position, measured as the
159 * number of average-size characters off-screen
160 * to the left for a line with no left
162 int curOffset; /* Actual x scroll position, measured as the
163 * number of chars off-screen to the left. */
164 int maxLength; /* Length in chars of longest line that's
165 * visible in window (length may exceed window
166 * size). If there's no wrapping, this will
168 double xScrollFirst, xScrollLast;
169 /* Most recent values reported to horizontal
170 * scrollbar; used to eliminate unnecessary
172 double yScrollFirst, yScrollLast;
173 /* Most recent values reported to vertical
174 * scrollbar; used to eliminate unnecessary
178 * Miscellaneous information:
181 int dLinesInvalidated; /* This value is set to 1 whenever something
182 * happens that invalidates information in
183 * DLine structures; if a redisplay
184 * is in progress, it will see this and
185 * abort the redisplay. This is needed
186 * because, for example, an embedded window
187 * could change its size when it is first
188 * displayed, invalidating the DLine that
189 * is currently being displayed. If redisplay
190 * continues, it will use freed memory and
191 * could dump core. */
192 int flags; /* Various flag values: see below for
197 * In CkTextDispChunk structures for character segments, the clientData
198 * field points to one of the following structures:
201 typedef struct CharInfo {
202 int numChars; /* Number of characters to display. */
203 CkWindow *winPtr; /* For Ck_SetWindowAttr. */
204 char chars[4]; /* Characters to display. Actual size
205 * will be numChars, not 4. THIS MUST BE
206 * THE LAST FIELD IN THE STRUCTURE. */
210 * Flag values for DInfo structures:
212 * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
213 * for this window are partially or completely
214 * out of date and need to be recomputed.
215 * REDRAW_PENDING: Means that a when-idle handler has been
216 * scheduled to update the display.
217 * REDRAW_BORDERS: Means window border or pad area has
218 * potentially been damaged and must be redrawn.
219 * REPICK_NEEDED: 1 means that the widget has been modified
220 * in a way that could change the current
221 * character (a different character might be
222 * under the mouse cursor now). Need to
223 * recompute the current character before
224 * the next redisplay.
227 #define DINFO_OUT_OF_DATE 1
228 #define REDRAW_PENDING 2
229 #define REDRAW_BORDERS 4
230 #define REPICK_NEEDED 8
233 * The following counters keep statistics about redisplay that can be
234 * checked to see how clever this code is at reducing redisplays.
237 static int numRedisplays; /* Number of calls to DisplayText. */
238 static int linesRedrawn; /* Number of calls to DisplayDLine. */
241 * Forward declarations for procedures defined later in this file:
244 static void AdjustForTab _ANSI_ARGS_((CkText *textPtr,
245 CkTextTabArray *tabArrayPtr, int index,
246 CkTextDispChunk *chunkPtr));
247 static void CharBboxProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
248 int index, int y, int lineHeight, int baseline,
249 int *xPtr, int *yPtr, int *widthPtr,
251 static void CharDisplayProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
252 int x, int y, int height, int baseline,
253 WINDOW *window, int screenY));
254 static int CharMeasureProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
256 static void CharUndisplayProc _ANSI_ARGS_((CkText *textPtr,
257 CkTextDispChunk *chunkPtr));
258 static void DisplayDLine _ANSI_ARGS_((CkText *textPtr,
259 DLine *dlPtr, DLine *prevPtr, WINDOW *window));
260 static void DisplayText _ANSI_ARGS_((ClientData clientData));
261 static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr,
262 CkTextIndex *indexPtr));
263 static void FreeDLines _ANSI_ARGS_((CkText *textPtr,
264 DLine *firstPtr, DLine *lastPtr, int unlink));
265 static void FreeStyle _ANSI_ARGS_((CkText *textPtr,
267 static Style * GetStyle _ANSI_ARGS_((CkText *textPtr,
268 CkTextIndex *indexPtr));
269 static void GetXView _ANSI_ARGS_((Tcl_Interp *interp,
270 CkText *textPtr, int report));
271 static void GetYView _ANSI_ARGS_((Tcl_Interp *interp,
272 CkText *textPtr, int report));
273 static DLine * LayoutDLine _ANSI_ARGS_((CkText *textPtr,
274 CkTextIndex *indexPtr));
275 static void MeasureUp _ANSI_ARGS_((CkText *textPtr,
276 CkTextIndex *srcPtr, int distance,
277 CkTextIndex *dstPtr));
278 static void UpdateDisplayInfo _ANSI_ARGS_((CkText *textPtr));
279 static void ScrollByLines _ANSI_ARGS_((CkText *textPtr,
281 static int SizeOfTab _ANSI_ARGS_((CkText *textPtr,
282 CkTextTabArray *tabArrayPtr, int index, int x,
286 *----------------------------------------------------------------------
288 * CkTextCreateDInfo --
290 * This procedure is called when a new text widget is created.
291 * Its job is to set up display-related information for the widget.
297 * A DInfo data structure is allocated and initialized and attached
300 *----------------------------------------------------------------------
304 CkTextCreateDInfo(textPtr)
305 CkText *textPtr; /* Overall information for text widget. */
307 register DInfo *dInfoPtr;
309 dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
310 Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
311 dInfoPtr->dLinePtr = NULL;
312 dInfoPtr->topOfEof = 0;
313 dInfoPtr->newCharOffset = 0;
314 dInfoPtr->curOffset = 0;
315 dInfoPtr->maxLength = 0;
316 dInfoPtr->xScrollFirst = -1;
317 dInfoPtr->xScrollLast = -1;
318 dInfoPtr->yScrollFirst = -1;
319 dInfoPtr->yScrollLast = -1;
320 dInfoPtr->dLinesInvalidated = 0;
321 dInfoPtr->flags = DINFO_OUT_OF_DATE;
322 textPtr->dInfoPtr = dInfoPtr;
326 *----------------------------------------------------------------------
330 * This procedure is called to free up all of the private display
331 * information kept by this file for a text widget.
337 * Lots of resources get freed.
339 *----------------------------------------------------------------------
343 CkTextFreeDInfo(textPtr)
344 CkText *textPtr; /* Overall information for text widget. */
346 register DInfo *dInfoPtr = textPtr->dInfoPtr;
349 * Be careful to free up styleTable *after* freeing up all the
350 * DLines, so that the hash table is still intact to free up the
351 * style-related information from the lines. Once the lines are
352 * all free then styleTable will be empty.
355 FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
356 Tcl_DeleteHashTable(&dInfoPtr->styleTable);
357 if (dInfoPtr->flags & REDRAW_PENDING) {
358 Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
360 ckfree((char *) dInfoPtr);
364 *----------------------------------------------------------------------
368 * This procedure creates all the information needed to display
369 * text at a particular location.
372 * The return value is a pointer to a Style structure that
373 * corresponds to *sValuePtr.
376 * A new entry may be created in the style table for the widget.
378 *----------------------------------------------------------------------
382 GetStyle(textPtr, indexPtr)
383 CkText *textPtr; /* Overall information about text widget. */
384 CkTextIndex *indexPtr; /* The character in the text for which
385 * display information is wanted. */
388 register CkTextTag *tagPtr;
389 StyleValues styleValues;
395 * The variables below keep track of the highest-priority specification
396 * that has occurred for each of the various fields of the StyleValues.
399 int bgPrio, fgPrio, attrPrio, justifyPrio;
400 int lMargin1Prio, lMargin2Prio, rMarginPrio;
401 int tabPrio, wrapPrio;
404 * Find out what tags are present for the character, then compute
405 * a StyleValues structure corresponding to those tags (scan
406 * through all of the tags, saving information for the highest-
410 tagPtrs = CkBTreeGetTags(indexPtr, &numTags);
411 bgPrio = fgPrio = attrPrio = justifyPrio = -1;
412 lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
413 tabPrio = wrapPrio = -1;
414 memset((VOID *) &styleValues, 0, sizeof(StyleValues));
415 styleValues.fg = textPtr->fg;
416 styleValues.bg = textPtr->bg;
417 styleValues.attr = textPtr->attr;
418 styleValues.justify = CK_JUSTIFY_LEFT;
419 styleValues.tabArrayPtr = textPtr->tabArrayPtr;
420 styleValues.wrapMode = textPtr->wrapMode;
421 for (i = 0 ; i < numTags; i++) {
423 if ((tagPtr->bg != -1) && (tagPtr->priority > bgPrio)) {
424 styleValues.bg = tagPtr->bg;
425 bgPrio = tagPtr->priority;
427 if ((tagPtr->fg != -1) && (tagPtr->priority > fgPrio)) {
428 styleValues.fg = tagPtr->fg;
429 fgPrio = tagPtr->priority;
431 if ((tagPtr->attr != -1) && (tagPtr->priority > attrPrio)) {
432 styleValues.attr = tagPtr->attr;
433 attrPrio = tagPtr->priority;
435 if ((tagPtr->justifyString != NULL)
436 && (tagPtr->priority > justifyPrio)) {
437 styleValues.justify = tagPtr->justify;
438 justifyPrio = tagPtr->priority;
440 if ((tagPtr->lMargin1String != NULL)
441 && (tagPtr->priority > lMargin1Prio)) {
442 styleValues.lMargin1 = tagPtr->lMargin1;
443 lMargin1Prio = tagPtr->priority;
445 if ((tagPtr->lMargin2String != NULL)
446 && (tagPtr->priority > lMargin2Prio)) {
447 styleValues.lMargin2 = tagPtr->lMargin2;
448 lMargin2Prio = tagPtr->priority;
450 if ((tagPtr->rMarginString != NULL)
451 && (tagPtr->priority > rMarginPrio)) {
452 styleValues.rMargin = tagPtr->rMargin;
453 rMarginPrio = tagPtr->priority;
455 if ((tagPtr->tabString != NULL)
456 && (tagPtr->priority > tabPrio)) {
457 styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
458 tabPrio = tagPtr->priority;
460 if ((tagPtr->wrapMode != NULL)
461 && (tagPtr->priority > wrapPrio)) {
462 styleValues.wrapMode = tagPtr->wrapMode;
463 wrapPrio = tagPtr->priority;
466 if (tagPtrs != NULL) {
467 ckfree((char *) tagPtrs);
471 * Use an existing style if there's one around that matches.
474 hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
475 (char *) &styleValues, &new);
477 stylePtr = (Style *) Tcl_GetHashValue(hPtr);
478 stylePtr->refCount++;
483 * No existing style matched. Make a new one.
486 stylePtr = (Style *) ckalloc(sizeof(Style));
487 stylePtr->refCount = 1;
488 stylePtr->sValuePtr = (StyleValues *)
489 Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
490 stylePtr->hPtr = hPtr;
491 Tcl_SetHashValue(hPtr, stylePtr);
496 *----------------------------------------------------------------------
500 * This procedure is called when a Style structure is no longer
501 * needed. It decrements the reference count and frees up the
502 * space for the style structure if the reference count is 0.
508 * The storage and other resources associated with the style
509 * are freed up if no-one's still using it.
511 *----------------------------------------------------------------------
515 FreeStyle(textPtr, stylePtr)
516 CkText *textPtr; /* Information about overall widget. */
517 register Style *stylePtr; /* Information about style to be freed. */
520 stylePtr->refCount--;
521 if (stylePtr->refCount == 0) {
522 Tcl_DeleteHashEntry(stylePtr->hPtr);
523 ckfree((char *) stylePtr);
528 *----------------------------------------------------------------------
532 * This procedure generates a single DLine structure for a display
533 * line whose leftmost character is given by indexPtr.
536 * The return value is a pointer to a DLine structure desribing the
537 * display line. All fields are filled in and correct except for
541 * Storage is allocated for the new DLine.
543 *----------------------------------------------------------------------
547 LayoutDLine(textPtr, indexPtr)
548 CkText *textPtr; /* Overall information about text widget. */
549 CkTextIndex *indexPtr; /* Beginning of display line. May not
550 * necessarily point to a character segment. */
552 register DLine *dlPtr; /* New display line. */
553 CkTextSegment *segPtr; /* Current segment in text. */
554 CkTextDispChunk *lastChunkPtr; /* Last chunk allocated so far
556 CkTextDispChunk *chunkPtr; /* Current chunk. */
557 CkTextIndex curIndex;
558 CkTextDispChunk *breakChunkPtr; /* Chunk containing best word break
560 CkTextIndex breakIndex; /* Index of first character in
562 int breakCharOffset; /* Character within breakChunkPtr just
563 * to right of best break point. */
564 int noCharsYet; /* Non-zero means that no characters
565 * have been placed on the line yet. */
566 int justify; /* How to justify line: taken from
567 * style for first character in line. */
568 int jIndent; /* Additional indentation (beyond
569 * margins) due to justification. */
570 int rMargin; /* Right margin width for line. */
571 Ck_Uid wrapMode; /* Wrap mode to use for this line. */
572 int x = 0, maxX = 0; /* Initializations needed only to
573 * stop compiler warnings. */
574 int wholeLine; /* Non-zero means this display line
575 * runs to the end of the text line. */
576 int tabIndex; /* Index of the current tab stop. */
577 int gotTab; /* Non-zero means the current chunk
579 CkTextDispChunk *tabChunkPtr; /* Pointer to the chunk containing
580 * the previous tab stop. */
581 int maxChars; /* Maximum number of characters to
582 * include in this chunk. */
583 CkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
584 * style for first character on line. */
585 int tabSize; /* Number of pixels consumed by current
588 StyleValues *sValuePtr;
591 * Create and initialize a new DLine structure.
594 dlPtr = (DLine *) ckalloc(sizeof(DLine));
595 dlPtr->index = *indexPtr;
600 dlPtr->chunkPtr = NULL;
601 dlPtr->nextPtr = NULL;
602 dlPtr->flags = NEW_LAYOUT;
605 * Each iteration of the loop below creates one CkTextDispChunk for
606 * the new display line. The line will always have at least one
607 * chunk (for the newline character at the end, if there's nothing
611 curIndex = *indexPtr;
615 breakChunkPtr = NULL;
617 justify = CK_JUSTIFY_LEFT;
622 wrapMode = ckTextCharUid;
626 * Find the first segment to consider for the line. Can't call
627 * CkTextIndexToSeg for this because it won't return a segment
628 * with zero size (such as the insertion cursor's mark).
631 for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
632 (offset > 0) && (offset >= segPtr->size);
633 offset -= segPtr->size, segPtr = segPtr->nextPtr) {
634 /* Empty loop body. */
637 while (segPtr != NULL) {
638 if (segPtr->typePtr->layoutProc == NULL) {
639 segPtr = segPtr->nextPtr;
643 if (chunkPtr == NULL) {
644 chunkPtr = (CkTextDispChunk *) ckalloc(sizeof(CkTextDispChunk));
645 chunkPtr->nextPtr = NULL;
647 chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
650 * Save style information such as justification and indentation,
651 * up until the first character is encountered, then retain that
652 * information for the rest of the line.
656 tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
657 justify = chunkPtr->stylePtr->sValuePtr->justify;
658 rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
659 wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
660 x = ((curIndex.charIndex == 0)
661 ? chunkPtr->stylePtr->sValuePtr->lMargin1
662 : chunkPtr->stylePtr->sValuePtr->lMargin2);
663 if (wrapMode == ckTextNoneUid) {
666 maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
675 * See if there is a tab in the current chunk; if so, only
676 * layout characters up to (and including) the tab.
680 maxChars = segPtr->size - offset;
681 if (justify == CK_JUSTIFY_LEFT) {
682 if (segPtr->typePtr == &ckTextCharType) {
685 for (p = segPtr->body.chars + offset; *p != 0; p++) {
687 maxChars = (p + 1 - segPtr->body.chars) - offset;
696 code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
697 offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
700 FreeStyle(textPtr, chunkPtr->stylePtr);
703 * This segment doesn't wish to display itself (e.g. most
707 segPtr = segPtr->nextPtr;
713 * No characters from this segment fit in the window: this
714 * means we're at the end of the display line.
717 if (chunkPtr != NULL) {
718 ckfree((char *) chunkPtr);
722 if (chunkPtr->numChars > 0) {
725 if (lastChunkPtr == NULL) {
726 dlPtr->chunkPtr = chunkPtr;
728 lastChunkPtr->nextPtr = chunkPtr;
730 lastChunkPtr = chunkPtr;
731 x += chunkPtr->width;
732 if (chunkPtr->breakIndex > 0) {
733 breakCharOffset = chunkPtr->breakIndex;
734 breakIndex = curIndex;
735 breakChunkPtr = chunkPtr;
737 if (chunkPtr->numChars != maxChars) {
742 * If we're at a new tab, adjust the layout for all the chunks
743 * pertaining to the previous tab. Also adjust the amount of
744 * space left in the line to account for space that will be eaten
750 AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
751 x = chunkPtr->x + chunkPtr->width;
754 tabChunkPtr = chunkPtr;
755 tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
756 if (tabSize >= (maxX - x)) {
760 curIndex.charIndex += chunkPtr->numChars;
761 offset += chunkPtr->numChars;
762 if (offset >= segPtr->size) {
764 segPtr = segPtr->nextPtr;
769 panic("LayoutDLine couldn't place any characters on a line");
771 wholeLine = (segPtr == NULL);
774 * We're at the end of the display line. Throw away everything
775 * after the most recent word break, if there is one; this may
776 * potentially require the last chunk to be layed out again.
779 if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
780 || (breakCharOffset != lastChunkPtr->numChars))) {
782 chunkPtr = breakChunkPtr->nextPtr;
783 if (chunkPtr == NULL) {
786 FreeStyle(textPtr, chunkPtr->stylePtr);
787 breakChunkPtr->nextPtr = chunkPtr->nextPtr;
788 (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
789 ckfree((char *) chunkPtr);
791 if (breakCharOffset != breakChunkPtr->numChars) {
792 (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
793 segPtr = CkTextIndexToSeg(&breakIndex, &offset);
794 (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
795 segPtr, offset, maxX, breakCharOffset, 0,
796 wrapMode, breakChunkPtr);
798 lastChunkPtr = breakChunkPtr;
803 * Make tab adjustments for the last tab stop, if there is one.
806 if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
807 AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
811 * Make one more pass over the line to recompute various things
812 * like its height, length, and total number of characters. Also
813 * modify the x-locations of chunks to reflect justification.
814 * If we're not wrapping, I'm not sure what is the best way to
815 * handle left and center justification: should the total length,
816 * for purposes of justification, be (a) the window width, (b)
817 * the length of the longest line in the window, or (c) the length
818 * of the longest line in the text? (c) isn't available, (b) seems
819 * weird, since it can change with vertical scrolling, so (a) is
820 * what is implemented below.
823 if (wrapMode == ckTextNoneUid) {
824 maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
826 dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
827 if (justify == CK_JUSTIFY_LEFT) {
829 } else if (justify == CK_JUSTIFY_RIGHT) {
830 jIndent = maxX - dlPtr->length;
832 jIndent = (maxX - dlPtr->length)/2;
834 for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
835 chunkPtr = chunkPtr->nextPtr) {
836 chunkPtr->x += jIndent;
837 dlPtr->count += chunkPtr->numChars;
838 if (chunkPtr->minHeight > dlPtr->height) {
839 dlPtr->height = chunkPtr->minHeight;
841 sValuePtr = chunkPtr->stylePtr->sValuePtr;
843 sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
846 * Recompute line length: may have changed because of justification.
849 dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
854 *----------------------------------------------------------------------
856 * UpdateDisplayInfo --
858 * This procedure is invoked to recompute some or all of the
859 * DLine structures for a text widget. At the time it is called
860 * the DLine structures still left in the widget are guaranteed
861 * to be correct except that (a) the y-coordinates aren't
862 * necessarily correct, (b) there may be missing structures
863 * (the DLine structures get removed as soon as they are potentially
864 * out-of-date), and (c) DLine structures that don't start at the
865 * beginning of a line may be incorrect if previous information in
866 * the same line changed size in a way that moved a line boundary
867 * (DLines for any info that changed will have been deleted, but
868 * not DLines for unchanged info in the same text line).
874 * Upon return, the DLine information for textPtr correctly reflects
875 * the positions where characters will be displayed. However, this
876 * procedure doesn't actually bring the display up-to-date.
878 *----------------------------------------------------------------------
882 UpdateDisplayInfo(textPtr)
883 CkText *textPtr; /* Text widget to update. */
885 register DInfo *dInfoPtr = textPtr->dInfoPtr;
886 register DLine *dlPtr, *prevPtr;
888 CkTextLine *lastLinePtr;
889 int y, maxY, maxOffset;
891 if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
894 dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
897 * Delete any DLines that are now above the top of the window.
900 index = textPtr->topIndex;
901 dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
902 if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
903 FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
907 *--------------------------------------------------------------
908 * Scan through the contents of the window from top to bottom,
909 * recomputing information for lines that are missing.
910 *--------------------------------------------------------------
913 lastLinePtr = CkBTreeFindLine(textPtr->tree,
914 CkBTreeNumLines(textPtr->tree));
915 dlPtr = dInfoPtr->dLinePtr;
918 maxY = dInfoPtr->maxY;
920 register DLine *newPtr;
922 if (index.linePtr == lastLinePtr) {
927 * There are three possibilities right now:
928 * (a) the next DLine (dlPtr) corresponds exactly to the next
929 * information we want to display: just use it as-is.
930 * (b) the next DLine corresponds to a different line, or to
931 * a segment that will be coming later in the same line:
932 * leave this DLine alone in the hopes that we'll be able
933 * to use it later, then create a new DLine in front of
935 * (c) the next DLine corresponds to a segment in the line we
936 * want, but it's a segment that has already been processed
937 * or will never be processed. Delete the DLine and try
940 * One other twist on all this. It's possible for 3D borders
941 * to interact between lines (see DisplayLineBackground) so if
942 * a line is relayed out and has styles with 3D borders, its
943 * neighbors have to be redrawn if they have 3D borders too,
944 * since the interactions could have changed (the neighbors
945 * don't have to be relayed out, just redrawn).
948 if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
950 * Case (b) -- must make new DLine.
955 char string[TK_POS_CHARS];
958 * Debugging is enabled, so keep a log of all the lines
959 * that were re-layed out. The test suite uses this
963 CkTextPrintIndex(&index, string);
964 Tcl_SetVar2(textPtr->interp, "ck_textRelayout", (char *) NULL,
966 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
968 newPtr = LayoutDLine(textPtr, &index);
969 if (prevPtr == NULL) {
970 dInfoPtr->dLinePtr = newPtr;
972 prevPtr->nextPtr = newPtr;
974 newPtr->nextPtr = dlPtr;
978 * DlPtr refers to the line we want. Next check the
979 * index within the line.
982 if (index.charIndex == dlPtr->index.charIndex) {
984 * Case (a) -- can use existing display line as-is.
988 if (index.charIndex < dlPtr->index.charIndex) {
993 * Case (c) -- dlPtr is useless. Discard it and start
994 * again with the next display line.
997 newPtr = dlPtr->nextPtr;
998 FreeDLines(textPtr, dlPtr, newPtr, 0);
1004 * Advance to the start of the next line.
1011 CkTextIndexForwBytes(&index, dlPtr->count, &index);
1013 CkTextIndexForwChars(&index, dlPtr->count, &index);
1016 dlPtr = dlPtr->nextPtr;
1019 * If we switched text lines, delete any DLines left for the
1023 if (index.linePtr != prevPtr->index.linePtr) {
1024 register DLine *nextPtr;
1027 while ((nextPtr != NULL)
1028 && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
1029 nextPtr = nextPtr->nextPtr;
1031 if (nextPtr != dlPtr) {
1032 FreeDLines(textPtr, dlPtr, nextPtr, 0);
1033 prevPtr->nextPtr = nextPtr;
1039 * It's important to have the following check here rather than in
1040 * the while statement for the loop, so that there's always at least
1041 * one DLine generated, regardless of how small the window is. This
1042 * keeps a lot of other code from breaking.
1051 * Delete any DLine structures that don't fit on the screen.
1054 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
1057 *--------------------------------------------------------------
1058 * If there is extra space at the bottom of the window (because
1059 * we've hit the end of the text), then bring in more lines at
1060 * the top of the window, if there are any, to fill in the view.
1061 *--------------------------------------------------------------
1065 int lineNum, spaceLeft, charsToCount;
1069 * Layout an entire text line (potentially > 1 display line),
1070 * then link in as many display lines as fit without moving
1071 * the bottom line out of the window. Repeat this until
1072 * all the extra space has been used up or we've reached the
1073 * beginning of the text.
1076 spaceLeft = maxY - y;
1077 lineNum = CkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
1078 charsToCount = dInfoPtr->dLinePtr->index.charIndex;
1079 if (charsToCount == 0) {
1080 charsToCount = INT_MAX;
1083 for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
1084 index.linePtr = CkBTreeFindLine(textPtr->tree, lineNum);
1085 index.charIndex = 0;
1088 dlPtr = LayoutDLine(textPtr, &index);
1089 dlPtr->nextPtr = lowestPtr;
1092 if (dlPtr->length == 0 && dlPtr->height == 0) {
1096 CkTextIndexForwBytes(&index, dlPtr->count, &index);
1098 CkTextIndexForwChars(&index, dlPtr->count, &index);
1100 charsToCount -= dlPtr->count;
1101 } while ((charsToCount > 0)
1102 && (index.linePtr == lowestPtr->index.linePtr));
1105 * Scan through the display lines from the bottom one up to
1109 while (lowestPtr != NULL) {
1111 spaceLeft -= dlPtr->height;
1112 if (spaceLeft < 0) {
1115 lowestPtr = dlPtr->nextPtr;
1116 dlPtr->nextPtr = dInfoPtr->dLinePtr;
1117 dInfoPtr->dLinePtr = dlPtr;
1119 char string[TK_POS_CHARS];
1121 CkTextPrintIndex(&dlPtr->index, string);
1122 Tcl_SetVar2(textPtr->interp, "ck_textRelayout",
1123 (char *) NULL, string,
1124 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1127 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
1128 charsToCount = INT_MAX;
1132 * Now we're all done except that the y-coordinates in all the
1133 * DLines are wrong and the top index for the text is wrong.
1137 textPtr->topIndex = dInfoPtr->dLinePtr->index;
1139 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1140 dlPtr = dlPtr->nextPtr) {
1141 if (y > dInfoPtr->maxY) {
1142 panic("Added too many new lines in UpdateDisplayInfo");
1150 *--------------------------------------------------------------
1151 * If the old top or bottom line has scrolled elsewhere on the
1152 * screen, we may not be able to re-use its old contents by
1153 * copying bits (e.g., a beveled edge that was drawn when it was
1154 * at the top or bottom won't be drawn when the line is in the
1155 * middle and its neighbor has a matching background). Similarly,
1156 * if the new top or bottom line came from somewhere else on the
1157 * screen, we may not be able to copy the old bits.
1158 *--------------------------------------------------------------
1161 dlPtr = dInfoPtr->dLinePtr;
1163 if (dlPtr->nextPtr == NULL) {
1164 dlPtr->flags &= ~TOP_LINE;
1165 dlPtr->flags |= BOTTOM_LINE;
1168 dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
1169 dlPtr = dlPtr->nextPtr;
1171 dInfoPtr->dLinePtr->flags |= TOP_LINE;
1174 * Arrange for scrollbars to be updated.
1177 textPtr->flags |= UPDATE_SCROLLBARS;
1180 *--------------------------------------------------------------
1181 * Deal with horizontal scrolling:
1182 * 1. If there's empty space to the right of the longest line,
1183 * shift the screen to the right to fill in the empty space.
1184 * 2. If the desired horizontal scroll position has changed,
1185 * force a full redisplay of all the lines in the widget.
1186 * 3. If the wrap mode isn't "none" then re-scroll to the base
1188 *--------------------------------------------------------------
1191 dInfoPtr->maxLength = 0;
1192 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1193 dlPtr = dlPtr->nextPtr) {
1194 if (dlPtr->length > dInfoPtr->maxLength) {
1195 dInfoPtr->maxLength = dlPtr->length;
1199 maxOffset = dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x);
1200 if (dInfoPtr->newCharOffset >= maxOffset) {
1201 dInfoPtr->newCharOffset = maxOffset != 0 ? maxOffset + 1 : 0;
1203 if (dInfoPtr->newCharOffset < 0) {
1204 dInfoPtr->newCharOffset = 0;
1206 if (dInfoPtr->newCharOffset != dInfoPtr->curOffset) {
1207 dInfoPtr->curOffset = dInfoPtr->newCharOffset;
1208 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1209 dlPtr = dlPtr->nextPtr) {
1216 *----------------------------------------------------------------------
1220 * This procedure is called to free up all of the resources
1221 * associated with one or more DLine structures.
1227 * Memory gets freed and various other resources are released.
1229 *----------------------------------------------------------------------
1233 FreeDLines(textPtr, firstPtr, lastPtr, unlink)
1234 CkText *textPtr; /* Information about overall text
1236 register DLine *firstPtr; /* Pointer to first DLine to free up. */
1237 DLine *lastPtr; /* Pointer to DLine just after last
1238 * one to free (NULL means everything
1239 * starting with firstPtr). */
1240 int unlink; /* 1 means DLines are currently linked
1241 * into the list rooted at
1242 * textPtr->dInfoPtr->dLinePtr and
1243 * they have to be unlinked. 0 means
1244 * just free without unlinking. */
1246 register CkTextDispChunk *chunkPtr, *nextChunkPtr;
1247 register DLine *nextDLinePtr;
1250 if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
1251 textPtr->dInfoPtr->dLinePtr = lastPtr;
1253 register DLine *prevPtr;
1254 for (prevPtr = textPtr->dInfoPtr->dLinePtr;
1255 prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
1256 /* Empty loop body. */
1258 prevPtr->nextPtr = lastPtr;
1261 while (firstPtr != lastPtr) {
1262 nextDLinePtr = firstPtr->nextPtr;
1263 for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
1264 chunkPtr = nextChunkPtr) {
1265 if (chunkPtr->undisplayProc != NULL) {
1266 (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
1268 FreeStyle(textPtr, chunkPtr->stylePtr);
1269 nextChunkPtr = chunkPtr->nextPtr;
1270 ckfree((char *) chunkPtr);
1272 ckfree((char *) firstPtr);
1273 firstPtr = nextDLinePtr;
1275 textPtr->dInfoPtr->dLinesInvalidated = 1;
1279 *----------------------------------------------------------------------
1283 * This procedure is invoked to draw a single line on the
1290 * The line given by dlPtr is drawn at its correct position in
1291 * textPtr's window. Note that this is one *display* line, not
1294 *----------------------------------------------------------------------
1298 DisplayDLine(textPtr, dlPtr, prevPtr, window)
1299 CkText *textPtr; /* Text widget in which to draw line. */
1300 register DLine *dlPtr; /* Information about line to draw. */
1301 DLine *prevPtr; /* Line just before one to draw, or NULL
1302 * if dlPtr is the top line. */
1305 register CkTextDispChunk *chunkPtr;
1306 DInfo *dInfoPtr = textPtr->dInfoPtr;
1310 * First, clear the area of the line to the background color for the
1314 Ck_SetWindowAttr(textPtr->winPtr, textPtr->fg, textPtr->bg, textPtr->attr);
1315 Ck_ClearToEol(textPtr->winPtr, 0, dlPtr->y);
1318 * Make yet another pass through all of the chunks to redraw all of
1319 * foreground information. Note: we have to call the displayProc
1320 * even for chunks that are off-screen. This is needed, for
1321 * example, so that embedded windows can be unmapped in this case.
1324 for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
1325 chunkPtr = chunkPtr->nextPtr) {
1326 x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curOffset;
1327 if (chunkPtr->displayProc == CkTextInsertDisplayProc) {
1328 (*chunkPtr->displayProc)(chunkPtr, x, 0, dlPtr->height,
1329 0, window, dlPtr->y);
1332 if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
1334 * Note: we have to call the displayProc even for chunks
1335 * that are off-screen. This is needed, for example, so
1336 * that embedded windows can be unmapped in this case.
1337 * Display the chunk at a coordinate that can be clearly
1338 * identified by the displayProc as being off-screen to
1339 * the left (the displayProc may not be able to tell if
1340 * something is off to the right).
1343 (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
1344 0, dlPtr->height, 0, window, dlPtr->y);
1346 (*chunkPtr->displayProc)(chunkPtr, x, 0,
1347 dlPtr->height, 0, window, dlPtr->y);
1349 if (dInfoPtr->dLinesInvalidated) {
1357 *----------------------------------------------------------------------
1361 * This procedure is invoked as a when-idle handler to update the
1362 * display. It only redisplays the parts of the text widget that
1369 * Information is redrawn on the screen.
1371 *----------------------------------------------------------------------
1375 DisplayText(clientData)
1376 ClientData clientData; /* Information about widget. */
1378 register CkText *textPtr = (CkText *) clientData;
1379 DInfo *dInfoPtr = textPtr->dInfoPtr;
1381 register DLine *dlPtr;
1384 int bottomY = 0; /* Initialization needed only to stop
1385 * compiler warnings. */
1387 if (textPtr->winPtr == NULL) {
1389 * The widget has been deleted. Don't do anything.
1396 Tcl_SetVar2(textPtr->interp, "ck_textRelayout", (char *) NULL,
1397 "", TCL_GLOBAL_ONLY);
1400 if (!(textPtr->winPtr->flags & CK_MAPPED) ||
1401 (dInfoPtr->maxX <= dInfoPtr->x) || (dInfoPtr->maxY <= dInfoPtr->y)) {
1402 UpdateDisplayInfo(textPtr);
1403 dInfoPtr->flags &= ~REDRAW_PENDING;
1408 Tcl_SetVar2(textPtr->interp, "ck_textRedraw", (char *) NULL,
1409 "", TCL_GLOBAL_ONLY);
1413 * Choose a new current item if that is needed (this could cause
1414 * event handlers to be invoked, hence the preserve/release calls
1415 * and the loop, since the handlers could conceivably necessitate
1416 * yet another current item calculation). The ckwin check is because
1417 * the whole window could go away in the Ck_Release call.
1420 while (dInfoPtr->flags & REPICK_NEEDED) {
1421 Ck_Preserve((ClientData) textPtr);
1422 dInfoPtr->flags &= ~REPICK_NEEDED;
1423 CkTextPickCurrent(textPtr, &textPtr->pickEvent);
1424 winPtr = textPtr->winPtr;
1425 Ck_Release((ClientData) textPtr);
1426 if (winPtr == NULL) {
1432 * First recompute what's supposed to be displayed.
1435 UpdateDisplayInfo(textPtr);
1436 dInfoPtr->dLinesInvalidated = 0;
1437 dInfoPtr->flags &= ~REDRAW_PENDING;
1440 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
1441 if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
1442 maxHeight = dlPtr->height;
1444 bottomY = dlPtr->y + dlPtr->height;
1446 if (maxHeight > dInfoPtr->maxY) {
1447 maxHeight = dInfoPtr->maxY;
1452 * Now we have to redraw the lines that couldn't be updated by
1453 * scrolling. First, compute the height of the largest line and
1454 * allocate an off-screen pixmap to use for double-buffered
1458 if (maxHeight > 0) {
1459 for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
1460 (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
1461 prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
1462 if (dlPtr->oldY != dlPtr->y) {
1464 char string[TK_POS_CHARS];
1465 CkTextPrintIndex(&dlPtr->index, string);
1466 Tcl_SetVar2(textPtr->interp, "ck_textRedraw",
1467 (char *) NULL, string,
1468 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1470 DisplayDLine(textPtr, dlPtr, prevPtr, textPtr->winPtr->window);
1471 if (dInfoPtr->dLinesInvalidated) {
1474 dlPtr->oldY = dlPtr->y;
1475 dlPtr->flags &= ~NEW_LAYOUT;
1481 * See if we need to refresh the part of the window below the
1482 * last line of text (if there is any such area).
1485 if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
1486 dInfoPtr->topOfEof = dInfoPtr->maxY;
1488 if (bottomY < dInfoPtr->topOfEof) {
1490 Tcl_SetVar2(textPtr->interp, "ck_textRedraw",
1491 (char *) NULL, "eof",
1492 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1494 Ck_SetWindowAttr(textPtr->winPtr, textPtr->fg, textPtr->bg,
1496 Ck_ClearToBot(textPtr->winPtr, 0, bottomY);
1498 dInfoPtr->topOfEof = bottomY;
1503 * Update the vertical scrollbar, if there is one. Note: it's
1504 * important to clear REDRAW_PENDING here, just in case the
1505 * scroll procedure does something that requires redisplay.
1508 if (textPtr->flags & UPDATE_SCROLLBARS) {
1509 textPtr->flags &= ~UPDATE_SCROLLBARS;
1510 if (textPtr->yScrollCmd != NULL) {
1511 GetYView(textPtr->interp, textPtr, 1);
1515 * Update the horizontal scrollbar, if any.
1518 if (textPtr->xScrollCmd != NULL) {
1519 GetXView(textPtr->interp, textPtr, 1);
1523 if (textPtr->insertX >= 0 &&
1524 textPtr->insertX < textPtr->winPtr->width &&
1525 textPtr->insertY >= 0 &&
1526 textPtr->insertY < textPtr->winPtr->height &&
1527 textPtr->winPtr->window != NULL) {
1528 wmove(textPtr->winPtr->window, textPtr->insertY, textPtr->insertX);
1529 Ck_SetHWCursor(textPtr->winPtr, 1);
1531 Ck_SetHWCursor(textPtr->winPtr, 0);
1532 Ck_EventuallyRefresh(textPtr->winPtr);
1536 *----------------------------------------------------------------------
1538 * CkTextEventuallyRepick --
1540 * This procedure is invoked whenever something happens that
1541 * could change the current character or the tags associated
1548 * A repick is scheduled as an idle handler.
1550 *----------------------------------------------------------------------
1555 CkTextEventuallyRepick(textPtr)
1556 CkText *textPtr; /* Widget record for text widget. */
1558 DInfo *dInfoPtr = textPtr->dInfoPtr;
1560 dInfoPtr->flags |= REPICK_NEEDED;
1561 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
1562 dInfoPtr->flags |= REDRAW_PENDING;
1563 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1568 *----------------------------------------------------------------------
1570 * CkTextRedrawRegion --
1572 * This procedure is invoked to schedule a redisplay for a given
1573 * region of a text widget. The redisplay itself may not occur
1574 * immediately: it's scheduled as a when-idle handler.
1580 * Information will eventually be redrawn on the screen.
1582 *----------------------------------------------------------------------
1587 CkTextRedrawRegion(textPtr, x, y, width, height)
1588 CkText *textPtr; /* Widget record for text widget. */
1589 int x, y; /* Coordinates of upper-left corner of area
1590 * to be redrawn, in pixels relative to
1591 * textPtr's window. */
1592 int width, height; /* Width and height of area to be redrawn. */
1594 register DLine *dlPtr;
1595 DInfo *dInfoPtr = textPtr->dInfoPtr;
1599 * Find all lines that overlap the given region and mark them for
1604 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1605 dlPtr = dlPtr->nextPtr) {
1606 if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
1610 if (dInfoPtr->topOfEof < maxY) {
1611 dInfoPtr->topOfEof = maxY;
1615 * Schedule the redisplay operation if there isn't one already
1619 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
1620 dInfoPtr->flags |= REDRAW_PENDING;
1621 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1626 *----------------------------------------------------------------------
1630 * This procedure is invoked when info in a text widget is about
1631 * to be modified in a way that changes how it is displayed (e.g.
1632 * characters were inserted or deleted, or tag information was
1633 * changed). This procedure must be called *before* a change is
1634 * made, so that indexes in the display information are still
1641 * The range of character between index1Ptr (inclusive) and
1642 * index2Ptr (exclusive) will be redisplayed at some point in the
1643 * future (the actual redisplay is scheduled as a when-idle handler).
1645 *----------------------------------------------------------------------
1649 CkTextChanged(textPtr, index1Ptr, index2Ptr)
1650 CkText *textPtr; /* Widget record for text widget. */
1651 CkTextIndex *index1Ptr; /* Index of first character to redisplay. */
1652 CkTextIndex *index2Ptr; /* Index of character just after last one
1655 DInfo *dInfoPtr = textPtr->dInfoPtr;
1656 DLine *firstPtr, *lastPtr;
1657 CkTextIndex rounded;
1660 * Schedule both a redisplay and a recomputation of display information.
1661 * It's done here rather than the end of the procedure for two reasons:
1663 * 1. If there are no display lines to update we'll want to return
1664 * immediately, well before the end of the procedure.
1665 * 2. It's important to arrange for the redisplay BEFORE calling
1666 * FreeDLines. The reason for this is subtle and has to do with
1667 * embedded windows. The chunk delete procedure for an embedded
1668 * window will schedule an idle handler to unmap the window.
1669 * However, we want the idle handler for redisplay to be called
1670 * first, so that it can put the embedded window back on the screen
1671 * again (if appropriate). This will prevent the window from ever
1672 * being unmapped, and thereby avoid flashing.
1675 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
1676 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1678 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
1681 * Find the DLines corresponding to index1Ptr and index2Ptr. There
1682 * is one tricky thing here, which is that we have to relayout in
1683 * units of whole text lines: round index1Ptr back to the beginning
1684 * of its text line, and include all the display lines after index2,
1685 * up to the end of its text line. This is necessary because the
1686 * indices stored in the display lines will no longer be valid. It's
1687 * also needed because any edit could change the way lines wrap.
1690 rounded = *index1Ptr;
1691 rounded.charIndex = 0;
1692 firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
1693 if (firstPtr == NULL) {
1696 lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
1697 while ((lastPtr != NULL)
1698 && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
1699 lastPtr = lastPtr->nextPtr;
1703 * Delete all the DLines from firstPtr up to but not including lastPtr.
1706 FreeDLines(textPtr, firstPtr, lastPtr, 1);
1710 *----------------------------------------------------------------------
1712 * CkTextRedrawTag --
1714 * This procedure is invoked to request a redraw of all characters
1715 * in a given range that have a particular tag on or off. It's
1716 * called, for example, when tag options change.
1722 * Information on the screen may be redrawn, and the layout of
1723 * the screen may change.
1725 *----------------------------------------------------------------------
1729 CkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
1730 CkText *textPtr; /* Widget record for text widget. */
1731 CkTextIndex *index1Ptr; /* First character in range to consider
1732 * for redisplay. NULL means start at
1733 * beginning of text. */
1734 CkTextIndex *index2Ptr; /* Character just after last one to consider
1735 * for redisplay. NULL means process all
1736 * the characters in the text. */
1737 CkTextTag *tagPtr; /* Information about tag. */
1738 int withTag; /* 1 means redraw characters that have the
1739 * tag, 0 means redraw those without. */
1741 register DLine *dlPtr;
1744 CkTextSearch search;
1745 DInfo *dInfoPtr = textPtr->dInfoPtr;
1746 CkTextIndex endOfText, *endIndexPtr;
1749 * Round up the starting position if it's before the first line
1750 * visible on the screen (we only care about what's on the screen).
1753 dlPtr = dInfoPtr->dLinePtr;
1754 if (dlPtr == NULL) {
1757 if ((index1Ptr == NULL) || (CkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
1758 index1Ptr = &dlPtr->index;
1762 * Set the stopping position if it wasn't specified.
1765 if (index2Ptr == NULL) {
1767 index2Ptr = CkTextMakeByteIndex(textPtr->tree,
1768 CkBTreeNumLines(textPtr->tree), 0, &endOfText);
1770 index2Ptr = CkTextMakeIndex(textPtr->tree,
1771 CkBTreeNumLines(textPtr->tree), 0, &endOfText);
1776 * Initialize a search through all transitions on the tag, starting
1777 * with the first transition where the tag's current state is different
1778 * from what it will eventually be.
1781 CkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
1782 tagOn = CkBTreeCharTagged(index1Ptr, tagPtr);
1783 if (tagOn != withTag) {
1784 if (!CkBTreeNextTag(&search)) {
1790 * Schedule a redisplay and layout recalculation if they aren't
1791 * already pending. This has to be done before calling FreeDLines,
1792 * for the reason given in CkTextChanged.
1795 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
1796 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1798 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
1801 * Each loop through the loop below is for one range of characters
1802 * where the tag's current state is different than its eventual
1803 * state. At the top of the loop, search contains information about
1804 * the first character in the range.
1809 * Find the first DLine structure in the range. Note: if the
1810 * desired character isn't the first in its text line, then look
1811 * for the character just before it instead. This is needed to
1812 * handle the case where the first character of a wrapped
1813 * display line just got smaller, so that it now fits on the
1814 * line before: need to relayout the line containing the
1815 * previous character.
1818 if (search.curIndex.charIndex == 0) {
1819 dlPtr = FindDLine(dlPtr, &search.curIndex);
1823 tmp = search.curIndex;
1825 dlPtr = FindDLine(dlPtr, &tmp);
1827 if (dlPtr == NULL) {
1832 * Find the first DLine structure that's past the end of the range.
1835 if (!CkBTreeNextTag(&search)) {
1836 endIndexPtr = index2Ptr;
1838 endIndexPtr = &search.curIndex;
1840 endPtr = FindDLine(dlPtr, endIndexPtr);
1841 if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
1842 && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
1843 endPtr = endPtr->nextPtr;
1847 * Delete all of the display lines in the range, so that they'll
1848 * be re-layed out and redrawn.
1851 FreeDLines(textPtr, dlPtr, endPtr, 1);
1855 * Find the first text line in the next range.
1858 if (!CkBTreeNextTag(&search)) {
1865 *----------------------------------------------------------------------
1867 * CkTextRelayoutWindow --
1869 * This procedure is called when something has happened that
1870 * invalidates the whole layout of characters on the screen, such
1871 * as a change in a configuration option for the overall text
1872 * widget or a change in the window size. It causes all display
1873 * information to be recomputed and the window to be redrawn.
1879 * All the display information will be recomputed for the window
1880 * and the window will be redrawn.
1882 *----------------------------------------------------------------------
1886 CkTextRelayoutWindow(textPtr)
1887 CkText *textPtr; /* Widget record for text widget. */
1889 DInfo *dInfoPtr = textPtr->dInfoPtr;
1892 * Schedule the window redisplay. See CkTextChanged for the
1893 * reason why this has to be done before any calls to FreeDLines.
1896 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
1897 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1899 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
1902 * Throw away all the current layout information.
1905 FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
1906 dInfoPtr->dLinePtr = NULL;
1909 * Recompute some overall things for the layout. Even if the
1910 * window gets very small, pretend that there's at least one
1911 * pixel of drawing space in it.
1916 dInfoPtr->maxX = textPtr->winPtr->width;
1917 dInfoPtr->maxY = textPtr->winPtr->height;
1918 dInfoPtr->topOfEof = dInfoPtr->maxY;
1921 * If the upper-left character isn't the first in a line, recompute
1922 * it. This is necessary because a change in the window's size
1923 * or options could change the way lines wrap.
1926 if (textPtr->topIndex.charIndex != 0) {
1927 MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
1932 *----------------------------------------------------------------------
1936 * This procedure is called to specify what lines are to be
1937 * displayed in a text widget.
1943 * The display will (eventually) be updated so that the position
1944 * given by "indexPtr" is visible on the screen at the position
1945 * determined by "pickPlace".
1947 *----------------------------------------------------------------------
1951 CkTextSetYView(textPtr, indexPtr, pickPlace)
1952 CkText *textPtr; /* Widget record for text widget. */
1953 CkTextIndex *indexPtr; /* Position that is to appear somewhere
1955 int pickPlace; /* 0 means topLine must appear at top of
1956 * screen. 1 means we get to pick where it
1957 * appears: minimize screen motion or else
1958 * display line at center of screen. */
1960 DInfo *dInfoPtr = textPtr->dInfoPtr;
1961 register DLine *dlPtr;
1962 int bottomY, close, lineIndex;
1963 CkTextIndex tmpIndex, rounded;
1966 * If the specified position is the extra line at the end of the
1967 * text, round it back to the last real line.
1970 lineIndex = CkBTreeLineIndex(indexPtr->linePtr);
1971 if (lineIndex == CkBTreeNumLines(indexPtr->tree)) {
1972 CkTextIndexBackChars(indexPtr, 1, &rounded);
1973 indexPtr = &rounded;
1978 * The specified position must go at the top of the screen.
1979 * Just leave all the DLine's alone: we may be able to reuse
1980 * some of the information that's currently on the screen
1981 * without redisplaying it all.
1984 if (indexPtr->charIndex == 0) {
1985 textPtr->topIndex = *indexPtr;
1987 MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
1989 goto scheduleUpdate;
1993 * We have to pick where to display the index. First, bring
1994 * the display information up to date and see if the index will be
1995 * completely visible in the current screen configuration. If so
1996 * then there's nothing to do.
1999 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2000 UpdateDisplayInfo(textPtr);
2002 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
2003 if (dlPtr != NULL) {
2004 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
2006 * Part of the line hangs off the bottom of the screen;
2007 * pretend the whole line is off-screen.
2011 } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
2012 && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
2018 * The desired line isn't already on-screen.
2021 bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
2022 close = (dInfoPtr->maxY - dInfoPtr->y)/3;
2023 if (dlPtr != NULL) {
2025 * The desired line is above the top of screen. If it is
2026 * "close" to the top of the window then make it the top
2027 * line on the screen.
2030 MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
2031 if (CkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
2032 MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
2033 goto scheduleUpdate;
2037 * The desired line is below the bottom of the screen. If it is
2038 * "close" to the bottom of the screen then position it at the
2039 * bottom of the screen.
2042 MeasureUp(textPtr, indexPtr, close, &tmpIndex);
2043 if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
2044 bottomY = dInfoPtr->maxY - dInfoPtr->y;
2049 * Our job now is to arrange the display so that indexPtr appears
2050 * as low on the screen as possible but with its bottom no lower
2051 * than bottomY. BottomY is the bottom of the window if the
2052 * desired line is just below the current screen, otherwise it
2053 * is the center of the window.
2056 MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
2059 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2060 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
2062 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2066 *--------------------------------------------------------------
2070 * Given one index, find the index of the first character
2071 * on the highest display line that would be displayed no more
2072 * than "distance" pixels above the given index.
2075 * *dstPtr is filled in with the index of the first character
2076 * on a display line. The display line is found by measuring
2077 * up "distance" pixels above the pixel just below an imaginary
2078 * display line that contains srcPtr. If the display line
2079 * that covers this coordinate actually extends above the
2080 * coordinate, then return the index of the next lower line
2081 * instead (i.e. the returned index will be completely visible
2082 * at or below the given y-coordinate).
2087 *--------------------------------------------------------------
2091 MeasureUp(textPtr, srcPtr, distance, dstPtr)
2092 CkText *textPtr; /* Text widget in which to measure. */
2093 CkTextIndex *srcPtr; /* Index of character from which to start
2095 int distance; /* Vertical distance in pixels measured
2096 * from the pixel just below the lowest
2097 * one in srcPtr's line. */
2098 CkTextIndex *dstPtr; /* Index to fill in with result. */
2100 int lineNum; /* Number of current line. */
2101 int charsToCount; /* Maximum number of characters to measure
2102 * in current line. */
2103 CkTextIndex bestIndex; /* Best candidate seen so far for result. */
2105 DLine *dlPtr, *lowestPtr;
2106 int noBestYet; /* 1 means bestIndex hasn't been set. */
2109 charsToCount = srcPtr->charIndex + 1;
2110 index.tree = srcPtr->tree;
2111 for (lineNum = CkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
2114 * Layout an entire text line (potentially > 1 display line).
2115 * For the first line, which contains srcPtr, only layout the
2116 * part up through srcPtr (charsToCount is non-infinite to
2117 * accomplish this). Make a list of all the display lines
2118 * in backwards order (the lowest DLine on the screen is first
2122 index.linePtr = CkBTreeFindLine(srcPtr->tree, lineNum);
2123 index.charIndex = 0;
2126 dlPtr = LayoutDLine(textPtr, &index);
2127 dlPtr->nextPtr = lowestPtr;
2130 CkTextIndexForwBytes(&index, dlPtr->count, &index);
2132 CkTextIndexForwChars(&index, dlPtr->count, &index);
2134 charsToCount -= dlPtr->count;
2135 } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
2138 * Scan through the display lines to see if we've covered enough
2139 * vertical distance. If so, save the starting index for the
2140 * line at the desired location.
2143 for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
2144 distance -= dlPtr->height;
2146 *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
2149 bestIndex = dlPtr->index;
2154 * Discard the display lines, then either return or prepare
2155 * for the next display line to lay out.
2158 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
2162 charsToCount = INT_MAX; /* Consider all chars. in next line. */
2166 * Ran off the beginning of the text. Return the first character
2170 CkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
2172 CkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
2177 *--------------------------------------------------------------
2181 * This procedure is invoked to process the "see" option for
2182 * the widget command for text widgets. See the user documentation
2183 * for details on what it does.
2186 * A standard Tcl result.
2189 * See the user documentation.
2191 *--------------------------------------------------------------
2195 CkTextSeeCmd(textPtr, interp, argc, argv)
2196 CkText *textPtr; /* Information about text widget. */
2197 Tcl_Interp *interp; /* Current interpreter. */
2198 int argc; /* Number of arguments. */
2199 char **argv; /* Argument strings. Someone else has already
2200 * parsed this command enough to know that
2201 * argv[1] is "see". */
2203 DInfo *dInfoPtr = textPtr->dInfoPtr;
2205 int x, y, width, height, lineWidth, charCount, oneThird, delta;
2207 CkTextDispChunk *chunkPtr;
2210 Tcl_AppendResult(interp, "wrong # args: should be \"",
2211 argv[0], " see index\"", (char *) NULL);
2214 if (CkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
2219 * If the specified position is the extra line at the end of the
2220 * text, round it back to the last real line.
2223 if (CkBTreeLineIndex(index.linePtr) == CkBTreeNumLines(index.tree)) {
2224 CkTextIndexBackChars(&index, 1, &index);
2228 * First get the desired position into the vertical range of the window.
2231 CkTextSetYView(textPtr, &index, 1);
2234 * Now make sure that the character is in view horizontally.
2237 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2238 UpdateDisplayInfo(textPtr);
2240 lineWidth = dInfoPtr->maxX - dInfoPtr->x;
2241 if (dInfoPtr->maxLength < lineWidth) {
2246 * Find the chunk that contains the desired index.
2249 dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
2250 charCount = index.charIndex - dlPtr->index.charIndex;
2251 for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
2252 if (charCount < chunkPtr->numChars) {
2255 charCount -= chunkPtr->numChars;
2259 * Call a chunk-specific procedure to find the horizontal range of
2260 * the character within the chunk.
2263 (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y,
2264 dlPtr->height, 0, &x, &y, &width, &height);
2265 delta = x - dInfoPtr->curOffset;
2266 oneThird = lineWidth/3;
2268 if (delta < -oneThird) {
2269 dInfoPtr->newCharOffset = (x - lineWidth/2);
2271 dInfoPtr->newCharOffset -= -delta;
2274 delta -= (lineWidth - width);
2276 if (delta > oneThird) {
2277 dInfoPtr->newCharOffset = (x - lineWidth/2);
2279 dInfoPtr->newCharOffset += delta + 1;
2285 dInfoPtr->flags |= DINFO_OUT_OF_DATE;
2286 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2287 dInfoPtr->flags |= REDRAW_PENDING;
2288 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
2294 *--------------------------------------------------------------
2298 * This procedure is invoked to process the "xview" option for
2299 * the widget command for text widgets. See the user documentation
2300 * for details on what it does.
2303 * A standard Tcl result.
2306 * See the user documentation.
2308 *--------------------------------------------------------------
2312 CkTextXviewCmd(textPtr, interp, argc, argv)
2313 CkText *textPtr; /* Information about text widget. */
2314 Tcl_Interp *interp; /* Current interpreter. */
2315 int argc; /* Number of arguments. */
2316 char **argv; /* Argument strings. Someone else has already
2317 * parsed this command enough to know that
2318 * argv[1] is "xview". */
2320 DInfo *dInfoPtr = textPtr->dInfoPtr;
2321 int type, charsPerPage, count, newOffset;
2324 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2325 UpdateDisplayInfo(textPtr);
2329 GetXView(interp, textPtr, 0);
2333 newOffset = dInfoPtr->newCharOffset;
2334 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
2336 case CK_SCROLL_ERROR:
2338 case CK_SCROLL_MOVETO:
2339 newOffset = (int) (fraction * dInfoPtr->maxLength);
2341 case CK_SCROLL_PAGES:
2342 charsPerPage = dInfoPtr->maxX - dInfoPtr->x - 2;
2343 if (charsPerPage < 1) {
2346 newOffset += charsPerPage*count;
2348 case CK_SCROLL_UNITS:
2353 dInfoPtr->newCharOffset = newOffset;
2354 dInfoPtr->flags |= DINFO_OUT_OF_DATE;
2355 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2356 dInfoPtr->flags |= REDRAW_PENDING;
2357 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
2363 *----------------------------------------------------------------------
2367 * This procedure is called to scroll a text widget up or down
2368 * by a given number of lines.
2374 * The view in textPtr's window changes to reflect the value
2377 *----------------------------------------------------------------------
2381 ScrollByLines(textPtr, offset)
2382 CkText *textPtr; /* Widget to scroll. */
2383 int offset; /* Amount by which to scroll, in *screen*
2384 * lines. Positive means that information
2385 * later in text becomes visible, negative
2386 * means that information earlier in the
2387 * text becomes visible. */
2389 int i, charsToCount, lineNum;
2390 CkTextIndex new, index;
2391 CkTextLine *lastLinePtr;
2392 DInfo *dInfoPtr = textPtr->dInfoPtr;
2393 DLine *dlPtr, *lowestPtr;
2397 * Must scroll up (to show earlier information in the text).
2398 * The code below is similar to that in MeasureUp, except that
2399 * it counts lines instead of pixels.
2402 charsToCount = textPtr->topIndex.charIndex + 1;
2403 index.tree = textPtr->tree;
2404 offset--; /* Skip line containing topIndex. */
2405 for (lineNum = CkBTreeLineIndex(textPtr->topIndex.linePtr);
2406 lineNum >= 0; lineNum--) {
2407 index.linePtr = CkBTreeFindLine(textPtr->tree, lineNum);
2408 index.charIndex = 0;
2411 dlPtr = LayoutDLine(textPtr, &index);
2412 dlPtr->nextPtr = lowestPtr;
2415 CkTextIndexForwBytes(&index, dlPtr->count, &index);
2417 CkTextIndexForwChars(&index, dlPtr->count, &index);
2419 charsToCount -= dlPtr->count;
2420 } while ((charsToCount > 0)
2421 && (index.linePtr == dlPtr->index.linePtr));
2423 for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
2426 textPtr->topIndex = dlPtr->index;
2432 * Discard the display lines, then either return or prepare
2433 * for the next display line to lay out.
2436 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
2438 goto scheduleUpdate;
2440 charsToCount = INT_MAX;
2444 * Ran off the beginning of the text. Return the first character
2448 CkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
2450 CkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
2454 * Scrolling down, to show later information in the text.
2455 * Just count lines from the current top of the window.
2458 lastLinePtr = CkBTreeFindLine(textPtr->tree,
2459 CkBTreeNumLines(textPtr->tree));
2460 for (i = 0; i < offset; i++) {
2461 dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
2462 dlPtr->nextPtr = NULL;
2464 if (dlPtr->length == 0 && dlPtr->height == 0) {
2467 CkTextIndexForwBytes(&textPtr->topIndex, dlPtr->count, &new);
2469 CkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
2471 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
2472 if (new.linePtr == lastLinePtr) {
2475 textPtr->topIndex = new;
2480 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2481 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
2483 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2487 *--------------------------------------------------------------
2491 * This procedure is invoked to process the "yview" option for
2492 * the widget command for text widgets. See the user documentation
2493 * for details on what it does.
2496 * A standard Tcl result.
2499 * See the user documentation.
2501 *--------------------------------------------------------------
2505 CkTextYviewCmd(textPtr, interp, argc, argv)
2506 CkText *textPtr; /* Information about text widget. */
2507 Tcl_Interp *interp; /* Current interpreter. */
2508 int argc; /* Number of arguments. */
2509 char **argv; /* Argument strings. Someone else has already
2510 * parsed this command enough to know that
2511 * argv[1] is "yview". */
2513 DInfo *dInfoPtr = textPtr->dInfoPtr;
2514 int pickPlace, lineNum, type, lineHeight;
2516 size_t switchLength;
2518 CkTextIndex index, new;
2519 CkTextLine *lastLinePtr;
2525 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2526 UpdateDisplayInfo(textPtr);
2530 GetYView(interp, textPtr, 0);
2535 * Next, handle the old syntax: "pathName yview ?-pickplace? where"
2539 if (argv[2][0] == '-') {
2540 switchLength = strlen(argv[2]);
2541 if ((switchLength >= 2)
2542 && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
2545 Tcl_AppendResult(interp, "wrong # args: should be \"",
2546 argv[0], " yview -pickplace lineNum|index\"",
2552 if ((argc == 3) || pickPlace) {
2553 if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
2555 CkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
2557 CkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
2559 CkTextSetYView(textPtr, &index, 0);
2564 * The argument must be a regular text index.
2567 Tcl_ResetResult(interp);
2568 if (CkTextGetIndex(interp, textPtr, argv[2+pickPlace],
2569 &index) != TCL_OK) {
2572 CkTextSetYView(textPtr, &index, pickPlace);
2577 * New syntax: dispatch based on argv[2].
2580 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
2582 case CK_SCROLL_ERROR:
2584 case CK_SCROLL_MOVETO:
2586 if (fraction > 1.0) {
2592 fraction *= CkBTreeNumLines(textPtr->tree);
2593 lineNum = (int) fraction;
2594 CkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
2595 bytesInLine = CkBTreeCharsInLine(index.linePtr);
2596 index.charIndex = (int)((bytesInLine * (fraction-lineNum)) + 0.5);
2597 if (index.charIndex >= bytesInLine) {
2598 CkTextMakeByteIndex(textPtr->tree, lineNum + 1, 0, &index);
2601 fraction *= CkBTreeNumLines(textPtr->tree);
2602 lineNum = (int) fraction;
2603 CkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
2604 CkTextIndexBackChars(&index, 1, &index);
2605 index.charIndex = (int) ((index.charIndex+1)*(fraction-lineNum));
2607 CkTextSetYView(textPtr, &index, 0);
2609 case CK_SCROLL_PAGES:
2611 * Scroll up or down by screenfulls. Actually, use the
2612 * window height minus two lines, so that there's some
2613 * overlap between adjacent pages.
2618 pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*(-count)
2620 MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
2621 if (CkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
2623 * A page of scrolling ended up being less than one line.
2624 * Scroll one line anyway.
2630 textPtr->topIndex = new;
2633 * Scrolling down by pages. Layout lines starting at the
2634 * top index and count through the desired vertical distance.
2637 pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*count;
2638 lastLinePtr = CkBTreeFindLine(textPtr->tree,
2639 CkBTreeNumLines(textPtr->tree));
2641 dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
2642 dlPtr->nextPtr = NULL;
2644 CkTextIndexForwBytes(&textPtr->topIndex, dlPtr->count,
2647 CkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
2650 pixels -= dlPtr->height;
2651 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
2652 if (new.linePtr == lastLinePtr) {
2655 textPtr->topIndex = new;
2656 } while (pixels > 0);
2658 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2659 Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
2661 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2663 case CK_SCROLL_UNITS:
2665 ScrollByLines(textPtr, count);
2672 *----------------------------------------------------------------------
2676 * This procedure computes the fractions that indicate what's
2677 * visible in a text window and, optionally, evaluates a
2678 * Tcl script to report them to the text's associated scrollbar.
2681 * If report is zero, then interp->result is filled in with
2682 * two real numbers separated by a space, giving the position of
2683 * the left and right edges of the window as fractions from 0 to
2684 * 1, where 0 means the left edge of the text and 1 means the right
2685 * edge. If report is non-zero, then interp->result isn't modified
2686 * directly, but instead a script is evaluated in interp to report
2687 * the new horizontal scroll position to the scrollbar (if the scroll
2688 * position hasn't changed then no script is invoked).
2693 *----------------------------------------------------------------------
2697 GetXView(interp, textPtr, report)
2698 Tcl_Interp *interp; /* If "report" is FALSE, string
2699 * describing visible range gets
2700 * stored in interp->result. */
2701 CkText *textPtr; /* Information about text widget. */
2702 int report; /* Non-zero means report info to
2703 * scrollbar if it has changed. */
2705 DInfo *dInfoPtr = textPtr->dInfoPtr;
2710 if (dInfoPtr->maxLength > 0) {
2711 first = ((double) dInfoPtr->curOffset)
2712 / dInfoPtr->maxLength;
2713 last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
2714 / dInfoPtr->maxLength;
2723 sprintf(interp->result, "%g %g", first, last);
2726 if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
2729 dInfoPtr->xScrollFirst = first;
2730 dInfoPtr->xScrollLast = last;
2731 sprintf(buffer, " %g %g", first, last);
2732 code = Tcl_VarEval(interp, textPtr->xScrollCmd,
2733 buffer, (char *) NULL);
2734 if (code != TCL_OK) {
2735 Tcl_AddErrorInfo(interp,
2736 "\n (horizontal scrolling command executed by text)");
2737 Tk_BackgroundError(interp);
2742 *----------------------------------------------------------------------
2746 * This procedure computes the fractions that indicate what's
2747 * visible in a text window and, optionally, evaluates a
2748 * Tcl script to report them to the text's associated scrollbar.
2751 * If report is zero, then interp->result is filled in with
2752 * two real numbers separated by a space, giving the position of
2753 * the top and bottom of the window as fractions from 0 to 1, where
2754 * 0 means the beginning of the text and 1 means the end. If
2755 * report is non-zero, then interp->result isn't modified directly,
2756 * but a script is evaluated in interp to report the new scroll
2757 * position to the scrollbar (if the scroll position hasn't changed
2758 * then no script is invoked).
2763 *----------------------------------------------------------------------
2767 GetYView(interp, textPtr, report)
2768 Tcl_Interp *interp; /* If "report" is FALSE, string
2769 * describing visible range gets
2770 * stored in interp->result. */
2771 CkText *textPtr; /* Information about text widget. */
2772 int report; /* Non-zero means report info to
2773 * scrollbar if it has changed. */
2775 DInfo *dInfoPtr = textPtr->dInfoPtr;
2779 int totalLines, code, count;
2781 dlPtr = dInfoPtr->dLinePtr;
2782 totalLines = CkBTreeNumLines(textPtr->tree);
2783 first = ((double) CkBTreeLineIndex(dlPtr->index.linePtr))
2784 + ((double) dlPtr->index.charIndex)
2785 / (CkBTreeCharsInLine(dlPtr->index.linePtr));
2786 first /= totalLines;
2788 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
2790 * The last line is only partially visible, so don't
2791 * count its characters in what's visible.
2796 if (dlPtr->nextPtr == NULL) {
2797 count = dlPtr->count;
2800 dlPtr = dlPtr->nextPtr;
2802 last = ((double) CkBTreeLineIndex(dlPtr->index.linePtr))
2803 + ((double) (dlPtr->index.charIndex + count))
2804 / (CkBTreeCharsInLine(dlPtr->index.linePtr));
2807 sprintf(interp->result, "%g %g", first, last);
2810 if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
2813 dInfoPtr->yScrollFirst = first;
2814 dInfoPtr->yScrollLast = last;
2815 sprintf(buffer, " %g %g", first, last);
2816 code = Tcl_VarEval(interp, textPtr->yScrollCmd,
2817 buffer, (char *) NULL);
2818 if (code != TCL_OK) {
2819 Tcl_AddErrorInfo(interp,
2820 "\n (vertical scrolling command executed by text)");
2821 Tk_BackgroundError(interp);
2826 *----------------------------------------------------------------------
2830 * This procedure is called to find the DLine corresponding to a
2834 * The return value is a pointer to the first DLine found in the
2835 * list headed by dlPtr that displays information at or after the
2836 * specified position. If there is no such line in the list then
2842 *----------------------------------------------------------------------
2846 FindDLine(dlPtr, indexPtr)
2847 register DLine *dlPtr; /* Pointer to first in list of DLines
2849 CkTextIndex *indexPtr; /* Index of desired character. */
2851 CkTextLine *linePtr;
2853 if (dlPtr == NULL) {
2856 if (CkBTreeLineIndex(indexPtr->linePtr)
2857 < CkBTreeLineIndex(dlPtr->index.linePtr)) {
2859 * The first display line is already past the desired line.
2865 * Find the first display line that covers the desired text line.
2868 linePtr = dlPtr->index.linePtr;
2869 while (linePtr != indexPtr->linePtr) {
2870 while (dlPtr->index.linePtr == linePtr) {
2871 dlPtr = dlPtr->nextPtr;
2872 if (dlPtr == NULL) {
2876 linePtr = CkBTreeNextLine(linePtr);
2877 if (linePtr == NULL) {
2878 panic("FindDLine reached end of text");
2881 if (indexPtr->linePtr != dlPtr->index.linePtr) {
2886 * Now get to the right position within the text line.
2889 while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
2890 dlPtr = dlPtr->nextPtr;
2891 if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
2899 *----------------------------------------------------------------------
2901 * CkTextPixelIndex --
2903 * Given an (x,y) coordinate on the screen, find the location of
2904 * the character closest to that location.
2907 * The index at *indexPtr is modified to refer to the character
2908 * on the display that is closest to (x,y).
2913 *----------------------------------------------------------------------
2917 CkTextPixelIndex(textPtr, x, y, indexPtr)
2918 CkText *textPtr; /* Widget record for text widget. */
2919 int x, y; /* Pixel coordinates of point in widget's
2921 CkTextIndex *indexPtr; /* This index gets filled in with the
2922 * index of the character nearest to (x,y). */
2924 DInfo *dInfoPtr = textPtr->dInfoPtr;
2925 register DLine *dlPtr;
2926 register CkTextDispChunk *chunkPtr;
2929 * Make sure that all of the layout information about what's
2930 * displayed where on the screen is up-to-date.
2933 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2934 UpdateDisplayInfo(textPtr);
2938 * If the coordinates are above the top of the window, then adjust
2939 * them to refer to the upper-right corner of the window. If they're
2940 * off to one side or the other, then adjust to the closest side.
2943 if (y < dInfoPtr->y) {
2947 if (x >= dInfoPtr->maxX) {
2948 x = dInfoPtr->maxX - 1;
2950 if (x < dInfoPtr->x) {
2955 * Find the display line containing the desired y-coordinate.
2958 for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
2959 dlPtr = dlPtr->nextPtr) {
2960 if (dlPtr->nextPtr == NULL) {
2962 * Y-coordinate is off the bottom of the displayed text.
2963 * Use the last character on the last line.
2966 x = dInfoPtr->maxX - 1;
2972 * Scan through the line's chunks to find the one that contains
2973 * the desired x-coordinate. Before doing this, translate the
2974 * x-coordinate from the coordinate system of the window to the
2975 * coordinate system of the line (to take account of x-scrolling).
2978 *indexPtr = dlPtr->index;
2979 x = x - dInfoPtr->x + dInfoPtr->curOffset;
2980 for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
2981 indexPtr->charIndex += chunkPtr->numChars,
2982 chunkPtr = chunkPtr->nextPtr) {
2983 if (chunkPtr->nextPtr == NULL) {
2985 indexPtr->charIndex += chunkPtr->numChars;
2986 CkTextIndexBackChars(indexPtr, 1, indexPtr);
2988 indexPtr->charIndex += chunkPtr->numChars - 1;
2995 * If the chunk has more than one character in it, ask it which
2996 * character is at the desired location.
2999 if (chunkPtr->numChars > 1) {
3000 indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
3005 *----------------------------------------------------------------------
3009 * Given an index, find the bounding box of the screen area
3010 * occupied by that character.
3013 * Zero is returned if the character is on the screen. -1
3014 * means the character isn't on the screen. If the return value
3015 * is 0, then the bounding box of the part of the character that's
3016 * visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
3022 *----------------------------------------------------------------------
3026 CkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
3027 CkText *textPtr; /* Widget record for text widget. */
3028 CkTextIndex *indexPtr; /* Index of character whose bounding
3029 * box is desired. */
3030 int *xPtr, *yPtr; /* Filled with character's upper-left
3032 int *widthPtr, *heightPtr; /* Filled in with character's dimensions. */
3034 DInfo *dInfoPtr = textPtr->dInfoPtr;
3036 register CkTextDispChunk *chunkPtr;
3040 * Make sure that all of the screen layout information is up to date.
3043 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3044 UpdateDisplayInfo(textPtr);
3048 * Find the display line containing the desired index.
3051 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
3052 if ((dlPtr == NULL) || (CkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
3057 * Find the chunk within the line that contains the desired
3061 index = indexPtr->charIndex - dlPtr->index.charIndex;
3062 for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
3063 if (chunkPtr == NULL) {
3066 if (index < chunkPtr->numChars) {
3069 index -= chunkPtr->numChars;
3073 * Call a chunk-specific procedure to find the horizontal range of
3074 * the character within the chunk, then fill in the vertical range.
3075 * The x-coordinate returned by bboxProc is a coordinate within a
3076 * line, not a coordinate on the screen. Translate it to reflect
3077 * horizontal scrolling.
3080 (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y,
3081 dlPtr->height, 0, xPtr, yPtr, widthPtr,
3083 *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curOffset;
3084 if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
3086 * Last character in display line. Give it all the space up to
3090 if (*xPtr > dInfoPtr->maxX) {
3091 *xPtr = dInfoPtr->maxX;
3093 *widthPtr = dInfoPtr->maxX - *xPtr;
3095 if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
3098 if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
3099 *widthPtr = dInfoPtr->maxX - *xPtr;
3100 if (*widthPtr <= 0) {
3104 if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
3105 *heightPtr = dInfoPtr->maxY - *yPtr;
3106 if (*heightPtr <= 0) {
3114 *----------------------------------------------------------------------
3116 * CkTextDLineInfo --
3118 * Given an index, return information about the display line
3119 * containing that character.
3122 * Zero is returned if the character is on the screen. -1
3123 * means the character isn't on the screen. If the return value
3124 * is 0, then information is returned in the variables pointed
3125 * to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
3130 *----------------------------------------------------------------------
3134 CkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
3135 CkText *textPtr; /* Widget record for text widget. */
3136 CkTextIndex *indexPtr; /* Index of character whose bounding
3137 * box is desired. */
3138 int *xPtr, *yPtr; /* Filled with line's upper-left
3140 int *widthPtr, *heightPtr; /* Filled in with line's dimensions. */
3141 int *basePtr; /* Filled in with the baseline position,
3142 * measured as an offset down from *yPtr. */
3144 DInfo *dInfoPtr = textPtr->dInfoPtr;
3148 * Make sure that all of the screen layout information is up to date.
3151 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3152 UpdateDisplayInfo(textPtr);
3156 * Find the display line containing the desired index.
3159 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
3160 if ((dlPtr == NULL) || (CkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
3164 *xPtr = dInfoPtr->x - dInfoPtr->curOffset + dlPtr->chunkPtr->x;
3165 *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
3167 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
3168 *heightPtr = dInfoPtr->maxY - dlPtr->y;
3170 *heightPtr = dlPtr->height;
3177 *--------------------------------------------------------------
3179 * CkTextCharLayoutProc --
3181 * This procedure is the "layoutProc" for character segments.
3184 * If there is something to display for the chunk then a
3185 * non-zero value is returned and the fields of chunkPtr
3186 * will be filled in (see the declaration of CkTextDispChunk
3187 * in ckText.h for details). If zero is returned it means
3188 * that no characters from this chunk fit in the window.
3189 * If -1 is returned it means that this segment just doesn't
3190 * need to be displayed (never happens for text).
3193 * Memory is allocated to hold additional information about
3196 *--------------------------------------------------------------
3200 CkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
3201 noCharsYet, wrapMode, chunkPtr)
3202 CkText *textPtr; /* Text widget being layed out. */
3203 CkTextIndex *indexPtr; /* Index of first character to lay out
3204 * (corresponds to segPtr and offset). */
3205 CkTextSegment *segPtr; /* Segment being layed out. */
3206 int offset; /* Offset within segment of first character
3208 int maxX; /* Chunk must not occupy pixels at this
3209 * position or higher. */
3210 int maxChars; /* Chunk must not include more than this
3211 * many characters. */
3212 int noCharsYet; /* Non-zero means no characters have been
3213 * assigned to this display line yet. */
3214 Ck_Uid wrapMode; /* How to handle line wrapping: ckTextCharUid,
3215 * ckTextNoneUid, or ckTextWordUid. */
3216 register CkTextDispChunk *chunkPtr;
3217 /* Structure to fill in with information
3218 * about this chunk. The x field has already
3219 * been set by the caller. */
3221 int nextX, charsThatFit, count, dummy;
3224 CkTextSegment *nextPtr;
3225 CkWindow *winPtr = textPtr->winPtr;
3228 * Figure out how many characters will fit in the space we've got.
3229 * Include the next character, even though it won't fit completely,
3230 * if any of the following is true:
3231 * (a) the chunk contains no characters and the display line contains
3232 * no characters yet (i.e. the line isn't wide enough to hold
3233 * even a single character).
3234 * (b) at least one pixel of the character is visible, we haven't
3235 * already exceeded the character limit, and the next character
3236 * is a white space character.
3239 p = segPtr->body.chars + offset;
3240 CkMeasureChars(winPtr->mainPtr, p, maxChars, chunkPtr->x,
3241 maxX, 0, CK_IGNORE_TABS, &nextX, &charsThatFit);
3242 if (charsThatFit < maxChars) {
3243 if ((charsThatFit == 0) && noCharsYet) {
3245 CkMeasureChars(winPtr->mainPtr, p, 1, chunkPtr->x, INT_MAX, 0,
3246 CK_IGNORE_TABS, &nextX, &dummy);
3248 if (p[charsThatFit] == '\n') {
3250 * A newline character takes up no space, so if the previous
3251 * character fits then so does the newline.
3256 if (charsThatFit == 0) {
3262 * Fill in the chunk structure and allocate and initialize a
3263 * CharInfo structure. If the last character is a newline
3264 * then don't bother to display it.
3267 chunkPtr->displayProc = CharDisplayProc;
3268 chunkPtr->undisplayProc = CharUndisplayProc;
3269 chunkPtr->measureProc = CharMeasureProc;
3270 chunkPtr->bboxProc = CharBboxProc;
3271 chunkPtr->numChars = charsThatFit;
3272 chunkPtr->minHeight = 1;
3273 chunkPtr->width = nextX - chunkPtr->x;
3274 chunkPtr->breakIndex = -1;
3275 ciPtr = (CharInfo *) ckalloc((unsigned)
3276 (sizeof(CharInfo) - 3 + charsThatFit));
3277 chunkPtr->clientData = (ClientData) ciPtr;
3278 ciPtr->numChars = charsThatFit;
3279 ciPtr->winPtr = textPtr->winPtr;
3280 strncpy(ciPtr->chars, p, (size_t) charsThatFit);
3281 if (p[charsThatFit-1] == '\n') {
3286 * Compute a break location. If we're in word wrap mode, a
3287 * break can occur after any space character, or at the end of
3288 * the chunk if the next segment (ignoring those with zero size)
3289 * is not a character segment.
3292 if (wrapMode != ckTextWordUid) {
3293 chunkPtr->breakIndex = chunkPtr->numChars;
3295 for (count = charsThatFit, p += charsThatFit-1; count > 0;
3297 if (isspace((unsigned char) *p)) {
3298 chunkPtr->breakIndex = count;
3302 if ((charsThatFit+offset) == segPtr->size) {
3303 for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
3304 nextPtr = nextPtr->nextPtr) {
3305 if (nextPtr->size != 0) {
3306 if (nextPtr->typePtr != &ckTextCharType) {
3307 chunkPtr->breakIndex = chunkPtr->numChars;
3318 *--------------------------------------------------------------
3320 * CharDisplayProc --
3322 * This procedure is called to display a character chunk on
3323 * the screen or in an off-screen pixmap.
3329 * Graphics are drawn.
3331 *--------------------------------------------------------------
3335 CharDisplayProc(chunkPtr, x, y, height, baseline, window, screenY)
3336 CkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
3337 int x; /* X-position in dst at which to
3338 * draw this chunk (may differ from
3339 * the x-position in the chunk because
3341 int y; /* Y-position at which to draw this
3343 int height; /* Total height of line. */
3344 int baseline; /* Offset of baseline from y. */
3346 int screenY; /* Y-coordinate in text window that
3347 * corresponds to y. */
3349 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
3351 StyleValues *sValuePtr;
3353 if ((x + chunkPtr->width) <= 0) {
3355 * The chunk is off-screen.
3361 stylePtr = chunkPtr->stylePtr;
3362 sValuePtr = stylePtr->sValuePtr;
3365 * Draw the text for this chunk.
3368 if (ciPtr->numChars > 0) {
3369 Ck_SetWindowAttr(ciPtr->winPtr, sValuePtr->fg, sValuePtr->bg,
3371 CkDisplayChars(ciPtr->winPtr->mainPtr, window, ciPtr->chars,
3373 screenY + baseline, x - chunkPtr->x, CK_IGNORE_TABS);
3378 *--------------------------------------------------------------
3380 * CharUndisplayProc --
3382 * This procedure is called when a character chunk is no
3383 * longer going to be displayed. It frees up resources
3384 * that were allocated to display the chunk.
3390 * Memory and other resources get freed.
3392 *--------------------------------------------------------------
3396 CharUndisplayProc(textPtr, chunkPtr)
3397 CkText *textPtr; /* Overall information about text
3399 CkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
3401 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
3403 ckfree((char *) ciPtr);
3407 *--------------------------------------------------------------
3409 * CharMeasureProc --
3411 * This procedure is called to determine which character in
3412 * a character chunk lies over a given x-coordinate.
3415 * The return value is the index *within the chunk* of the
3416 * character that covers the position given by "x".
3421 *--------------------------------------------------------------
3425 CharMeasureProc(chunkPtr, x)
3426 CkTextDispChunk *chunkPtr; /* Chunk containing desired coord. */
3427 int x; /* X-coordinate, in same coordinate
3428 * system as chunkPtr->x. */
3430 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
3433 CkMeasureChars(ciPtr->winPtr->mainPtr,
3434 ciPtr->chars, chunkPtr->numChars-1, chunkPtr->x,
3435 x, 0, CK_IGNORE_TABS, &endX, &charX);
3440 *--------------------------------------------------------------
3444 * This procedure is called to compute the bounding box of
3445 * the area occupied by a single character.
3448 * There is no return value. *xPtr and *yPtr are filled in
3449 * with the coordinates of the upper left corner of the
3450 * character, and *widthPtr and *heightPtr are filled in with
3451 * the dimensions of the character in pixels. Note: not all
3452 * of the returned bbox is necessarily visible on the screen
3453 * (the rightmost part might be off-screen to the right,
3454 * and the bottommost part might be off-screen to the bottom).
3459 *--------------------------------------------------------------
3463 CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
3464 widthPtr, heightPtr)
3465 CkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
3466 int index; /* Index of desired character within
3468 int y; /* Topmost pixel in area allocated
3470 int lineHeight; /* Height of line, in pixels. */
3471 int baseline; /* Location of line's baseline, in
3472 * pixels measured down from y. */
3473 int *xPtr, *yPtr; /* Gets filled in with coords of
3474 * character's upper-left pixel.
3475 * X-coord is in same coordinate
3476 * system as chunkPtr->x. */
3477 int *widthPtr; /* Gets filled in with width of
3478 * character, in pixels. */
3479 int *heightPtr; /* Gets filled in with height of
3480 * character, in pixels. */
3482 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
3483 CkWindow *winPtr = ciPtr->winPtr;
3486 maxX = chunkPtr->width + chunkPtr->x;
3487 CkMeasureChars(winPtr->mainPtr,
3488 ciPtr->chars, index, chunkPtr->x, 1000000, 0,
3489 CK_IGNORE_TABS, xPtr, &dummy);
3490 if (index == ciPtr->numChars) {
3492 * This situation only happens if the last character in a line
3493 * is a space character, in which case it absorbs all of the
3494 * extra space in the line (see CkTextCharLayoutProc).
3497 *widthPtr = maxX - *xPtr;
3498 } else if ((ciPtr->chars[index] == '\t')
3499 && (index == (ciPtr->numChars-1))) {
3501 * The desired character is a tab character that terminates a
3502 * chunk; give it all the space left in the chunk.
3505 *widthPtr = maxX - *xPtr;
3507 CkMeasureChars(winPtr->mainPtr,
3508 ciPtr->chars + index, 1, *xPtr, 1000000, 0,
3509 CK_IGNORE_TABS, widthPtr, &dummy);
3510 if (*widthPtr > maxX) {
3511 *widthPtr = maxX - *xPtr;
3516 *yPtr = y + baseline;
3521 *----------------------------------------------------------------------
3525 * This procedure is called to move a series of chunks right
3526 * in order to align them with a tab stop.
3532 * The width of chunkPtr gets adjusted so that it absorbs the
3533 * extra space due to the tab. The x locations in all the chunks
3534 * after chunkPtr are adjusted rightward to align with the tab
3535 * stop given by tabArrayPtr and index.
3537 *----------------------------------------------------------------------
3541 AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
3542 CkText *textPtr; /* Information about the text widget as
3544 CkTextTabArray *tabArrayPtr; /* Information about the tab stops
3545 * that apply to this line. May be
3546 * NULL to indicate default tabbing
3547 * (every 8 chars). */
3548 int index; /* Index of current tab stop. */
3549 CkTextDispChunk *chunkPtr; /* Chunk whose last character is
3550 * the tab; the following chunks
3551 * contain information to be shifted
3555 int x, desired, delta, width, decimal, i, gotDigit;
3556 CkTextDispChunk *chunkPtr2, *decimalChunkPtr;
3558 CharInfo *ciPtr = NULL; /* Initialization needed only to
3559 * prevent compiler warnings. */
3560 int tabX, prev, spaceWidth, dummy;
3562 CkTextTabAlign alignment;
3564 if (chunkPtr->nextPtr == NULL) {
3566 * Nothing after the actual tab; just return.
3573 * If no tab information has been given, do the usual thing:
3574 * round up to the next boundary of 8 average-sized characters.
3577 x = chunkPtr->nextPtr->x;
3578 if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
3580 * No tab information has been given, so use the default
3581 * interpretation of tabs.
3584 CkMeasureChars(textPtr->winPtr->mainPtr,
3585 "\t", 1, x, INT_MAX, 0, 0, &desired, &dummy);
3589 if (index < tabArrayPtr->numTabs) {
3590 alignment = tabArrayPtr->tabs[index].alignment;
3591 tabX = tabArrayPtr->tabs[index].location;
3594 * Ran out of tab stops; compute a tab position by extrapolating
3595 * from the last two tab positions.
3598 if (tabArrayPtr->numTabs > 1) {
3599 prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
3603 alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
3604 tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
3605 + (index + 1 - tabArrayPtr->numTabs)
3606 * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
3609 tabPtr = &tabArrayPtr->tabs[index];
3610 if (alignment == LEFT) {
3615 if ((alignment == CENTER) || (alignment == RIGHT)) {
3617 * Compute the width of all the information in the tab group,
3618 * then use it to pick a desired location.
3622 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
3623 chunkPtr2 = chunkPtr2->nextPtr) {
3624 width += chunkPtr2->width;
3626 if (alignment == CENTER) {
3627 desired = tabX - width/2;
3629 desired = tabX - width;
3635 * Must be numeric alignment. Search through the text to be
3636 * tabbed, looking for the last , or . before the first character
3637 * that isn't a number, comma, period, or sign.
3640 decimalChunkPtr = NULL;
3641 decimal = gotDigit = 0;
3642 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
3643 chunkPtr2 = chunkPtr2->nextPtr) {
3644 if (chunkPtr2->displayProc != CharDisplayProc) {
3647 ciPtr = (CharInfo *) chunkPtr2->clientData;
3648 for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
3649 if (isdigit((unsigned char) *p)) {
3651 } else if ((*p == '.') || (*p == ',')) {
3652 decimal = p-ciPtr->chars;
3653 decimalChunkPtr = chunkPtr2;
3654 } else if (gotDigit) {
3655 if (decimalChunkPtr == NULL) {
3656 decimal = p-ciPtr->chars;
3657 decimalChunkPtr = chunkPtr2;
3664 if (decimalChunkPtr != NULL) {
3667 ciPtr = (CharInfo *) decimalChunkPtr->clientData;
3668 CkMeasureChars(ciPtr->winPtr->mainPtr,
3669 ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0,
3670 CK_IGNORE_TABS, &curX, &dummy);
3671 desired = tabX - (curX - x);
3675 * There wasn't a decimal point. Right justify the text.
3679 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
3680 chunkPtr2 = chunkPtr2->nextPtr) {
3681 width += chunkPtr2->width;
3683 desired = tabX - width;
3687 * Shift all of the chunks to the right so that the left edge is
3688 * at the desired location, then expand the chunk containing the
3689 * tab. Be sure that the tab occupies at least the width of a
3694 delta = desired - x;
3695 CkMeasureChars(textPtr->winPtr->mainPtr, " ", 1, 0, INT_MAX,
3696 0, 0, &spaceWidth, &dummy);
3697 if (delta < spaceWidth) {
3700 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
3701 chunkPtr2 = chunkPtr2->nextPtr) {
3702 chunkPtr2->x += delta;
3704 chunkPtr->width += delta;
3708 *----------------------------------------------------------------------
3712 * This returns an estimate of the amount of white space that will
3713 * be consumed by a tab.
3716 * The return value is the minimum number of pixels that will
3717 * be occupied by the index'th tab of tabArrayPtr, assuming that
3718 * the current position on the line is x and the end of the
3719 * line is maxX. For numeric tabs, this is a conservative
3720 * estimate. The return value is always >= 0.
3725 *----------------------------------------------------------------------
3729 SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
3730 CkText *textPtr; /* Information about the text widget as
3732 CkTextTabArray *tabArrayPtr; /* Information about the tab stops
3733 * that apply to this line. NULL
3734 * means use default tabbing (every
3736 int index; /* Index of current tab stop. */
3737 int x; /* Current x-location in line. Only
3738 * used if tabArrayPtr == NULL. */
3739 int maxX; /* X-location of pixel just past the
3740 * right edge of the line. */
3742 int tabX, prev, result, spaceWidth, dummy;
3743 CkTextTabAlign alignment;
3744 CkWindow *winPtr = textPtr->winPtr;
3746 if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
3747 CkMeasureChars(winPtr->mainPtr, "\t", 1, x, INT_MAX,
3748 0, 0, &tabX, &dummy);
3751 if (index < tabArrayPtr->numTabs) {
3752 tabX = tabArrayPtr->tabs[index].location;
3753 alignment = tabArrayPtr->tabs[index].alignment;
3756 * Ran out of tab stops; compute a tab position by extrapolating
3757 * from the last two tab positions.
3760 if (tabArrayPtr->numTabs > 1) {
3761 prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
3765 tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
3766 + (index + 1 - tabArrayPtr->numTabs)
3767 * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
3768 alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
3770 if (alignment == CENTER) {
3772 * Be very careful in the arithmetic below, because maxX may
3773 * be the largest positive number: watch out for integer
3777 if ((maxX-tabX) < (tabX - x)) {
3778 result = (maxX - x) - 2*(maxX - tabX);
3784 if (alignment == RIGHT) {
3790 * Note: this treats NUMERIC alignment the same as LEFT
3791 * alignment, which is somewhat conservative. However, it's
3792 * pretty tricky at this point to figure out exactly where
3793 * the damn decimal point will be.
3803 CkMeasureChars(winPtr->mainPtr, " ", 1, 0, INT_MAX,
3804 0, 0, &spaceWidth, &dummy);
3805 if (result < spaceWidth) {
3806 result = spaceWidth;