/* * tixUtils.c -- * * This file contains some utility functions for Tix, such as the * subcommand handling functions and option handling functions. * * Copyright (c) 1993-1999 Ioi Kim Lam. * Copyright (c) 2000-2001 Tix Project Group. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: tixUtils.c,v 1.13 2008/02/28 04:29:17 hobbs Exp $ */ #include #include #include /* * Forward declarations for procedures defined later in this file: */ static int ReliefParseProc(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, CONST84 char *value, char *widRec, int offset); static char * ReliefPrintProc(ClientData clientData, Tk_Window tkwin, char *widRec, int offset, Tix_FreeProc **freeProcPtr); #define WRONG_ARGC 1 #define NO_MATCH 2 /*---------------------------------------------------------------------- * Tix_HandleSubCmds -- * * This function makes it easier to write major-minor style TCL * commands. It matches the minor command (sub-command) names * with names defined in the cmdInfo structure and call the * appropriate sub-command functions for you. This function will * automatically generate error messages when the user calls an * invalid sub-command or calls a sub-command with incorrect * number of arguments. * *---------------------------------------------------------------------- */ int Tix_HandleSubCmds(cmdInfo, subCmdInfo, clientData, interp, argc, argv) Tix_CmdInfo * cmdInfo; Tix_SubCmdInfo * subCmdInfo; ClientData clientData; /* Main window associated with * interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ CONST84 char **argv; /* Argument strings. */ { int i; int error = NO_MATCH; unsigned int len; Tix_SubCmdInfo * s; /* * First check if the number of arguments to the major command * is correct */ argc -= 1; if (argc < cmdInfo->minargc || (cmdInfo->maxargc != TIX_VAR_ARGS && argc > cmdInfo->maxargc)) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ", cmdInfo->info, "\".", (char *) NULL); return TCL_ERROR; } /* * Now try to match the subcommands with argv[1] */ argc -= 1; len = strlen(argv[1]); for (i = 0, s = subCmdInfo; i < cmdInfo->numSubCmds; i++, s++) { if (s->name == TIX_DEFAULT_SUBCMD) { if (s->checkArgvProc) { if (!((*s->checkArgvProc)(clientData, interp, argc+1, argv+1))) { /* Some improper argv in the arguments of the default * subcommand */ break; } } return (*s->proc)(clientData, interp, argc+1, argv+1); } if (s->namelen == TIX_DEFAULT_LEN) { s->namelen = strlen(s->name); } if (s->name[0] == argv[1][0] && strncmp(argv[1],s->name,len)==0) { if (argc < s->minargc) { error = WRONG_ARGC; break; } if (s->maxargc != TIX_VAR_ARGS && argc > s->maxargc) { error = WRONG_ARGC; break; } /* * Here we have a matched argc and command name --> go for it! */ return (*s->proc)(clientData, interp, argc, argv+2); } } if (error == WRONG_ARGC) { /* * got a match but incorrect number of arguments */ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ", argv[1], " ", s->info, "\"", (char *) NULL); } else { int max; /* * no match: let print out all the options */ Tcl_AppendResult(interp, "unknown option \"", argv[1], "\".", (char *) NULL); if (cmdInfo->numSubCmds == 0) { max = 0; } else { if (subCmdInfo[cmdInfo->numSubCmds-1].name == TIX_DEFAULT_SUBCMD) { max = cmdInfo->numSubCmds-1; } else { max = cmdInfo->numSubCmds; } } if (max == 0) { Tcl_AppendResult(interp, " This command does not take any options.", (char *) NULL); } else if (max == 1) { Tcl_AppendResult(interp, " Must be ", subCmdInfo->name, ".", (char *)NULL); } else { Tcl_AppendResult(interp, " Must be ", (char *) NULL); for (i = 0, s = subCmdInfo; i < max; i++, s++) { if (i == max-1) { Tcl_AppendResult(interp,"or ",s->name, ".", (char *) NULL); } else if (i == max-2) { Tcl_AppendResult(interp, s->name, " ", (char *) NULL); } else { Tcl_AppendResult(interp, s->name, ", ", (char *) NULL); } } } } return TCL_ERROR; } /*---------------------------------------------------------------------- * Tix_Exit -- * * Call the "exit" tcl command so that things can be cleaned up * before calling the unix exit(2); * *---------------------------------------------------------------------- */ void Tix_Exit(interp, code) Tcl_Interp* interp; int code; { const char *str; if (code != 0 && interp && ((str = Tcl_GetStringResult(interp)) != NULL)) { fprintf(stderr, "%s\n", str); fprintf(stderr, "%s\n", Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY)); } if (interp) { Tcl_EvalEx(interp, "exit", -1, TCL_GLOBAL_ONLY); } exit(code); } /*---------------------------------------------------------------------- * Tix_CreateCommands -- * * * Creates a list of commands stored in the array "commands" *---------------------------------------------------------------------- */ static int initialized = 0; void Tix_CreateCommands(interp, commands, clientData, deleteProc) Tcl_Interp *interp; Tix_TclCmd *commands; ClientData clientData; Tcl_CmdDeleteProc *deleteProc; { Tix_TclCmd * cmdPtr; if (!initialized) { Tcl_CmdInfo cmdInfo; initialized = 1; if (!Tcl_GetCommandInfo(interp,"image", (Tcl_CmdInfo *) &cmdInfo)) { Tcl_Panic("cannot find the \"image\" command"); } else if (cmdInfo.isNativeObjectProc == 1) { initialized = 2; /* we use objects */ } } for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) { Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc, clientData, deleteProc); } } /* *---------------------------------------------------------------------- * Tix_GetAnchorGC -- * * Get the GC for drawing the anchor dotted lines around anchor * elements. * * Results: * Returns a GC that can be passed to Tix_DrawAnchorLines for * drawing an anchor line for the given background color. * * Side effects: * None. *---------------------------------------------------------------------- */ GC Tix_GetAnchorGC(tkwin, bgColor) Tk_Window tkwin; XColor *bgColor; { XGCValues gcValues; XColor valueKey; XColor * anchorColor; int r, g, b; int max; /* * Get the best color to draw the dotted lines on the given background * color. */ r = bgColor->red; g = bgColor->green; b = bgColor->blue; r = (65535 - r) & 0xffff; g = (65535 - g) & 0xffff; b = (65535 - b) & 0xffff; max = r; if (max < g) { max = g; } if (max < b) { max = b; } max = max / 256; if (max > 96) { /* * scale color up */ r = (r * 255) / max; g = (g * 255) / max; b = (b * 255) / max; } else { /* * scale color down */ int min = r; if (min > g) { min = g; } if (min > b) { min = b; } r = r - min; g = g - min; b = b - min; } valueKey.red = r; valueKey.green = g; valueKey.blue = b; anchorColor = Tk_GetColorByValue(tkwin, &valueKey); gcValues.foreground = anchorColor->pixel; gcValues.graphics_exposures = False; gcValues.subwindow_mode = IncludeInferiors; return Tk_GetGC(tkwin, GCForeground|GCGraphicsExposures|GCSubwindowMode, &gcValues); } /* *---------------------------------------------------------------------- * Tix_DrawAnchorLines -- * * Draw dotted anchor lines around anchor elements. The exact * behavior is defined in the platform-specific * TixpDrawAnchorLines function. * * Results: * None. * * Side effects: * None. *---------------------------------------------------------------------- */ void Tix_DrawAnchorLines(display, drawable, gc, x, y, w, h) Display *display; Drawable drawable; GC gc; int x; int y; int w; int h; { TixpDrawAnchorLines(display, drawable, gc, x, y, w, h); } /*---------------------------------------------------------------------- * Tix_CreateSubWindow -- * * Creates a subwindow for a widget (usually used to draw headers, * e.g, HList and Grid widgets) *---------------------------------------------------------------------- */ Tk_Window Tix_CreateSubWindow(interp, tkwin, subPath) Tcl_Interp * interp; Tk_Window tkwin; CONST84 char * subPath; { Tcl_DString dString; Tk_Window subwin; Tcl_DStringInit(&dString); Tcl_DStringAppend(&dString, Tk_PathName(tkwin), (int) strlen(Tk_PathName(tkwin))); Tcl_DStringAppend(&dString, ".tixsw:", 7); Tcl_DStringAppend(&dString, subPath, (int) strlen(subPath)); subwin = Tk_CreateWindowFromPath(interp, tkwin, dString.string, (char *) NULL); Tcl_DStringFree(&dString); return subwin; } static int ErrorProc(clientData, errorEventPtr) ClientData clientData; XErrorEvent *errorEventPtr; /* unused */ { int * badAllocPtr = (int*) clientData; * badAllocPtr = 1; return 0; /* return 0 means error has been * handled properly */ } /* *---------------------------------------------------------------------- * Tix_GetRenderBuffer -- * * Returns a drawable for rendering a widget. If there is * sufficient graphics resource, a pixmap is returned so that * double-buffering can be done. However, if resource is * insufficient, then the windowId is returned. In the second * case happens, the caller of this function has two choices: (1) * draw to the window directly (which may lead to flickering on * the screen) or (2) try to allocate smaller pixmaps. * * Results: * An allocated pixmap of the same depth as the window, or the * window itself. * * Side effects: * A pixmap may be allocated. The caller should call * Tk_FreePixmap() to free the pixmap returned by this function. * *---------------------------------------------------------------------- */ /* * Uncomment this if you want to use single-buffer mode drawing to * debug paintings. */ /* #define PAINT_DEBUG 1 */ Drawable Tix_GetRenderBuffer(display, windowId, width, height, depth) Display *display; /* Display of the windowId */ Window windowId; /* Window to draw into */ int width; /* width of the drawing region */ int height; /* height of the drawing region */ int depth; /* Depth of the window. TODO remove this arg*/ { #ifdef PAINT_DEBUG return windowId; #else Tk_ErrorHandler handler; Pixmap pixmap; int badAlloc = 0; handler= Tk_CreateErrorHandler(display, BadAlloc, -1, -1, (Tk_ErrorProc *) ErrorProc, (ClientData) &badAlloc); pixmap = Tk_GetPixmap(display, windowId, width, height, depth); #if !defined(__WIN32__) && !defined(MAC_TCL) && !defined(MAC_OSX_TK) /* UNIX */ /* * This XSync call is necessary because X may delay the delivery of the * error message. This will make our graphics a bit slower, though, * especially over slow lines */ XSync(display, 0); #endif /* If ErrorProc() is eevr called, it is called before XSync returns */ Tk_DeleteErrorHandler(handler); if (!badAlloc) { return pixmap; } else { return windowId; } #endif } /* *---------------------------------------------------------------------- * * Tix_GlobalVarEval -- * * Given a variable number of string arguments, concatenate them * all together and execute the result as a Tcl command in the global * scope. * * Results: * A standard Tcl return result. An error message or other * result may be left in the interp's result. * * Side effects: * Depends on what was done by the command. * *---------------------------------------------------------------------- */ /* VARARGS2 */ /* ARGSUSED */ int Tix_GlobalVarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1) { va_list argList; Tcl_DString buf; char *string; Tcl_Interp *interp; int result; /* * Copy the strings one after the other into a single larger * string. Use stack-allocated space for small commands, but if * the command gets too large than call ckalloc to create the * space. */ interp = TCL_VARARGS_START(Tcl_Interp *,arg1,argList); Tcl_DStringInit(&buf); while (1) { string = va_arg(argList, char *); if (string == NULL) { break; } Tcl_DStringAppend(&buf, string, -1); } va_end(argList); result = Tcl_EvalEx(interp, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf), TCL_GLOBAL_ONLY); Tcl_DStringFree(&buf); return result; } /*---------------------------------------------------------------------- * TixGetHashTable -- * * This functions makes it possible to keep one hash table per * interpreter. This way, Tix classes can be used in multiple * interpreters. * *---------------------------------------------------------------------- */ static void DeleteHashTableProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp)); static void DeleteHashTableProc(clientData, interp) ClientData clientData; Tcl_Interp * interp; { Tcl_HashTable * htPtr = (Tcl_HashTable *)clientData; Tcl_HashSearch hashSearch; Tcl_HashEntry * hashPtr; for (hashPtr = Tcl_FirstHashEntry(htPtr, &hashSearch); hashPtr; hashPtr = Tcl_NextHashEntry(&hashSearch)) { Tcl_DeleteHashEntry(hashPtr); } Tcl_DeleteHashTable(htPtr); ckfree((char*)htPtr); } /* *---------------------------------------------------------------------- * TixGetHashTable() -- * * Returns a named hashtable to be used for the given * interpreter. Creates the hashtable if it doesn't exist yet. It * uses Tcl_GetAssocData to make sure that the hashtable is not * shared by different interpreters. * * Results: * Pointer to the hashtable. * * Side effects: * The hashtable is created if it doesn't exist yet. * *---------------------------------------------------------------------- */ Tcl_HashTable * TixGetHashTable(interp, name, deleteProc, keyType) Tcl_Interp * interp; char * name; Tcl_InterpDeleteProc *deleteProc; int keyType; { Tcl_HashTable * htPtr; htPtr = (Tcl_HashTable*)Tcl_GetAssocData(interp, name, NULL); if (htPtr == NULL) { htPtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(htPtr, keyType); Tcl_SetAssocData(interp, name, NULL, (ClientData)htPtr); if (deleteProc) { Tcl_CallWhenDeleted(interp, deleteProc, (ClientData)htPtr); } else { Tcl_CallWhenDeleted(interp, DeleteHashTableProc, (ClientData)htPtr); } } return htPtr; } /*---------------------------------------------------------------------- * * The Tix Customed Config Options * *---------------------------------------------------------------------- */ /*---------------------------------------------------------------------- * ReliefParseProc -- * * Parse the text string and store the Tix_Relief information * inside the widget record. *---------------------------------------------------------------------- */ static int ReliefParseProc(clientData, interp, tkwin, value, widRec,offset) ClientData clientData; Tcl_Interp *interp; Tk_Window tkwin; CONST84 char *value; char *widRec; /* Must point to a valid Tix_DItem struct */ int offset; { Tix_Relief * ptr = (Tix_Relief *)(widRec + offset); Tix_Relief newVal; if (value != NULL) { size_t len = strlen(value); if (strncmp(value, "raised", len) == 0) { newVal = TIX_RELIEF_RAISED; } else if (strncmp(value, "flat", len) == 0) { newVal = TIX_RELIEF_FLAT; } else if (strncmp(value, "sunken", len) == 0) { newVal = TIX_RELIEF_SUNKEN; } else if (strncmp(value, "groove", len) == 0) { newVal = TIX_RELIEF_GROOVE; } else if (strncmp(value, "ridge", len) == 0) { newVal = TIX_RELIEF_RIDGE; } else if (strncmp(value, "solid", len) == 0) { newVal = TIX_RELIEF_SOLID; } else { goto error; } } else { value = ""; goto error; } *ptr = newVal; return TCL_OK; error: Tcl_AppendResult(interp, "bad relief type \"", value, "\": must be flat, groove, raised, ridge, solid or sunken", NULL); return TCL_ERROR; } static char * ReliefPrintProc(clientData, tkwin, widRec,offset, freeProcPtr) ClientData clientData; Tk_Window tkwin; char *widRec; int offset; Tix_FreeProc **freeProcPtr; { Tix_Relief *ptr = (Tix_Relief*)(widRec+offset); switch (*ptr) { case TIX_RELIEF_RAISED: return "raised"; case TIX_RELIEF_FLAT: return "flat"; case TIX_RELIEF_SUNKEN: return "sunken"; case TIX_RELIEF_GROOVE: return "groove"; case TIX_RELIEF_RIDGE: return "ridge"; case TIX_RELIEF_SOLID: return "solid"; default: return "unknown"; } } /* * The global data structures to use in widget configSpecs arrays * * These are declared in */ Tk_CustomOption tixConfigRelief = { ReliefParseProc, ReliefPrintProc, 0, }; /* Tix_SetRcFileName -- * * Sets a user-specific startup file in a way that's compatible with * different versions of Tclsh */ void Tix_SetRcFileName(interp, rcFileName) Tcl_Interp * interp; CONST84 char * rcFileName; { /* * Starting from TCL 7.5, the symbol tcl_rcFileName is no longer * exported by libtcl.a. Instead, this variable must be set using * a TCL global variable */ Tcl_SetVar(interp, "tcl_rcFileName", rcFileName, TCL_GLOBAL_ONLY); } /* * The TkComputeTextGeometry function is no longer supported in Tk 8.0+ */ /* *---------------------------------------------------------------------- * * TixComputeTextGeometry -- * * This procedure computes the amount of screen space needed to * display a multi-line string of text. * * Results: * There is no return value. The dimensions of the screen area * needed to display the text are returned in *widthPtr, and *heightPtr. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TixComputeTextGeometry(font, string, numChars, wrapLength, widthPtr, heightPtr) TixFont font; /* Font that will be used to display text. */ CONST84 char *string; /* String whose dimensions are to be * computed. */ int numChars; /* Number of characters to consider from * string. -1 means the entire size of * the text string */ int wrapLength; /* Longest permissible line length, in * pixels. <= 0 means no automatic wrapping: * just let lines get as long as needed. */ int *widthPtr; /* Store width of string here. */ int *heightPtr; /* Store height of string here. */ { Tk_TextLayout textLayout; /* * The justification itself doesn't affect the geometry (size) of * the text string. We pass TK_JUSTIFY_LEFT. */ textLayout = Tk_ComputeTextLayout(font, string, numChars, wrapLength, TK_JUSTIFY_LEFT, 0, widthPtr, heightPtr); Tk_FreeTextLayout(textLayout); } /* *---------------------------------------------------------------------- * * TixDisplayText -- * * Display a text string on one or more lines. * * Results: * None. * * Side effects: * The text given by "string" gets displayed at the given location * in the given drawable with the given font etc. * *---------------------------------------------------------------------- */ void TixDisplayText(display, drawable, font, string, numChars, x, y, length, justify, underline, gc) Display *display; /* X display to use for drawing text. */ Drawable drawable; /* Window or pixmap in which to draw the * text. */ TixFont font; /* Font that determines geometry of text * (should be same as font in gc). */ CONST84 char *string; /* String to display; may contain embedded * newlines. */ int numChars; /* Number of characters to use from string. */ int x, y; /* Pixel coordinates within drawable of * upper left corner of display area. */ int length; /* Line length in pixels; used to compute * word wrap points and also for * justification. Must be > 0. */ Tk_Justify justify; /* How to justify lines. */ int underline; /* Index of character to underline, or < 0 * for no underlining. */ GC gc; /* Graphics context to use for drawing text. */ { Tk_TextLayout textLayout; int dummyx, dummyy; textLayout = Tk_ComputeTextLayout(font, string, numChars, length, justify, 0, &dummyx, &dummyy); Tk_DrawTextLayout(display, drawable, gc, textLayout, x, y, 0, -1); Tk_UnderlineTextLayout(display, drawable, gc, textLayout, x, y, underline); Tk_FreeTextLayout(textLayout); } /* *---------------------------------------------------------------------- * Tix_ZAlloc -- * * Allocate the memory block with ckalloc and zeros it. * * Results: * Same as ckalloc() except the new memory block is filled with * zero. Returns NULL if memory allocation fails. * * Side effects: * None. *---------------------------------------------------------------------- */ char * Tix_ZAlloc(nbytes) unsigned int nbytes; /* size of memory block to alloc, in * number of bytes.*/ { char * ptr = (char*)ckalloc(nbytes); if (ptr) { memset(ptr, 0, nbytes); } return ptr; }