diff options
Diffstat (limited to 'generic/tkFont.c')
-rw-r--r-- | generic/tkFont.c | 3008 |
1 files changed, 3008 insertions, 0 deletions
diff --git a/generic/tkFont.c b/generic/tkFont.c new file mode 100644 index 0000000..11929b6 --- /dev/null +++ b/generic/tkFont.c @@ -0,0 +1,3008 @@ +/* + * tkFont.c -- + * + * This file maintains a database of fonts for the Tk toolkit. + * It also provides several utility procedures for measuring and + * displaying text. + * + * Copyright (c) 1990-1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tkFont.c 1.74 97/10/10 14:34:11 + */ + +#include "tkInt.h" +#include "tkFont.h" + +/* + * The following structure is used to keep track of all the fonts that + * exist in the current application. It must be stored in the + * TkMainInfo for the application. + */ + +typedef struct TkFontInfo { + Tcl_HashTable fontCache; /* Map a string to an existing Tk_Font. + * Keys are CachedFontKey structs, values are + * TkFont structs. */ + Tcl_HashTable namedTable; /* Map a name to a set of attributes for a + * font, used when constructing a Tk_Font from + * a named font description. Keys are + * Tk_Uids, values are NamedFont structs. */ + TkMainInfo *mainPtr; /* Application that owns this structure. */ + int updatePending; +} TkFontInfo; + +/* + * The following structure is used as a key in the fontCache. + */ + +typedef struct CachedFontKey { + Display *display; /* Display for which font was constructed. */ + Tk_Uid string; /* String that describes font. */ +} CachedFontKey; + +/* + * The following data structure is used to keep track of the font attributes + * for each named font that has been defined. The named font is only deleted + * when the last reference to it goes away. + */ + +typedef struct NamedFont { + int refCount; /* Number of users of named font. */ + int deletePending; /* Non-zero if font should be deleted when + * last reference goes away. */ + TkFontAttributes fa; /* Desired attributes for named font. */ +} NamedFont; + +/* + * The following two structures are used to keep track of string + * measurement information when using the text layout facilities. + * + * A LayoutChunk represents a contiguous range of text that can be measured + * and displayed by low-level text calls. In general, chunks will be + * delimited by newlines and tabs. Low-level, platform-specific things + * like kerning and non-integer character widths may occur between the + * characters in a single chunk, but not between characters in different + * chunks. + * + * A TextLayout is a collection of LayoutChunks. It can be displayed with + * respect to any origin. It is the implementation of the Tk_TextLayout + * opaque token. + */ + +typedef struct LayoutChunk { + CONST char *start; /* Pointer to simple string to be displayed. + * This is a pointer into the TkTextLayout's + * string. */ + int numChars; /* The number of characters in this chunk. */ + int numDisplayChars; /* The number of characters to display when + * this chunk is displayed. Can be less than + * numChars if extra space characters were + * absorbed by the end of the chunk. This + * will be < 0 if this is a chunk that is + * holding a tab or newline. */ + int x, y; /* The origin of the first character in this + * chunk with respect to the upper-left hand + * corner of the TextLayout. */ + int totalWidth; /* Width in pixels of this chunk. Used + * when hit testing the invisible spaces at + * the end of a chunk. */ + int displayWidth; /* Width in pixels of the displayable + * characters in this chunk. Can be less than + * width if extra space characters were + * absorbed by the end of the chunk. */ +} LayoutChunk; + +typedef struct TextLayout { + Tk_Font tkfont; /* The font used when laying out the text. */ + CONST char *string; /* The string that was layed out. */ + int width; /* The maximum width of all lines in the + * text layout. */ + int numChunks; /* Number of chunks actually used in + * following array. */ + LayoutChunk chunks[1]; /* Array of chunks. The actual size will + * be maxChunks. THIS FIELD MUST BE THE LAST + * IN THE STRUCTURE. */ +} TextLayout; + +/* + * The following structures are used as two-way maps between the values for + * the fields in the TkFontAttributes structure and the strings used in + * Tcl, when parsing both option-value format and style-list format font + * name strings. + */ + +static TkStateMap weightMap[] = { + {TK_FW_NORMAL, "normal"}, + {TK_FW_BOLD, "bold"}, + {TK_FW_UNKNOWN, NULL} +}; + +static TkStateMap slantMap[] = { + {TK_FS_ROMAN, "roman"}, + {TK_FS_ITALIC, "italic"}, + {TK_FS_UNKNOWN, NULL} +}; + +static TkStateMap underlineMap[] = { + {1, "underline"}, + {0, NULL} +}; + +static TkStateMap overstrikeMap[] = { + {1, "overstrike"}, + {0, NULL} +}; + +/* + * The following structures are used when parsing XLFD's into a set of + * TkFontAttributes. + */ + +static TkStateMap xlfdWeightMap[] = { + {TK_FW_NORMAL, "normal"}, + {TK_FW_NORMAL, "medium"}, + {TK_FW_NORMAL, "book"}, + {TK_FW_NORMAL, "light"}, + {TK_FW_BOLD, "bold"}, + {TK_FW_BOLD, "demi"}, + {TK_FW_BOLD, "demibold"}, + {TK_FW_NORMAL, NULL} /* Assume anything else is "normal". */ +}; + +static TkStateMap xlfdSlantMap[] = { + {TK_FS_ROMAN, "r"}, + {TK_FS_ITALIC, "i"}, + {TK_FS_OBLIQUE, "o"}, + {TK_FS_ROMAN, NULL} /* Assume anything else is "roman". */ +}; + +static TkStateMap xlfdSetwidthMap[] = { + {TK_SW_NORMAL, "normal"}, + {TK_SW_CONDENSE, "narrow"}, + {TK_SW_CONDENSE, "semicondensed"}, + {TK_SW_CONDENSE, "condensed"}, + {TK_SW_UNKNOWN, NULL} +}; + +static TkStateMap xlfdCharsetMap[] = { + {TK_CS_NORMAL, "iso8859"}, + {TK_CS_SYMBOL, "adobe"}, + {TK_CS_SYMBOL, "sun"}, + {TK_CS_OTHER, NULL} +}; + +/* + * The following structure and defines specify the valid builtin options + * when configuring a set of font attributes. + */ + +static char *fontOpt[] = { + "-family", + "-size", + "-weight", + "-slant", + "-underline", + "-overstrike", + NULL +}; + +#define FONT_FAMILY 0 +#define FONT_SIZE 1 +#define FONT_WEIGHT 2 +#define FONT_SLANT 3 +#define FONT_UNDERLINE 4 +#define FONT_OVERSTRIKE 5 +#define FONT_NUMFIELDS 6 /* Length of fontOpt array. */ + +#define GetFontAttributes(tkfont) \ + ((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa) + +#define GetFontMetrics(tkfont) \ + ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm) + + +static int ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp, + Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[], + TkFontAttributes *faPtr)); +static int FieldSpecified _ANSI_ARGS_((CONST char *field)); +static int GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp, + CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr)); +static LayoutChunk * NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr, + int *maxPtr, CONST char *start, int numChars, + int curX, int newX, int y)); +static int ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp, + Tk_Window tkwin, Tcl_Obj *objPtr, + TkFontAttributes *faPtr)); +static void RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr)); +static void TheWorldHasChanged _ANSI_ARGS_(( + ClientData clientData)); +static void UpdateDependantFonts _ANSI_ARGS_((TkFontInfo *fiPtr, + Tk_Window tkwin, Tcl_HashEntry *namedHashPtr)); + + + + +/* + *--------------------------------------------------------------------------- + * + * TkFontPkgInit -- + * + * This procedure is called when an application is created. It + * initializes all the structures that are used by the font + * package on a per application basis. + * + * Results: + * Returns a token that must be stored in the TkMainInfo for this + * application. + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ +void +TkFontPkgInit(mainPtr) + TkMainInfo *mainPtr; /* The application being created. */ +{ + TkFontInfo *fiPtr; + + fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo)); + Tcl_InitHashTable(&fiPtr->fontCache, sizeof(CachedFontKey) / sizeof(int)); + Tcl_InitHashTable(&fiPtr->namedTable, TCL_ONE_WORD_KEYS); + fiPtr->mainPtr = mainPtr; + fiPtr->updatePending = 0; + mainPtr->fontInfoPtr = fiPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * TkFontPkgFree -- + * + * This procedure is called when an application is deleted. It + * deletes all the structures that were used by the font package + * for this application. + * + * Results: + * None. + * + * Side effects: + * Memory freed. + * + *--------------------------------------------------------------------------- + */ + +void +TkFontPkgFree(mainPtr) + TkMainInfo *mainPtr; /* The application being deleted. */ +{ + TkFontInfo *fiPtr; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + fiPtr = mainPtr->fontInfoPtr; + + if (fiPtr->fontCache.numEntries != 0) { + panic("TkFontPkgFree: all fonts should have been freed already"); + } + Tcl_DeleteHashTable(&fiPtr->fontCache); + + hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search); + while (hPtr != NULL) { + ckfree((char *) Tcl_GetHashValue(hPtr)); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(&fiPtr->namedTable); + if (fiPtr->updatePending != 0) { + Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr); + } + ckfree((char *) fiPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_FontObjCmd -- + * + * This procedure is implemented to process the "font" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tk_FontObjCmd(clientData, interp, objc, objv) + ClientData clientData; /* Main window associated with interpreter. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + int index; + Tk_Window tkwin; + TkFontInfo *fiPtr; + static char *optionStrings[] = { + "actual", "configure", "create", "delete", + "families", "measure", "metrics", "names", + NULL + }; + enum options { + FONT_ACTUAL, FONT_CONFIGURE, FONT_CREATE, FONT_DELETE, + FONT_FAMILIES, FONT_MEASURE, FONT_METRICS, FONT_NAMES + }; + + tkwin = (Tk_Window) clientData; + fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch ((enum options) index) { + case FONT_ACTUAL: { + int skip, result; + Tk_Font tkfont; + Tcl_Obj *objPtr; + CONST TkFontAttributes *faPtr; + + skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin); + if (skip < 0) { + return TCL_ERROR; + } + if ((objc < 3) || (objc - skip > 4)) { + Tcl_WrongNumArgs(interp, 2, objv, + "font ?-displayof window? ?option?"); + return TCL_ERROR; + } + tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); + if (tkfont == NULL) { + return TCL_ERROR; + } + objc -= skip; + objv += skip; + faPtr = GetFontAttributes(tkfont); + objPtr = NULL; + if (objc > 3) { + objPtr = objv[3]; + } + result = GetAttributeInfoObj(interp, faPtr, objPtr); + Tk_FreeFont(tkfont); + return result; + } + case FONT_CONFIGURE: { + int result; + char *string; + Tcl_Obj *objPtr; + NamedFont *nfPtr; + Tcl_HashEntry *namedHashPtr; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?"); + return TCL_ERROR; + } + string = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL)); + namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); + nfPtr = NULL; /* lint. */ + if (namedHashPtr != NULL) { + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + } + if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string, + "\" doesn't exist", NULL); + return TCL_ERROR; + } + if (objc == 3) { + objPtr = NULL; + } else if (objc == 4) { + objPtr = objv[3]; + } else { + result = ConfigAttributesObj(interp, tkwin, objc - 3, + objv + 3, &nfPtr->fa); + UpdateDependantFonts(fiPtr, tkwin, namedHashPtr); + return result; + } + return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr); + } + case FONT_CREATE: { + int skip, i; + char *name; + char buf[32]; + TkFontAttributes fa; + Tcl_HashEntry *namedHashPtr; + + skip = 3; + if (objc < 3) { + name = NULL; + } else { + name = Tcl_GetStringFromObj(objv[2], NULL); + if (name[0] == '-') { + name = NULL; + } + } + if (name == NULL) { + /* + * No font name specified. Generate one of the form "fontX". + */ + + for (i = 1; ; i++) { + sprintf(buf, "font%d", i); + namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, + Tk_GetUid(buf)); + if (namedHashPtr == NULL) { + break; + } + } + name = buf; + skip = 2; + } + TkInitFontAttributes(&fa); + if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip, + &fa) != TCL_OK) { + return TCL_ERROR; + } + if (TkCreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) { + return TCL_ERROR; + } + Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1); + break; + } + case FONT_DELETE: { + int i; + char *string; + NamedFont *nfPtr; + Tcl_HashEntry *namedHashPtr; + + /* + * Delete the named font. If there are still widgets using this + * font, then it isn't deleted right away. + */ + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?"); + return TCL_ERROR; + } + for (i = 2; i < objc; i++) { + string = Tk_GetUid(Tcl_GetStringFromObj(objv[i], NULL)); + namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); + if (namedHashPtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string, + "\" doesn't exist", (char *) NULL); + return TCL_ERROR; + } + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + if (nfPtr->refCount != 0) { + nfPtr->deletePending = 1; + } else { + Tcl_DeleteHashEntry(namedHashPtr); + ckfree((char *) nfPtr); + } + } + break; + } + case FONT_FAMILIES: { + int skip; + + skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin); + if (skip < 0) { + return TCL_ERROR; + } + if (objc - skip != 2) { + Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?"); + return TCL_ERROR; + } + TkpGetFontFamilies(interp, tkwin); + break; + } + case FONT_MEASURE: { + char *string; + Tk_Font tkfont; + int length, skip; + + skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin); + if (skip < 0) { + return TCL_ERROR; + } + if (objc - skip != 4) { + Tcl_WrongNumArgs(interp, 2, objv, + "font ?-displayof window? text"); + return TCL_ERROR; + } + tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); + if (tkfont == NULL) { + return TCL_ERROR; + } + string = Tcl_GetStringFromObj(objv[3 + skip], &length); + Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_TextWidth(tkfont, string, length)); + Tk_FreeFont(tkfont); + break; + } + case FONT_METRICS: { + char buf[64]; + Tk_Font tkfont; + int skip, index, i; + CONST TkFontMetrics *fmPtr; + static char *switches[] = { + "-ascent", "-descent", "-linespace", "-fixed", NULL + }; + + skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin); + if (skip < 0) { + return TCL_ERROR; + } + if ((objc < 3) || ((objc - skip) > 4)) { + Tcl_WrongNumArgs(interp, 2, objv, + "font ?-displayof window? ?option?"); + return TCL_ERROR; + } + tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); + if (tkfont == NULL) { + return TCL_ERROR; + } + objc -= skip; + objv += skip; + fmPtr = GetFontMetrics(tkfont); + if (objc == 3) { + sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d", + fmPtr->ascent, fmPtr->descent, + fmPtr->ascent + fmPtr->descent, + fmPtr->fixed); + Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1); + } else { + if (Tcl_GetIndexFromObj(interp, objv[3], switches, + "metric", 0, &index) != TCL_OK) { + Tk_FreeFont(tkfont); + return TCL_ERROR; + } + i = 0; /* Needed only to prevent compiler + * warning. */ + switch (index) { + case 0: i = fmPtr->ascent; break; + case 1: i = fmPtr->descent; break; + case 2: i = fmPtr->ascent + fmPtr->descent; break; + case 3: i = fmPtr->fixed; break; + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), i); + } + Tk_FreeFont(tkfont); + break; + } + case FONT_NAMES: { + char *string; + Tcl_Obj *strPtr; + NamedFont *nfPtr; + Tcl_HashSearch search; + Tcl_HashEntry *namedHashPtr; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "names"); + return TCL_ERROR; + } + namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search); + while (namedHashPtr != NULL) { + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + if (nfPtr->deletePending == 0) { + string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr); + strPtr = Tcl_NewStringObj(string, -1); + Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), strPtr); + } + namedHashPtr = Tcl_NextHashEntry(&search); + } + break; + } + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * UpdateDependantFonts, TheWorldHasChanged, RecomputeWidgets -- + * + * Called when the attributes of a named font changes. Updates all + * the instantiated fonts that depend on that named font and then + * uses the brute force approach and prepares every widget to + * recompute its geometry. + * + * Results: + * None. + * + * Side effects: + * Things get queued for redisplay. + * + *--------------------------------------------------------------------------- + */ + +static void +UpdateDependantFonts(fiPtr, tkwin, namedHashPtr) + TkFontInfo *fiPtr; /* Info about application's fonts. */ + Tk_Window tkwin; /* A window in the application. */ + Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */ +{ + Tcl_HashEntry *cacheHashPtr; + Tcl_HashSearch search; + TkFont *fontPtr; + NamedFont *nfPtr; + + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + if (nfPtr->refCount == 0) { + /* + * Well nobody's using this named font, so don't have to tell + * any widgets to recompute themselves. + */ + + return; + } + + + cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search); + while (cacheHashPtr != NULL) { + fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr); + if (fontPtr->namedHashPtr == namedHashPtr) { + TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa); + if (fiPtr->updatePending == 0) { + fiPtr->updatePending = 1; + Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr); + } + } + cacheHashPtr = Tcl_NextHashEntry(&search); + } +} + +static void +TheWorldHasChanged(clientData) + ClientData clientData; /* Info about application's fonts. */ +{ + TkFontInfo *fiPtr; + + fiPtr = (TkFontInfo *) clientData; + fiPtr->updatePending = 0; + + RecomputeWidgets(fiPtr->mainPtr->winPtr); +} + +static void +RecomputeWidgets(winPtr) + TkWindow *winPtr; /* Window to which command is sent. */ +{ + if ((winPtr->classProcsPtr != NULL) + && (winPtr->classProcsPtr->geometryProc != NULL)) { + (*winPtr->classProcsPtr->geometryProc)(winPtr->instanceData); + } + for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) { + RecomputeWidgets(winPtr); + } +} + +/* + *--------------------------------------------------------------------------- + * + * TkCreateNamedFont -- + * + * Create the specified named font with the given attributes in the + * named font table associated with the interp. + * + * Results: + * Returns TCL_OK if the font was successfully created, or TCL_ERROR + * if the named font already existed. If TCL_ERROR is returned, an + * error message is left in interp->result. + * + * Side effects: + * Assume there used to exist a named font by the specified name, and + * that the named font had been deleted, but there were still some + * widgets using the named font at the time it was deleted. If a + * new named font is created with the same name, all those widgets + * that were using the old named font will be redisplayed using + * the new named font's attributes. + * + *--------------------------------------------------------------------------- + */ + +int +TkCreateNamedFont(interp, tkwin, name, faPtr) + Tcl_Interp *interp; /* Interp for error return. */ + Tk_Window tkwin; /* A window associated with interp. */ + CONST char *name; /* Name for the new named font. */ + TkFontAttributes *faPtr; /* Attributes for the new named font. */ +{ + TkFontInfo *fiPtr; + Tcl_HashEntry *namedHashPtr; + int new; + NamedFont *nfPtr; + + fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; + + name = Tk_GetUid(name); + namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new); + + if (new == 0) { + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + if (nfPtr->deletePending == 0) { + interp->result[0] = '\0'; + Tcl_AppendResult(interp, "font \"", name, + "\" already exists", (char *) NULL); + return TCL_ERROR; + } + + /* + * Recreating a named font with the same name as a previous + * named font. Some widgets were still using that named + * font, so they need to get redisplayed. + */ + + nfPtr->fa = *faPtr; + nfPtr->deletePending = 0; + UpdateDependantFonts(fiPtr, tkwin, namedHashPtr); + return TCL_OK; + } + + nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont)); + nfPtr->deletePending = 0; + Tcl_SetHashValue(namedHashPtr, nfPtr); + nfPtr->fa = *faPtr; + nfPtr->refCount = 0; + nfPtr->deletePending = 0; + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_GetFont -- + * + * Given a string description of a font, map the description to a + * corresponding Tk_Font that represents the font. + * + * Results: + * The return value is token for the font, or NULL if an error + * prevented the font from being created. If NULL is returned, an + * error message will be left in interp->result. + * + * Side effects: + * Calls Tk_GetFontFromObj(), which modifies interp's result object, + * then copies the string from the result object into interp->result. + * This procedure will go away when Tk_ConfigureWidget() is + * made into an object command. + * + *--------------------------------------------------------------------------- + */ + +Tk_Font +Tk_GetFont(interp, tkwin, string) + Tcl_Interp *interp; /* Interp for database and error return. */ + Tk_Window tkwin; /* For display on which font will be used. */ + CONST char *string; /* String describing font, as: named font, + * native format, or parseable string. */ +{ + Tcl_Obj *strPtr; + Tk_Font tkfont; + + strPtr = Tcl_NewStringObj((char *) string, -1); + + tkfont = Tk_GetFontFromObj(interp, tkwin, strPtr); + if (tkfont == NULL) { + Tcl_SetResult(interp, + Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL), + TCL_VOLATILE); + } + + Tcl_DecrRefCount(strPtr); /* done with object */ + return tkfont; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_GetFontFromObj -- + * + * Given a string description of a font, map the description to a + * corresponding Tk_Font that represents the font. + * + * Results: + * The return value is token for the font, or NULL if an error + * prevented the font from being created. If NULL is returned, an + * error message will be left in interp's result object. + * + * Side effects: + * The font is added to an internal database with a reference + * count. For each call to this procedure, there should eventually + * be a call to Tk_FreeFont() so that the database is cleaned up when + * fonts aren't in use anymore. + * + *--------------------------------------------------------------------------- + */ + +Tk_Font +Tk_GetFontFromObj(interp, tkwin, objPtr) + Tcl_Interp *interp; /* Interp for database and error return. */ + Tk_Window tkwin; /* For display on which font will be used. */ + Tcl_Obj *objPtr; /* Object describing font, as: named font, + * native format, or parseable string. */ +{ + TkFontInfo *fiPtr; + CachedFontKey key; + Tcl_HashEntry *cacheHashPtr, *namedHashPtr; + TkFont *fontPtr; + int new, descent; + NamedFont *nfPtr; + char *string; + + fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; + string = Tcl_GetStringFromObj(objPtr, NULL); + + key.display = Tk_Display(tkwin); + key.string = Tk_GetUid(string); + cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache, (char *) &key, &new); + + if (new == 0) { + /* + * We have already constructed a font with this description for + * this display. Bump the reference count of the cached font. + */ + + fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr); + fontPtr->refCount++; + return (Tk_Font) fontPtr; + } + + namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, key.string); + if (namedHashPtr != NULL) { + /* + * Construct a font based on a named font. + */ + + nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); + nfPtr->refCount++; + + fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa); + } else { + /* + * Native font? + */ + + fontPtr = TkpGetNativeFont(tkwin, string); + if (fontPtr == NULL) { + TkFontAttributes fa; + + TkInitFontAttributes(&fa); + if (ParseFontNameObj(interp, tkwin, objPtr, &fa) != TCL_OK) { + Tcl_DeleteHashEntry(cacheHashPtr); + return NULL; + } + + /* + * String contained the attributes inline. + */ + + fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa); + } + } + Tcl_SetHashValue(cacheHashPtr, fontPtr); + + fontPtr->refCount = 1; + fontPtr->cacheHashPtr = cacheHashPtr; + fontPtr->namedHashPtr = namedHashPtr; + + Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, 0, 0, &fontPtr->tabWidth); + if (fontPtr->tabWidth == 0) { + fontPtr->tabWidth = fontPtr->fm.maxWidth; + } + fontPtr->tabWidth *= 8; + + /* + * Make sure the tab width isn't zero (some fonts may not have enough + * information to set a reasonable tab width). + */ + + if (fontPtr->tabWidth == 0) { + fontPtr->tabWidth = 1; + } + + /* + * Get information used for drawing underlines in generic code on a + * non-underlined font. + */ + + descent = fontPtr->fm.descent; + fontPtr->underlinePos = descent / 2; + fontPtr->underlineHeight = fontPtr->fa.pointsize / 10; + if (fontPtr->underlineHeight == 0) { + fontPtr->underlineHeight = 1; + } + if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) { + /* + * If this set of values would cause the bottom of the underline + * bar to stick below the descent of the font, jack the underline + * up a bit higher. + */ + + fontPtr->underlineHeight = descent - fontPtr->underlinePos; + if (fontPtr->underlineHeight == 0) { + fontPtr->underlinePos--; + fontPtr->underlineHeight = 1; + } + } + + return (Tk_Font) fontPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_NameOfFont -- + * + * Given a font, return a textual string identifying it. + * + * Results: + * The return value is the description that was passed to + * Tk_GetFont() to create the font. The storage for the returned + * string is only guaranteed to persist until the font is deleted. + * The caller should not modify this string. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +char * +Tk_NameOfFont(tkfont) + Tk_Font tkfont; /* Font whose name is desired. */ +{ + TkFont *fontPtr; + Tcl_HashEntry *hPtr; + CachedFontKey *keyPtr; + + fontPtr = (TkFont *) tkfont; + hPtr = fontPtr->cacheHashPtr; + + keyPtr = (CachedFontKey *) Tcl_GetHashKey(hPtr->tablePtr, hPtr); + return (char *) keyPtr->string; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_FreeFont -- + * + * Called to release a font allocated by Tk_GetFont(). + * + * Results: + * None. + * + * Side effects: + * The reference count associated with font is decremented, and + * only deallocated when no one is using it. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_FreeFont(tkfont) + Tk_Font tkfont; /* Font to be released. */ +{ + TkFont *fontPtr; + NamedFont *nfPtr; + + if (tkfont == NULL) { + return; + } + fontPtr = (TkFont *) tkfont; + fontPtr->refCount--; + if (fontPtr->refCount == 0) { + if (fontPtr->namedHashPtr != NULL) { + /* + * The font is being deleted. Determine if the associated named + * font definition should and/or can be deleted too. + */ + + nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr); + nfPtr->refCount--; + if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) { + Tcl_DeleteHashEntry(fontPtr->namedHashPtr); + ckfree((char *) nfPtr); + } + } + Tcl_DeleteHashEntry(fontPtr->cacheHashPtr); + TkpDeleteFont(fontPtr); + } +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_FontId -- + * + * Given a font, return an opaque handle that should be selected + * into the XGCValues structure in order to get the constructed + * gc to use this font. This procedure would go away if the + * XGCValues structure were replaced with a TkGCValues structure. + * + * Results: + * As above. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +Font +Tk_FontId(tkfont) + Tk_Font tkfont; /* Font that is going to be selected into GC. */ +{ + TkFont *fontPtr; + + fontPtr = (TkFont *) tkfont; + return fontPtr->fid; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_GetFontMetrics -- + * + * Returns overall ascent and descent metrics for the given font. + * These values can be used to space multiple lines of text and + * to align the baselines of text in different fonts. + * + * Results: + * If *heightPtr is non-NULL, it is filled with the overall height + * of the font, which is the sum of the ascent and descent. + * If *ascentPtr or *descentPtr is non-NULL, they are filled with + * the ascent and/or descent information for the font. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +void +Tk_GetFontMetrics(tkfont, fmPtr) + Tk_Font tkfont; /* Font in which metrics are calculated. */ + Tk_FontMetrics *fmPtr; /* Pointer to structure in which font + * metrics for tkfont will be stored. */ +{ + TkFont *fontPtr; + + fontPtr = (TkFont *) tkfont; + fmPtr->ascent = fontPtr->fm.ascent; + fmPtr->descent = fontPtr->fm.descent; + fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_PostscriptFontName -- + * + * Given a Tk_Font, return the name of the corresponding Postscript + * font. + * + * Results: + * The return value is the pointsize of the given Tk_Font. + * The name of the Postscript font is appended to dsPtr. + * + * Side effects: + * If the font does not exist on the printer, the print job will + * fail at print time. Given a "reasonable" Postscript printer, + * the following Tk_Font font families should print correctly: + * + * Avant Garde, Arial, Bookman, Courier, Courier New, Geneva, + * Helvetica, Monaco, New Century Schoolbook, New York, + * Palatino, Symbol, Times, Times New Roman, Zapf Chancery, + * and Zapf Dingbats. + * + * Any other Tk_Font font families may not print correctly + * because the computed Postscript font name may be incorrect. + * + *--------------------------------------------------------------------------- + */ + + +int +Tk_PostscriptFontName(tkfont, dsPtr) + Tk_Font tkfont; /* Font in which text will be printed. */ + Tcl_DString *dsPtr; /* Pointer to an initialized Tcl_DString to + * which the name of the Postscript font that + * corresponds to tkfont will be appended. */ +{ + TkFont *fontPtr; + char *family, *weightString, *slantString; + char *src, *dest; + int upper, len; + + len = Tcl_DStringLength(dsPtr); + fontPtr = (TkFont *) tkfont; + + /* + * Convert the case-insensitive Tk_Font family name to the + * case-sensitive Postscript family name. Take out any spaces and + * capitalize the first letter of each word. + */ + + family = fontPtr->fa.family; + if (strncasecmp(family, "itc ", 4) == 0) { + family = family + 4; + } + if ((strcasecmp(family, "Arial") == 0) + || (strcasecmp(family, "Geneva") == 0)) { + family = "Helvetica"; + } else if ((strcasecmp(family, "Times New Roman") == 0) + || (strcasecmp(family, "New York") == 0)) { + family = "Times"; + } else if ((strcasecmp(family, "Courier New") == 0) + || (strcasecmp(family, "Monaco") == 0)) { + family = "Courier"; + } else if (strcasecmp(family, "AvantGarde") == 0) { + family = "AvantGarde"; + } else if (strcasecmp(family, "ZapfChancery") == 0) { + family = "ZapfChancery"; + } else if (strcasecmp(family, "ZapfDingbats") == 0) { + family = "ZapfDingbats"; + } else { + /* + * Inline, capitalize the first letter of each word, lowercase the + * rest of the letters in each word, and then take out the spaces + * between the words. This may make the DString shorter, which is + * safe to do. + */ + + Tcl_DStringAppend(dsPtr, family, -1); + + src = dest = Tcl_DStringValue(dsPtr) + len; + upper = 1; + for (; *src != '\0'; src++, dest++) { + while (isspace(UCHAR(*src))) { + src++; + upper = 1; + } + *dest = *src; + if ((upper != 0) && (islower(UCHAR(*src)))) { + *dest = toupper(UCHAR(*src)); + } + upper = 0; + } + *dest = '\0'; + Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr)); + family = Tcl_DStringValue(dsPtr) + len; + } + if (family != Tcl_DStringValue(dsPtr) + len) { + Tcl_DStringAppend(dsPtr, family, -1); + family = Tcl_DStringValue(dsPtr) + len; + } + + if (strcasecmp(family, "NewCenturySchoolbook") == 0) { + Tcl_DStringSetLength(dsPtr, len); + Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1); + family = Tcl_DStringValue(dsPtr) + len; + } + + /* + * Get the string to use for the weight. + */ + + weightString = NULL; + if (fontPtr->fa.weight == TK_FW_NORMAL) { + if (strcmp(family, "Bookman") == 0) { + weightString = "Light"; + } else if (strcmp(family, "AvantGarde") == 0) { + weightString = "Book"; + } else if (strcmp(family, "ZapfChancery") == 0) { + weightString = "Medium"; + } + } else { + if ((strcmp(family, "Bookman") == 0) + || (strcmp(family, "AvantGarde") == 0)) { + weightString = "Demi"; + } else { + weightString = "Bold"; + } + } + + /* + * Get the string to use for the slant. + */ + + slantString = NULL; + if (fontPtr->fa.slant == TK_FS_ROMAN) { + ; + } else { + if ((strcmp(family, "Helvetica") == 0) + || (strcmp(family, "Courier") == 0) + || (strcmp(family, "AvantGarde") == 0)) { + slantString = "Oblique"; + } else { + slantString = "Italic"; + } + } + + /* + * The string "Roman" needs to be added to some fonts that are not bold + * and not italic. + */ + + if ((slantString == NULL) && (weightString == NULL)) { + if ((strcmp(family, "Times") == 0) + || (strcmp(family, "NewCenturySchlbk") == 0) + || (strcmp(family, "Palatino") == 0)) { + Tcl_DStringAppend(dsPtr, "-Roman", -1); + } + } else { + Tcl_DStringAppend(dsPtr, "-", -1); + if (weightString != NULL) { + Tcl_DStringAppend(dsPtr, weightString, -1); + } + if (slantString != NULL) { + Tcl_DStringAppend(dsPtr, slantString, -1); + } + } + + return fontPtr->fa.pointsize; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_TextWidth -- + * + * A wrapper function for the more complicated interface of + * Tk_MeasureChars. Computes how much space the given + * simple string needs. + * + * Results: + * The return value is the width (in pixels) of the given string. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_TextWidth(tkfont, string, numChars) + Tk_Font tkfont; /* Font in which text will be measured. */ + CONST char *string; /* String whose width will be computed. */ + int numChars; /* Number of characters to consider from + * string, or < 0 for strlen(). */ +{ + int width; + + if (numChars < 0) { + numChars = strlen(string); + } + Tk_MeasureChars(tkfont, string, numChars, 0, 0, &width); + return width; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_UnderlineChars -- + * + * This procedure draws an underline for a given range of characters + * in a given string. It doesn't draw the characters (which are + * assumed to have been displayed previously); it just draws the + * underline. This procedure would mainly be used to quickly + * underline a few characters without having to construct an + * underlined font. To produce properly underlined text, the + * appropriate underlined font should be constructed and used. + * + * Results: + * None. + * + * Side effects: + * Information gets displayed in "drawable". + * + *---------------------------------------------------------------------- + */ + +void +Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstChar, + lastChar) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context for actually drawing + * line. */ + Tk_Font tkfont; /* Font used in GC; must have been allocated + * by Tk_GetFont(). Used for character + * dimensions, etc. */ + CONST char *string; /* String containing characters to be + * underlined or overstruck. */ + int x, y; /* Coordinates at which first character of + * string is drawn. */ + int firstChar; /* Index of first character. */ + int lastChar; /* Index of one after the last character. */ +{ + TkFont *fontPtr; + int startX, endX; + + fontPtr = (TkFont *) tkfont; + + Tk_MeasureChars(tkfont, string, firstChar, 0, 0, &startX); + Tk_MeasureChars(tkfont, string, lastChar, 0, 0, &endX); + + XFillRectangle(display, drawable, gc, x + startX, + y + fontPtr->underlinePos, (unsigned int) (endX - startX), + (unsigned int) fontPtr->underlineHeight); +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_ComputeTextLayout -- + * + * Computes the amount of screen space needed to display a + * multi-line, justified string of text. Records all the + * measurements that were done to determine to size and + * positioning of the individual lines of text; this information + * can be used by the Tk_DrawTextLayout() procedure to + * display the text quickly (without remeasuring it). + * + * This procedure is useful for simple widgets that want to + * display single-font, multi-line text and want Tk to handle the + * details. + * + * Results: + * The return value is a Tk_TextLayout token that holds the + * measurement information for the given string. The token is + * only valid for the given string. If the string is freed, + * the token is no longer valid and must also be freed. To free + * the token, call Tk_FreeTextLayout(). + * + * The dimensions of the screen area needed to display the text + * are stored in *widthPtr and *heightPtr. + * + * Side effects: + * Memory is allocated to hold the measurement information. + * + *--------------------------------------------------------------------------- + */ + +Tk_TextLayout +Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags, + widthPtr, heightPtr) + Tk_Font tkfont; /* Font that will be used to display text. */ + CONST char *string; /* String whose dimensions are to be + * computed. */ + int numChars; /* Number of characters to consider from + * string, or < 0 for strlen(). */ + int wrapLength; /* Longest permissible line length, in + * pixels. <= 0 means no automatic wrapping: + * just let lines get as long as needed. */ + Tk_Justify justify; /* How to justify lines. */ + int flags; /* Flag bits OR-ed together. + * TK_IGNORE_TABS means that tab characters + * should not be expanded. TK_IGNORE_NEWLINES + * means that newline characters should not + * cause a line break. */ + int *widthPtr; /* Filled with width of string. */ + int *heightPtr; /* Filled with height of string. */ +{ + TkFont *fontPtr; + CONST char *start, *end, *special; + int n, y, charsThisChunk, maxChunks; + int baseline, height, curX, newX, maxWidth; + TextLayout *layoutPtr; + LayoutChunk *chunkPtr; + CONST TkFontMetrics *fmPtr; +#define MAX_LINES 50 + int staticLineLengths[MAX_LINES]; + int *lineLengths; + int maxLines, curLine, layoutHeight; + + lineLengths = staticLineLengths; + maxLines = MAX_LINES; + + fontPtr = (TkFont *) tkfont; + fmPtr = &fontPtr->fm; + + height = fmPtr->ascent + fmPtr->descent; + + if (numChars < 0) { + numChars = strlen(string); + } + + maxChunks = 1; + + layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout) + + (maxChunks - 1) * sizeof(LayoutChunk)); + layoutPtr->tkfont = tkfont; + layoutPtr->string = string; + layoutPtr->numChunks = 0; + + baseline = fmPtr->ascent; + maxWidth = 0; + + /* + * Divide the string up into simple strings and measure each string. + */ + + curX = 0; + + end = string + numChars; + special = string; + + flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES; + flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE; + curLine = 0; + for (start = string; start < end; ) { + if (start >= special) { + /* + * Find the next special character in the string. + */ + + for (special = start; special < end; special++) { + if (!(flags & TK_IGNORE_NEWLINES)) { + if ((*special == '\n') || (*special == '\r')) { + break; + } + } + if (!(flags & TK_IGNORE_TABS)) { + if (*special == '\t') { + break; + } + } + } + } + + /* + * Special points at the next special character (or the end of the + * string). Process characters between start and special. + */ + + chunkPtr = NULL; + if (start < special) { + charsThisChunk = Tk_MeasureChars(tkfont, start, special - start, + wrapLength - curX, flags, &newX); + newX += curX; + flags &= ~TK_AT_LEAST_ONE; + if (charsThisChunk > 0) { + chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, + charsThisChunk, curX, newX, baseline); + + start += charsThisChunk; + curX = newX; + } + } + + if ((start == special) && (special < end)) { + /* + * Handle the special character. + */ + + chunkPtr = NULL; + if (*special == '\t') { + newX = curX + fontPtr->tabWidth; + newX -= newX % fontPtr->tabWidth; + NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX, + baseline)->numDisplayChars = -1; + start++; + if ((start < end) && + ((wrapLength <= 0) || (newX <= wrapLength))) { + /* + * More chars can still fit on this line. + */ + + curX = newX; + flags &= ~TK_AT_LEAST_ONE; + continue; + } + } else { + NewChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000, + baseline)->numDisplayChars = -1; + start++; + goto wrapLine; + } + } + + /* + * No more characters are going to go on this line, either because + * no more characters can fit or there are no more characters left. + * Consume all extra spaces at end of line. + */ + + while ((start < end) && isspace(UCHAR(*start))) { + if (!(flags & TK_IGNORE_NEWLINES)) { + if ((*start == '\n') || (*start == '\r')) { + break; + } + } + if (!(flags & TK_IGNORE_TABS)) { + if (*start == '\t') { + break; + } + } + start++; + } + if (chunkPtr != NULL) { + /* + * Append all the extra spaces on this line to the end of the + * last text chunk. + */ + charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars); + if (charsThisChunk > 0) { + chunkPtr->numChars += Tk_MeasureChars(tkfont, + chunkPtr->start + chunkPtr->numChars, charsThisChunk, + 0, 0, &chunkPtr->totalWidth); + chunkPtr->totalWidth += curX; + } + } + + wrapLine: + flags |= TK_AT_LEAST_ONE; + + /* + * Save current line length, then move current position to start of + * next line. + */ + + if (curX > maxWidth) { + maxWidth = curX; + } + + /* + * Remember width of this line, so that all chunks on this line + * can be centered or right justified, if necessary. + */ + + if (curLine >= maxLines) { + int *newLengths; + + newLengths = (int *) ckalloc(2 * maxLines * sizeof(int)); + memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int)); + if (lineLengths != staticLineLengths) { + ckfree((char *) lineLengths); + } + lineLengths = newLengths; + maxLines *= 2; + } + lineLengths[curLine] = curX; + curLine++; + + curX = 0; + baseline += height; + } + + /* + * If last line ends with a newline, then we need to make a 0 width + * chunk on the next line. Otherwise "Hello" and "Hello\n" are the + * same height. + */ + + if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) { + if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') { + chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX, + 1000000000, baseline); + chunkPtr->numDisplayChars = -1; + baseline += height; + } + } + + /* + * Using maximum line length, shift all the chunks so that the lines are + * all justified correctly. + */ + + curLine = 0; + chunkPtr = layoutPtr->chunks; + y = chunkPtr->y; + for (n = 0; n < layoutPtr->numChunks; n++) { + int extra; + + if (chunkPtr->y != y) { + curLine++; + y = chunkPtr->y; + } + extra = maxWidth - lineLengths[curLine]; + if (justify == TK_JUSTIFY_CENTER) { + chunkPtr->x += extra / 2; + } else if (justify == TK_JUSTIFY_RIGHT) { + chunkPtr->x += extra; + } + chunkPtr++; + } + + layoutPtr->width = maxWidth; + layoutHeight = baseline - fmPtr->ascent; + if (layoutPtr->numChunks == 0) { + layoutHeight = height; + + /* + * This fake chunk is used by the other procedures so that they can + * pretend that there is a chunk with no chars in it, which makes + * the coding simpler. + */ + + layoutPtr->numChunks = 1; + layoutPtr->chunks[0].start = string; + layoutPtr->chunks[0].numChars = 0; + layoutPtr->chunks[0].numDisplayChars = -1; + layoutPtr->chunks[0].x = 0; + layoutPtr->chunks[0].y = fmPtr->ascent; + layoutPtr->chunks[0].totalWidth = 0; + layoutPtr->chunks[0].displayWidth = 0; + } + + if (widthPtr != NULL) { + *widthPtr = layoutPtr->width; + } + if (heightPtr != NULL) { + *heightPtr = layoutHeight; + } + if (lineLengths != staticLineLengths) { + ckfree((char *) lineLengths); + } + + return (Tk_TextLayout) layoutPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_FreeTextLayout -- + * + * This procedure is called to release the storage associated with + * a Tk_TextLayout when it is no longer needed. + * + * Results: + * None. + * + * Side effects: + * Memory is freed. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_FreeTextLayout(textLayout) + Tk_TextLayout textLayout; /* The text layout to be released. */ +{ + TextLayout *layoutPtr; + + layoutPtr = (TextLayout *) textLayout; + if (layoutPtr != NULL) { + ckfree((char *) layoutPtr); + } +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DrawTextLayout -- + * + * Use the information in the Tk_TextLayout token to display a + * multi-line, justified string of text. + * + * This procedure is useful for simple widgets that need to + * display single-font, multi-line text and want Tk to handle + * the details. + * + * Results: + * None. + * + * Side effects: + * Text drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context to use for drawing text. */ + Tk_TextLayout layout; /* Layout information, from a previous call + * to Tk_ComputeTextLayout(). */ + int x, y; /* Upper-left hand corner of rectangle in + * which to draw (pixels). */ + int firstChar; /* The index of the first character to draw + * from the given text item. 0 specfies the + * beginning. */ + int lastChar; /* The index just after the last character + * to draw from the given text item. A number + * < 0 means to draw all characters. */ +{ + TextLayout *layoutPtr; + int i, numDisplayChars, drawX; + LayoutChunk *chunkPtr; + + layoutPtr = (TextLayout *) layout; + if (layoutPtr == NULL) { + return; + } + + if (lastChar < 0) { + lastChar = 100000000; + } + chunkPtr = layoutPtr->chunks; + for (i = 0; i < layoutPtr->numChunks; i++) { + numDisplayChars = chunkPtr->numDisplayChars; + if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) { + if (firstChar <= 0) { + drawX = 0; + firstChar = 0; + } else { + Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstChar, + 0, 0, &drawX); + } + if (lastChar < numDisplayChars) { + numDisplayChars = lastChar; + } + Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, + chunkPtr->start + firstChar, numDisplayChars - firstChar, + x + chunkPtr->x + drawX, y + chunkPtr->y); + } + firstChar -= chunkPtr->numChars; + lastChar -= chunkPtr->numChars; + if (lastChar <= 0) { + break; + } + chunkPtr++; + } +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_UnderlineTextLayout -- + * + * Use the information in the Tk_TextLayout token to display an + * underline below an individual character. This procedure does + * not draw the text, just the underline. + * + * This procedure is useful for simple widgets that need to + * display single-font, multi-line text with an individual + * character underlined and want Tk to handle the details. + * To display larger amounts of underlined text, construct + * and use an underlined font. + * + * Results: + * None. + * + * Side effects: + * Underline drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context to use for drawing text. */ + Tk_TextLayout layout; /* Layout information, from a previous call + * to Tk_ComputeTextLayout(). */ + int x, y; /* Upper-left hand corner of rectangle in + * which to draw (pixels). */ + int underline; /* Index of the single character to + * underline, or -1 for no underline. */ +{ + TextLayout *layoutPtr; + TkFont *fontPtr; + int xx, yy, width, height; + + if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0) + && (width != 0)) { + layoutPtr = (TextLayout *) layout; + fontPtr = (TkFont *) layoutPtr->tkfont; + + XFillRectangle(display, drawable, gc, x + xx, + y + yy + fontPtr->fm.ascent + fontPtr->underlinePos, + (unsigned int) width, (unsigned int) fontPtr->underlineHeight); + } +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_PointToChar -- + * + * Use the information in the Tk_TextLayout token to determine the + * character closest to the given point. The point must be + * specified with respect to the upper-left hand corner of the + * text layout, which is considered to be located at (0, 0). + * + * Any point whose y-value is less that 0 will be considered closest + * to the first character in the text layout; any point whose y-value + * is greater than the height of the text layout will be considered + * closest to the last character in the text layout. + * + * Any point whose x-value is less than 0 will be considered closest + * to the first character on that line; any point whose x-value is + * greater than the width of the text layout will be considered + * closest to the last character on that line. + * + * Results: + * The return value is the index of the character that was + * closest to the point. Given a text layout with no characters, + * the value 0 will always be returned, referring to a hypothetical + * zero-width placeholder character. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_PointToChar(layout, x, y) + Tk_TextLayout layout; /* Layout information, from a previous call + * to Tk_ComputeTextLayout(). */ + int x, y; /* Coordinates of point to check, with + * respect to the upper-left corner of the + * text layout. */ +{ + TextLayout *layoutPtr; + LayoutChunk *chunkPtr, *lastPtr; + TkFont *fontPtr; + int i, n, dummy, baseline, pos; + + if (y < 0) { + /* + * Point lies above any line in this layout. Return the index of + * the first char. + */ + + return 0; + } + + /* + * Find which line contains the point. + */ + + layoutPtr = (TextLayout *) layout; + fontPtr = (TkFont *) layoutPtr->tkfont; + lastPtr = chunkPtr = layoutPtr->chunks; + for (i = 0; i < layoutPtr->numChunks; i++) { + baseline = chunkPtr->y; + if (y < baseline + fontPtr->fm.descent) { + if (x < chunkPtr->x) { + /* + * Point is to the left of all chunks on this line. Return + * the index of the first character on this line. + */ + + return chunkPtr->start - layoutPtr->string; + } + if (x >= layoutPtr->width) { + /* + * If point lies off right side of the text layout, return + * the last char in the last chunk on this line. Without + * this, it might return the index of the first char that + * was located outside of the text layout. + */ + + x = INT_MAX; + } + + /* + * Examine all chunks on this line to see which one contains + * the specified point. + */ + + lastPtr = chunkPtr; + while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline)) { + if (x < chunkPtr->x + chunkPtr->totalWidth) { + /* + * Point falls on one of the characters in this chunk. + */ + + if (chunkPtr->numDisplayChars < 0) { + /* + * This is a special chunk that encapsulates a single + * tab or newline char. + */ + + return chunkPtr->start - layoutPtr->string; + } + n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start, + chunkPtr->numChars, x + 1 - chunkPtr->x, + TK_PARTIAL_OK, &dummy); + return (chunkPtr->start + n - 1) - layoutPtr->string; + } + lastPtr = chunkPtr; + chunkPtr++; + i++; + } + + /* + * Point is to the right of all chars in all the chunks on this + * line. Return the index just past the last char in the last + * chunk on this line. + */ + + pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string; + if (i < layoutPtr->numChunks) { + pos--; + } + return pos; + } + lastPtr = chunkPtr; + chunkPtr++; + } + + /* + * Point lies below any line in this text layout. Return the index + * just past the last char. + */ + + return (lastPtr->start + lastPtr->numChars) - layoutPtr->string; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_CharBbox -- + * + * Use the information in the Tk_TextLayout token to return the + * bounding box for the character specified by index. + * + * The width of the bounding box is the advance width of the + * character, and does not include and left- or right-bearing. + * Any character that extends partially outside of the + * text layout is considered to be truncated at the edge. Any + * character which is located completely outside of the text + * layout is considered to be zero-width and pegged against + * the edge. + * + * The height of the bounding box is the line height for this font, + * extending from the top of the ascent to the bottom of the + * descent. Information about the actual height of the individual + * letter is not available. + * + * A text layout that contains no characters is considered to + * contain a single zero-width placeholder character. + * + * Results: + * The return value is 0 if the index did not specify a character + * in the text layout, or non-zero otherwise. In that case, + * *bbox is filled with the bounding box of the character. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr) + Tk_TextLayout layout; /* Layout information, from a previous call to + * Tk_ComputeTextLayout(). */ + int index; /* The index of the character whose bbox is + * desired. */ + int *xPtr, *yPtr; /* Filled with the upper-left hand corner, in + * pixels, of the bounding box for the character + * specified by index, if non-NULL. */ + int *widthPtr, *heightPtr; + /* Filled with the width and height of the + * bounding box for the character specified by + * index, if non-NULL. */ +{ + TextLayout *layoutPtr; + LayoutChunk *chunkPtr; + int i, x, w; + Tk_Font tkfont; + TkFont *fontPtr; + + if (index < 0) { + return 0; + } + + layoutPtr = (TextLayout *) layout; + chunkPtr = layoutPtr->chunks; + tkfont = layoutPtr->tkfont; + fontPtr = (TkFont *) tkfont; + + for (i = 0; i < layoutPtr->numChunks; i++) { + if (chunkPtr->numDisplayChars < 0) { + if (index == 0) { + x = chunkPtr->x; + w = chunkPtr->totalWidth; + goto check; + } + } else if (index < chunkPtr->numChars) { + if (xPtr != NULL) { + Tk_MeasureChars(tkfont, chunkPtr->start, index, 0, 0, &x); + x += chunkPtr->x; + } + if (widthPtr != NULL) { + Tk_MeasureChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w); + } + goto check; + } + index -= chunkPtr->numChars; + chunkPtr++; + } + if (index == 0) { + /* + * Special case to get location just past last char in layout. + */ + + chunkPtr--; + x = chunkPtr->x + chunkPtr->totalWidth; + w = 0; + } else { + return 0; + } + + /* + * Ensure that the bbox lies within the text layout. This forces all + * chars that extend off the right edge of the text layout to have + * truncated widths, and all chars that are completely off the right + * edge of the text layout to peg to the edge and have 0 width. + */ + check: + if (yPtr != NULL) { + *yPtr = chunkPtr->y - fontPtr->fm.ascent; + } + if (heightPtr != NULL) { + *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent; + } + + if (x > layoutPtr->width) { + x = layoutPtr->width; + } + if (xPtr != NULL) { + *xPtr = x; + } + if (widthPtr != NULL) { + if (x + w > layoutPtr->width) { + w = layoutPtr->width - x; + } + *widthPtr = w; + } + + return 1; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DistanceToTextLayout -- + * + * Computes the distance in pixels from the given point to the + * given text layout. Non-displaying space characters that occur + * at the end of individual lines in the text layout are ignored + * for hit detection purposes. + * + * Results: + * The return value is 0 if the point (x, y) is inside the text + * layout. If the point isn't inside the text layout then the + * return value is the distance in pixels from the point to the + * text item. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_DistanceToTextLayout(layout, x, y) + Tk_TextLayout layout; /* Layout information, from a previous call + * to Tk_ComputeTextLayout(). */ + int x, y; /* Coordinates of point to check, with + * respect to the upper-left corner of the + * text layout (in pixels). */ +{ + int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent; + LayoutChunk *chunkPtr; + TextLayout *layoutPtr; + TkFont *fontPtr; + + layoutPtr = (TextLayout *) layout; + fontPtr = (TkFont *) layoutPtr->tkfont; + ascent = fontPtr->fm.ascent; + descent = fontPtr->fm.descent; + + minDist = 0; + chunkPtr = layoutPtr->chunks; + for (i = 0; i < layoutPtr->numChunks; i++) { + if (chunkPtr->start[0] == '\n') { + /* + * Newline characters are not counted when computing distance + * (but tab characters would still be considered). + */ + + chunkPtr++; + continue; + } + + x1 = chunkPtr->x; + y1 = chunkPtr->y - ascent; + x2 = chunkPtr->x + chunkPtr->displayWidth; + y2 = chunkPtr->y + descent; + + if (x < x1) { + xDiff = x1 - x; + } else if (x >= x2) { + xDiff = x - x2 + 1; + } else { + xDiff = 0; + } + + if (y < y1) { + yDiff = y1 - y; + } else if (y >= y2) { + yDiff = y - y2 + 1; + } else { + yDiff = 0; + } + if ((xDiff == 0) && (yDiff == 0)) { + return 0; + } + dist = (int) hypot((double) xDiff, (double) yDiff); + if ((dist < minDist) || (minDist == 0)) { + minDist = dist; + } + chunkPtr++; + } + return minDist; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_IntersectTextLayout -- + * + * Determines whether a text layout lies entirely inside, + * entirely outside, or overlaps a given rectangle. Non-displaying + * space characters that occur at the end of individual lines in + * the text layout are ignored for intersection calculations. + * + * Results: + * The return value is -1 if the text layout is entirely outside of + * the rectangle, 0 if it overlaps, and 1 if it is entirely inside + * of the rectangle. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_IntersectTextLayout(layout, x, y, width, height) + Tk_TextLayout layout; /* Layout information, from a previous call + * to Tk_ComputeTextLayout(). */ + int x, y; /* Upper-left hand corner, in pixels, of + * rectangular area to compare with text + * layout. Coordinates are with respect to + * the upper-left hand corner of the text + * layout itself. */ + int width, height; /* The width and height of the above + * rectangular area, in pixels. */ +{ + int result, i, x1, y1, x2, y2; + TextLayout *layoutPtr; + LayoutChunk *chunkPtr; + TkFont *fontPtr; + int left, top, right, bottom; + + /* + * Scan the chunks one at a time, seeing whether each is entirely in, + * entirely out, or overlapping the rectangle. If an overlap is + * detected, return immediately; otherwise wait until all chunks have + * been processed and see if they were all inside or all outside. + */ + + layoutPtr = (TextLayout *) layout; + chunkPtr = layoutPtr->chunks; + fontPtr = (TkFont *) layoutPtr->tkfont; + + left = x; + top = y; + right = x + width; + bottom = y + height; + + result = 0; + for (i = 0; i < layoutPtr->numChunks; i++) { + if (chunkPtr->start[0] == '\n') { + /* + * Newline characters are not counted when computing area + * intersection (but tab characters would still be considered). + */ + + chunkPtr++; + continue; + } + + x1 = chunkPtr->x; + y1 = chunkPtr->y - fontPtr->fm.ascent; + x2 = chunkPtr->x + chunkPtr->displayWidth; + y2 = chunkPtr->y + fontPtr->fm.descent; + + if ((right < x1) || (left >= x2) + || (bottom < y1) || (top >= y2)) { + if (result == 1) { + return 0; + } + result = -1; + } else if ((x1 < left) || (x2 >= right) + || (y1 < top) || (y2 >= bottom)) { + return 0; + } else if (result == -1) { + return 0; + } else { + result = 1; + } + chunkPtr++; + } + return result; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_TextLayoutToPostscript -- + * + * Outputs the contents of a text layout in Postscript format. + * The set of lines in the text layout will be rendered by the user + * supplied Postscript function. The function should be of the form: + * + * justify x y string function -- + * + * Justify is -1, 0, or 1, depending on whether the following string + * should be left, center, or right justified, x and y is the + * location for the origin of the string, string is the sequence + * of characters to be printed, and function is the name of the + * caller-provided function; the function should leave nothing + * on the stack. + * + * The meaning of the origin of the string (x and y) depends on + * the justification. For left justification, x is where the + * left edge of the string should appear. For center justification, + * x is where the center of the string should appear. And for right + * justification, x is where the right edge of the string should + * appear. This behavior is necessary because, for example, right + * justified text on the screen is justified with screen metrics. + * The same string needs to be justified with printer metrics on + * the printer to appear in the correct place with respect to other + * similarly justified strings. In all circumstances, y is the + * location of the baseline for the string. + * + * Results: + * Interp->result is modified to hold the Postscript code that + * will render the text layout. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_TextLayoutToPostscript(interp, layout) + Tcl_Interp *interp; /* Filled with Postscript code. */ + Tk_TextLayout layout; /* The layout to be rendered. */ +{ +#define MAXUSE 128 + char buf[MAXUSE+10]; + LayoutChunk *chunkPtr; + int i, j, used, c, baseline; + TextLayout *layoutPtr; + + layoutPtr = (TextLayout *) layout; + chunkPtr = layoutPtr->chunks; + baseline = chunkPtr->y; + used = 0; + buf[used++] = '('; + for (i = 0; i < layoutPtr->numChunks; i++) { + if (baseline != chunkPtr->y) { + buf[used++] = ')'; + buf[used++] = '\n'; + buf[used++] = '('; + baseline = chunkPtr->y; + } + if (chunkPtr->numDisplayChars <= 0) { + if (chunkPtr->start[0] == '\t') { + buf[used++] = '\\'; + buf[used++] = 't'; + } + } else { + for (j = 0; j < chunkPtr->numDisplayChars; j++) { + c = UCHAR(chunkPtr->start[j]); + if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20) + || (c >= UCHAR(0x7f))) { + /* + * Tricky point: the "03" is necessary in the sprintf + * below, so that a full three digits of octal are + * always generated. Without the "03", a number + * following this sequence could be interpreted by + * Postscript as part of this sequence. + */ + + sprintf(buf + used, "\\%03o", c); + used += 4; + } else { + buf[used++] = c; + } + if (used >= MAXUSE) { + buf[used] = '\0'; + Tcl_AppendResult(interp, buf, (char *) NULL); + used = 0; + } + } + } + if (used >= MAXUSE) { + /* + * If there are a whole bunch of returns or tabs in a row, + * then buf[] could get filled up. + */ + + buf[used] = '\0'; + Tcl_AppendResult(interp, buf, (char *) NULL); + used = 0; + } + chunkPtr++; + } + buf[used++] = ')'; + buf[used++] = '\n'; + buf[used] = '\0'; + Tcl_AppendResult(interp, buf, (char *) NULL); +} + +/* + *--------------------------------------------------------------------------- + * + * TkInitFontAttributes -- + * + * Initialize the font attributes structure to contain sensible + * values. This must be called before using any other font + * attributes functions. + * + * Results: + * None. + * + * Side effects. + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkInitFontAttributes(faPtr) + TkFontAttributes *faPtr; /* The attributes structure to initialize. */ +{ + faPtr->family = NULL; + faPtr->pointsize = 0; + faPtr->weight = TK_FW_NORMAL; + faPtr->slant = TK_FS_ROMAN; + faPtr->underline = 0; + faPtr->overstrike = 0; +} + +/* + *--------------------------------------------------------------------------- + * + * ConfigAttributesObj -- + * + * Process command line options to fill in fields of a properly + * initialized font attributes structure. + * + * Results: + * A standard Tcl return value. If TCL_ERROR is returned, an + * error message will be left in interp's result object. + * + * Side effects: + * The fields of the font attributes structure get filled in with + * information from argc/argv. If an error occurs while parsing, + * the font attributes structure will contain all modifications + * specified in the command line options up to the point of the + * error. + * + *--------------------------------------------------------------------------- + */ + +static int +ConfigAttributesObj(interp, tkwin, objc, objv, faPtr) + Tcl_Interp *interp; /* Interp for error return. */ + Tk_Window tkwin; /* For display on which font will be used. */ + int objc; /* Number of elements in argv. */ + Tcl_Obj *CONST objv[]; /* Command line options. */ + TkFontAttributes *faPtr; /* Font attributes structure whose fields + * are to be modified. Structure must already + * be properly initialized. */ +{ + int i, n, index; + Tcl_Obj *value; + char *option, *string; + + if (objc & 1) { + string = Tcl_GetStringFromObj(objv[objc - 1], NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"", + string, "\" option", (char *) NULL); + return TCL_ERROR; + } + + for (i = 0; i < objc; i += 2) { + option = Tcl_GetStringFromObj(objv[i], NULL); + value = objv[i + 1]; + + if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1, + &index) != TCL_OK) { + return TCL_ERROR; + } + switch (index) { + case FONT_FAMILY: + string = Tcl_GetStringFromObj(value, NULL); + faPtr->family = Tk_GetUid(string); + break; + + case FONT_SIZE: + if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) { + return TCL_ERROR; + } + faPtr->pointsize = n; + break; + + case FONT_WEIGHT: + string = Tcl_GetStringFromObj(value, NULL); + n = TkFindStateNum(interp, option, weightMap, string); + if (n == TK_FW_UNKNOWN) { + return TCL_ERROR; + } + faPtr->weight = n; + break; + + case FONT_SLANT: + string = Tcl_GetStringFromObj(value, NULL); + n = TkFindStateNum(interp, option, slantMap, string); + if (n == TK_FS_UNKNOWN) { + return TCL_ERROR; + } + faPtr->slant = n; + break; + + case FONT_UNDERLINE: + if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) { + return TCL_ERROR; + } + faPtr->underline = n; + break; + + case FONT_OVERSTRIKE: + if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) { + return TCL_ERROR; + } + faPtr->overstrike = n; + break; + } + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * GetAttributeInfoObj -- + * + * Return information about the font attributes as a Tcl list. + * + * Results: + * The return value is TCL_OK if the objPtr was non-NULL and + * specified a valid font attribute, TCL_ERROR otherwise. If TCL_OK + * is returned, the interp's result object is modified to hold a + * description of either the current value of a single option, or a + * list of all options and their current values for the given font + * attributes. If TCL_ERROR is returned, the interp's result is + * set to an error message describing that the objPtr did not refer + * to a valid option. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +GetAttributeInfoObj(interp, faPtr, objPtr) + Tcl_Interp *interp; /* Interp to hold result. */ + CONST TkFontAttributes *faPtr; /* The font attributes to inspect. */ + Tcl_Obj *objPtr; /* If non-NULL, indicates the single + * option whose value is to be + * returned. Otherwise + * information is returned for + * all options. */ +{ + int i, index, start, end, num; + char *str; + Tcl_Obj *newPtr; + + start = 0; + end = FONT_NUMFIELDS; + if (objPtr != NULL) { + if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", 1, + &index) != TCL_OK) { + return TCL_ERROR; + } + start = index; + end = index + 1; + } + + for (i = start; i < end; i++) { + str = NULL; + num = 0; /* Needed only to prevent compiler + * warning. */ + switch (i) { + case FONT_FAMILY: + str = faPtr->family; + if (str == NULL) { + str = ""; + } + break; + + case FONT_SIZE: + num = faPtr->pointsize; + break; + + case FONT_WEIGHT: + str = TkFindStateString(weightMap, faPtr->weight); + break; + + case FONT_SLANT: + str = TkFindStateString(slantMap, faPtr->slant); + break; + + case FONT_UNDERLINE: + num = faPtr->underline; + break; + + case FONT_OVERSTRIKE: + num = faPtr->overstrike; + break; + } + if (objPtr == NULL) { + Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), + Tcl_NewStringObj(fontOpt[i], -1)); + if (str != NULL) { + newPtr = Tcl_NewStringObj(str, -1); + } else { + newPtr = Tcl_NewIntObj(num); + } + Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), + newPtr); + } else { + if (str != NULL) { + Tcl_SetStringObj(Tcl_GetObjResult(interp), str, -1); + } else { + Tcl_SetIntObj(Tcl_GetObjResult(interp), num); + } + } + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * ParseFontNameObj -- + * + * Converts a object into a set of font attributes that can be used + * to construct a font. + * + * The string rep of the object can be one of the following forms: + * XLFD (see X documentation) + * "Family [size [style] [style ...]]" + * "-option value [-option value ...]" + * + * Results: + * The return value is TCL_ERROR if the object was syntactically + * invalid. In that case an error message is left in interp's + * result object. Otherwise, fills the font attribute buffer with + * the values parsed from the string and returns TCL_OK; + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +ParseFontNameObj(interp, tkwin, objPtr, faPtr) + Tcl_Interp *interp; /* Interp for error return. */ + Tk_Window tkwin; /* For display on which font is used. */ + Tcl_Obj *objPtr; /* Parseable font description object. */ + TkFontAttributes *faPtr; /* Font attributes structure whose fields + * are to be modified. Structure must already + * be properly initialized. */ +{ + char *dash; + int objc, result, i, n; + Tcl_Obj **objv; + TkXLFDAttributes xa; + char *string; + + string = Tcl_GetStringFromObj(objPtr, NULL); + if (*string == '-') { + /* + * This may be an XLFD or an "-option value" string. + * + * If the string begins with "-*" or a "-foundry-family-*" pattern, + * then consider it an XLFD. + */ + + if (string[1] == '*') { + goto xlfd; + } + dash = strchr(string + 1, '-'); + if ((dash != NULL) && (!isspace(UCHAR(dash[-1])))) { + goto xlfd; + } + + if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { + return TCL_ERROR; + } + + return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr); + } + + if (*string == '*') { + /* + * This appears to be an XLFD. + */ + + xlfd: + xa.fa = *faPtr; + result = TkParseXLFD(string, &xa); + if (result == TCL_OK) { + *faPtr = xa.fa; + return result; + } + } + + /* + * Wasn't an XLFD or "-option value" string. Try it as a + * "font size style" list. + */ + + if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { + return TCL_ERROR; + } + if (objc < 1) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "font \"", string, + "\" doesn't exist", (char *) NULL); + return TCL_ERROR; + } + + faPtr->family = Tk_GetUid(Tcl_GetStringFromObj(objv[0], NULL)); + if (objc > 1) { + if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) { + return TCL_ERROR; + } + faPtr->pointsize = n; + } + + i = 2; + if (objc == 3) { + if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) { + return TCL_ERROR; + } + i = 0; + } + for ( ; i < objc; i++) { + string = Tcl_GetStringFromObj(objv[i], NULL); + n = TkFindStateNum(NULL, NULL, weightMap, string); + if (n != TK_FW_UNKNOWN) { + faPtr->weight = n; + continue; + } + n = TkFindStateNum(NULL, NULL, slantMap, string); + if (n != TK_FS_UNKNOWN) { + faPtr->slant = n; + continue; + } + n = TkFindStateNum(NULL, NULL, underlineMap, string); + if (n != 0) { + faPtr->underline = n; + continue; + } + n = TkFindStateNum(NULL, NULL, overstrikeMap, string); + if (n != 0) { + faPtr->overstrike = n; + continue; + } + + /* + * Unknown style. + */ + + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown font style \"", string, "\"", + (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * TkParseXLFD -- + * + * Break up a fully specified XLFD into a set of font attributes. + * + * Results: + * Return value is TCL_ERROR if string was not a fully specified XLFD. + * Otherwise, fills font attribute buffer with the values parsed + * from the XLFD and returns TCL_OK. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkParseXLFD(string, xaPtr) + CONST char *string; /* Parseable font description string. */ + TkXLFDAttributes *xaPtr; /* XLFD attributes structure whose fields + * are to be modified. Structure must already + * be properly initialized. */ +{ + char *src; + CONST char *str; + int i, j; + char *field[XLFD_NUMFIELDS + 2]; + Tcl_DString ds; + + memset(field, '\0', sizeof(field)); + + str = string; + if (*str == '-') { + str++; + } + + Tcl_DStringInit(&ds); + Tcl_DStringAppend(&ds, (char *) str, -1); + src = Tcl_DStringValue(&ds); + + field[0] = src; + for (i = 0; *src != '\0'; src++) { + if (isupper(UCHAR(*src))) { + *src = tolower(UCHAR(*src)); + } + if (*src == '-') { + i++; + if (i > XLFD_NUMFIELDS) { + break; + } + *src = '\0'; + field[i] = src + 1; + } + } + + /* + * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common, + * but it is (strictly) malformed, because the first * is eliding both + * the Setwidth and the Addstyle fields. If the Addstyle field is a + * number, then assume the above incorrect form was used and shift all + * the rest of the fields up by one, so the number gets interpreted + * as a pixelsize. This fix is so that we don't get a million reports + * that "it works under X, but gives a syntax error under Windows". + */ + + if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) { + if (atoi(field[XLFD_ADD_STYLE]) != 0) { + for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) { + field[j + 1] = field[j]; + } + field[XLFD_ADD_STYLE] = NULL; + i++; + } + } + + /* + * Bail if we don't have enough of the fields (up to pointsize). + */ + + if (i < XLFD_FAMILY) { + Tcl_DStringFree(&ds); + return TCL_ERROR; + } + + if (FieldSpecified(field[XLFD_FOUNDRY])) { + xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]); + } + + if (FieldSpecified(field[XLFD_FAMILY])) { + xaPtr->fa.family = Tk_GetUid(field[XLFD_FAMILY]); + } + if (FieldSpecified(field[XLFD_WEIGHT])) { + xaPtr->fa.weight = TkFindStateNum(NULL, NULL, xlfdWeightMap, + field[XLFD_WEIGHT]); + } + if (FieldSpecified(field[XLFD_SLANT])) { + xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap, + field[XLFD_SLANT]); + if (xaPtr->slant == TK_FS_ROMAN) { + xaPtr->fa.slant = TK_FS_ROMAN; + } else { + xaPtr->fa.slant = TK_FS_ITALIC; + } + } + if (FieldSpecified(field[XLFD_SETWIDTH])) { + xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap, + field[XLFD_SETWIDTH]); + } + + /* XLFD_ADD_STYLE ignored. */ + + /* + * Pointsize in tenths of a point, but treat it as tenths of a pixel. + */ + + if (FieldSpecified(field[XLFD_POINT_SIZE])) { + if (field[XLFD_POINT_SIZE][0] == '[') { + /* + * Some X fonts have the point size specified as follows: + * + * [ N1 N2 N3 N4 ] + * + * where N1 is the point size (in points, not decipoints!), and + * N2, N3, and N4 are some additional numbers that I don't know + * the purpose of, so I ignore them. + */ + + xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1); + } else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE], + &xaPtr->fa.pointsize) == TCL_OK) { + xaPtr->fa.pointsize /= 10; + } else { + return TCL_ERROR; + } + } + + /* + * Pixel height of font. If specified, overrides pointsize. + */ + + if (FieldSpecified(field[XLFD_PIXEL_SIZE])) { + if (field[XLFD_PIXEL_SIZE][0] == '[') { + /* + * Some X fonts have the pixel size specified as follows: + * + * [ N1 N2 N3 N4 ] + * + * where N1 is the pixel size, and where N2, N3, and N4 + * are some additional numbers that I don't know + * the purpose of, so I ignore them. + */ + + xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1); + } else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE], + &xaPtr->fa.pointsize) != TCL_OK) { + return TCL_ERROR; + } + } + + xaPtr->fa.pointsize = -xaPtr->fa.pointsize; + + /* XLFD_RESOLUTION_X ignored. */ + + /* XLFD_RESOLUTION_Y ignored. */ + + /* XLFD_SPACING ignored. */ + + /* XLFD_AVERAGE_WIDTH ignored. */ + + if (FieldSpecified(field[XLFD_REGISTRY])) { + xaPtr->charset = TkFindStateNum(NULL, NULL, xlfdCharsetMap, + field[XLFD_REGISTRY]); + } + if (FieldSpecified(field[XLFD_ENCODING])) { + xaPtr->encoding = atoi(field[XLFD_ENCODING]); + } + + Tcl_DStringFree(&ds); + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * FieldSpecified -- + * + * Helper function for TkParseXLFD(). Determines if a field in the + * XLFD was set to a non-null, non-don't-care value. + * + * Results: + * The return value is 0 if the field in the XLFD was not set and + * should be ignored, non-zero otherwise. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +FieldSpecified(field) + CONST char *field; /* The field of the XLFD to check. Strictly + * speaking, only when the string is "*" does it mean + * don't-care. However, an unspecified or question + * mark is also interpreted as don't-care. */ +{ + char ch; + + if (field == NULL) { + return 0; + } + ch = field[0]; + return (ch != '*' && ch != '?'); +} + +/* + *--------------------------------------------------------------------------- + * + * NewChunk -- + * + * Helper function for Tk_ComputeTextLayout(). Encapsulates a + * measured set of characters in a chunk that can be quickly + * drawn. + * + * Results: + * A pointer to the new chunk in the text layout. + * + * Side effects: + * The text layout is reallocated to hold more chunks as necessary. + * + * Currently, Tk_ComputeTextLayout() stores contiguous ranges of + * "normal" characters in a chunk, along with individual tab + * and newline chars in their own chunks. All characters in the + * text layout are accounted for. + * + *--------------------------------------------------------------------------- + */ +static LayoutChunk * +NewChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y) + TextLayout **layoutPtrPtr; + int *maxPtr; + CONST char *start; + int numChars; + int curX; + int newX; + int y; +{ + TextLayout *layoutPtr; + LayoutChunk *chunkPtr; + int maxChunks; + size_t s; + + layoutPtr = *layoutPtrPtr; + maxChunks = *maxPtr; + if (layoutPtr->numChunks == maxChunks) { + maxChunks *= 2; + s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk)); + layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s); + + *layoutPtrPtr = layoutPtr; + *maxPtr = maxChunks; + } + chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks]; + chunkPtr->start = start; + chunkPtr->numChars = numChars; + chunkPtr->numDisplayChars = numChars; + chunkPtr->x = curX; + chunkPtr->y = y; + chunkPtr->totalWidth = newX - curX; + chunkPtr->displayWidth = newX - curX; + layoutPtr->numChunks++; + + return chunkPtr; +} + |