www.gibmonks.com




  Previous section   Next section

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 46.  Writing a Tk Widget in C


The Widget Class Command

The Tcl command that creates an instance of a widget is known as the class command. In our example, the clock command creates a clock widget. The command procedure for clock follows. The procedure allocates the Clock data structure. It registers an event handler that gets called when the widget is exposed, resized, or gets the focus. It creates a new Tcl command that operates on the widget. Finally, it calls ClockConfigure to set up the widget according to the attributes specified on the command line and the default configuration specifications.

Example 46-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. */
   char **argv;        /* Argument strings. */
{
   Tk_Window main = (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;
   }
   tkwin = Tk_CreateWindowFromPath(interp, main,
           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;
}

The Tcl_Obj version, ClockObjCmd, does some additional work to set up an option table that is used to efficiently parse the command line options to the clock command. The option table is created the first time the clock command is used. The clientData for ClockObjCmd is initially NULL; it is used to store the option table once it is initialized. While ClockCmd uses the clientData to store a reference to the main Tk window, ClockObjCmd uses the Tk_MainWindow procedure to get a reference to the main Tk window.

Example 46-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 **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 *) Tcl_Alloc(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, 0) != TCL_OK)) {
      Tk_DestroyWindow(clockPtr->tkwin);
      return TCL_ERROR;
   }
   Tcl_SetStringObj(Tcl_GetObjResult(interp),
      Tk_PathName(clockPtr->tkwin), -1);
   return TCL_OK;
}

      Previous section   Next section
    Top