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


Configuring and Reconfiguring Attributes

When the widget is created or reconfigured, then the implementation needs to allocate the resources implied by the attribute settings. Each clock widget uses some colors and a font. These are described by graphics contexts that parameterize operations. Instead of specifying every possible attribute in graphics calls, a graphics context is initialized with a subset of the parameters, and this is passed into the graphic commands. The context can specify the foreground and background colors, clip masks, line styles, and so on. The clock widget allocates a graphics context once and reuses it each time the widget is displayed.

There are two kinds of color resources used by the widget. The focus highlight and the text foreground are simple colors. The background is a Tk_3DBorder, which is a set of colors used to render 3D borders. The background color is specified in the attribute, and the other colors are computed based on that color. The code uses Tk_3DBorderColor to map back to the original color for use in the background of the widget.

After the resources are set up, a call to redisplay the widget is scheduled for the next idle period. This is a standard idiom for Tk widgets. It means that you can create and reconfigure a widget in the middle of a script, and all the changes result in only one redisplay. The REDRAW_PENDING flag is used to ensure that only one redisplay is queued up at any time. The ClockConfigure procedure is shown in the next example:

Example 46-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 */
   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 46-8 shows the ClockObjConfigure procedure. The Tk_SetOptions interface, which is used to set fields in the Clock data structure, has one potential problem. It is possible that some configuration options are correct, while others cause errors. In this case, ClockObjConfigure backs out the changes, so the whole configuration has no effect. This requires a two-pass approach, with the second pass used to restore the original values. Tk_SetOptions has a feature that lets you classify changes to the widget. The GEOMETRY_MASK and GRAPHICS_MASK are bits defined by the clock widget to divide its attributes into two classes. It changes its graphics context or recomputes its geometry only if an attribute from the appropriate class is changed.

Example 46-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 *objv[]; /* The command line arguments */
{
   XGCValues gcValues;
   GC newGC;
   Tk_SavedOptions savedOptions;
   int mask, error, new;
   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
    */
   new = (clockPtr->clock == NULL);
   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);
      }
      if (new || (mask & GRAPHICS_MASK)) {
          /*
           * 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.
        */
       if (new || (mask & GEOMETRY_MASK)) {
          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;
       }
    }
    if (!error) {
       Tk_FreeSavedOptions(&savedOptions);
       return TCL_OK;
    } else {
       Tcl_SetObjResult(interp, errorResult);
       Tcl_DecrRefCount(errorResult);
       return TCL_ERROR;
    }
}

      Previous section   Next section
    Top