[http://www.tfh-berlin.de/~sleuthold/files/tcl/clock.jpg] [[May 06]] ---- The source of '''Clock Widget in C''' is from: '''Practical Programming in Tcl and Tk (fourth edition)''' written by ''Brent B. Welch, Ken Jones and Jeffrey Hobbs'' (Chapter 49, Writing a Tk Widget in C). I was trying to build the extension just for fun and education. And I wanted to see how it looks like. Here are some pictures (WinXP and Linux). ======tcl package require Tkclock oclock .oc pack .oc # wclock .wc # pack .wc ====== Some changes to the code where necessary to build and run it. I'm not sure if this is because of 8.x or real bugs. Search for '''MODIFY''' to see what I had to change. ======c /* Tkclock.cpp */ /* * Example 49-2 * The Clock widget data structure. */ #include "tk.h" #include /* MODIFY */ #ifndef __WIN32__ #include #else #include #define TCL_STORAGE_CLASS DLLEXPORT #endif typedef struct { Tk_Window tkwin; /* The window for the widget */ Display *display; /* Tk's handle on the display */ Tcl_Interp *interp; /* Interpreter of the widget */ Tcl_Command widgetCmd; /* clock instance command. */ Tk_OptionTable optionTable; /* Used to parse options */ /* * Clock-specific attributes. */ int borderWidth; /* Size of 3-D border */ Tcl_Obj *borderWidthPtr; /* Original string value */ int relief; /* Style of 3-D border */ Tk_3DBorder background; /* Color for border & background */ XColor *foreground; /* Color for the text */ XColor *highlight; /* Color for active highlight */ XColor *highlightBg; /* Color for neutral highlight */ int highlightWidth; /* Thickness of highlight rim */ Tcl_Obj *highlightWidthPtr; /* Original string value */ Tk_Font tkfont; /* Font info for the text */ char *format; /* Format for time string */ /* * Graphic contexts and other support. */ GC textGC; /* Text graphics context */ Tk_TimerToken token; /* Periodic callback handle*/ char *clock; /* Pointer to the clock string */ int numChars; /* length of the text */ int textWidth; /* in pixels */ Tcl_Obj *widthPtr; /* The original width string value*/ int textHeight; /* in pixels */ Tcl_Obj *heightPtr; /* The original height string value*/ int padX; /* Horizontal padding */ Tcl_Obj *padXPtr; /* The original padX string value*/ int padY; /* Vertical padding */ Tcl_Obj *padYPtr; /* The original padY string value */ int flags; /* Flags defined below */ } Clock; /* * Flag bit definitions. */ #define REDRAW_PENDING 0x1 #define GOT_FOCUS 0x2 #define TICKING 0x4 /* * Example 49-10 * Configuration specs for the clock widget. */ static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_BORDER, "-background", "background", "Background", "light blue", Tk_Offset(Clock, background), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", "white", Tk_Offset(Clock, background), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth","2", Tk_Offset(Clock, borderWidth), 0}, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "ridge", Tk_Offset(Clock, relief), 0}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", "black", Tk_Offset(Clock, foreground),0}, {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "red", Tk_Offset(Clock, highlight), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "black", Tk_Offset(Clock, highlight),TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "light blue", Tk_Offset(Clock, highlightBg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "black", Tk_Offset(Clock, highlightBg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness","HighlightThickness", "2", Tk_Offset(Clock, highlightWidth), 0}, {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Clock, padX), 0}, {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", "2", Tk_Offset(Clock, padY), 0}, {TK_CONFIG_STRING, "-format", "format", "Format", "%H:%M:%S", Tk_Offset(Clock, format), 0}, {TK_CONFIG_FONT, "-font", "font", "Font", "Courier 18", Tk_Offset(Clock, tkfont), 0}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; /* * Example 49-12 * The Tk_OptionSpec structure for the clock widget. */ #define GEOMETRY_MASK 0X1 #define GRAPHICS_MASK 0X2 static Tk_OptionSpec optionSpecs[] = { {TK_OPTION_BORDER, "-background", "background", "Background", "light blue", -1, Tk_Offset(Clock, background), 0, (ClientData) "white", GRAPHICS_MASK}, /* MOFIFY */ {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL, (char *) NULL, -1, 0, 0, (ClientData) "-background", 0}, {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", "2", Tk_Offset(Clock, borderWidthPtr), Tk_Offset(Clock, borderWidth), 0, 0, GEOMETRY_MASK}, /* MOFIFY */ {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL, (char *) NULL, -1, 0, 0, (ClientData) "-borderwidth", 0}, {TK_OPTION_RELIEF, "-relief", "relief", "Relief", "ridge", -1, Tk_Offset(Clock, relief), 0, 0, 0}, {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", "black",-1, Tk_Offset(Clock, foreground), 0, (ClientData) "black", GRAPHICS_MASK}, /* MOFIFY */ {TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL, (char *) NULL, -1, 0, 0, (ClientData) "-foreground", 0}, {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "red",-1, Tk_Offset(Clock, highlight), 0, (ClientData) "black", GRAPHICS_MASK}, {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "light blue",-1, Tk_Offset(Clock, highlightBg), 0, (ClientData) "white", GRAPHICS_MASK}, {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness","HighlightThickness", "2", Tk_Offset(Clock, highlightWidthPtr), Tk_Offset(Clock, highlightWidth), 0, 0, GEOMETRY_MASK}, {TK_OPTION_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Clock, padXPtr), Tk_Offset(Clock, padX), 0, 0, GEOMETRY_MASK}, {TK_OPTION_PIXELS, "-pady", "padY", "Pad", "2", Tk_Offset(Clock, padYPtr), Tk_Offset(Clock, padY), 0, 0, GEOMETRY_MASK}, {TK_OPTION_STRING, "-format", "format", "Format", "%H:%M:%S",-1, Tk_Offset(Clock, format), 0, 0, GEOMETRY_MASK}, {TK_OPTION_FONT, "-font", "font", "Font", "Courier 18", -1, Tk_Offset(Clock, tkfont), 0, 0, (GRAPHICS_MASK|GEOMETRY_MASK)}, {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, -1, 0, 0, 0, 0} }; /* * Example 49-1 * The Clock_Init procedure. */ int ClockCmd(ClientData clientData, Tcl_Interp *interp, int argc, CONST char *argv[]); int ClockObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); void ClockObjDelete(ClientData clientData); /* * forward all procedures */ static int ClockInstanceCmd(ClientData clientData, Tcl_Interp *interp, int argc, CONST char *argv[]); static int ClockInstanceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static int ClockConfigure(Tcl_Interp *interp, Clock *clockPtr, int argc, CONST char *argv[], int flags); static int ClockObjConfigure(Tcl_Interp *interp, Clock *clockPtr, int objc, Tcl_Obj *CONST objv[]); static void ComputeGeometry(Clock *clockPtr); static void ClockDisplay(ClientData clientData); static void ClockEventProc(ClientData clientData, XEvent *eventPtr); static void ClockDestroy(ClientData clientData); void ClockObjDelete(ClientData clientData); /* * Clock_Init is called when the package is loaded. */ EXTERN int Tkclock_Init(Tcl_Interp *interp) { if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { return TCL_ERROR; } /* MODIFY */ if (Tk_InitStubs(interp, "8.1", 0) == NULL) { return TCL_ERROR; } Tcl_CreateCommand(interp, "wclock", ClockCmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); Tcl_CreateObjCommand(interp, "oclock", ClockObjCmd, (ClientData)NULL, ClockObjDelete); Tcl_PkgRequire(interp, "Tk", "8.1",0); Tcl_PkgProvide(interp, "Tkclock", "1.0"); return TCL_OK; } /* * Example 49-3 * The ClockCmd command procedure. */ int ClockCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window of the app */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ CONST char **argv; /* Argument strings. */ { /* MODIFY */ /* Tk_Window main_tk = (Tk_Window) clientData; */ Clock *clockPtr; Tk_Window tkwin; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " pathName ?options?\"", (char *) NULL); return TCL_ERROR; } /* MODIFY */ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1], (char *) NULL); if (tkwin == NULL) { return TCL_ERROR; } /* * Set resource class. */ Tk_SetClass(tkwin, "Clock"); /* * Allocate and initialize the widget record. */ clockPtr = (Clock *) Tcl_Alloc(sizeof(Clock)); clockPtr->tkwin = tkwin; clockPtr->display = Tk_Display(tkwin); clockPtr->interp = interp; clockPtr->borderWidth = 0; clockPtr->highlightWidth = 0; clockPtr->relief = TK_RELIEF_FLAT; clockPtr->background = NULL; clockPtr->foreground = NULL; clockPtr->highlight = NULL; clockPtr->highlightBg = NULL; clockPtr->tkfont = NULL; clockPtr->textGC = None; clockPtr->token = NULL; clockPtr->clock = NULL; clockPtr->format = NULL; clockPtr->numChars = 0; clockPtr->textWidth = 0; clockPtr->textHeight = 0; clockPtr->padX = 0; clockPtr->padY = 0; clockPtr->flags = 0; /* * Register a handler for when the window is * exposed or resized. */ Tk_CreateEventHandler(clockPtr->tkwin, ExposureMask|StructureNotifyMask|FocusChangeMask, ClockEventProc, (ClientData) clockPtr); /* * Create a Tcl command that operates on the widget. */ clockPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(clockPtr->tkwin), ClockInstanceCmd, (ClientData) clockPtr, (void (*)()) NULL); /* * Parse the command line arguments. */ if (ClockConfigure(interp, clockPtr, argc-2, argv+2, 0) != TCL_OK) { Tk_DestroyWindow(clockPtr->tkwin); return TCL_ERROR; } interp->result = Tk_PathName(clockPtr->tkwin); return TCL_OK; } /* * Example 49-4 * The ClockObjCmd command procedure. */ int ClockObjCmd(clientData, interp, objc, objv) ClientData clientData; /* Main window of the app */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST objv[]; /* Argument values. */ { Tk_OptionTable optionTable; Clock *clockPtr; Tk_Window tkwin; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } optionTable = (Tk_OptionTable) clientData; if (optionTable == NULL) { Tcl_CmdInfo info; char *name; /* * Initialize the option table for this widget the * first time a clock widget is created. The option * table is saved as our client data. */ optionTable = Tk_CreateOptionTable(interp, optionSpecs); name = Tcl_GetString(objv[0]); Tcl_GetCommandInfo(interp, name, &info); info.objClientData = (ClientData) optionTable; Tcl_SetCommandInfo(interp, name, &info); } tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), (char *) NULL); if (tkwin == NULL) { return TCL_ERROR; } /* * Set resource class. */ Tk_SetClass(tkwin, "Clock"); /* * Allocate and initialize the widget record. */ clockPtr = (Clock *) ckalloc(sizeof(Clock)); clockPtr->tkwin = tkwin; clockPtr->display = Tk_Display(tkwin); clockPtr->interp = interp; clockPtr->optionTable = optionTable; clockPtr->borderWidth = 0; clockPtr->borderWidthPtr = NULL; clockPtr->highlightWidth = 0; clockPtr->highlightWidthPtr = NULL; clockPtr->relief = TK_RELIEF_FLAT; clockPtr->background = NULL; clockPtr->foreground = NULL; clockPtr->highlight = NULL; clockPtr->highlightBg = NULL; clockPtr->tkfont = NULL; clockPtr->textGC = None; clockPtr->token = NULL; clockPtr->clock = NULL; clockPtr->format = NULL; clockPtr->numChars = 0; clockPtr->textWidth = 0; clockPtr->widthPtr = NULL; clockPtr->textHeight = 0; clockPtr->heightPtr = NULL; clockPtr->padX = 0; clockPtr->padXPtr = NULL; clockPtr->padY = 0; clockPtr->padYPtr = NULL; clockPtr->flags = 0; /* * Register a handler for when the window is * exposed or resized. */ Tk_CreateEventHandler(clockPtr->tkwin, ExposureMask|StructureNotifyMask|FocusChangeMask, ClockEventProc, (ClientData) clockPtr); /* * Create a Tcl command that operates on the widget. */ clockPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(clockPtr->tkwin), ClockInstanceObjCmd, (ClientData) clockPtr, (void (*)()) NULL); /* * Parse the command line arguments. */ if ((Tk_InitOptions(interp, (char *)clockPtr, optionTable, tkwin) != TCL_OK) || (ClockObjConfigure(interp, clockPtr, objc-2, objv+2) != TCL_OK)) { Tk_DestroyWindow(clockPtr->tkwin); return TCL_ERROR; } Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(clockPtr->tkwin), -1); return TCL_OK; } /* * Example 49-5 * The ClockInstanceCmd command procedure. */ static int ClockInstanceCmd(clientData, interp, argc, argv) ClientData clientData; /* A pointer to a Clock struct */ Tcl_Interp *interp; /* The interpreter */ int argc; /* The number of arguments */ CONST char *argv[]; /* The command line arguments */ { Clock *clockPtr = (Clock *)clientData; int result = TCL_OK; char c; int len; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL); return TCL_ERROR; } c = argv[1][0]; len = strlen(argv[1]); if ((c == 'c') && (strncmp(argv[1], "cget", len) == 0) && (len >= 2)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " cget option\"", (char *) NULL); return TCL_ERROR; } result = Tk_ConfigureValue(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, argv[2], 0); } else if ((c == 'c') && (strncmp(argv[1], "configure", len) == 0) && (len >= 2)) { if (argc == 2) { /* * Return all configuration information. */ result = Tk_ConfigureInfo(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, (char *) NULL,0); } else if (argc == 3) { /* * Return info about one attribute, like cget. */ result = Tk_ConfigureInfo(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, argv[2], 0); } else { /* * Change one or more attributes. */ result = ClockConfigure(interp, clockPtr, argc-2, argv+2,TK_CONFIG_ARGV_ONLY); } } else { Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be cget, configure, position, or size", (char *) NULL); return TCL_ERROR; } return result; } /* * Example 49-6 * The ClockInstanceObjCmd command procedure. */ static int ClockInstanceObjCmd(clientData, interp, objc, objv) ClientData clientData; /* A pointer to a Clock struct */ Tcl_Interp *interp; /* The interpreter */ int objc; /* The number of arguments */ Tcl_Obj *CONST objv[]; /* The command line arguments */ { Clock *clockPtr = (Clock *)clientData; CONST char *commands[] = {"cget", "configure", NULL}; enum command {CLOCK_CGET, CLOCK_CONFIGURE}; int result; Tcl_Obj *objPtr; int index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); return TCL_ERROR; } result = Tcl_GetIndexFromObj(interp, objv[1], commands, "option", 0, &index); if (result != TCL_OK) { return result; } switch (index) { case CLOCK_CGET: { if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "cget option"); return TCL_ERROR; } objPtr = Tk_GetOptionValue(interp, (char *)clockPtr, clockPtr->optionTable, (objc == 3) ? objv[2] : NULL, clockPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } break; } case CLOCK_CONFIGURE: { if (objc <= 3) { /* * Return one item if the option is given, * or return all configuration information. */ objPtr = Tk_GetOptionInfo(interp, (char *) clockPtr, clockPtr->optionTable, (objc == 3) ? objv[2] : NULL, clockPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } } else { /* * Change one or more attributes. */ result = ClockObjConfigure(interp, clockPtr, objc-2, objv+2); } } } return TCL_OK; } /* * Example 49-7 * ClockConfigure allocates resources for the widget. */ static int ClockConfigure(interp, clockPtr, argc, argv, flags) Tcl_Interp *interp; /* For return values and errors */ Clock *clockPtr; /* The per-instance data structure */ int argc; /* Number of valid entries in argv */ CONST char *argv[]; /* The command line arguments */ int flags; /* Tk_ConfigureWidget flags */ { XGCValues gcValues; GC newGC; /* * Tk_ConfigureWidget parses the command line arguments * and looks for defaults in the resource database. */ if (Tk_ConfigureWidget(interp, clockPtr->tkwin, configSpecs, argc, argv, (char *) clockPtr, flags) != TCL_OK) { return TCL_ERROR; } /* * Give the widget a default background so it doesn\" t get * a random background between the time it is initially * displayed by the X server and we paint it */ Tk_SetWindowBackground(clockPtr->tkwin, Tk_3DBorderColor(clockPtr->background)->pixel); /* * Set up the graphics contexts to display the widget. * The context is used to draw off-screen pixmaps, * so turn off exposure notifications. */ gcValues.background = Tk_3DBorderColor(clockPtr->background)->pixel; gcValues.foreground = clockPtr->foreground->pixel; gcValues.font = Tk_FontId(clockPtr->tkfont); gcValues.graphics_exposures = False; newGC = Tk_GetGC(clockPtr->tkwin, GCBackground|GCForeground|GCFont|GCGraphicsExposures, &gcValues); if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } clockPtr->textGC = newGC; /* * Determine how big the widget wants to be. */ ComputeGeometry(clockPtr); /* * Set up a call to display ourself. */ if ((clockPtr->tkwin != NULL) && Tk_IsMapped(clockPtr->tkwin) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } return TCL_OK; } /* * Example 49-8 * ClockObjConfigure allocates resources for the widget. */ static int ClockObjConfigure(interp, clockPtr, objc, objv) Tcl_Interp *interp; /* For return values and errors */ Clock *clockPtr; /* The per-instance data structure */ int objc; /* Number of valid entries in argv */ Tcl_Obj *CONST objv[]; /* The command line arguments */ { XGCValues gcValues; GC newGC; Tk_SavedOptions savedOptions; int mask, error; Tcl_Obj *errorResult; /* * The first time through this loop we set the * configuration from the command line inputs. The second * pass is used to restore the configuration in case of * errors */ for (error = 0 ; error <= 1 ; error++) { if (!error) { /* * Tk_SetOptions parses the command arguments * and looks for defaults in the resource * database. */ if (Tk_SetOptions(interp, (char *) clockPtr, clockPtr->optionTable, objc, objv, clockPtr->tkwin, &savedOptions, &mask) != TCL_OK) { continue; } } else { /* * Restore options from saved values */ errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } /* MODIFY || clockPtr->textGC == None''' to force correct graphics context initialisation */ if (mask & GRAPHICS_MASK || clockPtr->textGC == None) { /* * Give the widget a default background so it doesn\" t * get a random background between the time it is * initially displayed by the system and we paint it */ Tk_SetBackgroundFromBorder(clockPtr->tkwin, clockPtr->background); /* * Set up the graphics contexts to display the widget. * The context is used to draw off-screen pixmaps, * so turn off exposure notifications. */ gcValues.background = Tk_3DBorderColor(clockPtr->background)->pixel; gcValues.foreground = clockPtr->foreground->pixel; gcValues.font = Tk_FontId(clockPtr->tkfont); gcValues.graphics_exposures = False; newGC = Tk_GetGC(clockPtr->tkwin, GCBackground|GCForeground|GCFont|GCGraphicsExposures, &gcValues); if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } clockPtr->textGC = newGC; } /* * Determine how big the widget wants to be. */ /* MODIFY || clockPtr->numChars == 0) to force a geometry query */ if (mask & GEOMETRY_MASK || clockPtr->numChars == 0) { ComputeGeometry(clockPtr); } /* * Set up a call to display ourself. */ if ((clockPtr->tkwin != NULL) && Tk_IsMapped(clockPtr->tkwin) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } /* * All OK, break out and avoid error rollback. */ break; } if (!error) { Tk_FreeSavedOptions(&savedOptions); return TCL_OK; } else { Tcl_SetObjResult(interp, errorResult); Tcl_DecrRefCount(errorResult); return TCL_ERROR; } } /* * Example 49-13 * ComputeGeometry computes the widget\" s size. */ static void ComputeGeometry(Clock *clockPtr) { int width, height; Tk_FontMetrics fm; /* Font size information */ struct tm *tmPtr; /* Time info split into fields */ /* MODIFY */ /* struct timeval tv; */ /* BSD-style time value */ int bd; /* Padding from borders */ char clock[1000]; /* Displayed time */ /* * Get the time and format it to see how big it will be. */ /* MODIFY */ #ifdef __WIN32__ time_t aclock; time( &aclock ); /* Get time in seconds */ tmPtr = localtime( &aclock ); /* Convert time to struct tm form */ #else struct timeval tv; /* BSD-style time value */ gettimeofday(&tv, NULL); tmPtr = localtime(&tv.tv_sec); #endif strftime(clock, 1000, clockPtr->format, tmPtr); if (clockPtr->clock != NULL) { ckfree(clockPtr->clock); } clockPtr->clock = ckalloc(1+strlen(clock)); clockPtr->numChars = strlen(clock); bd = clockPtr->highlightWidth + clockPtr->borderWidth; Tk_GetFontMetrics(clockPtr->tkfont, &fm); height = fm.linespace + 2*(bd + clockPtr->padY); /* MODIFY (-1, TK_PARTIAL_OK) to force unbounded linelength */ Tk_MeasureChars(clockPtr->tkfont, clock, clockPtr->numChars, -1, TK_PARTIAL_OK, &clockPtr->textWidth); width = clockPtr->textWidth + 2*(bd + clockPtr->padX); Tk_GeometryRequest(clockPtr->tkwin, width, height); Tk_SetInternalBorder(clockPtr->tkwin, bd); } /* * Example 49-14 * The ClockDisplay procedure. */ static void ClockDisplay(ClientData clientData) { Clock *clockPtr = (Clock *)clientData; Tk_Window tkwin = clockPtr->tkwin; GC gc; /* Graphics Context for highlight */ Tk_TextLayout layout; /* Text measurement state */ Pixmap pixmap; /* Temporary drawing area */ int /* offset,*/ x, y; /* Coordinates */ int width, height; /* Size */ struct tm *tmPtr; /* Time info split into fields */ /* MODIFY */ #ifdef __WIN32__ time_t aclock; #else struct timeval tv; /* BSD-style time value */ #endif /* * Make sure the clock still exists * and is mapped onto the display before painting. */ clockPtr->flags &= ~(REDRAW_PENDING|TICKING); if ((clockPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } /* * Format the time into a string. * localtime chops up the time into fields. * strftime formats the fields into a string. */ /* MODIFY */ #ifdef __WIN32__ /* time_t aclock; */ time( &aclock ); /* Get time in seconds */ tmPtr = localtime( &aclock ); /* Convert time to struct tm form */ #else struct timeval tv; /* BSD-style time value */ gettimeofday(&tv, NULL); tmPtr = localtime(&tv.tv_sec); #endif strftime(clockPtr->clock, clockPtr->numChars+1, clockPtr->format, tmPtr); /* * To avoid flicker when the display is updated, the new * image is painted in an offscreen pixmap and then * copied onto the display in one operation. Allocate the * pixmap and paint its background. */ pixmap = Tk_GetPixmap(clockPtr->display, Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); Tk_Fill3DRectangle(tkwin, pixmap, clockPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); /* * Paint the text first. */ layout = Tk_ComputeTextLayout(clockPtr->tkfont, clockPtr->clock, clockPtr->numChars, 0, TK_JUSTIFY_CENTER, 0, &width, &height); x = (Tk_Width(tkwin) - width)/2; y = (Tk_Height(tkwin) - height)/2; Tk_DrawTextLayout(clockPtr->display, pixmap, clockPtr->textGC, layout, x, y, 0, -1); /* * Display the borders, so they overwrite any of the * text that extends to the edge of the display. */ if (clockPtr->relief != TK_RELIEF_FLAT) { Tk_Draw3DRectangle(tkwin, pixmap, clockPtr->background, clockPtr->highlightWidth, clockPtr->highlightWidth, Tk_Width(tkwin) - 2*clockPtr->highlightWidth, Tk_Height(tkwin) - 2*clockPtr->highlightWidth, clockPtr->borderWidth, clockPtr->relief); } if (clockPtr->highlightWidth != 0) { /* * This GC is associated with the color, and Tk caches * the GC until the color is freed. Hence no freeGC. */ if (clockPtr->flags & GOT_FOCUS) { gc = Tk_GCForColor(clockPtr->highlight, pixmap); } else { gc = Tk_GCForColor(clockPtr->highlightBg, pixmap); } Tk_DrawFocusHighlight(tkwin, gc, clockPtr->highlightWidth, pixmap); } /* * Copy the information from the off-screen pixmap onto * the screen, then delete the pixmap. */ XCopyArea(clockPtr->display, pixmap, Tk_WindowId(tkwin), clockPtr->textGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); Tk_FreePixmap(clockPtr->display, pixmap); /* * Queue another call to ourselves. The rate at which * this is done could be optimized. */ clockPtr->token = Tk_CreateTimerHandler(1000, ClockDisplay, (ClientData)clockPtr); clockPtr->flags |= TICKING; } /* * Example 49-15 * The ClockEventProc handles window events. */ static void ClockEventProc(ClientData clientData, XEvent *eventPtr) { Clock *clockPtr = (Clock *) clientData; if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { goto redraw; } else if (eventPtr->type == DestroyNotify) { Tcl_DeleteCommandFromToken(clockPtr->interp, clockPtr->widgetCmd); /* * Zapping the tkwin lets the other procedures * know we are being destroyed. */ clockPtr->tkwin = NULL; if (clockPtr->flags & REDRAW_PENDING) { Tk_CancelIdleCall(ClockDisplay, (ClientData) clockPtr); clockPtr->flags &= ~REDRAW_PENDING; } if (clockPtr->flags & TICKING) { Tk_DeleteTimerHandler(clockPtr->token); clockPtr->flags &= ~TICKING; } /* * This results in a call to ClockDestroy. */ Tk_EventuallyFree((ClientData) clockPtr, ClockDestroy); } else if (eventPtr->type == FocusIn) { if (eventPtr->xfocus.detail != NotifyPointer) { clockPtr->flags |= GOT_FOCUS; if (clockPtr->highlightWidth > 0) { goto redraw; } } } else if (eventPtr->type == FocusOut) { if (eventPtr->xfocus.detail != NotifyPointer) { clockPtr->flags &= ~GOT_FOCUS; if (clockPtr->highlightWidth > 0) { goto redraw; } } } return; redraw: if ((clockPtr->tkwin != NULL) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } } /* * Example 49-16 * The ClockDestroy cleanup procedure. */ static void ClockDestroy(clientData) ClientData clientData; /* Info about entry widget. */ { register Clock *clockPtr = (Clock *) clientData; /* * Free up all the stuff that requires special handling, * then let Tk_FreeOptions handle resources associated * with the widget attributes. */ if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } if (clockPtr->clock != NULL) { Tcl_Free(clockPtr->clock); } if (clockPtr->flags & TICKING) { Tk_DeleteTimerHandler(clockPtr->token); } if (clockPtr->flags & REDRAW_PENDING) { Tk_CancelIdleCall(ClockDisplay, (ClientData) clockPtr); } /* * This frees up colors and fonts and any allocated * storage associated with the widget attributes. */ Tk_FreeOptions(configSpecs, (char *) clockPtr, clockPtr->display, 0); Tcl_Free((char *) clockPtr); } /* * Example 49-17 * The ClockObjDelete command. */ void ClockObjDelete(ClientData clientData) { Tk_OptionTable optionTable = (Tk_OptionTable) clientData; if (optionTable != NULL) { Tk_DeleteOptionTable(optionTable); } } ====== <> Education | Example