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