6 Tix Object Oriented Programming
   This chapter is intended for experienced programmers who want to
  create new Tix widgets. If you just want use the Tix widgets in your
  applications, you can skip this chapter.
6.1 Introduction to Tix Object Oriented Programming
  Tix comes with a simple object oriented programming (OOP) framework,
  the 
 Tix Intrinsics, for writing mega-widgets. The Tix
  Intrinsics is not a general purpose OOP system and it does not
  support some features found in general purpose OOP systems such as
  
 [incr Tcl]. However, the Tix Intrinsics is specially designed
  for writing mega-widgets. It provides a simple and efficient
  interface for creating mega-widgets so that you can avoid the
  complexity and overheads of the general purpose OOP extensions to
  Tcl.
  The hard thing about programming with mega-widgets is to make sure
  that each instance you create can handle its own activities.  Events
  must be directed to the right widget, procedures must act on data
  that is internal to that widget, and users should be able to change
  the options associated with the widget.  For instance, we'll show an
  arrow widget that needs to know what direction it's pointing; this
  requires each instance of the widget to have its own variable.
  Furthermore, each widget should respond properly to changes
  requested by the application programmer during the program's run.
  The whole reason people use Tcl/Tk is because they can alter things
  on the fly.
  The advantage of an object-oriented programming system is that you
  can easily associate a widget with its own data and procedures
  (methods).  This chapter shows how to do that, and how to configure
  data both at the time the widget is initialized and later during the
  program.
6.1.1 Widget Classes and Widget Instances
  All the mega-widget classes in Tix, such as TixComboBox and
  TixControl, are implemented in the Tix Intrinsics framework. Also,
  you can write new 
 widget classes with the Tix Intrinsics. In
  the next section, I'll go through all the steps of creating a new
  widget class in Tix. I'll illustrate the idea using a new class
  ``TixArrowButton'' as an example. TixArrowButton is essentially a
  button that can display an arrow in one of the for directions
  (see figure 
oop.tex.html#6-16-1 ).
(Figure 6-1) Arrow Buttons
  Once you have defined your classes, you can create  widget
  instances
 of these classes. For example, the following code will
  create four instances of your new TixArrowButton class:
tixArrowButton .up    -direction n
tixArrowButton .left  -direction e
tixArrowButton .right -direction w
tixArrowButton .down  -direction s
6.1.2 What is in a Widget Instance
  Each widget instance is composed of three integral parts: variables,
  methods and component widgets
 Variables
  Each widget instance is associated with a set of variables. In the
  example of an instance of the TixArrowButton class, we may use a
  variable to store the direction to which the arrow is pointing
  to. We may also use a variable to count how many times the user has
  pressed the button.
  Each variable can be public or private. Public variables may be
  accessed by the application programmer (usually via 
 configure  or cget 
 methods) and their names usually start with a dash
  (
 -). They usually are used to represent some user-configurable
  options of the widget instance. Private variables, on the other
  hand, cannot be accessed by the application programmer. They are
  usually used to store information about the widget instance that are
  of interests only to the widget writer.
  All the variables of an instance are stored in a global array that
  has the same name as the instance. For example, the variables of the
  instance 
 .up are stored in the global array  .up:. The
  public variable 
 -direction, which records the direction to
  which the arrow is pointing to, is stored in 
 .up(-direction).
  The private variable 
 count, which counts how many times the
  user has pressed the button, is stored in 
 .up(count). In
  comparison, the same variables of the 
 .down instance are
  stored in 
 .down(-direction) and  .down(count).
 Methods
  To carry out operations on the widget, you define a set of
  procedures called 
 methods (to use common object-oriented
  terminology). Each method can be declared as public or private. 
  Public methods
 can be called by the application programmer. For
  example, if the TixArrowButton class supports the public methods
  
 invoke and  invert, the application programmer can issue
  the commands to call these method for the widget instance 
 .up.
.up invert
.up invoke
  In contrast, 
 Private methods are of interests only to widget
  writers and cannot be called by application programmers.
 Component Widgets
  A Tix mega-widget is composed of one or more component
  widgets. The main part of a mega-widget is called the 
 root
  widget
, which is usually a frame widget that encompasses all other
  component widgets. The other component widgets are called 
  subwidgets
.
  The root widget has the same name as the the mega-widget itself. In
  the above example, we have a mega-widget called 
 .up. It has a
  root widget which is a frame widget and is also called 
  .up
. Inside  .up we have a button subwidget called   .up.button
.
  Similar to variables and methods, component widgets are also
  classified into public and private component widgets. Only public
  widgets may be accessed by the application programmer, via the 
  subwidget
 method (see section intro.tex.html#1.3.11.3.1 ) of each widget
  instance.
6.2 Widget Class Declaration
  The first step of writing a new widget class is to decide the base
  class from which the new class. Usually, if the new class does not
  share any common features with other classes, it should be derived
  from the TixPrimitive class. If it does share common features with
  other classes, then it should be derived from the appropriate base
  class. For example, if the new class support scrollbars, it should
  be derived from TixScrolledWidget; if it displays a label next to
  its ``main area'', then it should be derived from TixLabelWidget.
  In the case of our new TixArrowButton class, it doesn't really share
  any common features with other classes, so we decide to use the base
  class TixPrimitive as its superclass.
6.2.1 Using the tixWidgetClass Command
  We can use the  tixWidgetClass command to declare a new
  class. The syntax is:
tixWidgetClass classCommandName {
    -switch value
    -switch value
    ....
}
  For example, the following is the declaration section of
  TixArrowButton:
tixWidgetClass tixArrowButton {
    -classname  TixArrowButton
    -superclass tixPrimitive
    -method {
        flash invoke invert
    }
    -flag {
        -direction -state
    }
    -configspec {
        {-direction direction Direction e}
        {-state state State normal}
    }
    -alias {
        {-dir -direction}
    }
    -default {
        {*Button.anchor         c}
        {*Button.padX           5}
    }
}
(Figure 6-2) declaration of the TixArrowButton Class
  We'll look at what each option means as I describe the command in
  the following sections.
  The first argument for  tixWidgetClass is the  command
  name
 for the widget class ( tixArrowButton). Command names are
  used to create widgets of this class. For example, the code
tixArrowButton .arrow
  creates a widget instance 
 .arrow of the class
  TixArrowButton. Also, the command name is used as a prefix of all
  the methods of this class. For example, the 
 Foo and  Bar  methods of the class TixArrowButton will be written as 
  tixArrowButton:Foo
 and  tixArrowButton:Bar.
  The  class name of the class ( TixArrowButton)is specified
  by the 
 -classname switch inside the main body of the
  declaration. The class name is used only to specify options in the
  TK option database. For example, the following commands specifies
  the TixArrowButton widget instances should have the default value
  
 up for their  -direction option and the default value
  
 normal for their  -state option.
option add *TixArrowButton.direction up
option add *TixArrowButton.state     normal
   Notice the difference in the capitalization of the class name
  and the command name of the TixArrowButton class: both of them has
  the individual words capitalized, but the command name (
  tixArrowButton
)starts with a lower case letter while the class name
  (
 TixArrowButton) starts with an upper case letter. When you
  create your own classes, you should follow this naming convention.
  The 
 -superclass switch specifies the superclass of the new
  widget. In our example, we have set it to 
 tixPrimitive. Again,
  pay attention to the capitalization: we should use the command name
  of the superclass, not its class name.
6.3 Writing Methods
  After we have declared the new widget class, we can write methods
  for this class to define its behavior. Methods are just a special
  type of TCL procedures and they are created by the 
 proc  command. There are, however, three requirements for methods. First,
  their names must be prefixed by the command name of their
  class. Second, they must accept at least one argument and the first
  argument that they accept must be called 
 w. Third, the first
  command executed inside each method must be 
upvar #0 $w data
  For example, the following is an implementation of the invert method
  for the class TixArrowButton:
proc tixArrowButton:invert {w} {
    upvar #0 $w data
    set curDirection $data(-direction)
    case $curDirection {
        n {
            set newDirection s
        }
        s {
            set newDirection n
        }
        # ....
    }
}
  Notice that the name of the method is prefixed by the command name
  of the class (
 tixArrowButton). Also, the first and only
  argument that it accepts is 
 w and the first line it executes
  is ``
 upvar #0 $w data''.
  The argument  w specifies which widget instance this method
  should act upon. For example, if the user has issued the command
.up invert
  on an instance 
 .up of the class tixArrowButton, the method
  
 tixArrowButton:invert will be called and the argument  w  will have the value 
 .up.
  The  invert method is used to invert the direction of the
  arrow. Therefore, it should examine the variable 
  .up(-direction)
, which stores the current direction of the instance
  
 .up, and modify it appropriately. It turns out that in TCL,
  the only clean way to access an array whose name is stored in a
  variable is the ``
 upvar #0 $w data'' technique: essentially
  it tells the intepreter that the array data should be an alias for
  the global array whose name is stored in 
 $w. We will soon see
  how the widget's methods use the data array.
  Once the mysterious `` upvar #0 $w data'' line is explained,
  it becomes clear what the rest of the 
 tixArrowButton:invert  method does: it examines the current direction of the arrow, which
  is stored in 
 $data(-direction) and inverts it.
6.3.1 Declaring Public Methods
  All the methods of a class are by default private methods and cannot
  be accessed by the application programmer. If you want to make a
  method public, you can include its name in the 
 -method section
  of the class declaration. In our TixArrowButton example, we have
  declared that the methods 
 flash,  invert and  invoke  are public methods and they can be accessed by the application
  programmer. All other methods of the TixArrowButton class will be
  private.
  Usually, the names of private methods start with a capital letter
  with individual words capitalized. The names of public methods
  start with a lowercase letter.
6.4 Standard Initialization Methods
  Each new mega-widget class must supply three standard initialization
  methods.  When an instance of a Tix widget is created, three
  three methods will be called to initialize this instance. The
  methods are 
 InitWidgetRec,  ConstructWidget and   SetBindings
 and they will be called in that order. The following
  sections show how these methods can be implemented.
6.4.1 The InitWidgetRec Method
  The purpose of the  InitWidgetRec method is to initialize the
  variables of the widget instance. For example, the following
  implementation of 
 tixArrowButton:InitWidgetRec sets the   count
 variable of each newly created instance to zero.
proc tixArrowButton:InitWidgetRec {w} {
    upvar #0 $w data
    set data(count) 0
}
  Earlier, we showed how each widget you create is associated with an
  array of the same name.  Within the methods, you always refer to
  this array through the name 
 data --the method then works
  properly in each instance of the widget.
 Chaining Methods
  The above implementation is not sufficient because our
  TixArrowButton class is derived from TixPrimitive. The class
  derivation in Tix is basically an 
 is-a relationship:
  TixArrowButton 
 is a TixPrimitive. TixPrimitive defines the
  method 
 tixPrimitive:InitWidgetRec which sets up the instance
  variables of every instance of TixPrimitive. Since an instance of
  TixArrowButton is also an instance of TixPrimitive, we need to make
  sure that the instance variables defined by TixPrimitive are also
  properly initialized. The technique of calling a method defined in a
  superclass is called the 
 chaining of a method. The following
  implementation does this correctly:
proc tixArrowButton:InitWidgetRec {w} {
    upvar #0 $w data
    tixPrimitive:InitWidgetRec $w
    set data(count) 0
}
  Notice that 
 tixPrimitive:InitWidgetRec is called before anything
  else is done. This way, we can define new classes by means of
  successive refinement: we can first ask the superclass to set up the
  instance variables, then we can modify some of those variables when
  necessary and also define new variables.
 The tixChainMethod call
  The above implementation of  tixArrowButton:InitWidgetRec is
  correct but it may be cumbersome if we want to switch
  superclasses. For example, suppose we want to create a new base class
  TixArrowWidget, which presumably defines common attributes of any
  class that have arrows in them. Then, instead of deriving
  TixArrowButton directly from TixPrimitive, we decide to derive
  TixArrowButton from TixArrowWidget, which is in turn derived from
  TixPrimitive:
tixWidgetClass tixArrowWidget {
    -superclass tixPrimitive
    ...
}
tixWidgetClass tixArrowButton {
    -superclass tixArrowWidget
    ...
}
  Now we would need to change all the method chaining calls in
  TixArrowButton from:
tixPrimitive:SomeMethod
  to:
tixArrowWidget:SomeMethod
  This may be a lot of work because you may have chained methods in many
  places in the original implementation of TixArrowButton.
  The  tixChainMethod command solves this problem. It will
  automatically find a superclass that defines the method we want to
  chain and calls this method for us. For example, the following is a
  better implementation of 
 tixArrowButton:InitWidgetRec that
  uses 
 tixChainMethod to avoid calling   tixPrimitive:InitWidgetRec
 directly:
proc tixArrowButton:InitWidgetRec {w} {
    upvar #0 $w data
    tixChainMethod $w InitWidgetRec
    set data(count) 0
}
  Notice the order of the arguments for tixChainMethod: the name of
  the instance, 
 $w, is passed before the method we want to
  chain, 
 InitWidgetRec. In general, if the method we want to
  chain has $1+n$ arguments:
proc tixPrimitive:MethodToChain {w arg1 arg2 ... argn} {
    ...
}
  We call it with the arguments in the following order
tixChainMethod $w MethodToChain $arg1 $arg2 ... $argn
  We'll come back to more detailed discussion of 
 tixChainMethod  shortly. For the time being, let's take it for granted that 
  tixChainMethod
 must be used in the three standard initialization
  methods: 
 InitWidgetRec,  ConstructWidget and   SetBindings
6.4.2 The ConstructWidget Method
  The  ConstructWidget method is used to creates the components
  of a widget instance. In the case of TixArrowButton, we want to
  create a new button subwidget, whose name is 
 button, and use a
  bitmap to display an arrow on this button. Assuming the bitmap files
  are stored in the files 
 up.xbm,  down.xbm,  left.xbm  and 
 right.xbm, the string substitution   @$data(-direction).xbm
 will give us the appropriate bitmap
  depending on the current direction option of the widget instance.
proc tixArrowButton:ConstructWidget {w} {
    upvar #0 $w data
    tixChainMethod $w ConstructWidget
    set data(w:button) [button $w.button -bitmap @$data(-direction).xbm]
    pack $data(w:button) -expand yes -fill both
}
  The  tixArrowButton:ConstructWidget method shown above sets
  the variable 
 data(w:button) to be the pathname of the   button
 subwidget. As a convention of the Tix Intrinsics, we must
  declare a public subwidget 
 swid by storing its pathname in the
  variable 
 data(w: swid ).
6.4.3 The SetBindings Method
  In your interface, you want to handle a lot of events in the
  subwidgets that make up your mega-widget.  For instance, when
  somebody presses the button in a TixArrowButton widget, you want the
  button to handle the event. The 
 SetBindings method is used to
  creates event bindings for the components inside the mega-widget. In
  our TixArrowButton example, we use the bind command to specify that
  the method 
 tixArrowButton:IncrCount should be called each
  time when the user presses the first mouse button. As a result, we
  can count the number of times the user has pressed on the button
  (obviously for no better reasons than using it as a dumb example).
proc tixArrowButton:SetBindings {w} {
    upvar #0 $w data
    tixChainMethod $w SetBindings
    bind $data(w:button) <1> "tixArrowButton:IncrCount $w"
}
proc tixArrowButton:IncrCount {w} {
    upvar #0 $w data
    incr data(count)
}
6.5 Declaring and Using Variables
  The private variables of a widget class do not need to be
  declared. In fact they can be initialized and used anywhere by any
  method. Usually, however, general purpose private variables are
  initialized by the 
 InitWidgetRec method and subwidget
  variables are initialized in the 
 ConstructWidget method.
  We have seen in the  tixArrowButton:InitWidgetRec example that
  the private variable 
 data(count) was initialized there. Also,
  the private variable 
 data(w:button) was initialized in   tixArrowButton:ConstructWidget
 and subsequently used in   tixArrowButton:SetBindings
.
  In contrast, public variables must be declared inside the class
  declaration. The following arguments are used to declare the public
  variables and specify various options for them:
    -flag: As shown in the class declaration in figure
  
oop.tex.html#6-26-2 , the  -flag argument declares all the public
  variables of the TixArrowButton class, 
 -direction and   -state
    -configspec: We can use the  -configspec argument to
  specify the details of each public variable. For example, the
  following declaration
-configspec {
    {-direction direction Direction e}
    {-state state State normal}
}
   specifies that the  -direction variable has the resource
  name 
 direction and resource class  Direction; its default
  value is 
 e. The application programmer can assign value to
  this variable by using the 
 -direction option in the command
  line or by specifying resources in the Tk option database with its
  resource name or class. The declaration of 
 -state installs
  similar definitions for that variable.
    -alias: The  -alias argument is used to specify
  alternative names for public variables. In our example, the setting
-alias {
    {-dir -direction}
}
  specifies that 
 -dir is the same variable as   -direction
. Therefore, when the application issue the command
.up config -dir w
 it is the same as issuing
.up config -direction w
  The 
 -alias option provides only an alternative name for
  the application programmer. Inside the widget's implementation code,
  the variable is still accessed as 
 data(-direction),  not  
 data(-dir).  
6.5.1 Initialization of Public Variables
  When a widget instance is created, all of its public variables are
  initialized by the Tix Intrinsics before the 
 InitWidgetRec  method is called. Therefore, 
 InitWidgetRec and any other
  method of this widgte instance are free to assume that all the
  public variables have been properly initialized and use them as
  such.
  The public variables are initialized by the following criteria.
    Step 1: If the value of the variable is specified by the
  creation command, this value is used. For example, if the
  application programmer has created an instance in the following way:
tixArrowButton .arr -direction n
  The value 
 n will be used for the -direction variable.
    Step 2: if step 1 fails but the value of the variable is
  specified in the options database, that value is used. For example,
  if the user has created an instance in the following way:
option add *TixArrowButton.direction w
tixArrowButton .arr
  The value 
 w will be used for the  -direction variable.
    Step3: if step 2 also fails, the default value specified in
  the 
 -configspec secton of the class declaration will be used.
 Type Checker
  You can use a  type ckecker procedure to check whether the user
  has supplied a value of the correct type for a public variable. The
  type checker is specified in the 
 -configspec section of the
  class declaration after the default value. The following code
  specifies the type checker procedure 
 CheckDirection for the
  
 -direction variable:
    -configspec {
        {-direction direction Direction e CheckDirection}
        {-state state State normal}
    }
    ...
}
proc CheckDirection {dir} {
    if {[lsearch {n s w e} $dir] != -1} {
        return $dir
    } else {
        error "wrong direction value \"$dir\""
}
  Notice that no type checker has been specified for the 
 -state  variable and thus its value will not be checked.
  If a type checker procedure is specified for a public variable, this
  procedure will be called once the value of a public variable is
  determined by the three steps mentioned above.
6.5.2 Public Variable Configuration Methods
  After a widget instance is created, the user can assign new values
  to the public variables using the configure method. For example, the
  following code changes the 
 -direction variable of the   .arr
 instance to  n.
.arr configure -direction n
  In order for configuration to work, you have to define a
  configuration method that does what the programmer expects. The
  configuration method of a public variable is invoked whenever the
  user calls the configure method to change the value of this
  variable. The name of a configuration method must be the name of the
  public variable prefixed by the creation command of the class and
  
 :config. For example, the name configuration method for the
  
 -direction variable of the TixArrowButton class is   tixArrowButton:config-direction
. The following code implements
  this method:
proc tixArrowButton:config-direction {w value} {
    upvar #0 $w data
    $data(w:button) config -bitmap @$value.xbm
}
  Notice that when 
 tixArrowButton:config-direction is called,
  the 
 value parameter contains the new value of the   -direction
 variable but  data(-direction) contains the   old
 value. This is useful when the configuration method needs to
  check the previous value of the variable before taking in the new
  value.
  If a type checker is defined for a variable, it will be called
  before the configuration method is called. Therefore, the
  configuration method can assume that the type of the 
 value  parameter is got is always correct.
  Sometimes it is necessary to override the value supplied by the
  user. The following code illustrates this idea:
proc tixArrowButton:config-direction {w value} {
    upvar #0 $w data
    if {$value == "n"} {
        set value s
        set data(-direction) $value
    }
    $data(w:button) config -bitmap @$value.xbm
    return $data(-direction)
}
  Notice the above code always overrides values of 
 n to   s
. If you need to override the value, you must do the following two
  things:
    Explicitly set the instance variable inside the configuration
  method (the 
 set data(-direction) $value line).
    Return the modified value from the configuration method.
  If you do not need to override the value, you don't need to return
  anything from the configuration method. In this case, the Tix
  Intrinsics will assign the new value to the instance variable for
  you.
 Configuration Methods and Public Variable Initialization
  For efficiency reasons, the configuration methods are not called
  during the intialization of the public variables. If you want to
  force the configuration method to be called for a particular public
  variable, you can specify it in the 
 -forcecall section of the
  class declaration. In the following example, we force the
  configuration method of the 
 -direction variable to be called
  during intialization:
-forcecall {
    -direction
}
6.6 Summary of Widget Instance Initialization
  The creation of a widget instance is a complex process. You must
  understand how it works in order to write your widget classes. The
  following is the steps taken by the Tix Intrinsics when a widget
  instance is created:
    When the user creates an instance, the public variables are
  intilized as discussed in section 
oop.tex.html#6.5.16.5.1 . Type checkers
  are always called if they are specified. Configuration methods are
  called only if they are specified in the 
 -forcecall section.
    The  InitWidgetRec method is called. It should initialize 
  private variable, possibly according to the values the public
  variables.
    The  ConstructWidget method is called. It should create the
  component widgets. It should also store the names of public
  subwidgets into the subwidget variables.
    The  SetBinding method is called. It should create bindings for
  the component widgets.
  After the above steps, the creation of the instance is complete and the
  user can iterate with it using its widget command.
6.7 Loading the New Classes
  Usually, you can use a separate script file to store the
  implementaion of each new widget class. If you have several of those
  files, it will be a good idea to group the files into a single
  directory and create a 
 tclIndex file for them so that the new
  classes can be auto-loaded.
  Suppose you have put the class files into the directory   /usr/my/tix/classes
. You can create the  tclIndex file using
  the 
 tools/tixindex program that comes with Tix:
cd /usr/my/tix/classes
/usr/my/Tix4.0/tools/tixindex *.tcl
   The  tclIndex file must be created by the  tixindex  program. You cannot use the standard 
 auto_mkindex command
  that comes with Tcl.
  Once you have created the  tclIndex file, you can use your new
  widget classes by auto-loading. Here is a small demo program that
  uses the new TixArrowButton class:
#!/usr/local/bin/tixwish
lappend auto_path /usr/my/tix/classes
# Now I can use my TixArrowButton class!
#
tixArrowButton .arr -direction n
pack .arr
