]>
cvs.zerfleddert.de Git - micropolis/blob - src/tk/tktxdisp.c
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 1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTextDisp.c,v 1.20 92/08/24 09:24:18 ouster Exp $ SPRITE (Berkeley)";
29 * The following structure describes how to display a range of characters.
30 * The information is generated by scanning all of the tags associated
31 * with the characters and combining that with default information for
32 * the overall widget. These structures form the hash keys for
33 * dInfoPtr->styleTable.
36 typedef struct StyleValues
{
37 Tk_3DBorder border
; /* Used for drawing background under text.
38 * NULL means use widget background. */
39 int borderWidth
; /* Width of 3-D border for background. */
40 int relief
; /* 3-D relief for background. */
41 Pixmap bgStipple
; /* Stipple bitmap for background. None
42 * means draw solid. */
43 XColor
*fgColor
; /* Foreground color for text. */
44 XFontStruct
*fontPtr
; /* Font for displaying text. */
45 Pixmap fgStipple
; /* Stipple bitmap for text and other
46 * foreground stuff. None means draw
48 int underline
; /* Non-zero means draw underline underneath
53 * The following structure extends the StyleValues structure above with
54 * graphics contexts used to actually draw the characters. The entries
55 * in dInfoPtr->styleTable point to structures of this type.
58 typedef struct Style
{
59 int refCount
; /* Number of times this structure is
60 * referenced in Chunks. */
61 GC bgGC
; /* Graphics context for background. None
62 * unless background is stippled. */
63 GC fgGC
; /* Graphics context for foreground. */
64 StyleValues
*sValuePtr
; /* Raw information from which GCs were
66 Tcl_HashEntry
*hPtr
; /* Pointer to entry in styleTable. Used
71 * The following structure describes a range of characters, all on the
72 * same line of the display (which also means the same line of the text
73 * widget) and all having the same display attributes.
76 typedef struct Chunk
{
77 char *text
; /* Characters to display. */
78 int numChars
; /* Number of characters to display. */
79 Style
*stylePtr
; /* Style information used to display
81 int x
; /* X-coordinate of pixel at which to display
83 struct Chunk
*nextPtr
; /* Next in list of all chunks displayed on the
84 * same display line. */
88 * The following structure describes one line of the display, which may
89 * be either part or all of one line of the text.
92 typedef struct DLine
{
93 TkTextLine
*linePtr
; /* Pointer to structure in B-tree that
94 * contains characters displayed in this
96 int y
; /* Y-position at which line is supposed to
97 * be drawn (topmost pixel of rectangular
98 * area occupied by line). */
99 int oldY
; /* Y-position at which line currently
100 * appears on display. -1 means line isn't
101 * currently visible on display. This is
102 * used to move lines by scrolling rather
103 * than re-drawing. */
104 int height
; /* Height of line, in pixels. */
105 int baseline
; /* Offset of text baseline from y. */
106 Chunk
*chunkPtr
; /* Pointer to first chunk in list of all
107 * of those that are displayed on this
108 * line of the screen. */
109 struct DLine
*nextPtr
; /* Next in list of all display lines for
110 * this window. The list is sorted in
111 * order from top to bottom. Note: the
112 * next DLine doesn't always correspond
113 * to the next line of text: (a) can have
114 * multiple DLines for one text line, and
115 * (b) can have gaps where DLine's have been
116 * deleted because they're out of date. */
120 * Overall display information for a text widget:
123 typedef struct DInfo
{
124 Tcl_HashTable styleTable
; /* Hash table that maps from StyleValues to
125 * Styles for this widget. */
126 DLine
*dLinePtr
; /* First in list of all display lines for
127 * this widget, in order from top to bottom. */
128 GC copyGC
; /* Graphics context for copying from off-
129 * screen pixmaps onto screen. */
130 GC scrollGC
; /* Graphics context for copying from one place
131 * in the window to another (scrolling):
132 * differs from copyGC in that we need to get
133 * GraphicsExpose events. */
134 int x
; /* First x-coordinate that may be used for
135 * actually displaying line information.
136 * Leaves space for border, etc. */
137 int y
; /* First y-coordinate that may be used for
138 * actually displaying line information.
139 * Leaves space for border, etc. */
140 int maxX
; /* First x-coordinate to right of available
141 * space for displaying lines. */
142 int maxY
; /* First y-coordinate to bottom of available
143 * space for displaying lines. */
144 int topOfEof
; /* Top-most pixel (lowest y-value) that has
145 * been drawn in the appropriate fashion for
146 * the portion of the window after the last
147 * line of the text. This field is used to
148 * figure out when to redraw part or all of
150 int flags
; /* Various flag values: see below for
155 * Flag values for DInfo structures:
157 * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
158 * for this window are partially or completely
159 * out of date and need to be recomputed.
160 * REDRAW_PENDING: Means that a when-idle handler has been
161 * scheduled to update the display.
162 * REDRAW_BORDERS: Means window border or pad area has
163 * potentially been damaged and must be redrawn.
164 * REPICK_NEEDED: 1 means that the widget has been modified
165 * in a way that could change the current
166 * character (a different character might be
167 * under the mouse cursor now). Need to
168 * recompute the current character before
169 * the next redisplay.
172 #define DINFO_OUT_OF_DATE 1
173 #define REDRAW_PENDING 2
174 #define REDRAW_BORDERS 4
175 #define REPICK_NEEDED 8
178 * Structures of the type defined below are used to keep track of
179 * tags while scanning through the text to create DLine structures.
182 typedef struct TagInfo
{
183 int numTags
; /* Number of tags currently active (the first
184 * entries at *tagPtr). */
185 int arraySize
; /* Total number of entries at *tagPtr. We
186 * over-allocate the array to avoid continual
188 TkTextTag
**tagPtrs
; /* Pointer to array of pointers to active tags.
189 * Array has space for arraySize tags, and
190 * the first numTags are slots identify the
191 * active tags. Malloc'ed (but may be NULL). */
192 TkTextSearch search
; /* Used to scan for tag transitions. Current
193 * state identifies next tag transition. */
197 * The following counters keep statistics about redisplay that can be
198 * checked to see how clever this code is at reducing redisplays.
201 static int numRedisplays
; /* Number of calls to DisplayText. */
202 static int linesRedrawn
; /* Number of calls to DisplayDLine. */
203 static int numCopies
; /* Number of calls to XCopyArea to copy part
205 static int damagedCopies
; /* Number of times that XCopyAreas didn't
206 * completely work because some of the source
207 * information was damaged. */
208 static int TextUpdateTime
= 100; // Added by Don.
211 * Forward declarations for procedures defined later in this file:
214 static void ComputeStyleValues
_ANSI_ARGS_((TkText
*textPtr
,
215 int numTags
, TkTextTag
**tagPtr
,
216 StyleValues
*sValuePtr
));
217 static void DisplayDLine
_ANSI_ARGS_((TkText
*textPtr
,
218 DLine
*dlPtr
, Pixmap pixmap
));
219 static void DisplayText
_ANSI_ARGS_((ClientData clientData
));
220 static DLine
* FindDLine
_ANSI_ARGS_((DLine
*dlPtr
, int line
));
221 static void FreeDLines
_ANSI_ARGS_((TkText
*textPtr
,
222 DLine
*firstPtr
, DLine
*lastPtr
, int unlink
));
223 static void FreeStyle
_ANSI_ARGS_((Style
*stylePtr
));
224 static Style
* GetStyle
_ANSI_ARGS_((TkText
*textPtr
,
225 StyleValues
*sValuePtr
));
226 static DLine
* LayoutLine
_ANSI_ARGS_((TkText
*textPtr
, int line
,
227 TkTextLine
*linePtr
, TagInfo
*tInfoPtr
));
228 static void ToggleTag
_ANSI_ARGS_((TagInfo
*tInfoPtr
,
230 static void UpdateDisplayInfo
_ANSI_ARGS_((TkText
*textPtr
));
233 *----------------------------------------------------------------------
235 * TkTextCreateDInfo --
237 * This procedure is called when a new text widget is created.
238 * Its job is to set up display-related information for the widget.
244 * A DInfo data structure is allocated and initialized and attached
247 *----------------------------------------------------------------------
252 TkText
*textPtr
/* Overall information for text widget. */
255 register DInfo
*dInfoPtr
;
258 dInfoPtr
= (DInfo
*) ckalloc(sizeof(DInfo
));
259 Tcl_InitHashTable(&dInfoPtr
->styleTable
, sizeof(StyleValues
)/sizeof(int));
260 dInfoPtr
->dLinePtr
= NULL
;
261 gcValues
.graphics_exposures
= False
;
262 dInfoPtr
->copyGC
= Tk_GetGC(textPtr
->tkwin
, GCGraphicsExposures
, &gcValues
);
263 gcValues
.graphics_exposures
= True
;
264 dInfoPtr
->scrollGC
= Tk_GetGC(textPtr
->tkwin
, GCGraphicsExposures
,
266 dInfoPtr
->topOfEof
= 0;
267 dInfoPtr
->flags
= DINFO_OUT_OF_DATE
;
268 textPtr
->dInfoPtr
= dInfoPtr
;
272 *----------------------------------------------------------------------
276 * This procedure is called to free up all of the private display
277 * information kept by this file for a text widget.
283 * Lots of resources get freed.
285 *----------------------------------------------------------------------
290 TkText
*textPtr
/* Overall information for text widget. */
293 register DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
296 * Be careful to free up styleTable *after* freeing up all the
297 * DLines, so that the hash table is still intact to free up the
298 * style-related information from the lines. Once the lines are
299 * all free then styleTable will be empty.
302 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, (DLine
*) NULL
, 1);
303 Tcl_DeleteHashTable(&dInfoPtr
->styleTable
);
304 Tk_FreeGC(dInfoPtr
->copyGC
);
305 Tk_FreeGC(dInfoPtr
->scrollGC
);
306 if (dInfoPtr
->flags
& REDRAW_PENDING
) {
307 // Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
308 assert(textPtr
->updateTimerToken
!= NULL
);
309 if (textPtr
->updateTimerToken
!= NULL
) {
310 Tk_DeleteTimerHandler(textPtr
->updateTimerToken
);
311 textPtr
->updateTimerToken
= NULL
;
314 ckfree((char *) dInfoPtr
);
318 *----------------------------------------------------------------------
322 * This procedure creates graphics contexts needed to display
323 * text in a particular style, determined by "sValuePtr". It
324 * attempts to share style information as much as possible.
327 * The return value is a pointer to a Style structure that
328 * corresponds to *sValuePtr.
331 * A new entry may be created in the style table for the widget.
333 *----------------------------------------------------------------------
338 TkText
*textPtr
, /* Overall information about text widget. */
339 StyleValues
*sValuePtr
/* Information about desired style. */
349 * Use an existing style if there's one around that matches.
352 hPtr
= Tcl_CreateHashEntry(&textPtr
->dInfoPtr
->styleTable
,
353 (char *) sValuePtr
, &new);
355 stylePtr
= (Style
*) Tcl_GetHashValue(hPtr
);
356 stylePtr
->refCount
++;
361 * No existing style matched. Make a new one.
364 stylePtr
= (Style
*) ckalloc(sizeof(Style
));
365 stylePtr
->refCount
= 1;
366 if ((sValuePtr
->border
!= NULL
) && (sValuePtr
->bgStipple
!= None
)) {
367 gcValues
.foreground
= Tk_3DBorderColor(sValuePtr
->border
)->pixel
;
368 gcValues
.stipple
= sValuePtr
->bgStipple
;
369 gcValues
.fill_style
= FillStippled
;
370 stylePtr
->bgGC
= Tk_GetGC(textPtr
->tkwin
,
371 GCForeground
|GCStipple
|GCFillStyle
, &gcValues
);
373 stylePtr
->bgGC
= None
;
375 mask
= GCForeground
|GCFont
;
376 gcValues
.foreground
= sValuePtr
->fgColor
->pixel
;
377 gcValues
.font
= sValuePtr
->fontPtr
->fid
;
378 if (sValuePtr
->fgStipple
!= None
) {
379 gcValues
.stipple
= sValuePtr
->fgStipple
;
380 gcValues
.fill_style
= FillStippled
;
381 mask
|= GCStipple
|GCFillStyle
;
383 stylePtr
->fgGC
= Tk_GetGC(textPtr
->tkwin
, mask
, &gcValues
);
384 stylePtr
->sValuePtr
= (StyleValues
*)
385 Tcl_GetHashKey(&textPtr
->dInfoPtr
->styleTable
, hPtr
);
386 stylePtr
->hPtr
= hPtr
;
387 Tcl_SetHashValue(hPtr
, stylePtr
);
392 *----------------------------------------------------------------------
396 * This procedure is called when a Style structure is no longer
397 * needed. It decrements the reference count and frees up the
398 * space for the style structure if the reference count is 0.
404 * The storage and other resources associated with the style
405 * are freed up if no-one's still using it.
407 *----------------------------------------------------------------------
412 register Style
*stylePtr
/* Information about style to be freed. */
416 stylePtr
->refCount
--;
417 if (stylePtr
->refCount
== 0) {
418 if (stylePtr
->bgGC
!= None
) {
419 Tk_FreeGC(stylePtr
->bgGC
);
421 Tk_FreeGC(stylePtr
->fgGC
);
422 Tcl_DeleteHashEntry(stylePtr
->hPtr
);
423 ckfree((char *) stylePtr
);
428 *----------------------------------------------------------------------
430 * ComputeStyleValues --
432 * Given a list of tags that apply at a particular point, compute
433 * the StyleValues that correspond to that set of tags.
436 * All of the fields of *sValuePtr get filled in to hold the
437 * appropriate display information for the given set of tags
438 * in the given widget.
443 *----------------------------------------------------------------------
448 TkText
*textPtr
, /* Overall information for widget. */
449 int numTags
, /* Number of tags at *tagPtr. */
450 register TkTextTag
**tagPtrPtr
, /* Pointer to array of tag pointers. */
451 register StyleValues
*sValuePtr
/* Pointer to structure to fill in. */
454 register TkTextTag
*tagPtr
;
457 * The variables below keep track of the highest-priority specification
458 * that has occurred for each of the various fields of the StyleValues.
461 int borderPrio
, bgStipplePrio
;
462 int fgPrio
, fontPrio
, fgStipplePrio
;
464 borderPrio
= bgStipplePrio
= -1;
465 fgPrio
= fontPrio
= fgStipplePrio
= -1;
466 memset((VOID
*) sValuePtr
, 0, sizeof(StyleValues
));
467 sValuePtr
->fgColor
= textPtr
->fgColor
;
468 sValuePtr
->fontPtr
= textPtr
->fontPtr
;
471 * Scan through all of the tags, updating the StyleValues to hold
472 * the highest-priority information.
475 for ( ; numTags
> 0; tagPtrPtr
++, numTags
--) {
477 if ((tagPtr
->border
!= NULL
) && (tagPtr
->priority
> borderPrio
)) {
478 sValuePtr
->border
= tagPtr
->border
;
479 sValuePtr
->borderWidth
= tagPtr
->borderWidth
;
480 sValuePtr
->relief
= tagPtr
->relief
;
481 borderPrio
= tagPtr
->priority
;
483 if ((tagPtr
->bgStipple
!= None
)
484 && (tagPtr
->priority
> bgStipplePrio
)) {
485 sValuePtr
->bgStipple
= tagPtr
->bgStipple
;
486 bgStipplePrio
= tagPtr
->priority
;
488 if ((tagPtr
->fgColor
!= None
) && (tagPtr
->priority
> fgPrio
)) {
489 sValuePtr
->fgColor
= tagPtr
->fgColor
;
490 fgPrio
= tagPtr
->priority
;
492 if ((tagPtr
->fontPtr
!= None
) && (tagPtr
->priority
> fontPrio
)) {
493 sValuePtr
->fontPtr
= tagPtr
->fontPtr
;
494 fontPrio
= tagPtr
->priority
;
496 if ((tagPtr
->fgStipple
!= None
)
497 && (tagPtr
->priority
> fgStipplePrio
)) {
498 sValuePtr
->fgStipple
= tagPtr
->fgStipple
;
499 fgStipplePrio
= tagPtr
->priority
;
501 if (tagPtr
->underline
) {
502 sValuePtr
->underline
= 1;
508 *----------------------------------------------------------------------
512 * This procedure generates a linked list of one or more DLine
513 * structures, which describe how to display everything in one
517 * The return value is a pointer to one or more DLine structures
518 * linked into a linked list. The structures are completely filled
519 * in except for the y field, which the caller must supply. Also,
520 * the information at *tInfoPtr gets updated to refer to the state
521 * just after the last character of the line.
526 *----------------------------------------------------------------------
531 TkText
*textPtr
, /* Overall information about text widget. */
532 int line
, /* Index of line to layout. */
533 TkTextLine
*linePtr
, /* Line to layout (corresponds to line). */
534 TagInfo
*tInfoPtr
/* Information to help keep track of tags.
535 * Caller must have initialized to correspond
536 * to state just before start of line. */
540 DLine
*lastLinePtr
= NULL
; /* Initializations needed only to stop */
541 Chunk
*lastChunkPtr
= NULL
; /* compiler warnings. */
542 register DLine
*dlPtr
;
543 register Chunk
*chunkPtr
;
544 StyleValues styleValues
;
545 int ch
, charsThatFit
, ascent
, descent
, x
, maxX
;
550 * Each iteration of the loop below creates one DLine structure.
557 * Create and initialize a new DLine structure.
560 dlPtr
= (DLine
*) ckalloc(sizeof(DLine
));
561 dlPtr
->linePtr
= linePtr
;
564 dlPtr
->chunkPtr
= NULL
;
565 dlPtr
->nextPtr
= NULL
;
566 if (firstLinePtr
== NULL
) {
567 firstLinePtr
= dlPtr
;
569 lastLinePtr
->nextPtr
= dlPtr
;
574 * Each iteration of the loop below creates one Chunk for the
578 x
= textPtr
->dInfoPtr
->x
;
579 maxX
= textPtr
->dInfoPtr
->maxX
;
580 ascent
= descent
= 0;
582 chunkPtr
= (Chunk
*) ckalloc(sizeof(Chunk
));
583 chunkPtr
->numChars
= linePtr
->numBytes
- ch
;
584 chunkPtr
->text
= linePtr
->bytes
+ ch
;
586 chunkPtr
->nextPtr
= NULL
;
587 if (dlPtr
->chunkPtr
== NULL
) {
588 dlPtr
->chunkPtr
= chunkPtr
;
590 lastChunkPtr
->nextPtr
= chunkPtr
;
592 lastChunkPtr
= chunkPtr
;
595 * Update the tag array to include any tag transitions up
596 * through the current position, then find the next position
597 * with a transition on a tag that impacts the way things are
605 if ((tInfoPtr
->search
.linePtr
== NULL
)
606 || (tInfoPtr
->search
.line1
> line
)) {
609 tagPtr
= tInfoPtr
->search
.tagPtr
;
610 affectsDisplay
= TK_TAG_AFFECTS_DISPLAY(tagPtr
);
611 if ((tInfoPtr
->search
.line1
< line
)
612 || (tInfoPtr
->search
.ch1
<= ch
)) {
613 if (affectsDisplay
) {
614 ToggleTag(tInfoPtr
, tagPtr
);
617 if (affectsDisplay
) {
618 chunkPtr
->numChars
= tInfoPtr
->search
.ch1
- ch
;
622 (void) TkBTreeNextTag(&tInfoPtr
->search
);
626 * Create style information for this chunk.
629 ComputeStyleValues(textPtr
, tInfoPtr
->numTags
, tInfoPtr
->tagPtrs
,
631 chunkPtr
->stylePtr
= GetStyle(textPtr
, &styleValues
);
634 * See how many characters will fit on the line. If they don't
635 * all fit, then a number of compensations may have to be made.
637 * 1. Make sure that at least one character is displayed on
639 * 2. In wrap mode "none", allow a partial character to be
640 * displayed at the end of an incomplete line.
641 * 3. In wrap mode "word", search back to find the last space
642 * character, and terminate the line just after that space
643 * character. This involves a couple of extra complexities:
644 * - the last space may be several chunks back; in this
645 * case, delete all the chunks that are after the
647 * - if no words fit at all, then use character-wrap for
649 * - have to reinitialize the tag search information, since
650 * we may back up over tag toggles (they'll need to be
651 * reconsidered on the next DLine).
654 charsThatFit
= TkMeasureChars(styleValues
.fontPtr
,
655 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
,
657 if ((charsThatFit
< chunkPtr
->numChars
) || (x
>= maxX
)) {
659 chunkPtr
->numChars
= charsThatFit
;
661 if (ch
< (linePtr
->numBytes
- 1)) {
662 if ((charsThatFit
== 0) && (chunkPtr
== dlPtr
->chunkPtr
)) {
663 chunkPtr
->numChars
= 1;
665 } else if (textPtr
->wrapMode
== tkTextWordUid
) {
666 if (isspace(chunkPtr
->text
[charsThatFit
])) {
667 ch
+= 1; /* Include space on this line. */
669 register Chunk
*chunkPtr2
;
671 Chunk
*spaceChunkPtr
;
674 spaceChunkPtr
= NULL
;
676 for (chunkPtr2
= dlPtr
->chunkPtr
;
678 chunkPtr2
= chunkPtr2
->nextPtr
) {
679 for (count
= chunkPtr2
->numChars
- 1,
680 p
= chunkPtr2
->text
+ count
;
681 count
>= 0; count
--, p
--) {
683 spaceChunkPtr
= chunkPtr2
;
689 if (spaceChunkPtr
!= NULL
) {
690 spaceChunkPtr
->numChars
= space
;
691 ch
= (spaceChunkPtr
->text
+ space
+ 1)
693 if (chunkPtr
!= spaceChunkPtr
) {
694 chunkPtr
= spaceChunkPtr
;
695 if (tInfoPtr
->tagPtrs
!= NULL
) {
696 ckfree((char *) tInfoPtr
->tagPtrs
);
698 tInfoPtr
->tagPtrs
= TkBTreeGetTags(
699 textPtr
->tree
, dlPtr
->linePtr
, ch
,
701 TkBTreeStartSearch(textPtr
->tree
, line
,
703 TkBTreeNumLines(textPtr
->tree
), 0,
706 (void) TkBTreeNextTag(&tInfoPtr
->search
);
707 tInfoPtr
->arraySize
= tInfoPtr
->numTags
;
708 while (chunkPtr
->nextPtr
!= NULL
) {
709 chunkPtr2
= chunkPtr
->nextPtr
;
710 chunkPtr
->nextPtr
= chunkPtr2
->nextPtr
;
711 FreeStyle(chunkPtr2
->stylePtr
);
712 ckfree((char *) chunkPtr2
);
717 } else if (textPtr
->wrapMode
== tkTextNoneUid
) {
718 chunkPtr
->numChars
++;
723 ch
+= chunkPtr
->numChars
;
727 * Update height information for use later in computing
728 * line's overall height and baseline.
731 if (styleValues
.fontPtr
->ascent
> ascent
) {
732 ascent
= styleValues
.fontPtr
->ascent
;
734 if (styleValues
.fontPtr
->descent
> descent
) {
735 descent
= styleValues
.fontPtr
->descent
;
739 dlPtr
->height
= ascent
+ descent
;
740 dlPtr
->baseline
= ascent
;
743 * Quit when every character but the last character (the newline)
744 * has been accounted for. Also quit if the wrap mode is "none":
745 * this ignores all the characters that don't fit on the first
749 if ((ch
>= (linePtr
->numBytes
-1))
750 || (textPtr
->wrapMode
== tkTextNoneUid
)) {
758 *----------------------------------------------------------------------
762 * Update information about tags to reflect a transition on a
766 * The array at *tInfoPtr is modified to include tagPtr if it
767 * didn't already or to exclude it if it used to include it.
768 * The array will be reallocated to a larger size if needed.
773 *----------------------------------------------------------------------
778 register TagInfo
*tInfoPtr
, /* Tag information to be updated. */
779 TkTextTag
*tagPtr
/* Tag to be toggled into or out of
783 register TkTextTag
**tagPtrPtr
;
786 for (i
= tInfoPtr
->numTags
, tagPtrPtr
= tInfoPtr
->tagPtrs
;
787 i
> 0; i
--, tagPtrPtr
++) {
788 if (*tagPtrPtr
== tagPtr
) {
790 *tagPtrPtr
= tInfoPtr
->tagPtrs
[tInfoPtr
->numTags
];
796 * Tag not currently in array. Grow the array if necessary, then
800 if (tInfoPtr
->numTags
== tInfoPtr
->arraySize
) {
803 newPtrs
= (TkTextTag
**) ckalloc((unsigned)
804 ((tInfoPtr
->arraySize
+10) * sizeof(TkTextTag
*)));
805 if (tInfoPtr
->tagPtrs
!= NULL
) {
806 memcpy((VOID
*) newPtrs
, (VOID
*) tInfoPtr
->tagPtrs
,
807 tInfoPtr
->arraySize
* sizeof(TkTextTag
*));
808 ckfree((char *) tInfoPtr
->tagPtrs
);
810 tInfoPtr
->tagPtrs
= newPtrs
;
811 tInfoPtr
->arraySize
+= 10;
813 tInfoPtr
->tagPtrs
[tInfoPtr
->numTags
] = tagPtr
;
818 *----------------------------------------------------------------------
820 * UpdateDisplayInfo --
822 * This procedure is invoked to recompute some or all of the
823 * DLine structures for a text widget. At the time it is called
824 * the DLine structures still left in the widget are guaranteed
825 * to be correct (except for their y-coordinates), but there may
826 * be missing structures (the DLine structures get removed as
827 * soon as they are potentially out-of-date).
833 * Upon return, the DLine information for textPtr correctly reflects
834 * the positions where characters will be displayed. However, this
835 * procedure doesn't actually bring the display up-to-date.
837 *----------------------------------------------------------------------
842 TkText
*textPtr
/* Text widget to update. */
845 register DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
846 register DLine
*dlPtr
, *prevPtr
, *dlPtr2
;
851 if (!(dInfoPtr
->flags
& DINFO_OUT_OF_DATE
)) {
854 dInfoPtr
->flags
&= ~DINFO_OUT_OF_DATE
;
856 linePtr
= textPtr
->topLinePtr
;
857 dlPtr
= dInfoPtr
->dLinePtr
;
858 tagInfo
.tagPtrs
= TkBTreeGetTags(textPtr
->tree
, linePtr
, 0,
860 tagInfo
.arraySize
= tagInfo
.numTags
;
863 * Tricky point: initialize the tag search just *after* the first
864 * character in the line, since the tagInfo structure already has all
865 * the tags for the first character.
868 line
= TkBTreeLineIndex(linePtr
);
869 TkBTreeStartSearch(textPtr
->tree
, line
, 1, TkBTreeNumLines(textPtr
->tree
),
870 0, (TkTextTag
*) NULL
, &tagInfo
.search
);
871 TkBTreeNextTag(&tagInfo
.search
);
874 maxY
= dInfoPtr
->maxY
;
875 while ((linePtr
!= NULL
) && (y
< maxY
)) {
876 register DLine
*newPtr
;
878 * See if the next DLine matches the next line we want to
879 * appear on the screen. If so then we can just use its
880 * information. If not then create new DLine structures
881 * for the desired line and insert them into the list.
884 if ((dlPtr
== NULL
) || (dlPtr
->linePtr
!= linePtr
)) {
885 newPtr
= LayoutLine(textPtr
, line
, linePtr
, &tagInfo
);
886 if (prevPtr
== NULL
) {
887 dInfoPtr
->dLinePtr
= newPtr
;
889 prevPtr
->nextPtr
= newPtr
;
891 for (dlPtr2
= newPtr
; dlPtr2
->nextPtr
!= NULL
;
892 dlPtr2
= dlPtr2
->nextPtr
) {
893 /* Empty loop body. */
895 dlPtr2
->nextPtr
= dlPtr
;
900 * Skip to the next line, and update the y-position while
908 dlPtr
= dlPtr
->nextPtr
;
909 } while ((dlPtr
!= NULL
) && (dlPtr
->linePtr
== linePtr
));
910 linePtr
= TkBTreeNextLine(linePtr
);
915 * Delete any DLine structures that don't fit on the screen and free
919 FreeDLines(textPtr
, dlPtr
, (DLine
*) NULL
, 1);
920 if (tagInfo
.tagPtrs
!= NULL
) {
921 ckfree((char *) tagInfo
.tagPtrs
);
925 * Update the vertical scrollbar, if there is one.
928 if (textPtr
->yScrollCmd
!= NULL
) {
929 int numLines
, first
, result
, maxY
, height
;
933 * Count the number of text lines on the screen.
937 for (numLines
= 0, linePtr
= NULL
, dlPtr
= dInfoPtr
->dLinePtr
;
938 dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
939 if (dlPtr
->linePtr
!= linePtr
) {
941 linePtr
= dlPtr
->linePtr
;
943 maxY
= dlPtr
->y
+ dlPtr
->height
;
947 * If the screen isn't completely full, then estimate the number of
948 * lines that would fit on it if it were full.
951 height
= dInfoPtr
->maxY
- dInfoPtr
->y
;
954 (textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
);
955 } else if (maxY
< height
) {
956 numLines
= (numLines
* height
)/maxY
;
958 /* DEH: be reasonable if dLinePtr is null */
959 if (dInfoPtr
->dLinePtr
== NULL
) {
960 sprintf(string
, " 0 0 0 0");
962 first
= TkBTreeLineIndex(dInfoPtr
->dLinePtr
->linePtr
);
963 sprintf(string
, " %d %d %d %d", TkBTreeNumLines(textPtr
->tree
),
964 numLines
, first
, first
+numLines
-1);
966 result
= Tcl_VarEval(textPtr
->interp
, textPtr
->yScrollCmd
, string
,
968 if (result
!= TCL_OK
) {
969 TkBindError(textPtr
->interp
);
975 *----------------------------------------------------------------------
979 * This procedure is called to free up all of the resources
980 * associated with one or more DLine structures.
986 * Memory gets freed and various other resources are released.
988 *----------------------------------------------------------------------
993 TkText
*textPtr
, /* Information about overall text
995 register DLine
*firstPtr
, /* Pointer to first DLine to free up. */
996 DLine
*lastPtr
, /* Pointer to DLine just after last
997 * one to free (NULL means everything
998 * starting with firstPtr). */
999 int unlink
/* 1 means DLines are currently linked
1000 * into the list rooted at
1001 * textPtr->dInfoPtr->dLinePtr and
1002 * they have to be unlinked. 0 means
1003 * just free without unlinking. */
1006 register Chunk
*chunkPtr
, *nextChunkPtr
;
1007 register DLine
*nextDLinePtr
;
1010 if (textPtr
->dInfoPtr
->dLinePtr
== firstPtr
) {
1011 textPtr
->dInfoPtr
->dLinePtr
= lastPtr
;
1013 register DLine
*prevPtr
;
1014 for (prevPtr
= textPtr
->dInfoPtr
->dLinePtr
;
1015 prevPtr
->nextPtr
!= firstPtr
; prevPtr
= prevPtr
->nextPtr
) {
1016 /* Empty loop body. */
1018 prevPtr
->nextPtr
= lastPtr
;
1021 while (firstPtr
!= lastPtr
) {
1022 nextDLinePtr
= firstPtr
->nextPtr
;
1023 for (chunkPtr
= firstPtr
->chunkPtr
; chunkPtr
!= NULL
;
1024 chunkPtr
= nextChunkPtr
) {
1025 FreeStyle(chunkPtr
->stylePtr
);
1026 nextChunkPtr
= chunkPtr
->nextPtr
;
1027 ckfree((char *) chunkPtr
);
1029 ckfree((char *) firstPtr
);
1030 firstPtr
= nextDLinePtr
;
1035 *----------------------------------------------------------------------
1039 * This procedure is invoked to draw a single line on the
1046 * The line given by dlPtr is drawn at its correct position in
1047 * textPtr's window. Note that this is one *display* line, not
1050 *----------------------------------------------------------------------
1055 TkText
*textPtr
, /* Text widget in which to draw line. */
1056 register DLine
*dlPtr
, /* Information about line to draw. */
1057 Pixmap pixmap
/* Pixmap to use for double-buffering.
1058 * Caller must make sure it's large enough
1062 register Style
*stylePtr
;
1063 register StyleValues
*sValuePtr
;
1064 register Chunk
*chunkPtr
;
1065 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1067 int width
, height
, count
, x
;
1068 XFontStruct
*fontPtr
;
1071 * First, clear the area of the line to the background color for the
1075 display
= Tk_Display(textPtr
->tkwin
);
1076 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->border
, 0, 0,
1077 Tk_Width(textPtr
->tkwin
), dlPtr
->height
, 0, TK_RELIEF_FLAT
);
1080 * Next, cycle through all of the chunks in the line displaying
1081 * backgrounds. We need to do two passes, one for the backgrounds
1082 * and one for the characters, because some characters (e.g. italics
1083 * with heavy slants) may cross background boundaries. If some
1084 * backgrounds are drawn after some text, the later backgrounds may
1085 * obliterate parts of earlier characters.
1088 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1089 chunkPtr
= chunkPtr
->nextPtr
) {
1092 * Draw a special background for this chunk if one is specified
1093 * in its style. Two tricks here:
1094 * 1. if this is the last chunk in the line then extend the
1095 * background across to the end of the line.
1096 * 2. if the background is stippled, then we have to draw the
1097 * stippled part specially, since Tk_Fill3DRectangle doesn't
1101 stylePtr
= chunkPtr
->stylePtr
;
1102 sValuePtr
= stylePtr
->sValuePtr
;
1103 if (sValuePtr
->border
!= NULL
) {
1104 if (chunkPtr
->nextPtr
!= NULL
) {
1105 width
= chunkPtr
->nextPtr
->x
- chunkPtr
->x
;
1107 width
= Tk_Width(textPtr
->tkwin
) - chunkPtr
->x
;
1109 if (stylePtr
->bgGC
!= NULL
) {
1110 XFillRectangle(display
, pixmap
, stylePtr
->bgGC
, chunkPtr
->x
,
1111 0, (unsigned int) width
, (unsigned int) dlPtr
->height
);
1112 Tk_Draw3DRectangle(display
, pixmap
, sValuePtr
->border
,
1113 chunkPtr
->x
, 0, width
, dlPtr
->height
,
1114 sValuePtr
->borderWidth
, sValuePtr
->relief
);
1116 Tk_Fill3DRectangle(display
, pixmap
, sValuePtr
->border
,
1117 chunkPtr
->x
, 0, width
, dlPtr
->height
,
1118 sValuePtr
->borderWidth
, sValuePtr
->relief
);
1124 * If the insertion cursor is displayed on this line, then draw it
1125 * now, on top of the background but before the text. As a special
1126 * workaround to keep the cursor visible on mono displays, write the default
1127 * background in the cursor area (instead of nothing) when the cursor
1128 * isn't on. Otherwise the selection would hide the cursor.
1131 if ((textPtr
->insertAnnotPtr
->linePtr
== dlPtr
->linePtr
)
1132 && (textPtr
->state
== tkTextNormalUid
)
1133 && (textPtr
->flags
& GOT_FOCUS
)) {
1134 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1135 chunkPtr
= chunkPtr
->nextPtr
) {
1136 count
= textPtr
->insertAnnotPtr
->ch
1137 - (chunkPtr
->text
- dlPtr
->linePtr
->bytes
);
1141 if (count
> chunkPtr
->numChars
) {
1146 * Deciding whether to display the cursor just after the last
1147 * character in a line is tricky because of various wrap
1148 * modes. Do it unless we're in character wrap mode and
1149 * this line wraps, in which case it's better to display the
1150 * cursor on the next line. For word wrap, there's an
1151 * undisplayed space character that the user must be able to
1152 * position the cursor in front of. For no wrap, there's no
1153 * next line on which to display the cursor.
1155 if ((count
== chunkPtr
->numChars
)
1156 && (textPtr
->wrapMode
== tkTextCharUid
)
1157 && (chunkPtr
->text
[count
] != '\n')) {
1160 fontPtr
= chunkPtr
->stylePtr
->sValuePtr
->fontPtr
;
1161 TkMeasureChars(fontPtr
, chunkPtr
->text
, count
, chunkPtr
->x
,
1162 (int) 1000000, 0, &x
);
1163 if (textPtr
->flags
& INSERT_ON
) {
1164 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->insertBorder
,
1165 x
- textPtr
->insertWidth
/2,
1166 dlPtr
->baseline
- fontPtr
->ascent
,
1167 textPtr
->insertWidth
,
1168 fontPtr
->ascent
+ fontPtr
->descent
,
1169 textPtr
->insertBorderWidth
, TK_RELIEF_RAISED
);
1170 } else if (Tk_DefaultDepth(Tk_Screen(textPtr
->tkwin
)) == 1) {
1171 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->border
,
1172 x
- textPtr
->insertWidth
/2,
1173 dlPtr
->baseline
- fontPtr
->ascent
,
1174 textPtr
->insertWidth
,
1175 fontPtr
->ascent
+ fontPtr
->descent
,
1183 * Make another pass through all of the chunks to redraw all of
1184 * the text (and underlines, etc., if they're wanted).
1187 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1188 chunkPtr
= chunkPtr
->nextPtr
) {
1189 stylePtr
= chunkPtr
->stylePtr
;
1190 sValuePtr
= stylePtr
->sValuePtr
;
1191 if (chunkPtr
->numChars
> 0) {
1192 TkDisplayChars(display
, pixmap
, stylePtr
->fgGC
, sValuePtr
->fontPtr
,
1193 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
,
1194 dlPtr
->baseline
, 0);
1195 if (sValuePtr
->underline
) {
1196 TkUnderlineChars(display
, pixmap
, stylePtr
->fgGC
,
1197 sValuePtr
->fontPtr
, chunkPtr
->text
, chunkPtr
->x
,
1198 dlPtr
->baseline
, 0, 0, chunkPtr
->numChars
-1);
1204 * Copy the pixmap onto the screen. If this is the last line on
1205 * the screen, only copy a piece of the line, so that it doesn't
1206 * overflow into the border area. Another special trick: copy the
1207 * padding area to the left of the line; this is because the
1208 * insertion cursor sometimes overflows onto that area and we want
1209 * to get as much of the cursor as possible.
1212 height
= dlPtr
->height
;
1213 if ((height
+ dlPtr
->y
) > dInfoPtr
->maxY
) {
1214 height
= dInfoPtr
->maxY
- dlPtr
->y
;
1216 XCopyArea(display
, pixmap
, Tk_WindowId(textPtr
->tkwin
),
1217 dInfoPtr
->copyGC
, dInfoPtr
->x
- textPtr
->padX
, 0,
1218 dInfoPtr
->maxX
- (dInfoPtr
->x
- textPtr
->padX
),
1219 height
, dInfoPtr
->x
- textPtr
->padX
, dlPtr
->y
);
1224 *----------------------------------------------------------------------
1228 * This procedure is invoked as a when-idle handler to update the
1229 * display. It only redisplays the parts of the text widget that
1236 * Information is redrawn on the screen.
1238 *----------------------------------------------------------------------
1243 ClientData clientData
/* Information about widget. */
1246 register TkText
*textPtr
= (TkText
*) clientData
;
1247 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1249 register DLine
*dlPtr
;
1252 int bottomY
= 0; /* Initialization needed only to stop
1253 * compiler warnings. */
1255 assert(textPtr
->updateTimerToken
!= NULL
);
1257 textPtr
->updateTimerToken
= 0;
1259 if ((textPtr
->tkwin
== NULL
) || !Tk_IsMapped(textPtr
->tkwin
)
1260 || (dInfoPtr
->maxX
<= dInfoPtr
->x
)
1261 || (dInfoPtr
->maxY
<= dInfoPtr
->y
)) {
1267 * Choose a new current item if that is needed (this could cause
1268 * event handlers to be invoked, hence the preserve/release calls
1269 * and the loop, since the handlers could conceivably necessitate
1270 * yet another current item calculation). The tkwin check is because
1271 * the whole window could go away in the Tk_Release call.
1274 while (dInfoPtr
->flags
& REPICK_NEEDED
) {
1275 Tk_Preserve((ClientData
) textPtr
);
1276 dInfoPtr
->flags
&= ~REPICK_NEEDED
;
1277 TkTextPickCurrent(textPtr
, &textPtr
->pickEvent
);
1278 tkwin
= textPtr
->tkwin
;
1279 Tk_Release((ClientData
) textPtr
);
1280 if (tkwin
== NULL
) {
1286 * First recompute what's supposed to be displayed.
1289 UpdateDisplayInfo(textPtr
);
1292 * Redraw the borders if that's needed.
1295 if (dInfoPtr
->flags
& REDRAW_BORDERS
) {
1296 Tk_Draw3DRectangle(Tk_Display(textPtr
->tkwin
),
1297 Tk_WindowId(textPtr
->tkwin
), textPtr
->border
,
1298 0, 0, Tk_Width(textPtr
->tkwin
), Tk_Height(textPtr
->tkwin
),
1299 textPtr
->borderWidth
, textPtr
->relief
);
1303 * See if it's possible to bring some parts of the screen up-to-date
1304 * by scrolling (copying from other parts of the screen).
1307 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
1308 register DLine
*dlPtr2
;
1311 if ((dlPtr
->oldY
== -1) || (dlPtr
->y
== dlPtr
->oldY
)
1312 || ((dlPtr
->oldY
+ dlPtr
->height
) > dInfoPtr
->maxY
)) {
1317 * This line is already drawn somewhere in the window so it only
1318 * needs to be copied to its new location. See if there's a group
1319 * of lines that can all be copied together.
1322 offset
= dlPtr
->y
- dlPtr
->oldY
;
1323 height
= dlPtr
->height
;
1324 for (dlPtr2
= dlPtr
->nextPtr
; dlPtr2
!= NULL
;
1325 dlPtr2
= dlPtr2
->nextPtr
) {
1326 if ((dlPtr2
->oldY
== -1)
1327 || ((dlPtr2
->oldY
+ offset
) != dlPtr2
->y
)
1328 || ((dlPtr2
->oldY
+ dlPtr2
->height
) > dInfoPtr
->maxY
)) {
1331 height
+= dlPtr2
->height
;
1335 * Copy the information and update the lines to show that they've
1336 * been copied. Reduce the height of the area being copied if
1337 * necessary to avoid overwriting the border area.
1340 if ((dlPtr
->y
+ height
) > dInfoPtr
->maxY
) {
1341 height
= dInfoPtr
->maxY
- dlPtr
->y
;
1343 XCopyArea(Tk_Display(textPtr
->tkwin
), Tk_WindowId(textPtr
->tkwin
),
1344 Tk_WindowId(textPtr
->tkwin
), dInfoPtr
->scrollGC
,
1345 dInfoPtr
->x
- textPtr
->padX
, dlPtr
->oldY
,
1346 dInfoPtr
->maxX
- (dInfoPtr
->x
- textPtr
->padX
),
1347 height
, dInfoPtr
->x
- textPtr
->padX
, dlPtr
->y
);
1350 dlPtr
->oldY
= dlPtr
->y
;
1351 if (dlPtr
->nextPtr
== dlPtr2
) {
1354 dlPtr
= dlPtr
->nextPtr
;
1358 * It's possible that part of the area copied above was obscured.
1359 * To handle this situation, read expose-related events generated
1360 * during the XCopyArea operation.
1366 XWindowEvent(Tk_Display(textPtr
->tkwin
),
1367 Tk_WindowId(textPtr
->tkwin
), ExposureMask
, &event
);
1368 if (event
.type
== NoExpose
) {
1370 } else if (event
.type
== GraphicsExpose
) {
1371 TkTextRedrawRegion(textPtr
, event
.xgraphicsexpose
.x
,
1372 event
.xgraphicsexpose
.y
, event
.xgraphicsexpose
.width
,
1373 event
.xgraphicsexpose
.height
);
1374 if (event
.xgraphicsexpose
.count
== 0) {
1378 } else if (event
.type
== Expose
) {
1380 * A tricky situation. This event must already have been
1381 * queued up before the XCopyArea was issued. If the area
1382 * in this event overlaps the area copied, then some of the
1383 * bits that were copied were bogus. The easiest way to
1384 * handle this is to issue two redisplays: one for the
1385 * original area and one for the area shifted as if it was
1386 * in the copied area.
1389 TkTextRedrawRegion(textPtr
, event
.xexpose
.x
,
1390 event
.xexpose
.y
, event
.xexpose
.width
,
1391 event
.xexpose
.height
);
1392 TkTextRedrawRegion(textPtr
, event
.xexpose
.x
,
1393 event
.xexpose
.y
+ offset
, event
.xexpose
.width
,
1394 event
.xexpose
.height
);
1396 panic("DisplayText received unknown exposure event");
1402 * Now we have to redraw the lines that couldn't be updated by
1403 * scrolling. First, compute the height of the largest line and
1404 * allocate an off-screen pixmap to use for double-buffered
1409 for (dlPtr
= textPtr
->dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1410 dlPtr
= dlPtr
->nextPtr
) {
1411 if ((dlPtr
->height
> maxHeight
) && (dlPtr
->oldY
!= dlPtr
->y
)) {
1412 maxHeight
= dlPtr
->height
;
1414 bottomY
= dlPtr
->y
+ dlPtr
->height
;
1416 if (maxHeight
>= 0) {
1417 pixmap
= XCreatePixmap(Tk_Display(textPtr
->tkwin
),
1418 Tk_WindowId(textPtr
->tkwin
), Tk_Width(textPtr
->tkwin
),
1419 maxHeight
, Tk_DefaultDepth(Tk_Screen(textPtr
->tkwin
)));
1420 for (dlPtr
= textPtr
->dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1421 dlPtr
= dlPtr
->nextPtr
) {
1422 if (dlPtr
->oldY
!= dlPtr
->y
) {
1423 DisplayDLine(textPtr
, dlPtr
, pixmap
);
1424 dlPtr
->oldY
= dlPtr
->y
;
1427 XFreePixmap(Tk_Display(textPtr
->tkwin
), pixmap
);
1431 * Lastly, see if we need to refresh the part of the window below
1432 * the last line of text (if there is any such area).
1435 if (dInfoPtr
->topOfEof
> dInfoPtr
->maxY
) {
1436 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1438 if (bottomY
< dInfoPtr
->topOfEof
) {
1439 Tk_Fill3DRectangle(Tk_Display(textPtr
->tkwin
),
1440 Tk_WindowId(textPtr
->tkwin
), textPtr
->border
,
1441 dInfoPtr
->x
, bottomY
, dInfoPtr
->maxX
- dInfoPtr
->x
,
1442 dInfoPtr
->topOfEof
-bottomY
, 0, TK_RELIEF_FLAT
);
1444 dInfoPtr
->topOfEof
= bottomY
;
1445 if (dInfoPtr
->topOfEof
> dInfoPtr
->maxY
) {
1446 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1450 dInfoPtr
->flags
&= ~(REDRAW_PENDING
|REDRAW_BORDERS
);
1454 *----------------------------------------------------------------------
1456 * TkTextRedrawRegion --
1458 * This procedure is invoked to schedule a redisplay for a given
1459 * region of a text widget. The redisplay itself may not occur
1460 * immediately: it's scheduled as a when-idle handler.
1466 * Information will eventually be redrawn on the screen.
1468 *----------------------------------------------------------------------
1473 TkTextRedrawRegion (
1474 TkText
*textPtr
, /* Widget record for text widget. */
1476 int y
, /* Coordinates of upper-left corner of area
1477 * to be redrawn, in pixels relative to
1478 * textPtr's window. */
1480 int height
/* Width and height of area to be redrawn. */
1483 register DLine
*dlPtr
;
1484 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1488 * Find all lines that overlap the given region and mark them for
1493 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1494 dlPtr
= dlPtr
->nextPtr
) {
1495 if (((dlPtr
->y
+ dlPtr
->height
) > y
) && (dlPtr
->y
< maxY
)) {
1499 if (dInfoPtr
->topOfEof
< maxY
) {
1500 dInfoPtr
->topOfEof
= maxY
;
1504 * Schedule the redisplay operation if there isn't one already
1508 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1509 dInfoPtr
->flags
|= REDRAW_PENDING
;
1510 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1511 assert(textPtr
->updateTimerToken
== NULL
);
1512 if (textPtr
->updateTimerToken
== NULL
) {
1513 textPtr
->updateTimerToken
=
1514 Tk_CreateTimerHandler(
1517 (ClientData
) textPtr
);
1520 if ((x
< dInfoPtr
->x
) || (y
< dInfoPtr
->y
)
1521 || ((x
+ width
) > dInfoPtr
->maxX
) || (maxY
> dInfoPtr
->maxY
)) {
1522 dInfoPtr
->flags
|= REDRAW_BORDERS
;
1527 *----------------------------------------------------------------------
1529 * TkTextLinesChanged --
1531 * This procedure is invoked when lines in a text widget are about
1532 * to be modified in a way that changes how they are displayed (e.g.
1533 * characters were inserted, the line was deleted, or tag information
1534 * was changed). This procedure must be called *before* a change is
1535 * made, so that pointers to TkTextLines in the display information
1542 * The indicated lines will be redisplayed at some point in the
1543 * future (the actual redisplay is scheduled as a when-idle handler).
1545 *----------------------------------------------------------------------
1549 TkTextLinesChanged (
1550 TkText
*textPtr
, /* Widget record for text widget. */
1551 int first
, /* Index of first line that must be
1553 int last
/* Index of last line to redisplay. */
1556 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1557 DLine
*firstPtr
, *lastPtr
;
1560 * Find the DLines corresponding to first and last+1.
1563 firstPtr
= FindDLine(dInfoPtr
->dLinePtr
, first
);
1564 if (firstPtr
== NULL
) {
1567 lastPtr
= FindDLine(dInfoPtr
->dLinePtr
, last
+1);
1568 if (firstPtr
== lastPtr
) {
1573 * Delete all the DLines from first up through last (but not including
1574 * lastPtr, which points to the first line *outside* the range).
1577 FreeDLines(textPtr
, firstPtr
, lastPtr
, 1);
1580 * Schedule both a redisplay and a recomputation of display information.
1583 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1584 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1585 assert(textPtr
->updateTimerToken
== NULL
);
1586 if (textPtr
->updateTimerToken
== NULL
) {
1587 textPtr
->updateTimerToken
=
1588 Tk_CreateTimerHandler(
1591 (ClientData
) textPtr
);
1594 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1598 *----------------------------------------------------------------------
1600 * TkTextRedrawTag --
1602 * This procedure is invoked to request a redraw of all characters
1603 * in a given range of characters that have a particular tag on or
1604 * off. It's called, for example, when characters are tagged or
1605 * untagged, or when tag options change.
1611 * Information on the screen may be redrawn, and the layout of
1612 * the screen may change.
1614 *----------------------------------------------------------------------
1619 TkText
*textPtr
, /* Widget record for text widget. */
1621 int ch1
, /* Index of first character in range of
1624 int ch2
, /* Index of character just after last one
1625 * in range of interest. */
1626 TkTextTag
*tagPtr
, /* Information about tag. */
1627 int withTag
/* 1 means redraw characters that have the
1628 * tag, 0 means redraw those without. */
1631 register DLine
*dlPtr
;
1634 TkTextSearch search
;
1635 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1638 * Round up the starting position if it's before the first line
1639 * visible on the screen (we only care about what's on the screen).
1642 dlPtr
= dInfoPtr
->dLinePtr
;
1643 if (dlPtr
== NULL
) {
1646 topLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1647 if (topLine
> line1
) {
1653 * Initialize a search through all transitions on the tag, starting
1654 * with the first transition where the tag's current state is different
1655 * from what it will eventually be.
1658 TkBTreeStartSearch(textPtr
->tree
, line1
, ch1
+1, line2
, ch2
,
1660 tagOn
= TkBTreeCharTagged(search
.linePtr
, ch1
, tagPtr
);
1661 if (tagOn
!= withTag
) {
1662 if (!TkBTreeNextTag(&search
)) {
1668 * Each loop through the loop below is for one range of characters
1669 * where the tag's current state is different than its eventual
1670 * state. At the top of the loop, search contains information about
1671 * the first character in the range.
1676 * Find the first DLine structure in the range.
1679 dlPtr
= FindDLine(dlPtr
, search
.line1
);
1680 if (dlPtr
== NULL
) {
1685 * Find the first DLine structure that's past the end of the range.
1688 if (TkBTreeNextTag(&search
)) {
1689 endPtr
= FindDLine(dlPtr
,
1690 (search
.ch1
> 0) ? (search
.line1
+ 1) : search
.line1
);
1692 endPtr
= FindDLine(dlPtr
,
1693 (ch2
> 0) ? (search
.line2
+ 1) : search
.line2
);
1697 * Delete all of the display lines in the range, so that they'll
1698 * be re-layed out and redrawn.
1701 FreeDLines(textPtr
, dlPtr
, endPtr
, 1);
1705 * Find the first text line in the next range.
1708 if (!TkBTreeNextTag(&search
)) {
1714 * Lastly, schedule a redisplay and layout recalculation if they
1715 * aren't already pending.
1718 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1719 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1720 assert(textPtr
->updateTimerToken
== NULL
);
1721 if (textPtr
->updateTimerToken
== NULL
) {
1722 textPtr
->updateTimerToken
=
1723 Tk_CreateTimerHandler(
1726 (ClientData
) textPtr
);
1729 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1733 *----------------------------------------------------------------------
1735 * TkTextRelayoutWindow --
1737 * This procedure is called when something has happened that
1738 * invalidates the whole layout of characters on the screen, such
1739 * as a change in a configuration option for the overall text
1740 * widget or a change in the window size. It causes all display
1741 * information to be recomputed and the window to be redrawn.
1747 * All the display information will be recomputed for the window
1748 * and the window will be redrawn.
1750 *----------------------------------------------------------------------
1754 TkTextRelayoutWindow (
1755 TkText
*textPtr
/* Widget record for text widget. */
1758 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1761 * Throw away all the current layout information.
1764 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, (DLine
*) NULL
, 1);
1765 dInfoPtr
->dLinePtr
= NULL
;
1768 * Recompute some overall things for the layout.
1771 dInfoPtr
->x
= textPtr
->borderWidth
+ textPtr
->padX
;
1772 dInfoPtr
->y
= textPtr
->borderWidth
+ textPtr
->padY
;
1773 dInfoPtr
->maxX
= Tk_Width(textPtr
->tkwin
) - dInfoPtr
->x
;
1774 dInfoPtr
->maxY
= Tk_Height(textPtr
->tkwin
) - dInfoPtr
->y
;
1775 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1777 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1778 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1779 assert(textPtr
->updateTimerToken
== NULL
);
1780 if (textPtr
->updateTimerToken
== NULL
) {
1781 textPtr
->updateTimerToken
=
1782 Tk_CreateTimerHandler(
1785 (ClientData
) textPtr
);
1788 dInfoPtr
->flags
|= REDRAW_PENDING
| REDRAW_BORDERS
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1792 *----------------------------------------------------------------------
1796 * This procedure is called to specify what lines are to be
1797 * displayed in a text widget.
1803 * The display will (eventually) be updated so that the line
1804 * given by "line" is visible on the screen at the position
1805 * determined by "pickPlace".
1807 *----------------------------------------------------------------------
1812 TkText
*textPtr
, /* Widget record for text widget. */
1813 int line
, /* Number of line that is to appear somewhere
1814 * in the window. This line number must
1815 * be a valid one in the file. */
1816 int pickPlace
/* 0 means topLine must appear at top of
1817 * screen. 1 means we get to pick where it
1818 * appears: minimize screen motion or else
1819 * display line at center of screen. */
1822 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1823 register DLine
*dlPtr
, *dlPtr2
;
1824 TkTextLine
*linePtr
;
1825 int curTopLine
, curBotLine
;
1828 #define CLOSE_LINES 5
1832 * The line must go at the top of the screen. See if the new
1833 * topmost line is already somewhere on the screen. If so then
1834 * delete all the DLine structures ahead of it. Otherwise just
1835 * leave all the DLine's alone (if the new topmost line is above
1836 * the top of the current window, i.e. we're scrolling back towards
1837 * the beginning of the file we may be able to reuse some of the
1838 * information that's currently on the screen without redisplaying
1842 dlPtr
= FindDLine(dInfoPtr
->dLinePtr
, line
);
1843 if ((dlPtr
!= NULL
) && (dlPtr
!= dInfoPtr
->dLinePtr
)) {
1844 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, dlPtr
, 1);
1847 textPtr
->topLinePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1848 goto scheduleUpdate
;
1852 * We have to pick where to display the given line. First, bring
1853 * the display information up to date and see if the line will be
1854 * completely visible in the current screen configuration. If so
1855 * then there's nothing to do.
1858 if (dInfoPtr
->flags
& DINFO_OUT_OF_DATE
) {
1859 UpdateDisplayInfo(textPtr
);
1861 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1862 /* DEH: return if dlPtr is null */
1863 if ((dlPtr
= dInfoPtr
->dLinePtr
) == NULL
)
1865 for (; ; dlPtr
= dlPtr
->nextPtr
) {
1866 if (dlPtr
->nextPtr
== NULL
) {
1869 if ((dlPtr
->linePtr
== linePtr
)
1870 && (dlPtr
->nextPtr
->linePtr
!= linePtr
)) {
1874 if ((dlPtr
->linePtr
== linePtr
)
1875 && ((dlPtr
->y
+ dlPtr
->height
) <= dInfoPtr
->maxY
)) {
1880 * The desired line isn't already on-screen. See if it is within
1881 * a few lines of the top of the window. If so then just make it
1882 * the top line on the screen.
1885 bottomY
= (dInfoPtr
->y
+ dInfoPtr
->maxY
)/2;
1886 curTopLine
= TkBTreeLineIndex(dInfoPtr
->dLinePtr
->linePtr
);
1887 if (line
< curTopLine
) {
1888 if (line
>= (curTopLine
-CLOSE_LINES
)) {
1889 textPtr
->topLinePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1890 goto scheduleUpdate
;
1894 * The desired line is below the bottom of the screen. If it is
1895 * within a few lines of the bottom of the screen then position
1896 * it at the bottom of the screen. (At this point dlPtr points to
1897 * the last line on the screen)
1900 curBotLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1901 if (line
<= (curBotLine
+5)) {
1902 bottomY
= dInfoPtr
->maxY
;
1907 * Our job now is arrange the display so that "line" appears as
1908 * low on the screen as possible but with its bottom no lower
1909 * than bottomY (bottomY is the bottom of the window if the
1910 * desired line is just below the current screen, otherwise it
1911 * is the center of the window. Work upwards (through smaller
1912 * line numbers) computing how much space lines take, until we
1913 * fine the line that should be at the top of the screen.
1916 for (textPtr
->topLinePtr
= linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1917 ; line
--, textPtr
->topLinePtr
= linePtr
,
1918 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
)) {
1919 tagInfo
.tagPtrs
= TkBTreeGetTags(textPtr
->tree
, linePtr
, 0,
1921 tagInfo
.arraySize
= tagInfo
.numTags
;
1922 TkBTreeStartSearch(textPtr
->tree
, line
, 1, line
+1, 0,
1923 (TkTextTag
*) NULL
, &tagInfo
.search
);
1924 TkBTreeNextTag(&tagInfo
.search
);
1925 dlPtr
= LayoutLine(textPtr
, line
, linePtr
, &tagInfo
);
1926 for (dlPtr2
= dlPtr
; dlPtr2
!= NULL
; dlPtr2
= dlPtr2
->nextPtr
) {
1927 bottomY
-= dlPtr2
->height
;
1929 FreeDLines(textPtr
, dlPtr
, (DLine
*) NULL
, 0);
1930 if (tagInfo
.tagPtrs
!= NULL
) {
1931 ckfree((char *) tagInfo
.tagPtrs
);
1933 if ((bottomY
<= 0) || (line
<= 0)) {
1939 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1940 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1941 assert(textPtr
->updateTimerToken
== NULL
);
1942 if (textPtr
->updateTimerToken
== NULL
) {
1943 textPtr
->updateTimerToken
=
1944 Tk_CreateTimerHandler(
1947 (ClientData
) textPtr
);
1950 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1954 *----------------------------------------------------------------------
1958 * This procedure is called to find the DLine corresponding to a
1962 * The return value is a pointer to the first DLine found in the
1963 * list headed by dlPtr whose line number is greater or equal to
1964 * line. If there is no such line in the list then NULL is returned.
1969 *----------------------------------------------------------------------
1974 register DLine
*dlPtr
, /* Pointer to first in list of DLines
1976 int line
/* Line number in text that is desired. */
1979 TkTextLine
*linePtr
;
1982 if (dlPtr
== NULL
) {
1985 thisLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1986 while (thisLine
< line
) {
1988 * This DLine isn't the right one. Go on to the next DLine
1989 * (skipping multiple DLine's for the same text line).
1992 linePtr
= dlPtr
->linePtr
;
1994 dlPtr
= dlPtr
->nextPtr
;
1995 if (dlPtr
== NULL
) {
1998 } while (dlPtr
->linePtr
== linePtr
);
2001 * Step through text lines, keeping track of the line number
2002 * we're on, until we catch up to dlPtr (remember, there could
2003 * be gaps in the DLine list where DLine's have been deleted).
2007 linePtr
= TkBTreeNextLine(linePtr
);
2009 if (linePtr
== NULL
) {
2010 panic("FindDLine reached end of text");
2012 } while (linePtr
!= dlPtr
->linePtr
);
2018 *----------------------------------------------------------------------
2020 * TkTextCharAtLoc --
2022 * Given an (x,y) coordinate on the screen, find the location of
2023 * the closest character to that location.
2026 * The return value is a pointer to the text line containing the
2027 * character displayed closest to (x,y). The value at *chPtr is
2028 * overwritten with the index with that line of the closest
2034 *----------------------------------------------------------------------
2039 TkText
*textPtr
, /* Widget record for text widget. */
2041 int y
, /* Pixel coordinates of point in widget's
2043 int *chPtr
/* Place to store index-within-line of
2044 * closest character. */
2047 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
2048 register DLine
*dlPtr
;
2049 register Chunk
*chunkPtr
;
2054 * Make sure that all of the layout information about what's
2055 * displayed where on the screen is up-to-date.
2058 if (dInfoPtr
->flags
& DINFO_OUT_OF_DATE
) {
2059 UpdateDisplayInfo(textPtr
);
2063 * If the coordinates are above the top of the window, then adjust
2064 * them to refer to the upper-right corner of the window.
2067 if (y
< dInfoPtr
->y
) {
2070 } else if (y
>= dInfoPtr
->topOfEof
) {
2071 y
= dInfoPtr
->topOfEof
;
2074 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
2075 if (y
> (dlPtr
->y
+ dlPtr
->height
)) {
2076 if (dlPtr
->nextPtr
!= NULL
) {
2081 * The coordinates are off the bottom of the window. Adjust
2082 * them to refer to the lower-right character on the window.
2088 for (chunkPtr
= dlPtr
->chunkPtr
; ; chunkPtr
= chunkPtr
->nextPtr
) {
2089 if ((chunkPtr
->nextPtr
== NULL
) || (chunkPtr
->nextPtr
->x
> x
)) {
2093 count
= TkMeasureChars(chunkPtr
->stylePtr
->sValuePtr
->fontPtr
,
2094 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
, x
, 0, &endX
);
2095 if (count
>= chunkPtr
->numChars
) {
2097 * The point is off the end of the line. Return the character
2098 * after the last one that fit, unless that character appears
2099 * as the first character on the next DLine or unless the last
2100 * one that fit extends beyond the edge of the window.
2103 if ((dlPtr
->nextPtr
!= NULL
)
2104 && (dlPtr
->nextPtr
->chunkPtr
->text
2105 == (chunkPtr
->text
+ chunkPtr
->numChars
))) {
2106 count
= chunkPtr
->numChars
-1;
2108 if (endX
>= dInfoPtr
->maxX
) {
2109 count
= chunkPtr
->numChars
-1;
2112 *chPtr
= count
+ (chunkPtr
->text
- dlPtr
->linePtr
->bytes
);
2113 return dlPtr
->linePtr
;
2115 panic("TkTextCharAtLoc ran out of lines");
2116 return (TkTextLine
*) NULL
;