| 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 | } |