4319 lines
118 KiB
C
4319 lines
118 KiB
C
/*
|
||
* tkFont.c --
|
||
*
|
||
* This file maintains a database of fonts for the Tk toolkit. It also
|
||
* provides several utility functions for measuring and displaying text.
|
||
*
|
||
* Copyright (c) 1990-1994 The Regents of the University of California.
|
||
* Copyright (c) 1994-1998 Sun Microsystems, Inc.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution of
|
||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*/
|
||
|
||
#include "tkInt.h"
|
||
#include "tkFont.h"
|
||
#if defined(MAC_OSX_TK)
|
||
#include "tkMacOSXInt.h" /* Defines TK_DRAW_IN_CONTEXT */
|
||
#endif
|
||
|
||
/*
|
||
* 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 string font names, values are TkFont
|
||
* pointers. */
|
||
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 strings,
|
||
* values are NamedFont pointers. */
|
||
TkMainInfo *mainPtr; /* Application that owns this structure. */
|
||
int updatePending; /* Non-zero when a World Changed event has
|
||
* already been queued to handle a change to a
|
||
* named font. */
|
||
} TkFontInfo;
|
||
|
||
/*
|
||
* 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 numBytes; /* The number of bytes in this chunk. */
|
||
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[TKFLEXARRAY];/* 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 const TkStateMap weightMap[] = {
|
||
{TK_FW_NORMAL, "normal"},
|
||
{TK_FW_BOLD, "bold"},
|
||
{TK_FW_UNKNOWN, NULL}
|
||
};
|
||
|
||
static const TkStateMap slantMap[] = {
|
||
{TK_FS_ROMAN, "roman"},
|
||
{TK_FS_ITALIC, "italic"},
|
||
{TK_FS_UNKNOWN, NULL}
|
||
};
|
||
|
||
static const TkStateMap underlineMap[] = {
|
||
{1, "underline"},
|
||
{0, NULL}
|
||
};
|
||
|
||
static const TkStateMap overstrikeMap[] = {
|
||
{1, "overstrike"},
|
||
{0, NULL}
|
||
};
|
||
|
||
/*
|
||
* The following structures are used when parsing XLFD's into a set of
|
||
* TkFontAttributes.
|
||
*/
|
||
|
||
static const 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 const TkStateMap xlfdSlantMap[] = {
|
||
{TK_FS_ROMAN, "r"},
|
||
{TK_FS_ITALIC, "i"},
|
||
{TK_FS_OBLIQUE, "o"},
|
||
{TK_FS_ROMAN, NULL} /* Assume anything else is "roman". */
|
||
};
|
||
|
||
static const TkStateMap xlfdSetwidthMap[] = {
|
||
{TK_SW_NORMAL, "normal"},
|
||
{TK_SW_CONDENSE, "narrow"},
|
||
{TK_SW_CONDENSE, "semicondensed"},
|
||
{TK_SW_CONDENSE, "condensed"},
|
||
{TK_SW_UNKNOWN, NULL}
|
||
};
|
||
|
||
/*
|
||
* The following structure and defines specify the valid builtin options when
|
||
* configuring a set of font attributes.
|
||
*/
|
||
|
||
static const char *const 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
|
||
|
||
/*
|
||
* Hardcoded font aliases. These are used to describe (mostly) identical fonts
|
||
* whose names differ from platform to platform. If the user-supplied font
|
||
* name matches any of the names in one of the alias lists, the other names in
|
||
* the alias list are also automatically tried.
|
||
*/
|
||
|
||
static const char *const timesAliases[] = {
|
||
"Times", /* Unix. */
|
||
"Times New Roman", /* Windows. */
|
||
"New York", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const helveticaAliases[] = {
|
||
"Helvetica", /* Unix. */
|
||
"Arial", /* Windows. */
|
||
"Geneva", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const courierAliases[] = {
|
||
"Courier", /* Unix and Mac. */
|
||
"Courier New", /* Windows. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const minchoAliases[] = {
|
||
"mincho", /* Unix. */
|
||
"\357\274\255\357\274\263 \346\230\216\346\234\235",
|
||
/* Windows (MS mincho). */
|
||
"\346\234\254\346\230\216\346\234\235\342\210\222\357\274\255",
|
||
/* Mac (honmincho-M). */
|
||
NULL
|
||
};
|
||
|
||
static const char *const gothicAliases[] = {
|
||
"gothic", /* Unix. */
|
||
"\357\274\255\357\274\263 \343\202\264\343\202\267\343\203\203\343\202\257",
|
||
/* Windows (MS goshikku). */
|
||
"\344\270\270\343\202\264\343\202\267\343\203\203\343\202\257\342\210\222\357\274\255",
|
||
/* Mac (goshikku-M). */
|
||
NULL
|
||
};
|
||
|
||
static const char *const dingbatsAliases[] = {
|
||
"dingbats", "zapfdingbats", "itc zapfdingbats",
|
||
/* Unix. */
|
||
/* Windows. */
|
||
"zapf dingbats", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const *const fontAliases[] = {
|
||
timesAliases,
|
||
helveticaAliases,
|
||
courierAliases,
|
||
minchoAliases,
|
||
gothicAliases,
|
||
dingbatsAliases,
|
||
NULL
|
||
};
|
||
|
||
/*
|
||
* Hardcoded font classes. If the character cannot be found in the base font,
|
||
* the classes are examined in order to see if some other similar font should
|
||
* be examined also.
|
||
*/
|
||
|
||
static const char *const systemClass[] = {
|
||
"fixed", /* Unix. */
|
||
/* Windows. */
|
||
"chicago", "osaka", "sistemny",
|
||
/* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const serifClass[] = {
|
||
"times", "palatino", "mincho",
|
||
/* All platforms. */
|
||
"song ti", /* Unix. */
|
||
"ms serif", "simplified arabic",
|
||
/* Windows. */
|
||
"latinski", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const sansClass[] = {
|
||
"helvetica", "gothic", /* All platforms. */
|
||
/* Unix. */
|
||
"ms sans serif", "traditional arabic",
|
||
/* Windows. */
|
||
"bastion", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const monoClass[] = {
|
||
"courier", "gothic", /* All platforms. */
|
||
"fangsong ti", /* Unix. */
|
||
"simplified arabic fixed", /* Windows. */
|
||
"monaco", "pryamoy", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
static const char *const symbolClass[] = {
|
||
"symbol", "dingbats", "wingdings", NULL
|
||
};
|
||
|
||
static const char *const *const fontFallbacks[] = {
|
||
systemClass,
|
||
serifClass,
|
||
sansClass,
|
||
monoClass,
|
||
symbolClass,
|
||
NULL
|
||
};
|
||
|
||
/*
|
||
* Global fallbacks. If the character could not be found in the preferred
|
||
* fallback list, this list is examined. If the character still cannot be
|
||
* found, all font families in the system are examined.
|
||
*/
|
||
|
||
static const char *const globalFontClass[] = {
|
||
"symbol", /* All platforms. */
|
||
/* Unix. */
|
||
"lucida sans unicode", /* Windows. */
|
||
"bitstream cyberbit", /* Windows popular CJK font */
|
||
"chicago", /* Mac. */
|
||
NULL
|
||
};
|
||
|
||
#define GetFontAttributes(tkfont) \
|
||
((const TkFontAttributes *) &((TkFont *) (tkfont))->fa)
|
||
|
||
#define GetFontMetrics(tkfont) \
|
||
((const TkFontMetrics *) &((TkFont *) (tkfont))->fm)
|
||
|
||
|
||
static int ConfigAttributesObj(Tcl_Interp *interp,
|
||
Tk_Window tkwin, int objc, Tcl_Obj *const objv[],
|
||
TkFontAttributes *faPtr);
|
||
static void DupFontObjProc(Tcl_Obj *srcObjPtr, Tcl_Obj *dupObjPtr);
|
||
static int FieldSpecified(const char *field);
|
||
static void FreeFontObj(Tcl_Obj *objPtr);
|
||
static void FreeFontObjProc(Tcl_Obj *objPtr);
|
||
static int GetAttributeInfoObj(Tcl_Interp *interp,
|
||
const TkFontAttributes *faPtr, Tcl_Obj *objPtr);
|
||
static LayoutChunk * NewChunk(TextLayout **layoutPtrPtr, int *maxPtr,
|
||
const char *start, int numChars, int curX,
|
||
int newX, int y);
|
||
static int ParseFontNameObj(Tcl_Interp *interp, Tk_Window tkwin,
|
||
Tcl_Obj *objPtr, TkFontAttributes *faPtr);
|
||
static void RecomputeWidgets(TkWindow *winPtr);
|
||
static int SetFontFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
|
||
static void TheWorldHasChanged(ClientData clientData);
|
||
static void UpdateDependentFonts(TkFontInfo *fiPtr,
|
||
Tk_Window tkwin, Tcl_HashEntry *namedHashPtr);
|
||
|
||
/*
|
||
* The following structure defines the implementation of the "font" Tcl
|
||
* object, used for drawing. The internalRep.twoPtrValue.ptr1 field of each
|
||
* font object points to the TkFont structure for the font, or NULL.
|
||
*/
|
||
|
||
const Tcl_ObjType tkFontObjType = {
|
||
"font", /* name */
|
||
FreeFontObjProc, /* freeIntRepProc */
|
||
DupFontObjProc, /* dupIntRepProc */
|
||
NULL, /* updateStringProc */
|
||
SetFontFromAny /* setFromAnyProc */
|
||
};
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkFontPkgInit --
|
||
*
|
||
* This function 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:
|
||
* Stores a token in the mainPtr to hold information needed by this
|
||
* package on a per application basis.
|
||
*
|
||
* Side effects:
|
||
* Memory allocated.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkFontPkgInit(
|
||
TkMainInfo *mainPtr) /* The application being created. */
|
||
{
|
||
TkFontInfo *fiPtr = (TkFontInfo *)ckalloc(sizeof(TkFontInfo));
|
||
|
||
Tcl_InitHashTable(&fiPtr->fontCache, TCL_STRING_KEYS);
|
||
Tcl_InitHashTable(&fiPtr->namedTable, TCL_STRING_KEYS);
|
||
fiPtr->mainPtr = mainPtr;
|
||
fiPtr->updatePending = 0;
|
||
mainPtr->fontInfoPtr = fiPtr;
|
||
|
||
TkpFontPkgInit(mainPtr);
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkFontPkgFree --
|
||
*
|
||
* This function 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(
|
||
TkMainInfo *mainPtr) /* The application being deleted. */
|
||
{
|
||
TkFontInfo *fiPtr = mainPtr->fontInfoPtr;
|
||
Tcl_HashEntry *hPtr, *searchPtr;
|
||
Tcl_HashSearch search;
|
||
int fontsLeft = 0;
|
||
|
||
for (searchPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
|
||
searchPtr != NULL;
|
||
searchPtr = Tcl_NextHashEntry(&search)) {
|
||
fontsLeft++;
|
||
#ifdef DEBUG_FONTS
|
||
fprintf(stderr, "Font %s still in cache.\n",
|
||
(char *) Tcl_GetHashKey(&fiPtr->fontCache, searchPtr));
|
||
#endif
|
||
}
|
||
|
||
#ifdef PURIFY
|
||
if (fontsLeft) {
|
||
Tcl_Panic("TkFontPkgFree: all fonts should have been freed already");
|
||
}
|
||
#endif
|
||
|
||
Tcl_DeleteHashTable(&fiPtr->fontCache);
|
||
|
||
hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
|
||
while (hPtr != NULL) {
|
||
ckfree(Tcl_GetHashValue(hPtr));
|
||
hPtr = Tcl_NextHashEntry(&search);
|
||
}
|
||
Tcl_DeleteHashTable(&fiPtr->namedTable);
|
||
if (fiPtr->updatePending) {
|
||
Tcl_CancelIdleCall(TheWorldHasChanged, fiPtr);
|
||
}
|
||
ckfree(fiPtr);
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_FontObjCmd --
|
||
*
|
||
* This function 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 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 = (Tk_Window)clientData;
|
||
TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
|
||
static const char *const 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
|
||
};
|
||
|
||
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, n;
|
||
const char *s;
|
||
Tk_Font tkfont;
|
||
Tcl_Obj *optPtr, *charPtr, *resultPtr;
|
||
int uniChar = 0;
|
||
const TkFontAttributes *faPtr;
|
||
TkFontAttributes fa;
|
||
|
||
/*
|
||
* Params 0 and 1 are 'font actual'. Param 2 is the font name. 3-4 may
|
||
* be '-displayof $window'
|
||
*/
|
||
|
||
skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
|
||
if (skip < 0) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* Next parameter may be an option.
|
||
*/
|
||
|
||
n = skip + 3;
|
||
optPtr = NULL;
|
||
charPtr = NULL;
|
||
if (n < objc) {
|
||
s = Tcl_GetString(objv[n]);
|
||
if (s[0] == '-' && s[1] != '-') {
|
||
optPtr = objv[n];
|
||
n++;
|
||
} else {
|
||
optPtr = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Next parameter may be '--' to mark end of options.
|
||
*/
|
||
|
||
if (n < objc) {
|
||
if (!strcmp(Tcl_GetString(objv[n]), "--")) {
|
||
n++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Next parameter is the character to get font information for.
|
||
*/
|
||
|
||
if (n < objc) {
|
||
charPtr = objv[n];
|
||
n++;
|
||
}
|
||
|
||
/*
|
||
* If there were fewer than 3 args, or args remain, that's an error.
|
||
*/
|
||
|
||
if (objc < 3 || n < objc) {
|
||
Tcl_WrongNumArgs(interp, 2, objv,
|
||
"font ?-displayof window? ?option? ?--? ?char?");
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* The 'charPtr' arg must be a single Unicode.
|
||
*/
|
||
|
||
if (charPtr != NULL) {
|
||
const char *string = Tcl_GetString(charPtr);
|
||
int len = TkUtfToUniChar(string, &uniChar);
|
||
|
||
if (len != charPtr->length) {
|
||
resultPtr = Tcl_NewStringObj(
|
||
"expected a single character but got \"", -1);
|
||
Tcl_AppendLimitedToObj(resultPtr, string,
|
||
-1, 40, "...");
|
||
Tcl_AppendToObj(resultPtr, "\"", -1);
|
||
Tcl_SetObjResult(interp, resultPtr);
|
||
Tcl_SetErrorCode(interp, "TK", "VALUE", "FONT_SAMPLE", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Find the font.
|
||
*/
|
||
|
||
tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
|
||
if (tkfont == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* Determine the font attributes.
|
||
*/
|
||
|
||
if (charPtr == NULL) {
|
||
faPtr = GetFontAttributes(tkfont);
|
||
} else {
|
||
TkpGetFontAttrsForChar(tkwin, tkfont, uniChar, &fa);
|
||
faPtr = &fa;
|
||
}
|
||
result = GetAttributeInfoObj(interp, faPtr, optPtr);
|
||
|
||
Tk_FreeFont(tkfont);
|
||
return result;
|
||
}
|
||
case FONT_CONFIGURE: {
|
||
int result;
|
||
const char *string;
|
||
Tcl_Obj *objPtr;
|
||
NamedFont *nfPtr;
|
||
Tcl_HashEntry *namedHashPtr;
|
||
|
||
if (objc < 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "fontname ?-option value ...?");
|
||
return TCL_ERROR;
|
||
}
|
||
string = Tcl_GetString(objv[2]);
|
||
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
|
||
nfPtr = NULL;
|
||
if (namedHashPtr != NULL) {
|
||
nfPtr = (NamedFont *)Tcl_GetHashValue(namedHashPtr);
|
||
}
|
||
if ((namedHashPtr == NULL) || nfPtr->deletePending) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"named font \"%s\" doesn't exist", string));
|
||
Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT", string, 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);
|
||
UpdateDependentFonts(fiPtr, tkwin, namedHashPtr);
|
||
return result;
|
||
}
|
||
return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
|
||
}
|
||
case FONT_CREATE: {
|
||
int skip = 3, i;
|
||
const char *name;
|
||
char buf[16 + TCL_INTEGER_SPACE];
|
||
TkFontAttributes fa;
|
||
Tcl_HashEntry *namedHashPtr;
|
||
|
||
if (objc < 3) {
|
||
name = NULL;
|
||
} else {
|
||
name = Tcl_GetString(objv[2]);
|
||
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, 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_SetObjResult(interp, Tcl_NewStringObj(name, -1));
|
||
break;
|
||
}
|
||
case FONT_DELETE: {
|
||
int i, result = TCL_OK;
|
||
const char *string;
|
||
|
||
/*
|
||
* 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) && (result == TCL_OK); i++) {
|
||
string = Tcl_GetString(objv[i]);
|
||
result = TkDeleteNamedFont(interp, tkwin, string);
|
||
}
|
||
return result;
|
||
}
|
||
case FONT_FAMILIES: {
|
||
int 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: {
|
||
const char *string;
|
||
Tk_Font tkfont;
|
||
int length = 0, skip = 0;
|
||
|
||
if (objc > 4) {
|
||
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_AllocFontFromObj(interp, tkwin, objv[2]);
|
||
if (tkfont == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
string = Tcl_GetStringFromObj(objv[3 + skip], &length);
|
||
Tcl_SetObjResult(interp, Tcl_NewIntObj(
|
||
Tk_TextWidth(tkfont, string, length)));
|
||
Tk_FreeFont(tkfont);
|
||
break;
|
||
}
|
||
case FONT_METRICS: {
|
||
Tk_Font tkfont;
|
||
int skip, i;
|
||
const TkFontMetrics *fmPtr;
|
||
static const char *const 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_AllocFontFromObj(interp, tkwin, objv[2]);
|
||
if (tkfont == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
objc -= skip;
|
||
objv += skip;
|
||
fmPtr = GetFontMetrics(tkfont);
|
||
if (objc == 3) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"-ascent %d -descent %d -linespace %d -fixed %d",
|
||
fmPtr->ascent, fmPtr->descent,
|
||
fmPtr->ascent + fmPtr->descent, fmPtr->fixed));
|
||
} 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_SetObjResult(interp, Tcl_NewIntObj(i));
|
||
}
|
||
Tk_FreeFont(tkfont);
|
||
break;
|
||
}
|
||
case FONT_NAMES: {
|
||
Tcl_HashSearch search;
|
||
Tcl_HashEntry *namedHashPtr;
|
||
Tcl_Obj *resultPtr;
|
||
|
||
if (objc != 2) {
|
||
Tcl_WrongNumArgs(interp, 1, objv, "names");
|
||
return TCL_ERROR;
|
||
}
|
||
resultPtr = Tcl_NewObj();
|
||
namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
|
||
while (namedHashPtr != NULL) {
|
||
NamedFont *nfPtr = (NamedFont *)Tcl_GetHashValue(namedHashPtr);
|
||
|
||
if (!nfPtr->deletePending) {
|
||
char *string = (char *)Tcl_GetHashKey(&fiPtr->namedTable,
|
||
namedHashPtr);
|
||
|
||
Tcl_ListObjAppendElement(NULL, resultPtr,
|
||
Tcl_NewStringObj(string, -1));
|
||
}
|
||
namedHashPtr = Tcl_NextHashEntry(&search);
|
||
}
|
||
Tcl_SetObjResult(interp, resultPtr);
|
||
break;
|
||
}
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* UpdateDependentFonts, 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
|
||
UpdateDependentFonts(
|
||
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 = (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) {
|
||
for (fontPtr = (TkFont *)Tcl_GetHashValue(cacheHashPtr);
|
||
fontPtr != NULL; fontPtr = fontPtr->nextPtr) {
|
||
if (fontPtr->namedHashPtr == namedHashPtr) {
|
||
TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
|
||
if (!fiPtr->updatePending) {
|
||
fiPtr->updatePending = 1;
|
||
Tcl_DoWhenIdle(TheWorldHasChanged, fiPtr);
|
||
}
|
||
}
|
||
}
|
||
cacheHashPtr = Tcl_NextHashEntry(&search);
|
||
}
|
||
}
|
||
|
||
static void
|
||
TheWorldHasChanged(
|
||
ClientData clientData) /* Info about application's fonts. */
|
||
{
|
||
TkFontInfo *fiPtr = (TkFontInfo *)clientData;
|
||
|
||
/*
|
||
* On macOS it is catastrophic to recompute all widgets while the
|
||
* [NSView drawRect] method is drawing. The best that we can do in
|
||
* that situation is to abort the recomputation and hope for the best.
|
||
* This is ignored on other platforms.
|
||
*/
|
||
|
||
if (TkpWillDrawWidget(NULL)) {
|
||
return;
|
||
}
|
||
|
||
fiPtr->updatePending = 0;
|
||
RecomputeWidgets(fiPtr->mainPtr->winPtr);
|
||
}
|
||
|
||
static void
|
||
RecomputeWidgets(
|
||
TkWindow *winPtr) /* Window to which command is sent. */
|
||
{
|
||
Tk_ClassWorldChangedProc *proc =
|
||
Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc);
|
||
TkWindow *tkwinPtr;
|
||
|
||
if (proc != NULL) {
|
||
proc(winPtr->instanceData);
|
||
}
|
||
|
||
/*
|
||
* Notify all the descendants of this window that the world has changed.
|
||
*
|
||
* This could be done recursively or iteratively. The recursive version is
|
||
* easier to implement and understand, and typically, windows with a -font
|
||
* option will be leaf nodes in the widget heirarchy (buttons, labels,
|
||
* etc.), so the recursion depth will be shallow.
|
||
*
|
||
* However, the additional overhead of the recursive calls may become a
|
||
* performance problem if typical usage alters such that -font'ed widgets
|
||
* appear high in the hierarchy, causing deep recursion. This could happen
|
||
* with text widgets, or more likely with the labelframe
|
||
* widget. With these widgets it is possible, even likely, that a
|
||
* -font'ed widget (text or labelframe) will not be a leaf node, but
|
||
* will instead have many descendants. If this is ever found to cause a
|
||
* performance problem, it may be worth investigating an iterative version
|
||
* of the code below.
|
||
*/
|
||
|
||
for (tkwinPtr=winPtr->childList ; tkwinPtr!=NULL ; tkwinPtr=tkwinPtr->nextPtr) {
|
||
RecomputeWidgets(tkwinPtr);
|
||
}
|
||
|
||
/*
|
||
* Broadcast font change virtually for mega-widget layout managers.
|
||
* Do this after the font change has been propagated to core widgets.
|
||
*/
|
||
TkSendVirtualEvent((Tk_Window)winPtr, "TkWorldChanged",
|
||
Tcl_NewStringObj("FontChanged",-1));
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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 the interp's 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(
|
||
Tcl_Interp *interp, /* Interp for error return (can be NULL). */
|
||
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 = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
|
||
Tcl_HashEntry *namedHashPtr;
|
||
int isNew;
|
||
NamedFont *nfPtr;
|
||
|
||
namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &isNew);
|
||
if (!isNew) {
|
||
nfPtr = (NamedFont *)Tcl_GetHashValue(namedHashPtr);
|
||
if (!nfPtr->deletePending) {
|
||
if (interp) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"named font \"%s\" already exists", name));
|
||
Tcl_SetErrorCode(interp, "TK", "FONT", "EXISTS", 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;
|
||
UpdateDependentFonts(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;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkDeleteNamedFont --
|
||
*
|
||
* Delete the named font. If there are still widgets using this font,
|
||
* then it isn't deleted right away.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkDeleteNamedFont(
|
||
Tcl_Interp *interp, /* Interp for error return (can be NULL). */
|
||
Tk_Window tkwin, /* A window associated with interp. */
|
||
const char *name) /* Name for the new named font. */
|
||
{
|
||
TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
|
||
NamedFont *nfPtr;
|
||
Tcl_HashEntry *namedHashPtr;
|
||
|
||
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, name);
|
||
if (namedHashPtr == NULL) {
|
||
if (interp) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"named font \"%s\" doesn't exist", name));
|
||
Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT", name, NULL);
|
||
}
|
||
return TCL_ERROR;
|
||
}
|
||
nfPtr = (NamedFont *)Tcl_GetHashValue(namedHashPtr);
|
||
if (nfPtr->refCount != 0) {
|
||
nfPtr->deletePending = 1;
|
||
} else {
|
||
Tcl_DeleteHashEntry(namedHashPtr);
|
||
ckfree(nfPtr);
|
||
}
|
||
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 the interp's result.
|
||
*
|
||
* Side effects:
|
||
* The font is added to an internal database with a reference count. For
|
||
* each call to this function, there should eventually be a call to
|
||
* Tk_FreeFont() or Tk_FreeFontFromObj() so that the database is cleaned
|
||
* up when fonts aren't in use anymore.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
Tk_Font
|
||
Tk_GetFont(
|
||
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. */
|
||
{
|
||
Tk_Font tkfont;
|
||
Tcl_Obj *strPtr;
|
||
|
||
strPtr = Tcl_NewStringObj(string, -1);
|
||
Tcl_IncrRefCount(strPtr);
|
||
tkfont = Tk_AllocFontFromObj(interp, tkwin, strPtr);
|
||
Tcl_DecrRefCount(strPtr);
|
||
return tkfont;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_AllocFontFromObj --
|
||
*
|
||
* 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 function, there should eventually be a call to
|
||
* Tk_FreeFont() or Tk_FreeFontFromObj() so that the database is cleaned
|
||
* up when fonts aren't in use anymore.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
Tk_Font
|
||
Tk_AllocFontFromObj(
|
||
Tcl_Interp *interp, /* Interp for database and error return. */
|
||
Tk_Window tkwin, /* For screen on which font will be used. */
|
||
Tcl_Obj *objPtr) /* Object describing font, as: named font,
|
||
* native format, or parseable string. */
|
||
{
|
||
TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
|
||
Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
|
||
TkFont *fontPtr, *firstFontPtr, *oldFontPtr;
|
||
int isNew, descent;
|
||
NamedFont *nfPtr;
|
||
|
||
if (objPtr->typePtr != &tkFontObjType
|
||
|| objPtr->internalRep.twoPtrValue.ptr2 != fiPtr) {
|
||
SetFontFromAny(interp, objPtr);
|
||
}
|
||
|
||
oldFontPtr = (TkFont *)objPtr->internalRep.twoPtrValue.ptr1;
|
||
if (oldFontPtr != NULL) {
|
||
if (oldFontPtr->resourceRefCount == 0) {
|
||
/*
|
||
* This is a stale reference: it refers to a TkFont that's no
|
||
* longer in use. Clear the reference.
|
||
*/
|
||
|
||
FreeFontObj(objPtr);
|
||
oldFontPtr = NULL;
|
||
} else if (Tk_Screen(tkwin) == oldFontPtr->screen) {
|
||
oldFontPtr->resourceRefCount++;
|
||
return (Tk_Font) oldFontPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Next, search the list of fonts that have the name we want, to see if
|
||
* one of them is for the right screen.
|
||
*/
|
||
|
||
isNew = 0;
|
||
if (oldFontPtr != NULL) {
|
||
cacheHashPtr = oldFontPtr->cacheHashPtr;
|
||
FreeFontObj(objPtr);
|
||
} else {
|
||
cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache,
|
||
Tcl_GetString(objPtr), &isNew);
|
||
}
|
||
firstFontPtr = (TkFont *)Tcl_GetHashValue(cacheHashPtr);
|
||
for (fontPtr = firstFontPtr; (fontPtr != NULL);
|
||
fontPtr = fontPtr->nextPtr) {
|
||
if (Tk_Screen(tkwin) == fontPtr->screen) {
|
||
fontPtr->resourceRefCount++;
|
||
fontPtr->objRefCount++;
|
||
objPtr->internalRep.twoPtrValue.ptr1 = fontPtr;
|
||
objPtr->internalRep.twoPtrValue.ptr2 = fiPtr;
|
||
return (Tk_Font) fontPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* The desired font isn't in the table. Make a new one.
|
||
*/
|
||
|
||
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
|
||
Tcl_GetString(objPtr));
|
||
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, Tcl_GetString(objPtr));
|
||
if (fontPtr == NULL) {
|
||
TkFontAttributes fa;
|
||
Tcl_Obj *dupObjPtr = Tcl_DuplicateObj(objPtr);
|
||
|
||
if (ParseFontNameObj(interp, tkwin, dupObjPtr, &fa) != TCL_OK) {
|
||
if (isNew) {
|
||
Tcl_DeleteHashEntry(cacheHashPtr);
|
||
}
|
||
Tcl_DecrRefCount(dupObjPtr);
|
||
return NULL;
|
||
}
|
||
Tcl_DecrRefCount(dupObjPtr);
|
||
|
||
/*
|
||
* String contained the attributes inline.
|
||
*/
|
||
|
||
fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Detect the system font engine going wrong and fail more gracefully.
|
||
*/
|
||
|
||
if (fontPtr == NULL) {
|
||
if (isNew) {
|
||
Tcl_DeleteHashEntry(cacheHashPtr);
|
||
}
|
||
Tcl_SetObjResult(interp, Tcl_NewStringObj(
|
||
"failed to allocate font due to internal system font engine"
|
||
" problem", -1));
|
||
Tcl_SetErrorCode(interp, "TK", "FONT", "INTERNAL_PROBLEM", NULL);
|
||
return NULL;
|
||
}
|
||
|
||
fontPtr->resourceRefCount = 1;
|
||
fontPtr->objRefCount = 1;
|
||
fontPtr->cacheHashPtr = cacheHashPtr;
|
||
fontPtr->namedHashPtr = namedHashPtr;
|
||
fontPtr->screen = Tk_Screen(tkwin);
|
||
fontPtr->nextPtr = firstFontPtr;
|
||
Tcl_SetHashValue(cacheHashPtr, fontPtr);
|
||
|
||
Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, -1, 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 = (int) (TkFontGetPixels(tkwin, fontPtr->fa.size) / 10 + 0.5);
|
||
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;
|
||
}
|
||
}
|
||
|
||
objPtr->internalRep.twoPtrValue.ptr1 = fontPtr;
|
||
objPtr->internalRep.twoPtrValue.ptr2 = fiPtr;
|
||
return (Tk_Font) fontPtr;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tk_GetFontFromObj --
|
||
*
|
||
* Find the font that corresponds to a given object. The font must have
|
||
* already been created by Tk_GetFont or Tk_AllocFontFromObj.
|
||
*
|
||
* Results:
|
||
* The return value is a token for the font that matches objPtr and is
|
||
* suitable for use in tkwin.
|
||
*
|
||
* Side effects:
|
||
* If the object is not already a font ref, the conversion will free any
|
||
* old internal representation.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
Tk_Font
|
||
Tk_GetFontFromObj(
|
||
Tk_Window tkwin, /* The window that the font will be used
|
||
* in. */
|
||
Tcl_Obj *objPtr) /* The object from which to get the font. */
|
||
{
|
||
TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
|
||
TkFont *fontPtr;
|
||
Tcl_HashEntry *hashPtr;
|
||
|
||
if (objPtr->typePtr != &tkFontObjType
|
||
|| objPtr->internalRep.twoPtrValue.ptr2 != fiPtr) {
|
||
SetFontFromAny(NULL, objPtr);
|
||
}
|
||
|
||
fontPtr = (TkFont *)objPtr->internalRep.twoPtrValue.ptr1;
|
||
if (fontPtr != NULL) {
|
||
if (fontPtr->resourceRefCount == 0) {
|
||
/*
|
||
* This is a stale reference: it refers to a TkFont that's no
|
||
* longer in use. Clear the reference.
|
||
*/
|
||
|
||
FreeFontObj(objPtr);
|
||
fontPtr = NULL;
|
||
} else if (Tk_Screen(tkwin) == fontPtr->screen) {
|
||
return (Tk_Font) fontPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Next, search the list of fonts that have the name we want, to see if
|
||
* one of them is for the right screen.
|
||
*/
|
||
|
||
if (fontPtr != NULL) {
|
||
hashPtr = fontPtr->cacheHashPtr;
|
||
FreeFontObj(objPtr);
|
||
} else {
|
||
hashPtr = Tcl_FindHashEntry(&fiPtr->fontCache, Tcl_GetString(objPtr));
|
||
}
|
||
if (hashPtr != NULL) {
|
||
for (fontPtr = (TkFont *)Tcl_GetHashValue(hashPtr); fontPtr != NULL;
|
||
fontPtr = fontPtr->nextPtr) {
|
||
if (Tk_Screen(tkwin) == fontPtr->screen) {
|
||
fontPtr->objRefCount++;
|
||
objPtr->internalRep.twoPtrValue.ptr1 = fontPtr;
|
||
objPtr->internalRep.twoPtrValue.ptr2 = fiPtr;
|
||
return (Tk_Font) fontPtr;
|
||
}
|
||
}
|
||
}
|
||
|
||
Tcl_Panic("Tk_GetFontFromObj called with non-existent font!");
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* SetFontFromAny --
|
||
*
|
||
* Convert the internal representation of a Tcl object to the font
|
||
* internal form.
|
||
*
|
||
* Results:
|
||
* Always returns TCL_OK.
|
||
*
|
||
* Side effects:
|
||
* The object is left with its typePtr pointing to tkFontObjType. The
|
||
* TkFont pointer is NULL.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
SetFontFromAny(
|
||
TCL_UNUSED(Tcl_Interp *), /* Used for error reporting if not NULL. */
|
||
Tcl_Obj *objPtr) /* The object to convert. */
|
||
{
|
||
const Tcl_ObjType *typePtr;
|
||
|
||
/*
|
||
* Free the old internalRep before setting the new one.
|
||
*/
|
||
|
||
Tcl_GetString(objPtr);
|
||
typePtr = objPtr->typePtr;
|
||
if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
|
||
typePtr->freeIntRepProc(objPtr);
|
||
}
|
||
objPtr->typePtr = &tkFontObjType;
|
||
objPtr->internalRep.twoPtrValue.ptr1 = NULL;
|
||
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
|
||
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
const char *
|
||
Tk_NameOfFont(
|
||
Tk_Font tkfont) /* Font whose name is desired. */
|
||
{
|
||
TkFont *fontPtr = (TkFont *) tkfont;
|
||
|
||
return fontPtr->cacheHashPtr->key.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(
|
||
Tk_Font tkfont) /* Font to be released. */
|
||
{
|
||
TkFont *fontPtr = (TkFont *) tkfont, *prevPtr;
|
||
NamedFont *nfPtr;
|
||
|
||
if (fontPtr == NULL) {
|
||
return;
|
||
}
|
||
if (fontPtr->resourceRefCount-- > 1) {
|
||
return;
|
||
}
|
||
if (fontPtr->namedHashPtr != NULL) {
|
||
/*
|
||
* This font derived from a named font. Reduce the reference count on
|
||
* the named font and free it if no-one else is using it.
|
||
*/
|
||
|
||
nfPtr = (NamedFont *)Tcl_GetHashValue(fontPtr->namedHashPtr);
|
||
nfPtr->refCount--;
|
||
if ((nfPtr->refCount == 0) && nfPtr->deletePending) {
|
||
Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
|
||
ckfree(nfPtr);
|
||
}
|
||
}
|
||
|
||
prevPtr = (TkFont *)Tcl_GetHashValue(fontPtr->cacheHashPtr);
|
||
if (prevPtr == fontPtr) {
|
||
if (fontPtr->nextPtr == NULL) {
|
||
Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
|
||
} else {
|
||
Tcl_SetHashValue(fontPtr->cacheHashPtr, fontPtr->nextPtr);
|
||
}
|
||
} else {
|
||
while (prevPtr->nextPtr != fontPtr) {
|
||
prevPtr = prevPtr->nextPtr;
|
||
}
|
||
prevPtr->nextPtr = fontPtr->nextPtr;
|
||
}
|
||
|
||
TkpDeleteFont(fontPtr);
|
||
if (fontPtr->objRefCount == 0) {
|
||
ckfree(fontPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_FreeFontFromObj --
|
||
*
|
||
* Called to release a font inside a Tcl_Obj *. Decrements the refCount
|
||
* of the font and removes it from the hash tables if necessary.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The reference count associated with font is decremented, and only
|
||
* deallocated when no one is using it.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
Tk_FreeFontFromObj(
|
||
Tk_Window tkwin, /* The window this font lives in. Needed for
|
||
* the screen value. */
|
||
Tcl_Obj *objPtr) /* The Tcl_Obj * to be freed. */
|
||
{
|
||
Tk_FreeFont(Tk_GetFontFromObj(tkwin, objPtr));
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* FreeFontObjProc, FreeFontObj --
|
||
*
|
||
* This proc is called to release an object reference to a font. Called
|
||
* when the object's internal rep is released or when the cached fontPtr
|
||
* needs to be changed.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The object reference count is decremented. When both it and the hash
|
||
* ref count go to zero, the font's resources are released.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
FreeFontObjProc(
|
||
Tcl_Obj *objPtr) /* The object we are releasing. */
|
||
{
|
||
FreeFontObj(objPtr);
|
||
objPtr->typePtr = NULL;
|
||
}
|
||
|
||
static void
|
||
FreeFontObj(
|
||
Tcl_Obj *objPtr) /* The object we are releasing. */
|
||
{
|
||
TkFont *fontPtr = (TkFont *)objPtr->internalRep.twoPtrValue.ptr1;
|
||
|
||
if (fontPtr != NULL) {
|
||
fontPtr->objRefCount--;
|
||
if ((fontPtr->resourceRefCount == 0) && (fontPtr->objRefCount == 0)) {
|
||
ckfree(fontPtr);
|
||
}
|
||
objPtr->internalRep.twoPtrValue.ptr1 = NULL;
|
||
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* DupFontObjProc --
|
||
*
|
||
* When a cached font object is duplicated, this is called to update the
|
||
* internal reps.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The font's objRefCount is incremented and the internal rep of the copy
|
||
* is set to point to it.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
DupFontObjProc(
|
||
Tcl_Obj *srcObjPtr, /* The object we are copying from. */
|
||
Tcl_Obj *dupObjPtr) /* The object we are copying to. */
|
||
{
|
||
TkFont *fontPtr = (TkFont *)srcObjPtr->internalRep.twoPtrValue.ptr1;
|
||
|
||
dupObjPtr->typePtr = srcObjPtr->typePtr;
|
||
dupObjPtr->internalRep.twoPtrValue.ptr1 = fontPtr;
|
||
dupObjPtr->internalRep.twoPtrValue.ptr2
|
||
= srcObjPtr->internalRep.twoPtrValue.ptr2;
|
||
|
||
if (fontPtr != NULL) {
|
||
fontPtr->objRefCount++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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 function would go away if the XGCValues structure were
|
||
* replaced with a TkGCValues structure.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
Font
|
||
Tk_FontId(
|
||
Tk_Font tkfont) /* Font that is going to be selected into
|
||
* GC. */
|
||
{
|
||
TkFont *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(
|
||
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 = (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(
|
||
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 = (TkFont *) tkfont;
|
||
Tk_Uid family, weightString, slantString;
|
||
char *src, *dest;
|
||
int upper, len;
|
||
|
||
len = Tcl_DStringLength(dsPtr);
|
||
|
||
/*
|
||
* 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 {
|
||
int ch;
|
||
|
||
/*
|
||
* 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'; ) {
|
||
while (isspace(UCHAR(*src))) { /* INTL: ISO space */
|
||
src++;
|
||
upper = 1;
|
||
}
|
||
src += TkUtfToUniChar(src, &ch);
|
||
if (ch <= 0xffff) {
|
||
if (upper) {
|
||
ch = Tcl_UniCharToUpper(ch);
|
||
upper = 0;
|
||
} else {
|
||
ch = Tcl_UniCharToLower(ch);
|
||
}
|
||
} else {
|
||
upper = 0;
|
||
}
|
||
dest += TkUniCharToUtf(ch, dest);
|
||
}
|
||
*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) {
|
||
/* Do nothing */
|
||
} 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 (int)(fontPtr->fa.size + 0.5);
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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(
|
||
Tk_Font tkfont, /* Font in which text will be measured. */
|
||
const char *string, /* String whose width will be computed. */
|
||
int numBytes) /* Number of bytes to consider from string, or
|
||
* < 0 for strlen(). */
|
||
{
|
||
int width;
|
||
|
||
if (numBytes < 0) {
|
||
numBytes = strlen(string);
|
||
}
|
||
Tk_MeasureChars(tkfont, string, numBytes, -1, 0, &width);
|
||
return width;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_UnderlineChars, TkUnderlineCharsInContext --
|
||
*
|
||
* These procedures draw an underline for a given range of characters in
|
||
* a given string. They don't draw the characters (which are assumed to
|
||
* have been displayed previously); they just draw the underline. These
|
||
* procedures 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 *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, int y, /* Coordinates at which first character of
|
||
* string is drawn. */
|
||
int firstByte, /* Index of first byte of first character. */
|
||
int lastByte) /* Index of first byte after the last
|
||
* character. */
|
||
{
|
||
TkUnderlineCharsInContext(display, drawable, gc, tkfont, string,
|
||
lastByte, x, y, firstByte, lastByte);
|
||
}
|
||
|
||
void
|
||
TkUnderlineCharsInContext(
|
||
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 numBytes, /* Number of bytes in string. */
|
||
int x, int y, /* Coordinates at which the first character of
|
||
* the whole string would be drawn. */
|
||
int firstByte, /* Index of first byte of first character. */
|
||
int lastByte) /* Index of first byte after the last
|
||
* character. */
|
||
{
|
||
TkFont *fontPtr = (TkFont *) tkfont;
|
||
int startX, endX;
|
||
|
||
TkpMeasureCharsInContext(tkfont, string, numBytes, 0, firstByte, -1, 0,
|
||
&startX);
|
||
TkpMeasureCharsInContext(tkfont, string, numBytes, 0, lastByte, -1, 0,
|
||
&endX);
|
||
|
||
XFillRectangle(display, drawable, gc, x + startX,
|
||
y + fontPtr->underlinePos, (unsigned) (endX - startX),
|
||
(unsigned) 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() function to
|
||
* display the text quickly (without remeasuring it).
|
||
*
|
||
* This function 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(
|
||
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 = (TkFont *) tkfont;
|
||
const char *start, *endp, *special;
|
||
int n, y, bytesThisChunk, maxChunks, curLine, layoutHeight;
|
||
int baseline, height, curX, newX, maxWidth, *lineLengths;
|
||
TextLayout *layoutPtr;
|
||
LayoutChunk *chunkPtr;
|
||
const TkFontMetrics *fmPtr;
|
||
Tcl_DString lineBuffer;
|
||
|
||
Tcl_DStringInit(&lineBuffer);
|
||
|
||
if ((fontPtr == NULL) || (string == NULL)) {
|
||
if (widthPtr != NULL) {
|
||
*widthPtr = 0;
|
||
}
|
||
if (heightPtr != NULL) {
|
||
*heightPtr = 0;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
fmPtr = &fontPtr->fm;
|
||
|
||
height = fmPtr->ascent + fmPtr->descent;
|
||
|
||
if (numChars < 0) {
|
||
numChars = Tcl_NumUtfChars(string, -1);
|
||
}
|
||
if (wrapLength == 0) {
|
||
wrapLength = -1;
|
||
}
|
||
|
||
maxChunks = 1;
|
||
|
||
layoutPtr = (TextLayout *)ckalloc(Tk_Offset(TextLayout, chunks)
|
||
+ maxChunks * 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;
|
||
|
||
endp = TkUtfAtIndex(string, numChars);
|
||
special = string;
|
||
|
||
flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
|
||
flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;
|
||
for (start = string; start < endp; ) {
|
||
if (start >= special) {
|
||
/*
|
||
* Find the next special character in the string.
|
||
*
|
||
* INTL: Note that it is safe to increment by byte, because we are
|
||
* looking for 7-bit characters that will appear unchanged in
|
||
* UTF-8. At some point we may need to support the full Unicode
|
||
* whitespace set.
|
||
*/
|
||
|
||
for (special = start; special < endp; 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) {
|
||
bytesThisChunk = Tk_MeasureChars(tkfont, start, special - start,
|
||
wrapLength - curX, flags, &newX);
|
||
newX += curX;
|
||
flags &= ~TK_AT_LEAST_ONE;
|
||
if (bytesThisChunk > 0) {
|
||
chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
|
||
bytesThisChunk, curX, newX, baseline);
|
||
|
||
start += bytesThisChunk;
|
||
curX = newX;
|
||
}
|
||
}
|
||
|
||
if ((start == special) && (special < endp)) {
|
||
/*
|
||
* Handle the special character.
|
||
*
|
||
* INTL: Special will be pointing at a 7-bit character so we can
|
||
* safely treat it as a single byte.
|
||
*/
|
||
|
||
chunkPtr = NULL;
|
||
if (*special == '\t') {
|
||
newX = curX + fontPtr->tabWidth;
|
||
newX -= newX % fontPtr->tabWidth;
|
||
NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
|
||
baseline)->numDisplayChars = -1;
|
||
start++;
|
||
curX = newX;
|
||
flags &= ~TK_AT_LEAST_ONE;
|
||
if ((start < endp) &&
|
||
((wrapLength <= 0) || (newX <= wrapLength))) {
|
||
/*
|
||
* More chars can still fit on this line.
|
||
*/
|
||
|
||
continue;
|
||
}
|
||
} else {
|
||
NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
|
||
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 < endp) && isspace(UCHAR(*start))) { /* INTL: ISO space */
|
||
if (!(flags & TK_IGNORE_NEWLINES)) {
|
||
if ((*start == '\n') || (*start == '\r')) {
|
||
break;
|
||
}
|
||
}
|
||
if (!(flags & TK_IGNORE_TABS)) {
|
||
if (*start == '\t') {
|
||
break;
|
||
}
|
||
}
|
||
start++;
|
||
}
|
||
if (chunkPtr != NULL) {
|
||
const char *end;
|
||
|
||
/*
|
||
* Append all the extra spaces on this line to the end of the last
|
||
* text chunk. This is a little tricky because we are switching
|
||
* back and forth between characters and bytes.
|
||
*/
|
||
|
||
end = chunkPtr->start + chunkPtr->numBytes;
|
||
bytesThisChunk = start - end;
|
||
if (bytesThisChunk > 0) {
|
||
bytesThisChunk = Tk_MeasureChars(tkfont, end, bytesThisChunk,
|
||
-1, 0, &chunkPtr->totalWidth);
|
||
chunkPtr->numBytes += bytesThisChunk;
|
||
chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
|
||
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.
|
||
*/
|
||
|
||
Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
|
||
|
||
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)) {
|
||
if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
|
||
chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
|
||
curX, baseline);
|
||
chunkPtr->numDisplayChars = -1;
|
||
Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
|
||
baseline += height;
|
||
}
|
||
}
|
||
|
||
layoutPtr->width = maxWidth;
|
||
layoutHeight = baseline - fmPtr->ascent;
|
||
if (layoutPtr->numChunks == 0) {
|
||
layoutHeight = height;
|
||
|
||
/*
|
||
* This fake chunk is used by the other functions 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].numBytes = 0;
|
||
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;
|
||
} else {
|
||
/*
|
||
* Using maximum line length, shift all the chunks so that the lines
|
||
* are all justified correctly.
|
||
*/
|
||
|
||
curLine = 0;
|
||
chunkPtr = layoutPtr->chunks;
|
||
y = chunkPtr->y;
|
||
lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
|
||
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++;
|
||
}
|
||
}
|
||
|
||
if (widthPtr != NULL) {
|
||
*widthPtr = layoutPtr->width;
|
||
}
|
||
if (heightPtr != NULL) {
|
||
*heightPtr = layoutHeight;
|
||
}
|
||
Tcl_DStringFree(&lineBuffer);
|
||
|
||
return (Tk_TextLayout) layoutPtr;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_FreeTextLayout --
|
||
*
|
||
* This function 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(
|
||
Tk_TextLayout textLayout) /* The text layout to be released. */
|
||
{
|
||
TextLayout *layoutPtr = (TextLayout *) textLayout;
|
||
|
||
if (layoutPtr != NULL) {
|
||
ckfree(layoutPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* Tk_DrawTextLayout --
|
||
*
|
||
* Use the information in the Tk_TextLayout token to display a
|
||
* multi-line, justified string of text.
|
||
*
|
||
* This function 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 *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, int 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 specifies 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. */
|
||
{
|
||
#if 0
|
||
/* Use TkDrawAngledTextLayout() implementation - testing purposes at this point */
|
||
TkDrawAngledTextLayout(display, drawable, gc, layout, x, y, 0.0, firstChar, lastChar);
|
||
#else
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
int i, numDisplayChars, drawX;
|
||
const char *firstByte, *lastByte;
|
||
LayoutChunk *chunkPtr;
|
||
|
||
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;
|
||
firstByte = chunkPtr->start;
|
||
} else {
|
||
firstByte = TkUtfAtIndex(chunkPtr->start, firstChar);
|
||
Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
|
||
firstByte - chunkPtr->start, -1, 0, &drawX);
|
||
}
|
||
if (lastChar < numDisplayChars) {
|
||
numDisplayChars = lastChar;
|
||
}
|
||
lastByte = TkUtfAtIndex(chunkPtr->start, numDisplayChars);
|
||
#if TK_DRAW_IN_CONTEXT
|
||
TkpDrawCharsInContext(display, drawable, gc, layoutPtr->tkfont,
|
||
chunkPtr->start, chunkPtr->numBytes,
|
||
firstByte - chunkPtr->start, lastByte - firstByte,
|
||
x+chunkPtr->x, y+chunkPtr->y);
|
||
#else /* !TK_DRAW_IN_CONTEXT */
|
||
Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, firstByte,
|
||
lastByte - firstByte, x+chunkPtr->x+drawX, y+chunkPtr->y);
|
||
#endif /* TK_DRAW_IN_CONTEXT */
|
||
}
|
||
firstChar -= chunkPtr->numChars;
|
||
lastChar -= chunkPtr->numChars;
|
||
if (lastChar <= 0) {
|
||
break;
|
||
}
|
||
chunkPtr++;
|
||
}
|
||
#endif /* Use TkDrawAngledTextLayout() implementation */
|
||
}
|
||
|
||
void
|
||
TkDrawAngledTextLayout(
|
||
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, int y, /* Upper-left hand corner of rectangle in
|
||
* which to draw (pixels). */
|
||
double angle,
|
||
int firstChar, /* The index of the first character to draw
|
||
* from the given text item. 0 specifies 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 = (TextLayout *) layout;
|
||
int i, numDisplayChars, drawX;
|
||
const char *firstByte, *lastByte;
|
||
LayoutChunk *chunkPtr;
|
||
double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0);
|
||
|
||
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)) {
|
||
double dx, dy;
|
||
|
||
if (firstChar <= 0) {
|
||
drawX = 0;
|
||
firstChar = 0;
|
||
firstByte = chunkPtr->start;
|
||
} else {
|
||
firstByte = TkUtfAtIndex(chunkPtr->start, firstChar);
|
||
Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
|
||
firstByte - chunkPtr->start, -1, 0, &drawX);
|
||
}
|
||
if (lastChar < numDisplayChars) {
|
||
numDisplayChars = lastChar;
|
||
}
|
||
lastByte = TkUtfAtIndex(chunkPtr->start, numDisplayChars);
|
||
#if TK_DRAW_IN_CONTEXT
|
||
dx = cosA * (chunkPtr->x) + sinA * (chunkPtr->y);
|
||
dy = -sinA * (chunkPtr->x) + cosA * (chunkPtr->y);
|
||
if (angle == 0.0) {
|
||
TkpDrawCharsInContext(display, drawable, gc,
|
||
layoutPtr->tkfont, chunkPtr->start, chunkPtr->numBytes,
|
||
firstByte - chunkPtr->start, lastByte - firstByte,
|
||
(int)(x + dx), (int)(y + dy));
|
||
} else {
|
||
TkpDrawAngledCharsInContext(display, drawable, gc,
|
||
layoutPtr->tkfont, chunkPtr->start, chunkPtr->numBytes,
|
||
firstByte - chunkPtr->start, lastByte - firstByte,
|
||
x+dx, y+dy, angle);
|
||
}
|
||
#else /* !TK_DRAW_IN_CONTEXT */
|
||
dx = cosA * (chunkPtr->x + drawX) + sinA * (chunkPtr->y);
|
||
dy = -sinA * (chunkPtr->x + drawX) + cosA * (chunkPtr->y);
|
||
if (angle == 0.0) {
|
||
Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
|
||
firstByte, lastByte - firstByte,
|
||
(int)(x + dx), (int)(y + dy));
|
||
} else {
|
||
TkDrawAngledChars(display, drawable, gc, layoutPtr->tkfont,
|
||
firstByte, lastByte - firstByte, x+dx, y+dy, angle);
|
||
}
|
||
#endif /* TK_DRAW_IN_CONTEXT */
|
||
}
|
||
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 function does not draw the text,
|
||
* just the underline.
|
||
*
|
||
* This function 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 *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, int 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. */
|
||
{
|
||
int xx, yy, width, height;
|
||
|
||
if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
|
||
&& (width != 0)) {
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
TkFont *fontPtr = (TkFont *) layoutPtr->tkfont;
|
||
|
||
XFillRectangle(display, drawable, gc, x + xx,
|
||
y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
|
||
(unsigned) width, (unsigned) fontPtr->underlineHeight);
|
||
}
|
||
}
|
||
|
||
void
|
||
TkUnderlineAngledTextLayout(
|
||
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, int y, /* Upper-left hand corner of rectangle in
|
||
* which to draw (pixels). */
|
||
double angle,
|
||
int underline) /* Index of the single character to underline,
|
||
* or -1 for no underline. */
|
||
{
|
||
int xx, yy, width, height;
|
||
|
||
if (angle == 0.0) {
|
||
Tk_UnderlineTextLayout(display, drawable, gc, layout, x,y, underline);
|
||
return;
|
||
}
|
||
|
||
if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
|
||
&& (width != 0)) {
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
TkFont *fontPtr = (TkFont *) layoutPtr->tkfont;
|
||
double sinA = sin(angle*PI/180), cosA = cos(angle*PI/180);
|
||
double dy = yy + fontPtr->fm.ascent + fontPtr->underlinePos;
|
||
XPoint points[5];
|
||
|
||
/*
|
||
* Note that we're careful to only round a double value once, which
|
||
* minimizes roundoff errors.
|
||
*/
|
||
|
||
points[0].x = x + ROUND16(xx*cosA + dy*sinA);
|
||
points[0].y = y + ROUND16(dy*cosA - xx*sinA);
|
||
points[1].x = x + ROUND16(xx*cosA + dy*sinA + width*cosA);
|
||
points[1].y = y + ROUND16(dy*cosA - xx*sinA - width*sinA);
|
||
if (fontPtr->underlineHeight == 1) {
|
||
/*
|
||
* Thin underlines look better when rotated when drawn as a line
|
||
* rather than a rectangle; the rasterizer copes better.
|
||
*/
|
||
|
||
XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin);
|
||
} else {
|
||
points[2].x = x + ROUND16(xx*cosA + dy*sinA + width*cosA
|
||
+ fontPtr->underlineHeight*sinA);
|
||
points[2].y = y + ROUND16(dy*cosA - xx*sinA - width*sinA
|
||
+ fontPtr->underlineHeight*cosA);
|
||
points[3].x = x + ROUND16(xx*cosA + dy*sinA
|
||
+ fontPtr->underlineHeight*sinA);
|
||
points[3].y = y + ROUND16(dy*cosA - xx*sinA
|
||
+ fontPtr->underlineHeight*cosA);
|
||
points[4].x = points[0].x;
|
||
points[4].y = points[0].y;
|
||
XFillPolygon(display, drawable, gc, points, 5, Complex,
|
||
CoordModeOrigin);
|
||
XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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(
|
||
Tk_TextLayout layout, /* Layout information, from a previous call to
|
||
* Tk_ComputeTextLayout(). */
|
||
int x, int y) /* Coordinates of point to check, with respect
|
||
* to the upper-left corner of the text
|
||
* layout. */
|
||
{
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
LayoutChunk *chunkPtr, *lastPtr;
|
||
TkFont *fontPtr;
|
||
int i, n, dummy, baseline, pos, numChars;
|
||
|
||
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.
|
||
*/
|
||
|
||
fontPtr = (TkFont *) layoutPtr->tkfont;
|
||
lastPtr = chunkPtr = layoutPtr->chunks;
|
||
numChars = 0;
|
||
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 numChars;
|
||
}
|
||
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 numChars;
|
||
}
|
||
n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
|
||
chunkPtr->numBytes, x - chunkPtr->x, 0, &dummy);
|
||
return numChars + Tcl_NumUtfChars(chunkPtr->start, n);
|
||
}
|
||
numChars += chunkPtr->numChars;
|
||
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 = numChars;
|
||
if (i < layoutPtr->numChunks) {
|
||
pos--;
|
||
}
|
||
return pos;
|
||
}
|
||
numChars += chunkPtr->numChars;
|
||
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(
|
||
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, int *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, int *heightPtr)
|
||
/* Filled with the width and height of the
|
||
* bounding box for the character specified by
|
||
* index, if non-NULL. */
|
||
{
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
LayoutChunk *chunkPtr;
|
||
int i, x = 0, w;
|
||
Tk_Font tkfont;
|
||
TkFont *fontPtr;
|
||
const char *end;
|
||
|
||
if (index < 0) {
|
||
return 0;
|
||
}
|
||
|
||
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) {
|
||
end = TkUtfAtIndex(chunkPtr->start, index);
|
||
if (xPtr != NULL) {
|
||
Tk_MeasureChars(tkfont, chunkPtr->start,
|
||
end - chunkPtr->start, -1, 0, &x);
|
||
x += chunkPtr->x;
|
||
}
|
||
if (widthPtr != NULL) {
|
||
int ch;
|
||
Tk_MeasureChars(tkfont, end, TkUtfToUniChar(end, &ch), -1, 0, &w);
|
||
}
|
||
goto check;
|
||
}
|
||
index -= chunkPtr->numChars;
|
||
chunkPtr++;
|
||
}
|
||
if (index != 0) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Special case to get location just past last char in layout.
|
||
*/
|
||
|
||
chunkPtr--;
|
||
x = chunkPtr->x + chunkPtr->totalWidth;
|
||
w = 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(
|
||
Tk_TextLayout layout, /* Layout information, from a previous call
|
||
* to Tk_ComputeTextLayout(). */
|
||
int x, int 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;
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
LayoutChunk *chunkPtr;
|
||
TkFont *fontPtr;
|
||
|
||
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(
|
||
Tk_TextLayout layout, /* Layout information, from a previous call to
|
||
* Tk_ComputeTextLayout(). */
|
||
int x, int 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, int height) /* The width and height of the above
|
||
* rectangular area, in pixels. */
|
||
{
|
||
int result, i, x1, y1, x2, y2;
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
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.
|
||
*/
|
||
|
||
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') || (chunkPtr->numBytes == 0)) {
|
||
/*
|
||
* Newline characters and empty chunks 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;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkIntersectAngledTextLayout --
|
||
*
|
||
* Determines whether a text layout that has been turned by an angle
|
||
* about its top-left coordinae 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.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
static inline int
|
||
PointInQuadrilateral(
|
||
double qx[],
|
||
double qy[],
|
||
double x,
|
||
double y)
|
||
{
|
||
int i;
|
||
|
||
for (i=0 ; i<4 ; i++) {
|
||
double sideDX = qx[(i+1)%4] - qx[i];
|
||
double sideDY = qy[(i+1)%4] - qy[i];
|
||
double dx = x - qx[i];
|
||
double dy = y - qy[i];
|
||
|
||
if (sideDX*dy < sideDY*dx) {
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static inline int
|
||
SidesIntersect(
|
||
double ax1, double ay1, double ax2, double ay2,
|
||
double bx1, double by1, double bx2, double by2)
|
||
{
|
||
#if 0
|
||
/* http://www.freelunchdesign.com/cgi-bin/codwiki.pl?DiscussionTopics/CollideMeUpBaby */
|
||
|
||
double a1, b1, c1, a2, b2, c2, r1, r2, r3, r4, denom;
|
||
|
||
a1 = ay2 - ay1;
|
||
b1 = ax1 - ax2;
|
||
c1 = (ax2 * ay1) - (ax1 * ay2);
|
||
r3 = (a1 * bx1) + (b1 * by1) + c1;
|
||
r4 = (a1 * bx2) + (b1 * by2) + c1;
|
||
if ((r3 != 0.0) && (r4 != 0.0) && (r3*r4 > 0.0)) {
|
||
return 0;
|
||
}
|
||
|
||
a2 = by2 - by1;
|
||
b2 = bx1 - bx2;
|
||
c2 = (bx2 * by1) - (bx1 * by2);
|
||
r1 = (a2 * ax1) + (b2 * ay1) + c2;
|
||
r2 = (a2 * ax2) + (b2 * ay2) + c2;
|
||
if ((r1 != 0.0) && (r2 != 0.0) && (r1*r2 > 0.0)) {
|
||
return 0;
|
||
}
|
||
|
||
denom = (a1 * b2) - (a2 * b1);
|
||
return (denom != 0.0);
|
||
#else
|
||
/*
|
||
* A more efficient version. Two line segments intersect if, when seen
|
||
* from the perspective of one line, the two endpoints of the other
|
||
* segment lie on opposite sides of the line, and vice versa. "Lie on
|
||
* opposite sides" is computed by taking the cross products and seeing if
|
||
* they are of opposite signs.
|
||
*/
|
||
|
||
double dx, dy, dx1, dy1;
|
||
|
||
dx = ax2 - ax1;
|
||
dy = ay2 - ay1;
|
||
dx1 = bx1 - ax1;
|
||
dy1 = by1 - ay1;
|
||
if ((dx*dy1-dy*dx1 > 0.0) == (dx*(by2-ay1)-dy*(bx2-ax1) > 0.0)) {
|
||
return 0;
|
||
}
|
||
dx = bx2 - bx1;
|
||
dy = by2 - by1;
|
||
if ((dy*dx1-dx*dy1 > 0.0) == (dx*(ay2-by1)-dy*(ax2-bx1) > 0.0)) {
|
||
return 0;
|
||
}
|
||
return 1;
|
||
#endif
|
||
}
|
||
|
||
int
|
||
TkIntersectAngledTextLayout(
|
||
Tk_TextLayout layout, /* Layout information, from a previous call to
|
||
* Tk_ComputeTextLayout(). */
|
||
int x, int 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, int height, /* The width and height of the above
|
||
* rectangular area, in pixels. */
|
||
double angle)
|
||
{
|
||
int i, x1, y1, x2, y2;
|
||
TextLayout *layoutPtr;
|
||
LayoutChunk *chunkPtr;
|
||
TkFont *fontPtr;
|
||
double c = cos(angle * PI/180.0), s = sin(angle * PI/180.0);
|
||
double rx[4], ry[4];
|
||
|
||
if (angle == 0.0) {
|
||
return Tk_IntersectTextLayout(layout, x, y, width, height);
|
||
}
|
||
|
||
/*
|
||
* Compute the coordinates of the rectangle, rotated into text layout
|
||
* space.
|
||
*/
|
||
|
||
rx[0] = x*c - y*s;
|
||
ry[0] = y*c + x*s;
|
||
rx[1] = (x+width)*c - y*s;
|
||
ry[1] = y*c + (x+width)*s;
|
||
rx[2] = (x+width)*c - (y+height)*s;
|
||
ry[2] = (y+height)*c + (x+width)*s;
|
||
rx[3] = x*c - (y+height)*s;
|
||
ry[3] = (y+height)*c + x*s;
|
||
|
||
/*
|
||
* Want to know if all chunks are inside the rectangle, or if there is any
|
||
* overlap. First, we check to see if all chunks are inside; if and only
|
||
* if they are, we're in the "inside" case.
|
||
*/
|
||
|
||
layoutPtr = (TextLayout *) layout;
|
||
chunkPtr = layoutPtr->chunks;
|
||
fontPtr = (TkFont *) layoutPtr->tkfont;
|
||
|
||
for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
|
||
if (chunkPtr->start[0] == '\n') {
|
||
/*
|
||
* Newline characters are not counted when computing area
|
||
* intersection (but tab characters would still be considered).
|
||
*/
|
||
|
||
continue;
|
||
}
|
||
|
||
x1 = chunkPtr->x;
|
||
y1 = chunkPtr->y - fontPtr->fm.ascent;
|
||
x2 = chunkPtr->x + chunkPtr->displayWidth;
|
||
y2 = chunkPtr->y + fontPtr->fm.descent;
|
||
if ( !PointInQuadrilateral(rx, ry, x1, y1) ||
|
||
!PointInQuadrilateral(rx, ry, x2, y1) ||
|
||
!PointInQuadrilateral(rx, ry, x2, y2) ||
|
||
!PointInQuadrilateral(rx, ry, x1, y2)) {
|
||
goto notInside;
|
||
}
|
||
}
|
||
return 1;
|
||
|
||
/*
|
||
* Next, check to see if all the points of the rectangle are inside a
|
||
* single chunk; if they are, we're in an "overlap" case.
|
||
*/
|
||
|
||
notInside:
|
||
chunkPtr = layoutPtr->chunks;
|
||
|
||
for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
|
||
double cx[4], cy[4];
|
||
|
||
if (chunkPtr->start[0] == '\n') {
|
||
/*
|
||
* Newline characters are not counted when computing area
|
||
* intersection (but tab characters would still be considered).
|
||
*/
|
||
|
||
continue;
|
||
}
|
||
|
||
cx[0] = cx[3] = chunkPtr->x;
|
||
cy[0] = cy[1] = chunkPtr->y - fontPtr->fm.ascent;
|
||
cx[1] = cx[2] = chunkPtr->x + chunkPtr->displayWidth;
|
||
cy[2] = cy[3] = chunkPtr->y + fontPtr->fm.descent;
|
||
if ( PointInQuadrilateral(cx, cy, rx[0], ry[0]) &&
|
||
PointInQuadrilateral(cx, cy, rx[1], ry[1]) &&
|
||
PointInQuadrilateral(cx, cy, rx[2], ry[2]) &&
|
||
PointInQuadrilateral(cx, cy, rx[3], ry[3])) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* If we're overlapping now, we must be partially in and out of at least
|
||
* one chunk. If that is the case, there must be one line segment of the
|
||
* rectangle that is touching or crossing a line segment of a chunk.
|
||
*/
|
||
|
||
chunkPtr = layoutPtr->chunks;
|
||
|
||
for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
|
||
int j;
|
||
|
||
if (chunkPtr->start[0] == '\n') {
|
||
/*
|
||
* Newline characters are not counted when computing area
|
||
* intersection (but tab characters would still be considered).
|
||
*/
|
||
|
||
continue;
|
||
}
|
||
|
||
x1 = chunkPtr->x;
|
||
y1 = chunkPtr->y - fontPtr->fm.ascent;
|
||
x2 = chunkPtr->x + chunkPtr->displayWidth;
|
||
y2 = chunkPtr->y + fontPtr->fm.descent;
|
||
|
||
for (j=0 ; j<4 ; j++) {
|
||
int k = (j+1) % 4;
|
||
|
||
if ( SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y1, x2,y1) ||
|
||
SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y1, x2,y2) ||
|
||
SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y2, x1,y2) ||
|
||
SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y2, x1,y1)) {
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* They must be wholly non-overlapping.
|
||
*/
|
||
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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:
|
||
* The interp's result is modified to hold the Postscript code that will
|
||
* render the text layout.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
Tk_TextLayoutToPostscript(
|
||
Tcl_Interp *interp, /* Filled with Postscript code. */
|
||
Tk_TextLayout layout) /* The layout to be rendered. */
|
||
{
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
LayoutChunk *chunkPtr = layoutPtr->chunks;
|
||
int baseline = chunkPtr->y;
|
||
Tcl_Obj *psObj = Tcl_NewObj();
|
||
int i, j, len;
|
||
const char *p, *glyphname;
|
||
char uindex[5], c, *ps;
|
||
int ch;
|
||
|
||
Tcl_AppendToObj(psObj, "[(", -1);
|
||
for (i = 0; i < layoutPtr->numChunks; i++, chunkPtr++) {
|
||
if (baseline != chunkPtr->y) {
|
||
Tcl_AppendToObj(psObj, ")]\n[(", -1);
|
||
baseline = chunkPtr->y;
|
||
}
|
||
if (chunkPtr->numDisplayChars <= 0) {
|
||
if (chunkPtr->start[0] == '\t') {
|
||
Tcl_AppendToObj(psObj, "\\t", -1);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
for (p=chunkPtr->start, j=0; j<chunkPtr->numDisplayChars; j++) {
|
||
/*
|
||
* INTL: We only handle symbols that have an encoding as a glyph
|
||
* from the standard set defined by Adobe. The rest get punted.
|
||
* Eventually this should be revised to handle more sophsticiated
|
||
* international postscript fonts.
|
||
*/
|
||
|
||
p += TkUtfToUniChar(p, &ch);
|
||
if ((ch == '(') || (ch == ')') || (ch == '\\') || (ch < 0x20)) {
|
||
/*
|
||
* 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.
|
||
*/
|
||
|
||
Tcl_AppendPrintfToObj(psObj, "\\%03o", ch);
|
||
continue;
|
||
} else if (ch <= 0x7f) {
|
||
/*
|
||
* Normal ASCII character.
|
||
*/
|
||
|
||
c = (char) ch;
|
||
Tcl_AppendToObj(psObj, &c, 1);
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* This character doesn't belong to the ASCII character set, so we
|
||
* use the full glyph name.
|
||
*/
|
||
|
||
if (ch > 0xffff) {
|
||
goto noMapping;
|
||
}
|
||
sprintf(uindex, "%04X", ch); /* endianness? */
|
||
glyphname = Tcl_GetVar2(interp, "::tk::psglyphs", uindex, 0);
|
||
if (glyphname) {
|
||
ps = Tcl_GetStringFromObj(psObj, &len);
|
||
if (ps[len-1] == '(') {
|
||
/*
|
||
* In-place edit. Ewww!
|
||
*/
|
||
|
||
ps[len-1] = '/';
|
||
} else {
|
||
Tcl_AppendToObj(psObj, ")/", -1);
|
||
}
|
||
Tcl_AppendToObj(psObj, glyphname, -1);
|
||
Tcl_AppendToObj(psObj, "(", -1);
|
||
} else {
|
||
/*
|
||
* No known mapping for the character into the space of
|
||
* PostScript glyphs. Ignore it. :-(
|
||
*/
|
||
noMapping: ;
|
||
|
||
#ifdef TK_DEBUG_POSTSCRIPT_OUTPUT
|
||
fprintf(stderr, "Warning: no mapping to PostScript "
|
||
"glyphs for \\u%04x\n", ch);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
Tcl_AppendToObj(psObj, ")]\n", -1);
|
||
Tcl_AppendObjToObj(Tcl_GetObjResult(interp), psObj);
|
||
Tcl_DecrRefCount(psObj);
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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(
|
||
Tcl_Interp *interp, /* Interp for error return. */
|
||
TCL_UNUSED(Tk_Window), /* 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 *optionPtr, *valuePtr;
|
||
const char *value;
|
||
|
||
for (i = 0; i < objc; i += 2) {
|
||
optionPtr = objv[i];
|
||
|
||
if (Tcl_GetIndexFromObj(interp, optionPtr, fontOpt, "option", 1,
|
||
&index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if ((i+2 >= objc) && (objc & 1)) {
|
||
/*
|
||
* This test occurs after Tcl_GetIndexFromObj() so that "font
|
||
* create xyz -xyz" will return the error message that "-xyz" is a
|
||
* bad option, rather than that the value for "-xyz" is missing.
|
||
*/
|
||
|
||
if (interp != NULL) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"value for \"%s\" option missing",
|
||
Tcl_GetString(optionPtr)));
|
||
Tcl_SetErrorCode(interp, "TK", "FONT", "NO_ATTRIBUTE", NULL);
|
||
}
|
||
return TCL_ERROR;
|
||
}
|
||
valuePtr = objv[i + 1];
|
||
|
||
switch (index) {
|
||
case FONT_FAMILY:
|
||
value = Tcl_GetString(valuePtr);
|
||
faPtr->family = Tk_GetUid(value);
|
||
break;
|
||
case FONT_SIZE:
|
||
if (Tcl_GetIntFromObj(interp, valuePtr, &n) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
faPtr->size = (double)n;
|
||
break;
|
||
case FONT_WEIGHT:
|
||
n = TkFindStateNumObj(interp, optionPtr, weightMap, valuePtr);
|
||
if (n == TK_FW_UNKNOWN) {
|
||
return TCL_ERROR;
|
||
}
|
||
faPtr->weight = n;
|
||
break;
|
||
case FONT_SLANT:
|
||
n = TkFindStateNumObj(interp, optionPtr, slantMap, valuePtr);
|
||
if (n == TK_FS_UNKNOWN) {
|
||
return TCL_ERROR;
|
||
}
|
||
faPtr->slant = n;
|
||
break;
|
||
case FONT_UNDERLINE:
|
||
if (Tcl_GetBooleanFromObj(interp, valuePtr, &n) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
faPtr->underline = n;
|
||
break;
|
||
case FONT_OVERSTRIKE:
|
||
if (Tcl_GetBooleanFromObj(interp, valuePtr, &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(
|
||
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;
|
||
const char *str;
|
||
Tcl_Obj *valuePtr, *resultPtr = NULL;
|
||
|
||
start = 0;
|
||
end = FONT_NUMFIELDS;
|
||
if (objPtr != NULL) {
|
||
if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", TCL_EXACT,
|
||
&index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
start = index;
|
||
end = index + 1;
|
||
}
|
||
|
||
valuePtr = NULL;
|
||
if (objPtr == NULL) {
|
||
resultPtr = Tcl_NewObj();
|
||
}
|
||
for (i = start; i < end; i++) {
|
||
switch (i) {
|
||
case FONT_FAMILY:
|
||
str = faPtr->family;
|
||
valuePtr = Tcl_NewStringObj(str, ((str == NULL) ? 0 : -1));
|
||
break;
|
||
|
||
case FONT_SIZE:
|
||
if (faPtr->size >= 0.0) {
|
||
valuePtr = Tcl_NewIntObj((int)(faPtr->size + 0.5));
|
||
} else {
|
||
valuePtr = Tcl_NewIntObj(-(int)(-faPtr->size + 0.5));
|
||
}
|
||
break;
|
||
|
||
case FONT_WEIGHT:
|
||
str = TkFindStateString(weightMap, faPtr->weight);
|
||
valuePtr = Tcl_NewStringObj(str, -1);
|
||
break;
|
||
|
||
case FONT_SLANT:
|
||
str = TkFindStateString(slantMap, faPtr->slant);
|
||
valuePtr = Tcl_NewStringObj(str, -1);
|
||
break;
|
||
|
||
case FONT_UNDERLINE:
|
||
valuePtr = Tcl_NewBooleanObj(faPtr->underline);
|
||
break;
|
||
|
||
case FONT_OVERSTRIKE:
|
||
valuePtr = Tcl_NewBooleanObj(faPtr->overstrike);
|
||
break;
|
||
}
|
||
if (objPtr != NULL) {
|
||
Tcl_SetObjResult(interp, valuePtr);
|
||
return TCL_OK;
|
||
}
|
||
Tcl_ListObjAppendElement(NULL, resultPtr,
|
||
Tcl_NewStringObj(fontOpt[i], -1));
|
||
Tcl_ListObjAppendElement(NULL, resultPtr, valuePtr);
|
||
}
|
||
Tcl_SetObjResult(interp, resultPtr);
|
||
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] [style1 [style2 ...]"
|
||
* "-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(
|
||
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) /* Filled with attributes parsed from font
|
||
* name. Any attributes that were not
|
||
* specified in font name are filled with
|
||
* default values. */
|
||
{
|
||
const char *dash;
|
||
int objc, result, i, n;
|
||
Tcl_Obj **objv;
|
||
const char *string;
|
||
|
||
TkInitFontAttributes(faPtr);
|
||
|
||
string = Tcl_GetString(objPtr);
|
||
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]))) { /* INTL: ISO space */
|
||
goto xlfd;
|
||
}
|
||
|
||
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
|
||
}
|
||
|
||
if (*string == '*') {
|
||
/*
|
||
* This is appears to be an XLFD. Under Unix, all valid XLFDs were
|
||
* already handled by TkpGetNativeFont. If we are here, either we have
|
||
* something that initially looks like an XLFD but isn't or we have
|
||
* encountered an XLFD on Windows or Mac.
|
||
*/
|
||
|
||
xlfd:
|
||
result = TkFontParseXLFD(string, faPtr, NULL);
|
||
if (result == TCL_OK) {
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
* If the string failed to parse but was considered to be a XLFD
|
||
* then it may be a "-option value" string with a hyphenated family
|
||
* name as per bug 2791352
|
||
*/
|
||
|
||
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
if (ConfigAttributesObj(interp, tkwin, objc, objv, faPtr) == TCL_OK) {
|
||
return TCL_OK;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Wasn't an XLFD or "-option value" string. Try it as a "font size style"
|
||
* list.
|
||
*/
|
||
|
||
if ((Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
|
||
|| (objc < 1)) {
|
||
if (interp != NULL) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"font \"%s\" doesn't exist", string));
|
||
Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT", string, NULL);
|
||
}
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
faPtr->family = Tk_GetUid(Tcl_GetString(objv[0]));
|
||
if (objc > 1) {
|
||
if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
faPtr->size = (double)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++) {
|
||
n = TkFindStateNumObj(NULL, NULL, weightMap, objv[i]);
|
||
if (n != TK_FW_UNKNOWN) {
|
||
faPtr->weight = n;
|
||
continue;
|
||
}
|
||
n = TkFindStateNumObj(NULL, NULL, slantMap, objv[i]);
|
||
if (n != TK_FS_UNKNOWN) {
|
||
faPtr->slant = n;
|
||
continue;
|
||
}
|
||
n = TkFindStateNumObj(NULL, NULL, underlineMap, objv[i]);
|
||
if (n != 0) {
|
||
faPtr->underline = n;
|
||
continue;
|
||
}
|
||
n = TkFindStateNumObj(NULL, NULL, overstrikeMap, objv[i]);
|
||
if (n != 0) {
|
||
faPtr->overstrike = n;
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* Unknown style.
|
||
*/
|
||
|
||
if (interp != NULL) {
|
||
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
|
||
"unknown font style \"%s\"", Tcl_GetString(objv[i])));
|
||
Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT_STYLE",
|
||
Tcl_GetString(objv[i]), NULL);
|
||
}
|
||
return TCL_ERROR;
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* 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(
|
||
TextLayout **layoutPtrPtr,
|
||
int *maxPtr,
|
||
const char *start,
|
||
int numBytes,
|
||
int curX,
|
||
int newX,
|
||
int y)
|
||
{
|
||
TextLayout *layoutPtr;
|
||
LayoutChunk *chunkPtr;
|
||
int maxChunks, numChars;
|
||
size_t s;
|
||
|
||
layoutPtr = *layoutPtrPtr;
|
||
maxChunks = *maxPtr;
|
||
if (layoutPtr->numChunks == maxChunks) {
|
||
maxChunks *= 2;
|
||
s = Tk_Offset(TextLayout, chunks) + (maxChunks * sizeof(LayoutChunk));
|
||
layoutPtr = (TextLayout *)ckrealloc(layoutPtr, s);
|
||
|
||
*layoutPtrPtr = layoutPtr;
|
||
*maxPtr = maxChunks;
|
||
}
|
||
numChars = Tcl_NumUtfChars(start, numBytes);
|
||
chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
|
||
chunkPtr->start = start;
|
||
chunkPtr->numBytes = numBytes;
|
||
chunkPtr->numChars = numChars;
|
||
chunkPtr->numDisplayChars = numChars;
|
||
chunkPtr->x = curX;
|
||
chunkPtr->y = y;
|
||
chunkPtr->totalWidth = newX - curX;
|
||
chunkPtr->displayWidth = newX - curX;
|
||
layoutPtr->numChunks++;
|
||
|
||
return chunkPtr;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkFontParseXLFD --
|
||
*
|
||
* 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
|
||
TkFontParseXLFD(
|
||
const char *string, /* Parseable font description string. */
|
||
TkFontAttributes *faPtr, /* Filled with attributes parsed from font
|
||
* name. Any attributes that were not
|
||
* specified in font name are filled with
|
||
* default values. */
|
||
TkXLFDAttributes *xaPtr) /* Filled with X-specific attributes parsed
|
||
* from font name. Any attributes that were
|
||
* not specified in font name are filled with
|
||
* default values. May be NULL if such
|
||
* information is not desired. */
|
||
{
|
||
char *src;
|
||
const char *str;
|
||
int i, j;
|
||
char *field[XLFD_NUMFIELDS + 2];
|
||
Tcl_DString ds;
|
||
TkXLFDAttributes xa;
|
||
|
||
if (xaPtr == NULL) {
|
||
xaPtr = &xa;
|
||
}
|
||
TkInitFontAttributes(faPtr);
|
||
TkInitXLFDAttributes(xaPtr);
|
||
|
||
memset(field, '\0', sizeof(field));
|
||
|
||
str = string;
|
||
if (*str == '-') {
|
||
str++;
|
||
}
|
||
|
||
Tcl_DStringInit(&ds);
|
||
Tcl_DStringAppend(&ds, str, -1);
|
||
src = Tcl_DStringValue(&ds);
|
||
|
||
field[0] = src;
|
||
for (i = 0; *src != '\0'; src++) {
|
||
if (!(*src & 0x80)
|
||
&& Tcl_UniCharIsUpper(UCHAR(*src))) {
|
||
*src = (char) Tcl_UniCharToLower(UCHAR(*src));
|
||
}
|
||
if (*src == '-') {
|
||
i++;
|
||
if (i == XLFD_NUMFIELDS) {
|
||
continue;
|
||
}
|
||
*src = '\0';
|
||
field[i] = src + 1;
|
||
if (i > XLFD_NUMFIELDS) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 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 right 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 (as a native font name), but gives a syntax error under Windows (as a
|
||
* parsed set of attributes)".
|
||
*/
|
||
|
||
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])) {
|
||
faPtr->family = Tk_GetUid(field[XLFD_FAMILY]);
|
||
}
|
||
if (FieldSpecified(field[XLFD_WEIGHT])) {
|
||
faPtr->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) {
|
||
faPtr->slant = TK_FS_ROMAN;
|
||
} else {
|
||
faPtr->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 for
|
||
* historical compatibility.
|
||
*/
|
||
|
||
faPtr->size = 12.0;
|
||
|
||
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.
|
||
*/
|
||
|
||
faPtr->size = atof(field[XLFD_POINT_SIZE] + 1);
|
||
} else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
|
||
&i) == TCL_OK) {
|
||
faPtr->size = i/10.0;
|
||
} 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.
|
||
*/
|
||
|
||
faPtr->size = atof(field[XLFD_PIXEL_SIZE] + 1);
|
||
} else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
|
||
&i) == TCL_OK) {
|
||
faPtr->size = (double)i;
|
||
} else {
|
||
return TCL_ERROR;
|
||
}
|
||
}
|
||
|
||
faPtr->size = -faPtr->size;
|
||
|
||
/* XLFD_RESOLUTION_X ignored. */
|
||
|
||
/* XLFD_RESOLUTION_Y ignored. */
|
||
|
||
/* XLFD_SPACING ignored. */
|
||
|
||
/* XLFD_AVERAGE_WIDTH ignored. */
|
||
|
||
if (FieldSpecified(field[XLFD_CHARSET])) {
|
||
xaPtr->charset = Tk_GetUid(field[XLFD_CHARSET]);
|
||
} else {
|
||
xaPtr->charset = Tk_GetUid("iso8859-1");
|
||
}
|
||
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(
|
||
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 != '?');
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetPixels --
|
||
*
|
||
* Given a font size specification (as described in the TkFontAttributes
|
||
* structure) return the number of pixels it represents.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
double
|
||
TkFontGetPixels(
|
||
Tk_Window tkwin, /* For point->pixel conversion factor. */
|
||
double size) /* Font size. */
|
||
{
|
||
double d;
|
||
|
||
if (size <= 0.0) {
|
||
return -size;
|
||
}
|
||
|
||
d = size * 25.4 / 72.0;
|
||
d *= WidthOfScreen(Tk_Screen(tkwin));
|
||
d /= WidthMMOfScreen(Tk_Screen(tkwin));
|
||
return d;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetPoints --
|
||
*
|
||
* Given a font size specification (as described in the TkFontAttributes
|
||
* structure) return the number of points it represents.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
double
|
||
TkFontGetPoints(
|
||
Tk_Window tkwin, /* For pixel->point conversion factor. */
|
||
double size) /* Font size. */
|
||
{
|
||
double d;
|
||
|
||
if (size >= 0.0) {
|
||
return size;
|
||
}
|
||
|
||
d = -size * 72.0 / 25.4;
|
||
d *= WidthMMOfScreen(Tk_Screen(tkwin));
|
||
d /= WidthOfScreen(Tk_Screen(tkwin));
|
||
return d;
|
||
}
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetAliasList --
|
||
*
|
||
* Given a font name, find the list of all aliases for that font name.
|
||
* One of the names in this list will probably be the name that this
|
||
* platform expects when asking for the font.
|
||
*
|
||
* Results:
|
||
* As above. The return value is NULL if the font name has no aliases.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
const char *const *
|
||
TkFontGetAliasList(
|
||
const char *faceName) /* Font name to test for aliases. */
|
||
{
|
||
int i, j;
|
||
|
||
for (i = 0; fontAliases[i] != NULL; i++) {
|
||
for (j = 0; fontAliases[i][j] != NULL; j++) {
|
||
if (strcasecmp(faceName, fontAliases[i][j]) == 0) {
|
||
return fontAliases[i];
|
||
}
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetFallbacks --
|
||
*
|
||
* Get the list of font fallbacks that the platform-specific code can use
|
||
* to try to find the closest matching font the name requested.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
const char *const *const *
|
||
TkFontGetFallbacks(void)
|
||
{
|
||
return fontFallbacks;
|
||
}
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetGlobalClass --
|
||
*
|
||
* Get the list of fonts to try if the requested font name does not
|
||
* exist and no fallbacks for that font name could be used either.
|
||
* The names in this list are considered preferred over all the other
|
||
* font names in the system when looking for a last-ditch fallback.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
const char *const *
|
||
TkFontGetGlobalClass(void)
|
||
{
|
||
return globalFontClass;
|
||
}
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* TkFontGetSymbolClass --
|
||
*
|
||
* Get the list of fonts that are symbolic; used if the operating system
|
||
* cannot apriori identify symbolic fonts on its own.
|
||
*
|
||
* Results:
|
||
* As above.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
const char *const *
|
||
TkFontGetSymbolClass(void)
|
||
{
|
||
return symbolClass;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkDebugFont --
|
||
*
|
||
* This function returns debugging information about a font.
|
||
*
|
||
* Results:
|
||
* The return value is a list with one sublist for each TkFont
|
||
* corresponding to "name". Each sublist has two elements that contain
|
||
* the resourceRefCount and objRefCount fields from the TkFont structure.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
Tcl_Obj *
|
||
TkDebugFont(
|
||
Tk_Window tkwin, /* The window in which the font will be used
|
||
* (not currently used). */
|
||
const char *name) /* Name of the desired color. */
|
||
{
|
||
TkFont *fontPtr;
|
||
Tcl_HashEntry *hashPtr;
|
||
Tcl_Obj *resultPtr, *objPtr;
|
||
|
||
resultPtr = Tcl_NewObj();
|
||
hashPtr = Tcl_FindHashEntry(
|
||
&((TkWindow *) tkwin)->mainPtr->fontInfoPtr->fontCache, name);
|
||
if (hashPtr != NULL) {
|
||
fontPtr = (TkFont *)Tcl_GetHashValue(hashPtr);
|
||
if (fontPtr == NULL) {
|
||
Tcl_Panic("TkDebugFont found empty hash table entry");
|
||
}
|
||
for ( ; (fontPtr != NULL); fontPtr = fontPtr->nextPtr) {
|
||
objPtr = Tcl_NewObj();
|
||
Tcl_ListObjAppendElement(NULL, objPtr,
|
||
Tcl_NewIntObj(fontPtr->resourceRefCount));
|
||
Tcl_ListObjAppendElement(NULL, objPtr,
|
||
Tcl_NewIntObj(fontPtr->objRefCount));
|
||
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
|
||
}
|
||
}
|
||
return resultPtr;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFontGetFirstTextLayout --
|
||
*
|
||
* This function returns the first chunk of a Tk_TextLayout, i.e. until
|
||
* the first font change on the first line (or the whole first line if
|
||
* there is no such font change).
|
||
*
|
||
* Results:
|
||
* The return value is the byte length of the chunk, the chunk itself is
|
||
* copied into dst and its Tk_Font into font.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkFontGetFirstTextLayout(
|
||
Tk_TextLayout layout, /* Layout information, from a previous call to
|
||
* Tk_ComputeTextLayout(). */
|
||
Tk_Font *font,
|
||
char *dst)
|
||
{
|
||
TextLayout *layoutPtr = (TextLayout *) layout;
|
||
LayoutChunk *chunkPtr;
|
||
int numBytesInChunk;
|
||
|
||
if ((layoutPtr == NULL) || (layoutPtr->numChunks == 0)
|
||
|| (layoutPtr->chunks->numDisplayChars <= 0)) {
|
||
dst[0] = '\0';
|
||
return 0;
|
||
}
|
||
chunkPtr = layoutPtr->chunks;
|
||
numBytesInChunk = chunkPtr->numBytes;
|
||
strncpy(dst, chunkPtr->start, numBytesInChunk);
|
||
*font = layoutPtr->tkfont;
|
||
return numBytesInChunk;
|
||
}
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode: c
|
||
* c-basic-offset: 4
|
||
* fill-column: 78
|
||
* End:
|
||
*/
|