Previous section   Next section

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

Table of Contents
Chapter 42.  Managing User Preferences

Defining Preferences

This section describes the Pref_Add procedure that an application uses to define preference items. A preference item defines a relationship between a Tcl variable and a resource name. If the Tcl variable is undefined at the time Pref_Add is called, then it is set from the value for the resource. If the resource is not defined, then the variable is set to the default value.


Hide simple data structures with Tcl procedures.

A default value, a label, and a more extensive help string are associated with each item, which is represented by a Tcl list of five elements. A few short routines hide the layout of the item lists and make the rest of the code read better:

Example 42-2 Adding preference items.
proc PrefVar { item } {lindex $item 0 }
proc PrefRes { item } {lindex $item 1 }
proc PrefDefault { item } {lindex $item 2 }
proc PrefComment { item } {lindex $item 3 }
proc PrefHelp { item } {lindex $item 4 }

proc Pref_Add { prefs } {
   global pref
   append pref(items) $prefs " "
   foreach item $prefs {
      set varName [PrefVar $item]
      set resName [PrefRes $item]
      set value [PrefValue $varName $resName]
      if {$value == {}} {
         # Set variables that are still not set
         set default [PrefDefault $item]
         switch -regexp -- $default {
            ^CHOICE {
               PrefValueSet $varName [lindex $default 1]
            ^OFF {
               PrefValueSet $varName 0
            ^ON {
               PrefValueSet $varName 1
            default {
               # This is a string or numeric
               PrefValueSet $varName $default

The procedures PrefValue and PrefValueSet are used to query and set the value of the named variable, which can be an array element or a simple variable. The upvar #0 command sets the variable in the global scope.

Example 42-3 Setting preference variables.
# PrefValue returns the value of the variable if it exists,
# otherwise it returns the resource database value
proc PrefValue { varName res } {
   upvar #0 $varName var
   if [info exists var] {
      return $var
   set var [option get . $res {}]
# PrefValueSet defines a variable in the global scope.
proc PrefValueSet { varName value } {
   upvar #0 $varName var
   set var $value

An important side effect of the Pref_Add call is that the variables in the preference item are defined at the global scope. It is also worth noting that PrefValue will honor any existing value for a variable, so if the variable is already set at the global scope, then neither the resource value nor the default value will be used. It is easy to change PrefValue to always set the variable if this is not the behavior you want. Here is a sample call to Pref_Add:

Example 42-4 Using the preferences package.
Pref_Add {
   {win(scrollside) scrollbarSide {CHOICE left right}
      "Scrollbar placement"
"Scrollbars can be positioned on either the left or
right side of the text and canvas widgets."}
   {win(typeinkills) typeinKills OFF
      "Type-in kills selection"
"This setting determines whether or not the selection
is deleted when new text is typed in."}
   {win(scrollspeed) scrollSpeed 15 "Scrolling speed"
"This parameter affects the scrolling rate when a selection
is dragged off the edge of the window. Smaller numbers
scroll faster, but can consume more CPU."}

Any number of preference items can be specified in a call to Pref_Add. The list-of-lists structure is created by proper placement of the curly braces, and it is preserved when the argument is appended to pref(items), which is the master list of preferences. In this example, Pref_Add gets passed a single argument that is a Tcl list with three elements. The Tcl variables are array elements, presumably related to the Win module of the application. The resource names are associated with the main application as opposed to any particular widget. They are specified in the database like this:

*scrollbarSide: left
*typeinKills: 0
*scrollSpeed: 15

      Previous section   Next section