home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkFont.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  22.4 KB  |  785 lines

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