diff options
Diffstat (limited to 'tk8.6/generic/tkUtil.c')
-rw-r--r-- | tk8.6/generic/tkUtil.c | 1281 |
1 files changed, 1281 insertions, 0 deletions
diff --git a/tk8.6/generic/tkUtil.c b/tk8.6/generic/tkUtil.c new file mode 100644 index 0000000..e686826 --- /dev/null +++ b/tk8.6/generic/tkUtil.c @@ -0,0 +1,1281 @@ +/* + * tkUtil.c -- + * + * This file contains miscellaneous utility functions that are used by + * the rest of Tk, such as a function for drawing a focus highlight. + * + * Copyright (c) 1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkInt.h" + +/* + * The structure below defines the implementation of the "statekey" Tcl + * object, used for quickly finding a mapping in a TkStateMap. + */ + +const Tcl_ObjType tkStateKeyObjType = { + "statekey", /* name */ + NULL, /* freeIntRepProc */ + NULL, /* dupIntRepProc */ + NULL, /* updateStringProc */ + NULL /* setFromAnyProc */ +}; + +/* + *-------------------------------------------------------------- + * + * TkStateParseProc -- + * + * This function is invoked during option processing to handle the + * "-state" and "-default" options. + * + * Results: + * A standard Tcl return value. + * + * Side effects: + * The state for a given item gets replaced by the state indicated in the + * value argument. + * + *-------------------------------------------------------------- + */ + +int +TkStateParseProc( + ClientData clientData, /* some flags.*/ + Tcl_Interp *interp, /* Used for reporting errors. */ + Tk_Window tkwin, /* Window containing canvas widget. */ + const char *value, /* Value of option. */ + char *widgRec, /* Pointer to record for item. */ + int offset) /* Offset into item. */ +{ + int c; + int flags = PTR2INT(clientData); + size_t length; + Tcl_Obj *msgObj; + + register Tk_State *statePtr = (Tk_State *) (widgRec + offset); + + if (value == NULL || *value == 0) { + *statePtr = TK_STATE_NULL; + return TCL_OK; + } + + c = value[0]; + length = strlen(value); + + if ((c == 'n') && (strncmp(value, "normal", length) == 0)) { + *statePtr = TK_STATE_NORMAL; + return TCL_OK; + } + if ((c == 'd') && (strncmp(value, "disabled", length) == 0)) { + *statePtr = TK_STATE_DISABLED; + return TCL_OK; + } + if ((c == 'a') && (flags&1) && (strncmp(value, "active", length) == 0)) { + *statePtr = TK_STATE_ACTIVE; + return TCL_OK; + } + if ((c == 'h') && (flags&2) && (strncmp(value, "hidden", length) == 0)) { + *statePtr = TK_STATE_HIDDEN; + return TCL_OK; + } + + msgObj = Tcl_ObjPrintf("bad %s value \"%s\": must be normal", + ((flags & 4) ? "-default" : "state"), value); + if (flags & 1) { + Tcl_AppendToObj(msgObj, ", active", -1); + } + if (flags & 2) { + Tcl_AppendToObj(msgObj, ", hidden", -1); + } + if (flags & 3) { + Tcl_AppendToObj(msgObj, ",", -1); + } + Tcl_AppendToObj(msgObj, " or disabled", -1); + Tcl_SetObjResult(interp, msgObj); + Tcl_SetErrorCode(interp, "TK", "VALUE", "STATE", NULL); + *statePtr = TK_STATE_NORMAL; + return TCL_ERROR; +} + +/* + *-------------------------------------------------------------- + * + * TkStatePrintProc -- + * + * This function is invoked by the Tk configuration code to produce a + * printable string for the "-state" configuration option. + * + * Results: + * The return value is a string describing the state for the item + * referred to by "widgRec". In addition, *freeProcPtr is filled in with + * the address of a function to call to free the result string when it's + * no longer needed (or NULL to indicate that the string doesn't need to + * be freed). + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +const char * +TkStatePrintProc( + ClientData clientData, /* Ignored. */ + Tk_Window tkwin, /* Window containing canvas widget. */ + char *widgRec, /* Pointer to record for item. */ + int offset, /* Offset into item. */ + Tcl_FreeProc **freeProcPtr) /* Pointer to variable to fill in with + * information about how to reclaim storage + * for return string. */ +{ + register Tk_State *statePtr = (Tk_State *) (widgRec + offset); + + switch (*statePtr) { + case TK_STATE_NORMAL: + return "normal"; + case TK_STATE_DISABLED: + return "disabled"; + case TK_STATE_HIDDEN: + return "hidden"; + case TK_STATE_ACTIVE: + return "active"; + default: + return ""; + } +} + +/* + *-------------------------------------------------------------- + * + * TkOrientParseProc -- + * + * This function is invoked during option processing to handle the + * "-orient" option. + * + * Results: + * A standard Tcl return value. + * + * Side effects: + * The orientation for a given item gets replaced by the orientation + * indicated in the value argument. + * + *-------------------------------------------------------------- + */ + +int +TkOrientParseProc( + ClientData clientData, /* some flags.*/ + Tcl_Interp *interp, /* Used for reporting errors. */ + Tk_Window tkwin, /* Window containing canvas widget. */ + const char *value, /* Value of option. */ + char *widgRec, /* Pointer to record for item. */ + int offset) /* Offset into item. */ +{ + int c; + size_t length; + + register int *orientPtr = (int *) (widgRec + offset); + + if (value == NULL || *value == 0) { + *orientPtr = 0; + return TCL_OK; + } + + c = value[0]; + length = strlen(value); + + if ((c == 'h') && (strncmp(value, "horizontal", length) == 0)) { + *orientPtr = 0; + return TCL_OK; + } + if ((c == 'v') && (strncmp(value, "vertical", length) == 0)) { + *orientPtr = 1; + return TCL_OK; + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad orientation \"%s\": must be vertical or horizontal", + value)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "ORIENTATION", NULL); + *orientPtr = 0; + return TCL_ERROR; +} + +/* + *-------------------------------------------------------------- + * + * TkOrientPrintProc -- + * + * This function is invoked by the Tk configuration code to produce a + * printable string for the "-orient" configuration option. + * + * Results: + * The return value is a string describing the orientation for the item + * referred to by "widgRec". In addition, *freeProcPtr is filled in with + * the address of a function to call to free the result string when it's + * no longer needed (or NULL to indicate that the string doesn't need to + * be freed). + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +const char * +TkOrientPrintProc( + ClientData clientData, /* Ignored. */ + Tk_Window tkwin, /* Window containing canvas widget. */ + char *widgRec, /* Pointer to record for item. */ + int offset, /* Offset into item. */ + Tcl_FreeProc **freeProcPtr) /* Pointer to variable to fill in with + * information about how to reclaim storage + * for return string. */ +{ + register int *statePtr = (int *) (widgRec + offset); + + if (*statePtr) { + return "vertical"; + } else { + return "horizontal"; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkOffsetParseProc -- + * + * Converts the offset of a stipple or tile into the Tk_TSOffset + * structure. + * + *---------------------------------------------------------------------- + */ + +int +TkOffsetParseProc( + ClientData clientData, /* not used */ + Tcl_Interp *interp, /* Interpreter to send results back to */ + Tk_Window tkwin, /* Window on same display as tile */ + const char *value, /* Name of image */ + char *widgRec, /* Widget structure record */ + int offset) /* Offset of tile in record */ +{ + Tk_TSOffset *offsetPtr = (Tk_TSOffset *) (widgRec + offset); + Tk_TSOffset tsoffset; + const char *q, *p; + int result; + Tcl_Obj *msgObj; + + if ((value == NULL) || (*value == 0)) { + tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_MIDDLE; + goto goodTSOffset; + } + tsoffset.flags = 0; + p = value; + + switch (value[0]) { + case '#': + if (PTR2INT(clientData) & TK_OFFSET_RELATIVE) { + tsoffset.flags = TK_OFFSET_RELATIVE; + p++; + break; + } + goto badTSOffset; + case 'e': + switch(value[1]) { + case '\0': + tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_MIDDLE; + goto goodTSOffset; + case 'n': + if (value[2]!='d' || value[3]!='\0') { + goto badTSOffset; + } + tsoffset.flags = INT_MAX; + goto goodTSOffset; + } + case 'w': + if (value[1] != '\0') {goto badTSOffset;} + tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_MIDDLE; + goto goodTSOffset; + case 'n': + if ((value[1] != '\0') && (value[2] != '\0')) { + goto badTSOffset; + } + switch(value[1]) { + case '\0': + tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_TOP; + goto goodTSOffset; + case 'w': + tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_TOP; + goto goodTSOffset; + case 'e': + tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_TOP; + goto goodTSOffset; + } + goto badTSOffset; + case 's': + if ((value[1] != '\0') && (value[2] != '\0')) { + goto badTSOffset; + } + switch(value[1]) { + case '\0': + tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_BOTTOM; + goto goodTSOffset; + case 'w': + tsoffset.flags = TK_OFFSET_LEFT|TK_OFFSET_BOTTOM; + goto goodTSOffset; + case 'e': + tsoffset.flags = TK_OFFSET_RIGHT|TK_OFFSET_BOTTOM; + goto goodTSOffset; + } + goto badTSOffset; + case 'c': + if (strncmp(value, "center", strlen(value)) != 0) { + goto badTSOffset; + } + tsoffset.flags = TK_OFFSET_CENTER|TK_OFFSET_MIDDLE; + goto goodTSOffset; + } + + /* + * Check for an extra offset. + */ + + q = strchr(p, ','); + if (q == NULL) { + if (PTR2INT(clientData) & TK_OFFSET_INDEX) { + if (Tcl_GetInt(interp, (char *) p, &tsoffset.flags) != TCL_OK) { + Tcl_ResetResult(interp); + goto badTSOffset; + } + tsoffset.flags |= TK_OFFSET_INDEX; + goto goodTSOffset; + } + goto badTSOffset; + } + + *((char *) q) = 0; + result = Tk_GetPixels(interp, tkwin, (char *) p, &tsoffset.xoffset); + *((char *) q) = ','; + if (result != TCL_OK) { + return TCL_ERROR; + } + if (Tk_GetPixels(interp, tkwin, (char*)q+1, &tsoffset.yoffset) != TCL_OK) { + return TCL_ERROR; + } + + /* + * Below is a hack to allow the stipple/tile offset to be stored in the + * internal tile structure. Most of the times, offsetPtr is a pointer to + * an already existing tile structure. However if this structure is not + * already created, we must do it with Tk_GetTile()!!!! + */ + + goodTSOffset: + memcpy(offsetPtr, &tsoffset, sizeof(Tk_TSOffset)); + return TCL_OK; + + badTSOffset: + msgObj = Tcl_ObjPrintf("bad offset \"%s\": expected \"x,y\"", value); + if (PTR2INT(clientData) & TK_OFFSET_RELATIVE) { + Tcl_AppendToObj(msgObj, ", \"#x,y\"", -1); + } + if (PTR2INT(clientData) & TK_OFFSET_INDEX) { + Tcl_AppendToObj(msgObj, ", <index>", -1); + } + Tcl_AppendToObj(msgObj, ", n, ne, e, se, s, sw, w, nw, or center", -1); + Tcl_SetObjResult(interp, msgObj); + Tcl_SetErrorCode(interp, "TK", "VALUE", "OFFSET", NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TkOffsetPrintProc -- + * + * Returns the offset of the tile. + * + * Results: + * The offset of the tile is returned. + * + *---------------------------------------------------------------------- + */ + +const char * +TkOffsetPrintProc( + ClientData clientData, /* not used */ + Tk_Window tkwin, /* not used */ + char *widgRec, /* Widget structure record */ + int offset, /* Offset of tile in record */ + Tcl_FreeProc **freeProcPtr) /* not used */ +{ + Tk_TSOffset *offsetPtr = (Tk_TSOffset *) (widgRec + offset); + char *p, *q; + + if (offsetPtr->flags & TK_OFFSET_INDEX) { + if (offsetPtr->flags >= INT_MAX) { + return "end"; + } + p = ckalloc(32); + sprintf(p, "%d", offsetPtr->flags & ~TK_OFFSET_INDEX); + *freeProcPtr = TCL_DYNAMIC; + return p; + } + if (offsetPtr->flags & TK_OFFSET_TOP) { + if (offsetPtr->flags & TK_OFFSET_LEFT) { + return "nw"; + } else if (offsetPtr->flags & TK_OFFSET_CENTER) { + return "n"; + } else if (offsetPtr->flags & TK_OFFSET_RIGHT) { + return "ne"; + } + } else if (offsetPtr->flags & TK_OFFSET_MIDDLE) { + if (offsetPtr->flags & TK_OFFSET_LEFT) { + return "w"; + } else if (offsetPtr->flags & TK_OFFSET_CENTER) { + return "center"; + } else if (offsetPtr->flags & TK_OFFSET_RIGHT) { + return "e"; + } + } else if (offsetPtr->flags & TK_OFFSET_BOTTOM) { + if (offsetPtr->flags & TK_OFFSET_LEFT) { + return "sw"; + } else if (offsetPtr->flags & TK_OFFSET_CENTER) { + return "s"; + } else if (offsetPtr->flags & TK_OFFSET_RIGHT) { + return "se"; + } + } + q = p = ckalloc(32); + if (offsetPtr->flags & TK_OFFSET_RELATIVE) { + *q++ = '#'; + } + sprintf(q, "%d,%d", offsetPtr->xoffset, offsetPtr->yoffset); + *freeProcPtr = TCL_DYNAMIC; + return p; +} + +/* + *---------------------------------------------------------------------- + * + * TkPixelParseProc -- + * + * Converts the name of an image into a tile. + * + *---------------------------------------------------------------------- + */ + +int +TkPixelParseProc( + ClientData clientData, /* If non-NULL, negative values are allowed as + * well. */ + Tcl_Interp *interp, /* Interpreter to send results back to */ + Tk_Window tkwin, /* Window on same display as tile */ + const char *value, /* Name of image */ + char *widgRec, /* Widget structure record */ + int offset) /* Offset of tile in record */ +{ + double *doublePtr = (double *) (widgRec + offset); + int result; + + result = TkGetDoublePixels(interp, tkwin, value, doublePtr); + + if ((result == TCL_OK) && (clientData == NULL) && (*doublePtr < 0.0)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad screen distance \"%s\"", value)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "PIXELS", NULL); + return TCL_ERROR; + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TkPixelPrintProc -- + * + * Returns the name of the tile. + * + * Results: + * The name of the tile is returned. + * + *---------------------------------------------------------------------- + */ + +const char * +TkPixelPrintProc( + ClientData clientData, /* not used */ + Tk_Window tkwin, /* not used */ + char *widgRec, /* Widget structure record */ + int offset, /* Offset of tile in record */ + Tcl_FreeProc **freeProcPtr) /* not used */ +{ + double *doublePtr = (double *) (widgRec + offset); + char *p = ckalloc(24); + + Tcl_PrintDouble(NULL, *doublePtr, p); + *freeProcPtr = TCL_DYNAMIC; + return p; +} + +/* + *---------------------------------------------------------------------- + * + * TkDrawInsetFocusHighlight -- + * + * This function draws a rectangular ring around the outside of a widget + * to indicate that it has received the input focus. It takes an + * additional padding argument that specifies how much padding is present + * outside the widget. + * + * Results: + * None. + * + * Side effects: + * A rectangle "width" pixels wide is drawn in "drawable", corresponding + * to the outer area of "tkwin". + * + *---------------------------------------------------------------------- + */ + +void +TkDrawInsetFocusHighlight( + Tk_Window tkwin, /* Window whose focus highlight ring is to be + * drawn. */ + GC gc, /* Graphics context to use for drawing the + * highlight ring. */ + int width, /* Width of the highlight ring, in pixels. */ + Drawable drawable, /* Where to draw the ring (typically a pixmap + * for double buffering). */ + int padding) /* Width of padding outside of widget. */ +{ + XRectangle rects[4]; + + rects[0].x = padding; + rects[0].y = padding; + rects[0].width = Tk_Width(tkwin) - (2 * padding); + rects[0].height = width; + rects[1].x = padding; + rects[1].y = Tk_Height(tkwin) - width - padding; + rects[1].width = Tk_Width(tkwin) - (2 * padding); + rects[1].height = width; + rects[2].x = padding; + rects[2].y = width + padding; + rects[2].width = width; + rects[2].height = Tk_Height(tkwin) - 2*width - 2*padding; + rects[3].x = Tk_Width(tkwin) - width - padding; + rects[3].y = rects[2].y; + rects[3].width = width; + rects[3].height = rects[2].height; + XFillRectangles(Tk_Display(tkwin), drawable, gc, rects, 4); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_DrawFocusHighlight -- + * + * This function draws a rectangular ring around the outside of a widget + * to indicate that it has received the input focus. + * + * This function is now deprecated. Use TkpDrawHighlightBorder instead, + * since this function does not handle drawing the Focus ring properly on + * the Macintosh - you need to know the background GC as well as the + * foreground since the Mac focus ring separated from the widget by a 1 + * pixel border. + * + * Results: + * None. + * + * Side effects: + * A rectangle "width" pixels wide is drawn in "drawable", corresponding + * to the outer area of "tkwin". + * + *---------------------------------------------------------------------- + */ + +void +Tk_DrawFocusHighlight( + Tk_Window tkwin, /* Window whose focus highlight ring is to be + * drawn. */ + GC gc, /* Graphics context to use for drawing the + * highlight ring. */ + int width, /* Width of the highlight ring, in pixels. */ + Drawable drawable) /* Where to draw the ring (typically a pixmap + * for double buffering). */ +{ + TkDrawInsetFocusHighlight(tkwin, gc, width, drawable, 0); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetScrollInfo -- + * + * This function is invoked to parse "xview" and "yview" scrolling + * commands for widgets using the new scrolling command syntax ("moveto" + * or "scroll" options). + * + * Results: + * The return value is either TK_SCROLL_MOVETO, TK_SCROLL_PAGES, + * TK_SCROLL_UNITS, or TK_SCROLL_ERROR. This indicates whether the + * command was successfully parsed and what form the command took. If + * TK_SCROLL_MOVETO, *dblPtr is filled in with the desired position; if + * TK_SCROLL_PAGES or TK_SCROLL_UNITS, *intPtr is filled in with the + * number of lines to move (may be negative); if TK_SCROLL_ERROR, the + * interp's result contains an error message. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tk_GetScrollInfo( + Tcl_Interp *interp, /* Used for error reporting. */ + int argc, /* # arguments for command. */ + const char **argv, /* Arguments for command. */ + double *dblPtr, /* Filled in with argument "moveto" option, if + * any. */ + int *intPtr) /* Filled in with number of pages or lines to + * scroll, if any. */ +{ + int c = argv[2][0]; + size_t length = strlen(argv[2]); + + if ((c == 'm') && (strncmp(argv[2], "moveto", length) == 0)) { + if (argc != 4) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "wrong # args: should be \"%s %s %s\"", + argv[0], argv[1], "moveto fraction")); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TK_SCROLL_ERROR; + } + if (Tcl_GetDouble(interp, argv[3], dblPtr) != TCL_OK) { + return TK_SCROLL_ERROR; + } + return TK_SCROLL_MOVETO; + } else if ((c == 's') + && (strncmp(argv[2], "scroll", length) == 0)) { + if (argc != 5) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "wrong # args: should be \"%s %s %s\"", + argv[0], argv[1], "scroll number units|pages")); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TK_SCROLL_ERROR; + } + if (Tcl_GetInt(interp, argv[3], intPtr) != TCL_OK) { + return TK_SCROLL_ERROR; + } + length = strlen(argv[4]); + c = argv[4][0]; + if ((c == 'p') && (strncmp(argv[4], "pages", length) == 0)) { + return TK_SCROLL_PAGES; + } else if ((c == 'u') && (strncmp(argv[4], "units", length) == 0)) { + return TK_SCROLL_UNITS; + } + + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad argument \"%s\": must be units or pages", argv[4])); + Tcl_SetErrorCode(interp, "TK", "VALUE", "SCROLL_UNITS", NULL); + return TK_SCROLL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unknown option \"%s\": must be moveto or scroll", argv[2])); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", argv[2], + NULL); + return TK_SCROLL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetScrollInfoObj -- + * + * This function is invoked to parse "xview" and "yview" scrolling + * commands for widgets using the new scrolling command syntax ("moveto" + * or "scroll" options). + * + * Results: + * The return value is either TK_SCROLL_MOVETO, TK_SCROLL_PAGES, + * TK_SCROLL_UNITS, or TK_SCROLL_ERROR. This indicates whether the + * command was successfully parsed and what form the command took. If + * TK_SCROLL_MOVETO, *dblPtr is filled in with the desired position; if + * TK_SCROLL_PAGES or TK_SCROLL_UNITS, *intPtr is filled in with the + * number of lines to move (may be negative); if TK_SCROLL_ERROR, the + * interp's result contains an error message. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tk_GetScrollInfoObj( + Tcl_Interp *interp, /* Used for error reporting. */ + int objc, /* # arguments for command. */ + Tcl_Obj *const objv[], /* Arguments for command. */ + double *dblPtr, /* Filled in with argument "moveto" option, if + * any. */ + int *intPtr) /* Filled in with number of pages or lines to + * scroll, if any. */ +{ + const char *arg = Tcl_GetString(objv[2]); + size_t length = objv[2]->length; + +#define ArgPfxEq(str) \ + ((arg[0] == str[0]) && !strncmp(arg, str, (unsigned)length)) + + if (ArgPfxEq("moveto")) { + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "moveto fraction"); + return TK_SCROLL_ERROR; + } + if (Tcl_GetDoubleFromObj(interp, objv[3], dblPtr) != TCL_OK) { + return TK_SCROLL_ERROR; + } + return TK_SCROLL_MOVETO; + } else if (ArgPfxEq("scroll")) { + if (objc != 5) { + Tcl_WrongNumArgs(interp, 2, objv, "scroll number units|pages"); + return TK_SCROLL_ERROR; + } + if (Tcl_GetIntFromObj(interp, objv[3], intPtr) != TCL_OK) { + return TK_SCROLL_ERROR; + } + + arg = Tcl_GetString(objv[4]); + length = objv[4]->length; + if (ArgPfxEq("pages")) { + return TK_SCROLL_PAGES; + } else if (ArgPfxEq("units")) { + return TK_SCROLL_UNITS; + } + + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad argument \"%s\": must be units or pages", arg)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "SCROLL_UNITS", NULL); + return TK_SCROLL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unknown option \"%s\": must be moveto or scroll", arg)); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", arg, NULL); + return TK_SCROLL_ERROR; +} + +/* + *--------------------------------------------------------------------------- + * + * TkComputeAnchor -- + * + * Determine where to place a rectangle so that it will be properly + * anchored with respect to the given window. Used by widgets to align a + * box of text inside a window. When anchoring with respect to one of the + * sides, the rectangle be placed inside of the internal border of the + * window. + * + * Results: + * *xPtr and *yPtr set to the upper-left corner of the rectangle anchored + * in the window. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkComputeAnchor( + Tk_Anchor anchor, /* Desired anchor. */ + Tk_Window tkwin, /* Anchored with respect to this window. */ + int padX, int padY, /* Use this extra padding inside window, in + * addition to the internal border. */ + int innerWidth, int innerHeight, + /* Size of rectangle to anchor in window. */ + int *xPtr, int *yPtr) /* Returns upper-left corner of anchored + * rectangle. */ +{ + /* + * Handle the horizontal parts. + */ + + switch (anchor) { + case TK_ANCHOR_NW: + case TK_ANCHOR_W: + case TK_ANCHOR_SW: + *xPtr = Tk_InternalBorderLeft(tkwin) + padX; + break; + + case TK_ANCHOR_N: + case TK_ANCHOR_CENTER: + case TK_ANCHOR_S: + *xPtr = (Tk_Width(tkwin) - innerWidth - Tk_InternalBorderLeft(tkwin) - + Tk_InternalBorderRight(tkwin)) / 2 + + Tk_InternalBorderLeft(tkwin); + break; + + default: + *xPtr = Tk_Width(tkwin) - Tk_InternalBorderRight(tkwin) - padX + - innerWidth; + break; + } + + /* + * Handle the vertical parts. + */ + + switch (anchor) { + case TK_ANCHOR_NW: + case TK_ANCHOR_N: + case TK_ANCHOR_NE: + *yPtr = Tk_InternalBorderTop(tkwin) + padY; + break; + + case TK_ANCHOR_W: + case TK_ANCHOR_CENTER: + case TK_ANCHOR_E: + *yPtr = (Tk_Height(tkwin) - innerHeight- Tk_InternalBorderTop(tkwin) - + Tk_InternalBorderBottom(tkwin)) / 2 + + Tk_InternalBorderTop(tkwin); + break; + + default: + *yPtr = Tk_Height(tkwin) - Tk_InternalBorderBottom(tkwin) - padY + - innerHeight; + break; + } +} + +/* + *--------------------------------------------------------------------------- + * + * TkFindStateString -- + * + * Given a lookup table, map a number to a string in the table. + * + * Results: + * If numKey was equal to the numeric key of one of the elements in the + * table, returns the string key of that element. Returns NULL if numKey + * was not equal to any of the numeric keys in the table. + * + * Side effects. + * None. + * + *--------------------------------------------------------------------------- + */ + +const char * +TkFindStateString( + const TkStateMap *mapPtr, /* The state table. */ + int numKey) /* The key to try to find in the table. */ +{ + for (; mapPtr->strKey!=NULL ; mapPtr++) { + if (numKey == mapPtr->numKey) { + return mapPtr->strKey; + } + } + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * TkFindStateNum, TkFindStateNumObj -- + * + * Given a lookup table, map a string to a number in the table. + * + * Results: + * If strKey was equal to the string keys of one of the elements in the + * table, returns the numeric key of that element. Returns the numKey + * associated with the last element (the NULL string one) in the table if + * strKey was not equal to any of the string keys in the table. In that + * case, an error message is also left in the interp's result (if interp + * is not NULL). + * + * Side effects. + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkFindStateNum( + Tcl_Interp *interp, /* Interp for error reporting. */ + const char *option, /* String to use when constructing error. */ + const TkStateMap *mapPtr, /* Lookup table. */ + const char *strKey) /* String to try to find in lookup table. */ +{ + const TkStateMap *mPtr; + + /* + * See if the value is in the state map. + */ + + for (mPtr = mapPtr; mPtr->strKey != NULL; mPtr++) { + if (strcmp(strKey, mPtr->strKey) == 0) { + return mPtr->numKey; + } + } + + /* + * Not there. Generate an error message (if we can) and return the + * default. + */ + + if (interp != NULL) { + Tcl_Obj *msgObj; + + mPtr = mapPtr; + msgObj = Tcl_ObjPrintf("bad %s value \"%s\": must be %s", + option, strKey, mPtr->strKey); + for (mPtr++; mPtr->strKey != NULL; mPtr++) { + Tcl_AppendPrintfToObj(msgObj, ",%s %s", + ((mPtr[1].strKey != NULL) ? "" : "or "), mPtr->strKey); + } + Tcl_SetObjResult(interp, msgObj); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", option, strKey, NULL); + } + return mPtr->numKey; +} + +int +TkFindStateNumObj( + Tcl_Interp *interp, /* Interp for error reporting. */ + Tcl_Obj *optionPtr, /* String to use when constructing error. */ + const TkStateMap *mapPtr, /* Lookup table. */ + Tcl_Obj *keyPtr) /* String key to find in lookup table. */ +{ + const TkStateMap *mPtr; + const char *key; + const Tcl_ObjType *typePtr; + + /* + * See if the value is in the object cache. + */ + + if ((keyPtr->typePtr == &tkStateKeyObjType) + && (keyPtr->internalRep.twoPtrValue.ptr1 == mapPtr)) { + return PTR2INT(keyPtr->internalRep.twoPtrValue.ptr2); + } + + /* + * Not there. Look in the state map. + */ + + key = Tcl_GetString(keyPtr); + for (mPtr = mapPtr; mPtr->strKey != NULL; mPtr++) { + if (strcmp(key, mPtr->strKey) == 0) { + typePtr = keyPtr->typePtr; + if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { + typePtr->freeIntRepProc(keyPtr); + } + keyPtr->internalRep.twoPtrValue.ptr1 = (void *) mapPtr; + keyPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(mPtr->numKey); + keyPtr->typePtr = &tkStateKeyObjType; + return mPtr->numKey; + } + } + + /* + * Not there either. Generate an error message (if we can) and return the + * default. + */ + + if (interp != NULL) { + Tcl_Obj *msgObj; + + mPtr = mapPtr; + msgObj = Tcl_ObjPrintf( + "bad %s value \"%s\": must be %s", + Tcl_GetString(optionPtr), key, mPtr->strKey); + for (mPtr++; mPtr->strKey != NULL; mPtr++) { + Tcl_AppendPrintfToObj(msgObj, ",%s %s", + ((mPtr[1].strKey != NULL) ? "" : " or"), mPtr->strKey); + } + Tcl_SetObjResult(interp, msgObj); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", Tcl_GetString(optionPtr), + key, NULL); + } + return mPtr->numKey; +} + +/* + * ---------------------------------------------------------------------- + * + * TkBackgroundEvalObjv -- + * + * Evaluate a command while ensuring that we do not affect the + * interpreters state. This is important when evaluating script + * during background tasks. + * + * Results: + * A standard Tcl result code. + * + * Side Effects: + * The interpreters variables and code may be modified by the script + * but the result will not be modified. + * + * ---------------------------------------------------------------------- + */ + +int +TkBackgroundEvalObjv( + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv, + int flags) +{ + Tcl_InterpState state; + int n, r = TCL_OK; + + /* + * Record the state of the interpreter. + */ + + Tcl_Preserve(interp); + state = Tcl_SaveInterpState(interp, TCL_OK); + + /* + * Evaluate the command and handle any error. + */ + + for (n = 0; n < objc; ++n) { + Tcl_IncrRefCount(objv[n]); + } + r = Tcl_EvalObjv(interp, objc, objv, flags); + for (n = 0; n < objc; ++n) { + Tcl_DecrRefCount(objv[n]); + } + if (r == TCL_ERROR) { + Tcl_AddErrorInfo(interp, "\n (background event handler)"); + Tcl_BackgroundException(interp, r); + } + + /* + * Restore the state of the interpreter. + */ + + (void) Tcl_RestoreInterpState(interp, state); + Tcl_Release(interp); + + return r; +} + +/* + *---------------------------------------------------------------------- + * + * TkMakeEnsemble -- + * + * Create an ensemble from a table of implementation commands. This may + * be called recursively to create sub-ensembles. + * + * Results: + * Handle for the ensemble, or NULL if creation of it fails. + * + *---------------------------------------------------------------------- + */ + +Tcl_Command +TkMakeEnsemble( + Tcl_Interp *interp, + const char *namespace, + const char *name, + ClientData clientData, + const TkEnsemble map[]) +{ + Tcl_Namespace *namespacePtr = NULL; + Tcl_Command ensemble = NULL; + Tcl_Obj *dictObj = NULL, *nameObj; + Tcl_DString ds; + int i; + + if (map == NULL) { + return NULL; + } + + Tcl_DStringInit(&ds); + + namespacePtr = Tcl_FindNamespace(interp, namespace, NULL, 0); + if (namespacePtr == NULL) { + namespacePtr = Tcl_CreateNamespace(interp, namespace, NULL, NULL); + if (namespacePtr == NULL) { + Tcl_Panic("failed to create namespace \"%s\"", namespace); + } + } + + nameObj = Tcl_NewStringObj(name, -1); + ensemble = Tcl_FindEnsemble(interp, nameObj, 0); + Tcl_DecrRefCount(nameObj); + if (ensemble == NULL) { + ensemble = Tcl_CreateEnsemble(interp, name, namespacePtr, + TCL_ENSEMBLE_PREFIX); + if (ensemble == NULL) { + Tcl_Panic("failed to create ensemble \"%s\"", name); + } + } + + Tcl_DStringSetLength(&ds, 0); + Tcl_DStringAppend(&ds, namespace, -1); + if (!(strlen(namespace) == 2 && namespace[1] == ':')) { + Tcl_DStringAppend(&ds, "::", -1); + } + Tcl_DStringAppend(&ds, name, -1); + + dictObj = Tcl_NewObj(); + for (i = 0; map[i].name != NULL ; ++i) { + Tcl_Obj *nameObj, *fqdnObj; + + nameObj = Tcl_NewStringObj(map[i].name, -1); + fqdnObj = Tcl_NewStringObj(Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds)); + Tcl_AppendStringsToObj(fqdnObj, "::", map[i].name, NULL); + Tcl_DictObjPut(NULL, dictObj, nameObj, fqdnObj); + if (map[i].proc) { + Tcl_CreateObjCommand(interp, Tcl_GetString(fqdnObj), + map[i].proc, clientData, NULL); + } else if (map[i].subensemble) { + TkMakeEnsemble(interp, Tcl_DStringValue(&ds), + map[i].name, clientData, map[i].subensemble); + } + } + + if (ensemble) { + Tcl_SetEnsembleMappingDict(interp, ensemble, dictObj); + } + + Tcl_DStringFree(&ds); + return ensemble; +} + +/* + *---------------------------------------------------------------------- + * + * TkSendVirtualEvent -- + * + * Send a virtual event notification to the specified target window. + * Equivalent to: + * "event generate $target <<$eventName>> -data $detail" + * + * Note that we use Tk_QueueWindowEvent, not Tk_HandleEvent, so this + * routine does not reenter the interpreter. + * + *---------------------------------------------------------------------- + */ + +void +TkSendVirtualEvent( + Tk_Window target, + const char *eventName, + Tcl_Obj *detail) +{ + union {XEvent general; XVirtualEvent virtual;} event; + + memset(&event, 0, sizeof(event)); + event.general.xany.type = VirtualEvent; + event.general.xany.serial = NextRequest(Tk_Display(target)); + event.general.xany.send_event = False; + event.general.xany.window = Tk_WindowId(target); + event.general.xany.display = Tk_Display(target); + event.virtual.name = Tk_GetUid(eventName); + if (detail != NULL) { + event.virtual.user_data = detail; + } + + Tk_QueueWindowEvent(&event.general, TCL_QUEUE_TAIL); +} + +#if TCL_UTF_MAX <= 4 +/* + *--------------------------------------------------------------------------- + * + * TkUtfToUniChar -- + * + * Almost the same as Tcl_UtfToUniChar but using int instead of Tcl_UniChar. + * This function is capable of collapsing a upper/lower surrogate pair to a + * single unicode character. So, up to 6 bytes might be consumed. + * + * Results: + * *chPtr is filled with the Tcl_UniChar, and the return value is the + * number of bytes from the UTF-8 string that were consumed. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkUtfToUniChar( + const char *src, /* The UTF-8 string. */ + int *chPtr) /* Filled with the Tcl_UniChar represented by + * the UTF-8 string. */ +{ + Tcl_UniChar uniChar = 0; + + int len = Tcl_UtfToUniChar(src, &uniChar); + if ((uniChar & 0xfc00) == 0xd800) { + Tcl_UniChar high = uniChar; + /* This can only happen if Tcl is compiled with TCL_UTF_MAX=4, + * or when a high surrogate character is detected in UTF-8 form */ + int len2 = Tcl_UtfToUniChar(src+len, &uniChar); + if ((uniChar & 0xfc00) == 0xdc00) { + *chPtr = (((high & 0x3ff) << 10) | (uniChar & 0x3ff)) + 0x10000; + len += len2; + } else { + *chPtr = high; + } + } else { + *chPtr = uniChar; + } + return len; +} + +/* + *--------------------------------------------------------------------------- + * + * TkUniCharToUtf -- + * + * Almost the same as Tcl_UniCharToUtf but producing surrogates if + * TCL_UTF_MAX==3. So, up to 6 bytes might be produced. + * + * Results: + * *buf is filled with the UTF-8 string, and the return value is the + * number of bytes produced. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int TkUniCharToUtf(int ch, char *buf) +{ + int size = Tcl_UniCharToUtf(ch, buf); + if ((ch > 0xffff) && (ch <= 0x10ffff) && (size < 4)) { + /* Hey, this is wrong, we must be running TCL_UTF_MAX==3 + * The best thing we can do is spit out 2 surrogates */ + ch -= 0x10000; + size = Tcl_UniCharToUtf(((ch >> 10) | 0xd800), buf); + size += Tcl_UniCharToUtf(((ch & 0x3ff) | 0xdc00), buf+size); + } + return size; +} + + +#endif +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |