]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tktxdisp.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tktxdisp.c
1 /*
2 * tkTextDisp.c --
3 *
4 * This module provides facilities to display text widgets. It is
5 * the only place where information is kept about the screen layout
6 * of text widgets.
7 *
8 * Copyright 1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
16 */
17
18 #ifndef lint
19 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextDisp.c,v 1.20 92/08/24 09:24:18 ouster Exp $ SPRITE (Berkeley)";
20 #endif
21
22 #include "tkconfig.h"
23 #include "tkint.h"
24 #include "tktext.h"
25
26 #include <assert.h>
27
28 /*
29 * The following structure describes how to display a range of characters.
30 * The information is generated by scanning all of the tags associated
31 * with the characters and combining that with default information for
32 * the overall widget. These structures form the hash keys for
33 * dInfoPtr->styleTable.
34 */
35
36 typedef struct StyleValues {
37 Tk_3DBorder border; /* Used for drawing background under text.
38 * NULL means use widget background. */
39 int borderWidth; /* Width of 3-D border for background. */
40 int relief; /* 3-D relief for background. */
41 Pixmap bgStipple; /* Stipple bitmap for background. None
42 * means draw solid. */
43 XColor *fgColor; /* Foreground color for text. */
44 XFontStruct *fontPtr; /* Font for displaying text. */
45 Pixmap fgStipple; /* Stipple bitmap for text and other
46 * foreground stuff. None means draw
47 * solid.*/
48 int underline; /* Non-zero means draw underline underneath
49 * text. */
50 } StyleValues;
51
52 /*
53 * The following structure extends the StyleValues structure above with
54 * graphics contexts used to actually draw the characters. The entries
55 * in dInfoPtr->styleTable point to structures of this type.
56 */
57
58 typedef struct Style {
59 int refCount; /* Number of times this structure is
60 * referenced in Chunks. */
61 GC bgGC; /* Graphics context for background. None
62 * unless background is stippled. */
63 GC fgGC; /* Graphics context for foreground. */
64 StyleValues *sValuePtr; /* Raw information from which GCs were
65 * derived. */
66 Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable. Used
67 * to delete entry. */
68 } Style;
69
70 /*
71 * The following structure describes a range of characters, all on the
72 * same line of the display (which also means the same line of the text
73 * widget) and all having the same display attributes.
74 */
75
76 typedef struct Chunk {
77 char *text; /* Characters to display. */
78 int numChars; /* Number of characters to display. */
79 Style *stylePtr; /* Style information used to display
80 * characters. */
81 int x; /* X-coordinate of pixel at which to display
82 * the characters. */
83 struct Chunk *nextPtr; /* Next in list of all chunks displayed on the
84 * same display line. */
85 } Chunk;
86
87 /*
88 * The following structure describes one line of the display, which may
89 * be either part or all of one line of the text.
90 */
91
92 typedef struct DLine {
93 TkTextLine *linePtr; /* Pointer to structure in B-tree that
94 * contains characters displayed in this
95 * line. */
96 int y; /* Y-position at which line is supposed to
97 * be drawn (topmost pixel of rectangular
98 * area occupied by line). */
99 int oldY; /* Y-position at which line currently
100 * appears on display. -1 means line isn't
101 * currently visible on display. This is
102 * used to move lines by scrolling rather
103 * than re-drawing. */
104 int height; /* Height of line, in pixels. */
105 int baseline; /* Offset of text baseline from y. */
106 Chunk *chunkPtr; /* Pointer to first chunk in list of all
107 * of those that are displayed on this
108 * line of the screen. */
109 struct DLine *nextPtr; /* Next in list of all display lines for
110 * this window. The list is sorted in
111 * order from top to bottom. Note: the
112 * next DLine doesn't always correspond
113 * to the next line of text: (a) can have
114 * multiple DLines for one text line, and
115 * (b) can have gaps where DLine's have been
116 * deleted because they're out of date. */
117 } DLine;
118
119 /*
120 * Overall display information for a text widget:
121 */
122
123 typedef struct DInfo {
124 Tcl_HashTable styleTable; /* Hash table that maps from StyleValues to
125 * Styles for this widget. */
126 DLine *dLinePtr; /* First in list of all display lines for
127 * this widget, in order from top to bottom. */
128 GC copyGC; /* Graphics context for copying from off-
129 * screen pixmaps onto screen. */
130 GC scrollGC; /* Graphics context for copying from one place
131 * in the window to another (scrolling):
132 * differs from copyGC in that we need to get
133 * GraphicsExpose events. */
134 int x; /* First x-coordinate that may be used for
135 * actually displaying line information.
136 * Leaves space for border, etc. */
137 int y; /* First y-coordinate that may be used for
138 * actually displaying line information.
139 * Leaves space for border, etc. */
140 int maxX; /* First x-coordinate to right of available
141 * space for displaying lines. */
142 int maxY; /* First y-coordinate to bottom of available
143 * space for displaying lines. */
144 int topOfEof; /* Top-most pixel (lowest y-value) that has
145 * been drawn in the appropriate fashion for
146 * the portion of the window after the last
147 * line of the text. This field is used to
148 * figure out when to redraw part or all of
149 * the eof field. */
150 int flags; /* Various flag values: see below for
151 * definitions. */
152 } DInfo;
153
154 /*
155 * Flag values for DInfo structures:
156 *
157 * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
158 * for this window are partially or completely
159 * out of date and need to be recomputed.
160 * REDRAW_PENDING: Means that a when-idle handler has been
161 * scheduled to update the display.
162 * REDRAW_BORDERS: Means window border or pad area has
163 * potentially been damaged and must be redrawn.
164 * REPICK_NEEDED: 1 means that the widget has been modified
165 * in a way that could change the current
166 * character (a different character might be
167 * under the mouse cursor now). Need to
168 * recompute the current character before
169 * the next redisplay.
170 */
171
172 #define DINFO_OUT_OF_DATE 1
173 #define REDRAW_PENDING 2
174 #define REDRAW_BORDERS 4
175 #define REPICK_NEEDED 8
176
177 /*
178 * Structures of the type defined below are used to keep track of
179 * tags while scanning through the text to create DLine structures.
180 */
181
182 typedef struct TagInfo {
183 int numTags; /* Number of tags currently active (the first
184 * entries at *tagPtr). */
185 int arraySize; /* Total number of entries at *tagPtr. We
186 * over-allocate the array to avoid continual
187 * reallocations. */
188 TkTextTag **tagPtrs; /* Pointer to array of pointers to active tags.
189 * Array has space for arraySize tags, and
190 * the first numTags are slots identify the
191 * active tags. Malloc'ed (but may be NULL). */
192 TkTextSearch search; /* Used to scan for tag transitions. Current
193 * state identifies next tag transition. */
194 } TagInfo;
195
196 /*
197 * The following counters keep statistics about redisplay that can be
198 * checked to see how clever this code is at reducing redisplays.
199 */
200
201 static int numRedisplays; /* Number of calls to DisplayText. */
202 static int linesRedrawn; /* Number of calls to DisplayDLine. */
203 static int numCopies; /* Number of calls to XCopyArea to copy part
204 * of the screen. */
205 static int damagedCopies; /* Number of times that XCopyAreas didn't
206 * completely work because some of the source
207 * information was damaged. */
208 static int TextUpdateTime = 100; // Added by Don.
209
210 /*
211 * Forward declarations for procedures defined later in this file:
212 */
213
214 static void ComputeStyleValues _ANSI_ARGS_((TkText *textPtr,
215 int numTags, TkTextTag **tagPtr,
216 StyleValues *sValuePtr));
217 static void DisplayDLine _ANSI_ARGS_((TkText *textPtr,
218 DLine *dlPtr, Pixmap pixmap));
219 static void DisplayText _ANSI_ARGS_((ClientData clientData));
220 static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr, int line));
221 static void FreeDLines _ANSI_ARGS_((TkText *textPtr,
222 DLine *firstPtr, DLine *lastPtr, int unlink));
223 static void FreeStyle _ANSI_ARGS_((Style *stylePtr));
224 static Style * GetStyle _ANSI_ARGS_((TkText *textPtr,
225 StyleValues *sValuePtr));
226 static DLine * LayoutLine _ANSI_ARGS_((TkText *textPtr, int line,
227 TkTextLine *linePtr, TagInfo *tInfoPtr));
228 static void ToggleTag _ANSI_ARGS_((TagInfo *tInfoPtr,
229 TkTextTag *tagPtr));
230 static void UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
231 \f
232 /*
233 *----------------------------------------------------------------------
234 *
235 * TkTextCreateDInfo --
236 *
237 * This procedure is called when a new text widget is created.
238 * Its job is to set up display-related information for the widget.
239 *
240 * Results:
241 * None.
242 *
243 * Side effects:
244 * A DInfo data structure is allocated and initialized and attached
245 * to textPtr.
246 *
247 *----------------------------------------------------------------------
248 */
249
250 void
251 TkTextCreateDInfo (
252 TkText *textPtr /* Overall information for text widget. */
253 )
254 {
255 register DInfo *dInfoPtr;
256 XGCValues gcValues;
257
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,
265 &gcValues);
266 dInfoPtr->topOfEof = 0;
267 dInfoPtr->flags = DINFO_OUT_OF_DATE;
268 textPtr->dInfoPtr = dInfoPtr;
269 }
270 \f
271 /*
272 *----------------------------------------------------------------------
273 *
274 * TkTextFreeDInfo --
275 *
276 * This procedure is called to free up all of the private display
277 * information kept by this file for a text widget.
278 *
279 * Results:
280 * None.
281 *
282 * Side effects:
283 * Lots of resources get freed.
284 *
285 *----------------------------------------------------------------------
286 */
287
288 void
289 TkTextFreeDInfo (
290 TkText *textPtr /* Overall information for text widget. */
291 )
292 {
293 register DInfo *dInfoPtr = textPtr->dInfoPtr;
294
295 /*
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.
300 */
301
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;
312 }
313 }
314 ckfree((char *) dInfoPtr);
315 }
316 \f
317 /*
318 *----------------------------------------------------------------------
319 *
320 * GetStyle --
321 *
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.
325 *
326 * Results:
327 * The return value is a pointer to a Style structure that
328 * corresponds to *sValuePtr.
329 *
330 * Side effects:
331 * A new entry may be created in the style table for the widget.
332 *
333 *----------------------------------------------------------------------
334 */
335
336 static Style *
337 GetStyle(
338 TkText *textPtr, /* Overall information about text widget. */
339 StyleValues *sValuePtr /* Information about desired style. */
340 )
341 {
342 Style *stylePtr;
343 Tcl_HashEntry *hPtr;
344 int new;
345 XGCValues gcValues;
346 unsigned long mask;
347
348 /*
349 * Use an existing style if there's one around that matches.
350 */
351
352 hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
353 (char *) sValuePtr, &new);
354 if (!new) {
355 stylePtr = (Style *) Tcl_GetHashValue(hPtr);
356 stylePtr->refCount++;
357 return stylePtr;
358 }
359
360 /*
361 * No existing style matched. Make a new one.
362 */
363
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);
372 } else {
373 stylePtr->bgGC = None;
374 }
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;
382 }
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);
388 return stylePtr;
389 }
390 \f
391 /*
392 *----------------------------------------------------------------------
393 *
394 * FreeStyle --
395 *
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.
399 *
400 * Results:
401 * None.
402 *
403 * Side effects:
404 * The storage and other resources associated with the style
405 * are freed up if no-one's still using it.
406 *
407 *----------------------------------------------------------------------
408 */
409
410 static void
411 FreeStyle (
412 register Style *stylePtr /* Information about style to be freed. */
413 )
414
415 {
416 stylePtr->refCount--;
417 if (stylePtr->refCount == 0) {
418 if (stylePtr->bgGC != None) {
419 Tk_FreeGC(stylePtr->bgGC);
420 }
421 Tk_FreeGC(stylePtr->fgGC);
422 Tcl_DeleteHashEntry(stylePtr->hPtr);
423 ckfree((char *) stylePtr);
424 }
425 }
426 \f
427 /*
428 *----------------------------------------------------------------------
429 *
430 * ComputeStyleValues --
431 *
432 * Given a list of tags that apply at a particular point, compute
433 * the StyleValues that correspond to that set of tags.
434 *
435 * Results:
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.
439 *
440 * Side effects:
441 * None.
442 *
443 *----------------------------------------------------------------------
444 */
445
446 static void
447 ComputeStyleValues(
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. */
452 )
453 {
454 register TkTextTag *tagPtr;
455
456 /*
457 * The variables below keep track of the highest-priority specification
458 * that has occurred for each of the various fields of the StyleValues.
459 */
460
461 int borderPrio, bgStipplePrio;
462 int fgPrio, fontPrio, fgStipplePrio;
463
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;
469
470 /*
471 * Scan through all of the tags, updating the StyleValues to hold
472 * the highest-priority information.
473 */
474
475 for ( ; numTags > 0; tagPtrPtr++, numTags--) {
476 tagPtr = *tagPtrPtr;
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;
482 }
483 if ((tagPtr->bgStipple != None)
484 && (tagPtr->priority > bgStipplePrio)) {
485 sValuePtr->bgStipple = tagPtr->bgStipple;
486 bgStipplePrio = tagPtr->priority;
487 }
488 if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
489 sValuePtr->fgColor = tagPtr->fgColor;
490 fgPrio = tagPtr->priority;
491 }
492 if ((tagPtr->fontPtr != None) && (tagPtr->priority > fontPrio)) {
493 sValuePtr->fontPtr = tagPtr->fontPtr;
494 fontPrio = tagPtr->priority;
495 }
496 if ((tagPtr->fgStipple != None)
497 && (tagPtr->priority > fgStipplePrio)) {
498 sValuePtr->fgStipple = tagPtr->fgStipple;
499 fgStipplePrio = tagPtr->priority;
500 }
501 if (tagPtr->underline) {
502 sValuePtr->underline = 1;
503 }
504 }
505 }
506 \f
507 /*
508 *----------------------------------------------------------------------
509 *
510 * LayoutLine --
511 *
512 * This procedure generates a linked list of one or more DLine
513 * structures, which describe how to display everything in one
514 * line of the text.
515 *
516 * Results:
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.
522 *
523 * Side effects:
524 * None.
525 *
526 *----------------------------------------------------------------------
527 */
528
529 static DLine *
530 LayoutLine (
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. */
537 )
538 {
539 DLine *firstLinePtr;
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;
546
547 firstLinePtr = NULL;
548
549 /*
550 * Each iteration of the loop below creates one DLine structure.
551 */
552
553 ch = 0;
554 while (1) {
555
556 /*
557 * Create and initialize a new DLine structure.
558 */
559
560 dlPtr = (DLine *) ckalloc(sizeof(DLine));
561 dlPtr->linePtr = linePtr;
562 dlPtr->y = 0;
563 dlPtr->oldY = -1;
564 dlPtr->chunkPtr = NULL;
565 dlPtr->nextPtr = NULL;
566 if (firstLinePtr == NULL) {
567 firstLinePtr = dlPtr;
568 } else {
569 lastLinePtr->nextPtr = dlPtr;
570 }
571 lastLinePtr = dlPtr;
572
573 /*
574 * Each iteration of the loop below creates one Chunk for the
575 * new display line.
576 */
577
578 x = textPtr->dInfoPtr->x;
579 maxX = textPtr->dInfoPtr->maxX;
580 ascent = descent = 0;
581 while (x < maxX) {
582 chunkPtr = (Chunk *) ckalloc(sizeof(Chunk));
583 chunkPtr->numChars = linePtr->numBytes - ch;
584 chunkPtr->text = linePtr->bytes + ch;
585 chunkPtr->x = x;
586 chunkPtr->nextPtr = NULL;
587 if (dlPtr->chunkPtr == NULL) {
588 dlPtr->chunkPtr = chunkPtr;
589 } else {
590 lastChunkPtr->nextPtr = chunkPtr;
591 }
592 lastChunkPtr = chunkPtr;
593
594 /*
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
598 * displayed.
599 */
600
601 while (1) {
602 int affectsDisplay;
603 TkTextTag *tagPtr;
604
605 if ((tInfoPtr->search.linePtr == NULL)
606 || (tInfoPtr->search.line1 > line)) {
607 break;
608 }
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);
615 }
616 } else {
617 if (affectsDisplay) {
618 chunkPtr->numChars = tInfoPtr->search.ch1 - ch;
619 break;
620 }
621 }
622 (void) TkBTreeNextTag(&tInfoPtr->search);
623 }
624
625 /*
626 * Create style information for this chunk.
627 */
628
629 ComputeStyleValues(textPtr, tInfoPtr->numTags, tInfoPtr->tagPtrs,
630 &styleValues);
631 chunkPtr->stylePtr = GetStyle(textPtr, &styleValues);
632
633 /*
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.
636 *
637 * 1. Make sure that at least one character is displayed on
638 * each line.
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
646 * space.
647 * - if no words fit at all, then use character-wrap for
648 * this DLine.
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).
652 */
653
654 charsThatFit = TkMeasureChars(styleValues.fontPtr,
655 chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
656 maxX, 0, &x);
657 if ((charsThatFit < chunkPtr->numChars) || (x >= maxX)) {
658 x = maxX;
659 chunkPtr->numChars = charsThatFit;
660 ch += charsThatFit;
661 if (ch < (linePtr->numBytes - 1)) {
662 if ((charsThatFit == 0) && (chunkPtr == dlPtr->chunkPtr)) {
663 chunkPtr->numChars = 1;
664 ch++;
665 } else if (textPtr->wrapMode == tkTextWordUid) {
666 if (isspace(chunkPtr->text[charsThatFit])) {
667 ch += 1; /* Include space on this line. */
668 } else {
669 register Chunk *chunkPtr2;
670 register char *p;
671 Chunk *spaceChunkPtr;
672 int count, space;
673
674 spaceChunkPtr = NULL;
675 space = 0;
676 for (chunkPtr2 = dlPtr->chunkPtr;
677 chunkPtr2 != NULL;
678 chunkPtr2 = chunkPtr2->nextPtr) {
679 for (count = chunkPtr2->numChars - 1,
680 p = chunkPtr2->text + count;
681 count >= 0; count--, p--) {
682 if (isspace(*p)) {
683 spaceChunkPtr = chunkPtr2;
684 space = count;
685 break;
686 }
687 }
688 }
689 if (spaceChunkPtr != NULL) {
690 spaceChunkPtr->numChars = space;
691 ch = (spaceChunkPtr->text + space + 1)
692 - linePtr->bytes;
693 if (chunkPtr != spaceChunkPtr) {
694 chunkPtr = spaceChunkPtr;
695 if (tInfoPtr->tagPtrs != NULL) {
696 ckfree((char *) tInfoPtr->tagPtrs);
697 }
698 tInfoPtr->tagPtrs = TkBTreeGetTags(
699 textPtr->tree, dlPtr->linePtr, ch,
700 &tInfoPtr->numTags);
701 TkBTreeStartSearch(textPtr->tree, line,
702 ch+1,
703 TkBTreeNumLines(textPtr->tree), 0,
704 (TkTextTag *) NULL,
705 &tInfoPtr->search);
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);
713 }
714 }
715 }
716 }
717 } else if (textPtr->wrapMode == tkTextNoneUid) {
718 chunkPtr->numChars++;
719 ch++;
720 }
721 }
722 } else {
723 ch += chunkPtr->numChars;
724 }
725
726 /*
727 * Update height information for use later in computing
728 * line's overall height and baseline.
729 */
730
731 if (styleValues.fontPtr->ascent > ascent) {
732 ascent = styleValues.fontPtr->ascent;
733 }
734 if (styleValues.fontPtr->descent > descent) {
735 descent = styleValues.fontPtr->descent;
736 }
737 }
738
739 dlPtr->height = ascent + descent;
740 dlPtr->baseline = ascent;
741
742 /*
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
746 * line.
747 */
748
749 if ((ch >= (linePtr->numBytes-1))
750 || (textPtr->wrapMode == tkTextNoneUid)) {
751 break;
752 }
753 }
754 return firstLinePtr;
755 }
756 \f
757 /*
758 *----------------------------------------------------------------------
759 *
760 * ToggleTag --
761 *
762 * Update information about tags to reflect a transition on a
763 * particular tag.
764 *
765 * Results:
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.
769 *
770 * Side effects:
771 * None.
772 *
773 *----------------------------------------------------------------------
774 */
775
776 static void
777 ToggleTag (
778 register TagInfo *tInfoPtr, /* Tag information to be updated. */
779 TkTextTag *tagPtr /* Tag to be toggled into or out of
780 * *tInfoPtr. */
781 )
782 {
783 register TkTextTag **tagPtrPtr;
784 int i;
785
786 for (i = tInfoPtr->numTags, tagPtrPtr = tInfoPtr->tagPtrs;
787 i > 0; i--, tagPtrPtr++) {
788 if (*tagPtrPtr == tagPtr) {
789 tInfoPtr->numTags--;
790 *tagPtrPtr = tInfoPtr->tagPtrs[tInfoPtr->numTags];
791 return;
792 }
793 }
794
795 /*
796 * Tag not currently in array. Grow the array if necessary, then
797 * add the tag to it.
798 */
799
800 if (tInfoPtr->numTags == tInfoPtr->arraySize) {
801 TkTextTag **newPtrs;
802
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);
809 }
810 tInfoPtr->tagPtrs = newPtrs;
811 tInfoPtr->arraySize += 10;
812 }
813 tInfoPtr->tagPtrs[tInfoPtr->numTags] = tagPtr;
814 tInfoPtr->numTags++;
815 }
816 \f
817 /*
818 *----------------------------------------------------------------------
819 *
820 * UpdateDisplayInfo --
821 *
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).
828 *
829 * Results:
830 * None.
831 *
832 * Side effects:
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.
836 *
837 *----------------------------------------------------------------------
838 */
839
840 static void
841 UpdateDisplayInfo (
842 TkText *textPtr /* Text widget to update. */
843 )
844 {
845 register DInfo *dInfoPtr = textPtr->dInfoPtr;
846 register DLine *dlPtr, *prevPtr, *dlPtr2;
847 TkTextLine *linePtr;
848 TagInfo tagInfo;
849 int line, y, maxY;
850
851 if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
852 return;
853 }
854 dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
855
856 linePtr = textPtr->topLinePtr;
857 dlPtr = dInfoPtr->dLinePtr;
858 tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
859 &tagInfo.numTags);
860 tagInfo.arraySize = tagInfo.numTags;
861
862 /*
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.
866 */
867
868 line = TkBTreeLineIndex(linePtr);
869 TkBTreeStartSearch(textPtr->tree, line, 1, TkBTreeNumLines(textPtr->tree),
870 0, (TkTextTag *) NULL, &tagInfo.search);
871 TkBTreeNextTag(&tagInfo.search);
872 prevPtr = NULL;
873 y = dInfoPtr->y;
874 maxY = dInfoPtr->maxY;
875 while ((linePtr != NULL) && (y < maxY)) {
876 register DLine *newPtr;
877 /*
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.
882 */
883
884 if ((dlPtr == NULL) || (dlPtr->linePtr != linePtr)) {
885 newPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
886 if (prevPtr == NULL) {
887 dInfoPtr->dLinePtr = newPtr;
888 } else {
889 prevPtr->nextPtr = newPtr;
890 }
891 for (dlPtr2 = newPtr; dlPtr2->nextPtr != NULL;
892 dlPtr2 = dlPtr2->nextPtr) {
893 /* Empty loop body. */
894 }
895 dlPtr2->nextPtr = dlPtr;
896 dlPtr = newPtr;
897 }
898
899 /*
900 * Skip to the next line, and update the y-position while
901 * skipping.
902 */
903
904 do {
905 dlPtr->y = y;
906 y += dlPtr->height;
907 prevPtr = dlPtr;
908 dlPtr = dlPtr->nextPtr;
909 } while ((dlPtr != NULL) && (dlPtr->linePtr == linePtr));
910 linePtr = TkBTreeNextLine(linePtr);
911 line++;
912 }
913
914 /*
915 * Delete any DLine structures that don't fit on the screen and free
916 * up the tag array.
917 */
918
919 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
920 if (tagInfo.tagPtrs != NULL) {
921 ckfree((char *) tagInfo.tagPtrs);
922 }
923
924 /*
925 * Update the vertical scrollbar, if there is one.
926 */
927
928 if (textPtr->yScrollCmd != NULL) {
929 int numLines, first, result, maxY, height;
930 char string[60];
931
932 /*
933 * Count the number of text lines on the screen.
934 */
935
936 maxY = 0;
937 for (numLines = 0, linePtr = NULL, dlPtr = dInfoPtr->dLinePtr;
938 dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
939 if (dlPtr->linePtr != linePtr) {
940 numLines++;
941 linePtr = dlPtr->linePtr;
942 }
943 maxY = dlPtr->y + dlPtr->height;
944 }
945
946 /*
947 * If the screen isn't completely full, then estimate the number of
948 * lines that would fit on it if it were full.
949 */
950
951 height = dInfoPtr->maxY - dInfoPtr->y;
952 if (numLines == 0) {
953 numLines = height /
954 (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
955 } else if (maxY < height) {
956 numLines = (numLines * height)/maxY;
957 }
958 /* DEH: be reasonable if dLinePtr is null */
959 if (dInfoPtr->dLinePtr == NULL) {
960 sprintf(string, " 0 0 0 0");
961 } else {
962 first = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
963 sprintf(string, " %d %d %d %d", TkBTreeNumLines(textPtr->tree),
964 numLines, first, first+numLines-1);
965 }
966 result = Tcl_VarEval(textPtr->interp, textPtr->yScrollCmd, string,
967 (char *) NULL);
968 if (result != TCL_OK) {
969 TkBindError(textPtr->interp);
970 }
971 }
972 }
973 \f
974 /*
975 *----------------------------------------------------------------------
976 *
977 * FreeDLines --
978 *
979 * This procedure is called to free up all of the resources
980 * associated with one or more DLine structures.
981 *
982 * Results:
983 * None.
984 *
985 * Side effects:
986 * Memory gets freed and various other resources are released.
987 *
988 *----------------------------------------------------------------------
989 */
990
991 static void
992 FreeDLines (
993 TkText *textPtr, /* Information about overall text
994 * widget. */
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. */
1004 )
1005 {
1006 register Chunk *chunkPtr, *nextChunkPtr;
1007 register DLine *nextDLinePtr;
1008
1009 if (unlink) {
1010 if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
1011 textPtr->dInfoPtr->dLinePtr = lastPtr;
1012 } else {
1013 register DLine *prevPtr;
1014 for (prevPtr = textPtr->dInfoPtr->dLinePtr;
1015 prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
1016 /* Empty loop body. */
1017 }
1018 prevPtr->nextPtr = lastPtr;
1019 }
1020 }
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);
1028 }
1029 ckfree((char *) firstPtr);
1030 firstPtr = nextDLinePtr;
1031 }
1032 }
1033 \f
1034 /*
1035 *----------------------------------------------------------------------
1036 *
1037 * DisplayDLine --
1038 *
1039 * This procedure is invoked to draw a single line on the
1040 * screen.
1041 *
1042 * Results:
1043 * None.
1044 *
1045 * Side effects:
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
1048 * one *text* line.
1049 *
1050 *----------------------------------------------------------------------
1051 */
1052
1053 static void
1054 DisplayDLine (
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
1059 * to hold line. */
1060 )
1061 {
1062 register Style *stylePtr;
1063 register StyleValues *sValuePtr;
1064 register Chunk *chunkPtr;
1065 DInfo *dInfoPtr = textPtr->dInfoPtr;
1066 Display *display;
1067 int width, height, count, x;
1068 XFontStruct *fontPtr;
1069
1070 /*
1071 * First, clear the area of the line to the background color for the
1072 * text widget.
1073 */
1074
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);
1078
1079 /*
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.
1086 */
1087
1088 for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
1089 chunkPtr = chunkPtr->nextPtr) {
1090
1091 /*
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
1098 * do stipples.
1099 */
1100
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;
1106 } else {
1107 width = Tk_Width(textPtr->tkwin) - chunkPtr->x;
1108 }
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);
1115 } else {
1116 Tk_Fill3DRectangle(display, pixmap, sValuePtr->border,
1117 chunkPtr->x, 0, width, dlPtr->height,
1118 sValuePtr->borderWidth, sValuePtr->relief);
1119 }
1120 }
1121 }
1122
1123 /*
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.
1129 */
1130
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);
1138 if (count < 0) {
1139 break;
1140 }
1141 if (count > chunkPtr->numChars) {
1142 continue;
1143 }
1144
1145 /*
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.
1154 */
1155 if ((count == chunkPtr->numChars)
1156 && (textPtr->wrapMode == tkTextCharUid)
1157 && (chunkPtr->text[count] != '\n')) {
1158 continue;
1159 }
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,
1176 0, TK_RELIEF_FLAT);
1177 }
1178
1179 }
1180 }
1181
1182 /*
1183 * Make another pass through all of the chunks to redraw all of
1184 * the text (and underlines, etc., if they're wanted).
1185 */
1186
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);
1199 }
1200 }
1201 }
1202
1203 /*
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.
1210 */
1211
1212 height = dlPtr->height;
1213 if ((height + dlPtr->y) > dInfoPtr->maxY) {
1214 height = dInfoPtr->maxY - dlPtr->y;
1215 }
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);
1220 linesRedrawn++;
1221 }
1222 \f
1223 /*
1224 *----------------------------------------------------------------------
1225 *
1226 * DisplayText --
1227 *
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
1230 * are out of date.
1231 *
1232 * Results:
1233 * None.
1234 *
1235 * Side effects:
1236 * Information is redrawn on the screen.
1237 *
1238 *----------------------------------------------------------------------
1239 */
1240
1241 static void
1242 DisplayText (
1243 ClientData clientData /* Information about widget. */
1244 )
1245 {
1246 register TkText *textPtr = (TkText *) clientData;
1247 DInfo *dInfoPtr = textPtr->dInfoPtr;
1248 Tk_Window tkwin;
1249 register DLine *dlPtr;
1250 Pixmap pixmap;
1251 int maxHeight;
1252 int bottomY = 0; /* Initialization needed only to stop
1253 * compiler warnings. */
1254
1255 assert(textPtr->updateTimerToken != NULL);
1256
1257 textPtr->updateTimerToken = 0;
1258
1259 if ((textPtr->tkwin == NULL) || !Tk_IsMapped(textPtr->tkwin)
1260 || (dInfoPtr->maxX <= dInfoPtr->x)
1261 || (dInfoPtr->maxY <= dInfoPtr->y)) {
1262 goto done;
1263 }
1264 numRedisplays++;
1265
1266 /*
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.
1272 */
1273
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) {
1281 return;
1282 }
1283 }
1284
1285 /*
1286 * First recompute what's supposed to be displayed.
1287 */
1288
1289 UpdateDisplayInfo(textPtr);
1290
1291 /*
1292 * Redraw the borders if that's needed.
1293 */
1294
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);
1300 }
1301
1302 /*
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).
1305 */
1306
1307 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
1308 register DLine *dlPtr2;
1309 int offset, height;
1310
1311 if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
1312 || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
1313 continue;
1314 }
1315
1316 /*
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.
1320 */
1321
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)) {
1329 break;
1330 }
1331 height += dlPtr2->height;
1332 }
1333
1334 /*
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.
1338 */
1339
1340 if ((dlPtr->y + height) > dInfoPtr->maxY) {
1341 height = dInfoPtr->maxY - dlPtr->y;
1342 }
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);
1348 numCopies++;
1349 while (1) {
1350 dlPtr->oldY = dlPtr->y;
1351 if (dlPtr->nextPtr == dlPtr2) {
1352 break;
1353 }
1354 dlPtr = dlPtr->nextPtr;
1355 }
1356
1357 /*
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.
1361 */
1362
1363 while (1) {
1364 XEvent event;
1365
1366 XWindowEvent(Tk_Display(textPtr->tkwin),
1367 Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
1368 if (event.type == NoExpose) {
1369 break;
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) {
1375 damagedCopies++;
1376 break;
1377 }
1378 } else if (event.type == Expose) {
1379 /*
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.
1387 */
1388
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);
1395 } else {
1396 panic("DisplayText received unknown exposure event");
1397 }
1398 }
1399 }
1400
1401 /*
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
1405 * displays.
1406 */
1407
1408 maxHeight = -1;
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;
1413 }
1414 bottomY = dlPtr->y + dlPtr->height;
1415 }
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;
1425 }
1426 }
1427 XFreePixmap(Tk_Display(textPtr->tkwin), pixmap);
1428 }
1429
1430 /*
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).
1433 */
1434
1435 if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
1436 dInfoPtr->topOfEof = dInfoPtr->maxY;
1437 }
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);
1443 }
1444 dInfoPtr->topOfEof = bottomY;
1445 if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
1446 dInfoPtr->topOfEof = dInfoPtr->maxY;
1447 }
1448
1449 done:
1450 dInfoPtr->flags &= ~(REDRAW_PENDING|REDRAW_BORDERS);
1451 }
1452 \f
1453 /*
1454 *----------------------------------------------------------------------
1455 *
1456 * TkTextRedrawRegion --
1457 *
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.
1461 *
1462 * Results:
1463 * None.
1464 *
1465 * Side effects:
1466 * Information will eventually be redrawn on the screen.
1467 *
1468 *----------------------------------------------------------------------
1469 */
1470
1471 /* ARGSUSED */
1472 void
1473 TkTextRedrawRegion (
1474 TkText *textPtr, /* Widget record for text widget. */
1475 int x,
1476 int y, /* Coordinates of upper-left corner of area
1477 * to be redrawn, in pixels relative to
1478 * textPtr's window. */
1479 int width,
1480 int height /* Width and height of area to be redrawn. */
1481 )
1482 {
1483 register DLine *dlPtr;
1484 DInfo *dInfoPtr = textPtr->dInfoPtr;
1485 int maxY;
1486
1487 /*
1488 * Find all lines that overlap the given region and mark them for
1489 * redisplay.
1490 */
1491
1492 maxY = y + height;
1493 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1494 dlPtr = dlPtr->nextPtr) {
1495 if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
1496 dlPtr->oldY = -1;
1497 }
1498 }
1499 if (dInfoPtr->topOfEof < maxY) {
1500 dInfoPtr->topOfEof = maxY;
1501 }
1502
1503 /*
1504 * Schedule the redisplay operation if there isn't one already
1505 * scheduled.
1506 */
1507
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(
1515 TextUpdateTime,
1516 DisplayText,
1517 (ClientData) textPtr);
1518 }
1519 }
1520 if ((x < dInfoPtr->x) || (y < dInfoPtr->y)
1521 || ((x + width) > dInfoPtr->maxX) || (maxY > dInfoPtr->maxY)) {
1522 dInfoPtr->flags |= REDRAW_BORDERS;
1523 }
1524 }
1525 \f
1526 /*
1527 *----------------------------------------------------------------------
1528 *
1529 * TkTextLinesChanged --
1530 *
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
1536 * are still valid.
1537 *
1538 * Results:
1539 * None.
1540 *
1541 * Side effects:
1542 * The indicated lines will be redisplayed at some point in the
1543 * future (the actual redisplay is scheduled as a when-idle handler).
1544 *
1545 *----------------------------------------------------------------------
1546 */
1547
1548 void
1549 TkTextLinesChanged (
1550 TkText *textPtr, /* Widget record for text widget. */
1551 int first, /* Index of first line that must be
1552 * redisplayed. */
1553 int last /* Index of last line to redisplay. */
1554 )
1555 {
1556 DInfo *dInfoPtr = textPtr->dInfoPtr;
1557 DLine *firstPtr, *lastPtr;
1558
1559 /*
1560 * Find the DLines corresponding to first and last+1.
1561 */
1562
1563 firstPtr = FindDLine(dInfoPtr->dLinePtr, first);
1564 if (firstPtr == NULL) {
1565 return;
1566 }
1567 lastPtr = FindDLine(dInfoPtr->dLinePtr, last+1);
1568 if (firstPtr == lastPtr) {
1569 return;
1570 }
1571
1572 /*
1573 * Delete all the DLines from first up through last (but not including
1574 * lastPtr, which points to the first line *outside* the range).
1575 */
1576
1577 FreeDLines(textPtr, firstPtr, lastPtr, 1);
1578
1579 /*
1580 * Schedule both a redisplay and a recomputation of display information.
1581 */
1582
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(
1589 TextUpdateTime,
1590 DisplayText,
1591 (ClientData) textPtr);
1592 }
1593 }
1594 dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
1595 }
1596 \f
1597 /*
1598 *----------------------------------------------------------------------
1599 *
1600 * TkTextRedrawTag --
1601 *
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.
1606 *
1607 * Results:
1608 * None.
1609 *
1610 * Side effects:
1611 * Information on the screen may be redrawn, and the layout of
1612 * the screen may change.
1613 *
1614 *----------------------------------------------------------------------
1615 */
1616
1617 void
1618 TkTextRedrawTag (
1619 TkText *textPtr, /* Widget record for text widget. */
1620 int line1,
1621 int ch1, /* Index of first character in range of
1622 * interest. */
1623 int line2,
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. */
1629 )
1630 {
1631 register DLine *dlPtr;
1632 DLine *endPtr;
1633 int topLine, tagOn;
1634 TkTextSearch search;
1635 DInfo *dInfoPtr = textPtr->dInfoPtr;
1636
1637 /*
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).
1640 */
1641
1642 dlPtr = dInfoPtr->dLinePtr;
1643 if (dlPtr == NULL) {
1644 return;
1645 }
1646 topLine = TkBTreeLineIndex(dlPtr->linePtr);
1647 if (topLine > line1) {
1648 line1 = topLine;
1649 ch1 = 0;
1650 }
1651
1652 /*
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.
1656 */
1657
1658 TkBTreeStartSearch(textPtr->tree, line1, ch1+1, line2, ch2,
1659 tagPtr, &search);
1660 tagOn = TkBTreeCharTagged(search.linePtr, ch1, tagPtr);
1661 if (tagOn != withTag) {
1662 if (!TkBTreeNextTag(&search)) {
1663 return;
1664 }
1665 }
1666
1667 /*
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.
1672 */
1673
1674 while (1) {
1675 /*
1676 * Find the first DLine structure in the range.
1677 */
1678
1679 dlPtr = FindDLine(dlPtr, search.line1);
1680 if (dlPtr == NULL) {
1681 break;
1682 }
1683
1684 /*
1685 * Find the first DLine structure that's past the end of the range.
1686 */
1687
1688 if (TkBTreeNextTag(&search)) {
1689 endPtr = FindDLine(dlPtr,
1690 (search.ch1 > 0) ? (search.line1 + 1) : search.line1);
1691 } else {
1692 endPtr = FindDLine(dlPtr,
1693 (ch2 > 0) ? (search.line2 + 1) : search.line2);
1694 }
1695
1696 /*
1697 * Delete all of the display lines in the range, so that they'll
1698 * be re-layed out and redrawn.
1699 */
1700
1701 FreeDLines(textPtr, dlPtr, endPtr, 1);
1702 dlPtr = endPtr;
1703
1704 /*
1705 * Find the first text line in the next range.
1706 */
1707
1708 if (!TkBTreeNextTag(&search)) {
1709 break;
1710 }
1711 }
1712
1713 /*
1714 * Lastly, schedule a redisplay and layout recalculation if they
1715 * aren't already pending.
1716 */
1717
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(
1724 TextUpdateTime,
1725 DisplayText,
1726 (ClientData) textPtr);
1727 }
1728 }
1729 dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
1730 }
1731 \f
1732 /*
1733 *----------------------------------------------------------------------
1734 *
1735 * TkTextRelayoutWindow --
1736 *
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.
1742 *
1743 * Results:
1744 * None.
1745 *
1746 * Side effects:
1747 * All the display information will be recomputed for the window
1748 * and the window will be redrawn.
1749 *
1750 *----------------------------------------------------------------------
1751 */
1752
1753 void
1754 TkTextRelayoutWindow (
1755 TkText *textPtr /* Widget record for text widget. */
1756 )
1757 {
1758 DInfo *dInfoPtr = textPtr->dInfoPtr;
1759
1760 /*
1761 * Throw away all the current layout information.
1762 */
1763
1764 FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
1765 dInfoPtr->dLinePtr = NULL;
1766
1767 /*
1768 * Recompute some overall things for the layout.
1769 */
1770
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;
1776
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(
1783 TextUpdateTime,
1784 DisplayText,
1785 (ClientData) textPtr);
1786 }
1787 }
1788 dInfoPtr->flags |= REDRAW_PENDING | REDRAW_BORDERS | DINFO_OUT_OF_DATE | REPICK_NEEDED;
1789 }
1790 \f
1791 /*
1792 *----------------------------------------------------------------------
1793 *
1794 * TkTextSetView --
1795 *
1796 * This procedure is called to specify what lines are to be
1797 * displayed in a text widget.
1798 *
1799 * Results:
1800 * None.
1801 *
1802 * Side effects:
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".
1806 *
1807 *----------------------------------------------------------------------
1808 */
1809
1810 void
1811 TkTextSetView (
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. */
1820 )
1821 {
1822 DInfo *dInfoPtr = textPtr->dInfoPtr;
1823 register DLine *dlPtr, *dlPtr2;
1824 TkTextLine *linePtr;
1825 int curTopLine, curBotLine;
1826 int bottomY;
1827 TagInfo tagInfo;
1828 #define CLOSE_LINES 5
1829
1830 if (!pickPlace) {
1831 /*
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
1839 * it all.
1840 */
1841
1842 dlPtr = FindDLine(dInfoPtr->dLinePtr, line);
1843 if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
1844 FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
1845 }
1846
1847 textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
1848 goto scheduleUpdate;
1849 }
1850
1851 /*
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.
1856 */
1857
1858 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
1859 UpdateDisplayInfo(textPtr);
1860 }
1861 linePtr = TkBTreeFindLine(textPtr->tree, line);
1862 /* DEH: return if dlPtr is null */
1863 if ((dlPtr = dInfoPtr->dLinePtr) == NULL)
1864 return;
1865 for (; ; dlPtr = dlPtr->nextPtr) {
1866 if (dlPtr->nextPtr == NULL) {
1867 break;
1868 }
1869 if ((dlPtr->linePtr == linePtr)
1870 && (dlPtr->nextPtr->linePtr != linePtr)) {
1871 break;
1872 }
1873 }
1874 if ((dlPtr->linePtr == linePtr)
1875 && ((dlPtr->y + dlPtr->height) <= dInfoPtr->maxY)) {
1876 return;
1877 }
1878
1879 /*
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.
1883 */
1884
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;
1891 }
1892 } else {
1893 /*
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)
1898 */
1899
1900 curBotLine = TkBTreeLineIndex(dlPtr->linePtr);
1901 if (line <= (curBotLine+5)) {
1902 bottomY = dInfoPtr->maxY;
1903 }
1904 }
1905
1906 /*
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.
1914 */
1915
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,
1920 &tagInfo.numTags);
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;
1928 }
1929 FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
1930 if (tagInfo.tagPtrs != NULL) {
1931 ckfree((char *) tagInfo.tagPtrs);
1932 }
1933 if ((bottomY <= 0) || (line <= 0)) {
1934 break;
1935 }
1936 }
1937
1938 scheduleUpdate:
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(
1945 TextUpdateTime,
1946 DisplayText,
1947 (ClientData) textPtr);
1948 }
1949 }
1950 dInfoPtr->flags |= REDRAW_PENDING | DINFO_OUT_OF_DATE | REPICK_NEEDED;
1951 }
1952 \f
1953 /*
1954 *----------------------------------------------------------------------
1955 *
1956 * FindDLine --
1957 *
1958 * This procedure is called to find the DLine corresponding to a
1959 * given text line.
1960 *
1961 * Results:
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.
1965 *
1966 * Side effects:
1967 * None.
1968 *
1969 *----------------------------------------------------------------------
1970 */
1971
1972 static DLine *
1973 FindDLine (
1974 register DLine *dlPtr, /* Pointer to first in list of DLines
1975 * to search. */
1976 int line /* Line number in text that is desired. */
1977 )
1978 {
1979 TkTextLine *linePtr;
1980 int thisLine;
1981
1982 if (dlPtr == NULL) {
1983 return NULL;
1984 }
1985 thisLine = TkBTreeLineIndex(dlPtr->linePtr);
1986 while (thisLine < line) {
1987 /*
1988 * This DLine isn't the right one. Go on to the next DLine
1989 * (skipping multiple DLine's for the same text line).
1990 */
1991
1992 linePtr = dlPtr->linePtr;
1993 do {
1994 dlPtr = dlPtr->nextPtr;
1995 if (dlPtr == NULL) {
1996 return NULL;
1997 }
1998 } while (dlPtr->linePtr == linePtr);
1999
2000 /*
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).
2004 */
2005
2006 do {
2007 linePtr = TkBTreeNextLine(linePtr);
2008 thisLine++;
2009 if (linePtr == NULL) {
2010 panic("FindDLine reached end of text");
2011 }
2012 } while (linePtr != dlPtr->linePtr);
2013 }
2014 return dlPtr;
2015 }
2016 \f
2017 /*
2018 *----------------------------------------------------------------------
2019 *
2020 * TkTextCharAtLoc --
2021 *
2022 * Given an (x,y) coordinate on the screen, find the location of
2023 * the closest character to that location.
2024 *
2025 * Results:
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
2029 * character.
2030 *
2031 * Side effects:
2032 * None.
2033 *
2034 *----------------------------------------------------------------------
2035 */
2036
2037 TkTextLine *
2038 TkTextCharAtLoc (
2039 TkText *textPtr, /* Widget record for text widget. */
2040 int x,
2041 int y, /* Pixel coordinates of point in widget's
2042 * window. */
2043 int *chPtr /* Place to store index-within-line of
2044 * closest character. */
2045 )
2046 {
2047 DInfo *dInfoPtr = textPtr->dInfoPtr;
2048 register DLine *dlPtr;
2049 register Chunk *chunkPtr;
2050 int count;
2051 int endX;
2052
2053 /*
2054 * Make sure that all of the layout information about what's
2055 * displayed where on the screen is up-to-date.
2056 */
2057
2058 if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2059 UpdateDisplayInfo(textPtr);
2060 }
2061
2062 /*
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.
2065 */
2066
2067 if (y < dInfoPtr->y) {
2068 y = dInfoPtr->y;
2069 x = dInfoPtr->x;
2070 } else if (y >= dInfoPtr->topOfEof) {
2071 y = dInfoPtr->topOfEof;
2072 x = dInfoPtr->maxX;
2073 }
2074 for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
2075 if (y > (dlPtr->y + dlPtr->height)) {
2076 if (dlPtr->nextPtr != NULL) {
2077 continue;
2078 }
2079
2080 /*
2081 * The coordinates are off the bottom of the window. Adjust
2082 * them to refer to the lower-right character on the window.
2083 */
2084
2085 y = dlPtr->y;
2086 x = dInfoPtr->maxX;
2087 }
2088 for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
2089 if ((chunkPtr->nextPtr == NULL) || (chunkPtr->nextPtr->x > x)) {
2090 break;
2091 }
2092 }
2093 count = TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
2094 chunkPtr->text, chunkPtr->numChars, chunkPtr->x, x, 0, &endX);
2095 if (count >= chunkPtr->numChars) {
2096 /*
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.
2101 */
2102
2103 if ((dlPtr->nextPtr != NULL)
2104 && (dlPtr->nextPtr->chunkPtr->text
2105 == (chunkPtr->text + chunkPtr->numChars))) {
2106 count = chunkPtr->numChars-1;
2107 }
2108 if (endX >= dInfoPtr->maxX) {
2109 count = chunkPtr->numChars-1;
2110 }
2111 }
2112 *chPtr = count + (chunkPtr->text - dlPtr->linePtr->bytes);
2113 return dlPtr->linePtr;
2114 }
2115 panic("TkTextCharAtLoc ran out of lines");
2116 return (TkTextLine *) NULL;
2117 }
Impressum, Datenschutz