]>
Commit | Line | Data |
---|---|---|
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. | |
7 | * | |
8 | * Copyright 1990 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/tkFont.c,v 1.21 92/06/15 14:00:19 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif | |
21 | ||
22 | #include "tkconfig.h" | |
23 | #include "tkint.h" | |
24 | ||
25 | /* | |
26 | * This module caches extra information about fonts in addition to | |
27 | * what X already provides. The extra information is used by the | |
28 | * TkMeasureChars procedure, and consists of two parts: a type and | |
29 | * a width. The type is one of the following: | |
30 | * | |
31 | * NORMAL: Standard character. | |
32 | * TAB: Tab character: output enough space to | |
33 | * get to next tab stop. | |
34 | * NEWLINE: Newline character: don't output anything more | |
35 | * on this line (character has infinite width). | |
36 | * REPLACE: This character doesn't print: instead of | |
37 | * displaying character, display a replacement | |
38 | * sequence of the form "\xdd" where dd is the | |
39 | * hex equivalent of the character. | |
40 | * SKIP: Don't display anything for this character. This | |
41 | * is only used where the font doesn't contain | |
42 | * all the characters needed to generate | |
43 | * replacement sequences. | |
44 | * The width gives the total width of the displayed character or | |
45 | * sequence: for replacement sequences, it gives the width of the | |
46 | * sequence. | |
47 | */ | |
48 | ||
49 | #define NORMAL 1 | |
50 | #define TAB 2 | |
51 | #define NEWLINE 3 | |
52 | #define REPLACE 4 | |
53 | #define SKIP 5 | |
54 | ||
55 | /* | |
56 | * One of the following data structures exists for each font that is | |
57 | * currently active. The structure is indexed with two hash tables, | |
58 | * one based on font name and one based on XFontStruct address. | |
59 | */ | |
60 | ||
61 | typedef struct { | |
62 | XFontStruct *fontStructPtr; /* X information about font. */ | |
63 | Display *display; /* Display to which font belongs. */ | |
64 | int refCount; /* Number of active uses of this font. */ | |
65 | char *types; /* Malloc'ed array giving types of all | |
66 | * chars in the font (may be NULL). */ | |
67 | unsigned char *widths; /* Malloc'ed array giving widths of all | |
68 | * chars in the font (may be NULL). */ | |
69 | int tabWidth; /* Width of tabs in this font. */ | |
70 | Tcl_HashEntry *nameHashPtr; /* Entry in name-based hash table (needed | |
71 | * when deleting this structure). */ | |
72 | } TkFont; | |
73 | ||
74 | /* | |
75 | * Hash table for name -> TkFont mapping, and key structure used to | |
76 | * index into that table: | |
77 | */ | |
78 | ||
79 | static Tcl_HashTable nameTable; | |
80 | typedef struct { | |
81 | Tk_Uid name; /* Name of font. */ | |
82 | Display *display; /* Display for which font is valid. */ | |
83 | } NameKey; | |
84 | ||
85 | /* | |
86 | * Hash table for font struct -> TkFont mapping. This table is | |
87 | * indexed by the XFontStruct address. | |
88 | */ | |
89 | ||
90 | static Tcl_HashTable fontTable; | |
91 | ||
92 | static int initialized = 0; /* 0 means static structures haven't been | |
93 | * initialized yet. */ | |
94 | ||
95 | /* | |
96 | * To speed up TkMeasureChars, the variables below keep the last | |
97 | * mapping from (XFontStruct *) to (TkFont *). | |
98 | */ | |
99 | ||
100 | static TkFont *lastFontPtr = NULL; | |
101 | static XFontStruct *lastFontStructPtr = NULL; | |
102 | ||
103 | /* | |
104 | * Characters used when displaying control sequences as their | |
105 | * hex equivalents. | |
106 | */ | |
107 | ||
108 | static char hexChars[] = "0123456789abcdefx\\"; | |
109 | ||
110 | /* | |
111 | * Forward declarations for procedures defined in this file: | |
112 | */ | |
113 | ||
114 | static void FontInit _ANSI_ARGS_((void)); | |
115 | static void SetFontMetrics _ANSI_ARGS_((TkFont *fontPtr)); | |
116 | \f | |
117 | /* | |
118 | *---------------------------------------------------------------------- | |
119 | * | |
120 | * Tk_GetFontStruct -- | |
121 | * | |
122 | * Given a string name for a font, map the name to an XFontStruct | |
123 | * describing the font. | |
124 | * | |
125 | * Results: | |
126 | * The return value is normally a pointer to the font description | |
127 | * for the desired font. If an error occurs in mapping the string | |
128 | * to a font, then an error message will be left in interp->result | |
129 | * and NULL will be returned. | |
130 | * | |
131 | * Side effects: | |
132 | * The font is added to an internal database with a reference count. | |
133 | * For each call to this procedure, there should eventually be a call | |
134 | * to Tk_FreeFontStruct, so that the database is cleaned up when fonts | |
135 | * aren't in use anymore. | |
136 | * | |
137 | *---------------------------------------------------------------------- | |
138 | */ | |
139 | ||
140 | XFontStruct * | |
141 | Tk_GetFontStruct(interp, tkwin, name) | |
142 | Tcl_Interp *interp; /* Place to leave error message if | |
143 | * font can't be found. */ | |
144 | Tk_Window tkwin; /* Window in which font will be used. */ | |
145 | Tk_Uid name; /* Name of font (in form suitable for | |
146 | * passing to XLoadQueryFont). */ | |
147 | { | |
148 | NameKey nameKey; | |
149 | Tcl_HashEntry *nameHashPtr, *fontHashPtr; | |
150 | int new; | |
151 | register TkFont *fontPtr; | |
152 | XFontStruct *fontStructPtr; | |
153 | ||
154 | if (!initialized) { | |
155 | FontInit(); | |
156 | } | |
157 | ||
158 | /* | |
159 | * First, check to see if there's already a mapping for this font | |
160 | * name. | |
161 | */ | |
162 | ||
163 | nameKey.name = name; | |
164 | nameKey.display = Tk_Display(tkwin); | |
165 | nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new); | |
166 | if (!new) { | |
167 | fontPtr = (TkFont *) Tcl_GetHashValue(nameHashPtr); | |
168 | fontPtr->refCount++; | |
169 | return fontPtr->fontStructPtr; | |
170 | } | |
171 | ||
172 | /* | |
173 | * The name isn't currently known. Map from the name to a font, and | |
174 | * add a new structure to the database. | |
175 | */ | |
176 | ||
177 | fontStructPtr = XLoadQueryFont(nameKey.display, name); | |
178 | if (fontStructPtr == NULL) { | |
179 | Tcl_DeleteHashEntry(nameHashPtr); | |
180 | Tcl_AppendResult(interp, "font \"", name, "\" doesn't exist", | |
181 | (char *) NULL); | |
182 | return NULL; | |
183 | } | |
184 | fontPtr = (TkFont *) ckalloc(sizeof(TkFont)); | |
185 | fontPtr->display = nameKey.display; | |
186 | fontPtr->fontStructPtr = fontStructPtr; | |
187 | fontPtr->refCount = 1; | |
188 | fontPtr->types = NULL; | |
189 | fontPtr->widths = NULL; | |
190 | fontPtr->nameHashPtr = nameHashPtr; | |
191 | fontHashPtr = Tcl_CreateHashEntry(&fontTable, (char *) fontStructPtr, &new); | |
192 | if (!new) { | |
193 | panic("XFontStruct already registered in Tk_GetFontStruct"); | |
194 | } | |
195 | Tcl_SetHashValue(nameHashPtr, fontPtr); | |
196 | Tcl_SetHashValue(fontHashPtr, fontPtr); | |
197 | return fontPtr->fontStructPtr; | |
198 | } | |
199 | \f | |
200 | /* | |
201 | *-------------------------------------------------------------- | |
202 | * | |
203 | * Tk_NameOfFontStruct -- | |
204 | * | |
205 | * Given a font, return a textual string identifying it. | |
206 | * | |
207 | * Results: | |
208 | * If font was created by Tk_GetFontStruct, then the return | |
209 | * value is the "string" that was used to create it. | |
210 | * Otherwise the return value is a string giving the X | |
211 | * identifier for the font. The storage for the returned | |
212 | * string is only guaranteed to persist up until the next | |
213 | * call to this procedure. | |
214 | * | |
215 | * Side effects: | |
216 | * None. | |
217 | * | |
218 | *-------------------------------------------------------------- | |
219 | */ | |
220 | ||
221 | char * | |
222 | Tk_NameOfFontStruct(fontStructPtr) | |
223 | XFontStruct *fontStructPtr; /* Font whose name is desired. */ | |
224 | { | |
225 | Tcl_HashEntry *fontHashPtr; | |
226 | TkFont *fontPtr; | |
227 | static char string[20]; | |
228 | ||
229 | if (!initialized) { | |
230 | printid: | |
231 | sprintf(string, "font id 0x%x", fontStructPtr->fid); | |
232 | return string; | |
233 | } | |
234 | fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr); | |
235 | if (fontHashPtr == NULL) { | |
236 | goto printid; | |
237 | } | |
238 | fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr); | |
239 | return ((NameKey *) fontPtr->nameHashPtr->key.words)->name; | |
240 | } | |
241 | \f | |
242 | /* | |
243 | *---------------------------------------------------------------------- | |
244 | * | |
245 | * Tk_FreeFontStruct -- | |
246 | * | |
247 | * This procedure is called to release a font allocated by | |
248 | * Tk_GetFontStruct. | |
249 | * | |
250 | * Results: | |
251 | * None. | |
252 | * | |
253 | * Side effects: | |
254 | * The reference count associated with font is decremented, and | |
255 | * the font is officially deallocated if no-one is using it | |
256 | * anymore. | |
257 | * | |
258 | *---------------------------------------------------------------------- | |
259 | */ | |
260 | ||
261 | void | |
262 | Tk_FreeFontStruct(fontStructPtr) | |
263 | XFontStruct *fontStructPtr; /* Font to be released. */ | |
264 | { | |
265 | Tcl_HashEntry *fontHashPtr; | |
266 | register TkFont *fontPtr; | |
267 | ||
268 | if (!initialized) { | |
269 | panic("Tk_FreeFontStruct called before Tk_GetFontStruct"); | |
270 | } | |
271 | ||
272 | fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr); | |
273 | if (fontHashPtr == NULL) { | |
274 | panic("Tk_FreeFontStruct received unknown font argument"); | |
275 | } | |
276 | fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr); | |
277 | fontPtr->refCount--; | |
278 | if (fontPtr->refCount == 0) { | |
279 | XFreeFont(fontPtr->display, fontPtr->fontStructPtr); | |
280 | Tcl_DeleteHashEntry(fontPtr->nameHashPtr); | |
281 | Tcl_DeleteHashEntry(fontHashPtr); | |
282 | if (fontPtr->types != NULL) { | |
283 | ckfree(fontPtr->types); | |
284 | } | |
285 | if (fontPtr->widths != NULL) { | |
286 | ckfree((char *) fontPtr->widths); | |
287 | } | |
288 | ckfree((char *) fontPtr); | |
289 | lastFontStructPtr = NULL; | |
290 | } | |
291 | } | |
292 | \f | |
293 | /* | |
294 | *---------------------------------------------------------------------- | |
295 | * | |
296 | * FontInit -- | |
297 | * | |
298 | * Initialize the structure used for font management. | |
299 | * | |
300 | * Results: | |
301 | * None. | |
302 | * | |
303 | * Side effects: | |
304 | * Read the code. | |
305 | * | |
306 | *---------------------------------------------------------------------- | |
307 | */ | |
308 | ||
309 | static void | |
310 | FontInit() | |
311 | { | |
312 | initialized = 1; | |
313 | Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int)); | |
314 | Tcl_InitHashTable(&fontTable, TCL_ONE_WORD_KEYS); | |
315 | } | |
316 | \f | |
317 | /* | |
318 | *-------------------------------------------------------------- | |
319 | * | |
320 | * SetFontMetrics -- | |
321 | * | |
322 | * This procedure is called to fill in the "widths" and "types" | |
323 | * arrays for a font. | |
324 | * | |
325 | * Results: | |
326 | * None. | |
327 | * | |
328 | * Side effects: | |
329 | * FontPtr gets modified to hold font metric information. | |
330 | * | |
331 | *-------------------------------------------------------------- | |
332 | */ | |
333 | ||
334 | static void | |
335 | SetFontMetrics(fontPtr) | |
336 | register TkFont *fontPtr; /* Font structure in which to | |
337 | * set metrics. */ | |
338 | { | |
339 | int i, replaceOK, baseWidth; | |
340 | register XFontStruct *fontStructPtr = fontPtr->fontStructPtr; | |
341 | char *p; | |
342 | ||
343 | /* | |
344 | * Pass 1: initialize the arrays. | |
345 | */ | |
346 | ||
347 | fontPtr->types = (char *) ckalloc(256); | |
348 | fontPtr->widths = (unsigned char *) ckalloc(256); | |
349 | for (i = 0; i < 256; i++) { | |
350 | fontPtr->types[i] = REPLACE; | |
351 | } | |
352 | ||
353 | /* | |
354 | * Pass 2: for all characters that exist in the font and are | |
355 | * not control characters, fill in the type and width | |
356 | * information. | |
357 | */ | |
358 | ||
359 | for (i = ' '; i < 256; i++) { | |
360 | if ((i == 0177) || (i < fontStructPtr->min_char_or_byte2) | |
361 | || (i > fontStructPtr->max_char_or_byte2)) { | |
362 | continue; | |
363 | } | |
364 | fontPtr->types[i] = NORMAL; | |
365 | if (fontStructPtr->per_char == NULL) { | |
366 | fontPtr->widths[i] = fontStructPtr->min_bounds.width; | |
367 | } else { | |
368 | fontPtr->widths[i] = fontStructPtr->per_char[i | |
369 | - fontStructPtr->min_char_or_byte2].width; | |
370 | } | |
371 | } | |
372 | ||
373 | /* | |
374 | * Pass 3: fill in information for characters that have to | |
375 | * be replaced with "\xhh" strings. If the font doesn't | |
376 | * have the characters needed for this, then just use the | |
377 | * font's default character. | |
378 | */ | |
379 | ||
380 | replaceOK = 1; | |
381 | baseWidth = fontPtr->widths['\\'] + fontPtr->widths['x']; | |
382 | for (p = hexChars; *p != 0; p++) { | |
383 | if (fontPtr->types[*p] != NORMAL) { | |
384 | replaceOK = 0; | |
385 | break; | |
386 | } | |
387 | } | |
388 | for (i = 0; i < 256; i++) { | |
389 | if (fontPtr->types[i] != REPLACE) { | |
390 | continue; | |
391 | } | |
392 | if (replaceOK) { | |
393 | fontPtr->widths[i] = baseWidth | |
394 | + fontPtr->widths[hexChars[i & 0xf]] | |
395 | + fontPtr->widths[hexChars[(i>>4) & 0xf]]; | |
396 | } else { | |
397 | fontPtr->types[i] = SKIP; | |
398 | fontPtr->widths[i] = 0; | |
399 | } | |
400 | } | |
401 | ||
402 | /* | |
403 | * Lastly, fill in special information for newline and tab. | |
404 | */ | |
405 | ||
406 | fontPtr->types['\n'] = NEWLINE; | |
407 | fontPtr->widths['\n'] = 0; | |
408 | fontPtr->types['\t'] = TAB; | |
409 | fontPtr->widths['\t'] = 0; | |
410 | if (fontPtr->types['0'] == NORMAL) { | |
411 | fontPtr->tabWidth = 8*fontPtr->widths['0']; | |
412 | } else { | |
413 | fontPtr->tabWidth = 8*fontStructPtr->max_bounds.width; | |
414 | } | |
415 | ||
416 | /* | |
417 | * Make sure the tab width isn't zero (some fonts may not have enough | |
418 | * information to set a reasonable tab width). | |
419 | */ | |
420 | ||
421 | if (fontPtr->tabWidth == 0) { | |
422 | fontPtr->tabWidth = 1; | |
423 | } | |
424 | } | |
425 | \f | |
426 | /* | |
427 | *-------------------------------------------------------------- | |
428 | * | |
429 | * TkMeasureChars -- | |
430 | * | |
431 | * Measure the number of characters from a string that | |
432 | * will fit in a given horizontal span. The measurement | |
433 | * is done under the assumption that TkDisplayChars will | |
434 | * be used to actually display the characters. | |
435 | * | |
436 | * Results: | |
437 | * The return value is the number of characters from source | |
438 | * that fit in the span given by startX and maxX. *nextXPtr | |
439 | * is filled in with the x-coordinate at which the first | |
440 | * character that didn't fit would be drawn, if it were to | |
441 | * be drawn. | |
442 | * | |
443 | * Side effects: | |
444 | * None. | |
445 | * | |
446 | *-------------------------------------------------------------- | |
447 | */ | |
448 | ||
449 | int | |
450 | TkMeasureChars(fontStructPtr, source, maxChars, startX, maxX, flags, nextXPtr) | |
451 | XFontStruct *fontStructPtr; /* Font in which to draw characters. */ | |
452 | char *source; /* Characters to be displayed. Need not | |
453 | * be NULL-terminated. */ | |
454 | int maxChars; /* Maximum # of characters to consider from | |
455 | * source. */ | |
456 | int startX; /* X-position at which first character will | |
457 | * be drawn. */ | |
458 | int maxX; /* Don't consider any character that would | |
459 | * cross this x-position. */ | |
460 | int flags; /* Various flag bits OR-ed together. | |
461 | * TK_WHOLE_WORDS means stop on a word boundary | |
462 | * (just before a space character) if | |
463 | * possible. TK_AT_LEAST_ONE means always | |
464 | * return a value of at least one, even | |
465 | * if the character doesn't fit. | |
466 | * TK_PARTIAL_OK means it's OK to display only | |
467 | * a part of the last character in the line. | |
468 | * TK_NEWLINES_NOT_SPECIAL means that newlines | |
469 | * are treated just like other control chars: | |
470 | * they don't terminate the line,*/ | |
471 | int *nextXPtr; /* Return x-position of terminating | |
472 | * character here. */ | |
473 | { | |
474 | register TkFont *fontPtr; | |
475 | register char *p; /* Current character. */ | |
476 | register int c; | |
477 | char *term; /* Pointer to most recent character that | |
478 | * may legally be a terminating character. */ | |
479 | int termX; /* X-position just after term. */ | |
480 | int curX; /* X-position corresponding to p. */ | |
481 | int newX; /* X-position corresponding to p+1. */ | |
482 | int type; | |
483 | ||
484 | /* | |
485 | * Find the TkFont structure for this font, and make sure its | |
486 | * font metrics exist. | |
487 | */ | |
488 | ||
489 | if (lastFontStructPtr == fontStructPtr) { | |
490 | fontPtr = lastFontPtr; | |
491 | } else { | |
492 | Tcl_HashEntry *fontHashPtr; | |
493 | ||
494 | if (!initialized) { | |
495 | badArg: | |
496 | panic("TkMeasureChars received unknown font argument"); | |
497 | } | |
498 | ||
499 | fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr); | |
500 | if (fontHashPtr == NULL) { | |
501 | goto badArg; | |
502 | } | |
503 | fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr); | |
504 | lastFontStructPtr = fontPtr->fontStructPtr; | |
505 | lastFontPtr = fontPtr; | |
506 | } | |
507 | if (fontPtr->types == NULL) { | |
508 | SetFontMetrics(fontPtr); | |
509 | } | |
510 | ||
511 | /* | |
512 | * Scan the input string one character at a time, until a character | |
513 | * is found that crosses maxX. | |
514 | */ | |
515 | ||
516 | newX = curX = startX; | |
517 | termX = 0; /* Not needed, but eliminates compiler warning. */ | |
518 | term = source; | |
519 | for (p = source, c = *p & 0xff; maxChars > 0; p++, maxChars--) { | |
520 | type = fontPtr->types[c]; | |
521 | if (type == NORMAL) { | |
522 | newX += fontPtr->widths[c]; | |
523 | } else if (type == TAB) { | |
524 | newX += fontPtr->tabWidth; | |
525 | newX -= newX % fontPtr->tabWidth; | |
526 | } else if (type == REPLACE) { | |
527 | replaceType: | |
528 | newX += fontPtr->widths['\\'] + fontPtr->widths['x'] | |
529 | + fontPtr->widths[hexChars[(c >> 4) & 0xf]] | |
530 | + fontPtr->widths[hexChars[c & 0xf]]; | |
531 | } else if (type == NEWLINE) { | |
532 | if (flags & TK_NEWLINES_NOT_SPECIAL) { | |
533 | goto replaceType; | |
534 | } | |
535 | break; | |
536 | } else if (type != SKIP) { | |
537 | panic("Unknown type %d in TkMeasureChars", type); | |
538 | } | |
539 | if (newX > maxX) { | |
540 | break; | |
541 | } | |
542 | c = p[1] & 0xff; | |
543 | if (isspace(c) || (c == 0)) { | |
544 | term = p+1; | |
545 | termX = newX; | |
546 | } | |
547 | curX = newX; | |
548 | } | |
549 | ||
550 | /* | |
551 | * P points to the first character that doesn't fit in the desired | |
552 | * span. Use the flags to figure out what to return. | |
553 | */ | |
554 | ||
555 | if ((flags & TK_PARTIAL_OK) && (curX < maxX)) { | |
556 | curX = newX; | |
557 | p++; | |
558 | } | |
559 | if ((flags & TK_AT_LEAST_ONE) && (term == source) && (maxChars > 0) | |
560 | & !isspace(*term)) { | |
561 | term = p; | |
562 | termX = curX; | |
563 | if (term == source) { | |
564 | term++; | |
565 | termX = newX; | |
566 | } | |
567 | } else if ((maxChars == 0) || !(flags & TK_WHOLE_WORDS)) { | |
568 | term = p; | |
569 | termX = curX; | |
570 | } | |
571 | *nextXPtr = termX; | |
572 | return term-source; | |
573 | } | |
574 | \f | |
575 | /* | |
576 | *-------------------------------------------------------------- | |
577 | * | |
578 | * TkDisplayChars -- | |
579 | * | |
580 | * Draw a string of characters on the screen, converting | |
581 | * tabs to the right number of spaces and control characters | |
582 | * to sequences of the form "\xhh" where hh are two hex | |
583 | * digits. | |
584 | * | |
585 | * Results: | |
586 | * None. | |
587 | * | |
588 | * Side effects: | |
589 | * Information gets drawn on the screen. | |
590 | * | |
591 | *-------------------------------------------------------------- | |
592 | */ | |
593 | ||
594 | void | |
595 | TkDisplayChars(display, drawable, gc, fontStructPtr, string, numChars, | |
596 | x, y, flags) | |
597 | Display *display; /* Display on which to draw. */ | |
598 | Drawable drawable; /* Window or pixmap in which to draw. */ | |
599 | GC gc; /* Graphics context for actually drawing | |
600 | * characters. */ | |
601 | XFontStruct *fontStructPtr; /* Font used in GC; must have been allocated | |
602 | * by Tk_GetFontStruct. Used to compute sizes | |
603 | * of tabs, etc. */ | |
604 | char *string; /* Characters to be displayed. */ | |
605 | int numChars; /* Number of characters to display from | |
606 | * string. */ | |
607 | int x, y; /* Coordinates at which to draw string. */ | |
608 | int flags; /* Flags to control display. Only | |
609 | * TK_NEWLINES_NOT_SPECIAL is supported right | |
610 | * now. See TkMeasureChars for information | |
611 | * about it. */ | |
612 | { | |
613 | register TkFont *fontPtr; | |
614 | register char *p; /* Current character being scanned. */ | |
615 | register int c; | |
616 | int type; | |
617 | char *start; /* First character waiting to be displayed. */ | |
618 | int startX; /* X-coordinate corresponding to start. */ | |
619 | int curX; /* X-coordinate corresponding to p. */ | |
620 | char replace[10]; | |
621 | ||
622 | /* | |
623 | * Find the TkFont structure for this font, and make sure its | |
624 | * font metrics exist. | |
625 | */ | |
626 | ||
627 | if (lastFontStructPtr == fontStructPtr) { | |
628 | fontPtr = lastFontPtr; | |
629 | } else { | |
630 | Tcl_HashEntry *fontHashPtr; | |
631 | ||
632 | if (!initialized) { | |
633 | badArg: | |
634 | panic("TkDisplayChars received unknown font argument"); | |
635 | } | |
636 | ||
637 | fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr); | |
638 | if (fontHashPtr == NULL) { | |
639 | goto badArg; | |
640 | } | |
641 | fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr); | |
642 | lastFontStructPtr = fontPtr->fontStructPtr; | |
643 | lastFontPtr = fontPtr; | |
644 | } | |
645 | if (fontPtr->types == NULL) { | |
646 | SetFontMetrics(fontPtr); | |
647 | } | |
648 | ||
649 | /* | |
650 | * Scan the string one character at a time. Display control | |
651 | * characters immediately, but delay displaying normal characters | |
652 | * in order to pass many characters to the server all together. | |
653 | */ | |
654 | ||
655 | startX = curX = x; | |
656 | start = string; | |
657 | for (p = string; numChars > 0; numChars--, p++) { | |
658 | c = *p & 0xff; | |
659 | type = fontPtr->types[c]; | |
660 | if (type == NORMAL) { | |
661 | curX += fontPtr->widths[c]; | |
662 | continue; | |
663 | } | |
664 | if (p != start) { | |
665 | XDrawString(display, drawable, gc, startX, y, start, p - start); | |
666 | startX = curX; | |
667 | } | |
668 | if (type == TAB) { | |
669 | curX += fontPtr->tabWidth; | |
670 | curX -= curX % fontPtr->tabWidth; | |
671 | } else if (type == REPLACE) { | |
672 | doReplace: | |
673 | replace[0] = '\\'; | |
674 | replace[1] = 'x'; | |
675 | replace[2] = hexChars[(c >> 4) & 0xf]; | |
676 | replace[3] = hexChars[c & 0xf]; | |
677 | XDrawString(display, drawable, gc, startX, y, replace, 4); | |
678 | curX += fontPtr->widths[replace[0]] | |
679 | + fontPtr->widths[replace[1]] | |
680 | + fontPtr->widths[replace[2]] | |
681 | + fontPtr->widths[replace[3]]; | |
682 | } else if (type == NEWLINE) { | |
683 | if (flags & TK_NEWLINES_NOT_SPECIAL) { | |
684 | goto doReplace; | |
685 | } | |
686 | y += fontStructPtr->ascent + fontStructPtr->descent; | |
687 | curX = x; | |
688 | } else if (type != SKIP) { | |
689 | panic("Unknown type %d in TkDisplayChars", type); | |
690 | } | |
691 | startX = curX; | |
692 | start = p+1; | |
693 | } | |
694 | ||
695 | /* | |
696 | * At the very end, there may be one last batch of normal characters | |
697 | * to display. | |
698 | */ | |
699 | ||
700 | if (p != start) { | |
701 | XDrawString(display, drawable, gc, startX, y, start, p - start); | |
702 | } | |
703 | } | |
704 | \f | |
705 | /* | |
706 | *---------------------------------------------------------------------- | |
707 | * | |
708 | * TkUnderlineChars -- | |
709 | * | |
710 | * This procedure draws an underline for a given range of characters | |
711 | * in a given string, using appropriate information for the string's | |
712 | * font. It doesn't draw the characters (which are assumed to have | |
713 | * been displayed previously); it just draws the underline. | |
714 | * | |
715 | * Results: | |
716 | * None. | |
717 | * | |
718 | * Side effects: | |
719 | * Information gets displayed in "drawable". | |
720 | * | |
721 | *---------------------------------------------------------------------- | |
722 | */ | |
723 | ||
724 | void | |
725 | TkUnderlineChars(display, drawable, gc, fontStructPtr, string, x, y, | |
726 | flags, firstChar, lastChar) | |
727 | Display *display; /* Display on which to draw. */ | |
728 | Drawable drawable; /* Window or pixmap in which to draw. */ | |
729 | GC gc; /* Graphics context for actually drawing | |
730 | * underline. */ | |
731 | XFontStruct *fontStructPtr; /* Font used in GC; must have been allocated | |
732 | * by Tk_GetFontStruct. Used to character | |
733 | * dimensions, etc. */ | |
734 | char *string; /* String containing characters to be | |
735 | * underlined. */ | |
736 | int x, y; /* Coordinates at which first character of | |
737 | * string is drawn. */ | |
738 | int flags; /* Flags that were passed to TkDisplayChars. */ | |
739 | int firstChar; /* Index of first character to underline. */ | |
740 | int lastChar; /* Index of last character to underline. */ | |
741 | { | |
742 | int xUnder, yUnder, width, height; | |
743 | unsigned long value; | |
744 | ||
745 | /* | |
746 | * First compute the vertical span of the underline, using font | |
747 | * properties if they exist. | |
748 | */ | |
749 | ||
750 | if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) { | |
751 | yUnder = y + value; | |
752 | } else { | |
753 | yUnder = y + fontStructPtr->max_bounds.descent/2; | |
754 | } | |
755 | if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) { | |
756 | height = value; | |
757 | } else { | |
758 | height = 2; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Now compute the horizontal span of the underline. | |
763 | */ | |
764 | ||
765 | TkMeasureChars(fontStructPtr, string, firstChar, x, (int) 1000000, flags, | |
766 | &xUnder); | |
767 | TkMeasureChars(fontStructPtr, string+firstChar, lastChar+1-firstChar, | |
768 | xUnder, (int) 1000000, flags, &width); | |
769 | width -= xUnder; | |
770 | ||
771 | XFillRectangle(display, drawable, gc, xUnder, yUnder, | |
772 | (unsigned int) width, (unsigned int) height); | |
773 | } |