1 /*
2 * tkFont.c --
3 *
4 * This file maintains a database of looked-up fonts for the Tk
5 * toolkit, in order to avoid round-trips to the server to map
6 * font names to XFontStructs. It also provides several utility
7 * procedures for measuring and displaying text.
8 *
9 * Copyright (c) 1990-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11 *
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * SCCS: @(#) tkFont.c 1.38 96/02/15 18:53:31
16 */
17
18 #include "tkInt.h"
19
20 /*
21 * This module caches extra information about fonts in addition to
22 * what X already provides. The extra information is used by the
23 * TkMeasureChars procedure, and consists of two parts: a type and
24 * a width. The type is one of the following:
25 *
26 * NORMAL: Standard character.
27 * TAB: Tab character: output enough space to
28 * get to next tab stop.
29 * NEWLINE: Newline character: don't output anything more
30 * on this line (character has infinite width).
31 * REPLACE: This character doesn't print: instead of
32 * displaying character, display a replacement
33 * sequence like "\n" (for those characters where
34 * ANSI C defines such a sequence) or a sequence
35 * of the form "\xdd" where dd is the hex equivalent
36 * of the character.
37 * SKIP: Don't display anything for this character. This
38 * is only used where the font doesn't contain
39 * all the characters needed to generate
40 * replacement sequences.
41 * The width gives the total width of the displayed character or
42 * sequence: for replacement sequences, it gives the width of the
43 * sequence.
44 */
45
46 #define NORMAL 1
47 #define TAB 2
48 #define NEWLINE 3
49 #define REPLACE 4
50 #define SKIP 5
51
52 /*
53 * One of the following data structures exists for each font that is
54 * currently active. The structure is indexed with two hash tables,
55 * one based on font name and one based on XFontStruct address.
56 */
57
58 typedef struct {
59 XFontStruct *fontStructPtr; /* X information about font. */
60 Display *display; /* Display to which font belongs. */
61 int refCount; /* Number of active uses of this font. */
62 char *types; /* Malloc'ed array giving types of all
63 * chars in the font (may be NULL). */
64 unsigned char *widths; /* Malloc'ed array giving widths of all
65 * chars in the font (may be NULL). */
66 int tabWidth; /* Width of tabs in this font. */
67 Tcl_HashEntry *nameHashPtr; /* Entry in name-based hash table (needed
68 * when deleting this structure). */
69 } TkFont;
70
71 /*
72 * Hash table for name -> TkFont mapping, and key structure used to
73 * index into that table:
74 */
75
76 static Tcl_HashTable nameTable;
77 typedef struct {
78 Tk_Uid name; /* Name of font. */
79 Display *display; /* Display for which font is valid. */
80 } NameKey;
81
82 /*
83 * Hash table for font struct -> TkFont mapping. This table is
84 * indexed by the XFontStruct address.
85 */
86
87 static Tcl_HashTable fontTable;
88
89 static int initialized = 0; /* 0 means static structures haven't been
90 * initialized yet. */
91
92 /*
93 * To speed up TkMeasureChars, the variables below keep the last
94 * mapping from (XFontStruct *) to (TkFont *).
95 */
96
97 static TkFont *lastFontPtr = NULL;
98 static XFontStruct *lastFontStructPtr = NULL;
99
100 /*
101 * Characters used when displaying control sequences.
102 */
103
104 static char hexChars[] = "0123456789abcdefxtnvr\\";
105
106 /*
107 * The following table maps some control characters to sequences
108 * like '\n' rather than '\x10'. A zero entry in the table means
109 * no such mapping exists, and the table only maps characters
110 * less than 0x10.
111 */
112
113 static char mapChars[] = {
114 0, 0, 0, 0, 0, 0, 0,
115 'a', 'b', 't', 'n', 'v', 'f', 'r',
116 0
117 };
118
119 /*
120 * Forward declarations for procedures defined in this file:
121 */
122
123 static void FontInit _ANSI_ARGS_((void));
124 static void SetFontMetrics _ANSI_ARGS_((TkFont *fontPtr));
125
126 /*
127 *----------------------------------------------------------------------
128 *
129 * Tk_GetFontStruct --
130 *
131 * Given a string name for a font, map the name to an XFontStruct
132 * describing the font.
133 *
134 * Results:
135 * The return value is normally a pointer to the font description
136 * for the desired font. If an error occurs in mapping the string
137 * to a font, then an error message will be left in interp->result
138 * and NULL will be returned.
139 *
140 * Side effects:
141 * The font is added to an internal database with a reference count.
142 * For each call to this procedure, there should eventually be a call
143 * to Tk_FreeFontStruct, so that the database is cleaned up when fonts
144 * aren't in use anymore.
145 *
146 *----------------------------------------------------------------------
147 */
148
149 XFontStruct *
Tk_GetFontStruct(interp,tkwin,name)150 Tk_GetFontStruct(interp, tkwin, name)
151 Tcl_Interp *interp; /* Place to leave error message if
152 * font can't be found. */
153 Tk_Window tkwin; /* Window in which font will be used. */
154 Tk_Uid name; /* Name of font (in form suitable for
155 * passing to XLoadQueryFont). */
156 {
157 NameKey nameKey;
158 Tcl_HashEntry *nameHashPtr, *fontHashPtr;
159 int new;
160 register TkFont *fontPtr;
161 XFontStruct *fontStructPtr;
162
163 if (!initialized) {
164 FontInit();
165 }
166
167 /*
168 * First, check to see if there's already a mapping for this font
169 * name.
170 */
171
172 nameKey.name = name;
173 nameKey.display = Tk_Display(tkwin);
174 nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);
175 if (!new) {
176 fontPtr = (TkFont *) Tcl_GetHashValue(nameHashPtr);
177 fontPtr->refCount++;
178 return fontPtr->fontStructPtr;
179 }
180
181 /*
182 * The name isn't currently known. Map from the name to a font, and
183 * add a new structure to the database.
184 */
185
186 fontStructPtr = XLoadQueryFont(nameKey.display, name);
187 if (fontStructPtr == NULL) {
188 Tcl_DeleteHashEntry(nameHashPtr);
189 Tcl_AppendResult(interp, "font \"", name, "\" doesn't exist",
190 (char *) NULL);
191 return NULL;
192 }
193 fontPtr = (TkFont *) ckalloc(sizeof(TkFont));
194 fontPtr->display = nameKey.display;
195 fontPtr->fontStructPtr = fontStructPtr;
196 fontPtr->refCount = 1;
197 fontPtr->types = NULL;
198 fontPtr->widths = NULL;
199 fontPtr->nameHashPtr = nameHashPtr;
200 fontHashPtr = Tcl_CreateHashEntry(&fontTable, (char *) fontStructPtr, &new);
201 if (!new) {
202 panic("XFontStruct already registered in Tk_GetFontStruct");
203 }
204 Tcl_SetHashValue(nameHashPtr, fontPtr);
205 Tcl_SetHashValue(fontHashPtr, fontPtr);
206 return fontPtr->fontStructPtr;
207 }
208
209 /*
210 *--------------------------------------------------------------
211 *
212 * Tk_NameOfFontStruct --
213 *
214 * Given a font, return a textual string identifying it.
215 *
216 * Results:
217 * If font was created by Tk_GetFontStruct, then the return
218 * value is the "string" that was used to create it.
219 * Otherwise the return value is a string giving the X
220 * identifier for the font. The storage for the returned
221 * string is only guaranteed to persist up until the next
222 * call to this procedure.
223 *
224 * Side effects:
225 * None.
226 *
227 *--------------------------------------------------------------
228 */
229
230 char *
Tk_NameOfFontStruct(fontStructPtr)231 Tk_NameOfFontStruct(fontStructPtr)
232 XFontStruct *fontStructPtr; /* Font whose name is desired. */
233 {
234 Tcl_HashEntry *fontHashPtr;
235 TkFont *fontPtr;
236 void *ptr;
237 static char string[20];
238
239 if (!initialized) {
240 printid:
241 sprintf(string, "font id 0x%x", (unsigned int) fontStructPtr->fid);
242 return string;
243 }
244 fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
245 if (fontHashPtr == NULL) {
246 goto printid;
247 }
248 fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
249 ptr = fontPtr->nameHashPtr->key.words;
250 return ((NameKey *) ptr)->name;
251 }
252
253 /*
254 *----------------------------------------------------------------------
255 *
256 * Tk_FreeFontStruct --
257 *
258 * This procedure is called to release a font allocated by
259 * Tk_GetFontStruct.
260 *
261 * Results:
262 * None.
263 *
264 * Side effects:
265 * The reference count associated with font is decremented, and
266 * the font is officially deallocated if no-one is using it
267 * anymore.
268 *
269 *----------------------------------------------------------------------
270 */
271
272 void
Tk_FreeFontStruct(fontStructPtr)273 Tk_FreeFontStruct(fontStructPtr)
274 XFontStruct *fontStructPtr; /* Font to be released. */
275 {
276 Tcl_HashEntry *fontHashPtr;
277 register TkFont *fontPtr;
278
279 if (!initialized) {
280 panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
281 }
282
283 fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
284 if (fontHashPtr == NULL) {
285 panic("Tk_FreeFontStruct received unknown font argument");
286 }
287 fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
288 fontPtr->refCount--;
289 if (fontPtr->refCount == 0) {
290 /*
291 * We really should call Tk_FreeXId below to release the font's
292 * resource identifier, but this seems to cause problems on
293 * many X servers (as of 5/1/94) where the font resource isn't
294 * really released, which can cause the wrong font to be used
295 * later on. So, don't release the resource id after all, even
296 * though this results in an id leak.
297 *
298 * Tk_FreeXId(fontPtr->display, (XID) fontPtr->fontStructPtr->fid);
299 */
300
301 XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
302 Tcl_DeleteHashEntry(fontPtr->nameHashPtr);
303 Tcl_DeleteHashEntry(fontHashPtr);
304 if (fontPtr->types != NULL) {
305 ckfree(fontPtr->types);
306 }
307 if (fontPtr->widths != NULL) {
308 ckfree((char *) fontPtr->widths);
309 }
310 ckfree((char *) fontPtr);
311 lastFontStructPtr = NULL;
312 }
313 }
314
315 /*
316 *----------------------------------------------------------------------
317 *
318 * FontInit --
319 *
320 * Initialize the structure used for font management.
321 *
322 * Results:
323 * None.
324 *
325 * Side effects:
326 * Read the code.
327 *
328 *----------------------------------------------------------------------
329 */
330
331 static void
FontInit()332 FontInit()
333 {
334 initialized = 1;
335 Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));
336 Tcl_InitHashTable(&fontTable, TCL_ONE_WORD_KEYS);
337 }
338
339 /*
340 *--------------------------------------------------------------
341 *
342 * SetFontMetrics --
343 *
344 * This procedure is called to fill in the "widths" and "types"
345 * arrays for a font.
346 *
347 * Results:
348 * None.
349 *
350 * Side effects:
351 * FontPtr gets modified to hold font metric information.
352 *
353 *--------------------------------------------------------------
354 */
355
356 static void
SetFontMetrics(fontPtr)357 SetFontMetrics(fontPtr)
358 register TkFont *fontPtr; /* Font structure in which to
359 * set metrics. */
360 {
361 int i, replaceOK;
362 register XFontStruct *fontStructPtr = fontPtr->fontStructPtr;
363 char *p;
364
365 /*
366 * Pass 1: initialize the arrays.
367 */
368
369 fontPtr->types = (char *) ckalloc(256);
370 fontPtr->widths = (unsigned char *) ckalloc(256);
371 for (i = 0; i < 256; i++) {
372 fontPtr->types[i] = REPLACE;
373 }
374
375 /*
376 * Pass 2: for all characters that exist in the font and are
377 * not control characters, fill in the type and width
378 * information.
379 */
380
381 for (i = 0; i < 256; i++) {
382 if ((i == 0177) || (i < fontStructPtr->min_char_or_byte2)
383 || (i > fontStructPtr->max_char_or_byte2)) {
384 continue;
385 }
386 fontPtr->types[i] = NORMAL;
387 if (fontStructPtr->per_char == NULL) {
388 fontPtr->widths[i] = fontStructPtr->min_bounds.width;
389 } else {
390 fontPtr->widths[i] = fontStructPtr->per_char[i
391 - fontStructPtr->min_char_or_byte2].width;
392 }
393 }
394
395 /*
396 * Pass 3: fill in information for characters that have to
397 * be replaced with "\xhh" or "\n" strings. If the font doesn't
398 * have the characters needed for this, then just use the
399 * font's default character.
400 */
401
402 replaceOK = 1;
403 for (p = hexChars; *p != 0; p++) {
404 if (fontPtr->types[*p] != NORMAL) {
405 replaceOK = 0;
406 break;
407 }
408 }
409 for (i = 0; i < 256; i++) {
410 if (fontPtr->types[i] != REPLACE) {
411 continue;
412 }
413 if (replaceOK) {
414 if ((i < sizeof(mapChars)) && (mapChars[i] != 0)) {
415 fontPtr->widths[i] = fontPtr->widths['\\']
416 + fontPtr->widths[mapChars[i]];
417 } else {
418 fontPtr->widths[i] = fontPtr->widths['\\']
419 + fontPtr->widths['x']
420 + fontPtr->widths[hexChars[i & 0xf]]
421 + fontPtr->widths[hexChars[(i>>4) & 0xf]];
422 }
423 } else {
424 fontPtr->types[i] = SKIP;
425 fontPtr->widths[i] = 0;
426 }
427 }
428
429 /*
430 * Lastly, fill in special information for newline and tab.
431 */
432
433 fontPtr->types['\n'] = NEWLINE;
434 fontPtr->types['\t'] = TAB;
435 fontPtr->widths['\t'] = 0;
436 if (fontPtr->types['0'] == NORMAL) {
437 fontPtr->tabWidth = 8*fontPtr->widths['0'];
438 } else {
439 fontPtr->tabWidth = 8*fontStructPtr->max_bounds.width;
440 }
441
442 /*
443 * Make sure the tab width isn't zero (some fonts may not have enough
444 * information to set a reasonable tab width).
445 */
446
447 if (fontPtr->tabWidth == 0) {
448 fontPtr->tabWidth = 1;
449 }
450 }
451
452 /*
453 *--------------------------------------------------------------
454 *
455 * TkMeasureChars --
456 *
457 * Measure the number of characters from a string that
458 * will fit in a given horizontal span. The measurement
459 * is done under the assumption that TkDisplayChars will
460 * be used to actually display the characters.
461 *
462 * Results:
463 * The return value is the number of characters from source
464 * that fit in the span given by startX and maxX. *nextXPtr
465 * is filled in with the x-coordinate at which the first
466 * character that didn't fit would be drawn, if it were to
467 * be drawn.
468 *
469 * Side effects:
470 * None.
471 *
472 *--------------------------------------------------------------
473 */
474
475 int
TkMeasureChars(fontStructPtr,source,maxChars,startX,maxX,tabOrigin,flags,nextXPtr)476 TkMeasureChars(fontStructPtr, source, maxChars, startX, maxX,
477 tabOrigin, flags, nextXPtr)
478 XFontStruct *fontStructPtr; /* Font in which to draw characters. */
479 char *source; /* Characters to be displayed. Need not
480 * be NULL-terminated. */
481 int maxChars; /* Maximum # of characters to consider from
482 * source. */
483 int startX; /* X-position at which first character will
484 * be drawn. */
485 int maxX; /* Don't consider any character that would
486 * cross this x-position. */
487 int tabOrigin; /* X-location that serves as "origin" for
488 * tab stops. */
489 int flags; /* Various flag bits OR-ed together.
490 * TK_WHOLE_WORDS means stop on a word boundary
491 * (just before a space character) if
492 * possible. TK_AT_LEAST_ONE means always
493 * return a value of at least one, even
494 * if the character doesn't fit.
495 * TK_PARTIAL_OK means it's OK to display only
496 * a part of the last character in the line.
497 * TK_NEWLINES_NOT_SPECIAL means that newlines
498 * are treated just like other control chars:
499 * they don't terminate the line.
500 * TK_IGNORE_TABS means give all tabs zero
501 * width. */
502 int *nextXPtr; /* Return x-position of terminating
503 * character here. */
504 {
505 register TkFont *fontPtr;
506 register char *p; /* Current character. */
507 register int c;
508 char *term; /* Pointer to most recent character that
509 * may legally be a terminating character. */
510 int termX; /* X-position just after term. */
511 int curX; /* X-position corresponding to p. */
512 int newX; /* X-position corresponding to p+1. */
513 int type;
514 int rem;
515
516 /*
517 * Find the TkFont structure for this font, and make sure its
518 * font metrics exist.
519 */
520
521 if (lastFontStructPtr == fontStructPtr) {
522 fontPtr = lastFontPtr;
523 } else {
524 Tcl_HashEntry *fontHashPtr;
525
526 if (!initialized) {
527 badArg:
528 panic("TkMeasureChars received unknown font argument");
529 }
530
531 fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
532 if (fontHashPtr == NULL) {
533 goto badArg;
534 }
535 fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
536 lastFontStructPtr = fontPtr->fontStructPtr;
537 lastFontPtr = fontPtr;
538 }
539 if (fontPtr->types == NULL) {
540 SetFontMetrics(fontPtr);
541 }
542
543 /*
544 * Scan the input string one character at a time, until a character
545 * is found that crosses maxX.
546 */
547
548 newX = curX = startX;
549 termX = 0; /* Not needed, but eliminates compiler warning. */
550 term = source;
551 for (p = source, c = *p & 0xff; maxChars > 0; p++, maxChars--) {
552 type = fontPtr->types[c];
553 if ((type == NORMAL) || (type == REPLACE)) {
554 newX += fontPtr->widths[c];
555 } else if (type == TAB) {
556 if (!(flags & TK_IGNORE_TABS)) {
557 newX += fontPtr->tabWidth;
558 rem = (newX - tabOrigin) % fontPtr->tabWidth;
559 if (rem < 0) {
560 rem += fontPtr->tabWidth;
561 }
562 newX -= rem;
563 }
564 } else if (type == NEWLINE) {
565 if (flags & TK_NEWLINES_NOT_SPECIAL) {
566 newX += fontPtr->widths[c];
567 } else {
568 break;
569 }
570 } else if (type != SKIP) {
571 panic("Unknown type %d in TkMeasureChars", type);
572 }
573 if (newX > maxX) {
574 break;
575 }
576 if (maxChars > 1) {
577 c = p[1] & 0xff;
578 } else {
579 /*
580 * Can't look at next character: it could be in uninitialized
581 * memory.
582 */
583
584 c = 0;
585 }
586 if (isspace(UCHAR(c)) || (c == 0)) {
587 term = p+1;
588 termX = newX;
589 }
590 curX = newX;
591 }
592
593 /*
594 * P points to the first character that doesn't fit in the desired
595 * span. Use the flags to figure out what to return.
596 */
597
598 if ((flags & TK_PARTIAL_OK) && (curX < maxX)) {
599 curX = newX;
600 p++;
601 }
602 if ((flags & TK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
603 && !isspace(UCHAR(*term))) {
604 term = p;
605 termX = curX;
606 if (term == source) {
607 term++;
608 termX = newX;
609 }
610 } else if ((maxChars == 0) || !(flags & TK_WHOLE_WORDS)) {
611 term = p;
612 termX = curX;
613 }
614 *nextXPtr = termX;
615 return term-source;
616 }
617
618 /*
619 *--------------------------------------------------------------
620 *
621 * TkDisplayChars --
622 *
623 * Draw a string of characters on the screen, converting
624 * tabs to the right number of spaces and control characters
625 * to sequences of the form "\xhh" where hh are two hex
626 * digits.
627 *
628 * Results:
629 * None.
630 *
631 * Side effects:
632 * Information gets drawn on the screen.
633 *
634 *--------------------------------------------------------------
635 */
636
637 void
TkDisplayChars(display,drawable,gc,fontStructPtr,string,numChars,x,y,tabOrigin,flags)638 TkDisplayChars(display, drawable, gc, fontStructPtr, string, numChars,
639 x, y, tabOrigin, flags)
640 Display *display; /* Display on which to draw. */
641 Drawable drawable; /* Window or pixmap in which to draw. */
642 GC gc; /* Graphics context for actually drawing
643 * characters. */
644 XFontStruct *fontStructPtr; /* Font used in GC; must have been allocated
645 * by Tk_GetFontStruct. Used to compute sizes
646 * of tabs, etc. */
647 char *string; /* Characters to be displayed. */
648 int numChars; /* Number of characters to display from
649 * string. */
650 int x, y; /* Coordinates at which to draw string. */
651 int tabOrigin; /* X-location that serves as "origin" for
652 * tab stops. */
653 int flags; /* Flags to control display. Only
654 * TK_NEWLINES_NOT_SPECIAL and TK_IGNORE_TABS
655 * are supported right now. See
656 * TkMeasureChars for information about it. */
657 {
658 register TkFont *fontPtr;
659 register char *p; /* Current character being scanned. */
660 register int c;
661 int type;
662 char *start; /* First character waiting to be displayed. */
663 int startX; /* X-coordinate corresponding to start. */
664 int curX; /* X-coordinate corresponding to p. */
665 char replace[10];
666 int rem;
667
668 /*
669 * Find the TkFont structure for this font, and make sure its
670 * font metrics exist.
671 */
672
673 if (lastFontStructPtr == fontStructPtr) {
674 fontPtr = lastFontPtr;
675 } else {
676 Tcl_HashEntry *fontHashPtr;
677
678 if (!initialized) {
679 badArg:
680 panic("TkDisplayChars received unknown font argument");
681 }
682
683 fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
684 if (fontHashPtr == NULL) {
685 goto badArg;
686 }
687 fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
688 lastFontStructPtr = fontPtr->fontStructPtr;
689 lastFontPtr = fontPtr;
690 }
691 if (fontPtr->types == NULL) {
692 SetFontMetrics(fontPtr);
693 }
694
695 /*
696 * Scan the string one character at a time. Display control
697 * characters immediately, but delay displaying normal characters
698 * in order to pass many characters to the server all together.
699 */
700
701 startX = curX = x;
702 start = string;
703 for (p = string; numChars > 0; numChars--, p++) {
704 c = *p & 0xff;
705 type = fontPtr->types[c];
706 if (type == NORMAL) {
707 curX += fontPtr->widths[c];
708 continue;
709 }
710 if (p != start) {
711 XDrawString(display, drawable, gc, startX, y, start, p - start);
712 startX = curX;
713 }
714 if (type == TAB) {
715 if (!(flags & TK_IGNORE_TABS)) {
716 curX += fontPtr->tabWidth;
717 rem = (curX - tabOrigin) % fontPtr->tabWidth;
718 if (rem < 0) {
719 rem += fontPtr->tabWidth;
720 }
721 curX -= rem;
722 }
723 } else if (type == REPLACE ||
724 (type == NEWLINE && flags & TK_NEWLINES_NOT_SPECIAL)) {
725 if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
726 replace[0] = '\\';
727 replace[1] = mapChars[c];
728 XDrawString(display, drawable, gc, startX, y, replace, 2);
729 curX += fontPtr->widths[replace[0]]
730 + fontPtr->widths[replace[1]];
731 } else {
732 replace[0] = '\\';
733 replace[1] = 'x';
734 replace[2] = hexChars[(c >> 4) & 0xf];
735 replace[3] = hexChars[c & 0xf];
736 XDrawString(display, drawable, gc, startX, y, replace, 4);
737 curX += fontPtr->widths[replace[0]]
738 + fontPtr->widths[replace[1]]
739 + fontPtr->widths[replace[2]]
740 + fontPtr->widths[replace[3]];
741 }
742 } else if (type == NEWLINE) {
743 y += fontStructPtr->ascent + fontStructPtr->descent;
744 curX = x;
745 } else if (type != SKIP) {
746 panic("Unknown type %d in TkDisplayChars", type);
747 }
748 startX = curX;
749 start = p+1;
750 }
751
752 /*
753 * At the very end, there may be one last batch of normal characters
754 * to display.
755 */
756
757 if (p != start) {
758 XDrawString(display, drawable, gc, startX, y, start, p - start);
759 }
760 }
761
762 /*
763 *----------------------------------------------------------------------
764 *
765 * TkUnderlineChars --
766 *
767 * This procedure draws an underline for a given range of characters
768 * in a given string, using appropriate information for the string's
769 * font. It doesn't draw the characters (which are assumed to have
770 * been displayed previously); it just draws the underline.
771 *
772 * Results:
773 * None.
774 *
775 * Side effects:
776 * Information gets displayed in "drawable".
777 *
778 *----------------------------------------------------------------------
779 */
780
781 void
TkUnderlineChars(display,drawable,gc,fontStructPtr,string,x,y,tabOrigin,flags,firstChar,lastChar)782 TkUnderlineChars(display, drawable, gc, fontStructPtr, string, x, y,
783 tabOrigin, flags, firstChar, lastChar)
784 Display *display; /* Display on which to draw. */
785 Drawable drawable; /* Window or pixmap in which to draw. */
786 GC gc; /* Graphics context for actually drawing
787 * underline. */
788 XFontStruct *fontStructPtr; /* Font used in GC; must have been allocated
789 * by Tk_GetFontStruct. Used to character
790 * dimensions, etc. */
791 char *string; /* String containing characters to be
792 * underlined. */
793 int x, y; /* Coordinates at which first character of
794 * string is drawn. */
795 int tabOrigin; /* X-location that serves as "origin" for
796 * tab stops. */
797 int flags; /* Flags that were passed to TkDisplayChars. */
798 int firstChar; /* Index of first character to underline. */
799 int lastChar; /* Index of last character to underline. */
800 {
801 int xUnder, yUnder, width, height;
802 unsigned long value;
803
804 /*
805 * First compute the vertical span of the underline, using font
806 * properties if they exist.
807 */
808
809 if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
810 yUnder = y + value;
811 } else {
812 yUnder = y + fontStructPtr->max_bounds.descent/2;
813 }
814 if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
815 height = value;
816 } else {
817 height = 2;
818 }
819
820 /*
821 * Now compute the horizontal span of the underline.
822 */
823
824 TkMeasureChars(fontStructPtr, string, firstChar, x, (int) 1000000,
825 tabOrigin, flags, &xUnder);
826 TkMeasureChars(fontStructPtr, string+firstChar, lastChar+1-firstChar,
827 xUnder, (int) 1000000, tabOrigin, flags, &width);
828 width -= xUnder;
829
830 XFillRectangle(display, drawable, gc, xUnder, yUnder,
831 (unsigned int) width, (unsigned int) height);
832 }
833
834 /*
835 *----------------------------------------------------------------------
836 *
837 * TkComputeTextGeometry --
838 *
839 * This procedure computes the amount of screen space needed to
840 * display a multi-line string of text.
841 *
842 * Results:
843 * There is no return value. The dimensions of the screen area
844 * needed to display the text are returned in *widthPtr, and *heightPtr.
845 *
846 * Side effects:
847 * None.
848 *
849 *----------------------------------------------------------------------
850 */
851
852 void
TkComputeTextGeometry(fontStructPtr,string,numChars,wrapLength,widthPtr,heightPtr)853 TkComputeTextGeometry(fontStructPtr, string, numChars, wrapLength,
854 widthPtr, heightPtr)
855 XFontStruct *fontStructPtr; /* Font that will be used to display text. */
856 char *string; /* String whose dimensions are to be
857 * computed. */
858 int numChars; /* Number of characters to consider from
859 * string. */
860 int wrapLength; /* Longest permissible line length, in
861 * pixels. <= 0 means no automatic wrapping:
862 * just let lines get as long as needed. */
863 int *widthPtr; /* Store width of string here. */
864 int *heightPtr; /* Store height of string here. */
865 {
866 int thisWidth, maxWidth, numLines;
867 char *p;
868
869 if (wrapLength <= 0) {
870 wrapLength = INT_MAX;
871 }
872 maxWidth = 0;
873 for (numLines = 1, p = string; (p - string) < numChars; numLines++) {
874 p += TkMeasureChars(fontStructPtr, p, numChars - (p - string), 0,
875 wrapLength, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
876 if (thisWidth > maxWidth) {
877 maxWidth = thisWidth;
878 }
879 if (*p == 0) {
880 break;
881 }
882
883 /*
884 * If the character that didn't fit in this line was a white
885 * space character then skip it.
886 */
887
888 if (isspace(UCHAR(*p))) {
889 p++;
890 }
891 }
892 *widthPtr = maxWidth;
893 *heightPtr = numLines * (fontStructPtr->ascent + fontStructPtr->descent);
894 }
895
896 /*
897 *----------------------------------------------------------------------
898 *
899 * TkDisplayText --
900 *
901 * Display a text string on one or more lines.
902 *
903 * Results:
904 * None.
905 *
906 * Side effects:
907 * The text given by "string" gets displayed at the given location
908 * in the given drawable with the given font etc.
909 *
910 *----------------------------------------------------------------------
911 */
912
913 void
TkDisplayText(display,drawable,fontStructPtr,string,numChars,x,y,length,justify,underline,gc)914 TkDisplayText(display, drawable, fontStructPtr, string, numChars, x, y,
915 length, justify, underline, gc)
916 Display *display; /* X display to use for drawing text. */
917 Drawable drawable; /* Window or pixmap in which to draw the
918 * text. */
919 XFontStruct *fontStructPtr; /* Font that determines geometry of text
920 * (should be same as font in gc). */
921 char *string; /* String to display; may contain embedded
922 * newlines. */
923 int numChars; /* Number of characters to use from string. */
924 int x, y; /* Pixel coordinates within drawable of
925 * upper left corner of display area. */
926 int length; /* Line length in pixels; used to compute
927 * word wrap points and also for
928 * justification. Must be > 0. */
929 Tk_Justify justify; /* How to justify lines. */
930 int underline; /* Index of character to underline, or < 0
931 * for no underlining. */
932 GC gc; /* Graphics context to use for drawing text. */
933 {
934 char *p;
935 int charsThisLine, lengthThisLine, xThisLine;
936
937 /*
938 * Work through the string one line at a time. Display each line
939 * in four steps:
940 * 1. Compute the line's length.
941 * 2. Figure out where to display the line for justification.
942 * 3. Display the line.
943 * 4. Underline one character if needed.
944 */
945
946 y += fontStructPtr->ascent;
947 for (p = string; numChars > 0; ) {
948 charsThisLine = TkMeasureChars(fontStructPtr, p, numChars, 0, length,
949 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &lengthThisLine);
950 if (justify == TK_JUSTIFY_LEFT) {
951 xThisLine = x;
952 } else if (justify == TK_JUSTIFY_CENTER) {
953 xThisLine = x + (length - lengthThisLine)/2;
954 } else {
955 xThisLine = x + (length - lengthThisLine);
956 }
957 TkDisplayChars(display, drawable, gc, fontStructPtr, p, charsThisLine,
958 xThisLine, y, xThisLine, 0);
959 if ((underline >= 0) && (underline < charsThisLine)) {
960 TkUnderlineChars(display, drawable, gc, fontStructPtr, p,
961 xThisLine, y, xThisLine, 0, underline, underline);
962 }
963 p += charsThisLine;
964 numChars -= charsThisLine;
965 underline -= charsThisLine;
966 y += fontStructPtr->ascent + fontStructPtr->descent;
967
968 /*
969 * If the character that didn't fit was a space character, skip it.
970 */
971
972 if (isspace(UCHAR(*p))) {
973 p++;
974 numChars--;
975 underline--;
976 }
977 }
978 }
979