]>
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 *----------------------------------------------------------------------
251 TkTextCreateDInfo(textPtr
)
252 TkText
*textPtr
; /* Overall information for text widget. */
254 register DInfo
*dInfoPtr
;
257 dInfoPtr
= (DInfo
*) ckalloc(sizeof(DInfo
));
258 Tcl_InitHashTable(&dInfoPtr
->styleTable
, sizeof(StyleValues
)/sizeof(int));
259 dInfoPtr
->dLinePtr
= NULL
;
260 gcValues
.graphics_exposures
= False
;
261 dInfoPtr
->copyGC
= Tk_GetGC(textPtr
->tkwin
, GCGraphicsExposures
, &gcValues
);
262 gcValues
.graphics_exposures
= True
;
263 dInfoPtr
->scrollGC
= Tk_GetGC(textPtr
->tkwin
, GCGraphicsExposures
,
265 dInfoPtr
->topOfEof
= 0;
266 dInfoPtr
->flags
= DINFO_OUT_OF_DATE
;
267 textPtr
->dInfoPtr
= dInfoPtr
;
271 *----------------------------------------------------------------------
275 * This procedure is called to free up all of the private display
276 * information kept by this file for a text widget.
282 * Lots of resources get freed.
284 *----------------------------------------------------------------------
288 TkTextFreeDInfo(textPtr
)
289 TkText
*textPtr
; /* Overall information for text widget. */
291 register DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
294 * Be careful to free up styleTable *after* freeing up all the
295 * DLines, so that the hash table is still intact to free up the
296 * style-related information from the lines. Once the lines are
297 * all free then styleTable will be empty.
300 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, (DLine
*) NULL
, 1);
301 Tcl_DeleteHashTable(&dInfoPtr
->styleTable
);
302 Tk_FreeGC(dInfoPtr
->copyGC
);
303 Tk_FreeGC(dInfoPtr
->scrollGC
);
304 if (dInfoPtr
->flags
& REDRAW_PENDING
) {
305 // Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
306 assert(textPtr
->updateTimerToken
!= NULL
);
307 if (textPtr
->updateTimerToken
!= NULL
) {
308 Tk_DeleteTimerHandler(textPtr
->updateTimerToken
);
309 textPtr
->updateTimerToken
= NULL
;
312 ckfree((char *) dInfoPtr
);
316 *----------------------------------------------------------------------
320 * This procedure creates graphics contexts needed to display
321 * text in a particular style, determined by "sValuePtr". It
322 * attempts to share style information as much as possible.
325 * The return value is a pointer to a Style structure that
326 * corresponds to *sValuePtr.
329 * A new entry may be created in the style table for the widget.
331 *----------------------------------------------------------------------
335 GetStyle(textPtr
, sValuePtr
)
336 TkText
*textPtr
; /* Overall information about text widget. */
337 StyleValues
*sValuePtr
; /* Information about desired style. */
346 * Use an existing style if there's one around that matches.
349 hPtr
= Tcl_CreateHashEntry(&textPtr
->dInfoPtr
->styleTable
,
350 (char *) sValuePtr
, &new);
352 stylePtr
= (Style
*) Tcl_GetHashValue(hPtr
);
353 stylePtr
->refCount
++;
358 * No existing style matched. Make a new one.
361 stylePtr
= (Style
*) ckalloc(sizeof(Style
));
362 stylePtr
->refCount
= 1;
363 if ((sValuePtr
->border
!= NULL
) && (sValuePtr
->bgStipple
!= None
)) {
364 gcValues
.foreground
= Tk_3DBorderColor(sValuePtr
->border
)->pixel
;
365 gcValues
.stipple
= sValuePtr
->bgStipple
;
366 gcValues
.fill_style
= FillStippled
;
367 stylePtr
->bgGC
= Tk_GetGC(textPtr
->tkwin
,
368 GCForeground
|GCStipple
|GCFillStyle
, &gcValues
);
370 stylePtr
->bgGC
= None
;
372 mask
= GCForeground
|GCFont
;
373 gcValues
.foreground
= sValuePtr
->fgColor
->pixel
;
374 gcValues
.font
= sValuePtr
->fontPtr
->fid
;
375 if (sValuePtr
->fgStipple
!= None
) {
376 gcValues
.stipple
= sValuePtr
->fgStipple
;
377 gcValues
.fill_style
= FillStippled
;
378 mask
|= GCStipple
|GCFillStyle
;
380 stylePtr
->fgGC
= Tk_GetGC(textPtr
->tkwin
, mask
, &gcValues
);
381 stylePtr
->sValuePtr
= (StyleValues
*)
382 Tcl_GetHashKey(&textPtr
->dInfoPtr
->styleTable
, hPtr
);
383 stylePtr
->hPtr
= hPtr
;
384 Tcl_SetHashValue(hPtr
, stylePtr
);
389 *----------------------------------------------------------------------
393 * This procedure is called when a Style structure is no longer
394 * needed. It decrements the reference count and frees up the
395 * space for the style structure if the reference count is 0.
401 * The storage and other resources associated with the style
402 * are freed up if no-one's still using it.
404 *----------------------------------------------------------------------
409 register Style
*stylePtr
; /* Information about style to be freed. */
412 stylePtr
->refCount
--;
413 if (stylePtr
->refCount
== 0) {
414 if (stylePtr
->bgGC
!= None
) {
415 Tk_FreeGC(stylePtr
->bgGC
);
417 Tk_FreeGC(stylePtr
->fgGC
);
418 Tcl_DeleteHashEntry(stylePtr
->hPtr
);
419 ckfree((char *) stylePtr
);
424 *----------------------------------------------------------------------
426 * ComputeStyleValues --
428 * Given a list of tags that apply at a particular point, compute
429 * the StyleValues that correspond to that set of tags.
432 * All of the fields of *sValuePtr get filled in to hold the
433 * appropriate display information for the given set of tags
434 * in the given widget.
439 *----------------------------------------------------------------------
443 ComputeStyleValues(textPtr
, numTags
, tagPtrPtr
, sValuePtr
)
444 TkText
*textPtr
; /* Overall information for widget. */
445 int numTags
; /* Number of tags at *tagPtr. */
446 register TkTextTag
**tagPtrPtr
; /* Pointer to array of tag pointers. */
447 register StyleValues
*sValuePtr
; /* Pointer to structure to fill in. */
449 register TkTextTag
*tagPtr
;
452 * The variables below keep track of the highest-priority specification
453 * that has occurred for each of the various fields of the StyleValues.
456 int borderPrio
, bgStipplePrio
;
457 int fgPrio
, fontPrio
, fgStipplePrio
;
459 borderPrio
= bgStipplePrio
= -1;
460 fgPrio
= fontPrio
= fgStipplePrio
= -1;
461 memset((VOID
*) sValuePtr
, 0, sizeof(StyleValues
));
462 sValuePtr
->fgColor
= textPtr
->fgColor
;
463 sValuePtr
->fontPtr
= textPtr
->fontPtr
;
466 * Scan through all of the tags, updating the StyleValues to hold
467 * the highest-priority information.
470 for ( ; numTags
> 0; tagPtrPtr
++, numTags
--) {
472 if ((tagPtr
->border
!= NULL
) && (tagPtr
->priority
> borderPrio
)) {
473 sValuePtr
->border
= tagPtr
->border
;
474 sValuePtr
->borderWidth
= tagPtr
->borderWidth
;
475 sValuePtr
->relief
= tagPtr
->relief
;
476 borderPrio
= tagPtr
->priority
;
478 if ((tagPtr
->bgStipple
!= None
)
479 && (tagPtr
->priority
> bgStipplePrio
)) {
480 sValuePtr
->bgStipple
= tagPtr
->bgStipple
;
481 bgStipplePrio
= tagPtr
->priority
;
483 if ((tagPtr
->fgColor
!= None
) && (tagPtr
->priority
> fgPrio
)) {
484 sValuePtr
->fgColor
= tagPtr
->fgColor
;
485 fgPrio
= tagPtr
->priority
;
487 if ((tagPtr
->fontPtr
!= None
) && (tagPtr
->priority
> fontPrio
)) {
488 sValuePtr
->fontPtr
= tagPtr
->fontPtr
;
489 fontPrio
= tagPtr
->priority
;
491 if ((tagPtr
->fgStipple
!= None
)
492 && (tagPtr
->priority
> fgStipplePrio
)) {
493 sValuePtr
->fgStipple
= tagPtr
->fgStipple
;
494 fgStipplePrio
= tagPtr
->priority
;
496 if (tagPtr
->underline
) {
497 sValuePtr
->underline
= 1;
503 *----------------------------------------------------------------------
507 * This procedure generates a linked list of one or more DLine
508 * structures, which describe how to display everything in one
512 * The return value is a pointer to one or more DLine structures
513 * linked into a linked list. The structures are completely filled
514 * in except for the y field, which the caller must supply. Also,
515 * the information at *tInfoPtr gets updated to refer to the state
516 * just after the last character of the line.
521 *----------------------------------------------------------------------
525 LayoutLine(textPtr
, line
, linePtr
, tInfoPtr
)
526 TkText
*textPtr
; /* Overall information about text widget. */
527 int line
; /* Index of line to layout. */
528 TkTextLine
*linePtr
; /* Line to layout (corresponds to line). */
529 TagInfo
*tInfoPtr
; /* Information to help keep track of tags.
530 * Caller must have initialized to correspond
531 * to state just before start of line. */
534 DLine
*lastLinePtr
= NULL
; /* Initializations needed only to stop */
535 Chunk
*lastChunkPtr
= NULL
; /* compiler warnings. */
536 register DLine
*dlPtr
;
537 register Chunk
*chunkPtr
;
538 StyleValues styleValues
;
539 int ch
, charsThatFit
, ascent
, descent
, x
, maxX
;
544 * Each iteration of the loop below creates one DLine structure.
551 * Create and initialize a new DLine structure.
554 dlPtr
= (DLine
*) ckalloc(sizeof(DLine
));
555 dlPtr
->linePtr
= linePtr
;
558 dlPtr
->chunkPtr
= NULL
;
559 dlPtr
->nextPtr
= NULL
;
560 if (firstLinePtr
== NULL
) {
561 firstLinePtr
= dlPtr
;
563 lastLinePtr
->nextPtr
= dlPtr
;
568 * Each iteration of the loop below creates one Chunk for the
572 x
= textPtr
->dInfoPtr
->x
;
573 maxX
= textPtr
->dInfoPtr
->maxX
;
574 ascent
= descent
= 0;
576 chunkPtr
= (Chunk
*) ckalloc(sizeof(Chunk
));
577 chunkPtr
->numChars
= linePtr
->numBytes
- ch
;
578 chunkPtr
->text
= linePtr
->bytes
+ ch
;
580 chunkPtr
->nextPtr
= NULL
;
581 if (dlPtr
->chunkPtr
== NULL
) {
582 dlPtr
->chunkPtr
= chunkPtr
;
584 lastChunkPtr
->nextPtr
= chunkPtr
;
586 lastChunkPtr
= chunkPtr
;
589 * Update the tag array to include any tag transitions up
590 * through the current position, then find the next position
591 * with a transition on a tag that impacts the way things are
599 if ((tInfoPtr
->search
.linePtr
== NULL
)
600 || (tInfoPtr
->search
.line1
> line
)) {
603 tagPtr
= tInfoPtr
->search
.tagPtr
;
604 affectsDisplay
= TK_TAG_AFFECTS_DISPLAY(tagPtr
);
605 if ((tInfoPtr
->search
.line1
< line
)
606 || (tInfoPtr
->search
.ch1
<= ch
)) {
607 if (affectsDisplay
) {
608 ToggleTag(tInfoPtr
, tagPtr
);
611 if (affectsDisplay
) {
612 chunkPtr
->numChars
= tInfoPtr
->search
.ch1
- ch
;
616 (void) TkBTreeNextTag(&tInfoPtr
->search
);
620 * Create style information for this chunk.
623 ComputeStyleValues(textPtr
, tInfoPtr
->numTags
, tInfoPtr
->tagPtrs
,
625 chunkPtr
->stylePtr
= GetStyle(textPtr
, &styleValues
);
628 * See how many characters will fit on the line. If they don't
629 * all fit, then a number of compensations may have to be made.
631 * 1. Make sure that at least one character is displayed on
633 * 2. In wrap mode "none", allow a partial character to be
634 * displayed at the end of an incomplete line.
635 * 3. In wrap mode "word", search back to find the last space
636 * character, and terminate the line just after that space
637 * character. This involves a couple of extra complexities:
638 * - the last space may be several chunks back; in this
639 * case, delete all the chunks that are after the
641 * - if no words fit at all, then use character-wrap for
643 * - have to reinitialize the tag search information, since
644 * we may back up over tag toggles (they'll need to be
645 * reconsidered on the next DLine).
648 charsThatFit
= TkMeasureChars(styleValues
.fontPtr
,
649 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
,
651 if ((charsThatFit
< chunkPtr
->numChars
) || (x
>= maxX
)) {
653 chunkPtr
->numChars
= charsThatFit
;
655 if (ch
< (linePtr
->numBytes
- 1)) {
656 if ((charsThatFit
== 0) && (chunkPtr
== dlPtr
->chunkPtr
)) {
657 chunkPtr
->numChars
= 1;
659 } else if (textPtr
->wrapMode
== tkTextWordUid
) {
660 if (isspace(chunkPtr
->text
[charsThatFit
])) {
661 ch
+= 1; /* Include space on this line. */
663 register Chunk
*chunkPtr2
;
665 Chunk
*spaceChunkPtr
;
668 spaceChunkPtr
= NULL
;
670 for (chunkPtr2
= dlPtr
->chunkPtr
;
672 chunkPtr2
= chunkPtr2
->nextPtr
) {
673 for (count
= chunkPtr2
->numChars
- 1,
674 p
= chunkPtr2
->text
+ count
;
675 count
>= 0; count
--, p
--) {
677 spaceChunkPtr
= chunkPtr2
;
683 if (spaceChunkPtr
!= NULL
) {
684 spaceChunkPtr
->numChars
= space
;
685 ch
= (spaceChunkPtr
->text
+ space
+ 1)
687 if (chunkPtr
!= spaceChunkPtr
) {
688 chunkPtr
= spaceChunkPtr
;
689 if (tInfoPtr
->tagPtrs
!= NULL
) {
690 ckfree((char *) tInfoPtr
->tagPtrs
);
692 tInfoPtr
->tagPtrs
= TkBTreeGetTags(
693 textPtr
->tree
, dlPtr
->linePtr
, ch
,
695 TkBTreeStartSearch(textPtr
->tree
, line
,
697 TkBTreeNumLines(textPtr
->tree
), 0,
700 (void) TkBTreeNextTag(&tInfoPtr
->search
);
701 tInfoPtr
->arraySize
= tInfoPtr
->numTags
;
702 while (chunkPtr
->nextPtr
!= NULL
) {
703 chunkPtr2
= chunkPtr
->nextPtr
;
704 chunkPtr
->nextPtr
= chunkPtr2
->nextPtr
;
705 FreeStyle(chunkPtr2
->stylePtr
);
706 ckfree((char *) chunkPtr2
);
711 } else if (textPtr
->wrapMode
== tkTextNoneUid
) {
712 chunkPtr
->numChars
++;
717 ch
+= chunkPtr
->numChars
;
721 * Update height information for use later in computing
722 * line's overall height and baseline.
725 if (styleValues
.fontPtr
->ascent
> ascent
) {
726 ascent
= styleValues
.fontPtr
->ascent
;
728 if (styleValues
.fontPtr
->descent
> descent
) {
729 descent
= styleValues
.fontPtr
->descent
;
733 dlPtr
->height
= ascent
+ descent
;
734 dlPtr
->baseline
= ascent
;
737 * Quit when every character but the last character (the newline)
738 * has been accounted for. Also quit if the wrap mode is "none":
739 * this ignores all the characters that don't fit on the first
743 if ((ch
>= (linePtr
->numBytes
-1))
744 || (textPtr
->wrapMode
== tkTextNoneUid
)) {
752 *----------------------------------------------------------------------
756 * Update information about tags to reflect a transition on a
760 * The array at *tInfoPtr is modified to include tagPtr if it
761 * didn't already or to exclude it if it used to include it.
762 * The array will be reallocated to a larger size if needed.
767 *----------------------------------------------------------------------
771 ToggleTag(tInfoPtr
, tagPtr
)
772 register TagInfo
*tInfoPtr
; /* Tag information to be updated. */
773 TkTextTag
*tagPtr
; /* Tag to be toggled into or out of
776 register TkTextTag
**tagPtrPtr
;
779 for (i
= tInfoPtr
->numTags
, tagPtrPtr
= tInfoPtr
->tagPtrs
;
780 i
> 0; i
--, tagPtrPtr
++) {
781 if (*tagPtrPtr
== tagPtr
) {
783 *tagPtrPtr
= tInfoPtr
->tagPtrs
[tInfoPtr
->numTags
];
789 * Tag not currently in array. Grow the array if necessary, then
793 if (tInfoPtr
->numTags
== tInfoPtr
->arraySize
) {
796 newPtrs
= (TkTextTag
**) ckalloc((unsigned)
797 ((tInfoPtr
->arraySize
+10) * sizeof(TkTextTag
*)));
798 if (tInfoPtr
->tagPtrs
!= NULL
) {
799 memcpy((VOID
*) newPtrs
, (VOID
*) tInfoPtr
->tagPtrs
,
800 tInfoPtr
->arraySize
* sizeof(TkTextTag
*));
801 ckfree((char *) tInfoPtr
->tagPtrs
);
803 tInfoPtr
->tagPtrs
= newPtrs
;
804 tInfoPtr
->arraySize
+= 10;
806 tInfoPtr
->tagPtrs
[tInfoPtr
->numTags
] = tagPtr
;
811 *----------------------------------------------------------------------
813 * UpdateDisplayInfo --
815 * This procedure is invoked to recompute some or all of the
816 * DLine structures for a text widget. At the time it is called
817 * the DLine structures still left in the widget are guaranteed
818 * to be correct (except for their y-coordinates), but there may
819 * be missing structures (the DLine structures get removed as
820 * soon as they are potentially out-of-date).
826 * Upon return, the DLine information for textPtr correctly reflects
827 * the positions where characters will be displayed. However, this
828 * procedure doesn't actually bring the display up-to-date.
830 *----------------------------------------------------------------------
834 UpdateDisplayInfo(textPtr
)
835 TkText
*textPtr
; /* Text widget to update. */
837 register DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
838 register DLine
*dlPtr
, *prevPtr
, *dlPtr2
;
843 if (!(dInfoPtr
->flags
& DINFO_OUT_OF_DATE
)) {
846 dInfoPtr
->flags
&= ~DINFO_OUT_OF_DATE
;
848 linePtr
= textPtr
->topLinePtr
;
849 dlPtr
= dInfoPtr
->dLinePtr
;
850 tagInfo
.tagPtrs
= TkBTreeGetTags(textPtr
->tree
, linePtr
, 0,
852 tagInfo
.arraySize
= tagInfo
.numTags
;
855 * Tricky point: initialize the tag search just *after* the first
856 * character in the line, since the tagInfo structure already has all
857 * the tags for the first character.
860 line
= TkBTreeLineIndex(linePtr
);
861 TkBTreeStartSearch(textPtr
->tree
, line
, 1, TkBTreeNumLines(textPtr
->tree
),
862 0, (TkTextTag
*) NULL
, &tagInfo
.search
);
863 TkBTreeNextTag(&tagInfo
.search
);
866 maxY
= dInfoPtr
->maxY
;
867 while ((linePtr
!= NULL
) && (y
< maxY
)) {
868 register DLine
*newPtr
;
870 * See if the next DLine matches the next line we want to
871 * appear on the screen. If so then we can just use its
872 * information. If not then create new DLine structures
873 * for the desired line and insert them into the list.
876 if ((dlPtr
== NULL
) || (dlPtr
->linePtr
!= linePtr
)) {
877 newPtr
= LayoutLine(textPtr
, line
, linePtr
, &tagInfo
);
878 if (prevPtr
== NULL
) {
879 dInfoPtr
->dLinePtr
= newPtr
;
881 prevPtr
->nextPtr
= newPtr
;
883 for (dlPtr2
= newPtr
; dlPtr2
->nextPtr
!= NULL
;
884 dlPtr2
= dlPtr2
->nextPtr
) {
885 /* Empty loop body. */
887 dlPtr2
->nextPtr
= dlPtr
;
892 * Skip to the next line, and update the y-position while
900 dlPtr
= dlPtr
->nextPtr
;
901 } while ((dlPtr
!= NULL
) && (dlPtr
->linePtr
== linePtr
));
902 linePtr
= TkBTreeNextLine(linePtr
);
907 * Delete any DLine structures that don't fit on the screen and free
911 FreeDLines(textPtr
, dlPtr
, (DLine
*) NULL
, 1);
912 if (tagInfo
.tagPtrs
!= NULL
) {
913 ckfree((char *) tagInfo
.tagPtrs
);
917 * Update the vertical scrollbar, if there is one.
920 if (textPtr
->yScrollCmd
!= NULL
) {
921 int numLines
, first
, result
, maxY
, height
;
925 * Count the number of text lines on the screen.
929 for (numLines
= 0, linePtr
= NULL
, dlPtr
= dInfoPtr
->dLinePtr
;
930 dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
931 if (dlPtr
->linePtr
!= linePtr
) {
933 linePtr
= dlPtr
->linePtr
;
935 maxY
= dlPtr
->y
+ dlPtr
->height
;
939 * If the screen isn't completely full, then estimate the number of
940 * lines that would fit on it if it were full.
943 height
= dInfoPtr
->maxY
- dInfoPtr
->y
;
946 (textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
);
947 } else if (maxY
< height
) {
948 numLines
= (numLines
* height
)/maxY
;
950 /* DEH: be reasonable if dLinePtr is null */
951 if (dInfoPtr
->dLinePtr
== NULL
) {
952 sprintf(string
, " 0 0 0 0");
954 first
= TkBTreeLineIndex(dInfoPtr
->dLinePtr
->linePtr
);
955 sprintf(string
, " %d %d %d %d", TkBTreeNumLines(textPtr
->tree
),
956 numLines
, first
, first
+numLines
-1);
958 result
= Tcl_VarEval(textPtr
->interp
, textPtr
->yScrollCmd
, string
,
960 if (result
!= TCL_OK
) {
961 TkBindError(textPtr
->interp
);
967 *----------------------------------------------------------------------
971 * This procedure is called to free up all of the resources
972 * associated with one or more DLine structures.
978 * Memory gets freed and various other resources are released.
980 *----------------------------------------------------------------------
984 FreeDLines(textPtr
, firstPtr
, lastPtr
, unlink
)
985 TkText
*textPtr
; /* Information about overall text
987 register DLine
*firstPtr
; /* Pointer to first DLine to free up. */
988 DLine
*lastPtr
; /* Pointer to DLine just after last
989 * one to free (NULL means everything
990 * starting with firstPtr). */
991 int unlink
; /* 1 means DLines are currently linked
992 * into the list rooted at
993 * textPtr->dInfoPtr->dLinePtr and
994 * they have to be unlinked. 0 means
995 * just free without unlinking. */
997 register Chunk
*chunkPtr
, *nextChunkPtr
;
998 register DLine
*nextDLinePtr
;
1001 if (textPtr
->dInfoPtr
->dLinePtr
== firstPtr
) {
1002 textPtr
->dInfoPtr
->dLinePtr
= lastPtr
;
1004 register DLine
*prevPtr
;
1005 for (prevPtr
= textPtr
->dInfoPtr
->dLinePtr
;
1006 prevPtr
->nextPtr
!= firstPtr
; prevPtr
= prevPtr
->nextPtr
) {
1007 /* Empty loop body. */
1009 prevPtr
->nextPtr
= lastPtr
;
1012 while (firstPtr
!= lastPtr
) {
1013 nextDLinePtr
= firstPtr
->nextPtr
;
1014 for (chunkPtr
= firstPtr
->chunkPtr
; chunkPtr
!= NULL
;
1015 chunkPtr
= nextChunkPtr
) {
1016 FreeStyle(chunkPtr
->stylePtr
);
1017 nextChunkPtr
= chunkPtr
->nextPtr
;
1018 ckfree((char *) chunkPtr
);
1020 ckfree((char *) firstPtr
);
1021 firstPtr
= nextDLinePtr
;
1026 *----------------------------------------------------------------------
1030 * This procedure is invoked to draw a single line on the
1037 * The line given by dlPtr is drawn at its correct position in
1038 * textPtr's window. Note that this is one *display* line, not
1041 *----------------------------------------------------------------------
1045 DisplayDLine(textPtr
, dlPtr
, pixmap
)
1046 TkText
*textPtr
; /* Text widget in which to draw line. */
1047 register DLine
*dlPtr
; /* Information about line to draw. */
1048 Pixmap pixmap
; /* Pixmap to use for double-buffering.
1049 * Caller must make sure it's large enough
1052 register Style
*stylePtr
;
1053 register StyleValues
*sValuePtr
;
1054 register Chunk
*chunkPtr
;
1055 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1057 int width
, height
, count
, x
;
1058 XFontStruct
*fontPtr
;
1061 * First, clear the area of the line to the background color for the
1065 display
= Tk_Display(textPtr
->tkwin
);
1066 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->border
, 0, 0,
1067 Tk_Width(textPtr
->tkwin
), dlPtr
->height
, 0, TK_RELIEF_FLAT
);
1070 * Next, cycle through all of the chunks in the line displaying
1071 * backgrounds. We need to do two passes, one for the backgrounds
1072 * and one for the characters, because some characters (e.g. italics
1073 * with heavy slants) may cross background boundaries. If some
1074 * backgrounds are drawn after some text, the later backgrounds may
1075 * obliterate parts of earlier characters.
1078 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1079 chunkPtr
= chunkPtr
->nextPtr
) {
1082 * Draw a special background for this chunk if one is specified
1083 * in its style. Two tricks here:
1084 * 1. if this is the last chunk in the line then extend the
1085 * background across to the end of the line.
1086 * 2. if the background is stippled, then we have to draw the
1087 * stippled part specially, since Tk_Fill3DRectangle doesn't
1091 stylePtr
= chunkPtr
->stylePtr
;
1092 sValuePtr
= stylePtr
->sValuePtr
;
1093 if (sValuePtr
->border
!= NULL
) {
1094 if (chunkPtr
->nextPtr
!= NULL
) {
1095 width
= chunkPtr
->nextPtr
->x
- chunkPtr
->x
;
1097 width
= Tk_Width(textPtr
->tkwin
) - chunkPtr
->x
;
1099 if (stylePtr
->bgGC
!= NULL
) {
1100 XFillRectangle(display
, pixmap
, stylePtr
->bgGC
, chunkPtr
->x
,
1101 0, (unsigned int) width
, (unsigned int) dlPtr
->height
);
1102 Tk_Draw3DRectangle(display
, pixmap
, sValuePtr
->border
,
1103 chunkPtr
->x
, 0, width
, dlPtr
->height
,
1104 sValuePtr
->borderWidth
, sValuePtr
->relief
);
1106 Tk_Fill3DRectangle(display
, pixmap
, sValuePtr
->border
,
1107 chunkPtr
->x
, 0, width
, dlPtr
->height
,
1108 sValuePtr
->borderWidth
, sValuePtr
->relief
);
1114 * If the insertion cursor is displayed on this line, then draw it
1115 * now, on top of the background but before the text. As a special
1116 * workaround to keep the cursor visible on mono displays, write the default
1117 * background in the cursor area (instead of nothing) when the cursor
1118 * isn't on. Otherwise the selection would hide the cursor.
1121 if ((textPtr
->insertAnnotPtr
->linePtr
== dlPtr
->linePtr
)
1122 && (textPtr
->state
== tkTextNormalUid
)
1123 && (textPtr
->flags
& GOT_FOCUS
)) {
1124 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1125 chunkPtr
= chunkPtr
->nextPtr
) {
1126 count
= textPtr
->insertAnnotPtr
->ch
1127 - (chunkPtr
->text
- dlPtr
->linePtr
->bytes
);
1131 if (count
> chunkPtr
->numChars
) {
1136 * Deciding whether to display the cursor just after the last
1137 * character in a line is tricky because of various wrap
1138 * modes. Do it unless we're in character wrap mode and
1139 * this line wraps, in which case it's better to display the
1140 * cursor on the next line. For word wrap, there's an
1141 * undisplayed space character that the user must be able to
1142 * position the cursor in front of. For no wrap, there's no
1143 * next line on which to display the cursor.
1145 if ((count
== chunkPtr
->numChars
)
1146 && (textPtr
->wrapMode
== tkTextCharUid
)
1147 && (chunkPtr
->text
[count
] != '\n')) {
1150 fontPtr
= chunkPtr
->stylePtr
->sValuePtr
->fontPtr
;
1151 TkMeasureChars(fontPtr
, chunkPtr
->text
, count
, chunkPtr
->x
,
1152 (int) 1000000, 0, &x
);
1153 if (textPtr
->flags
& INSERT_ON
) {
1154 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->insertBorder
,
1155 x
- textPtr
->insertWidth
/2,
1156 dlPtr
->baseline
- fontPtr
->ascent
,
1157 textPtr
->insertWidth
,
1158 fontPtr
->ascent
+ fontPtr
->descent
,
1159 textPtr
->insertBorderWidth
, TK_RELIEF_RAISED
);
1160 } else if (Tk_DefaultDepth(Tk_Screen(textPtr
->tkwin
)) == 1) {
1161 Tk_Fill3DRectangle(display
, pixmap
, textPtr
->border
,
1162 x
- textPtr
->insertWidth
/2,
1163 dlPtr
->baseline
- fontPtr
->ascent
,
1164 textPtr
->insertWidth
,
1165 fontPtr
->ascent
+ fontPtr
->descent
,
1173 * Make another pass through all of the chunks to redraw all of
1174 * the text (and underlines, etc., if they're wanted).
1177 for (chunkPtr
= dlPtr
->chunkPtr
; chunkPtr
!= NULL
;
1178 chunkPtr
= chunkPtr
->nextPtr
) {
1179 stylePtr
= chunkPtr
->stylePtr
;
1180 sValuePtr
= stylePtr
->sValuePtr
;
1181 if (chunkPtr
->numChars
> 0) {
1182 TkDisplayChars(display
, pixmap
, stylePtr
->fgGC
, sValuePtr
->fontPtr
,
1183 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
,
1184 dlPtr
->baseline
, 0);
1185 if (sValuePtr
->underline
) {
1186 TkUnderlineChars(display
, pixmap
, stylePtr
->fgGC
,
1187 sValuePtr
->fontPtr
, chunkPtr
->text
, chunkPtr
->x
,
1188 dlPtr
->baseline
, 0, 0, chunkPtr
->numChars
-1);
1194 * Copy the pixmap onto the screen. If this is the last line on
1195 * the screen, only copy a piece of the line, so that it doesn't
1196 * overflow into the border area. Another special trick: copy the
1197 * padding area to the left of the line; this is because the
1198 * insertion cursor sometimes overflows onto that area and we want
1199 * to get as much of the cursor as possible.
1202 height
= dlPtr
->height
;
1203 if ((height
+ dlPtr
->y
) > dInfoPtr
->maxY
) {
1204 height
= dInfoPtr
->maxY
- dlPtr
->y
;
1206 XCopyArea(display
, pixmap
, Tk_WindowId(textPtr
->tkwin
),
1207 dInfoPtr
->copyGC
, dInfoPtr
->x
- textPtr
->padX
, 0,
1208 dInfoPtr
->maxX
- (dInfoPtr
->x
- textPtr
->padX
),
1209 height
, dInfoPtr
->x
- textPtr
->padX
, dlPtr
->y
);
1214 *----------------------------------------------------------------------
1218 * This procedure is invoked as a when-idle handler to update the
1219 * display. It only redisplays the parts of the text widget that
1226 * Information is redrawn on the screen.
1228 *----------------------------------------------------------------------
1232 DisplayText(clientData
)
1233 ClientData clientData
; /* Information about widget. */
1235 register TkText
*textPtr
= (TkText
*) clientData
;
1236 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1238 register DLine
*dlPtr
;
1241 int bottomY
= 0; /* Initialization needed only to stop
1242 * compiler warnings. */
1244 assert(textPtr
->updateTimerToken
!= NULL
);
1246 textPtr
->updateTimerToken
= 0;
1248 if ((textPtr
->tkwin
== NULL
) || !Tk_IsMapped(textPtr
->tkwin
)
1249 || (dInfoPtr
->maxX
<= dInfoPtr
->x
)
1250 || (dInfoPtr
->maxY
<= dInfoPtr
->y
)) {
1256 * Choose a new current item if that is needed (this could cause
1257 * event handlers to be invoked, hence the preserve/release calls
1258 * and the loop, since the handlers could conceivably necessitate
1259 * yet another current item calculation). The tkwin check is because
1260 * the whole window could go away in the Tk_Release call.
1263 while (dInfoPtr
->flags
& REPICK_NEEDED
) {
1264 Tk_Preserve((ClientData
) textPtr
);
1265 dInfoPtr
->flags
&= ~REPICK_NEEDED
;
1266 TkTextPickCurrent(textPtr
, &textPtr
->pickEvent
);
1267 tkwin
= textPtr
->tkwin
;
1268 Tk_Release((ClientData
) textPtr
);
1269 if (tkwin
== NULL
) {
1275 * First recompute what's supposed to be displayed.
1278 UpdateDisplayInfo(textPtr
);
1281 * Redraw the borders if that's needed.
1284 if (dInfoPtr
->flags
& REDRAW_BORDERS
) {
1285 Tk_Draw3DRectangle(Tk_Display(textPtr
->tkwin
),
1286 Tk_WindowId(textPtr
->tkwin
), textPtr
->border
,
1287 0, 0, Tk_Width(textPtr
->tkwin
), Tk_Height(textPtr
->tkwin
),
1288 textPtr
->borderWidth
, textPtr
->relief
);
1292 * See if it's possible to bring some parts of the screen up-to-date
1293 * by scrolling (copying from other parts of the screen).
1296 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
1297 register DLine
*dlPtr2
;
1300 if ((dlPtr
->oldY
== -1) || (dlPtr
->y
== dlPtr
->oldY
)
1301 || ((dlPtr
->oldY
+ dlPtr
->height
) > dInfoPtr
->maxY
)) {
1306 * This line is already drawn somewhere in the window so it only
1307 * needs to be copied to its new location. See if there's a group
1308 * of lines that can all be copied together.
1311 offset
= dlPtr
->y
- dlPtr
->oldY
;
1312 height
= dlPtr
->height
;
1313 for (dlPtr2
= dlPtr
->nextPtr
; dlPtr2
!= NULL
;
1314 dlPtr2
= dlPtr2
->nextPtr
) {
1315 if ((dlPtr2
->oldY
== -1)
1316 || ((dlPtr2
->oldY
+ offset
) != dlPtr2
->y
)
1317 || ((dlPtr2
->oldY
+ dlPtr2
->height
) > dInfoPtr
->maxY
)) {
1320 height
+= dlPtr2
->height
;
1324 * Copy the information and update the lines to show that they've
1325 * been copied. Reduce the height of the area being copied if
1326 * necessary to avoid overwriting the border area.
1329 if ((dlPtr
->y
+ height
) > dInfoPtr
->maxY
) {
1330 height
= dInfoPtr
->maxY
- dlPtr
->y
;
1332 XCopyArea(Tk_Display(textPtr
->tkwin
), Tk_WindowId(textPtr
->tkwin
),
1333 Tk_WindowId(textPtr
->tkwin
), dInfoPtr
->scrollGC
,
1334 dInfoPtr
->x
- textPtr
->padX
, dlPtr
->oldY
,
1335 dInfoPtr
->maxX
- (dInfoPtr
->x
- textPtr
->padX
),
1336 height
, dInfoPtr
->x
- textPtr
->padX
, dlPtr
->y
);
1339 dlPtr
->oldY
= dlPtr
->y
;
1340 if (dlPtr
->nextPtr
== dlPtr2
) {
1343 dlPtr
= dlPtr
->nextPtr
;
1347 * It's possible that part of the area copied above was obscured.
1348 * To handle this situation, read expose-related events generated
1349 * during the XCopyArea operation.
1355 XWindowEvent(Tk_Display(textPtr
->tkwin
),
1356 Tk_WindowId(textPtr
->tkwin
), ExposureMask
, &event
);
1357 if (event
.type
== NoExpose
) {
1359 } else if (event
.type
== GraphicsExpose
) {
1360 TkTextRedrawRegion(textPtr
, event
.xgraphicsexpose
.x
,
1361 event
.xgraphicsexpose
.y
, event
.xgraphicsexpose
.width
,
1362 event
.xgraphicsexpose
.height
);
1363 if (event
.xgraphicsexpose
.count
== 0) {
1367 } else if (event
.type
== Expose
) {
1369 * A tricky situation. This event must already have been
1370 * queued up before the XCopyArea was issued. If the area
1371 * in this event overlaps the area copied, then some of the
1372 * bits that were copied were bogus. The easiest way to
1373 * handle this is to issue two redisplays: one for the
1374 * original area and one for the area shifted as if it was
1375 * in the copied area.
1378 TkTextRedrawRegion(textPtr
, event
.xexpose
.x
,
1379 event
.xexpose
.y
, event
.xexpose
.width
,
1380 event
.xexpose
.height
);
1381 TkTextRedrawRegion(textPtr
, event
.xexpose
.x
,
1382 event
.xexpose
.y
+ offset
, event
.xexpose
.width
,
1383 event
.xexpose
.height
);
1385 panic("DisplayText received unknown exposure event");
1391 * Now we have to redraw the lines that couldn't be updated by
1392 * scrolling. First, compute the height of the largest line and
1393 * allocate an off-screen pixmap to use for double-buffered
1398 for (dlPtr
= textPtr
->dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1399 dlPtr
= dlPtr
->nextPtr
) {
1400 if ((dlPtr
->height
> maxHeight
) && (dlPtr
->oldY
!= dlPtr
->y
)) {
1401 maxHeight
= dlPtr
->height
;
1403 bottomY
= dlPtr
->y
+ dlPtr
->height
;
1405 if (maxHeight
>= 0) {
1406 pixmap
= XCreatePixmap(Tk_Display(textPtr
->tkwin
),
1407 Tk_WindowId(textPtr
->tkwin
), Tk_Width(textPtr
->tkwin
),
1408 maxHeight
, Tk_DefaultDepth(Tk_Screen(textPtr
->tkwin
)));
1409 for (dlPtr
= textPtr
->dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1410 dlPtr
= dlPtr
->nextPtr
) {
1411 if (dlPtr
->oldY
!= dlPtr
->y
) {
1412 DisplayDLine(textPtr
, dlPtr
, pixmap
);
1413 dlPtr
->oldY
= dlPtr
->y
;
1416 XFreePixmap(Tk_Display(textPtr
->tkwin
), pixmap
);
1420 * Lastly, see if we need to refresh the part of the window below
1421 * the last line of text (if there is any such area).
1424 if (dInfoPtr
->topOfEof
> dInfoPtr
->maxY
) {
1425 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1427 if (bottomY
< dInfoPtr
->topOfEof
) {
1428 Tk_Fill3DRectangle(Tk_Display(textPtr
->tkwin
),
1429 Tk_WindowId(textPtr
->tkwin
), textPtr
->border
,
1430 dInfoPtr
->x
, bottomY
, dInfoPtr
->maxX
- dInfoPtr
->x
,
1431 dInfoPtr
->topOfEof
-bottomY
, 0, TK_RELIEF_FLAT
);
1433 dInfoPtr
->topOfEof
= bottomY
;
1434 if (dInfoPtr
->topOfEof
> dInfoPtr
->maxY
) {
1435 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1439 dInfoPtr
->flags
&= ~(REDRAW_PENDING
|REDRAW_BORDERS
);
1443 *----------------------------------------------------------------------
1445 * TkTextRedrawRegion --
1447 * This procedure is invoked to schedule a redisplay for a given
1448 * region of a text widget. The redisplay itself may not occur
1449 * immediately: it's scheduled as a when-idle handler.
1455 * Information will eventually be redrawn on the screen.
1457 *----------------------------------------------------------------------
1462 TkTextRedrawRegion(textPtr
, x
, y
, width
, height
)
1463 TkText
*textPtr
; /* Widget record for text widget. */
1464 int x
, y
; /* Coordinates of upper-left corner of area
1465 * to be redrawn, in pixels relative to
1466 * textPtr's window. */
1467 int width
, height
; /* Width and height of area to be redrawn. */
1469 register DLine
*dlPtr
;
1470 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1474 * Find all lines that overlap the given region and mark them for
1479 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
;
1480 dlPtr
= dlPtr
->nextPtr
) {
1481 if (((dlPtr
->y
+ dlPtr
->height
) > y
) && (dlPtr
->y
< maxY
)) {
1485 if (dInfoPtr
->topOfEof
< maxY
) {
1486 dInfoPtr
->topOfEof
= maxY
;
1490 * Schedule the redisplay operation if there isn't one already
1494 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1495 dInfoPtr
->flags
|= REDRAW_PENDING
;
1496 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1497 assert(textPtr
->updateTimerToken
== NULL
);
1498 if (textPtr
->updateTimerToken
== NULL
) {
1499 textPtr
->updateTimerToken
=
1500 Tk_CreateTimerHandler(
1503 (ClientData
) textPtr
);
1506 if ((x
< dInfoPtr
->x
) || (y
< dInfoPtr
->y
)
1507 || ((x
+ width
) > dInfoPtr
->maxX
) || (maxY
> dInfoPtr
->maxY
)) {
1508 dInfoPtr
->flags
|= REDRAW_BORDERS
;
1513 *----------------------------------------------------------------------
1515 * TkTextLinesChanged --
1517 * This procedure is invoked when lines in a text widget are about
1518 * to be modified in a way that changes how they are displayed (e.g.
1519 * characters were inserted, the line was deleted, or tag information
1520 * was changed). This procedure must be called *before* a change is
1521 * made, so that pointers to TkTextLines in the display information
1528 * The indicated lines will be redisplayed at some point in the
1529 * future (the actual redisplay is scheduled as a when-idle handler).
1531 *----------------------------------------------------------------------
1535 TkTextLinesChanged(textPtr
, first
, last
)
1536 TkText
*textPtr
; /* Widget record for text widget. */
1537 int first
; /* Index of first line that must be
1539 int last
; /* Index of last line to redisplay. */
1541 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1542 DLine
*firstPtr
, *lastPtr
;
1545 * Find the DLines corresponding to first and last+1.
1548 firstPtr
= FindDLine(dInfoPtr
->dLinePtr
, first
);
1549 if (firstPtr
== NULL
) {
1552 lastPtr
= FindDLine(dInfoPtr
->dLinePtr
, last
+1);
1553 if (firstPtr
== lastPtr
) {
1558 * Delete all the DLines from first up through last (but not including
1559 * lastPtr, which points to the first line *outside* the range).
1562 FreeDLines(textPtr
, firstPtr
, lastPtr
, 1);
1565 * Schedule both a redisplay and a recomputation of display information.
1568 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1569 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1570 assert(textPtr
->updateTimerToken
== NULL
);
1571 if (textPtr
->updateTimerToken
== NULL
) {
1572 textPtr
->updateTimerToken
=
1573 Tk_CreateTimerHandler(
1576 (ClientData
) textPtr
);
1579 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1583 *----------------------------------------------------------------------
1585 * TkTextRedrawTag --
1587 * This procedure is invoked to request a redraw of all characters
1588 * in a given range of characters that have a particular tag on or
1589 * off. It's called, for example, when characters are tagged or
1590 * untagged, or when tag options change.
1596 * Information on the screen may be redrawn, and the layout of
1597 * the screen may change.
1599 *----------------------------------------------------------------------
1603 TkTextRedrawTag(textPtr
, line1
, ch1
, line2
, ch2
, tagPtr
, withTag
)
1604 TkText
*textPtr
; /* Widget record for text widget. */
1605 int line1
, ch1
; /* Index of first character in range of
1607 int line2
, ch2
; /* Index of character just after last one
1608 * in range of interest. */
1609 TkTextTag
*tagPtr
; /* Information about tag. */
1610 int withTag
; /* 1 means redraw characters that have the
1611 * tag, 0 means redraw those without. */
1613 register DLine
*dlPtr
;
1616 TkTextSearch search
;
1617 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1620 * Round up the starting position if it's before the first line
1621 * visible on the screen (we only care about what's on the screen).
1624 dlPtr
= dInfoPtr
->dLinePtr
;
1625 if (dlPtr
== NULL
) {
1628 topLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1629 if (topLine
> line1
) {
1635 * Initialize a search through all transitions on the tag, starting
1636 * with the first transition where the tag's current state is different
1637 * from what it will eventually be.
1640 TkBTreeStartSearch(textPtr
->tree
, line1
, ch1
+1, line2
, ch2
,
1642 tagOn
= TkBTreeCharTagged(search
.linePtr
, ch1
, tagPtr
);
1643 if (tagOn
!= withTag
) {
1644 if (!TkBTreeNextTag(&search
)) {
1650 * Each loop through the loop below is for one range of characters
1651 * where the tag's current state is different than its eventual
1652 * state. At the top of the loop, search contains information about
1653 * the first character in the range.
1658 * Find the first DLine structure in the range.
1661 dlPtr
= FindDLine(dlPtr
, search
.line1
);
1662 if (dlPtr
== NULL
) {
1667 * Find the first DLine structure that's past the end of the range.
1670 if (TkBTreeNextTag(&search
)) {
1671 endPtr
= FindDLine(dlPtr
,
1672 (search
.ch1
> 0) ? (search
.line1
+ 1) : search
.line1
);
1674 endPtr
= FindDLine(dlPtr
,
1675 (ch2
> 0) ? (search
.line2
+ 1) : search
.line2
);
1679 * Delete all of the display lines in the range, so that they'll
1680 * be re-layed out and redrawn.
1683 FreeDLines(textPtr
, dlPtr
, endPtr
, 1);
1687 * Find the first text line in the next range.
1690 if (!TkBTreeNextTag(&search
)) {
1696 * Lastly, schedule a redisplay and layout recalculation if they
1697 * aren't already pending.
1700 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1701 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1702 assert(textPtr
->updateTimerToken
== NULL
);
1703 if (textPtr
->updateTimerToken
== NULL
) {
1704 textPtr
->updateTimerToken
=
1705 Tk_CreateTimerHandler(
1708 (ClientData
) textPtr
);
1711 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1715 *----------------------------------------------------------------------
1717 * TkTextRelayoutWindow --
1719 * This procedure is called when something has happened that
1720 * invalidates the whole layout of characters on the screen, such
1721 * as a change in a configuration option for the overall text
1722 * widget or a change in the window size. It causes all display
1723 * information to be recomputed and the window to be redrawn.
1729 * All the display information will be recomputed for the window
1730 * and the window will be redrawn.
1732 *----------------------------------------------------------------------
1736 TkTextRelayoutWindow(textPtr
)
1737 TkText
*textPtr
; /* Widget record for text widget. */
1739 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1742 * Throw away all the current layout information.
1745 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, (DLine
*) NULL
, 1);
1746 dInfoPtr
->dLinePtr
= NULL
;
1749 * Recompute some overall things for the layout.
1752 dInfoPtr
->x
= textPtr
->borderWidth
+ textPtr
->padX
;
1753 dInfoPtr
->y
= textPtr
->borderWidth
+ textPtr
->padY
;
1754 dInfoPtr
->maxX
= Tk_Width(textPtr
->tkwin
) - dInfoPtr
->x
;
1755 dInfoPtr
->maxY
= Tk_Height(textPtr
->tkwin
) - dInfoPtr
->y
;
1756 dInfoPtr
->topOfEof
= dInfoPtr
->maxY
;
1758 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1759 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1760 assert(textPtr
->updateTimerToken
== NULL
);
1761 if (textPtr
->updateTimerToken
== NULL
) {
1762 textPtr
->updateTimerToken
=
1763 Tk_CreateTimerHandler(
1766 (ClientData
) textPtr
);
1769 dInfoPtr
->flags
|= REDRAW_PENDING
| REDRAW_BORDERS
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1773 *----------------------------------------------------------------------
1777 * This procedure is called to specify what lines are to be
1778 * displayed in a text widget.
1784 * The display will (eventually) be updated so that the line
1785 * given by "line" is visible on the screen at the position
1786 * determined by "pickPlace".
1788 *----------------------------------------------------------------------
1792 TkTextSetView(textPtr
, line
, pickPlace
)
1793 TkText
*textPtr
; /* Widget record for text widget. */
1794 int line
; /* Number of line that is to appear somewhere
1795 * in the window. This line number must
1796 * be a valid one in the file. */
1797 int pickPlace
; /* 0 means topLine must appear at top of
1798 * screen. 1 means we get to pick where it
1799 * appears: minimize screen motion or else
1800 * display line at center of screen. */
1802 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
1803 register DLine
*dlPtr
, *dlPtr2
;
1804 TkTextLine
*linePtr
;
1805 int curTopLine
, curBotLine
;
1808 #define CLOSE_LINES 5
1812 * The line must go at the top of the screen. See if the new
1813 * topmost line is already somewhere on the screen. If so then
1814 * delete all the DLine structures ahead of it. Otherwise just
1815 * leave all the DLine's alone (if the new topmost line is above
1816 * the top of the current window, i.e. we're scrolling back towards
1817 * the beginning of the file we may be able to reuse some of the
1818 * information that's currently on the screen without redisplaying
1822 dlPtr
= FindDLine(dInfoPtr
->dLinePtr
, line
);
1823 if ((dlPtr
!= NULL
) && (dlPtr
!= dInfoPtr
->dLinePtr
)) {
1824 FreeDLines(textPtr
, dInfoPtr
->dLinePtr
, dlPtr
, 1);
1827 textPtr
->topLinePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1828 goto scheduleUpdate
;
1832 * We have to pick where to display the given line. First, bring
1833 * the display information up to date and see if the line will be
1834 * completely visible in the current screen configuration. If so
1835 * then there's nothing to do.
1838 if (dInfoPtr
->flags
& DINFO_OUT_OF_DATE
) {
1839 UpdateDisplayInfo(textPtr
);
1841 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1842 /* DEH: return if dlPtr is null */
1843 if ((dlPtr
= dInfoPtr
->dLinePtr
) == NULL
)
1845 for (; ; dlPtr
= dlPtr
->nextPtr
) {
1846 if (dlPtr
->nextPtr
== NULL
) {
1849 if ((dlPtr
->linePtr
== linePtr
)
1850 && (dlPtr
->nextPtr
->linePtr
!= linePtr
)) {
1854 if ((dlPtr
->linePtr
== linePtr
)
1855 && ((dlPtr
->y
+ dlPtr
->height
) <= dInfoPtr
->maxY
)) {
1860 * The desired line isn't already on-screen. See if it is within
1861 * a few lines of the top of the window. If so then just make it
1862 * the top line on the screen.
1865 bottomY
= (dInfoPtr
->y
+ dInfoPtr
->maxY
)/2;
1866 curTopLine
= TkBTreeLineIndex(dInfoPtr
->dLinePtr
->linePtr
);
1867 if (line
< curTopLine
) {
1868 if (line
>= (curTopLine
-CLOSE_LINES
)) {
1869 textPtr
->topLinePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1870 goto scheduleUpdate
;
1874 * The desired line is below the bottom of the screen. If it is
1875 * within a few lines of the bottom of the screen then position
1876 * it at the bottom of the screen. (At this point dlPtr points to
1877 * the last line on the screen)
1880 curBotLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1881 if (line
<= (curBotLine
+5)) {
1882 bottomY
= dInfoPtr
->maxY
;
1887 * Our job now is arrange the display so that "line" appears as
1888 * low on the screen as possible but with its bottom no lower
1889 * than bottomY (bottomY is the bottom of the window if the
1890 * desired line is just below the current screen, otherwise it
1891 * is the center of the window. Work upwards (through smaller
1892 * line numbers) computing how much space lines take, until we
1893 * fine the line that should be at the top of the screen.
1896 for (textPtr
->topLinePtr
= linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
1897 ; line
--, textPtr
->topLinePtr
= linePtr
,
1898 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
)) {
1899 tagInfo
.tagPtrs
= TkBTreeGetTags(textPtr
->tree
, linePtr
, 0,
1901 tagInfo
.arraySize
= tagInfo
.numTags
;
1902 TkBTreeStartSearch(textPtr
->tree
, line
, 1, line
+1, 0,
1903 (TkTextTag
*) NULL
, &tagInfo
.search
);
1904 TkBTreeNextTag(&tagInfo
.search
);
1905 dlPtr
= LayoutLine(textPtr
, line
, linePtr
, &tagInfo
);
1906 for (dlPtr2
= dlPtr
; dlPtr2
!= NULL
; dlPtr2
= dlPtr2
->nextPtr
) {
1907 bottomY
-= dlPtr2
->height
;
1909 FreeDLines(textPtr
, dlPtr
, (DLine
*) NULL
, 0);
1910 if (tagInfo
.tagPtrs
!= NULL
) {
1911 ckfree((char *) tagInfo
.tagPtrs
);
1913 if ((bottomY
<= 0) || (line
<= 0)) {
1919 if (!(dInfoPtr
->flags
& REDRAW_PENDING
)) {
1920 // Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
1921 assert(textPtr
->updateTimerToken
== NULL
);
1922 if (textPtr
->updateTimerToken
== NULL
) {
1923 textPtr
->updateTimerToken
=
1924 Tk_CreateTimerHandler(
1927 (ClientData
) textPtr
);
1930 dInfoPtr
->flags
|= REDRAW_PENDING
| DINFO_OUT_OF_DATE
| REPICK_NEEDED
;
1934 *----------------------------------------------------------------------
1938 * This procedure is called to find the DLine corresponding to a
1942 * The return value is a pointer to the first DLine found in the
1943 * list headed by dlPtr whose line number is greater or equal to
1944 * line. If there is no such line in the list then NULL is returned.
1949 *----------------------------------------------------------------------
1953 FindDLine(dlPtr
, line
)
1954 register DLine
*dlPtr
; /* Pointer to first in list of DLines
1956 int line
; /* Line number in text that is desired. */
1958 TkTextLine
*linePtr
;
1961 if (dlPtr
== NULL
) {
1964 thisLine
= TkBTreeLineIndex(dlPtr
->linePtr
);
1965 while (thisLine
< line
) {
1967 * This DLine isn't the right one. Go on to the next DLine
1968 * (skipping multiple DLine's for the same text line).
1971 linePtr
= dlPtr
->linePtr
;
1973 dlPtr
= dlPtr
->nextPtr
;
1974 if (dlPtr
== NULL
) {
1977 } while (dlPtr
->linePtr
== linePtr
);
1980 * Step through text lines, keeping track of the line number
1981 * we're on, until we catch up to dlPtr (remember, there could
1982 * be gaps in the DLine list where DLine's have been deleted).
1986 linePtr
= TkBTreeNextLine(linePtr
);
1988 if (linePtr
== NULL
) {
1989 panic("FindDLine reached end of text");
1991 } while (linePtr
!= dlPtr
->linePtr
);
1997 *----------------------------------------------------------------------
1999 * TkTextCharAtLoc --
2001 * Given an (x,y) coordinate on the screen, find the location of
2002 * the closest character to that location.
2005 * The return value is a pointer to the text line containing the
2006 * character displayed closest to (x,y). The value at *chPtr is
2007 * overwritten with the index with that line of the closest
2013 *----------------------------------------------------------------------
2017 TkTextCharAtLoc(textPtr
, x
, y
, chPtr
)
2018 TkText
*textPtr
; /* Widget record for text widget. */
2019 int x
, y
; /* Pixel coordinates of point in widget's
2021 int *chPtr
; /* Place to store index-within-line of
2022 * closest character. */
2024 DInfo
*dInfoPtr
= textPtr
->dInfoPtr
;
2025 register DLine
*dlPtr
;
2026 register Chunk
*chunkPtr
;
2031 * Make sure that all of the layout information about what's
2032 * displayed where on the screen is up-to-date.
2035 if (dInfoPtr
->flags
& DINFO_OUT_OF_DATE
) {
2036 UpdateDisplayInfo(textPtr
);
2040 * If the coordinates are above the top of the window, then adjust
2041 * them to refer to the upper-right corner of the window.
2044 if (y
< dInfoPtr
->y
) {
2047 } else if (y
>= dInfoPtr
->topOfEof
) {
2048 y
= dInfoPtr
->topOfEof
;
2051 for (dlPtr
= dInfoPtr
->dLinePtr
; dlPtr
!= NULL
; dlPtr
= dlPtr
->nextPtr
) {
2052 if (y
> (dlPtr
->y
+ dlPtr
->height
)) {
2053 if (dlPtr
->nextPtr
!= NULL
) {
2058 * The coordinates are off the bottom of the window. Adjust
2059 * them to refer to the lower-right character on the window.
2065 for (chunkPtr
= dlPtr
->chunkPtr
; ; chunkPtr
= chunkPtr
->nextPtr
) {
2066 if ((chunkPtr
->nextPtr
== NULL
) || (chunkPtr
->nextPtr
->x
> x
)) {
2070 count
= TkMeasureChars(chunkPtr
->stylePtr
->sValuePtr
->fontPtr
,
2071 chunkPtr
->text
, chunkPtr
->numChars
, chunkPtr
->x
, x
, 0, &endX
);
2072 if (count
>= chunkPtr
->numChars
) {
2074 * The point is off the end of the line. Return the character
2075 * after the last one that fit, unless that character appears
2076 * as the first character on the next DLine or unless the last
2077 * one that fit extends beyond the edge of the window.
2080 if ((dlPtr
->nextPtr
!= NULL
)
2081 && (dlPtr
->nextPtr
->chunkPtr
->text
2082 == (chunkPtr
->text
+ chunkPtr
->numChars
))) {
2083 count
= chunkPtr
->numChars
-1;
2085 if (endX
>= dInfoPtr
->maxX
) {
2086 count
= chunkPtr
->numChars
-1;
2089 *chPtr
= count
+ (chunkPtr
->text
- dlPtr
->linePtr
->bytes
);
2090 return dlPtr
->linePtr
;
2092 panic("TkTextCharAtLoc ran out of lines");
2093 return (TkTextLine
*) NULL
;