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-1997 Sun Microsystems, Inc.
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
26 * The following structure describes how to display a range of characters.
27 * The information is generated by scanning all of the tags associated
28 * with the characters and combining that with default information for
29 * the overall widget. These structures form the hash keys for
30 * dInfoPtr->styleTable.
33 typedef struct StyleValues {
34 Tk_3DBorder border; /* Used for drawing background under text.
35 * NULL means use widget background. */
36 int borderWidth; /* Width of 3-D border for background. */
37 int relief; /* 3-D relief for background. */
38 Pixmap bgStipple; /* Stipple bitmap for background. None
39 * means draw solid. */
40 XColor *fgColor; /* Foreground color for text. */
41 Tk_Font tkfont; /* Font for displaying text. */
42 Pixmap fgStipple; /* Stipple bitmap for text and other
43 * foreground stuff. None means draw
45 int justify; /* Justification style for text. */
46 int lMargin1; /* Left margin, in pixels, for first display
47 * line of each text line. */
48 int lMargin2; /* Left margin, in pixels, for second and
49 * later display lines of each text line. */
50 int offset; /* Offset in pixels of baseline, relative to
51 * baseline of line. */
52 int overstrike; /* Non-zero means draw overstrike through
54 int rMargin; /* Right margin, in pixels. */
55 int spacing1; /* Spacing above first dline in text line. */
56 int spacing2; /* Spacing between lines of dline. */
57 int spacing3; /* Spacing below last dline in text line. */
58 TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
60 int underline; /* Non-zero means draw underline underneath
62 int elide; /* Non-zero means draw text */
63 TkWrapMode wrapMode; /* How to handle wrap-around for this tag.
64 * One of TEXT_WRAPMODE_CHAR,
65 * TEXT_WRAPMODE_NONE or TEXT_WRAPMODE_WORD.*/
69 * The following structure extends the StyleValues structure above with
70 * graphics contexts used to actually draw the characters. The entries
71 * in dInfoPtr->styleTable point to structures of this type.
74 typedef struct TextStyle {
75 int refCount; /* Number of times this structure is
76 * referenced in Chunks. */
77 GC bgGC; /* Graphics context for background. None
78 * means use widget background. */
79 GC fgGC; /* Graphics context for foreground. */
80 StyleValues *sValuePtr; /* Raw information from which GCs were
82 Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable. Used
87 * The following macro determines whether two styles have the same
88 * background so that, for example, no beveled border should be drawn
92 #define SAME_BACKGROUND(s1, s2) \
93 (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
94 && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
95 && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
96 && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
99 * The following macro is used to compare two floating-point numbers
100 * to within a certain degree of scale. Direct comparison fails on
101 * processors where the processor and memory representations of FP
102 * numbers of a particular precision is different (e.g. Intel)
105 #define FP_EQUAL_SCALE(double1, double2, scaleFactor) \
106 (fabs((double1)-(double2))*((scaleFactor)+1.0) < 0.3)
109 * The following structure describes one line of the display, which may
110 * be either part or all of one line of the text.
113 typedef struct DLine {
114 TkTextIndex index; /* Identifies first character in text
115 * that is displayed on this line. */
116 int byteCount; /* Number of bytes accounted for by this
117 * display line, including a trailing space
118 * or newline that isn't actually displayed. */
119 int y; /* Y-position at which line is supposed to
120 * be drawn (topmost pixel of rectangular
121 * area occupied by line). */
122 int oldY; /* Y-position at which line currently
123 * appears on display. -1 means line isn't
124 * currently visible on display and must be
125 * redrawn. This is used to move lines by
126 * scrolling rather than re-drawing. */
127 int height; /* Height of line, in pixels. */
128 int baseline; /* Offset of text baseline from y, in
130 int spaceAbove; /* How much extra space was added to the
131 * top of the line because of spacing
132 * options. This is included in height
134 int spaceBelow; /* How much extra space was added to the
135 * bottom of the line because of spacing
136 * options. This is included in height. */
137 int length; /* Total length of line, in pixels. */
138 TkTextDispChunk *chunkPtr; /* Pointer to first chunk in list of all
139 * of those that are displayed on this
140 * line of the screen. */
141 struct DLine *nextPtr; /* Next in list of all display lines for
142 * this window. The list is sorted in
143 * order from top to bottom. Note: the
144 * next DLine doesn't always correspond
145 * to the next line of text: (a) can have
146 * multiple DLines for one text line, and
147 * (b) can have gaps where DLine's have been
148 * deleted because they're out of date. */
149 int flags; /* Various flag bits: see below for values. */
153 * Flag bits for DLine structures:
155 * HAS_3D_BORDER - Non-zero means that at least one of the
156 * chunks in this line has a 3D border, so
157 * it potentially interacts with 3D borders
158 * in neighboring lines (see
159 * DisplayLineBackground).
160 * NEW_LAYOUT - Non-zero means that the line has been
161 * re-layed out since the last time the
162 * display was updated.
163 * TOP_LINE - Non-zero means that this was the top line
164 * in the window the last time that the window
165 * was laid out. This is important because
166 * a line may be displayed differently if its
167 * at the top or bottom than if it's in the
168 * middle (e.g. beveled edges aren't displayed
169 * for middle lines if the adjacent line has
170 * a similar background).
171 * BOTTOM_LINE - Non-zero means that this was the bottom line
172 * in the window the last time that the window
174 * IS_DISABLED - This Dline cannot be edited.
177 #define HAS_3D_BORDER 1
180 #define BOTTOM_LINE 8
181 #define IS_DISABLED 16
184 * Overall display information for a text widget:
187 typedef struct TextDInfo {
188 Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
189 * to TextStyles for this widget. */
190 DLine *dLinePtr; /* First in list of all display lines for
191 * this widget, in order from top to bottom. */
192 GC copyGC; /* Graphics context for copying from off-
193 * screen pixmaps onto screen. */
194 GC scrollGC; /* Graphics context for copying from one place
195 * in the window to another (scrolling):
196 * differs from copyGC in that we need to get
197 * GraphicsExpose events. */
198 int x; /* First x-coordinate that may be used for
199 * actually displaying line information.
200 * Leaves space for border, etc. */
201 int y; /* First y-coordinate that may be used for
202 * actually displaying line information.
203 * Leaves space for border, etc. */
204 int maxX; /* First x-coordinate to right of available
205 * space for displaying lines. */
206 int maxY; /* First y-coordinate below available
207 * space for displaying lines. */
208 int topOfEof; /* Top-most pixel (lowest y-value) that has
209 * been drawn in the appropriate fashion for
210 * the portion of the window after the last
211 * line of the text. This field is used to
212 * figure out when to redraw part or all of
216 * Information used for scrolling:
219 int newByteOffset; /* Desired x scroll position, measured as the
220 * number of average-size characters off-screen
221 * to the left for a line with no left
223 int curPixelOffset; /* Actual x scroll position, measured as the
224 * number of pixels off-screen to the left. */
225 int maxLength; /* Length in pixels of longest line that's
226 * visible in window (length may exceed window
227 * size). If there's no wrapping, this will
229 double xScrollFirst, xScrollLast;
230 /* Most recent values reported to horizontal
231 * scrollbar; used to eliminate unnecessary
233 double yScrollFirst, yScrollLast;
234 /* Most recent values reported to vertical
235 * scrollbar; used to eliminate unnecessary
239 * The following information is used to implement scanning:
242 int scanMarkIndex; /* Byte index of character that was at the
243 * left edge of the window when the scan
245 int scanMarkX; /* X-position of mouse at time scan started. */
246 int scanTotalScroll; /* Total scrolling (in screen lines) that has
247 * occurred since scanMarkY was set. */
248 int scanMarkY; /* Y-position of mouse at time scan started. */
251 * Miscellaneous information:
254 int dLinesInvalidated; /* This value is set to 1 whenever something
255 * happens that invalidates information in
256 * DLine structures; if a redisplay
257 * is in progress, it will see this and
258 * abort the redisplay. This is needed
259 * because, for example, an embedded window
260 * could change its size when it is first
261 * displayed, invalidating the DLine that
262 * is currently being displayed. If redisplay
263 * continues, it will use freed memory and
264 * could dump core. */
265 int flags; /* Various flag values: see below for
270 * In TkTextDispChunk structures for character segments, the clientData
271 * field points to one of the following structures:
274 typedef struct CharInfo {
275 int numBytes; /* Number of bytes to display. */
276 char chars[4]; /* UTF characters to display. Actual size
277 * will be numBytes, not 4. THIS MUST BE
278 * THE LAST FIELD IN THE STRUCTURE. */
282 * Flag values for TextDInfo structures:
284 * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
285 * for this window are partially or completely
286 * out of date and need to be recomputed.
287 * REDRAW_PENDING: Means that a when-idle handler has been
288 * scheduled to update the display.
289 * REDRAW_BORDERS: Means window border or pad area has
290 * potentially been damaged and must be redrawn.
291 * REPICK_NEEDED: 1 means that the widget has been modified
292 * in a way that could change the current
293 * character (a different character might be
294 * under the mouse cursor now). Need to
295 * recompute the current character before
296 * the next redisplay.
299 #define DINFO_OUT_OF_DATE 1
300 #define REDRAW_PENDING 2
301 #define REDRAW_BORDERS 4
302 #define REPICK_NEEDED 8
305 * The following counters keep statistics about redisplay that can be
306 * checked to see how clever this code is at reducing redisplays.
309 static int numRedisplays; /* Number of calls to DisplayText. */
310 static int linesRedrawn; /* Number of calls to DisplayDLine. */
311 static int numCopies; /* Number of calls to XCopyArea to copy part
315 * Forward declarations for procedures defined later in this file:
318 static void AdjustForTab _ANSI_ARGS_((TkText *textPtr,
319 TkTextTabArray *tabArrayPtr, int index,
320 TkTextDispChunk *chunkPtr));
321 static void CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
322 int index, int y, int lineHeight, int baseline,
323 int *xPtr, int *yPtr, int *widthPtr,
325 static void CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
326 int x, int y, int height, int baseline,
327 Display *display, Drawable dst, int screenY));
328 static int CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
330 static void CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
331 TkTextDispChunk *chunkPtr));
334 Definitions of elided procs.
335 Compiler can't inline these since we use pointers to these functions.
336 ElideDisplayProc, ElideUndisplayProc special-cased for speed,
337 as potentially many elided DLine chunks if large, tag toggle-filled
340 static void ElideBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
341 int index, int y, int lineHeight, int baseline,
342 int *xPtr, int *yPtr, int *widthPtr,
344 static int ElideMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
347 static void DisplayDLine _ANSI_ARGS_((TkText *textPtr,
348 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
349 static void DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
350 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
351 static void DisplayText _ANSI_ARGS_((ClientData clientData));
352 static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr,
353 TkTextIndex *indexPtr));
354 static void FreeDLines _ANSI_ARGS_((TkText *textPtr,
355 DLine *firstPtr, DLine *lastPtr, int unlink));
356 static void FreeStyle _ANSI_ARGS_((TkText *textPtr,
357 TextStyle *stylePtr));
358 static TextStyle * GetStyle _ANSI_ARGS_((TkText *textPtr,
359 TkTextIndex *indexPtr));
360 static void GetXView _ANSI_ARGS_((Tcl_Interp *interp,
361 TkText *textPtr, int report));
362 static void GetYView _ANSI_ARGS_((Tcl_Interp *interp,
363 TkText *textPtr, int report));
364 static DLine * LayoutDLine _ANSI_ARGS_((TkText *textPtr,
365 TkTextIndex *indexPtr));
366 static int MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
367 CONST char *source, int maxBytes, int startX,
368 int maxX, int tabOrigin, int *nextXPtr));
369 static void MeasureUp _ANSI_ARGS_((TkText *textPtr,
370 TkTextIndex *srcPtr, int distance,
371 TkTextIndex *dstPtr));
372 static int NextTabStop _ANSI_ARGS_((Tk_Font tkfont, int x,
374 static void UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
375 static void ScrollByLines _ANSI_ARGS_((TkText *textPtr,
377 static int SizeOfTab _ANSI_ARGS_((TkText *textPtr,
378 TkTextTabArray *tabArrayPtr, int index, int x,
380 static void TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
385 *----------------------------------------------------------------------
387 * TkTextCreateDInfo --
389 * This procedure is called when a new text widget is created.
390 * Its job is to set up display-related information for the widget.
396 * A TextDInfo data structure is allocated and initialized and attached
399 *----------------------------------------------------------------------
403 TkTextCreateDInfo(textPtr)
404 TkText *textPtr; /* Overall information for text widget. */
406 register TextDInfo *dInfoPtr;
409 dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
410 Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
411 dInfoPtr->dLinePtr = NULL;
412 dInfoPtr->copyGC = None;
413 gcValues.graphics_exposures = True;
414 dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
416 dInfoPtr->topOfEof = 0;
417 dInfoPtr->newByteOffset = 0;
418 dInfoPtr->curPixelOffset = 0;
419 dInfoPtr->maxLength = 0;
420 dInfoPtr->xScrollFirst = -1;
421 dInfoPtr->xScrollLast = -1;
422 dInfoPtr->yScrollFirst = -1;
423 dInfoPtr->yScrollLast = -1;
424 dInfoPtr->scanMarkIndex = 0;
425 dInfoPtr->scanMarkX = 0;
426 dInfoPtr->scanTotalScroll = 0;
427 dInfoPtr->scanMarkY = 0;
428 dInfoPtr->dLinesInvalidated = 0;
429 dInfoPtr->flags = DINFO_OUT_OF_DATE;
430 textPtr->dInfoPtr = dInfoPtr;
434 *----------------------------------------------------------------------
438 * This procedure is called to free up all of the private display
439 * information kept by this file for a text widget.
445 * Lots of resources get freed.
447 *----------------------------------------------------------------------
451 TkTextFreeDInfo(textPtr)
452 TkText *textPtr; /* Overall information for text widget. */
454 register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
457 * Be careful to free up styleTable *after* freeing up all the
458 * DLines, so that the hash table is still intact to free up the
459 * style-related information from the lines. Once the lines are
460 * all free then styleTable will be empty.
463 FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
464 Tcl_DeleteHashTable(&dInfoPtr->styleTable);
465 if (dInfoPtr->copyGC != None) {
466 Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
468 Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
469 if (dInfoPtr->flags & REDRAW_PENDING) {
470 Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
472 ckfree((char *) dInfoPtr);
476 *----------------------------------------------------------------------
480 * This procedure creates all the information needed to display
481 * text at a particular location.
484 * The return value is a pointer to a TextStyle structure that
485 * corresponds to *sValuePtr.
488 * A new entry may be created in the style table for the widget.
490 *----------------------------------------------------------------------
494 GetStyle(textPtr, indexPtr)
495 TkText *textPtr; /* Overall information about text widget. */
496 TkTextIndex *indexPtr; /* The character in the text for which
497 * display information is wanted. */
500 register TkTextTag *tagPtr;
501 StyleValues styleValues;
509 * The variables below keep track of the highest-priority specification
510 * that has occurred for each of the various fields of the StyleValues.
513 int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
514 int fgPrio, fontPrio, fgStipplePrio;
515 int underlinePrio, elidePrio, justifyPrio, offsetPrio;
516 int lMargin1Prio, lMargin2Prio, rMarginPrio;
517 int spacing1Prio, spacing2Prio, spacing3Prio;
518 int overstrikePrio, tabPrio, wrapPrio;
521 * Find out what tags are present for the character, then compute
522 * a StyleValues structure corresponding to those tags (scan
523 * through all of the tags, saving information for the highest-
527 tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
528 borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
529 fgPrio = fontPrio = fgStipplePrio = -1;
530 underlinePrio = elidePrio = justifyPrio = offsetPrio = -1;
531 lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
532 spacing1Prio = spacing2Prio = spacing3Prio = -1;
533 overstrikePrio = tabPrio = wrapPrio = -1;
534 memset((VOID *) &styleValues, 0, sizeof(StyleValues));
535 styleValues.relief = TK_RELIEF_FLAT;
536 styleValues.fgColor = textPtr->fgColor;
537 styleValues.tkfont = textPtr->tkfont;
538 styleValues.justify = TK_JUSTIFY_LEFT;
539 styleValues.spacing1 = textPtr->spacing1;
540 styleValues.spacing2 = textPtr->spacing2;
541 styleValues.spacing3 = textPtr->spacing3;
542 styleValues.tabArrayPtr = textPtr->tabArrayPtr;
543 styleValues.wrapMode = textPtr->wrapMode;
544 styleValues.elide = 0;
545 for (i = 0 ; i < numTags; i++) {
549 * On Windows and Mac, we need to skip the selection tag if
550 * we don't have focus.
553 #ifndef ALWAYS_SHOW_SELECTION
554 if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
559 if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
560 styleValues.border = tagPtr->border;
561 borderPrio = tagPtr->priority;
563 if ((tagPtr->bdString != NULL)
564 && (tagPtr->priority > borderWidthPrio)) {
565 styleValues.borderWidth = tagPtr->borderWidth;
566 borderWidthPrio = tagPtr->priority;
568 if ((tagPtr->reliefString != NULL)
569 && (tagPtr->priority > reliefPrio)) {
570 if (styleValues.border == NULL) {
571 styleValues.border = textPtr->border;
573 styleValues.relief = tagPtr->relief;
574 reliefPrio = tagPtr->priority;
576 if ((tagPtr->bgStipple != None)
577 && (tagPtr->priority > bgStipplePrio)) {
578 styleValues.bgStipple = tagPtr->bgStipple;
579 bgStipplePrio = tagPtr->priority;
581 if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
582 styleValues.fgColor = tagPtr->fgColor;
583 fgPrio = tagPtr->priority;
585 if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
586 styleValues.tkfont = tagPtr->tkfont;
587 fontPrio = tagPtr->priority;
589 if ((tagPtr->fgStipple != None)
590 && (tagPtr->priority > fgStipplePrio)) {
591 styleValues.fgStipple = tagPtr->fgStipple;
592 fgStipplePrio = tagPtr->priority;
594 if ((tagPtr->justifyString != NULL)
595 && (tagPtr->priority > justifyPrio)) {
596 styleValues.justify = tagPtr->justify;
597 justifyPrio = tagPtr->priority;
599 if ((tagPtr->lMargin1String != NULL)
600 && (tagPtr->priority > lMargin1Prio)) {
601 styleValues.lMargin1 = tagPtr->lMargin1;
602 lMargin1Prio = tagPtr->priority;
604 if ((tagPtr->lMargin2String != NULL)
605 && (tagPtr->priority > lMargin2Prio)) {
606 styleValues.lMargin2 = tagPtr->lMargin2;
607 lMargin2Prio = tagPtr->priority;
609 if ((tagPtr->offsetString != NULL)
610 && (tagPtr->priority > offsetPrio)) {
611 styleValues.offset = tagPtr->offset;
612 offsetPrio = tagPtr->priority;
614 if ((tagPtr->overstrikeString != NULL)
615 && (tagPtr->priority > overstrikePrio)) {
616 styleValues.overstrike = tagPtr->overstrike;
617 overstrikePrio = tagPtr->priority;
619 if ((tagPtr->rMarginString != NULL)
620 && (tagPtr->priority > rMarginPrio)) {
621 styleValues.rMargin = tagPtr->rMargin;
622 rMarginPrio = tagPtr->priority;
624 if ((tagPtr->spacing1String != NULL)
625 && (tagPtr->priority > spacing1Prio)) {
626 styleValues.spacing1 = tagPtr->spacing1;
627 spacing1Prio = tagPtr->priority;
629 if ((tagPtr->spacing2String != NULL)
630 && (tagPtr->priority > spacing2Prio)) {
631 styleValues.spacing2 = tagPtr->spacing2;
632 spacing2Prio = tagPtr->priority;
634 if ((tagPtr->spacing3String != NULL)
635 && (tagPtr->priority > spacing3Prio)) {
636 styleValues.spacing3 = tagPtr->spacing3;
637 spacing3Prio = tagPtr->priority;
639 if ((tagPtr->tabString != NULL)
640 && (tagPtr->priority > tabPrio)) {
641 styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
642 tabPrio = tagPtr->priority;
644 if ((tagPtr->underlineString != NULL)
645 && (tagPtr->priority > underlinePrio)) {
646 styleValues.underline = tagPtr->underline;
647 underlinePrio = tagPtr->priority;
649 if ((tagPtr->elideString != NULL)
650 && (tagPtr->priority > elidePrio)) {
651 styleValues.elide = tagPtr->elide;
652 elidePrio = tagPtr->priority;
654 if ((tagPtr->wrapMode != TEXT_WRAPMODE_NULL)
655 && (tagPtr->priority > wrapPrio)) {
656 styleValues.wrapMode = tagPtr->wrapMode;
657 wrapPrio = tagPtr->priority;
660 if (tagPtrs != NULL) {
661 ckfree((char *) tagPtrs);
665 * Use an existing style if there's one around that matches.
668 hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
669 (char *) &styleValues, &new);
671 stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
672 stylePtr->refCount++;
677 * No existing style matched. Make a new one.
680 stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
681 stylePtr->refCount = 1;
682 if (styleValues.border != NULL) {
683 gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
685 if (styleValues.bgStipple != None) {
686 gcValues.stipple = styleValues.bgStipple;
687 gcValues.fill_style = FillStippled;
688 mask |= GCStipple|GCFillStyle;
690 stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
692 stylePtr->bgGC = None;
695 gcValues.font = Tk_FontId(styleValues.tkfont);
696 mask |= GCForeground;
697 gcValues.foreground = styleValues.fgColor->pixel;
698 if (styleValues.fgStipple != None) {
699 gcValues.stipple = styleValues.fgStipple;
700 gcValues.fill_style = FillStippled;
701 mask |= GCStipple|GCFillStyle;
703 stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
704 stylePtr->sValuePtr = (StyleValues *)
705 Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
706 stylePtr->hPtr = hPtr;
707 Tcl_SetHashValue(hPtr, stylePtr);
712 *----------------------------------------------------------------------
716 * This procedure is called when a TextStyle structure is no longer
717 * needed. It decrements the reference count and frees up the
718 * space for the style structure if the reference count is 0.
724 * The storage and other resources associated with the style
725 * are freed up if no-one's still using it.
727 *----------------------------------------------------------------------
731 FreeStyle(textPtr, stylePtr)
732 TkText *textPtr; /* Information about overall widget. */
733 register TextStyle *stylePtr; /* Information about style to free. */
736 stylePtr->refCount--;
737 if (stylePtr->refCount == 0) {
738 if (stylePtr->bgGC != None) {
739 Tk_FreeGC(textPtr->display, stylePtr->bgGC);
741 if (stylePtr->fgGC != None) {
742 Tk_FreeGC(textPtr->display, stylePtr->fgGC);
744 Tcl_DeleteHashEntry(stylePtr->hPtr);
745 ckfree((char *) stylePtr);
750 *----------------------------------------------------------------------
754 * This procedure generates a single DLine structure for a display
755 * line whose leftmost character is given by indexPtr.
758 * The return value is a pointer to a DLine structure desribing the
759 * display line. All fields are filled in and correct except for
763 * Storage is allocated for the new DLine.
765 *----------------------------------------------------------------------
769 LayoutDLine(textPtr, indexPtr)
770 TkText *textPtr; /* Overall information about text widget. */
771 TkTextIndex *indexPtr; /* Beginning of display line. May not
772 * necessarily point to a character segment. */
774 register DLine *dlPtr; /* New display line. */
775 TkTextSegment *segPtr; /* Current segment in text. */
776 TkTextDispChunk *lastChunkPtr; /* Last chunk allocated so far
778 TkTextDispChunk *chunkPtr; /* Current chunk. */
779 TkTextIndex curIndex;
780 TkTextDispChunk *breakChunkPtr; /* Chunk containing best word break
782 TkTextIndex breakIndex; /* Index of first character in
784 int breakByteOffset; /* Byte offset of character within
785 * breakChunkPtr just to right of best
787 int noCharsYet; /* Non-zero means that no characters
788 * have been placed on the line yet. */
789 int justify; /* How to justify line: taken from
790 * style for the first character in
792 int jIndent; /* Additional indentation (beyond
793 * margins) due to justification. */
794 int rMargin; /* Right margin width for line. */
795 TkWrapMode wrapMode; /* Wrap mode to use for this line. */
796 int x = 0, maxX = 0; /* Initializations needed only to
797 * stop compiler warnings. */
798 int wholeLine; /* Non-zero means this display line
799 * runs to the end of the text line. */
800 int tabIndex; /* Index of the current tab stop. */
801 int gotTab; /* Non-zero means the current chunk
803 TkTextDispChunk *tabChunkPtr; /* Pointer to the chunk containing
804 * the previous tab stop. */
805 int maxBytes; /* Maximum number of bytes to
806 * include in this chunk. */
807 TkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
808 * style for the first character on
810 int tabSize; /* Number of pixels consumed by current
812 TkTextDispChunk *lastCharChunkPtr; /* Pointer to last chunk in display
813 * lines with numBytes > 0. Used to
814 * drop 0-sized chunks from the end
816 int byteOffset, ascent, descent, code, elide, elidesize;
817 StyleValues *sValuePtr;
820 * Create and initialize a new DLine structure.
823 dlPtr = (DLine *) ckalloc(sizeof(DLine));
824 dlPtr->index = *indexPtr;
825 dlPtr->byteCount = 0;
830 dlPtr->chunkPtr = NULL;
831 dlPtr->nextPtr = NULL;
832 dlPtr->flags = NEW_LAYOUT;
835 * Special case entirely elide line as there may be 1000s or more
837 elide = TkTextIsElided(textPtr, indexPtr); /* save a malloc */
838 if (elide && indexPtr->byteIndex==0) {
840 for (segPtr = indexPtr->linePtr->segPtr;
841 elide && (segPtr != NULL);
842 segPtr = segPtr->nextPtr) {
843 if ((elidesize = segPtr->size) > 0) {
844 maxBytes += elidesize;
846 * If have we have a tag toggle, there is a chance
847 * that invisibility state changed, so bail out
849 } else if ((segPtr->typePtr == &tkTextToggleOffType)
850 || (segPtr->typePtr == &tkTextToggleOnType)) {
851 if (segPtr->body.toggle.tagPtr->elideString != NULL) {
852 elide = (segPtr->typePtr == &tkTextToggleOffType)
853 ^ segPtr->body.toggle.tagPtr->elide;
859 dlPtr->byteCount = maxBytes;
860 dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0;
866 * Each iteration of the loop below creates one TkTextDispChunk for
867 * the new display line. The line will always have at least one
868 * chunk (for the newline character at the end, if there's nothing
872 curIndex = *indexPtr;
877 breakChunkPtr = NULL;
879 justify = TK_JUSTIFY_LEFT;
884 wrapMode = TEXT_WRAPMODE_CHAR;
886 lastCharChunkPtr = NULL;
889 * Find the first segment to consider for the line. Can't call
890 * TkTextIndexToSeg for this because it won't return a segment
891 * with zero size (such as the insertion cursor's mark).
894 for (byteOffset = curIndex.byteIndex, segPtr = curIndex.linePtr->segPtr;
895 (byteOffset > 0) && (byteOffset >= segPtr->size);
896 byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
897 /* Empty loop body. */
900 while (segPtr != NULL) {
902 * Every line still gets at least one chunk due to expectations
903 * in the rest of the code, but we are able to skip elided portions
904 * of the line quickly.
905 * If current chunk is elided and last chunk was too, coalese
907 if (elide && (lastChunkPtr != NULL)
908 && (lastChunkPtr->displayProc == NULL /*ElideDisplayProc*/)) {
909 if ((elidesize = segPtr->size - byteOffset) > 0) {
910 curIndex.byteIndex += elidesize;
911 lastChunkPtr->numBytes += elidesize;
912 breakByteOffset = lastChunkPtr->breakIndex = lastChunkPtr->numBytes;
914 * If have we have a tag toggle, there is a chance
915 * that invisibility state changed, so bail out
917 } else if ((segPtr->typePtr == &tkTextToggleOffType)
918 || (segPtr->typePtr == &tkTextToggleOnType)) {
919 if (segPtr->body.toggle.tagPtr->elideString != NULL) {
920 elide = (segPtr->typePtr == &tkTextToggleOffType)
921 ^ segPtr->body.toggle.tagPtr->elide;
926 segPtr = segPtr->nextPtr;
927 if (segPtr == NULL && chunkPtr != NULL) {
928 ckfree((char *) chunkPtr);
933 if (segPtr->typePtr->layoutProc == NULL) {
934 segPtr = segPtr->nextPtr;
938 if (chunkPtr == NULL) {
939 chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
940 chunkPtr->nextPtr = NULL;
942 chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
943 elide = chunkPtr->stylePtr->sValuePtr->elide;
946 * Save style information such as justification and indentation,
947 * up until the first character is encountered, then retain that
948 * information for the rest of the line.
952 tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
953 justify = chunkPtr->stylePtr->sValuePtr->justify;
954 rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
955 wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
956 x = ((curIndex.byteIndex == 0)
957 ? chunkPtr->stylePtr->sValuePtr->lMargin1
958 : chunkPtr->stylePtr->sValuePtr->lMargin2);
959 if (wrapMode == TEXT_WRAPMODE_NONE) {
962 maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
971 * See if there is a tab in the current chunk; if so, only
972 * layout characters up to (and including) the tab.
976 maxBytes = segPtr->size - byteOffset;
977 if (!elide && justify == TK_JUSTIFY_LEFT) {
978 if (segPtr->typePtr == &tkTextCharType) {
981 for (p = segPtr->body.chars + byteOffset; *p != 0; p++) {
983 maxBytes = (p + 1 - segPtr->body.chars) - byteOffset;
991 if (elide && maxBytes) {
992 /* don't free style here, as other code expects to be able to do that */
993 /*breakByteOffset =*/ chunkPtr->breakIndex = chunkPtr->numBytes = maxBytes;
995 chunkPtr->minAscent = chunkPtr->minDescent = chunkPtr->minHeight = 0;
997 /* would just like to point to canonical empty chunk */
998 chunkPtr->displayProc = (Tk_ChunkDisplayProc *) NULL;
999 chunkPtr->undisplayProc = (Tk_ChunkUndisplayProc *) NULL;
1000 chunkPtr->measureProc = ElideMeasureProc;
1001 chunkPtr->bboxProc = ElideBboxProc;
1005 code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
1006 byteOffset, maxX-tabSize, maxBytes, noCharsYet, wrapMode,
1009 FreeStyle(textPtr, chunkPtr->stylePtr);
1012 * This segment doesn't wish to display itself (e.g. most
1016 segPtr = segPtr->nextPtr;
1022 * No characters from this segment fit in the window: this
1023 * means we're at the end of the display line.
1026 if (chunkPtr != NULL) {
1027 ckfree((char *) chunkPtr);
1031 if (chunkPtr->numBytes > 0) {
1033 lastCharChunkPtr = chunkPtr;
1035 if (lastChunkPtr == NULL) {
1036 dlPtr->chunkPtr = chunkPtr;
1038 lastChunkPtr->nextPtr = chunkPtr;
1040 lastChunkPtr = chunkPtr;
1041 x += chunkPtr->width;
1042 if (chunkPtr->breakIndex > 0) {
1043 breakByteOffset = chunkPtr->breakIndex;
1044 breakIndex = curIndex;
1045 breakChunkPtr = chunkPtr;
1047 if (chunkPtr->numBytes != maxBytes) {
1052 * If we're at a new tab, adjust the layout for all the chunks
1053 * pertaining to the previous tab. Also adjust the amount of
1054 * space left in the line to account for space that will be eaten
1059 if (tabIndex >= 0) {
1060 AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
1061 x = chunkPtr->x + chunkPtr->width;
1064 tabChunkPtr = chunkPtr;
1065 tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
1066 if ((maxX >= 0) && (tabSize >= maxX - x)) {
1070 curIndex.byteIndex += chunkPtr->numBytes;
1071 byteOffset += chunkPtr->numBytes;
1072 if (byteOffset >= segPtr->size) {
1074 segPtr = segPtr->nextPtr;
1080 panic("LayoutDLine couldn't place any characters on a line");
1082 wholeLine = (segPtr == NULL);
1085 * We're at the end of the display line. Throw away everything
1086 * after the most recent word break, if there is one; this may
1087 * potentially require the last chunk to be layed out again.
1090 if (breakChunkPtr == NULL) {
1092 * This code makes sure that we don't accidentally display
1093 * chunks with no characters at the end of the line (such as
1094 * the insertion cursor). These chunks belong on the next
1095 * line. So, throw away everything after the last chunk that
1096 * has characters in it.
1099 breakChunkPtr = lastCharChunkPtr;
1100 breakByteOffset = breakChunkPtr->numBytes;
1102 if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
1103 || (breakByteOffset != lastChunkPtr->numBytes))) {
1105 chunkPtr = breakChunkPtr->nextPtr;
1106 if (chunkPtr == NULL) {
1109 FreeStyle(textPtr, chunkPtr->stylePtr);
1110 breakChunkPtr->nextPtr = chunkPtr->nextPtr;
1111 (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
1112 ckfree((char *) chunkPtr);
1114 if (breakByteOffset != breakChunkPtr->numBytes) {
1115 (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
1116 segPtr = TkTextIndexToSeg(&breakIndex, &byteOffset);
1117 (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
1118 segPtr, byteOffset, maxX, breakByteOffset, 0,
1119 wrapMode, breakChunkPtr);
1121 lastChunkPtr = breakChunkPtr;
1127 * Make tab adjustments for the last tab stop, if there is one.
1130 if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
1131 AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
1135 * Make one more pass over the line to recompute various things
1136 * like its height, length, and total number of bytes. Also
1137 * modify the x-locations of chunks to reflect justification.
1138 * If we're not wrapping, I'm not sure what is the best way to
1139 * handle left and center justification: should the total length,
1140 * for purposes of justification, be (a) the window width, (b)
1141 * the length of the longest line in the window, or (c) the length
1142 * of the longest line in the text? (c) isn't available, (b) seems
1143 * weird, since it can change with vertical scrolling, so (a) is
1144 * what is implemented below.
1147 if (wrapMode == TEXT_WRAPMODE_NONE) {
1148 maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
1150 dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
1151 if (justify == TK_JUSTIFY_LEFT) {
1153 } else if (justify == TK_JUSTIFY_RIGHT) {
1154 jIndent = maxX - dlPtr->length;
1156 jIndent = (maxX - dlPtr->length)/2;
1158 ascent = descent = 0;
1159 for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
1160 chunkPtr = chunkPtr->nextPtr) {
1161 chunkPtr->x += jIndent;
1162 dlPtr->byteCount += chunkPtr->numBytes;
1163 if (chunkPtr->minAscent > ascent) {
1164 ascent = chunkPtr->minAscent;
1166 if (chunkPtr->minDescent > descent) {
1167 descent = chunkPtr->minDescent;
1169 if (chunkPtr->minHeight > dlPtr->height) {
1170 dlPtr->height = chunkPtr->minHeight;
1172 sValuePtr = chunkPtr->stylePtr->sValuePtr;
1173 if ((sValuePtr->borderWidth > 0)
1174 && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1175 dlPtr->flags |= HAS_3D_BORDER;
1178 if (dlPtr->height < (ascent + descent)) {
1179 dlPtr->height = ascent + descent;
1180 dlPtr->baseline = ascent;
1182 dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
1184 sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
1185 if (dlPtr->index.byteIndex == 0) {
1186 dlPtr->spaceAbove = sValuePtr->spacing1;
1188 dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
1191 dlPtr->spaceBelow = sValuePtr->spacing3;
1193 dlPtr->spaceBelow = sValuePtr->spacing2/2;
1195 dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
1196 dlPtr->baseline += dlPtr->spaceAbove;
1199 * Recompute line length: may have changed because of justification.
1202 dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
1207 *----------------------------------------------------------------------
1209 * UpdateDisplayInfo --
1211 * This procedure is invoked to recompute some or all of the
1212 * DLine structures for a text widget. At the time it is called
1213 * the DLine structures still left in the widget are guaranteed
1214 * to be correct except that (a) the y-coordinates aren't
1215 * necessarily correct, (b) there may be missing structures
1216 * (the DLine structures get removed as soon as they are potentially
1217 * out-of-date), and (c) DLine structures that don't start at the
1218 * beginning of a line may be incorrect if previous information in
1219 * the same line changed size in a way that moved a line boundary
1220 * (DLines for any info that changed will have been deleted, but
1221 * not DLines for unchanged info in the same text line).
1227 * Upon return, the DLine information for textPtr correctly reflects
1228 * the positions where characters will be displayed. However, this
1229 * procedure doesn't actually bring the display up-to-date.
1231 *----------------------------------------------------------------------
1235 UpdateDisplayInfo(textPtr)
1236 TkText *textPtr; /* Text widget to update. */
1238 register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1239 register DLine *dlPtr, *prevPtr;
1241 TkTextLine *lastLinePtr;
1242 int y, maxY, pixelOffset, maxOffset;
1244 if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
1247 dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
1250 * Delete any DLines that are now above the top of the window.
1253 index = textPtr->topIndex;
1254 dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
1255 if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
1256 FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
1260 *--------------------------------------------------------------
1261 * Scan through the contents of the window from top to bottom,
1262 * recomputing information for lines that are missing.
1263 *--------------------------------------------------------------
1266 lastLinePtr = TkBTreeFindLine(textPtr->tree,
1267 TkBTreeNumLines(textPtr->tree));
1268 dlPtr = dInfoPtr->dLinePtr;
1271 maxY = dInfoPtr->maxY;
1273 register DLine *newPtr;
1275 if (index.linePtr == lastLinePtr) {
1280 * There are three possibilities right now:
1281 * (a) the next DLine (dlPtr) corresponds exactly to the next
1282 * information we want to display: just use it as-is.
1283 * (b) the next DLine corresponds to a different line, or to
1284 * a segment that will be coming later in the same line:
1285 * leave this DLine alone in the hopes that we'll be able
1286 * to use it later, then create a new DLine in front of
1288 * (c) the next DLine corresponds to a segment in the line we
1289 * want, but it's a segment that has already been processed
1290 * or will never be processed. Delete the DLine and try
1293 * One other twist on all this. It's possible for 3D borders
1294 * to interact between lines (see DisplayLineBackground) so if
1295 * a line is relayed out and has styles with 3D borders, its
1296 * neighbors have to be redrawn if they have 3D borders too,
1297 * since the interactions could have changed (the neighbors
1298 * don't have to be relayed out, just redrawn).
1301 if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
1303 * Case (b) -- must make new DLine.
1308 char string[TK_POS_CHARS];
1311 * Debugging is enabled, so keep a log of all the lines
1312 * that were re-layed out. The test suite uses this
1316 TkTextPrintIndex(&index, string);
1317 Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
1319 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1321 newPtr = LayoutDLine(textPtr, &index);
1322 if (prevPtr == NULL) {
1323 dInfoPtr->dLinePtr = newPtr;
1325 prevPtr->nextPtr = newPtr;
1326 if (prevPtr->flags & HAS_3D_BORDER) {
1330 newPtr->nextPtr = dlPtr;
1334 * DlPtr refers to the line we want. Next check the
1335 * index within the line.
1338 if (index.byteIndex == dlPtr->index.byteIndex) {
1340 * Case (a) -- can use existing display line as-is.
1343 if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
1344 && (prevPtr->flags & (NEW_LAYOUT))) {
1349 if (index.byteIndex < dlPtr->index.byteIndex) {
1354 * Case (c) -- dlPtr is useless. Discard it and start
1355 * again with the next display line.
1358 newPtr = dlPtr->nextPtr;
1359 FreeDLines(textPtr, dlPtr, newPtr, 0);
1361 if (prevPtr != NULL) {
1362 prevPtr->nextPtr = newPtr;
1364 dInfoPtr->dLinePtr = newPtr;
1370 * Advance to the start of the next line.
1376 TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
1378 dlPtr = dlPtr->nextPtr;
1381 * If we switched text lines, delete any DLines left for the
1385 if (index.linePtr != prevPtr->index.linePtr) {
1386 register DLine *nextPtr;
1389 while ((nextPtr != NULL)
1390 && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
1391 nextPtr = nextPtr->nextPtr;
1393 if (nextPtr != dlPtr) {
1394 FreeDLines(textPtr, dlPtr, nextPtr, 0);
1395 prevPtr->nextPtr = nextPtr;
1401 * It's important to have the following check here rather than in
1402 * the while statement for the loop, so that there's always at least
1403 * one DLine generated, regardless of how small the window is. This
1404 * keeps a lot of other code from breaking.
1413 * Delete any DLine structures that don't fit on the screen.
1416 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
1419 *--------------------------------------------------------------
1420 * If there is extra space at the bottom of the window (because
1421 * we've hit the end of the text), then bring in more lines at
1422 * the top of the window, if there are any, to fill in the view.
1423 *--------------------------------------------------------------
1427 int lineNum, spaceLeft, bytesToCount;
1431 * Layout an entire text line (potentially > 1 display line),
1432 * then link in as many display lines as fit without moving
1433 * the bottom line out of the window. Repeat this until
1434 * all the extra space has been used up or we've reached the
1435 * beginning of the text.
1438 spaceLeft = maxY - y;
1439 lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
1440 bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
1441 if (bytesToCount == 0) {
1442 bytesToCount = INT_MAX;
1445 for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
1446 index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
1447 index.byteIndex = 0;
1451 dlPtr = LayoutDLine(textPtr, &index);
1452 dlPtr->nextPtr = lowestPtr;
1454 if (dlPtr->length == 0 && dlPtr->height == 0) { bytesToCount--; break; } /* elide */
1455 TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
1456 bytesToCount -= dlPtr->byteCount;
1457 } while ((bytesToCount > 0)
1458 && (index.linePtr == lowestPtr->index.linePtr));
1461 * Scan through the display lines from the bottom one up to
1465 while (lowestPtr != NULL) {
1467 spaceLeft -= dlPtr->height;
1468 if (spaceLeft < 0) {
1471 lowestPtr = dlPtr->nextPtr;
1472 dlPtr->nextPtr = dInfoPtr->dLinePtr;
1473 dInfoPtr->dLinePtr = dlPtr;
1475 char string[TK_POS_CHARS];
1477 TkTextPrintIndex(&dlPtr->index, string);
1478 Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
1479 (char *) NULL, string,
1480 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1483 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
1484 bytesToCount = INT_MAX;
1488 * Now we're all done except that the y-coordinates in all the
1489 * DLines are wrong and the top index for the text is wrong.
1493 textPtr->topIndex = dInfoPtr->dLinePtr->index;
1495 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1496 dlPtr = dlPtr->nextPtr) {
1497 if (y > dInfoPtr->maxY) {
1498 panic("Added too many new lines in UpdateDisplayInfo");
1506 *--------------------------------------------------------------
1507 * If the old top or bottom line has scrolled elsewhere on the
1508 * screen, we may not be able to re-use its old contents by
1509 * copying bits (e.g., a beveled edge that was drawn when it was
1510 * at the top or bottom won't be drawn when the line is in the
1511 * middle and its neighbor has a matching background). Similarly,
1512 * if the new top or bottom line came from somewhere else on the
1513 * screen, we may not be able to copy the old bits.
1514 *--------------------------------------------------------------
1517 dlPtr = dInfoPtr->dLinePtr;
1518 if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
1522 if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
1523 && (dlPtr->flags & HAS_3D_BORDER)) {
1526 if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
1527 && (dlPtr->flags & HAS_3D_BORDER)) {
1530 if (dlPtr->nextPtr == NULL) {
1531 if ((dlPtr->flags & HAS_3D_BORDER)
1532 && !(dlPtr->flags & BOTTOM_LINE)) {
1535 dlPtr->flags &= ~TOP_LINE;
1536 dlPtr->flags |= BOTTOM_LINE;
1539 dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
1540 dlPtr = dlPtr->nextPtr;
1542 dInfoPtr->dLinePtr->flags |= TOP_LINE;
1545 * Arrange for scrollbars to be updated.
1548 textPtr->flags |= UPDATE_SCROLLBARS;
1551 *--------------------------------------------------------------
1552 * Deal with horizontal scrolling:
1553 * 1. If there's empty space to the right of the longest line,
1554 * shift the screen to the right to fill in the empty space.
1555 * 2. If the desired horizontal scroll position has changed,
1556 * force a full redisplay of all the lines in the widget.
1557 * 3. If the wrap mode isn't "none" then re-scroll to the base
1559 *--------------------------------------------------------------
1562 dInfoPtr->maxLength = 0;
1563 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1564 dlPtr = dlPtr->nextPtr) {
1565 if (dlPtr->length > dInfoPtr->maxLength) {
1566 dInfoPtr->maxLength = dlPtr->length;
1569 maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
1570 + textPtr->charWidth - 1)/textPtr->charWidth;
1571 if (dInfoPtr->newByteOffset > maxOffset) {
1572 dInfoPtr->newByteOffset = maxOffset;
1574 if (dInfoPtr->newByteOffset < 0) {
1575 dInfoPtr->newByteOffset = 0;
1577 pixelOffset = dInfoPtr->newByteOffset * textPtr->charWidth;
1578 if (pixelOffset != dInfoPtr->curPixelOffset) {
1579 dInfoPtr->curPixelOffset = pixelOffset;
1580 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1581 dlPtr = dlPtr->nextPtr) {
1588 *----------------------------------------------------------------------
1592 * This procedure is called to free up all of the resources
1593 * associated with one or more DLine structures.
1599 * Memory gets freed and various other resources are released.
1601 *----------------------------------------------------------------------
1605 FreeDLines(textPtr, firstPtr, lastPtr, unlink)
1606 TkText *textPtr; /* Information about overall text
1608 register DLine *firstPtr; /* Pointer to first DLine to free up. */
1609 DLine *lastPtr; /* Pointer to DLine just after last
1610 * one to free (NULL means everything
1611 * starting with firstPtr). */
1612 int unlink; /* 1 means DLines are currently linked
1613 * into the list rooted at
1614 * textPtr->dInfoPtr->dLinePtr and
1615 * they have to be unlinked. 0 means
1616 * just free without unlinking. */
1618 register TkTextDispChunk *chunkPtr, *nextChunkPtr;
1619 register DLine *nextDLinePtr;
1622 if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
1623 textPtr->dInfoPtr->dLinePtr = lastPtr;
1625 register DLine *prevPtr;
1626 for (prevPtr = textPtr->dInfoPtr->dLinePtr;
1627 prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
1628 /* Empty loop body. */
1630 prevPtr->nextPtr = lastPtr;
1633 while (firstPtr != lastPtr) {
1634 nextDLinePtr = firstPtr->nextPtr;
1635 for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
1636 chunkPtr = nextChunkPtr) {
1637 if (chunkPtr->undisplayProc != NULL) {
1638 (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
1640 FreeStyle(textPtr, chunkPtr->stylePtr);
1641 nextChunkPtr = chunkPtr->nextPtr;
1642 ckfree((char *) chunkPtr);
1644 ckfree((char *) firstPtr);
1645 firstPtr = nextDLinePtr;
1647 textPtr->dInfoPtr->dLinesInvalidated = 1;
1651 *----------------------------------------------------------------------
1655 * This procedure is invoked to draw a single line on the
1662 * The line given by dlPtr is drawn at its correct position in
1663 * textPtr's window. Note that this is one *display* line, not
1666 *----------------------------------------------------------------------
1670 DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
1671 TkText *textPtr; /* Text widget in which to draw line. */
1672 register DLine *dlPtr; /* Information about line to draw. */
1673 DLine *prevPtr; /* Line just before one to draw, or NULL
1674 * if dlPtr is the top line. */
1675 Pixmap pixmap; /* Pixmap to use for double-buffering.
1676 * Caller must make sure it's large enough
1679 register TkTextDispChunk *chunkPtr;
1680 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1684 if (dlPtr->chunkPtr == NULL) return;
1687 * First, clear the area of the line to the background color for the
1691 display = Tk_Display(textPtr->tkwin);
1692 Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
1693 Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
1696 * Next, draw background information for the whole line.
1699 DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
1702 * Make another pass through all of the chunks to redraw the
1703 * insertion cursor, if it is visible on this line. Must do
1704 * it here rather than in the foreground pass below because
1705 * otherwise a wide insertion cursor will obscure the character
1709 if (textPtr->state == TK_STATE_NORMAL) {
1710 for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
1711 chunkPtr = chunkPtr->nextPtr) {
1712 x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
1713 if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
1714 (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
1715 dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1716 dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1717 dlPtr->y + dlPtr->spaceAbove);
1723 * Make yet another pass through all of the chunks to redraw all of
1724 * foreground information. Note: we have to call the displayProc
1725 * even for chunks that are off-screen. This is needed, for
1726 * example, so that embedded windows can be unmapped in this case.
1730 for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
1731 chunkPtr = chunkPtr->nextPtr) {
1732 if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
1734 * Already displayed the insertion cursor above. Don't
1740 x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
1741 if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
1743 * Note: we have to call the displayProc even for chunks
1744 * that are off-screen. This is needed, for example, so
1745 * that embedded windows can be unmapped in this case.
1746 * Display the chunk at a coordinate that can be clearly
1747 * identified by the displayProc as being off-screen to
1748 * the left (the displayProc may not be able to tell if
1749 * something is off to the right).
1752 if (chunkPtr->displayProc != NULL)
1753 (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
1755 dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1756 dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1757 dlPtr->y + dlPtr->spaceAbove);
1759 /* don't call if elide. This tax ok since not very many visible DLine's in
1760 an area, but potentially many elide ones */
1761 if (chunkPtr->displayProc != NULL)
1762 (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
1763 dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1764 dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1765 dlPtr->y + dlPtr->spaceAbove);
1767 if (dInfoPtr->dLinesInvalidated) {
1773 * Copy the pixmap onto the screen. If this is the last line on
1774 * the screen then copy a piece of the line, so that it doesn't
1775 * overflow into the border area. Another special trick: copy the
1776 * padding area to the left of the line; this is because the
1777 * insertion cursor sometimes overflows onto that area and we want
1778 * to get as much of the cursor as possible.
1781 height = dlPtr->height;
1782 if ((height + dlPtr->y) > dInfoPtr->maxY) {
1783 height = dInfoPtr->maxY - dlPtr->y;
1785 XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
1786 dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
1787 (unsigned) height, dInfoPtr->x, dlPtr->y);
1792 *--------------------------------------------------------------
1794 * DisplayLineBackground --
1796 * This procedure is called to fill in the background for
1797 * a display line. It draws 3D borders cleverly so that
1798 * adjacent chunks with the same style (whether on the same
1799 * line or different lines) have a single 3D border around
1803 * There is no return value. Pixmap is filled in with background
1804 * information for dlPtr.
1809 *--------------------------------------------------------------
1813 DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
1814 TkText *textPtr; /* Text widget containing line. */
1815 register DLine *dlPtr; /* Information about line to draw. */
1816 DLine *prevPtr; /* Line just above dlPtr, or NULL if dlPtr
1817 * is the top-most line in the window. */
1818 Pixmap pixmap; /* Pixmap to use for double-buffering.
1819 * Caller must make sure it's large enough
1820 * to hold line. Caller must also have
1821 * filled it with the background color for
1824 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1825 TkTextDispChunk *chunkPtr; /* Pointer to chunk in the current line. */
1826 TkTextDispChunk *chunkPtr2; /* Pointer to chunk in the line above or
1827 * below the current one. NULL if we're to
1828 * the left of or to the right of the chunks
1830 TkTextDispChunk *nextPtr2; /* Next chunk after chunkPtr2 (it's not the
1831 * same as chunkPtr2->nextPtr in the case
1832 * where chunkPtr2 is NULL because the line
1834 int leftX; /* The left edge of the region we're
1835 * currently working on. */
1836 int leftXIn; /* 1 means beveled edge at leftX slopes right
1837 * as it goes down, 0 means it slopes left
1838 * as it goes down. */
1839 int rightX; /* Right edge of chunkPtr. */
1840 int rightX2; /* Right edge of chunkPtr2. */
1841 int matchLeft; /* Does the style of this line match that
1842 * of its neighbor just to the left of
1843 * the current x coordinate? */
1844 int matchRight; /* Does line's style match its neighbor
1845 * just to the right of the current x-coord? */
1846 int minX, maxX, xOffset;
1847 StyleValues *sValuePtr;
1852 * Pass 1: scan through dlPtr from left to right. For each range of
1853 * chunks with the same style, draw the main background for the style
1854 * plus the vertical parts of the 3D borders (the left and right
1858 display = Tk_Display(textPtr->tkwin);
1859 minX = dInfoPtr->curPixelOffset;
1860 xOffset = dInfoPtr->x - minX;
1861 maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
1862 chunkPtr = dlPtr->chunkPtr;
1865 * Note A: in the following statement, and a few others later in
1866 * this file marked with "See Note A above", the right side of the
1867 * assignment was replaced with 0 on 6/18/97. This has the effect
1868 * of highlighting the empty space to the left of a line whenever
1869 * the leftmost character of the line is highlighted. This way,
1870 * multi-line highlights always line up along their left edges.
1871 * However, this may look funny in the case where a single word is
1872 * highlighted. To undo the change, replace "leftX = 0" with "leftX
1873 * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
1874 * here and at all the marked points below. This restores the old
1875 * behavior where empty space to the left of a line is not
1876 * highlighted, leaving a ragged left edge for multi-line
1881 for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
1882 if ((chunkPtr->nextPtr != NULL)
1883 && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
1884 chunkPtr->stylePtr)) {
1887 sValuePtr = chunkPtr->stylePtr->sValuePtr;
1888 rightX = chunkPtr->x + chunkPtr->width;
1889 if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1892 if (chunkPtr->stylePtr->bgGC != None) {
1893 /* Not visible - bail out now */
1894 if (rightX + xOffset <= 0) {
1900 * Trim the start position for drawing to be no further away than
1901 * -borderWidth. The reason is that on many X servers drawing from
1902 * -32768 (or less) to +something simply does not display
1903 * correctly. [Patch #541999]
1905 if ((leftX + xOffset) < -(sValuePtr->borderWidth)) {
1906 leftX = -sValuePtr->borderWidth - xOffset;
1908 if ((rightX - leftX) > 32767) {
1909 rightX = leftX + 32767;
1912 XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
1913 leftX + xOffset, 0, (unsigned int) (rightX - leftX),
1914 (unsigned int) dlPtr->height);
1915 if (sValuePtr->relief != TK_RELIEF_FLAT) {
1916 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1917 leftX + xOffset, 0, sValuePtr->borderWidth,
1918 dlPtr->height, 1, sValuePtr->relief);
1919 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1920 rightX - sValuePtr->borderWidth + xOffset,
1921 0, sValuePtr->borderWidth, dlPtr->height, 0,
1929 * Pass 2: draw the horizontal bevels along the top of the line. To
1930 * do this, scan through dlPtr from left to right while simultaneously
1931 * scanning through the line just above dlPtr. ChunkPtr2 and nextPtr2
1932 * refer to two adjacent chunks in the line above.
1935 chunkPtr = dlPtr->chunkPtr;
1936 leftX = 0; /* See Note A above. */
1938 rightX = chunkPtr->x + chunkPtr->width;
1939 if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1943 if (prevPtr != NULL && prevPtr->chunkPtr != NULL) {
1945 * Find the chunk in the previous line that covers leftX.
1948 nextPtr2 = prevPtr->chunkPtr;
1949 rightX2 = 0; /* See Note A above. */
1950 while (rightX2 <= leftX) {
1951 chunkPtr2 = nextPtr2;
1952 if (chunkPtr2 == NULL) {
1955 nextPtr2 = chunkPtr2->nextPtr;
1956 rightX2 = chunkPtr2->x + chunkPtr2->width;
1957 if (nextPtr2 == NULL) {
1966 while (leftX < maxX) {
1967 matchLeft = (chunkPtr2 != NULL)
1968 && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
1969 sValuePtr = chunkPtr->stylePtr->sValuePtr;
1970 if (rightX <= rightX2) {
1972 * The chunk in our line is about to end. If its style
1973 * changes then draw the bevel for the current style.
1976 if ((chunkPtr->nextPtr == NULL)
1977 || !SAME_BACKGROUND(chunkPtr->stylePtr,
1978 chunkPtr->nextPtr->stylePtr)) {
1979 if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1980 Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
1981 sValuePtr->border, leftX + xOffset, 0,
1982 rightX - leftX, sValuePtr->borderWidth, leftXIn,
1983 1, 1, sValuePtr->relief);
1989 * If the chunk in the line above is also ending at
1990 * the same point then advance to the next chunk in
1994 if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
1998 chunkPtr = chunkPtr->nextPtr;
1999 if (chunkPtr == NULL) {
2002 rightX = chunkPtr->x + chunkPtr->width;
2003 if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
2010 * The chunk in the line above is ending at an x-position where
2011 * there is no change in the style of the current line. If the
2012 * style above matches the current line on one side of the change
2013 * but not on the other, we have to draw an L-shaped piece of
2017 matchRight = (nextPtr2 != NULL)
2018 && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
2019 if (matchLeft && !matchRight) {
2020 if (sValuePtr->relief != TK_RELIEF_FLAT) {
2021 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2022 rightX2 - sValuePtr->borderWidth + xOffset, 0,
2023 sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
2026 leftX = rightX2 - sValuePtr->borderWidth;
2028 } else if (!matchLeft && matchRight
2029 && (sValuePtr->relief != TK_RELIEF_FLAT)) {
2030 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2031 rightX2 + xOffset, 0, sValuePtr->borderWidth,
2032 sValuePtr->borderWidth, 1, sValuePtr->relief);
2033 Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2034 leftX + xOffset, 0, rightX2 + sValuePtr->borderWidth -leftX,
2035 sValuePtr->borderWidth, leftXIn, 0, 1,
2040 chunkPtr2 = nextPtr2;
2041 if (chunkPtr2 == NULL) {
2044 nextPtr2 = chunkPtr2->nextPtr;
2045 rightX2 = chunkPtr2->x + chunkPtr2->width;
2046 if (nextPtr2 == NULL) {
2052 * Pass 3: draw the horizontal bevels along the bottom of the line.
2053 * This uses the same approach as pass 2.
2056 chunkPtr = dlPtr->chunkPtr;
2057 leftX = 0; /* See Note A above. */
2059 rightX = chunkPtr->x + chunkPtr->width;
2060 if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
2064 if (dlPtr->nextPtr != NULL && dlPtr->nextPtr->chunkPtr != NULL) {
2066 * Find the chunk in the previous line that covers leftX.
2069 nextPtr2 = dlPtr->nextPtr->chunkPtr;
2070 rightX2 = 0; /* See Note A above. */
2071 while (rightX2 <= leftX) {
2072 chunkPtr2 = nextPtr2;
2073 if (chunkPtr2 == NULL) {
2076 nextPtr2 = chunkPtr2->nextPtr;
2077 rightX2 = chunkPtr2->x + chunkPtr2->width;
2078 if (nextPtr2 == NULL) {
2087 while (leftX < maxX) {
2088 matchLeft = (chunkPtr2 != NULL)
2089 && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
2090 sValuePtr = chunkPtr->stylePtr->sValuePtr;
2091 if (rightX <= rightX2) {
2092 if ((chunkPtr->nextPtr == NULL)
2093 || !SAME_BACKGROUND(chunkPtr->stylePtr,
2094 chunkPtr->nextPtr->stylePtr)) {
2095 if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
2096 Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
2097 sValuePtr->border, leftX + xOffset,
2098 dlPtr->height - sValuePtr->borderWidth,
2099 rightX - leftX, sValuePtr->borderWidth, leftXIn,
2100 0, 0, sValuePtr->relief);
2104 if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
2108 chunkPtr = chunkPtr->nextPtr;
2109 if (chunkPtr == NULL) {
2112 rightX = chunkPtr->x + chunkPtr->width;
2113 if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
2119 matchRight = (nextPtr2 != NULL)
2120 && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
2121 if (matchLeft && !matchRight) {
2122 if (sValuePtr->relief != TK_RELIEF_FLAT) {
2123 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2124 rightX2 - sValuePtr->borderWidth + xOffset,
2125 dlPtr->height - sValuePtr->borderWidth,
2126 sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
2129 leftX = rightX2 - sValuePtr->borderWidth;
2131 } else if (!matchLeft && matchRight
2132 && (sValuePtr->relief != TK_RELIEF_FLAT)) {
2133 Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2134 rightX2 + xOffset, dlPtr->height - sValuePtr->borderWidth,
2135 sValuePtr->borderWidth, sValuePtr->borderWidth,
2136 1, sValuePtr->relief);
2137 Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
2138 leftX + xOffset, dlPtr->height - sValuePtr->borderWidth,
2139 rightX2 + sValuePtr->borderWidth - leftX,
2140 sValuePtr->borderWidth, leftXIn, 1, 0, sValuePtr->relief);
2144 chunkPtr2 = nextPtr2;
2145 if (chunkPtr2 == NULL) {
2148 nextPtr2 = chunkPtr2->nextPtr;
2149 rightX2 = chunkPtr2->x + chunkPtr2->width;
2150 if (nextPtr2 == NULL) {
2158 *----------------------------------------------------------------------
2162 * This procedure is invoked as a when-idle handler to update the
2163 * display. It only redisplays the parts of the text widget that
2170 * Information is redrawn on the screen.
2172 *----------------------------------------------------------------------
2176 DisplayText(clientData)
2177 ClientData clientData; /* Information about widget. */
2179 register TkText *textPtr = (TkText *) clientData;
2180 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2182 register DLine *dlPtr;
2185 int maxHeight, borders;
2186 int bottomY = 0; /* Initialization needed only to stop
2187 * compiler warnings. */
2190 if (textPtr->tkwin == NULL) {
2193 * The widget has been deleted. Don't do anything.
2199 interp = textPtr->interp;
2200 Tcl_Preserve((ClientData) interp);
2203 Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
2207 if (textPtr->tkwin == NULL) {
2210 * The widget has been deleted. Don't do anything.
2216 if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
2217 || (dInfoPtr->maxY <= dInfoPtr->y)) {
2218 UpdateDisplayInfo(textPtr);
2219 dInfoPtr->flags &= ~REDRAW_PENDING;
2224 Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
2228 if (textPtr->tkwin == NULL) {
2231 * The widget has been deleted. Don't do anything.
2238 * Choose a new current item if that is needed (this could cause
2239 * event handlers to be invoked, hence the preserve/release calls
2240 * and the loop, since the handlers could conceivably necessitate
2241 * yet another current item calculation). The tkwin check is because
2242 * the whole window could go away in the Tcl_Release call.
2245 while (dInfoPtr->flags & REPICK_NEEDED) {
2246 Tcl_Preserve((ClientData) textPtr);
2247 dInfoPtr->flags &= ~REPICK_NEEDED;
2248 TkTextPickCurrent(textPtr, &textPtr->pickEvent);
2249 tkwin = textPtr->tkwin;
2250 Tcl_Release((ClientData) textPtr);
2251 if (tkwin == NULL) {
2257 * First recompute what's supposed to be displayed.
2260 UpdateDisplayInfo(textPtr);
2261 dInfoPtr->dLinesInvalidated = 0;
2264 * See if it's possible to bring some parts of the screen up-to-date
2265 * by scrolling (copying from other parts of the screen).
2268 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
2269 register DLine *dlPtr2;
2270 int offset, height, y, oldY;
2273 if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
2274 || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
2279 * This line is already drawn somewhere in the window so it only
2280 * needs to be copied to its new location. See if there's a group
2281 * of lines that can all be copied together.
2284 offset = dlPtr->y - dlPtr->oldY;
2285 height = dlPtr->height;
2287 for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
2288 dlPtr2 = dlPtr2->nextPtr) {
2289 if ((dlPtr2->oldY == -1)
2290 || ((dlPtr2->oldY + offset) != dlPtr2->y)
2291 || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
2294 height += dlPtr2->height;
2298 * Reduce the height of the area being copied if necessary to
2299 * avoid overwriting the border area.
2302 if ((y + height) > dInfoPtr->maxY) {
2303 height = dInfoPtr->maxY -y;
2308 * Update the lines we are going to scroll to show that they
2313 dlPtr->oldY = dlPtr->y;
2314 if (dlPtr->nextPtr == dlPtr2) {
2317 dlPtr = dlPtr->nextPtr;
2321 * Scan through the lines following the copied ones to see if
2322 * we are going to overwrite them with the copy operation.
2323 * If so, mark them for redisplay.
2326 for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
2327 if ((dlPtr2->oldY != -1)
2328 && ((dlPtr2->oldY + dlPtr2->height) > y)
2329 && (dlPtr2->oldY < (y + height))) {
2335 * Now scroll the lines. This may generate damage which we
2336 * handle by calling TextInvalidateRegion to mark the display
2340 damageRgn = TkCreateRegion();
2341 if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
2343 (dInfoPtr->maxX - dInfoPtr->x), height,
2344 0, y - oldY, damageRgn)) {
2345 TextInvalidateRegion(textPtr, damageRgn);
2348 TkDestroyRegion(damageRgn);
2352 * Clear the REDRAW_PENDING flag here. This is actually pretty
2353 * tricky. We want to wait until *after* doing the scrolling,
2354 * since that could generate more areas to redraw and don't
2355 * want to reschedule a redisplay for them. On the other hand,
2356 * we can't wait until after all the redisplaying, because the
2357 * act of redisplaying could actually generate more redisplays
2358 * (e.g. in the case of a nested window with event bindings triggered
2362 dInfoPtr->flags &= ~REDRAW_PENDING;
2365 * Redraw the borders if that's needed.
2368 if (dInfoPtr->flags & REDRAW_BORDERS) {
2370 Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
2371 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2374 if (textPtr->tkwin == NULL) {
2377 * The widget has been deleted. Don't do anything.
2383 Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2384 textPtr->border, textPtr->highlightWidth,
2385 textPtr->highlightWidth,
2386 Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
2387 Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
2388 textPtr->borderWidth, textPtr->relief);
2389 if (textPtr->highlightWidth != 0) {
2392 bgGC = Tk_GCForColor(textPtr->highlightBgColorPtr,
2393 Tk_WindowId(textPtr->tkwin));
2394 if (textPtr->flags & GOT_FOCUS) {
2395 fgGC = Tk_GCForColor(textPtr->highlightColorPtr,
2396 Tk_WindowId(textPtr->tkwin));
2397 TkpDrawHighlightBorder(textPtr->tkwin, fgGC, bgGC,
2398 textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
2400 TkpDrawHighlightBorder(textPtr->tkwin, bgGC, bgGC,
2401 textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
2404 borders = textPtr->borderWidth + textPtr->highlightWidth;
2405 if (textPtr->padY > 0) {
2406 Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2407 textPtr->border, borders, borders,
2408 Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
2410 Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2411 textPtr->border, borders,
2412 Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
2413 Tk_Width(textPtr->tkwin) - 2*borders,
2414 textPtr->padY, 0, TK_RELIEF_FLAT);
2416 if (textPtr->padX > 0) {
2417 Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2418 textPtr->border, borders, borders + textPtr->padY,
2420 Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
2422 Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2424 Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
2425 borders + textPtr->padY, textPtr->padX,
2426 Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
2429 dInfoPtr->flags &= ~REDRAW_BORDERS;
2433 * Now we have to redraw the lines that couldn't be updated by
2434 * scrolling. First, compute the height of the largest line and
2435 * allocate an off-screen pixmap to use for double-buffered
2440 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
2441 dlPtr = dlPtr->nextPtr) {
2442 if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
2443 maxHeight = dlPtr->height;
2445 bottomY = dlPtr->y + dlPtr->height;
2447 if (maxHeight > dInfoPtr->maxY) {
2448 maxHeight = dInfoPtr->maxY;
2450 if (maxHeight > 0) {
2451 pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
2452 Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
2453 maxHeight, Tk_Depth(textPtr->tkwin));
2454 for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
2455 (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
2456 prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
2457 if (dlPtr->chunkPtr == NULL) continue;
2458 if (dlPtr->oldY != dlPtr->y) {
2460 char string[TK_POS_CHARS];
2461 TkTextPrintIndex(&dlPtr->index, string);
2462 Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
2463 (char *) NULL, string,
2464 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2466 DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
2467 if (dInfoPtr->dLinesInvalidated) {
2468 Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
2471 dlPtr->oldY = dlPtr->y;
2472 dlPtr->flags &= ~NEW_LAYOUT;
2474 /*prevPtr = dlPtr;*/
2476 Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
2480 * See if we need to refresh the part of the window below the
2481 * last line of text (if there is any such area). Refresh the
2482 * padding area on the left too, since the insertion cursor might
2483 * have been displayed there previously).
2486 if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
2487 dInfoPtr->topOfEof = dInfoPtr->maxY;
2489 if (bottomY < dInfoPtr->topOfEof) {
2491 Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
2492 (char *) NULL, "eof",
2493 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2496 if (textPtr->tkwin == NULL) {
2499 * The widget has been deleted. Don't do anything.
2505 Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2506 textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
2507 dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
2508 dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
2510 dInfoPtr->topOfEof = bottomY;
2515 * Update the vertical scrollbar, if there is one. Note: it's
2516 * important to clear REDRAW_PENDING here, just in case the
2517 * scroll procedure does something that requires redisplay.
2520 if (textPtr->flags & UPDATE_SCROLLBARS) {
2521 textPtr->flags &= ~UPDATE_SCROLLBARS;
2522 if (textPtr->yScrollCmd != NULL) {
2523 GetYView(textPtr->interp, textPtr, 1);
2526 if (textPtr->tkwin == NULL) {
2529 * The widget has been deleted. Don't do anything.
2536 * Update the horizontal scrollbar, if any.
2539 if (textPtr->xScrollCmd != NULL) {
2540 GetXView(textPtr->interp, textPtr, 1);
2545 Tcl_Release((ClientData) interp);
2549 *----------------------------------------------------------------------
2551 * TkTextEventuallyRepick --
2553 * This procedure is invoked whenever something happens that
2554 * could change the current character or the tags associated
2561 * A repick is scheduled as an idle handler.
2563 *----------------------------------------------------------------------
2568 TkTextEventuallyRepick(textPtr)
2569 TkText *textPtr; /* Widget record for text widget. */
2571 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2573 dInfoPtr->flags |= REPICK_NEEDED;
2574 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2575 dInfoPtr->flags |= REDRAW_PENDING;
2576 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2581 *----------------------------------------------------------------------
2583 * TkTextRedrawRegion --
2585 * This procedure is invoked to schedule a redisplay for a given
2586 * region of a text widget. The redisplay itself may not occur
2587 * immediately: it's scheduled as a when-idle handler.
2593 * Information will eventually be redrawn on the screen.
2595 *----------------------------------------------------------------------
2600 TkTextRedrawRegion(textPtr, x, y, width, height)
2601 TkText *textPtr; /* Widget record for text widget. */
2602 int x, y; /* Coordinates of upper-left corner of area
2603 * to be redrawn, in pixels relative to
2604 * textPtr's window. */
2605 int width, height; /* Width and height of area to be redrawn. */
2607 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2608 TkRegion damageRgn = TkCreateRegion();
2614 rect.height = height;
2615 TkUnionRectWithRegion(&rect, damageRgn, damageRgn);
2617 TextInvalidateRegion(textPtr, damageRgn);
2619 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2620 dInfoPtr->flags |= REDRAW_PENDING;
2621 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2623 TkDestroyRegion(damageRgn);
2627 *----------------------------------------------------------------------
2629 * TextInvalidateRegion --
2631 * Mark a region of text as invalid.
2637 * Updates the display information for the text widget.
2639 *----------------------------------------------------------------------
2643 TextInvalidateRegion(textPtr, region)
2644 TkText *textPtr; /* Widget record for text widget. */
2645 TkRegion region; /* Region of area to redraw. */
2647 register DLine *dlPtr;
2648 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2653 * Find all lines that overlap the given region and mark them for
2657 TkClipBox(region, &rect);
2658 maxY = rect.y + rect.height;
2659 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
2660 dlPtr = dlPtr->nextPtr) {
2661 if ((dlPtr->oldY != -1) && (TkRectInRegion(region, rect.x, dlPtr->y,
2662 rect.width, (unsigned int) dlPtr->height) != RectangleOut)) {
2666 if (dInfoPtr->topOfEof < maxY) {
2667 dInfoPtr->topOfEof = maxY;
2671 * Schedule the redisplay operation if there isn't one already
2675 inset = textPtr->borderWidth + textPtr->highlightWidth;
2676 if ((rect.x < (inset + textPtr->padX))
2677 || (rect.y < (inset + textPtr->padY))
2678 || ((int) (rect.x + rect.width) > (Tk_Width(textPtr->tkwin)
2679 - inset - textPtr->padX))
2680 || (maxY > (Tk_Height(textPtr->tkwin) - inset - textPtr->padY))) {
2681 dInfoPtr->flags |= REDRAW_BORDERS;
2686 *----------------------------------------------------------------------
2690 * This procedure is invoked when info in a text widget is about
2691 * to be modified in a way that changes how it is displayed (e.g.
2692 * characters were inserted or deleted, or tag information was
2693 * changed). This procedure must be called *before* a change is
2694 * made, so that indexes in the display information are still
2701 * The range of character between index1Ptr (inclusive) and
2702 * index2Ptr (exclusive) will be redisplayed at some point in the
2703 * future (the actual redisplay is scheduled as a when-idle handler).
2705 *----------------------------------------------------------------------
2709 TkTextChanged(textPtr, index1Ptr, index2Ptr)
2710 TkText *textPtr; /* Widget record for text widget. */
2711 TkTextIndex *index1Ptr; /* Index of first character to redisplay. */
2712 TkTextIndex *index2Ptr; /* Index of character just after last one
2715 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2716 DLine *firstPtr, *lastPtr;
2717 TkTextIndex rounded;
2720 * Schedule both a redisplay and a recomputation of display information.
2721 * It's done here rather than the end of the procedure for two reasons:
2723 * 1. If there are no display lines to update we'll want to return
2724 * immediately, well before the end of the procedure.
2725 * 2. It's important to arrange for the redisplay BEFORE calling
2726 * FreeDLines. The reason for this is subtle and has to do with
2727 * embedded windows. The chunk delete procedure for an embedded
2728 * window will schedule an idle handler to unmap the window.
2729 * However, we want the idle handler for redisplay to be called
2730 * first, so that it can put the embedded window back on the screen
2731 * again (if appropriate). This will prevent the window from ever
2732 * being unmapped, and thereby avoid flashing.
2735 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2736 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2738 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2741 * Find the DLines corresponding to index1Ptr and index2Ptr. There
2742 * is one tricky thing here, which is that we have to relayout in
2743 * units of whole text lines: round index1Ptr back to the beginning
2744 * of its text line, and include all the display lines after index2,
2745 * up to the end of its text line. This is necessary because the
2746 * indices stored in the display lines will no longer be valid. It's
2747 * also needed because any edit could change the way lines wrap.
2750 rounded = *index1Ptr;
2751 rounded.byteIndex = 0;
2752 firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
2753 if (firstPtr == NULL) {
2756 lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
2757 while ((lastPtr != NULL)
2758 && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
2759 lastPtr = lastPtr->nextPtr;
2763 * Delete all the DLines from firstPtr up to but not including lastPtr.
2766 FreeDLines(textPtr, firstPtr, lastPtr, 1);
2770 *----------------------------------------------------------------------
2772 * TkTextRedrawTag --
2774 * This procedure is invoked to request a redraw of all characters
2775 * in a given range that have a particular tag on or off. It's
2776 * called, for example, when tag options change.
2782 * Information on the screen may be redrawn, and the layout of
2783 * the screen may change.
2785 *----------------------------------------------------------------------
2789 TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
2790 TkText *textPtr; /* Widget record for text widget. */
2791 TkTextIndex *index1Ptr; /* First character in range to consider
2792 * for redisplay. NULL means start at
2793 * beginning of text. */
2794 TkTextIndex *index2Ptr; /* Character just after last one to consider
2795 * for redisplay. NULL means process all
2796 * the characters in the text. */
2797 TkTextTag *tagPtr; /* Information about tag. */
2798 int withTag; /* 1 means redraw characters that have the
2799 * tag, 0 means redraw those without. */
2801 register DLine *dlPtr;
2804 TkTextSearch search;
2805 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2806 TkTextIndex *curIndexPtr;
2807 TkTextIndex endOfText, *endIndexPtr;
2810 * Round up the starting position if it's before the first line
2811 * visible on the screen (we only care about what's on the screen).
2814 dlPtr = dInfoPtr->dLinePtr;
2815 if (dlPtr == NULL) {
2818 if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
2819 index1Ptr = &dlPtr->index;
2823 * Set the stopping position if it wasn't specified.
2826 if (index2Ptr == NULL) {
2827 index2Ptr = TkTextMakeByteIndex(textPtr->tree,
2828 TkBTreeNumLines(textPtr->tree), 0, &endOfText);
2832 * Initialize a search through all transitions on the tag, starting
2833 * with the first transition where the tag's current state is different
2834 * from what it will eventually be.
2837 TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
2839 * Make our own curIndex because at this point search.curIndex
2840 * may not equal index1Ptr->curIndex in the case the first tag toggle
2841 * comes after index1Ptr (See the use of FindTagStart in TkBTreeStartSearch)
2843 curIndexPtr = index1Ptr;
2844 tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
2845 if (tagOn != withTag) {
2846 if (!TkBTreeNextTag(&search)) {
2849 curIndexPtr = &search.curIndex;
2853 * Schedule a redisplay and layout recalculation if they aren't
2854 * already pending. This has to be done before calling FreeDLines,
2855 * for the reason given in TkTextChanged.
2858 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2859 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2861 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2864 * Each loop through the loop below is for one range of characters
2865 * where the tag's current state is different than its eventual
2866 * state. At the top of the loop, search contains information about
2867 * the first character in the range.
2872 * Find the first DLine structure in the range. Note: if the
2873 * desired character isn't the first in its text line, then look
2874 * for the character just before it instead. This is needed to
2875 * handle the case where the first character of a wrapped
2876 * display line just got smaller, so that it now fits on the
2877 * line before: need to relayout the line containing the
2878 * previous character.
2881 if (curIndexPtr->byteIndex == 0) {
2882 dlPtr = FindDLine(dlPtr, curIndexPtr);
2888 dlPtr = FindDLine(dlPtr, &tmp);
2890 if (dlPtr == NULL) {
2895 * Find the first DLine structure that's past the end of the range.
2898 if (!TkBTreeNextTag(&search)) {
2899 endIndexPtr = index2Ptr;
2901 curIndexPtr = &search.curIndex;
2902 endIndexPtr = curIndexPtr;
2904 endPtr = FindDLine(dlPtr, endIndexPtr);
2905 if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
2906 && (endPtr->index.byteIndex < endIndexPtr->byteIndex)) {
2907 endPtr = endPtr->nextPtr;
2911 * Delete all of the display lines in the range, so that they'll
2912 * be re-layed out and redrawn.
2915 FreeDLines(textPtr, dlPtr, endPtr, 1);
2919 * Find the first text line in the next range.
2922 if (!TkBTreeNextTag(&search)) {
2929 *----------------------------------------------------------------------
2931 * TkTextRelayoutWindow --
2933 * This procedure is called when something has happened that
2934 * invalidates the whole layout of characters on the screen, such
2935 * as a change in a configuration option for the overall text
2936 * widget or a change in the window size. It causes all display
2937 * information to be recomputed and the window to be redrawn.
2943 * All the display information will be recomputed for the window
2944 * and the window will be redrawn.
2946 *----------------------------------------------------------------------
2950 TkTextRelayoutWindow(textPtr)
2951 TkText *textPtr; /* Widget record for text widget. */
2953 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2958 * Schedule the window redisplay. See TkTextChanged for the
2959 * reason why this has to be done before any calls to FreeDLines.
2962 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2963 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2965 dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
2969 * (Re-)create the graphics context for drawing the traversal
2973 gcValues.graphics_exposures = False;
2974 new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
2975 if (dInfoPtr->copyGC != None) {
2976 Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
2978 dInfoPtr->copyGC = new;
2981 * Throw away all the current layout information.
2984 FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
2985 dInfoPtr->dLinePtr = NULL;
2988 * Recompute some overall things for the layout. Even if the
2989 * window gets very small, pretend that there's at least one
2990 * pixel of drawing space in it.
2993 if (textPtr->highlightWidth < 0) {
2994 textPtr->highlightWidth = 0;
2996 dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
2998 dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
3000 dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
3001 - textPtr->borderWidth - textPtr->padX;
3002 if (dInfoPtr->maxX <= dInfoPtr->x) {
3003 dInfoPtr->maxX = dInfoPtr->x + 1;
3005 dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
3006 - textPtr->borderWidth - textPtr->padY;
3007 if (dInfoPtr->maxY <= dInfoPtr->y) {
3008 dInfoPtr->maxY = dInfoPtr->y + 1;
3010 dInfoPtr->topOfEof = dInfoPtr->maxY;
3013 * If the upper-left character isn't the first in a line, recompute
3014 * it. This is necessary because a change in the window's size
3015 * or options could change the way lines wrap.
3018 if (textPtr->topIndex.byteIndex != 0) {
3019 MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
3023 * Invalidate cached scrollbar positions, so that scrollbars
3024 * sliders will be udpated.
3027 dInfoPtr->xScrollFirst = dInfoPtr->xScrollLast = -1;
3028 dInfoPtr->yScrollFirst = dInfoPtr->yScrollLast = -1;
3032 *----------------------------------------------------------------------
3036 * This procedure is called to specify what lines are to be
3037 * displayed in a text widget.
3043 * The display will (eventually) be updated so that the position
3044 * given by "indexPtr" is visible on the screen at the position
3045 * determined by "pickPlace".
3047 *----------------------------------------------------------------------
3051 TkTextSetYView(textPtr, indexPtr, pickPlace)
3052 TkText *textPtr; /* Widget record for text widget. */
3053 TkTextIndex *indexPtr; /* Position that is to appear somewhere
3055 int pickPlace; /* 0 means topLine must appear at top of
3056 * screen. 1 means we get to pick where it
3057 * appears: minimize screen motion or else
3058 * display line at center of screen. */
3060 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3061 register DLine *dlPtr;
3062 int bottomY, close, lineIndex;
3063 TkTextIndex tmpIndex, rounded;
3067 * If the specified position is the extra line at the end of the
3068 * text, round it back to the last real line.
3071 lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
3072 if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
3073 TkTextIndexBackChars(indexPtr, 1, &rounded);
3074 indexPtr = &rounded;
3079 * The specified position must go at the top of the screen.
3080 * Just leave all the DLine's alone: we may be able to reuse
3081 * some of the information that's currently on the screen
3082 * without redisplaying it all.
3085 if (indexPtr->byteIndex == 0) {
3086 textPtr->topIndex = *indexPtr;
3088 MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
3090 goto scheduleUpdate;
3094 * We have to pick where to display the index. First, bring
3095 * the display information up to date and see if the index will be
3096 * completely visible in the current screen configuration. If so
3097 * then there's nothing to do.
3100 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3101 UpdateDisplayInfo(textPtr);
3103 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
3104 if (dlPtr != NULL) {
3105 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
3107 * Part of the line hangs off the bottom of the screen;
3108 * pretend the whole line is off-screen.
3112 } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
3113 && (dlPtr->index.byteIndex <= indexPtr->byteIndex)) {
3119 * The desired line isn't already on-screen. Figure out what
3120 * it means to be "close" to the top or bottom of the screen.
3121 * Close means within 1/3 of the screen height or within three
3122 * lines, whichever is greater. Add one extra line also, to
3123 * account for the way MeasureUp rounds.
3126 Tk_GetFontMetrics(textPtr->tkfont, &fm);
3127 bottomY = (dInfoPtr->y + dInfoPtr->maxY + fm.linespace)/2;
3128 close = (dInfoPtr->maxY - dInfoPtr->y)/3;
3129 if (close < 3*fm.linespace) {
3130 close = 3*fm.linespace;
3132 close += fm.linespace;
3133 if (dlPtr != NULL) {
3135 * The desired line is above the top of screen. If it is
3136 * "close" to the top of the window then make it the top
3137 * line on the screen.
3140 MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
3141 if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
3142 MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
3143 goto scheduleUpdate;
3147 * The desired line is below the bottom of the screen. If it is
3148 * "close" to the bottom of the screen then position it at the
3149 * bottom of the screen.
3152 MeasureUp(textPtr, indexPtr, close, &tmpIndex);
3153 if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
3154 bottomY = dInfoPtr->maxY - dInfoPtr->y;
3159 * Our job now is to arrange the display so that indexPtr appears
3160 * as low on the screen as possible but with its bottom no lower
3161 * than bottomY. BottomY is the bottom of the window if the
3162 * desired line is just below the current screen, otherwise it
3163 * is a half-line lower than the center of the window.
3166 MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
3169 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3170 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3172 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3176 *--------------------------------------------------------------
3180 * Given one index, find the index of the first character
3181 * on the highest display line that would be displayed no more
3182 * than "distance" pixels above the given index.
3185 * *dstPtr is filled in with the index of the first character
3186 * on a display line. The display line is found by measuring
3187 * up "distance" pixels above the pixel just below an imaginary
3188 * display line that contains srcPtr. If the display line
3189 * that covers this coordinate actually extends above the
3190 * coordinate, then return the index of the next lower line
3191 * instead (i.e. the returned index will be completely visible
3192 * at or below the given y-coordinate).
3197 *--------------------------------------------------------------
3201 MeasureUp(textPtr, srcPtr, distance, dstPtr)
3202 TkText *textPtr; /* Text widget in which to measure. */
3203 TkTextIndex *srcPtr; /* Index of character from which to start
3205 int distance; /* Vertical distance in pixels measured
3206 * from the pixel just below the lowest
3207 * one in srcPtr's line. */
3208 TkTextIndex *dstPtr; /* Index to fill in with result. */
3210 int lineNum; /* Number of current line. */
3211 int bytesToCount; /* Maximum number of bytes to measure in
3213 TkTextIndex bestIndex; /* Best candidate seen so far for result. */
3215 DLine *dlPtr, *lowestPtr;
3216 int noBestYet; /* 1 means bestIndex hasn't been set. */
3219 bytesToCount = srcPtr->byteIndex + 1;
3220 index.tree = srcPtr->tree;
3221 for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
3224 * Layout an entire text line (potentially > 1 display line).
3225 * For the first line, which contains srcPtr, only layout the
3226 * part up through srcPtr (bytesToCount is non-infinite to
3227 * accomplish this). Make a list of all the display lines
3228 * in backwards order (the lowest DLine on the screen is first
3232 index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
3233 index.byteIndex = 0;
3236 dlPtr = LayoutDLine(textPtr, &index);
3237 dlPtr->nextPtr = lowestPtr;
3239 TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
3240 bytesToCount -= dlPtr->byteCount;
3241 } while ((bytesToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
3244 * Scan through the display lines to see if we've covered enough
3245 * vertical distance. If so, save the starting index for the
3246 * line at the desired location.
3249 for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
3250 distance -= dlPtr->height;
3252 *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
3255 bestIndex = dlPtr->index;
3260 * Discard the display lines, then either return or prepare
3261 * for the next display line to lay out.
3264 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
3268 bytesToCount = INT_MAX; /* Consider all chars. in next line. */
3272 * Ran off the beginning of the text. Return the first character
3276 TkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
3280 *--------------------------------------------------------------
3284 * This procedure is invoked to process the "see" option for
3285 * the widget command for text widgets. See the user documentation
3286 * for details on what it does.
3289 * A standard Tcl result.
3292 * See the user documentation.
3294 *--------------------------------------------------------------
3298 TkTextSeeCmd(textPtr, interp, argc, argv)
3299 TkText *textPtr; /* Information about text widget. */
3300 Tcl_Interp *interp; /* Current interpreter. */
3301 int argc; /* Number of arguments. */
3302 CONST char **argv; /* Argument strings. Someone else has already
3303 * parsed this command enough to know that
3304 * argv[1] is "see". */
3306 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3308 int x, y, width, height, lineWidth, byteCount, oneThird, delta;
3310 TkTextDispChunk *chunkPtr;
3313 Tcl_AppendResult(interp, "wrong # args: should be \"",
3314 argv[0], " see index\"", (char *) NULL);
3317 if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
3322 * If the specified position is the extra line at the end of the
3323 * text, round it back to the last real line.
3326 if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
3327 TkTextIndexBackChars(&index, 1, &index);
3331 * First get the desired position into the vertical range of the window.
3334 TkTextSetYView(textPtr, &index, 1);
3337 * Now make sure that the character is in view horizontally.
3340 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3341 UpdateDisplayInfo(textPtr);
3343 lineWidth = dInfoPtr->maxX - dInfoPtr->x;
3344 if (dInfoPtr->maxLength < lineWidth) {
3349 * Find the chunk that contains the desired index.
3352 dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
3353 byteCount = index.byteIndex - dlPtr->index.byteIndex;
3354 for (chunkPtr = dlPtr->chunkPtr; chunkPtr!=NULL ; chunkPtr = chunkPtr->nextPtr) {
3355 if (byteCount < chunkPtr->numBytes) {
3358 byteCount -= chunkPtr->numBytes;
3362 * Call a chunk-specific procedure to find the horizontal range of
3363 * the character within the chunk.
3366 if (chunkPtr!=NULL) { /* chunkPtr==NULL iff trying to see in elided region */
3367 (*chunkPtr->bboxProc)(chunkPtr, byteCount, dlPtr->y + dlPtr->spaceAbove,
3368 dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
3369 dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
3371 delta = x - dInfoPtr->curPixelOffset;
3372 oneThird = lineWidth/3;
3374 if (delta < -oneThird) {
3375 dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
3377 dInfoPtr->newByteOffset -= ((-delta) + textPtr->charWidth - 1)
3378 / textPtr->charWidth;
3381 delta -= (lineWidth - width);
3383 if (delta > oneThird) {
3384 dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
3386 dInfoPtr->newByteOffset += (delta + textPtr->charWidth - 1)
3387 / textPtr->charWidth;
3393 dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3394 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3395 dInfoPtr->flags |= REDRAW_PENDING;
3396 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3402 *--------------------------------------------------------------
3406 * This procedure is invoked to process the "xview" option for
3407 * the widget command for text widgets. See the user documentation
3408 * for details on what it does.
3411 * A standard Tcl result.
3414 * See the user documentation.
3416 *--------------------------------------------------------------
3420 TkTextXviewCmd(textPtr, interp, argc, argv)
3421 TkText *textPtr; /* Information about text widget. */
3422 Tcl_Interp *interp; /* Current interpreter. */
3423 int argc; /* Number of arguments. */
3424 CONST char **argv; /* Argument strings. Someone else has already
3425 * parsed this command enough to know that
3426 * argv[1] is "xview". */
3428 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3429 int type, charsPerPage, count, newOffset;
3432 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3433 UpdateDisplayInfo(textPtr);
3437 GetXView(interp, textPtr, 0);
3441 newOffset = dInfoPtr->newByteOffset;
3442 type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
3444 case TK_SCROLL_ERROR:
3446 case TK_SCROLL_MOVETO:
3447 if (fraction > 1.0) {
3453 newOffset = (int) (((fraction * dInfoPtr->maxLength) / textPtr->charWidth)
3456 case TK_SCROLL_PAGES:
3457 charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
3459 if (charsPerPage < 1) {
3462 newOffset += charsPerPage * count;
3464 case TK_SCROLL_UNITS:
3469 dInfoPtr->newByteOffset = newOffset;
3470 dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3471 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3472 dInfoPtr->flags |= REDRAW_PENDING;
3473 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3479 *----------------------------------------------------------------------
3483 * This procedure is called to scroll a text widget up or down
3484 * by a given number of lines.
3490 * The view in textPtr's window changes to reflect the value
3493 *----------------------------------------------------------------------
3497 ScrollByLines(textPtr, offset)
3498 TkText *textPtr; /* Widget to scroll. */
3499 int offset; /* Amount by which to scroll, in *screen*
3500 * lines. Positive means that information
3501 * later in text becomes visible, negative
3502 * means that information earlier in the
3503 * text becomes visible. */
3505 int i, bytesToCount, lineNum;
3506 TkTextIndex new, index;
3507 TkTextLine *lastLinePtr;
3508 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3509 DLine *dlPtr, *lowestPtr;
3513 * Must scroll up (to show earlier information in the text).
3514 * The code below is similar to that in MeasureUp, except that
3515 * it counts lines instead of pixels.
3518 bytesToCount = textPtr->topIndex.byteIndex + 1;
3519 index.tree = textPtr->tree;
3520 offset--; /* Skip line containing topIndex. */
3521 for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
3522 lineNum >= 0; lineNum--) {
3523 index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
3524 index.byteIndex = 0;
3527 dlPtr = LayoutDLine(textPtr, &index);
3528 dlPtr->nextPtr = lowestPtr;
3530 TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
3531 bytesToCount -= dlPtr->byteCount;
3532 } while ((bytesToCount > 0)
3533 && (index.linePtr == dlPtr->index.linePtr));
3535 for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
3538 textPtr->topIndex = dlPtr->index;
3544 * Discard the display lines, then either return or prepare
3545 * for the next display line to lay out.
3548 FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
3550 goto scheduleUpdate;
3552 bytesToCount = INT_MAX;
3556 * Ran off the beginning of the text. Return the first character
3560 TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
3563 * Scrolling down, to show later information in the text.
3564 * Just count lines from the current top of the window.
3567 lastLinePtr = TkBTreeFindLine(textPtr->tree,
3568 TkBTreeNumLines(textPtr->tree));
3569 for (i = 0; i < offset; i++) {
3570 dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
3571 if (dlPtr->length == 0 && dlPtr->height == 0) offset++;
3572 dlPtr->nextPtr = NULL;
3573 TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
3574 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
3575 if (new.linePtr == lastLinePtr) {
3578 textPtr->topIndex = new;
3583 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3584 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3586 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3590 *--------------------------------------------------------------
3594 * This procedure is invoked to process the "yview" option for
3595 * the widget command for text widgets. See the user documentation
3596 * for details on what it does.
3599 * A standard Tcl result.
3602 * See the user documentation.
3604 *--------------------------------------------------------------
3608 TkTextYviewCmd(textPtr, interp, argc, argv)
3609 TkText *textPtr; /* Information about text widget. */
3610 Tcl_Interp *interp; /* Current interpreter. */
3611 int argc; /* Number of arguments. */
3612 CONST char **argv; /* Argument strings. Someone else has already
3613 * parsed this command enough to know that
3614 * argv[1] is "yview". */
3616 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3617 int pickPlace, lineNum, type, bytesInLine;
3620 size_t switchLength;
3622 TkTextIndex index, new;
3623 TkTextLine *lastLinePtr;
3626 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3627 UpdateDisplayInfo(textPtr);
3631 GetYView(interp, textPtr, 0);
3636 * Next, handle the old syntax: "pathName yview ?-pickplace? where"
3640 if (argv[2][0] == '-') {
3641 switchLength = strlen(argv[2]);
3642 if ((switchLength >= 2)
3643 && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
3646 Tcl_AppendResult(interp, "wrong # args: should be \"",
3647 argv[0], " yview -pickplace lineNum|index\"",
3653 if ((argc == 3) || pickPlace) {
3654 if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
3655 TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
3656 TkTextSetYView(textPtr, &index, 0);
3661 * The argument must be a regular text index.
3664 Tcl_ResetResult(interp);
3665 if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
3666 &index) != TCL_OK) {
3669 TkTextSetYView(textPtr, &index, pickPlace);
3674 * New syntax: dispatch based on argv[2].
3677 type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
3679 case TK_SCROLL_ERROR:
3681 case TK_SCROLL_MOVETO:
3682 if (fraction > 1.0) {
3688 fraction *= TkBTreeNumLines(textPtr->tree);
3689 lineNum = (int) fraction;
3690 TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
3691 bytesInLine = TkBTreeBytesInLine(index.linePtr);
3692 index.byteIndex = (int)((bytesInLine * (fraction-lineNum)) + 0.5);
3693 if (index.byteIndex >= bytesInLine) {
3694 TkTextMakeByteIndex(textPtr->tree, lineNum + 1, 0, &index);
3696 TkTextSetYView(textPtr, &index, 0);
3698 case TK_SCROLL_PAGES:
3700 * Scroll up or down by screenfuls. Actually, use the
3701 * window height minus two lines, so that there's some
3702 * overlap between adjacent pages.
3705 Tk_GetFontMetrics(textPtr->tkfont, &fm);
3707 pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*(-count)
3709 MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
3710 if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
3712 * A page of scrolling ended up being less than one line.
3713 * Scroll one line anyway.
3719 textPtr->topIndex = new;
3722 * Scrolling down by pages. Layout lines starting at the
3723 * top index and count through the desired vertical distance.
3726 pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*count;
3727 lastLinePtr = TkBTreeFindLine(textPtr->tree,
3728 TkBTreeNumLines(textPtr->tree));
3730 dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
3731 dlPtr->nextPtr = NULL;
3732 TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount,
3734 pixels -= dlPtr->height;
3735 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
3736 if (new.linePtr == lastLinePtr) {
3739 textPtr->topIndex = new;
3740 } while (pixels > 0);
3742 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3743 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3745 dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3747 case TK_SCROLL_UNITS:
3749 ScrollByLines(textPtr, count);
3756 *--------------------------------------------------------------
3760 * This procedure is invoked to process the "scan" option for
3761 * the widget command for text widgets. See the user documentation
3762 * for details on what it does.
3765 * A standard Tcl result.
3768 * See the user documentation.
3770 *--------------------------------------------------------------
3774 TkTextScanCmd(textPtr, interp, argc, argv)
3775 register TkText *textPtr; /* Information about text widget. */
3776 Tcl_Interp *interp; /* Current interpreter. */
3777 int argc; /* Number of arguments. */
3778 CONST char **argv; /* Argument strings. Someone else has already
3779 * parsed this command enough to know that
3780 * argv[1] is "scan". */
3782 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3784 int c, x, y, totalScroll, newByte, maxByte, gain=10;
3788 if ((argc != 5) && (argc != 6)) {
3789 Tcl_AppendResult(interp, "wrong # args: should be \"",
3790 argv[0], " scan mark x y\" or \"",
3791 argv[0], " scan dragto x y ?gain?\"", (char *) NULL);
3794 if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
3797 if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
3800 if ((argc == 6) && (Tcl_GetInt(interp, argv[5], &gain) != TCL_OK))
3803 length = strlen(argv[2]);
3804 if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
3806 * Amplify the difference between the current position and the
3807 * mark position to compute how much the view should shift, then
3808 * update the mark position to correspond to the new view. If we
3809 * run off the edge of the text, reset the mark point so that the
3810 * current position continues to correspond to the edge of the
3811 * window. This means that the picture will start dragging as
3812 * soon as the mouse reverses direction (without this reset, might
3813 * have to slide mouse a long ways back before the picture starts
3817 newByte = dInfoPtr->scanMarkIndex + (gain*(dInfoPtr->scanMarkX - x))
3818 / (textPtr->charWidth);
3819 maxByte = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
3820 + textPtr->charWidth - 1)/textPtr->charWidth;
3823 dInfoPtr->scanMarkIndex = 0;
3824 dInfoPtr->scanMarkX = x;
3825 } else if (newByte > maxByte) {
3827 dInfoPtr->scanMarkIndex = maxByte;
3828 dInfoPtr->scanMarkX = x;
3830 dInfoPtr->newByteOffset = newByte;
3832 Tk_GetFontMetrics(textPtr->tkfont, &fm);
3833 totalScroll = (gain*(dInfoPtr->scanMarkY - y)) / fm.linespace;
3834 if (totalScroll != dInfoPtr->scanTotalScroll) {
3835 index = textPtr->topIndex;
3836 ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
3837 dInfoPtr->scanTotalScroll = totalScroll;
3838 if ((index.linePtr == textPtr->topIndex.linePtr) &&
3839 (index.byteIndex == textPtr->topIndex.byteIndex)) {
3840 dInfoPtr->scanTotalScroll = 0;
3841 dInfoPtr->scanMarkY = y;
3844 } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
3845 dInfoPtr->scanMarkIndex = dInfoPtr->newByteOffset;
3846 dInfoPtr->scanMarkX = x;
3847 dInfoPtr->scanTotalScroll = 0;
3848 dInfoPtr->scanMarkY = y;
3850 Tcl_AppendResult(interp, "bad scan option \"", argv[2],
3851 "\": must be mark or dragto", (char *) NULL);
3854 dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3855 if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3856 dInfoPtr->flags |= REDRAW_PENDING;
3857 Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3863 *----------------------------------------------------------------------
3867 * This procedure computes the fractions that indicate what's
3868 * visible in a text window and, optionally, evaluates a
3869 * Tcl script to report them to the text's associated scrollbar.
3872 * If report is zero, then the interp's result is filled in with
3873 * two real numbers separated by a space, giving the position of
3874 * the left and right edges of the window as fractions from 0 to
3875 * 1, where 0 means the left edge of the text and 1 means the right
3876 * edge. If report is non-zero, then the interp's result isn't modified
3877 * directly, but instead a script is evaluated in interp to report
3878 * the new horizontal scroll position to the scrollbar (if the scroll
3879 * position hasn't changed then no script is invoked).
3884 *----------------------------------------------------------------------
3888 GetXView(interp, textPtr, report)
3889 Tcl_Interp *interp; /* If "report" is FALSE, string
3890 * describing visible range gets
3891 * stored in the interp's result. */
3892 TkText *textPtr; /* Information about text widget. */
3893 int report; /* Non-zero means report info to
3894 * scrollbar if it has changed. */
3896 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3897 char buffer[TCL_DOUBLE_SPACE * 2 + 1];
3901 if (dInfoPtr->maxLength > 0) {
3902 first = ((double) dInfoPtr->curPixelOffset)
3903 / dInfoPtr->maxLength;
3904 last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
3905 / dInfoPtr->maxLength;
3914 sprintf(buffer, "%g %g", first, last);
3915 Tcl_SetResult(interp, buffer, TCL_VOLATILE);
3918 if (FP_EQUAL_SCALE(first, dInfoPtr->xScrollFirst, dInfoPtr->maxLength) &&
3919 FP_EQUAL_SCALE(last, dInfoPtr->xScrollLast, dInfoPtr->maxLength)) {
3922 dInfoPtr->xScrollFirst = first;
3923 dInfoPtr->xScrollLast = last;
3924 sprintf(buffer, " %g %g", first, last);
3925 code = Tcl_VarEval(interp, textPtr->xScrollCmd,
3926 buffer, (char *) NULL);
3927 if (code != TCL_OK) {
3928 Tcl_AddErrorInfo(interp,
3929 "\n (horizontal scrolling command executed by text)");
3930 Tcl_BackgroundError(interp);
3935 *----------------------------------------------------------------------
3939 * This procedure computes the fractions that indicate what's
3940 * visible in a text window and, optionally, evaluates a
3941 * Tcl script to report them to the text's associated scrollbar.
3944 * If report is zero, then the interp's result is filled in with
3945 * two real numbers separated by a space, giving the position of
3946 * the top and bottom of the window as fractions from 0 to 1, where
3947 * 0 means the beginning of the text and 1 means the end. If
3948 * report is non-zero, then the interp's result isn't modified directly,
3949 * but a script is evaluated in interp to report the new scroll
3950 * position to the scrollbar (if the scroll position hasn't changed
3951 * then no script is invoked).
3956 *----------------------------------------------------------------------
3960 GetYView(interp, textPtr, report)
3961 Tcl_Interp *interp; /* If "report" is FALSE, string
3962 * describing visible range gets
3963 * stored in the interp's result. */
3964 TkText *textPtr; /* Information about text widget. */
3965 int report; /* Non-zero means report info to
3966 * scrollbar if it has changed. */
3968 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3969 char buffer[TCL_DOUBLE_SPACE * 2 + 1];
3972 int totalLines, code, count;
3974 dlPtr = dInfoPtr->dLinePtr;
3975 totalLines = TkBTreeNumLines(textPtr->tree);
3976 first = (double) TkBTreeLineIndex(dlPtr->index.linePtr)
3977 + (double) dlPtr->index.byteIndex
3978 / TkBTreeBytesInLine(dlPtr->index.linePtr);
3979 first /= totalLines;
3981 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
3983 * The last line is only partially visible, so don't
3984 * count its characters in what's visible.
3989 if (dlPtr->nextPtr == NULL) {
3990 count = dlPtr->byteCount;
3993 dlPtr = dlPtr->nextPtr;
3995 last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
3996 + ((double) (dlPtr->index.byteIndex + count))
3997 / (TkBTreeBytesInLine(dlPtr->index.linePtr));
4000 sprintf(buffer, "%g %g", first, last);
4001 Tcl_SetResult(interp, buffer, TCL_VOLATILE);
4004 if (FP_EQUAL_SCALE(first, dInfoPtr->yScrollFirst, totalLines) &&
4005 FP_EQUAL_SCALE(last, dInfoPtr->yScrollLast, totalLines)) {
4008 dInfoPtr->yScrollFirst = first;
4009 dInfoPtr->yScrollLast = last;
4010 sprintf(buffer, " %g %g", first, last);
4011 code = Tcl_VarEval(interp, textPtr->yScrollCmd, buffer, (char *) NULL);
4012 if (code != TCL_OK) {
4013 Tcl_AddErrorInfo(interp,
4014 "\n (vertical scrolling command executed by text)");
4015 Tcl_BackgroundError(interp);
4020 *----------------------------------------------------------------------
4024 * This procedure is called to find the DLine corresponding to a
4028 * The return value is a pointer to the first DLine found in the
4029 * list headed by dlPtr that displays information at or after the
4030 * specified position. If there is no such line in the list then
4036 *----------------------------------------------------------------------
4040 FindDLine(dlPtr, indexPtr)
4041 register DLine *dlPtr; /* Pointer to first in list of DLines
4043 TkTextIndex *indexPtr; /* Index of desired character. */
4045 TkTextLine *linePtr;
4047 if (dlPtr == NULL) {
4050 if (TkBTreeLineIndex(indexPtr->linePtr)
4051 < TkBTreeLineIndex(dlPtr->index.linePtr)) {
4053 * The first display line is already past the desired line.
4059 * Find the first display line that covers the desired text line.
4062 linePtr = dlPtr->index.linePtr;
4063 while (linePtr != indexPtr->linePtr) {
4064 while (dlPtr->index.linePtr == linePtr) {
4065 dlPtr = dlPtr->nextPtr;
4066 if (dlPtr == NULL) {
4070 linePtr = TkBTreeNextLine(linePtr);
4071 if (linePtr == NULL) {
4072 panic("FindDLine reached end of text");
4075 if (indexPtr->linePtr != dlPtr->index.linePtr) {
4080 * Now get to the right position within the text line.
4083 while (indexPtr->byteIndex >= (dlPtr->index.byteIndex + dlPtr->byteCount)) {
4084 dlPtr = dlPtr->nextPtr;
4085 if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
4093 *----------------------------------------------------------------------
4095 * TkTextPixelIndex --
4097 * Given an (x,y) coordinate on the screen, find the location of
4098 * the character closest to that location.
4101 * The index at *indexPtr is modified to refer to the character
4102 * on the display that is closest to (x,y).
4107 *----------------------------------------------------------------------
4111 TkTextPixelIndex(textPtr, x, y, indexPtr)
4112 TkText *textPtr; /* Widget record for text widget. */
4113 int x, y; /* Pixel coordinates of point in widget's
4115 TkTextIndex *indexPtr; /* This index gets filled in with the
4116 * index of the character nearest to (x,y). */
4118 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
4119 register DLine *dlPtr, *validdlPtr;
4120 register TkTextDispChunk *chunkPtr;
4123 * Make sure that all of the layout information about what's
4124 * displayed where on the screen is up-to-date.
4127 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
4128 UpdateDisplayInfo(textPtr);
4132 * If the coordinates are above the top of the window, then adjust
4133 * them to refer to the upper-right corner of the window. If they're
4134 * off to one side or the other, then adjust to the closest side.
4137 if (y < dInfoPtr->y) {
4141 if (x >= dInfoPtr->maxX) {
4142 x = dInfoPtr->maxX - 1;
4144 if (x < dInfoPtr->x) {
4149 * Find the display line containing the desired y-coordinate.
4152 for (dlPtr = validdlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
4153 dlPtr = dlPtr->nextPtr) {
4154 if (dlPtr->chunkPtr !=NULL) validdlPtr = dlPtr;
4155 if (dlPtr->nextPtr == NULL) {
4157 * Y-coordinate is off the bottom of the displayed text.
4158 * Use the last character on the last line.
4161 x = dInfoPtr->maxX - 1;
4165 if (dlPtr->chunkPtr == NULL) dlPtr = validdlPtr;
4169 * Scan through the line's chunks to find the one that contains
4170 * the desired x-coordinate. Before doing this, translate the
4171 * x-coordinate from the coordinate system of the window to the
4172 * coordinate system of the line (to take account of x-scrolling).
4175 *indexPtr = dlPtr->index;
4176 x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
4177 for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
4178 indexPtr->byteIndex += chunkPtr->numBytes,
4179 chunkPtr = chunkPtr->nextPtr) {
4180 if (chunkPtr->nextPtr == NULL) {
4181 indexPtr->byteIndex += chunkPtr->numBytes;
4182 TkTextIndexBackChars(indexPtr, 1, indexPtr);
4188 * If the chunk has more than one byte in it, ask it which
4189 * character is at the desired location.
4192 if (chunkPtr->numBytes > 1) {
4193 indexPtr->byteIndex += (*chunkPtr->measureProc)(chunkPtr, x);
4198 *----------------------------------------------------------------------
4202 * Given an index, find the bounding box of the screen area
4203 * occupied by that character.
4206 * Zero is returned if the character is on the screen. -1
4207 * means the character isn't on the screen. If the return value
4208 * is 0, then the bounding box of the part of the character that's
4209 * visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
4215 *----------------------------------------------------------------------
4219 TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
4220 TkText *textPtr; /* Widget record for text widget. */
4221 TkTextIndex *indexPtr; /* Index of character whose bounding
4222 * box is desired. */
4223 int *xPtr, *yPtr; /* Filled with character's upper-left
4225 int *widthPtr, *heightPtr; /* Filled in with character's dimensions. */
4227 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
4229 register TkTextDispChunk *chunkPtr;
4233 * Make sure that all of the screen layout information is up to date.
4236 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
4237 UpdateDisplayInfo(textPtr);
4241 * Find the display line containing the desired index.
4244 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
4245 if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
4250 * Find the chunk within the line that contains the desired
4254 byteIndex = indexPtr->byteIndex - dlPtr->index.byteIndex;
4255 for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
4256 if (chunkPtr == NULL) {
4259 if (byteIndex < chunkPtr->numBytes) {
4262 byteIndex -= chunkPtr->numBytes;
4266 * Call a chunk-specific procedure to find the horizontal range of
4267 * the character within the chunk, then fill in the vertical range.
4268 * The x-coordinate returned by bboxProc is a coordinate within a
4269 * line, not a coordinate on the screen. Translate it to reflect
4270 * horizontal scrolling.
4273 (*chunkPtr->bboxProc)(chunkPtr, byteIndex, dlPtr->y + dlPtr->spaceAbove,
4274 dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
4275 dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
4277 *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
4278 if ((byteIndex == (chunkPtr->numBytes - 1)) && (chunkPtr->nextPtr == NULL)) {
4280 * Last character in display line. Give it all the space up to
4284 if (*xPtr > dInfoPtr->maxX) {
4285 *xPtr = dInfoPtr->maxX;
4287 *widthPtr = dInfoPtr->maxX - *xPtr;
4289 if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
4292 if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
4293 *widthPtr = dInfoPtr->maxX - *xPtr;
4294 if (*widthPtr <= 0) {
4298 if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
4299 *heightPtr = dInfoPtr->maxY - *yPtr;
4300 if (*heightPtr <= 0) {
4308 *----------------------------------------------------------------------
4310 * TkTextDLineInfo --
4312 * Given an index, return information about the display line
4313 * containing that character.
4316 * Zero is returned if the character is on the screen. -1
4317 * means the character isn't on the screen. If the return value
4318 * is 0, then information is returned in the variables pointed
4319 * to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
4324 *----------------------------------------------------------------------
4328 TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
4329 TkText *textPtr; /* Widget record for text widget. */
4330 TkTextIndex *indexPtr; /* Index of character whose bounding
4331 * box is desired. */
4332 int *xPtr, *yPtr; /* Filled with line's upper-left
4334 int *widthPtr, *heightPtr; /* Filled in with line's dimensions. */
4335 int *basePtr; /* Filled in with the baseline position,
4336 * measured as an offset down from *yPtr. */
4338 TextDInfo *dInfoPtr = textPtr->dInfoPtr;
4343 * Make sure that all of the screen layout information is up to date.
4346 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
4347 UpdateDisplayInfo(textPtr);
4351 * Find the display line containing the desired index.
4354 dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
4355 if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
4359 dlx = (dlPtr->chunkPtr != NULL? dlPtr->chunkPtr->x: 0);
4360 *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlx;
4361 *widthPtr = dlPtr->length - dlx;
4363 if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
4364 *heightPtr = dInfoPtr->maxY - dlPtr->y;
4366 *heightPtr = dlPtr->height;
4368 *basePtr = dlPtr->baseline;
4373 ElideBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
4374 widthPtr, heightPtr)
4375 TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
4376 int index; /* Index of desired character within
4378 int y; /* Topmost pixel in area allocated
4380 int lineHeight; /* Height of line, in pixels. */
4381 int baseline; /* Location of line's baseline, in
4382 * pixels measured down from y. */
4383 int *xPtr, *yPtr; /* Gets filled in with coords of
4384 * character's upper-left pixel.
4385 * X-coord is in same coordinate
4386 * system as chunkPtr->x. */
4387 int *widthPtr; /* Gets filled in with width of
4388 * character, in pixels. */
4389 int *heightPtr; /* Gets filled in with height of
4390 * character, in pixels. */
4392 *xPtr = chunkPtr->x;
4394 *widthPtr = *heightPtr = 0;
4399 ElideMeasureProc(chunkPtr, x)
4400 TkTextDispChunk *chunkPtr; /* Chunk containing desired coord. */
4401 int x; /* X-coordinate, in same coordinate
4402 * system as chunkPtr->x. */
4404 return 0 /*chunkPtr->numBytes - 1*/;
4408 *--------------------------------------------------------------
4410 * TkTextCharLayoutProc --
4412 * This procedure is the "layoutProc" for character segments.
4415 * If there is something to display for the chunk then a
4416 * non-zero value is returned and the fields of chunkPtr
4417 * will be filled in (see the declaration of TkTextDispChunk
4418 * in tkText.h for details). If zero is returned it means
4419 * that no characters from this chunk fit in the window.
4420 * If -1 is returned it means that this segment just doesn't
4421 * need to be displayed (never happens for text).
4424 * Memory is allocated to hold additional information about
4427 *--------------------------------------------------------------
4431 TkTextCharLayoutProc(textPtr, indexPtr, segPtr, byteOffset, maxX, maxBytes,
4432 noCharsYet, wrapMode, chunkPtr)
4433 TkText *textPtr; /* Text widget being layed out. */
4434 TkTextIndex *indexPtr; /* Index of first character to lay out
4435 * (corresponds to segPtr and offset). */
4436 TkTextSegment *segPtr; /* Segment being layed out. */
4437 int byteOffset; /* Byte offset within segment of first
4438 * character to consider. */
4439 int maxX; /* Chunk must not occupy pixels at this
4440 * position or higher. */
4441 int maxBytes; /* Chunk must not include more than this
4442 * many characters. */
4443 int noCharsYet; /* Non-zero means no characters have been
4444 * assigned to this display line yet. */
4445 TkWrapMode wrapMode; /* How to handle line wrapping: TEXT_WRAPMODE_CHAR,
4446 * TEXT_WRAPMODE_NONE, or TEXT_WRAPMODE_WORD. */
4447 register TkTextDispChunk *chunkPtr;
4448 /* Structure to fill in with information
4449 * about this chunk. The x field has already
4450 * been set by the caller. */
4453 int nextX, bytesThatFit, count;
4456 TkTextSegment *nextPtr;
4460 * Figure out how many characters will fit in the space we've got.
4461 * Include the next character, even though it won't fit completely,
4462 * if any of the following is true:
4463 * (a) the chunk contains no characters and the display line contains
4464 * no characters yet (i.e. the line isn't wide enough to hold
4465 * even a single character).
4466 * (b) at least one pixel of the character is visible, we haven't
4467 * already exceeded the character limit, and the next character
4468 * is a white space character.
4471 p = segPtr->body.chars + byteOffset;
4472 tkfont = chunkPtr->stylePtr->sValuePtr->tkfont;
4473 bytesThatFit = MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX, 0,
4475 if (bytesThatFit < maxBytes) {
4476 if ((bytesThatFit == 0) && noCharsYet) {
4479 bytesThatFit = MeasureChars(tkfont, p, Tcl_UtfToUniChar(p, &ch),
4480 chunkPtr->x, -1, 0, &nextX);
4482 if ((nextX < maxX) && ((p[bytesThatFit] == ' ')
4483 || (p[bytesThatFit] == '\t'))) {
4485 * Space characters are funny, in that they are considered
4486 * to fit if there is at least one pixel of space left on the
4487 * line. Just give the space character whatever space is left.
4493 if (p[bytesThatFit] == '\n') {
4495 * A newline character takes up no space, so if the previous
4496 * character fits then so does the newline.
4501 if (bytesThatFit == 0) {
4506 Tk_GetFontMetrics(tkfont, &fm);
4509 * Fill in the chunk structure and allocate and initialize a
4510 * CharInfo structure. If the last character is a newline
4511 * then don't bother to display it.
4514 chunkPtr->displayProc = CharDisplayProc;
4515 chunkPtr->undisplayProc = CharUndisplayProc;
4516 chunkPtr->measureProc = CharMeasureProc;
4517 chunkPtr->bboxProc = CharBboxProc;
4518 chunkPtr->numBytes = bytesThatFit;
4519 chunkPtr->minAscent = fm.ascent + chunkPtr->stylePtr->sValuePtr->offset;
4520 chunkPtr->minDescent = fm.descent - chunkPtr->stylePtr->sValuePtr->offset;
4521 chunkPtr->minHeight = 0;
4522 chunkPtr->width = nextX - chunkPtr->x;
4523 chunkPtr->breakIndex = -1;
4524 ciPtr = (CharInfo *) ckalloc((unsigned)
4525 (sizeof(CharInfo) - 3 + bytesThatFit));
4526 chunkPtr->clientData = (ClientData) ciPtr;
4527 ciPtr->numBytes = bytesThatFit;
4528 strncpy(ciPtr->chars, p, (size_t) bytesThatFit);
4529 if (p[bytesThatFit - 1] == '\n') {
4534 * Compute a break location. If we're in word wrap mode, a
4535 * break can occur after any space character, or at the end of
4536 * the chunk if the next segment (ignoring those with zero size)
4537 * is not a character segment.
4540 if (wrapMode != TEXT_WRAPMODE_WORD) {
4541 chunkPtr->breakIndex = chunkPtr->numBytes;
4543 for (count = bytesThatFit, p += bytesThatFit - 1; count > 0;
4545 if (isspace(UCHAR(*p))) {
4546 chunkPtr->breakIndex = count;
4550 if ((bytesThatFit + byteOffset) == segPtr->size) {
4551 for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
4552 nextPtr = nextPtr->nextPtr) {
4553 if (nextPtr->size != 0) {
4554 if (nextPtr->typePtr != &tkTextCharType) {
4555 chunkPtr->breakIndex = chunkPtr->numBytes;
4566 *--------------------------------------------------------------
4568 * CharDisplayProc --
4570 * This procedure is called to display a character chunk on
4571 * the screen or in an off-screen pixmap.
4577 * Graphics are drawn.
4579 *--------------------------------------------------------------
4583 CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
4584 TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
4585 int x; /* X-position in dst at which to
4586 * draw this chunk (may differ from
4587 * the x-position in the chunk because
4589 int y; /* Y-position at which to draw this
4591 int height; /* Total height of line. */
4592 int baseline; /* Offset of baseline from y. */
4593 Display *display; /* Display to use for drawing. */
4594 Drawable dst; /* Pixmap or window in which to draw
4596 int screenY; /* Y-coordinate in text window that
4597 * corresponds to y. */
4599 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4600 TextStyle *stylePtr;
4601 StyleValues *sValuePtr;
4602 int offsetBytes, offsetX;
4604 if ((x + chunkPtr->width) <= 0) {
4606 * The chunk is off-screen.
4612 stylePtr = chunkPtr->stylePtr;
4613 sValuePtr = stylePtr->sValuePtr;
4616 * If the text sticks out way to the left of the window, skip
4617 * over the characters that aren't in the visible part of the
4618 * window. This is essential if x is very negative (such as
4619 * less than 32K); otherwise overflow problems will occur
4620 * in servers that use 16-bit arithmetic, like X.
4626 offsetBytes = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
4627 ciPtr->numBytes, x, 0, x - chunkPtr->x, &offsetX);
4631 * Draw the text, underline, and overstrike for this chunk.
4634 if (!sValuePtr->elide && (ciPtr->numBytes > offsetBytes) && (stylePtr->fgGC != None)) {
4635 int numBytes = ciPtr->numBytes - offsetBytes;
4636 char *string = ciPtr->chars + offsetBytes;
4638 if ((numBytes > 0) && (string[numBytes - 1] == '\t')) {
4641 Tk_DrawChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, string,
4642 numBytes, offsetX, y + baseline - sValuePtr->offset);
4643 if (sValuePtr->underline) {
4644 Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
4645 ciPtr->chars + offsetBytes, offsetX,
4646 y + baseline - sValuePtr->offset, 0, numBytes);
4649 if (sValuePtr->overstrike) {
4652 Tk_GetFontMetrics(sValuePtr->tkfont, &fm);
4653 Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
4654 ciPtr->chars + offsetBytes, offsetX,
4655 y + baseline - sValuePtr->offset
4656 - fm.descent - (fm.ascent * 3) / 10,
4663 *--------------------------------------------------------------
4665 * CharUndisplayProc --
4667 * This procedure is called when a character chunk is no
4668 * longer going to be displayed. It frees up resources
4669 * that were allocated to display the chunk.
4675 * Memory and other resources get freed.
4677 *--------------------------------------------------------------
4681 CharUndisplayProc(textPtr, chunkPtr)
4682 TkText *textPtr; /* Overall information about text
4684 TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
4686 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4688 ckfree((char *) ciPtr);
4692 *--------------------------------------------------------------
4694 * CharMeasureProc --
4696 * This procedure is called to determine which character in
4697 * a character chunk lies over a given x-coordinate.
4700 * The return value is the index *within the chunk* of the
4701 * character that covers the position given by "x".
4706 *--------------------------------------------------------------
4710 CharMeasureProc(chunkPtr, x)
4711 TkTextDispChunk *chunkPtr; /* Chunk containing desired coord. */
4712 int x; /* X-coordinate, in same coordinate
4713 * system as chunkPtr->x. */
4715 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4718 return MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
4719 chunkPtr->numBytes - 1, chunkPtr->x, x, 0, &endX);
4724 *--------------------------------------------------------------
4728 * This procedure is called to compute the bounding box of
4729 * the area occupied by a single character.
4732 * There is no return value. *xPtr and *yPtr are filled in
4733 * with the coordinates of the upper left corner of the
4734 * character, and *widthPtr and *heightPtr are filled in with
4735 * the dimensions of the character in pixels. Note: not all
4736 * of the returned bbox is necessarily visible on the screen
4737 * (the rightmost part might be off-screen to the right,
4738 * and the bottommost part might be off-screen to the bottom).
4743 *--------------------------------------------------------------
4747 CharBboxProc(chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
4748 widthPtr, heightPtr)
4749 TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
4750 int byteIndex; /* Byte offset of desired character
4751 * within the chunk. */
4752 int y; /* Topmost pixel in area allocated
4754 int lineHeight; /* Height of line, in pixels. */
4755 int baseline; /* Location of line's baseline, in
4756 * pixels measured down from y. */
4757 int *xPtr, *yPtr; /* Gets filled in with coords of
4758 * character's upper-left pixel.
4759 * X-coord is in same coordinate
4760 * system as chunkPtr->x. */
4761 int *widthPtr; /* Gets filled in with width of
4762 * character, in pixels. */
4763 int *heightPtr; /* Gets filled in with height of
4764 * character, in pixels. */
4766 CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4769 maxX = chunkPtr->width + chunkPtr->x;
4770 MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
4771 byteIndex, chunkPtr->x, -1, 0, xPtr);
4773 if (byteIndex == ciPtr->numBytes) {
4775 * This situation only happens if the last character in a line
4776 * is a space character, in which case it absorbs all of the
4777 * extra space in the line (see TkTextCharLayoutProc).
4780 *widthPtr = maxX - *xPtr;
4781 } else if ((ciPtr->chars[byteIndex] == '\t')
4782 && (byteIndex == ciPtr->numBytes - 1)) {
4784 * The desired character is a tab character that terminates a
4785 * chunk; give it all the space left in the chunk.
4788 *widthPtr = maxX - *xPtr;
4790 MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont,
4791 ciPtr->chars + byteIndex, 1, *xPtr, -1, 0, widthPtr);
4792 if (*widthPtr > maxX) {
4793 *widthPtr = maxX - *xPtr;
4798 *yPtr = y + baseline - chunkPtr->minAscent;
4799 *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
4803 *----------------------------------------------------------------------
4807 * This procedure is called to move a series of chunks right
4808 * in order to align them with a tab stop.
4814 * The width of chunkPtr gets adjusted so that it absorbs the
4815 * extra space due to the tab. The x locations in all the chunks
4816 * after chunkPtr are adjusted rightward to align with the tab
4817 * stop given by tabArrayPtr and index.
4819 *----------------------------------------------------------------------
4823 AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
4824 TkText *textPtr; /* Information about the text widget as
4826 TkTextTabArray *tabArrayPtr; /* Information about the tab stops
4827 * that apply to this line. May be
4828 * NULL to indicate default tabbing
4829 * (every 8 chars). */
4830 int index; /* Index of current tab stop. */
4831 TkTextDispChunk *chunkPtr; /* Chunk whose last character is
4832 * the tab; the following chunks
4833 * contain information to be shifted
4837 int x, desired, delta, width, decimal, i, gotDigit;
4838 TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
4840 int tabX, prev, spaceWidth;
4842 TkTextTabAlign alignment;
4844 if (chunkPtr->nextPtr == NULL) {
4846 * Nothing after the actual tab; just return.
4853 * If no tab information has been given, do the usual thing:
4854 * round up to the next boundary of 8 average-sized characters.
4857 x = chunkPtr->nextPtr->x;
4858 if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
4860 * No tab information has been given, so use the default
4861 * interpretation of tabs.
4864 desired = NextTabStop(textPtr->tkfont, x, 0);
4868 if (index < tabArrayPtr->numTabs) {
4869 alignment = tabArrayPtr->tabs[index].alignment;
4870 tabX = tabArrayPtr->tabs[index].location;
4873 * Ran out of tab stops; compute a tab position by extrapolating
4874 * from the last two tab positions.
4877 if (tabArrayPtr->numTabs > 1) {
4878 prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
4882 alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
4883 tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
4884 + (index + 1 - tabArrayPtr->numTabs)
4885 * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
4888 if (alignment == LEFT) {
4893 if ((alignment == CENTER) || (alignment == RIGHT)) {
4895 * Compute the width of all the information in the tab group,
4896 * then use it to pick a desired location.
4900 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4901 chunkPtr2 = chunkPtr2->nextPtr) {
4902 width += chunkPtr2->width;
4904 if (alignment == CENTER) {
4905 desired = tabX - width/2;
4907 desired = tabX - width;
4913 * Must be numeric alignment. Search through the text to be
4914 * tabbed, looking for the last , or . before the first character
4915 * that isn't a number, comma, period, or sign.
4918 decimalChunkPtr = NULL;
4919 decimal = gotDigit = 0;
4920 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4921 chunkPtr2 = chunkPtr2->nextPtr) {
4922 if (chunkPtr2->displayProc != CharDisplayProc) {
4925 ciPtr = (CharInfo *) chunkPtr2->clientData;
4926 for (p = ciPtr->chars, i = 0; i < ciPtr->numBytes; p++, i++) {
4927 if (isdigit(UCHAR(*p))) {
4929 } else if ((*p == '.') || (*p == ',')) {
4930 decimal = p-ciPtr->chars;
4931 decimalChunkPtr = chunkPtr2;
4932 } else if (gotDigit) {
4933 if (decimalChunkPtr == NULL) {
4934 decimal = p-ciPtr->chars;
4935 decimalChunkPtr = chunkPtr2;
4942 if (decimalChunkPtr != NULL) {
4945 ciPtr = (CharInfo *) decimalChunkPtr->clientData;
4946 MeasureChars(decimalChunkPtr->stylePtr->sValuePtr->tkfont,
4947 ciPtr->chars, decimal, decimalChunkPtr->x, -1, 0, &curX);
4948 desired = tabX - (curX - x);
4952 * There wasn't a decimal point. Right justify the text.
4956 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4957 chunkPtr2 = chunkPtr2->nextPtr) {
4958 width += chunkPtr2->width;
4960 desired = tabX - width;
4964 * Shift all of the chunks to the right so that the left edge is
4965 * at the desired location, then expand the chunk containing the
4966 * tab. Be sure that the tab occupies at least the width of a
4971 delta = desired - x;
4972 MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
4973 if (delta < spaceWidth) {
4976 for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4977 chunkPtr2 = chunkPtr2->nextPtr) {
4978 chunkPtr2->x += delta;
4980 chunkPtr->width += delta;
4984 *----------------------------------------------------------------------
4988 * This returns an estimate of the amount of white space that will
4989 * be consumed by a tab.
4992 * The return value is the minimum number of pixels that will
4993 * be occupied by the index'th tab of tabArrayPtr, assuming that
4994 * the current position on the line is x and the end of the
4995 * line is maxX. For numeric tabs, this is a conservative
4996 * estimate. The return value is always >= 0.
5001 *----------------------------------------------------------------------
5005 SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
5006 TkText *textPtr; /* Information about the text widget as
5008 TkTextTabArray *tabArrayPtr; /* Information about the tab stops
5009 * that apply to this line. NULL
5010 * means use default tabbing (every
5012 int index; /* Index of current tab stop. */
5013 int x; /* Current x-location in line. Only
5014 * used if tabArrayPtr == NULL. */
5015 int maxX; /* X-location of pixel just past the
5016 * right edge of the line. */
5018 int tabX, prev, result, spaceWidth;
5019 TkTextTabAlign alignment;
5021 if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
5022 tabX = NextTabStop(textPtr->tkfont, x, 0);
5025 if (index < tabArrayPtr->numTabs) {
5026 tabX = tabArrayPtr->tabs[index].location;
5027 alignment = tabArrayPtr->tabs[index].alignment;
5030 * Ran out of tab stops; compute a tab position by extrapolating
5031 * from the last two tab positions.
5034 if (tabArrayPtr->numTabs > 1) {
5035 prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
5039 tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
5040 + (index + 1 - tabArrayPtr->numTabs)
5041 * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
5042 alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
5044 if (alignment == CENTER) {
5046 * Be very careful in the arithmetic below, because maxX may
5047 * be the largest positive number: watch out for integer
5051 if ((maxX-tabX) < (tabX - x)) {
5052 result = (maxX - x) - 2*(maxX - tabX);
5058 if (alignment == RIGHT) {
5064 * Note: this treats NUMERIC alignment the same as LEFT
5065 * alignment, which is somewhat conservative. However, it's
5066 * pretty tricky at this point to figure out exactly where
5067 * the damn decimal point will be.
5077 MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
5078 if (result < spaceWidth) {
5079 result = spaceWidth;
5085 *---------------------------------------------------------------------------
5089 * Given the current position, determine where the next default
5090 * tab stop would be located. This procedure is called when the
5091 * current chunk in the text has no tabs defined and so the default
5092 * tab spacing for the font should be used.
5095 * The location in pixels of the next tab stop.
5100 *---------------------------------------------------------------------------
5104 NextTabStop(tkfont, x, tabOrigin)
5105 Tk_Font tkfont; /* Font in which chunk that contains tab
5106 * stop will be drawn. */
5107 int x; /* X-position in pixels where last
5108 * character was drawn. The next tab stop
5109 * occurs somewhere after this location. */
5110 int tabOrigin; /* The origin for tab stops. May be
5111 * non-zero if text has been scrolled. */
5115 tabWidth = Tk_TextWidth(tkfont, "0", 1) * 8;
5116 if (tabWidth == 0) {
5121 rem = (x - tabOrigin) % tabWidth;
5130 *---------------------------------------------------------------------------
5134 * Determine the number of characters from the string that will fit
5135 * in the given horizontal span. The measurement is done under the
5136 * assumption that Tk_DrawTextLayout will be used to actually display
5139 * If tabs are encountered in the string, they will be expanded
5140 * to the next tab stop, unless the TK_IGNORE_TABS flag is specified.
5142 * If a newline is encountered in the string, the line will be
5143 * broken at that point, unless the TK_NEWSLINES_NOT_SPECIAL flag
5147 * The return value is the number of bytes from source
5148 * that fit in the span given by startX and maxX. *nextXPtr
5149 * is filled in with the x-coordinate at which the first
5150 * character that didn't fit would be drawn, if it were to
5156 *--------------------------------------------------------------
5160 MeasureChars(tkfont, source, maxBytes, startX, maxX, tabOrigin, nextXPtr)
5161 Tk_Font tkfont; /* Font in which to draw characters. */
5162 CONST char *source; /* Characters to be displayed. Need not
5163 * be NULL-terminated. */
5164 int maxBytes; /* Maximum # of bytes to consider from
5166 int startX; /* X-position at which first character will
5168 int maxX; /* Don't consider any character that would
5169 * cross this x-position. */
5170 int tabOrigin; /* X-location that serves as "origin" for
5172 int *nextXPtr; /* Return x-position of terminating
5173 * character here. */
5175 int curX, width, ch;
5176 CONST char *special, *end, *start;
5181 end = source + maxBytes;
5182 for (start = source; start < end; ) {
5183 if (start >= special) {
5185 * Find the next special character in the string.
5188 for (special = start; special < end; special++) {
5190 if ((ch == '\t') || (ch == '\n')) {
5197 * Special points at the next special character (or the end of the
5198 * string). Process characters between start and special.
5201 if ((maxX >= 0) && (curX >= maxX)) {
5204 start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX,
5207 if (start < special) {
5209 * No more chars fit in line.
5214 if (special < end) {
5224 return start - source;