home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkFont.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  81.0 KB  |  2,985 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkFont.c --
  3.  *
  4.  *    This file maintains a database of fonts for the Tk toolkit.
  5.  *    It also provides several utility procedures for measuring and
  6.  *    displaying text.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkFont.c 1.72 97/07/22 15:33:45
  15.  */
  16.  
  17. #include "tkInt.h"
  18. #include "tkFont.h"
  19.  
  20. /*
  21.  * The following structure is used to keep track of all the fonts that
  22.  * exist in the current application.  It must be stored in the
  23.  * TkMainInfo for the application.
  24.  */
  25.  
  26. typedef struct TkFontInfo {
  27.     Tcl_HashTable fontCache;    /* Map a string to an existing Tk_Font.
  28.                  * Keys are CachedFontKey structs, values are
  29.                  * TkFont structs. */
  30.     Tcl_HashTable namedTable;    /* Map a name to a set of attributes for a
  31.                  * font, used when constructing a Tk_Font from
  32.                  * a named font description.  Keys are
  33.                  * Tk_Uids, values are NamedFont structs. */
  34.     TkMainInfo *mainPtr;    /* Application that owns this structure. */
  35.     int updatePending;        
  36. } TkFontInfo;
  37.  
  38. /*
  39.  * The following structure is used as a key in the fontCache.
  40.  */
  41.  
  42. typedef struct CachedFontKey {
  43.     Display *display;        /* Display for which font was constructed. */
  44.     Tk_Uid string;        /* String that describes font. */
  45. } CachedFontKey;
  46.  
  47. /*
  48.  * The following data structure is used to keep track of the font attributes
  49.  * for each named font that has been defined.  The named font is only deleted
  50.  * when the last reference to it goes away.
  51.  */
  52.  
  53. typedef struct NamedFont {
  54.     int refCount;        /* Number of users of named font. */
  55.     int deletePending;        /* Non-zero if font should be deleted when
  56.                  * last reference goes away. */
  57.     TkFontAttributes fa;    /* Desired attributes for named font. */
  58. } NamedFont;
  59.     
  60. /*
  61.  * The following two structures are used to keep track of string
  62.  * measurement information when using the text layout facilities.
  63.  *
  64.  * A LayoutChunk represents a contiguous range of text that can be measured
  65.  * and displayed by low-level text calls.  In general, chunks will be
  66.  * delimited by newlines and tabs.  Low-level, platform-specific things
  67.  * like kerning and non-integer character widths may occur between the
  68.  * characters in a single chunk, but not between characters in different
  69.  * chunks.
  70.  *
  71.  * A TextLayout is a collection of LayoutChunks.  It can be displayed with
  72.  * respect to any origin.  It is the implementation of the Tk_TextLayout
  73.  * opaque token.
  74.  */
  75.  
  76. typedef struct LayoutChunk {
  77.     CONST char *start;        /* Pointer to simple string to be displayed.
  78.                  * This is a pointer into the TkTextLayout's
  79.                  * string. */
  80.     int numChars;        /* The number of characters in this chunk. */
  81.     int numDisplayChars;    /* The number of characters to display when
  82.                  * this chunk is displayed.  Can be less than
  83.                  * numChars if extra space characters were
  84.                  * absorbed by the end of the chunk.  This
  85.                  * will be < 0 if this is a chunk that is
  86.                  * holding a tab or newline. */
  87.     int x, y;            /* The origin of the first character in this
  88.                  * chunk with respect to the upper-left hand
  89.                  * corner of the TextLayout. */
  90.     int totalWidth;        /* Width in pixels of this chunk.  Used
  91.                  * when hit testing the invisible spaces at
  92.                  * the end of a chunk. */
  93.     int displayWidth;        /* Width in pixels of the displayable
  94.                  * characters in this chunk.  Can be less than
  95.                  * width if extra space characters were
  96.                  * absorbed by the end of the chunk. */
  97. } LayoutChunk;
  98.  
  99. typedef struct TextLayout {
  100.     Tk_Font tkfont;        /* The font used when laying out the text. */
  101.     CONST char *string;        /* The string that was layed out. */
  102.     int width;            /* The maximum width of all lines in the
  103.                  * text layout. */
  104.     int numChunks;        /* Number of chunks actually used in
  105.                  * following array. */
  106.     LayoutChunk chunks[1];    /* Array of chunks.  The actual size will
  107.                  * be maxChunks.  THIS FIELD MUST BE THE LAST
  108.                  * IN THE STRUCTURE. */
  109. } TextLayout;
  110.  
  111. /*
  112.  * The following structures are used as two-way maps between the values for
  113.  * the fields in the TkFontAttributes structure and the strings used in
  114.  * Tcl, when parsing both option-value format and style-list format font
  115.  * name strings.
  116.  */
  117.  
  118. static TkStateMap weightMap[] = {
  119.     {TK_FW_NORMAL,    "normal"},
  120.     {TK_FW_BOLD,    "bold"},
  121.     {TK_FW_UNKNOWN,    NULL}
  122. };
  123.  
  124. static TkStateMap slantMap[] = {
  125.     {TK_FS_ROMAN,    "roman"},
  126.     {TK_FS_ITALIC,    "italic"},
  127.     {TK_FS_UNKNOWN,    NULL}
  128. };
  129.  
  130. static TkStateMap underlineMap[] = {
  131.     {1,            "underline"},
  132.     {0,            NULL}
  133. };
  134.  
  135. static TkStateMap overstrikeMap[] = {
  136.     {1,            "overstrike"},
  137.     {0,            NULL}
  138. };
  139.  
  140. /*
  141.  * The following structures are used when parsing XLFD's into a set of
  142.  * TkFontAttributes.
  143.  */
  144.  
  145. static TkStateMap xlfdWeightMap[] = {
  146.     {TK_FW_NORMAL,    "normal"},
  147.     {TK_FW_NORMAL,    "medium"},
  148.     {TK_FW_NORMAL,    "book"},
  149.     {TK_FW_NORMAL,    "light"},
  150.     {TK_FW_BOLD,    "bold"},
  151.     {TK_FW_BOLD,    "demi"},
  152.     {TK_FW_BOLD,    "demibold"},
  153.     {TK_FW_NORMAL,    NULL}        /* Assume anything else is "normal". */
  154. }; 
  155.  
  156. static TkStateMap xlfdSlantMap[] = {
  157.     {TK_FS_ROMAN,    "r"},
  158.     {TK_FS_ITALIC,    "i"},
  159.     {TK_FS_OBLIQUE,    "o"},
  160.     {TK_FS_ROMAN,    NULL}        /* Assume anything else is "roman". */
  161. };
  162.  
  163. static TkStateMap xlfdSetwidthMap[] = {
  164.     {TK_SW_NORMAL,    "normal"},
  165.     {TK_SW_CONDENSE,    "narrow"},
  166.     {TK_SW_CONDENSE,    "semicondensed"},
  167.     {TK_SW_CONDENSE,    "condensed"},
  168.     {TK_SW_UNKNOWN,    NULL}
  169. };
  170.  
  171. static TkStateMap xlfdCharsetMap[] = {
  172.     {TK_CS_NORMAL,    "iso8859"},
  173.     {TK_CS_SYMBOL,    "adobe"},
  174.     {TK_CS_SYMBOL,    "sun"},
  175.     {TK_CS_OTHER,    NULL}
  176. };
  177.     
  178. /*
  179.  * The following structure and defines specify the valid builtin options 
  180.  * when configuring a set of font attributes.
  181.  */
  182.  
  183. static char *fontOpt[] = {
  184.     "-family",
  185.     "-size",
  186.     "-weight",
  187.     "-slant",
  188.     "-underline",
  189.     "-overstrike",
  190.     NULL
  191. };
  192.  
  193. #define FONT_FAMILY    0
  194. #define FONT_SIZE    1
  195. #define FONT_WEIGHT    2
  196. #define FONT_SLANT    3
  197. #define FONT_UNDERLINE    4
  198. #define FONT_OVERSTRIKE    5
  199. #define FONT_NUMFIELDS    6        /* Length of fontOpt array. */
  200.  
  201. #define GetFontAttributes(tkfont) \
  202.         ((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa)
  203.  
  204. #define GetFontMetrics(tkfont)    \
  205.         ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm)
  206.  
  207.  
  208. static int        ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp,
  209.                 Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[],
  210.                 TkFontAttributes *faPtr));
  211. static int        FieldSpecified _ANSI_ARGS_((CONST char *field));
  212. static void        GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp,
  213.                 CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr));
  214. static LayoutChunk *    NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr,
  215.                 int *maxPtr, CONST char *start, int numChars,
  216.                 int curX, int newX, int y));
  217. static int        ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp,
  218.                 Tk_Window tkwin, Tcl_Obj *objPtr,
  219.                 TkFontAttributes *faPtr));
  220. static void        RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr));
  221. static void        TheWorldHasChanged _ANSI_ARGS_((
  222.                 ClientData clientData));
  223. static void        UpdateDependantFonts _ANSI_ARGS_((TkFontInfo *fiPtr,
  224.                 Tk_Window tkwin, Tcl_HashEntry *namedHashPtr));
  225.  
  226.             
  227.  
  228.  
  229. /*
  230.  *---------------------------------------------------------------------------
  231.  *
  232.  * TkFontPkgInit --
  233.  *
  234.  *    This procedure is called when an application is created.  It
  235.  *    initializes all the structures that are used by the font
  236.  *    package on a per application basis.
  237.  *
  238.  * Results:
  239.  *    Returns a token that must be stored in the TkMainInfo for this
  240.  *    application.
  241.  *
  242.  * Side effects:
  243.  *    Memory allocated.
  244.  *
  245.  *---------------------------------------------------------------------------
  246.  */
  247. void
  248. TkFontPkgInit(mainPtr)
  249.     TkMainInfo *mainPtr;    /* The application being created. */
  250. {
  251.     TkFontInfo *fiPtr;
  252.  
  253.     fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo));
  254.     Tcl_InitHashTable(&fiPtr->fontCache, sizeof(CachedFontKey) / sizeof(int));
  255.     Tcl_InitHashTable(&fiPtr->namedTable, TCL_ONE_WORD_KEYS);
  256.     fiPtr->mainPtr = mainPtr;
  257.     fiPtr->updatePending = 0;
  258.     mainPtr->fontInfoPtr = fiPtr;
  259. }
  260.  
  261. /*
  262.  *---------------------------------------------------------------------------
  263.  *
  264.  * TkFontPkgFree --
  265.  *
  266.  *    This procedure is called when an application is deleted.  It
  267.  *    deletes all the structures that were used by the font package
  268.  *    for this application.
  269.  *
  270.  * Results:
  271.  *    None.
  272.  *
  273.  * Side effects:
  274.  *    Memory freed.
  275.  *
  276.  *---------------------------------------------------------------------------
  277.  */
  278.  
  279. void
  280. TkFontPkgFree(mainPtr)
  281.     TkMainInfo *mainPtr;    /* The application being deleted. */
  282. {
  283.     TkFontInfo *fiPtr;
  284.     Tcl_HashEntry *hPtr;
  285.     Tcl_HashSearch search;
  286.  
  287.     fiPtr = mainPtr->fontInfoPtr;
  288.  
  289.     if (fiPtr->fontCache.numEntries != 0) {
  290.     panic("TkFontPkgFree: all fonts should have been freed already");
  291.     }
  292.     Tcl_DeleteHashTable(&fiPtr->fontCache);
  293.  
  294.     hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
  295.     while (hPtr != NULL) {
  296.     ckfree((char *) Tcl_GetHashValue(hPtr));
  297.     hPtr = Tcl_NextHashEntry(&search);
  298.     }
  299.     Tcl_DeleteHashTable(&fiPtr->namedTable);
  300.     if (fiPtr->updatePending != 0) {
  301.     Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr);
  302.     }
  303.     ckfree((char *) fiPtr);
  304. }
  305.  
  306. /*
  307.  *---------------------------------------------------------------------------
  308.  *
  309.  * Tk_FontObjCmd -- 
  310.  *
  311.  *    This procedure is implemented to process the "font" Tcl command.
  312.  *    See the user documentation for details on what it does.
  313.  *
  314.  * Results:
  315.  *    A standard Tcl result.
  316.  *
  317.  * Side effects:
  318.  *    See the user documentation.
  319.  *
  320.  *----------------------------------------------------------------------
  321.  */
  322.  
  323. int
  324. Tk_FontObjCmd(clientData, interp, objc, objv)
  325.     ClientData clientData;    /* Main window associated with interpreter. */
  326.     Tcl_Interp *interp;        /* Current interpreter. */
  327.     int objc;            /* Number of arguments. */
  328.     Tcl_Obj *CONST objv[];    /* Argument objects. */
  329. {
  330.     int index;
  331.     Tk_Window tkwin;
  332.     TkFontInfo *fiPtr;
  333.     static char *optionStrings[] = {
  334.     "actual",    "configure",    "create",    "delete",
  335.     "families",    "measure",    "metrics",    "names",
  336.     NULL
  337.     };
  338.     enum options {
  339.     FONT_ACTUAL,    FONT_CONFIGURE,    FONT_CREATE,    FONT_DELETE,
  340.     FONT_FAMILIES,    FONT_MEASURE,    FONT_METRICS,    FONT_NAMES
  341.     };
  342.  
  343.     tkwin = (Tk_Window) clientData;
  344.     fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
  345.  
  346.     if (objc < 2) {
  347.     Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
  348.     return TCL_ERROR;
  349.     }
  350.     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
  351.         &index) != TCL_OK) {
  352.     return TCL_ERROR;
  353.     }
  354.  
  355.     switch ((enum options) index) {
  356.     case FONT_ACTUAL: {
  357.         int skip;
  358.         Tk_Font tkfont;
  359.         Tcl_Obj *objPtr;
  360.         CONST TkFontAttributes *faPtr;
  361.  
  362.         skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
  363.         if (skip < 0) {
  364.         return TCL_ERROR;
  365.         }
  366.         if ((objc < 3) || (objc - skip > 4)) {
  367.         Tcl_WrongNumArgs(interp, 2, objv,
  368.             "font ?-displayof window? ?option?");
  369.         return TCL_ERROR;
  370.         }
  371.         tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
  372.         if (tkfont == NULL) {
  373.         return TCL_ERROR;
  374.         }
  375.         faPtr = GetFontAttributes(tkfont);
  376.         objPtr = NULL;
  377.         if (objc > 3) {
  378.         objPtr = objv[3 + skip];
  379.         }
  380.         GetAttributeInfoObj(interp, faPtr, objPtr);
  381.         Tk_FreeFont(tkfont);
  382.         break;
  383.     }
  384.     case FONT_CONFIGURE: {
  385.         int result;
  386.         char *string;
  387.         Tcl_Obj *objPtr;
  388.         NamedFont *nfPtr;
  389.         Tcl_HashEntry *namedHashPtr;
  390.  
  391.         if (objc < 3) {
  392.         Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?");
  393.         return TCL_ERROR;
  394.         }
  395.         string = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL));
  396.         namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
  397.         nfPtr = NULL;        /* lint. */
  398.         if (namedHashPtr != NULL) {
  399.         nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  400.         }
  401.         if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) {
  402.         Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
  403.             "\" doesn't exist", NULL);
  404.         return TCL_ERROR;
  405.         }
  406.         if (objc == 3) {
  407.         objPtr = NULL;
  408.         } else if (objc == 4) {
  409.         objPtr = objv[3];
  410.         } else {
  411.         result = ConfigAttributesObj(interp, tkwin, objc - 3,
  412.             objv + 3, &nfPtr->fa);
  413.         UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
  414.         return result;
  415.         }
  416.         GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
  417.         break;
  418.     }
  419.     case FONT_CREATE: {
  420.         int skip, i;
  421.         char *name;
  422.         char buf[32];
  423.         TkFontAttributes fa;
  424.         Tcl_HashEntry *namedHashPtr;
  425.  
  426.         skip = 3;
  427.         if (objc < 3) {
  428.         name = NULL;
  429.         } else {
  430.         name = Tcl_GetStringFromObj(objv[2], NULL);
  431.         if (name[0] == '-') {
  432.             name = NULL;
  433.         }
  434.         }
  435.         if (name == NULL) {
  436.         /*
  437.          * No font name specified.  Generate one of the form "fontX".
  438.          */
  439.  
  440.         for (i = 1; ; i++) {
  441.             sprintf(buf, "font%d", i);
  442.             namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
  443.                 Tk_GetUid(buf));
  444.             if (namedHashPtr == NULL) {
  445.             break;
  446.             }
  447.         }
  448.         name = buf;
  449.         skip = 2;
  450.         }
  451.         TkInitFontAttributes(&fa);
  452.         if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip,
  453.             &fa) != TCL_OK) {
  454.         return TCL_ERROR;
  455.         }
  456.         if (TkCreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) {
  457.         return TCL_ERROR;
  458.         }
  459.         Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
  460.         break;
  461.     }
  462.     case FONT_DELETE: {
  463.         int i;
  464.         char *string;
  465.         NamedFont *nfPtr;
  466.         Tcl_HashEntry *namedHashPtr;
  467.  
  468.         /*
  469.          * Delete the named font.  If there are still widgets using this
  470.          * font, then it isn't deleted right away.
  471.          */
  472.  
  473.         if (objc < 3) {
  474.         Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?");
  475.         return TCL_ERROR;
  476.         }
  477.         for (i = 2; i < objc; i++) {
  478.         string = Tk_GetUid(Tcl_GetStringFromObj(objv[i], NULL));
  479.         namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
  480.         if (namedHashPtr == NULL) {
  481.             Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
  482.                 "\" doesn't exist", (char *) NULL);
  483.             return TCL_ERROR;
  484.         }
  485.         nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  486.         if (nfPtr->refCount != 0) {
  487.             nfPtr->deletePending = 1;
  488.         } else {
  489.             Tcl_DeleteHashEntry(namedHashPtr);
  490.             ckfree((char *) nfPtr);
  491.         }
  492.         }
  493.         break;
  494.     }
  495.     case FONT_FAMILIES: {
  496.         int skip;
  497.  
  498.         skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
  499.         if (skip < 0) {
  500.         return TCL_ERROR;
  501.         }
  502.         if (objc - skip != 2) {
  503.         Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?");
  504.         return TCL_ERROR;
  505.         }
  506.         TkpGetFontFamilies(interp, tkwin);
  507.         break;
  508.     }
  509.     case FONT_MEASURE: {
  510.         char *string;
  511.         Tk_Font tkfont;
  512.         int length, skip;
  513.         
  514.         skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
  515.         if (skip < 0) {
  516.         return TCL_ERROR;
  517.         }
  518.         if (objc - skip != 4) {
  519.         Tcl_WrongNumArgs(interp, 2, objv,
  520.             "font ?-displayof window? text");
  521.         return TCL_ERROR;
  522.         }
  523.         tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
  524.         if (tkfont == NULL) {
  525.         return TCL_ERROR;
  526.         }
  527.         string = Tcl_GetStringFromObj(objv[3 + skip], &length);
  528.         Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_TextWidth(tkfont, string, length));
  529.         Tk_FreeFont(tkfont);
  530.         break;
  531.     }
  532.     case FONT_METRICS: {
  533.         char buf[64];
  534.         Tk_Font tkfont;
  535.         int skip, index, i;
  536.         CONST TkFontMetrics *fmPtr;
  537.         static char *switches[] = {
  538.         "-ascent", "-descent", "-linespace", "-fixed", NULL
  539.         };
  540.  
  541.         skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
  542.         if (skip < 0) {
  543.         return TCL_ERROR;
  544.         }
  545.         if ((objc < 3) || ((objc - skip) > 4)) {
  546.         Tcl_WrongNumArgs(interp, 2, objv,
  547.             "font ?-displayof window? ?option?");
  548.         return TCL_ERROR;
  549.         }
  550.         tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
  551.         if (tkfont == NULL) {
  552.         return TCL_ERROR;
  553.         }
  554.         fmPtr = GetFontMetrics(tkfont);
  555.         if (objc == 3) {
  556.         sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d",
  557.             fmPtr->ascent, fmPtr->descent,
  558.             fmPtr->ascent + fmPtr->descent,
  559.             fmPtr->fixed);
  560.         Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1);
  561.         } else {
  562.         if (Tcl_GetIndexFromObj(interp, objv[3 + skip], switches,
  563.             "metric", 0, &index) != TCL_OK) {
  564.             Tk_FreeFont(tkfont);
  565.             return TCL_ERROR;
  566.         }
  567.         i = 0;            /* Needed only to prevent compiler
  568.                      * warning. */
  569.         switch (index) {
  570.             case 0: i = fmPtr->ascent;            break;
  571.             case 1: i = fmPtr->descent;            break;
  572.             case 2: i = fmPtr->ascent + fmPtr->descent;    break;
  573.             case 3: i = fmPtr->fixed;            break;
  574.         }
  575.         Tcl_SetIntObj(Tcl_GetObjResult(interp), i);
  576.         }
  577.         Tk_FreeFont(tkfont);
  578.         break;
  579.     }
  580.     case FONT_NAMES: {
  581.         char *string;
  582.         Tcl_Obj *strPtr;
  583.         NamedFont *nfPtr;
  584.         Tcl_HashSearch search;
  585.         Tcl_HashEntry *namedHashPtr;
  586.         
  587.         if (objc != 2) {
  588.         Tcl_WrongNumArgs(interp, 1, objv, "names");
  589.         return TCL_ERROR;
  590.         }
  591.         namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
  592.         while (namedHashPtr != NULL) {
  593.         nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  594.         if (nfPtr->deletePending == 0) {
  595.             string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr);
  596.             strPtr = Tcl_NewStringObj(string, -1);
  597.             Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), strPtr);
  598.         }
  599.         namedHashPtr = Tcl_NextHashEntry(&search);
  600.         }
  601.         break;
  602.     }
  603.     }
  604.     return TCL_OK;
  605. }
  606.  
  607. /*
  608.  *---------------------------------------------------------------------------
  609.  *
  610.  * UpdateDependantFonts, TheWorldHasChanged, RecomputeWidgets --
  611.  *
  612.  *    Called when the attributes of a named font changes.  Updates all
  613.  *    the instantiated fonts that depend on that named font and then
  614.  *    uses the brute force approach and prepares every widget to
  615.  *    recompute its geometry.
  616.  *
  617.  * Results:
  618.  *    None.
  619.  *
  620.  * Side effects:
  621.  *    Things get queued for redisplay.
  622.  *
  623.  *---------------------------------------------------------------------------
  624.  */
  625.  
  626. static void
  627. UpdateDependantFonts(fiPtr, tkwin, namedHashPtr)
  628.     TkFontInfo *fiPtr;        /* Info about application's fonts. */
  629.     Tk_Window tkwin;        /* A window in the application. */
  630.     Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */
  631. {
  632.     Tcl_HashEntry *cacheHashPtr;
  633.     Tcl_HashSearch search;
  634.     TkFont *fontPtr;
  635.     NamedFont *nfPtr;
  636.  
  637.     nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  638.     if (nfPtr->refCount == 0) {
  639.     /*
  640.      * Well nobody's using this named font, so don't have to tell
  641.      * any widgets to recompute themselves.
  642.      */
  643.  
  644.     return;
  645.     }
  646.  
  647.  
  648.     cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
  649.     while (cacheHashPtr != NULL) {
  650.     fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
  651.     if (fontPtr->namedHashPtr == namedHashPtr) {
  652.         TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
  653.         if (fiPtr->updatePending == 0) {
  654.         fiPtr->updatePending = 1;
  655.         Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr);
  656.         }
  657.     }
  658.     cacheHashPtr = Tcl_NextHashEntry(&search);
  659.     }
  660. }
  661.  
  662. static void
  663. TheWorldHasChanged(clientData)
  664.     ClientData clientData;    /* Info about application's fonts. */
  665. {
  666.     TkFontInfo *fiPtr;
  667.  
  668.     fiPtr = (TkFontInfo *) clientData;
  669.     fiPtr->updatePending = 0;
  670.  
  671.     RecomputeWidgets(fiPtr->mainPtr->winPtr);
  672. }
  673.  
  674. static void
  675. RecomputeWidgets(winPtr)
  676.     TkWindow *winPtr;        /* Window to which command is sent. */
  677. {
  678.     if ((winPtr->classProcsPtr != NULL)
  679.         && (winPtr->classProcsPtr->geometryProc != NULL)) {
  680.     (*winPtr->classProcsPtr->geometryProc)(winPtr->instanceData);
  681.     }
  682.     for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
  683.     RecomputeWidgets(winPtr);
  684.     }
  685. }
  686.  
  687. /*
  688.  *---------------------------------------------------------------------------
  689.  *
  690.  * TkCreateNamedFont --
  691.  *
  692.  *    Create the specified named font with the given attributes in the
  693.  *    named font table associated with the interp.  
  694.  *
  695.  * Results:
  696.  *    Returns TCL_OK if the font was successfully created, or TCL_ERROR
  697.  *    if the named font already existed.  If TCL_ERROR is returned, an
  698.  *    error message is left in interp->result.
  699.  *
  700.  * Side effects:
  701.  *    Assume there used to exist a named font by the specified name, and
  702.  *    that the named font had been deleted, but there were still some
  703.  *    widgets using the named font at the time it was deleted.  If a
  704.  *    new named font is created with the same name, all those widgets
  705.  *    that were using the old named font will be redisplayed using
  706.  *    the new named font's attributes.
  707.  *
  708.  *---------------------------------------------------------------------------
  709.  */
  710.  
  711. int
  712. TkCreateNamedFont(interp, tkwin, name, faPtr)
  713.     Tcl_Interp *interp;        /* Interp for error return. */
  714.     Tk_Window tkwin;        /* A window associated with interp. */
  715.     CONST char *name;        /* Name for the new named font. */
  716.     TkFontAttributes *faPtr;    /* Attributes for the new named font. */
  717. {
  718.     TkFontInfo *fiPtr;
  719.     Tcl_HashEntry *namedHashPtr;
  720.     int new;
  721.     NamedFont *nfPtr;    
  722.  
  723.     fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
  724.  
  725.     name = Tk_GetUid(name);
  726.     namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new);
  727.             
  728.     if (new == 0) {
  729.     nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  730.     if (nfPtr->deletePending == 0) {
  731.         interp->result[0] = '\0';
  732.         Tcl_AppendResult(interp, "font \"", name,
  733.             "\" already exists", (char *) NULL);
  734.         return TCL_ERROR;
  735.     }
  736.  
  737.     /*
  738.      * Recreating a named font with the same name as a previous
  739.      * named font.  Some widgets were still using that named
  740.      * font, so they need to get redisplayed.
  741.      */
  742.  
  743.     nfPtr->fa = *faPtr;
  744.     nfPtr->deletePending = 0;
  745.     UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
  746.     return TCL_OK;
  747.     }
  748.  
  749.     nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont));
  750.     nfPtr->deletePending = 0;
  751.     Tcl_SetHashValue(namedHashPtr, nfPtr);
  752.     nfPtr->fa = *faPtr;
  753.     nfPtr->refCount = 0;    
  754.     nfPtr->deletePending = 0;
  755.     return TCL_OK;
  756. }
  757.  
  758. /*
  759.  *---------------------------------------------------------------------------
  760.  *
  761.  * Tk_GetFont -- 
  762.  *
  763.  *    Given a string description of a font, map the description to a
  764.  *    corresponding Tk_Font that represents the font.
  765.  *
  766.  * Results:
  767.  *    The return value is token for the font, or NULL if an error
  768.  *    prevented the font from being created.  If NULL is returned, an
  769.  *    error message will be left in interp->result.
  770.  *
  771.  * Side effects:
  772.  *    Calls Tk_GetFontFromObj(), which modifies interp's result object,
  773.  *    then copies the string from the result object into interp->result.
  774.  *    This procedure will go away when Tk_ConfigureWidget() is
  775.  *    made into an object command.
  776.  *
  777.  *---------------------------------------------------------------------------
  778.  */
  779.  
  780. Tk_Font
  781. Tk_GetFont(interp, tkwin, string)
  782.     Tcl_Interp *interp;        /* Interp for database and error return. */
  783.     Tk_Window tkwin;        /* For display on which font will be used. */
  784.     CONST char *string;        /* String describing font, as: named font,
  785.                  * native format, or parseable string. */
  786. {
  787.     Tcl_Obj *strPtr;
  788.     Tk_Font tkfont;
  789.     
  790.     strPtr = Tcl_NewStringObj((char *) string, -1);
  791.     
  792.     tkfont = Tk_GetFontFromObj(interp, tkwin, strPtr);
  793.     if (tkfont == NULL) {
  794.     Tcl_SetResult(interp,
  795.             Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL),
  796.         TCL_VOLATILE);
  797.     }
  798.  
  799.     Tcl_DecrRefCount(strPtr);    /* done with object */
  800.     return tkfont;
  801. }
  802.  
  803. /*
  804.  *---------------------------------------------------------------------------
  805.  *
  806.  * Tk_GetFontFromObj -- 
  807.  *
  808.  *    Given a string description of a font, map the description to a
  809.  *    corresponding Tk_Font that represents the font.
  810.  *
  811.  * Results:
  812.  *    The return value is token for the font, or NULL if an error
  813.  *    prevented the font from being created.  If NULL is returned, an
  814.  *    error message will be left in interp's result object.
  815.  *
  816.  * Side effects:
  817.  *     The font is added to an internal database with a reference
  818.  *    count.  For each call to this procedure, there should eventually
  819.  *    be a call to Tk_FreeFont() so that the database is cleaned up when
  820.  *    fonts aren't in use anymore.
  821.  *
  822.  *---------------------------------------------------------------------------
  823.  */
  824.  
  825. Tk_Font
  826. Tk_GetFontFromObj(interp, tkwin, objPtr)
  827.     Tcl_Interp *interp;        /* Interp for database and error return. */
  828.     Tk_Window tkwin;        /* For display on which font will be used. */
  829.     Tcl_Obj *objPtr;        /* Object describing font, as: named font,
  830.                  * native format, or parseable string. */
  831. {
  832.     TkFontInfo *fiPtr;
  833.     CachedFontKey key;
  834.     Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
  835.     TkFont *fontPtr;
  836.     int new, descent;
  837.     NamedFont *nfPtr;
  838.     char *string;
  839.     
  840.     fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
  841.     string = Tcl_GetStringFromObj(objPtr, NULL);
  842.  
  843.     key.display = Tk_Display(tkwin);
  844.     key.string = Tk_GetUid(string);
  845.     cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache, (char *) &key, &new);
  846.  
  847.     if (new == 0) {
  848.     /*
  849.      * We have already constructed a font with this description for
  850.      * this display.  Bump the reference count of the cached font.
  851.      */
  852.  
  853.     fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
  854.     fontPtr->refCount++;
  855.     return (Tk_Font) fontPtr;
  856.     }
  857.  
  858.     namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, key.string);
  859.     if (namedHashPtr != NULL) {
  860.     /*
  861.      * Construct a font based on a named font.
  862.      */
  863.  
  864.     nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
  865.     nfPtr->refCount++;
  866.  
  867.     fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa);
  868.     } else {
  869.     /*
  870.      * Native font?
  871.      */
  872.  
  873.     fontPtr = TkpGetNativeFont(tkwin, string);
  874.     if (fontPtr == NULL) {
  875.         TkFontAttributes fa;
  876.  
  877.         TkInitFontAttributes(&fa);
  878.         if (ParseFontNameObj(interp, tkwin, objPtr, &fa) != TCL_OK) {
  879.         Tcl_DeleteHashEntry(cacheHashPtr);
  880.         return NULL;
  881.         }
  882.  
  883.         /*
  884.          * String contained the attributes inline.
  885.          */
  886.  
  887.         fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
  888.     }
  889.     }
  890.     Tcl_SetHashValue(cacheHashPtr, fontPtr);
  891.  
  892.     fontPtr->refCount        = 1;
  893.     fontPtr->cacheHashPtr   = cacheHashPtr;
  894.     fontPtr->namedHashPtr   = namedHashPtr;
  895.  
  896.     Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, 0, 0, &fontPtr->tabWidth);
  897.     if (fontPtr->tabWidth == 0) {
  898.     fontPtr->tabWidth = fontPtr->fm.maxWidth;
  899.     }
  900.     fontPtr->tabWidth *= 8;
  901.  
  902.     /*
  903.      * Make sure the tab width isn't zero (some fonts may not have enough
  904.      * information to set a reasonable tab width).
  905.      */
  906.  
  907.     if (fontPtr->tabWidth == 0) {
  908.     fontPtr->tabWidth = 1;
  909.     }
  910.  
  911.     /*
  912.      * Get information used for drawing underlines in generic code on a
  913.      * non-underlined font.
  914.      */
  915.     
  916.     descent = fontPtr->fm.descent;
  917.     fontPtr->underlinePos = descent / 2;
  918.     fontPtr->underlineHeight = fontPtr->fa.pointsize / 10;
  919.     if (fontPtr->underlineHeight == 0) {
  920.     fontPtr->underlineHeight = 1;
  921.     }
  922.     if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) {
  923.     /*
  924.      * If this set of values would cause the bottom of the underline
  925.      * bar to stick below the descent of the font, jack the underline
  926.      * up a bit higher.
  927.      */
  928.  
  929.     fontPtr->underlineHeight = descent - fontPtr->underlinePos;
  930.     if (fontPtr->underlineHeight == 0) {
  931.         fontPtr->underlinePos--;
  932.         fontPtr->underlineHeight = 1;
  933.     }
  934.     }
  935.     
  936.     return (Tk_Font) fontPtr;
  937. }
  938.  
  939. /*
  940.  *---------------------------------------------------------------------------
  941.  *
  942.  * Tk_NameOfFont --
  943.  *
  944.  *    Given a font, return a textual string identifying it.
  945.  *
  946.  * Results:
  947.  *    The return value is the description that was passed to
  948.  *    Tk_GetFont() to create the font.  The storage for the returned
  949.  *    string is only guaranteed to persist until the font is deleted.
  950.  *    The caller should not modify this string.
  951.  *
  952.  * Side effects:
  953.  *    None.
  954.  *
  955.  *---------------------------------------------------------------------------
  956.  */
  957.  
  958. char *
  959. Tk_NameOfFont(tkfont)
  960.     Tk_Font tkfont;        /* Font whose name is desired. */
  961. {
  962.     TkFont *fontPtr;
  963.     Tcl_HashEntry *hPtr;
  964.     CachedFontKey *keyPtr;
  965.  
  966.     fontPtr = (TkFont *) tkfont;
  967.     hPtr = fontPtr->cacheHashPtr;
  968.  
  969.     keyPtr = (CachedFontKey *) Tcl_GetHashKey(hPtr->tablePtr, hPtr);
  970.     return (char *) keyPtr->string;    
  971. }
  972.  
  973. /*
  974.  *---------------------------------------------------------------------------
  975.  *
  976.  * Tk_FreeFont -- 
  977.  *
  978.  *    Called to release a font allocated by Tk_GetFont().
  979.  *
  980.  * Results:
  981.  *    None.
  982.  *
  983.  * Side effects:
  984.  *    The reference count associated with font is decremented, and
  985.  *    only deallocated when no one is using it.
  986.  *
  987.  *---------------------------------------------------------------------------
  988.  */
  989.  
  990. void
  991. Tk_FreeFont(tkfont)
  992.     Tk_Font tkfont;        /* Font to be released. */
  993. {
  994.     TkFont *fontPtr;
  995.     NamedFont *nfPtr;
  996.  
  997.     if (tkfont == NULL) {
  998.     return;
  999.     }
  1000.     fontPtr = (TkFont *) tkfont;
  1001.     fontPtr->refCount--;
  1002.     if (fontPtr->refCount == 0) {
  1003.     if (fontPtr->namedHashPtr != NULL) {
  1004.         /*
  1005.          * The font is being deleted.  Determine if the associated named
  1006.          * font definition should and/or can be deleted too.
  1007.          */
  1008.  
  1009.         nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr);
  1010.         nfPtr->refCount--;
  1011.         if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) {
  1012.         Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
  1013.         ckfree((char *) nfPtr);
  1014.         }
  1015.     }
  1016.     Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
  1017.     TkpDeleteFont(fontPtr);
  1018.     }
  1019. }
  1020.  
  1021. /*
  1022.  *---------------------------------------------------------------------------
  1023.  *
  1024.  * Tk_FontId --
  1025.  *
  1026.  *    Given a font, return an opaque handle that should be selected
  1027.  *    into the XGCValues structure in order to get the constructed
  1028.  *    gc to use this font.  This procedure would go away if the
  1029.  *    XGCValues structure were replaced with a TkGCValues structure.
  1030.  *
  1031.  * Results:
  1032.  *    As above.
  1033.  *
  1034.  * Side effects:
  1035.  *    None.
  1036.  *
  1037.  *---------------------------------------------------------------------------
  1038.  */
  1039.  
  1040. Font
  1041. Tk_FontId(tkfont)
  1042.     Tk_Font tkfont;    /* Font that is going to be selected into GC. */
  1043. {
  1044.     TkFont *fontPtr;
  1045.  
  1046.     fontPtr = (TkFont *) tkfont;
  1047.     return fontPtr->fid;
  1048. }
  1049.  
  1050. /*
  1051.  *---------------------------------------------------------------------------
  1052.  *
  1053.  * Tk_GetFontMetrics --
  1054.  *
  1055.  *    Returns overall ascent and descent metrics for the given font.
  1056.  *    These values can be used to space multiple lines of text and
  1057.  *    to align the baselines of text in different fonts.
  1058.  *
  1059.  * Results:
  1060.  *    If *heightPtr is non-NULL, it is filled with the overall height
  1061.  *    of the font, which is the sum of the ascent and descent.
  1062.  *    If *ascentPtr or *descentPtr is non-NULL, they are filled with
  1063.  *    the ascent and/or descent information for the font.
  1064.  *
  1065.  * Side effects:
  1066.  *    None.
  1067.  *
  1068.  *---------------------------------------------------------------------------
  1069.  */
  1070. void
  1071. Tk_GetFontMetrics(tkfont, fmPtr)
  1072.     Tk_Font tkfont;        /* Font in which metrics are calculated. */
  1073.     Tk_FontMetrics *fmPtr;    /* Pointer to structure in which font
  1074.                  * metrics for tkfont will be stored. */
  1075. {
  1076.     TkFont *fontPtr;
  1077.  
  1078.     fontPtr = (TkFont *) tkfont;
  1079.     fmPtr->ascent = fontPtr->fm.ascent;
  1080.     fmPtr->descent = fontPtr->fm.descent;
  1081.     fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent;
  1082. }
  1083.  
  1084. /*
  1085.  *---------------------------------------------------------------------------
  1086.  *
  1087.  * Tk_PostscriptFontName --
  1088.  *
  1089.  *    Given a Tk_Font, return the name of the corresponding Postscript
  1090.  *    font.
  1091.  *
  1092.  * Results:
  1093.  *    The return value is the pointsize of the given Tk_Font.
  1094.  *    The name of the Postscript font is appended to dsPtr.
  1095.  *
  1096.  * Side effects:
  1097.  *    If the font does not exist on the printer, the print job will
  1098.  *    fail at print time.  Given a "reasonable" Postscript printer,
  1099.  *    the following Tk_Font font families should print correctly:
  1100.  *
  1101.  *        Avant Garde, Arial, Bookman, Courier, Courier New, Geneva,
  1102.  *        Helvetica, Monaco, New Century Schoolbook, New York,
  1103.  *        Palatino, Symbol, Times, Times New Roman, Zapf Chancery,
  1104.  *        and Zapf Dingbats.
  1105.  *
  1106.  *    Any other Tk_Font font families may not print correctly
  1107.  *    because the computed Postscript font name may be incorrect.
  1108.  *
  1109.  *---------------------------------------------------------------------------
  1110.  */
  1111.  
  1112.  
  1113. int
  1114. Tk_PostscriptFontName(tkfont, dsPtr)
  1115.     Tk_Font tkfont;        /* Font in which text will be printed. */
  1116.     Tcl_DString *dsPtr;        /* Pointer to an initialized Tcl_DString to
  1117.                  * which the name of the Postscript font that
  1118.                  * corresponds to tkfont will be appended. */
  1119. {
  1120.     TkFont *fontPtr;
  1121.     char *family, *weightString, *slantString;
  1122.     char *src, *dest;
  1123.     int upper, len;
  1124.  
  1125.     len = Tcl_DStringLength(dsPtr);
  1126.     fontPtr = (TkFont *) tkfont;
  1127.  
  1128.     /*
  1129.      * Convert the case-insensitive Tk_Font family name to the
  1130.      * case-sensitive Postscript family name.  Take out any spaces and
  1131.      * capitalize the first letter of each word.
  1132.      */
  1133.  
  1134.     family = fontPtr->fa.family;
  1135.     if (strncasecmp(family, "itc ", 4) == 0) {
  1136.     family = family + 4;
  1137.     }
  1138.     if ((strcasecmp(family, "Arial") == 0)
  1139.         || (strcasecmp(family, "Geneva") == 0)) {
  1140.     family = "Helvetica";
  1141.     } else if ((strcasecmp(family, "Times New Roman") == 0)
  1142.         || (strcasecmp(family, "New York") == 0)) {
  1143.     family = "Times";
  1144.     } else if ((strcasecmp(family, "Courier New") == 0)
  1145.         || (strcasecmp(family, "Monaco") == 0)) {
  1146.     family = "Courier";
  1147.     } else if (strcasecmp(family, "AvantGarde") == 0) {
  1148.     family = "AvantGarde";
  1149.     } else if (strcasecmp(family, "ZapfChancery") == 0) {
  1150.     family = "ZapfChancery";
  1151.     } else if (strcasecmp(family, "ZapfDingbats") == 0) {
  1152.     family = "ZapfDingbats";
  1153.     } else {
  1154.     /*
  1155.      * Inline, capitalize the first letter of each word, lowercase the
  1156.      * rest of the letters in each word, and then take out the spaces
  1157.      * between the words.  This may make the DString shorter, which is
  1158.      * safe to do.
  1159.      */
  1160.  
  1161.     Tcl_DStringAppend(dsPtr, family, -1);
  1162.  
  1163.     src = dest = Tcl_DStringValue(dsPtr) + len;
  1164.     upper = 1;
  1165.     for (; *src != '\0'; src++, dest++) {
  1166.         while (isspace(UCHAR(*src))) {
  1167.         src++;
  1168.         upper = 1;
  1169.         }
  1170.         *dest = *src;
  1171.         if ((upper != 0) && (islower(UCHAR(*src)))) {
  1172.         *dest = toupper(UCHAR(*src));
  1173.         }
  1174.         upper = 0;
  1175.     }
  1176.     *dest = '\0';
  1177.     Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr));
  1178.     family = Tcl_DStringValue(dsPtr) + len;
  1179.     }
  1180.     if (family != Tcl_DStringValue(dsPtr) + len) {
  1181.     Tcl_DStringAppend(dsPtr, family, -1);
  1182.     family = Tcl_DStringValue(dsPtr) + len;
  1183.     }
  1184.  
  1185.     if (strcasecmp(family, "NewCenturySchoolbook") == 0) {
  1186.     Tcl_DStringSetLength(dsPtr, len);
  1187.     Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1);
  1188.     family = Tcl_DStringValue(dsPtr) + len;
  1189.     }
  1190.  
  1191.     /*
  1192.      * Get the string to use for the weight.
  1193.      */
  1194.  
  1195.     weightString = NULL;
  1196.     if (fontPtr->fa.weight == TK_FW_NORMAL) {
  1197.     if (strcmp(family, "Bookman") == 0) {
  1198.         weightString = "Light";
  1199.     } else if (strcmp(family, "AvantGarde") == 0) {
  1200.         weightString = "Book";
  1201.     } else if (strcmp(family, "ZapfChancery") == 0) {
  1202.         weightString = "Medium";
  1203.     }
  1204.     } else {
  1205.     if ((strcmp(family, "Bookman") == 0)
  1206.         || (strcmp(family, "AvantGarde") == 0)) {
  1207.         weightString = "Demi";
  1208.     } else {
  1209.         weightString = "Bold";
  1210.     }
  1211.     }
  1212.  
  1213.     /*
  1214.      * Get the string to use for the slant.
  1215.      */
  1216.  
  1217.     slantString = NULL;
  1218.     if (fontPtr->fa.slant == TK_FS_ROMAN) {
  1219.     ;
  1220.     } else {
  1221.     if ((strcmp(family, "Helvetica") == 0)
  1222.         || (strcmp(family, "Courier") == 0)
  1223.         || (strcmp(family, "AvantGarde") == 0)) {
  1224.         slantString = "Oblique";
  1225.     } else {
  1226.         slantString = "Italic";
  1227.     }
  1228.     }
  1229.  
  1230.     /*
  1231.      * The string "Roman" needs to be added to some fonts that are not bold
  1232.      * and not italic.
  1233.      */
  1234.  
  1235.     if ((slantString == NULL) && (weightString == NULL)) {
  1236.     if ((strcmp(family, "Times") == 0) 
  1237.         || (strcmp(family, "NewCenturySchlbk") == 0)
  1238.         || (strcmp(family, "Palatino") == 0)) {
  1239.         Tcl_DStringAppend(dsPtr, "-Roman", -1);
  1240.     }
  1241.     } else {
  1242.     Tcl_DStringAppend(dsPtr, "-", -1);
  1243.     if (weightString != NULL) {
  1244.         Tcl_DStringAppend(dsPtr, weightString, -1);
  1245.     }
  1246.     if (slantString != NULL) {
  1247.         Tcl_DStringAppend(dsPtr, slantString, -1);
  1248.     }
  1249.     }
  1250.  
  1251.     return fontPtr->fa.pointsize;
  1252. }
  1253.  
  1254. /*
  1255.  *---------------------------------------------------------------------------
  1256.  *
  1257.  * Tk_TextWidth --
  1258.  *
  1259.  *    A wrapper function for the more complicated interface of
  1260.  *    Tk_MeasureChars.  Computes how much space the given
  1261.  *    simple string needs.
  1262.  *
  1263.  * Results:
  1264.  *    The return value is the width (in pixels) of the given string.
  1265.  *
  1266.  * Side effects:
  1267.  *    None.
  1268.  *
  1269.  *---------------------------------------------------------------------------
  1270.  */
  1271.  
  1272. int
  1273. Tk_TextWidth(tkfont, string, numChars)
  1274.     Tk_Font tkfont;        /* Font in which text will be measured. */
  1275.     CONST char *string;        /* String whose width will be computed. */
  1276.     int numChars;        /* Number of characters to consider from
  1277.                  * string, or < 0 for strlen(). */
  1278. {
  1279.     int width;
  1280.  
  1281.     if (numChars < 0) {
  1282.     numChars = strlen(string);
  1283.     }
  1284.     Tk_MeasureChars(tkfont, string, numChars, 0, 0, &width);
  1285.     return width;
  1286. }
  1287.  
  1288. /*
  1289.  *---------------------------------------------------------------------------
  1290.  *
  1291.  * Tk_UnderlineChars --
  1292.  *
  1293.  *    This procedure draws an underline for a given range of characters
  1294.  *    in a given string.  It doesn't draw the characters (which are
  1295.  *    assumed to have been displayed previously); it just draws the
  1296.  *    underline.  This procedure would mainly be used to quickly
  1297.  *    underline a few characters without having to construct an
  1298.  *    underlined font.  To produce properly underlined text, the
  1299.  *    appropriate underlined font should be constructed and used. 
  1300.  *
  1301.  * Results:
  1302.  *    None.
  1303.  *
  1304.  * Side effects:
  1305.  *    Information gets displayed in "drawable".
  1306.  *
  1307.  *----------------------------------------------------------------------
  1308.  */
  1309.  
  1310. void
  1311. Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstChar,
  1312.     lastChar)
  1313.     Display *display;        /* Display on which to draw. */
  1314.     Drawable drawable;        /* Window or pixmap in which to draw. */
  1315.     GC gc;            /* Graphics context for actually drawing
  1316.                  * line. */
  1317.     Tk_Font tkfont;        /* Font used in GC;  must have been allocated
  1318.                  * by Tk_GetFont().  Used for character
  1319.                  * dimensions, etc. */
  1320.     CONST char *string;        /* String containing characters to be
  1321.                  * underlined or overstruck. */
  1322.     int x, y;            /* Coordinates at which first character of
  1323.                  * string is drawn. */
  1324.     int firstChar;        /* Index of first character. */
  1325.     int lastChar;        /* Index of one after the last character. */
  1326. {
  1327.     TkFont *fontPtr;
  1328.     int startX, endX;
  1329.  
  1330.     fontPtr = (TkFont *) tkfont;
  1331.     
  1332.     Tk_MeasureChars(tkfont, string, firstChar, 0, 0, &startX);
  1333.     Tk_MeasureChars(tkfont, string, lastChar, 0, 0, &endX);
  1334.  
  1335.     XFillRectangle(display, drawable, gc, x + startX,
  1336.         y + fontPtr->underlinePos, (unsigned int) (endX - startX),
  1337.         (unsigned int) fontPtr->underlineHeight);
  1338. }
  1339.  
  1340. /*
  1341.  *---------------------------------------------------------------------------
  1342.  *
  1343.  * Tk_ComputeTextLayout --
  1344.  *
  1345.  *    Computes the amount of screen space needed to display a
  1346.  *    multi-line, justified string of text.  Records all the
  1347.  *    measurements that were done to determine to size and
  1348.  *    positioning of the individual lines of text; this information
  1349.  *    can be used by the Tk_DrawTextLayout() procedure to
  1350.  *    display the text quickly (without remeasuring it).
  1351.  *
  1352.  *    This procedure is useful for simple widgets that want to
  1353.  *    display single-font, multi-line text and want Tk to handle the
  1354.  *    details.
  1355.  *
  1356.  * Results:
  1357.  *    The return value is a Tk_TextLayout token that holds the
  1358.  *    measurement information for the given string.  The token is
  1359.  *    only valid for the given string.  If the string is freed,
  1360.  *    the token is no longer valid and must also be freed.  To free
  1361.  *    the token, call Tk_FreeTextLayout().
  1362.  *
  1363.  *    The dimensions of the screen area needed to display the text
  1364.  *    are stored in *widthPtr and *heightPtr.
  1365.  *
  1366.  * Side effects:
  1367.  *    Memory is allocated to hold the measurement information.  
  1368.  *
  1369.  *---------------------------------------------------------------------------
  1370.  */
  1371.  
  1372. Tk_TextLayout
  1373. Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags,
  1374.     widthPtr, heightPtr)
  1375.     Tk_Font tkfont;        /* Font that will be used to display text. */
  1376.     CONST char *string;        /* String whose dimensions are to be
  1377.                  * computed. */
  1378.     int numChars;        /* Number of characters to consider from
  1379.                  * string, or < 0 for strlen(). */
  1380.     int wrapLength;        /* Longest permissible line length, in
  1381.                  * pixels.  <= 0 means no automatic wrapping:
  1382.                  * just let lines get as long as needed. */
  1383.     Tk_Justify justify;        /* How to justify lines. */
  1384.     int flags;            /* Flag bits OR-ed together.
  1385.                  * TK_IGNORE_TABS means that tab characters
  1386.                  * should not be expanded.  TK_IGNORE_NEWLINES
  1387.                  * means that newline characters should not
  1388.                  * cause a line break. */
  1389.     int *widthPtr;        /* Filled with width of string. */
  1390.     int *heightPtr;        /* Filled with height of string. */
  1391. {
  1392.     TkFont *fontPtr;
  1393.     CONST char *start, *end, *special;
  1394.     int n, y, charsThisChunk, maxChunks;
  1395.     int baseline, height, curX, newX, maxWidth;
  1396.     TextLayout *layoutPtr;
  1397.     LayoutChunk *chunkPtr;
  1398.     CONST TkFontMetrics *fmPtr;
  1399. #define MAX_LINES 50
  1400.     int staticLineLengths[MAX_LINES];
  1401.     int *lineLengths;
  1402.     int maxLines, curLine, layoutHeight;
  1403.  
  1404.     lineLengths = staticLineLengths;
  1405.     maxLines = MAX_LINES;
  1406.     
  1407.     fontPtr = (TkFont *) tkfont;
  1408.     fmPtr = &fontPtr->fm;
  1409.  
  1410.     height = fmPtr->ascent + fmPtr->descent;
  1411.  
  1412.     if (numChars < 0) {
  1413.     numChars = strlen(string);
  1414.     }
  1415.  
  1416.     maxChunks = 1;
  1417.  
  1418.     layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout)
  1419.         + (maxChunks - 1) * sizeof(LayoutChunk));
  1420.     layoutPtr->tkfont        = tkfont;
  1421.     layoutPtr->string        = string;
  1422.     layoutPtr->numChunks    = 0;
  1423.  
  1424.     baseline = fmPtr->ascent;
  1425.     maxWidth = 0;
  1426.  
  1427.     /*
  1428.      * Divide the string up into simple strings and measure each string.
  1429.      */
  1430.  
  1431.     curX = 0;
  1432.  
  1433.     end = string + numChars;
  1434.     special = string;
  1435.  
  1436.     flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
  1437.     flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;        
  1438.     curLine = 0;
  1439.     for (start = string; start < end; ) {
  1440.     if (start >= special) {
  1441.         /*
  1442.          * Find the next special character in the string.
  1443.          */
  1444.  
  1445.         for (special = start; special < end; special++) {
  1446.         if (!(flags & TK_IGNORE_NEWLINES)) {
  1447.             if ((*special == '\n') || (*special == '\r')) {
  1448.             break;
  1449.             }
  1450.         }
  1451.         if (!(flags & TK_IGNORE_TABS)) {
  1452.             if (*special == '\t') {
  1453.             break;
  1454.             }
  1455.         }
  1456.         }
  1457.     }
  1458.  
  1459.     /*
  1460.      * Special points at the next special character (or the end of the
  1461.      * string).  Process characters between start and special.
  1462.      */
  1463.  
  1464.     chunkPtr = NULL;
  1465.     if (start < special) {
  1466.         charsThisChunk = Tk_MeasureChars(tkfont, start, special - start,
  1467.             wrapLength - curX, flags, &newX);
  1468.         newX += curX;
  1469.         flags &= ~TK_AT_LEAST_ONE;
  1470.         if (charsThisChunk > 0) {
  1471.         chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
  1472.             charsThisChunk, curX, newX, baseline);
  1473.             
  1474.         start += charsThisChunk;
  1475.         curX = newX;
  1476.         }
  1477.     }
  1478.  
  1479.     if ((start == special) && (special < end)) {
  1480.         /*
  1481.          * Handle the special character.
  1482.          */
  1483.  
  1484.         chunkPtr = NULL;
  1485.         if (*special == '\t') {
  1486.         newX = curX + fontPtr->tabWidth;
  1487.         newX -= newX % fontPtr->tabWidth;
  1488.         NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
  1489.             baseline)->numDisplayChars = -1;
  1490.         start++;
  1491.         if ((start < end) &&
  1492.             ((wrapLength <= 0) || (newX <= wrapLength))) {
  1493.             /*
  1494.              * More chars can still fit on this line.
  1495.              */
  1496.  
  1497.             curX = newX;
  1498.             flags &= ~TK_AT_LEAST_ONE;
  1499.             continue;
  1500.         }
  1501.         } else {    
  1502.         NewChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000,
  1503.             baseline)->numDisplayChars = -1;
  1504.         start++;
  1505.         goto wrapLine;
  1506.         }
  1507.     }
  1508.  
  1509.     /*
  1510.      * No more characters are going to go on this line, either because
  1511.      * no more characters can fit or there are no more characters left.
  1512.      * Consume all extra spaces at end of line.  
  1513.      */
  1514.  
  1515.     while ((start < end) && isspace(UCHAR(*start))) {
  1516.         if (!(flags & TK_IGNORE_NEWLINES)) {
  1517.         if ((*start == '\n') || (*start == '\r')) {
  1518.             break;
  1519.         }
  1520.         }
  1521.         if (!(flags & TK_IGNORE_TABS)) {
  1522.         if (*start == '\t') {
  1523.             break;
  1524.         }
  1525.         }
  1526.         start++;
  1527.     }
  1528.     if (chunkPtr != NULL) {
  1529.         /*
  1530.          * Append all the extra spaces on this line to the end of the
  1531.          * last text chunk.
  1532.          */
  1533.         charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars);
  1534.         if (charsThisChunk > 0) {
  1535.         chunkPtr->numChars += Tk_MeasureChars(tkfont,
  1536.             chunkPtr->start + chunkPtr->numChars, charsThisChunk,
  1537.             0, 0, &chunkPtr->totalWidth);
  1538.         chunkPtr->totalWidth += curX;
  1539.         }
  1540.     }
  1541.  
  1542.         wrapLine: 
  1543.     flags |= TK_AT_LEAST_ONE;
  1544.  
  1545.     /*
  1546.      * Save current line length, then move current position to start of
  1547.      * next line.
  1548.      */
  1549.  
  1550.     if (curX > maxWidth) {
  1551.         maxWidth = curX;
  1552.     }
  1553.  
  1554.     /*
  1555.      * Remember width of this line, so that all chunks on this line
  1556.      * can be centered or right justified, if necessary.
  1557.      */
  1558.  
  1559.     if (curLine >= maxLines) {
  1560.         int *newLengths;
  1561.         
  1562.         newLengths = (int *) ckalloc(2 * maxLines * sizeof(int));
  1563.         memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int));
  1564.         if (lineLengths != staticLineLengths) {
  1565.             ckfree((char *) lineLengths);
  1566.         }
  1567.         lineLengths = newLengths;
  1568.         maxLines *= 2;
  1569.     }
  1570.     lineLengths[curLine] = curX;
  1571.     curLine++;
  1572.  
  1573.     curX = 0;
  1574.     baseline += height;
  1575.     }
  1576.  
  1577.     /*
  1578.      * Using maximum line length, shift all the chunks so that the lines are
  1579.      * all justified correctly.
  1580.      */
  1581.     
  1582.     curLine = 0;
  1583.     chunkPtr = layoutPtr->chunks;
  1584.     y = chunkPtr->y;
  1585.     for (n = 0; n < layoutPtr->numChunks; n++) {
  1586.     int extra;
  1587.  
  1588.     if (chunkPtr->y != y) {
  1589.         curLine++;
  1590.         y = chunkPtr->y;
  1591.     }
  1592.     extra = maxWidth - lineLengths[curLine];
  1593.     if (justify == TK_JUSTIFY_CENTER) {
  1594.         chunkPtr->x += extra / 2;
  1595.     } else if (justify == TK_JUSTIFY_RIGHT) {
  1596.         chunkPtr->x += extra;
  1597.     }
  1598.     chunkPtr++;
  1599.     }
  1600.  
  1601.     layoutPtr->width = maxWidth;
  1602.     layoutHeight = baseline - fmPtr->ascent;
  1603.     if (layoutPtr->numChunks == 0) {
  1604.     layoutHeight = height;
  1605.  
  1606.     /*
  1607.      * This fake chunk is used by the other procedures so that they can
  1608.      * pretend that there is a chunk with no chars in it, which makes
  1609.      * the coding simpler.
  1610.      */
  1611.  
  1612.     layoutPtr->numChunks = 1;
  1613.     layoutPtr->chunks[0].start        = string;
  1614.     layoutPtr->chunks[0].numChars        = 0;
  1615.     layoutPtr->chunks[0].numDisplayChars    = -1;
  1616.     layoutPtr->chunks[0].x            = 0;
  1617.     layoutPtr->chunks[0].y            = fmPtr->ascent;
  1618.     layoutPtr->chunks[0].totalWidth        = 0;
  1619.     layoutPtr->chunks[0].displayWidth    = 0;
  1620.     }
  1621.  
  1622.     if (widthPtr != NULL) {
  1623.     *widthPtr = layoutPtr->width;
  1624.     }
  1625.     if (heightPtr != NULL) {
  1626.     *heightPtr = layoutHeight;
  1627.     }
  1628.     if (lineLengths != staticLineLengths) {
  1629.     ckfree((char *) lineLengths);
  1630.     }
  1631.  
  1632.     return (Tk_TextLayout) layoutPtr;
  1633. }
  1634.  
  1635. /*
  1636.  *---------------------------------------------------------------------------
  1637.  *
  1638.  * Tk_FreeTextLayout --
  1639.  *
  1640.  *    This procedure is called to release the storage associated with
  1641.  *    a Tk_TextLayout when it is no longer needed.
  1642.  *
  1643.  * Results:
  1644.  *    None.
  1645.  *
  1646.  * Side effects:
  1647.  *    Memory is freed.
  1648.  *
  1649.  *---------------------------------------------------------------------------
  1650.  */
  1651.  
  1652. void
  1653. Tk_FreeTextLayout(textLayout)
  1654.     Tk_TextLayout textLayout;    /* The text layout to be released. */
  1655. {
  1656.     TextLayout *layoutPtr;
  1657.  
  1658.     layoutPtr = (TextLayout *) textLayout;
  1659.     if (layoutPtr != NULL) {
  1660.     ckfree((char *) layoutPtr);
  1661.     }
  1662. }
  1663.  
  1664. /*
  1665.  *---------------------------------------------------------------------------
  1666.  *
  1667.  * Tk_DrawTextLayout --
  1668.  *
  1669.  *    Use the information in the Tk_TextLayout token to display a
  1670.  *    multi-line, justified string of text.
  1671.  *
  1672.  *    This procedure is useful for simple widgets that need to
  1673.  *    display single-font, multi-line text and want Tk to handle
  1674.  *    the details.
  1675.  *
  1676.  * Results:
  1677.  *    None.
  1678.  *
  1679.  * Side effects:
  1680.  *    Text drawn on the screen.
  1681.  *
  1682.  *---------------------------------------------------------------------------
  1683.  */
  1684.  
  1685. void
  1686. Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)
  1687.     Display *display;        /* Display on which to draw. */
  1688.     Drawable drawable;        /* Window or pixmap in which to draw. */
  1689.     GC gc;            /* Graphics context to use for drawing text. */
  1690.     Tk_TextLayout layout;    /* Layout information, from a previous call
  1691.                  * to Tk_ComputeTextLayout(). */
  1692.     int x, y;            /* Upper-left hand corner of rectangle in
  1693.                  * which to draw (pixels). */
  1694.     int firstChar;        /* The index of the first character to draw
  1695.                  * from the given text item.  0 specfies the
  1696.                  * beginning. */
  1697.     int lastChar;        /* The index just after the last character
  1698.                  * to draw from the given text item.  A number
  1699.                  * < 0 means to draw all characters. */
  1700. {
  1701.     TextLayout *layoutPtr;
  1702.     int i, numDisplayChars, drawX;
  1703.     LayoutChunk *chunkPtr;
  1704.  
  1705.     layoutPtr = (TextLayout *) layout;
  1706.     if (layoutPtr == NULL) {
  1707.     return;
  1708.     }
  1709.  
  1710.     if (lastChar < 0) {
  1711.     lastChar = 100000000;
  1712.     }
  1713.     chunkPtr = layoutPtr->chunks;
  1714.     for (i = 0; i < layoutPtr->numChunks; i++) {
  1715.     numDisplayChars = chunkPtr->numDisplayChars;
  1716.     if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
  1717.         if (firstChar <= 0) {
  1718.         drawX = 0;
  1719.         firstChar = 0;
  1720.         } else {
  1721.         Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstChar,
  1722.             0, 0, &drawX);
  1723.         }
  1724.         if (lastChar < numDisplayChars) {
  1725.         numDisplayChars = lastChar;
  1726.         }
  1727.         Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
  1728.             chunkPtr->start + firstChar, numDisplayChars - firstChar,
  1729.             x + chunkPtr->x + drawX, y + chunkPtr->y);
  1730.     }
  1731.     firstChar -= chunkPtr->numChars;
  1732.     lastChar -= chunkPtr->numChars;
  1733.     if (lastChar <= 0) {
  1734.         break;
  1735.     }
  1736.     chunkPtr++;
  1737.     }
  1738. }
  1739.  
  1740. /*
  1741.  *---------------------------------------------------------------------------
  1742.  *
  1743.  * Tk_UnderlineTextLayout --
  1744.  *
  1745.  *    Use the information in the Tk_TextLayout token to display an
  1746.  *    underline below an individual character.  This procedure does
  1747.  *    not draw the text, just the underline.
  1748.  *
  1749.  *    This procedure is useful for simple widgets that need to
  1750.  *    display single-font, multi-line text with an individual
  1751.  *    character underlined and want Tk to handle the details.
  1752.  *    To display larger amounts of underlined text, construct
  1753.  *    and use an underlined font.
  1754.  *
  1755.  * Results:
  1756.  *    None.
  1757.  *
  1758.  * Side effects:
  1759.  *    Underline drawn on the screen.
  1760.  *
  1761.  *---------------------------------------------------------------------------
  1762.  */
  1763.  
  1764. void
  1765. Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline)
  1766.     Display *display;        /* Display on which to draw. */
  1767.     Drawable drawable;        /* Window or pixmap in which to draw. */
  1768.     GC gc;            /* Graphics context to use for drawing text. */
  1769.     Tk_TextLayout layout;    /* Layout information, from a previous call
  1770.                  * to Tk_ComputeTextLayout(). */
  1771.     int x, y;            /* Upper-left hand corner of rectangle in
  1772.                  * which to draw (pixels). */
  1773.     int underline;        /* Index of the single character to
  1774.                  * underline, or -1 for no underline. */
  1775. {
  1776.     TextLayout *layoutPtr;
  1777.     TkFont *fontPtr;
  1778.     int xx, yy, width, height;
  1779.  
  1780.     if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
  1781.         && (width != 0)) {
  1782.     layoutPtr = (TextLayout *) layout;
  1783.     fontPtr = (TkFont *) layoutPtr->tkfont;
  1784.  
  1785.     XFillRectangle(display, drawable, gc, x + xx, 
  1786.         y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
  1787.         (unsigned int) width, (unsigned int) fontPtr->underlineHeight);
  1788.     }
  1789. }
  1790.  
  1791. /*
  1792.  *---------------------------------------------------------------------------
  1793.  *
  1794.  * Tk_PointToChar --
  1795.  *
  1796.  *    Use the information in the Tk_TextLayout token to determine the
  1797.  *    character closest to the given point.  The point must be
  1798.  *    specified with respect to the upper-left hand corner of the
  1799.  *    text layout, which is considered to be located at (0, 0).
  1800.  *
  1801.  *    Any point whose y-value is less that 0 will be considered closest
  1802.  *    to the first character in the text layout; any point whose y-value
  1803.  *    is greater than the height of the text layout will be considered
  1804.  *    closest to the last character in the text layout.
  1805.  *
  1806.  *    Any point whose x-value is less than 0 will be considered closest
  1807.  *    to the first character on that line; any point whose x-value is
  1808.  *    greater than the width of the text layout will be considered
  1809.  *    closest to the last character on that line.
  1810.  *
  1811.  * Results:
  1812.  *    The return value is the index of the character that was
  1813.  *    closest to the point.  Given a text layout with no characters,
  1814.  *    the value 0 will always be returned, referring to a hypothetical
  1815.  *    zero-width placeholder character.
  1816.  *
  1817.  * Side effects:
  1818.  *    None.
  1819.  *
  1820.  *---------------------------------------------------------------------------
  1821.  */
  1822.  
  1823. int
  1824. Tk_PointToChar(layout, x, y)
  1825.     Tk_TextLayout layout;    /* Layout information, from a previous call
  1826.                  * to Tk_ComputeTextLayout(). */
  1827.     int x, y;            /* Coordinates of point to check, with
  1828.                  * respect to the upper-left corner of the
  1829.                  * text layout. */
  1830. {
  1831.     TextLayout *layoutPtr;
  1832.     LayoutChunk *chunkPtr, *lastPtr;
  1833.     TkFont *fontPtr;
  1834.     int i, n, dummy, baseline, pos;
  1835.  
  1836.     if (y < 0) {
  1837.     /*
  1838.      * Point lies above any line in this layout.  Return the index of
  1839.      * the first char.
  1840.      */
  1841.  
  1842.     return 0;
  1843.     }
  1844.  
  1845.     /*
  1846.      * Find which line contains the point.
  1847.      */
  1848.  
  1849.     layoutPtr = (TextLayout *) layout;
  1850.     fontPtr = (TkFont *) layoutPtr->tkfont;
  1851.     lastPtr = chunkPtr = layoutPtr->chunks;
  1852.     for (i = 0; i < layoutPtr->numChunks; i++) {
  1853.     baseline = chunkPtr->y;
  1854.     if (y < baseline + fontPtr->fm.descent) {
  1855.         if (x < chunkPtr->x) {
  1856.         /*
  1857.          * Point is to the left of all chunks on this line.  Return
  1858.          * the index of the first character on this line.
  1859.          */
  1860.  
  1861.         return chunkPtr->start - layoutPtr->string;
  1862.         }
  1863.         if (x >= layoutPtr->width) {
  1864.         /*
  1865.          * If point lies off right side of the text layout, return
  1866.          * the last char in the last chunk on this line.  Without
  1867.          * this, it might return the index of the first char that
  1868.          * was located outside of the text layout.
  1869.          */
  1870.  
  1871.         x = INT_MAX;
  1872.         }
  1873.  
  1874.         /*
  1875.          * Examine all chunks on this line to see which one contains
  1876.          * the specified point.
  1877.          */
  1878.  
  1879.         lastPtr = chunkPtr;
  1880.         while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline))  {
  1881.         if (x < chunkPtr->x + chunkPtr->totalWidth) {
  1882.             /*
  1883.              * Point falls on one of the characters in this chunk.
  1884.              */
  1885.  
  1886.             if (chunkPtr->numDisplayChars < 0) {
  1887.             /*
  1888.              * This is a special chunk that encapsulates a single
  1889.              * tab or newline char.
  1890.              */
  1891.  
  1892.             return chunkPtr->start - layoutPtr->string;
  1893.             }
  1894.             n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
  1895.                 chunkPtr->numChars, x + 1 - chunkPtr->x,
  1896.                 TK_PARTIAL_OK, &dummy);
  1897.             return (chunkPtr->start + n - 1) - layoutPtr->string;
  1898.         }
  1899.         lastPtr = chunkPtr;
  1900.         chunkPtr++;
  1901.         i++;
  1902.         }
  1903.  
  1904.         /*
  1905.          * Point is to the right of all chars in all the chunks on this
  1906.          * line.  Return the index just past the last char in the last
  1907.          * chunk on this line.
  1908.          */
  1909.  
  1910.         pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
  1911.         if (i < layoutPtr->numChunks) {
  1912.         pos--;
  1913.         }
  1914.         return pos;
  1915.     }
  1916.     lastPtr = chunkPtr;
  1917.     chunkPtr++;
  1918.     }
  1919.  
  1920.     /*
  1921.      * Point lies below any line in this text layout.  Return the index
  1922.      * just past the last char.
  1923.      */
  1924.  
  1925.     return (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
  1926. }
  1927.  
  1928. /*
  1929.  *---------------------------------------------------------------------------
  1930.  *
  1931.  * Tk_CharBbox --
  1932.  *
  1933.  *    Use the information in the Tk_TextLayout token to return the
  1934.  *    bounding box for the character specified by index.  
  1935.  *
  1936.  *    The width of the bounding box is the advance width of the
  1937.  *    character, and does not include and left- or right-bearing.
  1938.  *    Any character that extends partially outside of the
  1939.  *    text layout is considered to be truncated at the edge.  Any
  1940.  *    character which is located completely outside of the text
  1941.  *    layout is considered to be zero-width and pegged against
  1942.  *    the edge.
  1943.  *
  1944.  *    The height of the bounding box is the line height for this font,
  1945.  *    extending from the top of the ascent to the bottom of the
  1946.  *    descent.  Information about the actual height of the individual
  1947.  *    letter is not available.
  1948.  *
  1949.  *    A text layout that contains no characters is considered to
  1950.  *    contain a single zero-width placeholder character.
  1951.  * 
  1952.  * Results:
  1953.  *    The return value is 0 if the index did not specify a character
  1954.  *    in the text layout, or non-zero otherwise.  In that case,
  1955.  *    *bbox is filled with the bounding box of the character.
  1956.  *
  1957.  * Side effects:
  1958.  *    None.
  1959.  *
  1960.  *---------------------------------------------------------------------------
  1961.  */
  1962.  
  1963. int
  1964. Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)
  1965.     Tk_TextLayout layout;   /* Layout information, from a previous call to
  1966.                  * Tk_ComputeTextLayout(). */
  1967.     int index;            /* The index of the character whose bbox is
  1968.                  * desired. */
  1969.     int *xPtr, *yPtr;        /* Filled with the upper-left hand corner, in
  1970.                  * pixels, of the bounding box for the character
  1971.                  * specified by index, if non-NULL. */
  1972.     int *widthPtr, *heightPtr;
  1973.                 /* Filled with the width and height of the
  1974.                  * bounding box for the character specified by
  1975.                  * index, if non-NULL. */
  1976. {
  1977.     TextLayout *layoutPtr;
  1978.     LayoutChunk *chunkPtr;
  1979.     int i, x, w;
  1980.     Tk_Font tkfont;
  1981.     TkFont *fontPtr;
  1982.  
  1983.     if (index < 0) {
  1984.     return 0;
  1985.     }
  1986.  
  1987.     layoutPtr = (TextLayout *) layout;
  1988.     chunkPtr = layoutPtr->chunks;
  1989.     tkfont = layoutPtr->tkfont;
  1990.     fontPtr = (TkFont *) tkfont;
  1991.  
  1992.     for (i = 0; i < layoutPtr->numChunks; i++) {
  1993.     if (chunkPtr->numDisplayChars < 0) {
  1994.         if (index == 0) {
  1995.         x = chunkPtr->x;
  1996.         w = chunkPtr->totalWidth;
  1997.         goto check;
  1998.         }
  1999.     } else if (index < chunkPtr->numChars) {
  2000.         if (xPtr != NULL) {
  2001.         Tk_MeasureChars(tkfont, chunkPtr->start, index, 0, 0, &x);
  2002.         x += chunkPtr->x;
  2003.         }
  2004.         if (widthPtr != NULL) {
  2005.         Tk_MeasureChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w);
  2006.         }
  2007.         goto check;
  2008.     }
  2009.     index -= chunkPtr->numChars;
  2010.     chunkPtr++;
  2011.     }
  2012.     if (index == 0) {
  2013.     /*
  2014.      * Special case to get location just past last char in layout.
  2015.      */
  2016.  
  2017.     chunkPtr--;
  2018.     x = chunkPtr->x + chunkPtr->totalWidth;
  2019.     w = 0;
  2020.     } else {
  2021.     return 0;
  2022.     }
  2023.  
  2024.     /*
  2025.      * Ensure that the bbox lies within the text layout.  This forces all
  2026.      * chars that extend off the right edge of the text layout to have
  2027.      * truncated widths, and all chars that are completely off the right
  2028.      * edge of the text layout to peg to the edge and have 0 width.
  2029.      */
  2030.     check:
  2031.     if (yPtr != NULL) {
  2032.     *yPtr = chunkPtr->y - fontPtr->fm.ascent;
  2033.     }
  2034.     if (heightPtr != NULL) {
  2035.     *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent;
  2036.     }
  2037.  
  2038.     if (x > layoutPtr->width) {
  2039.     x = layoutPtr->width;
  2040.     }
  2041.     if (xPtr != NULL) {
  2042.     *xPtr = x;
  2043.     }
  2044.     if (widthPtr != NULL) {
  2045.     if (x + w > layoutPtr->width) {
  2046.         w = layoutPtr->width - x;
  2047.     }
  2048.     *widthPtr = w;
  2049.     }
  2050.  
  2051.     return 1;
  2052. }
  2053.  
  2054. /*
  2055.  *---------------------------------------------------------------------------
  2056.  *
  2057.  * Tk_DistanceToTextLayout --
  2058.  *
  2059.  *    Computes the distance in pixels from the given point to the
  2060.  *    given text layout.  Non-displaying space characters that occur
  2061.  *    at the end of individual lines in the text layout are ignored
  2062.  *    for hit detection purposes.
  2063.  *
  2064.  * Results:
  2065.  *    The return value is 0 if the point (x, y) is inside the text
  2066.  *    layout.  If the point isn't inside the text layout then the
  2067.  *    return value is the distance in pixels from the point to the
  2068.  *    text item.
  2069.  *
  2070.  * Side effects:
  2071.  *    None.
  2072.  *
  2073.  *---------------------------------------------------------------------------
  2074.  */
  2075.  
  2076. int
  2077. Tk_DistanceToTextLayout(layout, x, y)
  2078.     Tk_TextLayout layout;    /* Layout information, from a previous call
  2079.                  * to Tk_ComputeTextLayout(). */
  2080.     int x, y;            /* Coordinates of point to check, with
  2081.                  * respect to the upper-left corner of the
  2082.                  * text layout (in pixels). */
  2083. {
  2084.     int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
  2085.     LayoutChunk *chunkPtr;
  2086.     TextLayout *layoutPtr;
  2087.     TkFont *fontPtr;
  2088.  
  2089.     layoutPtr = (TextLayout *) layout;
  2090.     fontPtr = (TkFont *) layoutPtr->tkfont;
  2091.     ascent = fontPtr->fm.ascent;
  2092.     descent = fontPtr->fm.descent;
  2093.     
  2094.     minDist = 0;
  2095.     chunkPtr = layoutPtr->chunks;
  2096.     for (i = 0; i < layoutPtr->numChunks; i++) {
  2097.     if (chunkPtr->start[0] == '\n') {
  2098.         /*
  2099.          * Newline characters are not counted when computing distance
  2100.          * (but tab characters would still be considered).
  2101.          */
  2102.  
  2103.         chunkPtr++;
  2104.         continue;
  2105.     }
  2106.  
  2107.     x1 = chunkPtr->x;
  2108.     y1 = chunkPtr->y - ascent;
  2109.     x2 = chunkPtr->x + chunkPtr->displayWidth;
  2110.     y2 = chunkPtr->y + descent;
  2111.  
  2112.     if (x < x1) {
  2113.         xDiff = x1 - x;
  2114.     } else if (x >= x2) {
  2115.         xDiff = x - x2 + 1;
  2116.     } else {
  2117.         xDiff = 0;
  2118.     }
  2119.  
  2120.     if (y < y1) {
  2121.         yDiff = y1 - y;
  2122.     } else if (y >= y2) {
  2123.         yDiff = y - y2 + 1;
  2124.     } else {
  2125.         yDiff = 0;
  2126.     }
  2127.     if ((xDiff == 0) && (yDiff == 0)) {
  2128.         return 0;
  2129.     }
  2130.     dist = (int) hypot((double) xDiff, (double) yDiff);
  2131.     if ((dist < minDist) || (minDist == 0)) {
  2132.         minDist = dist;
  2133.     }
  2134.     chunkPtr++;
  2135.     }
  2136.     return minDist;
  2137. }
  2138.  
  2139. /*
  2140.  *---------------------------------------------------------------------------
  2141.  *
  2142.  * Tk_IntersectTextLayout --
  2143.  *
  2144.  *    Determines whether a text layout lies entirely inside,
  2145.  *    entirely outside, or overlaps a given rectangle.  Non-displaying
  2146.  *    space characters that occur at the end of individual lines in
  2147.  *    the text layout are ignored for intersection calculations.
  2148.  *
  2149.  * Results:
  2150.  *    The return value is -1 if the text layout is entirely outside of
  2151.  *    the rectangle, 0 if it overlaps, and 1 if it is entirely inside
  2152.  *    of the rectangle.
  2153.  *
  2154.  * Side effects:
  2155.  *    None.
  2156.  *
  2157.  *---------------------------------------------------------------------------
  2158.  */
  2159.  
  2160. int
  2161. Tk_IntersectTextLayout(layout, x, y, width, height)
  2162.     Tk_TextLayout layout;    /* Layout information, from a previous call
  2163.                  * to Tk_ComputeTextLayout(). */
  2164.     int x, y;            /* Upper-left hand corner, in pixels, of
  2165.                  * rectangular area to compare with text
  2166.                  * layout.  Coordinates are with respect to
  2167.                  * the upper-left hand corner of the text
  2168.                  * layout itself. */
  2169.     int width, height;        /* The width and height of the above
  2170.                  * rectangular area, in pixels. */
  2171. {
  2172.     int result, i, x1, y1, x2, y2;
  2173.     TextLayout *layoutPtr;
  2174.     LayoutChunk *chunkPtr;
  2175.     TkFont *fontPtr;
  2176.     int left, top, right, bottom;
  2177.  
  2178.     /*
  2179.      * Scan the chunks one at a time, seeing whether each is entirely in,
  2180.      * entirely out, or overlapping the rectangle.  If an overlap is
  2181.      * detected, return immediately; otherwise wait until all chunks have
  2182.      * been processed and see if they were all inside or all outside.
  2183.      */
  2184.     
  2185.     layoutPtr = (TextLayout *) layout;
  2186.     chunkPtr = layoutPtr->chunks;
  2187.     fontPtr = (TkFont *) layoutPtr->tkfont;
  2188.  
  2189.     left    = x;
  2190.     top        = y;
  2191.     right   = x + width;
  2192.     bottom  = y + height;
  2193.  
  2194.     result = 0;
  2195.     for (i = 0; i < layoutPtr->numChunks; i++) {
  2196.     if (chunkPtr->start[0] == '\n') {
  2197.         /*
  2198.          * Newline characters are not counted when computing area
  2199.          * intersection (but tab characters would still be considered).
  2200.          */
  2201.  
  2202.         chunkPtr++;
  2203.         continue;
  2204.     }
  2205.  
  2206.     x1 = chunkPtr->x;
  2207.     y1 = chunkPtr->y - fontPtr->fm.ascent;
  2208.     x2 = chunkPtr->x + chunkPtr->displayWidth;
  2209.     y2 = chunkPtr->y + fontPtr->fm.descent;
  2210.  
  2211.     if ((right < x1) || (left >= x2)
  2212.         || (bottom < y1) || (top >= y2)) {
  2213.         if (result == 1) {
  2214.         return 0;
  2215.         }
  2216.         result = -1;
  2217.     } else if ((x1 < left) || (x2 >= right)
  2218.         || (y1 < top) || (y2 >= bottom)) {
  2219.         return 0;
  2220.     } else if (result == -1) {
  2221.         return 0;
  2222.     } else {
  2223.         result = 1;
  2224.     }
  2225.     chunkPtr++;
  2226.     }
  2227.     return result;
  2228. }
  2229.  
  2230. /*
  2231.  *---------------------------------------------------------------------------
  2232.  *
  2233.  * Tk_TextLayoutToPostscript --
  2234.  *
  2235.  *    Outputs the contents of a text layout in Postscript format.
  2236.  *    The set of lines in the text layout will be rendered by the user
  2237.  *    supplied Postscript function.  The function should be of the form:
  2238.  *
  2239.  *        justify x y string  function  --
  2240.  *
  2241.  *    Justify is -1, 0, or 1, depending on whether the following string
  2242.  *    should be left, center, or right justified, x and y is the
  2243.  *    location for the origin of the string, string is the sequence
  2244.  *    of characters to be printed, and function is the name of the
  2245.  *    caller-provided function; the function should leave nothing
  2246.  *    on the stack.
  2247.  *
  2248.  *    The meaning of the origin of the string (x and y) depends on
  2249.  *    the justification.  For left justification, x is where the
  2250.  *    left edge of the string should appear.  For center justification,
  2251.  *    x is where the center of the string should appear.  And for right
  2252.  *    justification, x is where the right edge of the string should
  2253.  *    appear.  This behavior is necessary because, for example, right
  2254.  *    justified text on the screen is justified with screen metrics.
  2255.  *    The same string needs to be justified with printer metrics on
  2256.  *    the printer to appear in the correct place with respect to other
  2257.  *    similarly justified strings.  In all circumstances, y is the
  2258.  *    location of the baseline for the string.
  2259.  *
  2260.  * Results:
  2261.  *    Interp->result is modified to hold the Postscript code that
  2262.  *    will render the text layout.
  2263.  *
  2264.  * Side effects:
  2265.  *    None.
  2266.  *
  2267.  *---------------------------------------------------------------------------
  2268.  */
  2269.  
  2270. void
  2271. Tk_TextLayoutToPostscript(interp, layout)
  2272.     Tcl_Interp *interp;        /* Filled with Postscript code. */
  2273.     Tk_TextLayout layout;    /* The layout to be rendered. */
  2274. {
  2275. #define MAXUSE 128
  2276.     char buf[MAXUSE+10];
  2277.     LayoutChunk *chunkPtr;
  2278.     int i, j, used, c, baseline;
  2279.     TextLayout *layoutPtr;
  2280.  
  2281.     layoutPtr = (TextLayout *) layout;
  2282.     chunkPtr = layoutPtr->chunks;
  2283.     baseline = chunkPtr->y;
  2284.     used = 0;
  2285.     buf[used++] = '(';
  2286.     for (i = 0; i < layoutPtr->numChunks; i++) {
  2287.     if (baseline != chunkPtr->y) {
  2288.         buf[used++] = ')';
  2289.         buf[used++] = '\n';
  2290.         buf[used++] = '(';
  2291.         baseline = chunkPtr->y;
  2292.     }
  2293.     if (chunkPtr->numDisplayChars <= 0) {
  2294.         if (chunkPtr->start[0] == '\t') {
  2295.         buf[used++] = '\\';
  2296.         buf[used++] = 't';
  2297.         }
  2298.     } else {
  2299.         for (j = 0; j < chunkPtr->numDisplayChars; j++) {
  2300.         c = UCHAR(chunkPtr->start[j]);
  2301.         if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
  2302.             || (c >= UCHAR(0x7f))) {
  2303.             /*
  2304.              * Tricky point:  the "03" is necessary in the sprintf
  2305.              * below, so that a full three digits of octal are
  2306.              * always generated.  Without the "03", a number
  2307.              * following this sequence could be interpreted by
  2308.              * Postscript as part of this sequence.
  2309.              */
  2310.  
  2311.             sprintf(buf + used, "\\%03o", c);
  2312.             used += 4;
  2313.         } else {
  2314.             buf[used++] = c;
  2315.         }
  2316.         if (used >= MAXUSE) {
  2317.             buf[used] = '\0';
  2318.             Tcl_AppendResult(interp, buf, (char *) NULL);
  2319.             used = 0;
  2320.         }
  2321.         }
  2322.     }
  2323.     if (used >= MAXUSE) {
  2324.         /*
  2325.          * If there are a whole bunch of returns or tabs in a row,
  2326.          * then buf[] could get filled up.
  2327.          */
  2328.          
  2329.         buf[used] = '\0';
  2330.         Tcl_AppendResult(interp, buf, (char *) NULL);
  2331.         used = 0;
  2332.     }
  2333.     chunkPtr++;
  2334.     }
  2335.     buf[used++] = ')';
  2336.     buf[used++] = '\n';
  2337.     buf[used] = '\0';
  2338.     Tcl_AppendResult(interp, buf, (char *) NULL);
  2339. }
  2340.  
  2341. /*
  2342.  *---------------------------------------------------------------------------
  2343.  *
  2344.  * TkInitFontAttributes --
  2345.  *
  2346.  *    Initialize the font attributes structure to contain sensible
  2347.  *    values.  This must be called before using any other font
  2348.  *    attributes functions.
  2349.  *
  2350.  * Results:
  2351.  *    None.
  2352.  *
  2353.  * Side effects.
  2354.  *    None.
  2355.  *
  2356.  *---------------------------------------------------------------------------
  2357.  */
  2358.  
  2359. void
  2360. TkInitFontAttributes(faPtr)
  2361.     TkFontAttributes *faPtr;    /* The attributes structure to initialize. */
  2362. {
  2363.     faPtr->family    = NULL;
  2364.     faPtr->pointsize    = 0;
  2365.     faPtr->weight    = TK_FW_NORMAL;
  2366.     faPtr->slant    = TK_FS_ROMAN;
  2367.     faPtr->underline    = 0;
  2368.     faPtr->overstrike    = 0;
  2369. }
  2370.  
  2371. /*
  2372.  *---------------------------------------------------------------------------
  2373.  *
  2374.  * ConfigAttributesObj --
  2375.  *
  2376.  *    Process command line options to fill in fields of a properly
  2377.  *    initialized font attributes structure.
  2378.  *
  2379.  * Results:
  2380.  *    A standard Tcl return value.  If TCL_ERROR is returned, an
  2381.  *    error message will be left in interp's result object.
  2382.  *
  2383.  * Side effects:
  2384.  *    The fields of the font attributes structure get filled in with
  2385.  *    information from argc/argv.  If an error occurs while parsing,
  2386.  *    the font attributes structure will contain all modifications
  2387.  *    specified in the command line options up to the point of the
  2388.  *    error.
  2389.  *
  2390.  *---------------------------------------------------------------------------
  2391.  */
  2392.  
  2393. static int
  2394. ConfigAttributesObj(interp, tkwin, objc, objv, faPtr)
  2395.     Tcl_Interp *interp;        /* Interp for error return. */
  2396.     Tk_Window tkwin;        /* For display on which font will be used. */
  2397.     int objc;            /* Number of elements in argv. */
  2398.     Tcl_Obj *CONST objv[];    /* Command line options. */
  2399.     TkFontAttributes *faPtr;    /* Font attributes structure whose fields
  2400.                  * are to be modified.  Structure must already
  2401.                  * be properly initialized. */
  2402. {
  2403.     int i, n, index;
  2404.     Tcl_Obj *value;
  2405.     char *option, *string;
  2406.     
  2407.     if (objc & 1) {
  2408.     string = Tcl_GetStringFromObj(objv[objc - 1], NULL);
  2409.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"",
  2410.         string, "\" option", (char *) NULL);
  2411.     return TCL_ERROR;
  2412.     }
  2413.  
  2414.     for (i = 0; i < objc; i += 2) {
  2415.     option = Tcl_GetStringFromObj(objv[i], NULL);
  2416.     value = objv[i + 1];
  2417.  
  2418.     if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1,
  2419.         &index) != TCL_OK) {
  2420.         return TCL_ERROR;
  2421.     }
  2422.     switch (index) {
  2423.         case FONT_FAMILY:
  2424.         string = Tcl_GetStringFromObj(value, NULL);
  2425.         faPtr->family = Tk_GetUid(string);
  2426.         break;
  2427.  
  2428.         case FONT_SIZE:
  2429.         if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) {
  2430.             return TCL_ERROR;
  2431.         }
  2432.         faPtr->pointsize = n;
  2433.         break;
  2434.  
  2435.         case FONT_WEIGHT:
  2436.         string = Tcl_GetStringFromObj(value, NULL);
  2437.         n = TkFindStateNum(interp, option, weightMap, string);
  2438.         if (n == TK_FW_UNKNOWN) {
  2439.             return TCL_ERROR;
  2440.         }
  2441.         faPtr->weight = n;
  2442.         break;
  2443.  
  2444.         case FONT_SLANT: 
  2445.         string = Tcl_GetStringFromObj(value, NULL);
  2446.         n = TkFindStateNum(interp, option, slantMap, string);
  2447.         if (n == TK_FS_UNKNOWN) {
  2448.             return TCL_ERROR;
  2449.         }
  2450.         faPtr->slant = n;
  2451.         break;
  2452.  
  2453.         case FONT_UNDERLINE:
  2454.         if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
  2455.             return TCL_ERROR;
  2456.         }
  2457.         faPtr->underline = n;
  2458.         break;
  2459.  
  2460.         case FONT_OVERSTRIKE:
  2461.         if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
  2462.             return TCL_ERROR;
  2463.         }
  2464.         faPtr->overstrike = n;
  2465.         break;
  2466.     }
  2467.     }
  2468.     return TCL_OK;
  2469. }
  2470.  
  2471. /*
  2472.  *---------------------------------------------------------------------------
  2473.  *
  2474.  * GetAttributeInfoObj --
  2475.  *
  2476.  *    Return information about the font attributes as a Tcl list.
  2477.  *
  2478.  * Results:
  2479.  *    Interp's result object is modified to hold a description of either
  2480.  *    the current value of a single option, or a list of all options
  2481.  *    and their current values for the given font attributes.
  2482.  *
  2483.  * Side effects:
  2484.  *    None.
  2485.  *
  2486.  *---------------------------------------------------------------------------
  2487.  */
  2488.  
  2489. static void
  2490. GetAttributeInfoObj(interp, faPtr, objPtr)
  2491.     Tcl_Interp *interp;              /* Interp to hold result. */
  2492.     CONST TkFontAttributes *faPtr;    /* The font attributes to inspect. */
  2493.     Tcl_Obj *objPtr;              /* If non-NULL, indicates the single
  2494.                      * option whose value is to be
  2495.                      * returned. Otherwise
  2496.                      * information is returned for
  2497.                      * all options. */
  2498. {
  2499.     int i, index, start, end, num;
  2500.     char *str;
  2501.     Tcl_Obj *newPtr;
  2502.  
  2503.     start = 0;
  2504.     end = FONT_NUMFIELDS;
  2505.     if (objPtr != NULL) {
  2506.     if (Tcl_GetIndexFromObj(NULL, objPtr, fontOpt, "option", 1,
  2507.         &index) != TCL_OK) {
  2508.         return;
  2509.     }
  2510.     start = index;
  2511.     end = index + 1;
  2512.     }
  2513.  
  2514.     for (i = start; i < end; i++) {
  2515.     str = NULL;
  2516.     num = 0;            /* Needed only to prevent compiler
  2517.                      * warning. */
  2518.     switch (i) {
  2519.         case FONT_FAMILY:
  2520.         str = faPtr->family;
  2521.         if (str == NULL) {
  2522.             str = "";
  2523.         }
  2524.         break;
  2525.  
  2526.         case FONT_SIZE:
  2527.         num = faPtr->pointsize;
  2528.         break;
  2529.  
  2530.         case FONT_WEIGHT:
  2531.         str = TkFindStateString(weightMap, faPtr->weight);
  2532.         break;
  2533.     
  2534.         case FONT_SLANT:
  2535.         str = TkFindStateString(slantMap, faPtr->slant);
  2536.         break;
  2537.  
  2538.         case FONT_UNDERLINE:
  2539.         num = faPtr->underline;
  2540.         break;
  2541.  
  2542.         case FONT_OVERSTRIKE:
  2543.         num = faPtr->overstrike;
  2544.         break;
  2545.     }
  2546.     if (objPtr == NULL) {
  2547.         Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
  2548.             Tcl_NewStringObj(fontOpt[i], -1));
  2549.         if (str != NULL) {
  2550.         newPtr = Tcl_NewStringObj(str, -1);
  2551.         } else {
  2552.         newPtr = Tcl_NewIntObj(num);
  2553.         }
  2554.         Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
  2555.             newPtr);
  2556.     } else {
  2557.         if (str != NULL) {
  2558.         Tcl_SetStringObj(Tcl_GetObjResult(interp), str, -1);
  2559.         } else {
  2560.         Tcl_SetIntObj(Tcl_GetObjResult(interp), num);
  2561.         }
  2562.     }
  2563.     }
  2564. }
  2565.  
  2566. /*
  2567.  *---------------------------------------------------------------------------
  2568.  *
  2569.  * ParseFontNameObj --
  2570.  *
  2571.  *    Converts a object into a set of font attributes that can be used
  2572.  *    to construct a font.
  2573.  *
  2574.  *    The string rep of the object can be one of the following forms:
  2575.  *        XLFD (see X documentation)
  2576.  *        "Family [size [style] [style ...]]"
  2577.  *        "-option value [-option value ...]"
  2578.  *
  2579.  * Results:
  2580.  *    The return value is TCL_ERROR if the object was syntactically
  2581.  *    invalid.  In that case an error message is left in interp's
  2582.  *    result object.  Otherwise, fills the font attribute buffer with
  2583.  *    the values parsed from the string and returns TCL_OK;
  2584.  *
  2585.  * Side effects:
  2586.  *    None.
  2587.  *
  2588.  *---------------------------------------------------------------------------
  2589.  */
  2590.  
  2591. static int
  2592. ParseFontNameObj(interp, tkwin, objPtr, faPtr)
  2593.     Tcl_Interp *interp;        /* Interp for error return. */
  2594.     Tk_Window tkwin;        /* For display on which font is used. */
  2595.     Tcl_Obj *objPtr;        /* Parseable font description object. */
  2596.     TkFontAttributes *faPtr;    /* Font attributes structure whose fields
  2597.                  * are to be modified.  Structure must already
  2598.                  * be properly initialized. */
  2599. {
  2600.     char *dash;
  2601.     int objc, result, i, n;
  2602.     Tcl_Obj **objv;
  2603.     TkXLFDAttributes xa;
  2604.     char *string;
  2605.     
  2606.     string = Tcl_GetStringFromObj(objPtr, NULL);
  2607.     if (*string == '-') {
  2608.     /*
  2609.      * This may be an XLFD or an "-option value" string.
  2610.      *
  2611.      * If the string begins with "-*" or a "-foundry-family-*" pattern,
  2612.      * then consider it an XLFD.  
  2613.      */
  2614.  
  2615.     if (string[1] == '*') {
  2616.         goto xlfd;
  2617.     }
  2618.     dash = strchr(string + 1, '-');
  2619.     if ((dash != NULL) && (!isspace(UCHAR(dash[-1])))) {
  2620.         goto xlfd;
  2621.     }
  2622.  
  2623.     if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
  2624.         return TCL_ERROR;
  2625.     }
  2626.  
  2627.     return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
  2628.     }
  2629.     
  2630.     if (*string == '*') {
  2631.     /*
  2632.      * This appears to be an XLFD.
  2633.      */
  2634.  
  2635.     xlfd:
  2636.     xa.fa = *faPtr;
  2637.     result = TkParseXLFD(string, &xa);
  2638.     if (result == TCL_OK) {
  2639.         *faPtr = xa.fa;
  2640.         return result;
  2641.     }
  2642.     }
  2643.  
  2644.     /*
  2645.      * Wasn't an XLFD or "-option value" string.  Try it as a
  2646.      * "font size style" list.
  2647.      */
  2648.  
  2649.     if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
  2650.     return TCL_ERROR;
  2651.     }
  2652.     if (objc < 1) {
  2653.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "font \"", string,
  2654.         "\" doesn't exist", (char *) NULL);
  2655.     return TCL_ERROR;
  2656.     }
  2657.  
  2658.     faPtr->family = Tk_GetUid(Tcl_GetStringFromObj(objv[0], NULL));
  2659.     if (objc > 1) {
  2660.     if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
  2661.         return TCL_ERROR;
  2662.     }
  2663.     faPtr->pointsize = n;
  2664.     }
  2665.  
  2666.     i = 2;
  2667.     if (objc == 3) {
  2668.     if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) {
  2669.         return TCL_ERROR;
  2670.     }
  2671.     i = 0;
  2672.     }
  2673.     for ( ; i < objc; i++) {
  2674.     string = Tcl_GetStringFromObj(objv[i], NULL);
  2675.     n = TkFindStateNum(NULL, NULL, weightMap, string);
  2676.     if (n != TK_FW_UNKNOWN) {
  2677.         faPtr->weight = n;
  2678.         continue;
  2679.     }
  2680.     n = TkFindStateNum(NULL, NULL, slantMap, string);
  2681.     if (n != TK_FS_UNKNOWN) {
  2682.         faPtr->slant = n;
  2683.         continue;
  2684.     }
  2685.     n = TkFindStateNum(NULL, NULL, underlineMap, string);
  2686.     if (n != 0) {
  2687.         faPtr->underline = n;
  2688.         continue;
  2689.     }
  2690.     n = TkFindStateNum(NULL, NULL, overstrikeMap, string);
  2691.     if (n != 0) {
  2692.         faPtr->overstrike = n;
  2693.         continue;
  2694.     }
  2695.  
  2696.     /*
  2697.      * Unknown style.
  2698.      */
  2699.  
  2700.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  2701.         "unknown font style \"", string, "\"",
  2702.         (char *) NULL);
  2703.     return TCL_ERROR;
  2704.     }
  2705.     return TCL_OK;
  2706. }
  2707.  
  2708. /*
  2709.  *---------------------------------------------------------------------------
  2710.  *
  2711.  * TkParseXLFD --
  2712.  *
  2713.  *    Break up a fully specified XLFD into a set of font attributes.
  2714.  *
  2715.  * Results:
  2716.  *    Return value is TCL_ERROR if string was not a fully specified XLFD.
  2717.  *    Otherwise, fills font attribute buffer with the values parsed
  2718.  *    from the XLFD and returns TCL_OK.  
  2719.  *
  2720.  * Side effects:
  2721.  *    None.
  2722.  *
  2723.  *---------------------------------------------------------------------------
  2724.  */
  2725.  
  2726. int
  2727. TkParseXLFD(string, xaPtr)
  2728.     CONST char *string;        /* Parseable font description string. */
  2729.     TkXLFDAttributes *xaPtr;    /* XLFD attributes structure whose fields
  2730.                  * are to be modified.  Structure must already
  2731.                  * be properly initialized. */
  2732. {
  2733.     char *src;
  2734.     CONST char *str;
  2735.     int i, j;
  2736.     char *field[XLFD_NUMFIELDS + 2];
  2737.     Tcl_DString ds;
  2738.     
  2739.     memset(field, '\0', sizeof(field));
  2740.  
  2741.     str = string;
  2742.     if (*str == '-') {
  2743.     str++;
  2744.     }
  2745.  
  2746.     Tcl_DStringInit(&ds);
  2747.     Tcl_DStringAppend(&ds, (char *) str, -1);
  2748.     src = Tcl_DStringValue(&ds);
  2749.  
  2750.     field[0] = src;
  2751.     for (i = 0; *src != '\0'; src++) {
  2752.     if (isupper(UCHAR(*src))) {
  2753.         *src = tolower(UCHAR(*src));
  2754.     }
  2755.     if (*src == '-') {
  2756.         i++;
  2757.         if (i > XLFD_NUMFIELDS) {
  2758.         break;
  2759.         }
  2760.         *src = '\0';
  2761.         field[i] = src + 1;
  2762.     }
  2763.     }
  2764.  
  2765.     /*
  2766.      * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common, 
  2767.      * but it is (strictly) malformed, because the first * is eliding both
  2768.      * the Setwidth and the Addstyle fields.  If the Addstyle field is a
  2769.      * number, then assume the above incorrect form was used and shift all
  2770.      * the rest of the fields up by one, so the number gets interpreted
  2771.      * as a pixelsize.  This fix is so that we don't get a million reports
  2772.      * that "it works under X, but gives a syntax error under Windows".
  2773.      */
  2774.  
  2775.     if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) {
  2776.     if (atoi(field[XLFD_ADD_STYLE]) != 0) {
  2777.         for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) {
  2778.         field[j + 1] = field[j];
  2779.         }
  2780.         field[XLFD_ADD_STYLE] = NULL;
  2781.         i++;
  2782.     }
  2783.     }
  2784.  
  2785.     /*
  2786.      * Bail if we don't have enough of the fields (up to pointsize).
  2787.      */
  2788.  
  2789.     if (i < XLFD_FAMILY) {
  2790.     Tcl_DStringFree(&ds);
  2791.     return TCL_ERROR;
  2792.     }
  2793.  
  2794.     if (FieldSpecified(field[XLFD_FOUNDRY])) {
  2795.     xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]);
  2796.     }
  2797.  
  2798.     if (FieldSpecified(field[XLFD_FAMILY])) {
  2799.     xaPtr->fa.family = Tk_GetUid(field[XLFD_FAMILY]);
  2800.     }
  2801.     if (FieldSpecified(field[XLFD_WEIGHT])) {
  2802.     xaPtr->fa.weight = TkFindStateNum(NULL, NULL, xlfdWeightMap,
  2803.         field[XLFD_WEIGHT]);
  2804.     }
  2805.     if (FieldSpecified(field[XLFD_SLANT])) {
  2806.     xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap,
  2807.         field[XLFD_SLANT]);
  2808.     if (xaPtr->slant == TK_FS_ROMAN) {
  2809.         xaPtr->fa.slant = TK_FS_ROMAN;
  2810.     } else {
  2811.         xaPtr->fa.slant = TK_FS_ITALIC;
  2812.     }
  2813.     }
  2814.     if (FieldSpecified(field[XLFD_SETWIDTH])) {
  2815.     xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap,
  2816.         field[XLFD_SETWIDTH]);
  2817.     }
  2818.  
  2819.     /* XLFD_ADD_STYLE ignored. */
  2820.  
  2821.     /*
  2822.      * Pointsize in tenths of a point, but treat it as tenths of a pixel.
  2823.      */
  2824.  
  2825.     if (FieldSpecified(field[XLFD_POINT_SIZE])) {
  2826.     if (field[XLFD_POINT_SIZE][0] == '[') {
  2827.         /*
  2828.          * Some X fonts have the point size specified as follows:
  2829.          *
  2830.          *        [ N1 N2 N3 N4 ]
  2831.          *
  2832.          * where N1 is the point size (in points, not decipoints!), and
  2833.          * N2, N3, and N4 are some additional numbers that I don't know
  2834.          * the purpose of, so I ignore them.
  2835.          */
  2836.  
  2837.         xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1);
  2838.     } else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
  2839.         &xaPtr->fa.pointsize) == TCL_OK) {
  2840.         xaPtr->fa.pointsize /= 10;
  2841.     } else {
  2842.         return TCL_ERROR;
  2843.     }
  2844.     }
  2845.  
  2846.     /*
  2847.      * Pixel height of font.  If specified, overrides pointsize.
  2848.      */
  2849.  
  2850.     if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
  2851.     if (field[XLFD_PIXEL_SIZE][0] == '[') {
  2852.         /*
  2853.          * Some X fonts have the pixel size specified as follows:
  2854.          *
  2855.          *        [ N1 N2 N3 N4 ]
  2856.          *
  2857.          * where N1 is the pixel size, and where N2, N3, and N4 
  2858.          * are some additional numbers that I don't know
  2859.          * the purpose of, so I ignore them.
  2860.          */
  2861.  
  2862.         xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1);
  2863.     } else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
  2864.         &xaPtr->fa.pointsize) != TCL_OK) {
  2865.         return TCL_ERROR;
  2866.     }
  2867.     }
  2868.  
  2869.     xaPtr->fa.pointsize = -xaPtr->fa.pointsize;
  2870.  
  2871.     /* XLFD_RESOLUTION_X ignored. */
  2872.  
  2873.     /* XLFD_RESOLUTION_Y ignored. */
  2874.  
  2875.     /* XLFD_SPACING ignored. */
  2876.  
  2877.     /* XLFD_AVERAGE_WIDTH ignored. */
  2878.  
  2879.     if (FieldSpecified(field[XLFD_REGISTRY])) {
  2880.     xaPtr->charset = TkFindStateNum(NULL, NULL, xlfdCharsetMap,
  2881.         field[XLFD_REGISTRY]);
  2882.     }
  2883.     if (FieldSpecified(field[XLFD_ENCODING])) {
  2884.     xaPtr->encoding = atoi(field[XLFD_ENCODING]);
  2885.     }
  2886.  
  2887.     Tcl_DStringFree(&ds);
  2888.     return TCL_OK;
  2889. }
  2890.  
  2891. /*
  2892.  *---------------------------------------------------------------------------
  2893.  *
  2894.  * FieldSpecified --
  2895.  *
  2896.  *    Helper function for TkParseXLFD().  Determines if a field in the
  2897.  *    XLFD was set to a non-null, non-don't-care value.
  2898.  *
  2899.  * Results:
  2900.  *    The return value is 0 if the field in the XLFD was not set and
  2901.  *    should be ignored, non-zero otherwise.
  2902.  *
  2903.  * Side effects:
  2904.  *    None.
  2905.  *
  2906.  *---------------------------------------------------------------------------
  2907.  */
  2908.  
  2909. static int
  2910. FieldSpecified(field)
  2911.     CONST char *field;    /* The field of the XLFD to check.  Strictly
  2912.              * speaking, only when the string is "*" does it mean
  2913.              * don't-care.  However, an unspecified or question
  2914.              * mark is also interpreted as don't-care. */
  2915. {
  2916.     char ch;
  2917.  
  2918.     if (field == NULL) {
  2919.     return 0;
  2920.     }
  2921.     ch = field[0];
  2922.     return (ch != '*' && ch != '?');
  2923. }
  2924.  
  2925. /*
  2926.  *---------------------------------------------------------------------------
  2927.  *
  2928.  * NewChunk --
  2929.  *
  2930.  *    Helper function for Tk_ComputeTextLayout().  Encapsulates a
  2931.  *    measured set of characters in a chunk that can be quickly
  2932.  *    drawn.
  2933.  *
  2934.  * Results:
  2935.  *    A pointer to the new chunk in the text layout.
  2936.  *
  2937.  * Side effects:
  2938.  *    The text layout is reallocated to hold more chunks as necessary.
  2939.  *
  2940.  *    Currently, Tk_ComputeTextLayout() stores contiguous ranges of
  2941.  *    "normal" characters in a chunk, along with individual tab
  2942.  *    and newline chars in their own chunks.  All characters in the
  2943.  *    text layout are accounted for.
  2944.  *
  2945.  *---------------------------------------------------------------------------
  2946.  */
  2947. static LayoutChunk *
  2948. NewChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y)
  2949.     TextLayout **layoutPtrPtr;
  2950.     int *maxPtr;
  2951.     CONST char *start;
  2952.     int numChars;
  2953.     int curX;
  2954.     int newX;
  2955.     int y;
  2956. {
  2957.     TextLayout *layoutPtr;
  2958.     LayoutChunk *chunkPtr;
  2959.     int maxChunks;
  2960.     size_t s;
  2961.     
  2962.     layoutPtr = *layoutPtrPtr;
  2963.     maxChunks = *maxPtr;
  2964.     if (layoutPtr->numChunks == maxChunks) {
  2965.     maxChunks *= 2;
  2966.     s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk));
  2967.     layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s);
  2968.  
  2969.     *layoutPtrPtr = layoutPtr;
  2970.     *maxPtr = maxChunks;
  2971.     }
  2972.     chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
  2973.     chunkPtr->start        = start;
  2974.     chunkPtr->numChars        = numChars;
  2975.     chunkPtr->numDisplayChars    = numChars;
  2976.     chunkPtr->x            = curX;
  2977.     chunkPtr->y            = y;
  2978.     chunkPtr->totalWidth    = newX - curX;
  2979.     chunkPtr->displayWidth    = newX - curX;
  2980.     layoutPtr->numChunks++;
  2981.  
  2982.     return chunkPtr;
  2983. }
  2984.  
  2985.