MyWebUniversity.com Home Page
 



Darwin Mac OS X man pages main menu
snitfaq(n)                           Snit                           snitfaq(n)





NAME
       snitfaq - Snit Frequently Asked Questions

SYNOPSIS
       package require Tcl  8.4

       package require snit  ??00.93??



DESCRIPTION
OVERVIEW
       What is this document?

       This  is  an  atypical  FAQ list, in that few of the questions are fre-
       quently asked.  Rather, these are the questions I think a  newcomer  to
       Snit  should be asking.  This file is not a complete reference to Snit,
       however; that information is in the snit man page.

       What is Snit?

       Snit is a framework for defining abstract data types and megawidgets in
       pure  Tcl.   The name stands for "Snit's Not Incr Tcl", signifying that
       Snit takes a different approach to defining objects than does Incr Tcl,
       the best known object framework for Tcl.

       What version of Tcl does Snit require?

       Snit requires version Tcl 8.4 or later.

       What are Snit's goals?

       In developing Snit I had the following goals:


       ]o      It  should  be at least as efficient as the object code I'd been
              writing by hand.

       ]o      The fact that Snit was used in an object's implementation should
              be transparent (and irrelevant) to clients of that object.

       ]o      Snit  should  be able to encapsulate objects from other sources,
              particularly Tk widgets.

       ]o      Snit megawidgets should be (to the  extent  possible)  indistin-
              guishable in interface from Tk widgets.

       ]o      Snit  should  be  Tclish--that is, rather than trying to emulate
              C], Smalltalk, or anything else, it should try to  emulate  Tcl
              itself.

       ]o      It should have a simple, easy-to-use, easy-to-remember syntax.

       How is Snit different from other O frameworks?

       Snit is unique among Tcl object systems (so far as I know) in that it's
       a system based not on inheritance but on  delegation.   Object  systems
       based  on  inheritance  only  allow you to inherit from classes defined
       using the same system, and that's a shame.  In Tcl, an object  is  any-
       thing  that acts like an object; it shouldn't matter how the object was
       implemented.  I designed Snit to help me build applications out of  the
       materials at hand; thus, Snit is designed to be able to incorporate and
       build on any object, whether it's a hand-coded object, a Tk widget,  an
       Incr Tcl object, a BWidget or almost anything else.

       What can I do with Snit?

       Using Snit, a programmer can:

       ]o      Create abstract data types and Tk megawidgets.

       ]o      Define instance variables, type variables, and option variables.

       ]o      Define constructors, destructors, instance methods,  type  meth-
              ods, and several kinds of handler.

       ]o      Assemble  a  type  out of component types.  Instance methods and
              options can be delegated to the component types automatically.

OBJECTS
       What is an object?

       Obviously, a full description of object-oriented programming is  beyond
       the scope of this FAQ.  In simple terms, an object is an instance of an
       abstract data type--a coherent bundle of code and data.  There are many
       ways  to represent objects in Tcl/Tk; the best known example are the Tk
       widgets.  A widget is an object; it is represented by  a  Tcl  command.
       The object's methods are subcommands of the Tcl command.  Snit uses the
       same conventions as Tk widgets do.

       What is an abstract data type?

       In computer science terms, an abstract data  type  is  a  complex  data
       structure  along  with a set of operations, like a stack, a queue, or a
       binary tree--that is to say, in modern terms, an  object.   In  systems
       that include include some form of inheritance the word class is usually
       used instead of abstract data type, but as Snit doesn't do inheritance,
       the  older  term  seems  more  appropriate.   Sometimes  this is called
       object-based programming as opposed to object-oriented programming.

       In Snit, as in Tk, a type  is  a  command  that  creates  instances  --
       objects  -- which belong to the type.  Most types define some number of
       option which can be set at creation time, and usually  can  be  changed
       later.

       Further, an instance is also a Tcl command--a command that gives access
       to the operations which are defined for that abstract data type.   Con-
       ventionally,  the  operations  are  defined as subcommands, or instance
       methods of the instance command.  For example, to insert text into a Tk
       text widget, you use the text widget's insert method:

           # Create a text widget and insert some text in it.
           text .mytext -width 80 -height 24
           .mytext insert end "Howdy!"

       In  this  example, text is the type command and .mytext is the instance
       command.

       What kinds of abstract data types does Snit provide?

       Snit allows you to define three kinds of abstract data types:


       ]o      snit::::type

       ]o      snit::::widget

       ]o      snit::::widgetadaptor

       What is a snit::type?

       A snit::::type is a non-GUI abstract data type, e.g., a stack or a queue.
       snit::::types  are defined using the snit::::type command.  For example, if
       you were designing a kennel management system for a dog breeder,  you'd
       need a dog type.

       % snit::type dog {
           # ...
       }
       ::dog

       This definition defines a new command (::::dog, in this case) that can be
       used to define dog objects.

       An instance of a snit::::type can have instance methods,  instance  vari-
       ables,  options, and components.  The type itself can have type methods
       and procs.

       What is a snit::widget?

       A snit::::widget is a Tk megawidget built using Snit; it is very  similar
       to a snit::::type.  See WIDGETS.

       What is a snit::widgetadaptor?

       A  snit::::widgetadaptor uses Snit to wrap an existing widget type (e.g.,
       a Tk label), modifying its interface to a lesser or greater extent.  It
       is very similar to a snit::::widget.  See WIDGET ADAPTORS.

       How do I create an instance of a snit::type?

       You  create  an  instance of a snit::::type by passing the new instance's
       name to the type's create method.  In the following example, we  create
       a dog object called spot.

       % snit::type dog {
           # ....
       }
       ::dog
       % dog create spot
       ::spot

       The  create  method  name  can  be omitted so long as the instance name
       doesn't conflict with any defined type methods.  So the following exam-
       ple is identical to the previous example:

       % snit::type dog {
           # ....
       }
       ::dog
       % dog spot
       ::spot

       This document generally uses the shorter form.

       If the dog type defines options, these can usually be given defaults at
       creation time:

       % snit::type dog {
           option -breed mongrel
           option -color brown

           method bark {} { return "$self barks." }
       }
       ::dog
       % dog create spot -breed dalmation -color spotted
       ::spot
       % spot cget -breed
       dalmation
       % spot cget -color
       spotted

       Either way, the instance name now names a new Tcl command that is  used
       to  manipulate  the  object.  For example, the following code makes the
       dog bark:

       % spot bark
       ::spot barks.

       How do I refer to an object indirectly?

       Some programmers prefer to save the object name in a variable, and ref-
       erence it that way.  For example,

       % snit::type dog {
           option -breed mongrel
           option -color brown

           method bark {} { return "$self barks." }
       }
       ::dog
       % set d [dog spot -breed dalmation -color spotted]
       ::spot
       % $d cget -breed
       dalmation
       % $d bark
       ::spot barks.

       How can I generate the object name automatically?

       If  you'd  like Snit to generate an object name for you, use the %%AUTO%%
       keyword as the requested name:

           % snit::type dog {
            method bark {} { return "$self barks." }
           }
           ::dog
           % set d [dog %AUTO%]
           ::dog2
           % $d bark
           ::dog2 barks.

       The "%AUTO%" keyword can be embedded in a longer string:

           % set d [dog dog%AUTO%]
           ::dogdog4
           % $d bark
           ::dogdog4 barks.
           %

       Can types be renamed?

       Tcl's rename command renames other commands.  It's a  common  technique
       in  Tcl to modify an existing command by renaming it and defining a new
       command with the original name;  the  new  command  usually  calls  the
       renamed command.

       snit::::type's,  however,  should  never  be renamed; to do so breaks the
       connection between the type and its objects.

       Can objects be renamed?

       Tcl's rename command renames other commands.  It's a  common  technique
       in  Tcl to modify an existing command by renaming it and defining a new
       command with the original name;  the  new  command  usually  calls  the
       renamed command.

       All Snit objects (including widgets and widgetadaptors) can be renamed,
       though this flexibility has some consequences:


       ]o      In an instance method, self will  always  contain  the  object's
              current name, so instance methods can always call other instance
              methods using self.

       ]o      If the object is  renamed,  however,  then  $self's  value  will
              change.  Therefore, don't use $self for anything that will break
              if $self changes. For example, don't pass a callback command  to
              another object like this:

                  [list $self methodname args...]

              You'll  get an error if this command is called after your object
              is renamed.

       ]o      Instead, the object should pass the callback command like this:

              [mymethod methodname args...]

              The mymethod command returns code that  will  call  the  desired
              method  safely;  the caller of the callback can safely add addi-
              tional arguments to the end of the command as usual.

              For example, one could use this code to call a method when a  Tk
              button is pushed:

                  .btn configure -command [list $self buttonpress]

              This  will  break  if your instance is renamed.  Here's the safe
              way to do it:

                  .btn configure -command [mymethod buttonpress]

       ]o      Every object has a private namespace; the name of this namespace
              is  available  in method bodies, etc., as selfns.  This value is
              constant for the life the object.  Use selfns instead of self if
              you need a unique token to identify the object.

       ]o      When a snit::::widget's instance command is renamed, its Tk window
              name remains the same -- and is still extremely important.  Con-
              sequently,  the  Tk  window  name  is  available in snit::::widget
              method bodies, etc., as win.  This value  is  constant  for  the
              life  of  the object.  When creating child windows, it's best to
              use $$win.child rather than $$self.child as the name of the  child
              window.

       ]o      The  names  selfns  and win may not be used as explicit argument
              names for typemethods,  methods,  constructors,  or  onconfigure
              handlers.

       ]o      procs  defined  in  a  Snit type or widget definition used to be
              able to reference instance variables if self was passed to  them
              explicitly as the argument self; this is no longer the case.

       How do I destroy a Snit object?

       Every instance of a snit::::type has a destroy method:

           % snit::type dog {
            method bark {} { return "$self barks." }
           }
           ::dog
           % dog spot
           ::spot
           % spot bark
           ::spot barks.
           % spot destroy
           % info commands ::spot

       Snit megawidgets (i.e., instances of snit::::widget and snit::::widgetadap-
       tor) are destroyed like any other widget: by using the Tk destroy  com-
       mand  on the widget or on one of its ancestors in the window hierarchy.

       In addition, any Snit object of any type can be destroyed  by  renaming
       it to the empty string using the Tcl rename command.

INSTANCE METHODS
       What is an instance method?

       An  instance  method  is a procedure associated with a specific object.
       It is given free access to all of the object's type variables, instance
       variables, and so forth.

       How do I define an instance method?

       Instance  methods  are  defined in the type definition using the method
       statement.  Consider the following code that might be used to add  dogs
       to a computer simulation:

       % snit::type dog {
           method bark {} {
               return "$self barks."
           }

           method chase {thing} {
               return "$self chases $thing."
           }
       }
       ::dog

       A dog can bark, and it can chase things.

       The  method statement looks just like a normal Tcl proc, except that it
       appears in a snit::::type definition.  Notice that every instance  method
       gets  an  implicit  argument  called  self;  this argument contains the
       object's name.

       How does a client call an instance method?

       The method name becomes a subcommand of the object.  For example, let's
       put a simulated dog through its paces:

       % dog spot
       ::spot
       % spot bark
       ::spot barks.
       % spot chase cat
       ::spot chases cat.

       How does an instance method call another instance method?

       If  method A needs to call method B on the same object, it does so just
       as a client does: it calls method B  as  a  subcommand  of  the  object
       itself, using the object name stored in self.

       Suppose,  for example, that our dogs never chase anything without bark-
       ing at them:

       % snit::type dog {
           method bark {} {
               return "$self barks."
           }

           method chase {thing} {
               return "$self chases $thing.  [$self bark]"
           }
       }
       ::dog
       % dog spot
       ::spot
       % spot bark
       ::spot barks.
       % spot chase cat
       ::spot chases cat.  ::spot barks.

       Are there any limitations on instance method names?

       Not really, so long as you avoid the standard  instance  method  names:
       configure, configurelist, cget, destroy, and info.

       How do I make an instance method private?

       It's  often useful to define private methods, that is, instance methods
       intended to be called only by other methods of the same object.

       Snit doesn't implement any access control on instance methods,  so  all
       methods are de facto public.  Conventionally, though, the names of pub-
       lic methods begin with a lower-case letter, and the  names  of  private
       methods begin with an upper-case letter.

       For  example, suppose our simulated dogs only bark in response to other
       stimuli; they never bark just for fun.  So the  bark  method  could  be
       private:

       % snit::type dog {
           # Private by convention: begins with uppercase letter.
           method Bark {} {
               return "$self barks."
           }

           method chase {thing} {
               return "$self chases $thing. [$self Bark]"
           }
       }
       ::dog
       % dog fido
       ::fido
       % fido chase cat
       ::fido chases cat. ::fido barks.

       Are there any limitations on instance method arguments?

       Method  argument  lists  are defined just like normal Tcl proc argument
       lists; they can include default values, and the  args  argument.   How-
       ever,  every  method is called with a number of implicit arguments pro-
       vided by Snit in addition to those explicitly defined.   The  names  of
       these implicit arguments may not used to name explicit arguments.

       What implicit arguments are passed to each instance method?

       The  arguments implicitly passed to every method are type, selfns, win,
       and self.

       What is $type?

       The implicit argument type contains the fully  qualified  name  of  the
       object's type:

       % snit::type thing {
           method mytype {} {
               return $type
           }
       }
       ::thing
       % thing something
       ::something
       % something mytype
       ::thing

       What is $self?

       The  implicit argument self contains the object's fully qualified name.

       If the object's command is renamed, then self will change to  match  in
       subsequent  calls.  Thus, your code should not assume that self is con-
       stant unless you know for sure that the object will never be renamed.

       % snit::type thing {
           method myself {} {
               return $self
           }
       }
       ::thing
       % thing mutt
       ::mutt
       % mutt myself
       ::mutt
       % rename mutt jeff
       % jeff myself
       ::jeff

       What is $selfns?

       Each Snit object has a private namespace in which to store its instance
       variables  and  options.   The  implicit argument selfns is the name of
       this namespace; it never changes, and is constant for the life  of  the
       object, even if the object's name changes:

       % snit::type thing {
           method myNameSpace {} {
               return $selfns
           }
       }
       ::thing
       % thing jeff
       ::jeff
       % jeff myNameSpace
       ::thing::Snitinst3
       % rename jeff mutt
       % mutt myNameSpace
       ::thing::Snitinst3

       The  above  example reveals how Snit names an instance's private names-
       pace; however, you should not write code that depends on  the  specific
       naming convention, as it might change in future releases.

       What is $win?

       The  implicit  argument  win is defined for all Snit methods, including
       those of widgets and widgetadaptors, though it makes sense  mostly  for
       the  latter  two kinds.  win is simply the original name of the object,
       whether it's been renamed or not.  For widgets and  widgetadaptors,  it
       is  also therefore the name of a Tk window.  When a snit::::widgetadaptor
       is used to modify the interface of a  widget  or  megawidget,  it  must
       rename  the  widget's  original  command  and  replace it with its own.
       Thus, using win whenever the Tk window name is called for means that  a
       snit::::widget  or  snit::::widgetadaptor  can  be  adapted by a snit::::wid-
       getadaptor.  See WIDGETS for more information.

       How do I pass an instance method as a callback?

       It depends on the context.  Suppose in my  application  I  have  a  dog
       object named fido, and I want fido to bark when a Tk button is pressed.
       In this case, I pass the instance method in the normal way, as  a  sub-
       command of fido:

           button .bark -text "Bark!" -command [list fido bark]

       In  typical Tcl style, we use a callback to hook two independent compo-
       nents together.  But what if the dog object itself, passing one of  its
       own  instance  methods  to another object (one of its components, say)?
       The obvious thing to do is this:

       % snit::widget dog {
           constructor {args} {
               #...
               button $win.barkbtn -text "Bark!"  -command [list $self bark]
               #...
           }
       }
       ::dog

       (Note that in this example, our dog becomes a snit::::widget, because  it
       has  GUI  behavior.   See  WIDGETS for more.)  Thus, if we create a dog
       called .spot, it will create a Tk button called .barkbtn  and  pass  it
       $$self bark as the command.

       Now,  this  will  work--provided  that .spot is never renamed.  But why
       should .spot be renamed?  Surely renaming widgets is abnormal?  And  so
       it is--unless .spot is the hull component of a snit::::widgetadaptor.  If
       it is, then it will be renamed, and  .spot  will  name  the  snit::::wid-
       getadaptor  object.  When the button is pressed, the command $$self bark
       will be handled by the snit::::widgetadaptor, which might or might not do
       the right thing.

       There's a safer way to do it, and it looks like this:

       % snit::widget dog {
           constructor {args} {
               #...
               button $win.barkbtn -text "Bark!"  -command [mymethod bark]
               #...
           }
       }
       ::dog

       The  command mymethod can be used like list to build up a callback com-
       mand; the only difference is that mymethod returns a form of  the  com-
       mand that won't change if the instance's name changes.

       How do I delegate instance methods to a component?

       See DELEGATION.

INSTANCE VARIABLES
       What is an instance variable?

       An instance variable is a private variable associated with some partic-
       ular Snit object.  Instance variables can be scalars or arrays.

       How is a scalar instance variable defined?

       Scalar instance variables are defined in the type definition using  the
       variable  statement.   You can simply name it, or you can initialize it
       with a value:

       snit::type mytype {
           # Define variable "greeting" and initialize it with "Howdy!"
           variable greeting "Howdy!"
       }

       How is an array instance variable defined?

       Array instance variables are also defined using the  variable  command;
       however,  you  can't initialize them using the variable command.  Typi-
       cally, they get initialized in the constructor:

       snit::type mytype {
           # Define array variable "greetings"
           variable greetings

           constructor {args} {
               set greetings(formal) "Good Evening"
               set greetings(casual) "Howdy!"
           }
       }

       Are there any limitations on instance variable names?

       Just a few.

       First, every Snit  object  has  a  built-in  instance  variable  called
       options, which should never be redefined.

       Second,  all  names  beginning with "Snit" or "snit" are reserved for
       use by Snit internal code.

       Second, instance variable names with the namespace  delimiter  (::::)  in
       them are likely to cause great confusion.

       Do I need to declare instance variables before using them?

       No. Once you've defined an instance variable in the type definition, it
       can be used in any instance code  without  declaration.   This  differs
       from  normal  Tcl  practice, in which all non-local variables in a proc
       need to be declared.

       How do I pass an instance variable's name to another object?

       In Tk, it's common to pass a widget a variable name;  for  example,  Tk
       label  widgets  have  a  -textvariable  option which names the variable
       which will contain the widget's  text.   This  allows  the  program  to
       update the label's value just by assigning a new value to the variable.

       If you naively pass the instance variable name  to  the  label  widget,
       you'll  be confused by the result; Tk will assume that the name names a
       global variable.  Instead, you need to provide a fully-qualified  vari-
       able  name.   From  within an instance method or a constructor, you can
       fully qualify the variable's name using the varname command:

       snit::widget mywidget {
           variable labeltext ""

           constructor {args} {
               # ...

               label $win.label -textvariable [varname labeltext]

               # ...
           }
       }

       How do I make an instance variable public?

       Practically speaking, you  don't.   Instead,  you'll  implement  public
       variables as options.  Alternatively, you can write instance methods to
       set and get the variable's value.

OPTIONS
       What is an option?

       A type's options are the equivalent of what other object-oriented  lan-
       guages  would call public member variables or properties: they are data
       values which can be retrieved and (usually) set by the  clients  of  an
       object.   If  a  type  is  to be used a record type, it's possible that
       options are all that's needed.

       Snit's implementation of options follows the Tk model  fairly  exactly,
       except  that  snit::::type  objects can't interact with Tk's option data-
       base; snit::::widget and snit::::widgetadaptor objects, on the other  hand,
       can and do.

       How do I define an option?

       Options  are defined in the type definition using the option statement.
       Consider the following type, to be used in an application that  manages
       a list of dogs for a pet store:

       % snit::type dog {
           option -breed mongrel
           option -color brown
           option -akc 0
           option -shots 0
       }
       ::dog

       According  to  this,  a  dog has four notable properties, or options: a
       breed, a color, a flag that says whether it's pedigreed with the Ameri-
       can  Kennel  Club,  and  another  flag that says whether it has had its
       shots.  The default dog, evidently, is a brown mutt.

       If no default value is specified, the option's value  defaults  to  the
       empty string.

       The Snit man page refers to these as "locally defined" options.

       How can a client set options at object creation?

       The normal convention is that the client may pass any number of options
       and their values after the object's name at object creation.  For exam-
       ple,  the  ::dog command defined in the previous answer can now be used
       to define individual dogs.  Any or all of the options  may  be  set  at
       creation time.

         % dog spot -breed beagle -color "mottled" -akc 1 -shots 1
         ::spot
         % dog fido -shots 1
         ::fido

       So ::spot is a pedigreed beagle; ::fido is a typical mutt, but his own-
       ers evidently take care of him, because he's had his shots.

       Note: If the type defines a constructor, it  can  specify  a  different
       object-creation syntax.  See CONSTRUCTORS for more information.

       How can a client retrieve an option's value?

       Retrieve option values using the cget method:

       % spot cget -color
       mottled
       % fido cget -breed
       mongrel

       How can a client set options after object creation?

       Any  number  of  options  may  be  set  at one time using the configure
       instance method.  Suppose that closer inspection shows that ::fido is a
       rare Arctic Boar Hound of a lovely dun color:

         % fido configure -color dun -breed "Arctic Boar Hound"
         % fido cget -color
         dun
         % fido cget -breed
         Arctic Boar Hound

       Alternatively,  the  configurelist  method  takes a list of options and
       values; this is some times more convenient:

       % set features [list -color dun -breed "Arctic Boar Hound"]
       -color dun -breed {Arctic Boar Hound}
       % fido configurelist $features
       % fido cget -color
       dun
       % fido cget -breed
       Arctic Boar Hound

       How should an instance method access an option value?

       There are two ways an instance method can set and retrieve an  option's
       value.  One is to use the configure and cget methods, as shown below:

       % snit::type dog {
           option -weight 10

           method gainWeight {} {
               set wt [$self cget -weight]
               incr wt
               $self configure -weight $wt
           }
       }
       ::dog
       % dog fido
       ::fido
       % fido cget -weight
       10
       % fido gainWeight
       % fido cget -weight
       11

       Alternatively,  Snit provides a built-in array instance variable called
       options.  The indices are the option names; the values are  the  option
       values.  The method given above can thus be rewritten as follows:

           method gainWeight {
               incr options(-weight)
           }

       As  you  can see, using the options variable involves considerably less
       typing.  If you define onconfigure or oncget handlers, as described  in
       the  following  answers,  you  might wish to use the configure and cget
       methods anyway, just so that any special processing you've  implemented
       is sure to get done.

       How can I catch changes to an option's value?

       Use an onconfigure handler.

       What is an onconfigure handler?

       An  onconfigure  handler  is  a  special kind of instance method that's
       called whenever the related option is given a new value via the config-
       ure or configurelist instance methods. The handler can validate the new
       value, pass it to some other object, and anything else you'd like it to
       do.

       An  onconfigure  handler  is defined by an onconfigure statement in the
       type definition.  Here's what the default configuration behavior  would
       look like if written as an onconfigure handler:

       snit::type dog {
           option -color brown

           onconfigure -color {value} {
               set options(-color) $value
           }
       }

       The  name  of  the  handler is just the option name.  The argument list
       must have exactly one argument; it can be called almost  anything,  but
       conventionally it is called value.  Within the handler, the argument is
       set to the new value; also, all instance variables are available,  just
       as in an instance method.

       Note  that  if your handler doesn't put the value in the options array,
       it doesn't get updated.

       How can I catch accesses to an option's value?

       Use an oncget handler.

       What is an oncget handler?

       An oncget handler is a special kind of instance  method  that's  called
       whenever  the  related  option's value is queried via the cget instance
       method.  The handler can compute the value, retrieve it  from  a  data-
       base, or anything else you'd like it to do.

       An oncget handler is defined by an oncget statement in the type defini-
       tion.  Here's what the default behavior would look like if  written  as
       an oncget handler:

       snit::type dog {
           option -color brown

           oncget -color {
               return $options(-color)
           }
       }

       The  handler  takes no arguments, and so has no argument list; however,
       all instance variables are  available,  just  as  they  are  in  normal
       instance methods.

TYPE VARIABLES
       What is a type variable?

       A  type  variable  is  a  private  variable associated with a Snit type
       rather than with a particular instance of the type.  In C]  and  Java,
       the  equivalent  of  type variables are called static member variables.
       Type variables can be scalars or arrays.

       How is a scalar type variable defined?

       Scalar type variables are defined in  the  type  definition  using  the
       typevariable  statement.  You can simply name it, or you can initialize
       it with a value:

       snit::type mytype {
           # Define variable "greeting" and initialize it with "Howdy!"
           typevariable greeting "Howdy!"
       }

       Every object of type mytype now has access to a single variable  called
       greeting.

       How is an array type variable defined?

       Array-valued  type  variables  are  also defined using the typevariable
       command; however, you can't initialize them that way, just as you can't
       initialize array variables using Tcl's standard variable command.  Type
       constructors are the usual way to initialize  array-valued  type  vari-
       ables.

       Are there any limitations on type variable names?

       Type  variable  names  have  the same restrictions as instance variable
       names.

       Do I need to declare type variables before using them?

       No. Once you've defined a type variable in the type definition, it  can
       be  used in instance methods or type methods without declaration.  This
       differs from normal Tcl practice, in which all non-local variables in a
       proc need to be declared.

       How do I pass a type variable's name to another object?

       In  Tk,  it's  common to pass a widget a variable name; for example, Tk
       label widgets have a -textvariable  option  which  names  the  variable
       which  will  contain  the  widget's  text.   This allows the program to
       update the label's value just by assigning a new value to the variable.

       If you naively pass a type variable name to the label widget, you'll be
       confused by the result; Tk will assume that the  name  names  a  global
       variable.   Instead,  you  need  to  provide a fully-qualified variable
       name.  From within an instance method or a constructor, you  can  fully
       qualify the type variable's name using the typevarname command:

       snit::widget mywidget {
           typevariable labeltext ""

           constructor {args} {
               # ...

               label $win.label -textvariable [typevarname labeltext]

               # ...
           }
       }

       How do I make a type variable public?

       There are two ways to do this.  The preferred way is to write a pair of
       type methods to set and query the variable's value.

       Alternatively, you can publicize the variable's name in your documenta-
       tion and clients can access it directly.  For example,

       snit::type mytype {
           typevariable myvariable
       }

       set ::mytype::myvariable "New Value"

       As  shown, type variables are stored in the type's namespace, which has
       the same name as the type itself.

TYPE METHODS
       What is a type method?

       A type method is a procedure associated with  the  type  itself  rather
       than with any specific instance of the type.

       How do I define a type method?

       Type  methods  are  defined in the type definition using the typemethod
       statement:

           snit::type dog {
            # List of pedigreed dogs
            typevariable pedigreed

            typemethod pedigreedDogs {} {
                return $pedigreed
            }

            # ...
           }

       Suppose the dog type maintains a list of the names  of  the  dogs  that
       have pedigrees.  The pedigreedDogs type method returns this list.

       The typemethod statement looks just like a normal Tcl proc, except that
       it appears in a snit::::type definition.  It defines the method name, the
       argument list, and the body of the method.

       How does a client call a type method?

       The  method name becomes a subcommand of the type's command.  For exam-
       ple,

       snit::type dog {
           option -pedigreed 0

           # List of pedigreed dogs
           typevariable pedigreed

           typemethod pedigreedDogs {} {
               return $pedigreed
           }

           # ...
       }

       dog spot -pedigreed 1
       dog fido

       foreach dog [dog pedigreedDogs] { ... }

       Are there any limitations on type method names?

       Not really, so long as you avoid the standard type method names:

       create and info.

       How do I make a type method private?

       It's sometimes useful to define private type  methods,  that  is,  type
       methods intended to be called only by other type or instance methods of
       the same object.

       Snit doesn't implement any access control on type methods;  by  conven-
       tion,  the  names of public methods begin with a lower-case letter, and
       the names of private methods begin with an upper-case letter.

       Alternatively, a Snit proc can be used as a private  type  method;  see
       PROCS.

       Are there any limitations on type method arguments?

       Method  argument  lists  are defined just like normal Tcl proc argument
       lists; they can include default values, and the  args  argument.   How-
       ever, every type method is called with an implicit argument called type
       that contains the name of the type command.  In addition, type  methods
       should  by convention avoid using the names of the arguments implicitly
       defined for instance methods.

       How does an instance or type method call a type method?

       If an instance or type method needs to call a type  method,  it  should
       use type to do so:

       snit::type dog {

           typemethod pedigreedDogs {} { ... }

           typemethod printPedigrees {} {
               foreach obj [$type pedigreedDogs] { ... }
           }
       }

       How do I pass a type method as a callback?

       It's  common in Tcl to pass a snippet of code to another object, for it
       to call later.  Because types cannot be renamed, the  thing  to  do  is
       just use the type name, or, if the callback is registered from within a
       type method, type.  For example, suppose we want to  print  a  list  of
       pedigreed dogs when a Tk button is pushed:

         button .btn -text "Pedigrees" -command [list dog printPedigrees]
         pack .btn

PROCS
       What is a proc?

       A  Snit proc is really just a Tcl proc defined within the type's names-
       pace.  You can use procs for private code that  isn't  related  to  any
       particular  instance.   For example, I often find myself writing a proc
       to pop the first item off of a list stored in a variable.

       How do I define a proc?

       Procs are defined by including a proc statement in the type definition:

       snit::type mytype {
           # Pops and returns the first item from the list stored in the
           # listvar, updating the listvar
          proc pop {listvar} { ... }

          # ...
       }

       Are there any limitations on proc names?

       Any  name  can  be used, so long as it does not begin with Snit; names
       beginning with Snit are reserved for Snit's  own  use.   However,  the
       wise  programmer will avoid proc names like set, list, if, and so forth
       that would shadow standard Tcl command names.

       By convention, proc names, being private, begin with a capital  letter.

       How does a method call a proc?

       Just like it calls any Tcl command.  For example,

       snit::type mytype {
           # Pops and returns the first item from the list stored in the
           # listvar, updating the listvar
           proc Pop {listvar} { ... }

           variable requestQueue {}

           # Get one request from the queue and process it.
           method processRequest {} {
               set req [Pop requestQueue]
           }
       }

       How can I pass a proc to another object as a callback?

       I  tend  to  use  type  or instance methods for this purpose and ignore
       procs altogether.  But if you really  need  to,  the  codename  command
       returns the proc's fully qualified name.

TYPE CONSTRUCTORS
       What is a type constructor?

       A  type  constructor  is  a body of code that initializes the type as a
       whole, rather like a C] static initializer.  The body of a  type  con-
       structor is executed once when the type is defined, and never again.

       A type can have at most one type constructor.

       How do I define a type constructor?

       A type constructor is defined by using the typeconstructor statement in
       the type definition.  For example, suppose the type uses an  array-val-
       ued type variable as a look up table:

           % snit::type mytype {
            typevariable lookupTable

            typeconstructor {
                array set lookupTable {key value...}
            }
           }
           ::mytype
           %


CONSTRUCTORS
       What is a constructor?

       In  object-oriented programming, an object's constructor is responsible
       for initializing the object completely at creation time. The  construc-
       tor  receives  the  list  of options passed to the snit::::type command's
       create method and can then do whatever it likes.   That  might  include
       computing  instance  variable values, reading data from files, creating
       other objects, updating type variables, and so forth.

       The constructor doesn't return anything.

       How do I define a constructor?

       A constructor is defined by using the constructor statement in the type
       definition.   Suppose that it's desired to keep a list of all pedigreed
       dogs.  The list can be maintained in a type variable and retrieved by a
       type  method.   Whenever  a  dog  is  created, it can add itself to the
       list--provided that it's registered with the American Kennel Club.

       % snit::type dog {
           option -akc 0

           typevariable akcList {}

           constructor {args} {
               $self configurelist $args

               if {$options(-akc)} {
                   lappend akcList $self
               }
           }

           typemethod akclist {} {
               return $akcList
           }
       }
       ::dog
       % dog spot -akc 1
       ::spot
       % dog fido
       ::fido
       % dog akclist
       ::spot

       What does the default constructor do?

       If you don't provide a constructor explicitly, you get the default con-
       structor, which looks like this:

       % snit::type dog {
           option -breed mongrel
           option -color brown
           option -akc 0

           constructor {args} {
               $self configurelist $args
           }
       }
       ::dog
       % dog spot -breed dalmatian -color spotted -akc 1
       ::spot

       When  the  constructor is called, args will be set to the list of argu-
       ments that follow the object's name.  The  constructor  is  allowed  to
       interprete  this  list  any way it chooses; the normal convention is to
       assume that it's a list of option names and values,  as  shown  in  the
       example  above.   If  you  simply  want  to save the option values, you
       should use the configurelist method, as shown.

       Can I choose a different command line syntax for the constructor?

       Yes, you can.  For example, suppose we wanted to be sure that the breed
       was  explicitly  stated for every dog at creation time, and couldn't be
       changed thereafter.  One way to do that is as follows:

       % snit::type dog {
           variable breed

           option -color brown
           option -akc 0

           constructor {theBreed args} {
               set breed $theBreed
               $self configurelist $args
           }

           method breed {} {
               return $breed
           }
       }
       ::dog
       % dog spot dalmatian -color spotted -akc 1
       ::spot
       % spot breed
       dalmatian

       The drawback is that this creation  syntax  is  non-standard,  and  may
       limit the compatibility of your new type with other people's code.  For
       example, Snit generally assumes that components use the  standard  cre-
       ation syntax.

       Are there any limitations on constructor arguments?

       Constructor  argument lists are defined just like normal Tcl proc argu-
       ment list; they can include default  values,  and  the  args  argument.
       However,  the constructor is called with a number of implicit arguments
       provided by Snit in addition to those explicitly defined.  The names of
       these implicit arguments may not used to name explicit arguments.

       What implicit arguments are passed to the constructor?

       The  constructor  gets  the  same implicit arguments that are passed to
       instance methods: type, selfns, win, and self.

DESTRUCTORS
       What is a destructor?

       A destructor is a special kind of method that's called when  an  object
       is  destroyed.   It's responsible for doing any necessary clean-up when
       the object goes away: destroying  components,  closing  files,  and  so
       forth.

       How do I define a destructor?

       Destructors  are  defined by using the destructor statement in the type
       definition.  Suppose we're maintaining a list of pedigreed  dogs;  then
       we'll want to remove dogs from it when they are destroyed.

       % snit::type dog {
           option -akc 0

           typevariable akcList {}

           constructor {args} {
               $self configurelist $args

               if {$options(-akc)} {
                   lappend akcList $self
               }
           }

           destructor {
               set ndx [lsearch $akcList $self]

               if {$ndx != -1} {
                   set akcList [lreplace $akcList $ndx $ndx]
               }
           }

           typemethod akclist {} {
               return $akcList
           }
       }
       ::dog
       % dog spot -akc 1
       ::spot
       % dog fido -akc 1
       ::fido
       % dog akclist
       ::spot ::fido
       % fido destroy
       % dog akclist
       ::spot

       Are there any limitations on destructor arguments?

       Yes; a destructor has no explicit arguments.

       What implicit arguments are passed to the destructor?

       The  destructor  gets  the  same  implicit arguments that are passed to
       instance methods: type, selfns, win, and self.

       Must components be destroyed explicitly?

       Yes and no.

       For a Snit megawidget  (snit::::widgets  and  snit::::widgetadaptors),  any
       widget  components  created  by it will be destroyed automatically when
       the megawidget  is  destroyed,  in  keeping  with  normal  Tk  behavior
       (destroying  a  parent  widget  destroys the whole tree).  On the other
       hand, all non-widget components of a Snit megawidget,  and  all  compo-
       nents  of a normal snit::::type object, must be destroyed explicitly in a
       destructor.

COMPONENTS
       What is a component?

       Often an object will create and manage a number of other objects.   One
       example  is  a  Snit  megawidget  that composes a number of Tk widgets.
       These objects are part of the main object and are thus are called  com-
       ponents of it.

       But Snit also has a more precise meaning for component.  The components
       of a Snit object are those objects created by it to which  methods  and
       options  can  be  delegated.  See DELEGATION for more information about
       delegation.

       How do I create a component?

       First, you must decide what role a component plays within your  object,
       and give the role a name.  For example, suppose your dog object creates
       a tail object (the better to wag with, no doubt).  The tail object will
       have some command name, but you tell Snit about it using its role name,
       as follows:

       % snit::type dog {
           # Define component name as an instance variable
           variable mytail

           constructor {args} {
               # Create and save the component's command
               install mytail using tail %AUTO% -partof $self
               $self configurelist $args
           }

           method wag {} {
               $mytail wag
           }
       }
       ::dog

       As shown here, it doesn't matter what the tail object's real  name  is;
       the dog object refers to it by its component name.

       The  above  example  shows  one  way  to delegate the wag method to the
       mytail component; see DELEGATION for an easier way.

       How is a component named?

       A component has two names.  The first name represents the role the com-
       ponent object plays within the Snit object.  This is the component name
       proper, and is the name used to refer  to  the  component  within  Snit
       code.   The second name is the name of the actual component object cre-
       ated by the Snit object's constructor.  This second name  is  always  a
       Tcl command name, and is referred to as the component's object name.

       In the example in the previous FAQ, the component name is "mytail"; the
       "mytail" component's object name is chosen automatically by Snit  since
       %AUTO% was used when the component object was created.

       What does the install command do?

       The  install  command creates the component using the specified command
       (tail %%AUTO%% -partof $$self), and assigns the result to the mytail vari-
       able.   For  snit::::types, the install command shown above is equivalent
       to the following command:

           set mytail [tail %AUTO% -partof $self]

       For snit::::widgets and snit::::widgetadaptors, however, the install>  com-
       mand  also  queries  the  Tk option database and initializes the compo-
       nent's options accordingly.  For consistency, it's a good idea  to  get
       in the habit of using install for all components.

       Are there any limitations on component names?

       Yes.   snit::::widget  and  snit::::widgetadaptor  have a special component
       called the hull component; thus, the name hull should be  used  for  no
       other purpose.

       Component  names are in fact instance variable names, and so follow the
       rules for instance variables.

       Are there any limitations on component object names?

       Yes.  Component objects which are Tk widgets or megawidgets  must  have
       valid  Tk  window  names.   Component  objects which are not widgets or
       megawidgets must have fully-qualified command names, i.e., names  which
       include  the full namespace of the command.  Note that Snit always cre-
       ates objects with fully  qualified  names.   Second,  component  object
       names  must be unique.  This is no problem for widget components, since
       widget names are always unique; but consider the following code:

           snit::type tail { ... }

           snit::type dog {
               delegate method wag to mytail

               constructor {} {
                   install mytail using tail mytail
               }
           }

       This code uses the component name, "mytail", as  the  component  object
       name.  This is not good, and here's why: Snit instance code executes in
       the Snit type's namespace.  In this case, the mytail component is  cre-
       ated   in   the   ::dog::  namespace,  and  will  thus  have  the  name
       ::dog::mytail.

       Now, suppose you create two dogs.  Both will have  a  mytail  component
       called  ::dog::mytail.   In  other  words, you've got two dogs with one
       tail between them.  This is very bad.  Here are a  couple  of  ways  to
       avoid it:

       First,  if the component type is a snit::::type you can specify %AUTO% as
       its name, and be guaranteed to get a unique name.  This is  the  safest
       thing to do:

           install mytail using tail %AUTO%

       If  the  component type isn't a snit::::type you can base the component's
       object name on the type's name in some way:

            install mytail using tail $self.mytail

       This isn't as safe, but should usually work out okay.

       Must I destroy the components I create?

       That depends.  When a parent widget is destroyed, all child widgets are
       destroyed  automatically.   Thus,  if  your object is a snit::::widget or
       snit::::widgetadaptor you don't need to destroy any components  that  are
       widgets.

       Any  non-widget components, however, and all components of a snit::::type
       object, must be destroyed explicitly.  This is true whether you  assign
       them a component name or not.

       % snit::type dog {
           variable mytail

           constructor {args} {
               install mytail using tail %AUTO% -partof $self
               $self configurelist $args
           }

           destructor {
               $mytail destroy
           }
       }
       ::dog

       Note  that this code assumes that tail is also a snit::::type; if not, it
       might need to be destroyed in some other way.

       Can I expose a component's object command as part of my interface?

       Yes, and there are two ways to do it.  The most appropriate way is usu-
       ally  to use DELEGATION.  Delegation allows you to pass the options and
       methods you specify along to particular components.   This  effectively
       hides  the  components  from  the  users of your type, and ensures good
       encapsulation.

       However, there are times when it's appropriate, not to mention simpler,
       just to make the entire component part of your type's public interface.

       How do I expose a component's object command?

       Use the expose statement.  For example, suppose you're creating a  com-
       bobox  megawidget which owns a listbox widget, and you want to make the
       listbox's entire interface public.  You can do this:

       snit::widget combobox {
            expose listbox

            constructor {args} {
                install listbox using listbox $win.listbox ....

                #...
            }

            #...
       }

       combobox .mycombo

       The expose statement exposes the named component by defining  a  method
       of  the same name.  The method's arguments are passed along to the com-
       ponent.  Thus, the above code sets the listbox component's "-width"  to
       30.

       If  called with no arguments, the method returns the component's object
       name:

       % .mycombo listbox

       Usually you'll let the method name be the same as the  component  name;
       however,  you  can  rename  it if necessary.  The code in the following
       listing exposes the same interface as the previous example:

       snit::widget combobox {
            expose mylistbox as listbox

            constructor {args} {
                install mylistbox using listbox $win.mylistbox ....

                #...
            }

            #...
       }
       combobox .mycombo


DELEGATION
       What is delegation?

       Delegation, simply put, is when you pass a task you've  been  given  to
       one  of  your  assistants.   (You do have assistants, don't you?)  Snit
       objects can do the same thing.  The following example shows one way  in
       which  the  dog  object can delegate its wag method and its -taillength
       option to its tail component.

       % snit::type dog {
           variable mytail

           option -taillength

           onconfigure -taillength {value} {
                $mytail configure -length $value
           }

           oncget -taillength {
                $mytail cget -length
           }

           constructor {args} {
               install mytail using tail %AUTO% -partof $self
               $self configurelist $args
           }

           method wag {} {
               $mytail wag
           }
       }
       ::dog
       % snit::type tail {
           option -length 5
           option -partof
           method wag {} { return "Wag, wag, wag."}
       }
       ::tail
       % dog spot -taillength 7
       ::spot
       % spot cget -taillength
       7
       % spot wag
       Wag, wag, wag.

       This is the hard way to do it, by it demonstrates  what  delegation  is
       all about.  See the following answers for the easy way to do it.

       Note  that the constructor calls the configurelist method after it cre-
       ates its tail; otherwise, if -taillength appeared in the list  of  args
       we'd get an error.

       How can I delegate a method to a component object?

       Delegation occurs frequently enough that Snit makes it easy. Any method
       can be delegated to any component by placing a single  delegate  state-
       ment  in  the  type  definition.   (See COMPONENTS for more information
       about component names.)

       For example, here's a much better way to delegate the dog object's  wag
       method:

       % snit::type dog {
           delegate method wag to mytail

           constructor {args} {
               install mytail using tail %AUTO% -partof $self
               $self configurelist $args
           }
       }
       ::dog
       % snit::type tail {
           option -length 5
           option -partof
           method wag {} { return "Wag, wag, wag."}
       }
       ::tail
       % dog spot
       ::spot
       % spot wag
       Wag, wag, wag.

       This  code  has  the  same  affect as the code shown under the previous
       question: when a dog's wag method is called, the call and its arguments
       are passed along automatically to the tail object.

       Note  that  when  a component is mentioned in a delegate statement, the
       component's instance variable is defined implicitly.

       Note also that you can define a method name using the method statement,
       or you can define it using delegate; you can't do both.

       Can I delegate to a method with a different name?

       Suppose  the  tail  object has a wiggle method instead of a wag method,
       and you want to delegate the dog's wag  method  to  the  tail's  wiggle
       method.  It's easily done:

           % snit::type dog {
            delegate method wag to mytail as wiggle

            constructor {args} {
                install mytail using tail %AUTO% -partof $self
                $self configurelist $args
            }
           }
           ::dog
           % snit::type tail {
            option -length 5
            option -partof
            method wiggle {} { return "Wag, wag, wag."}
           }
           ::tail
           % dog spot
           ::spot
           % spot wag
           Wag, wag, wag.

       Can I delegate to a method with additional arguments?

       Suppose  the tail object has a wag method that takes as an argument the
       number of times the tail should be wagged.  You want  to  delegate  the
       dog's  wag  method  to  the tail's wag method, specifying that the tail
       should be wagged three times.  It's easily done:

           % snit::type dog {
            delegate method wag to mytail as {wag 3}

            constructor {args} {
                install mytail using tail %AUTO% -partof $self
                $self configurelist $args
            }
           }
           ::dog
           % snit::type tail {
            option -length 5
            option -partof
            method wag {count} {
                return [string repeat "Wag " $count]
            }
           }
           ::tail
           % dog spot
           ::spot
           % spot wag
           Wag Wag Wag
           %

       How can I delegate an option to a component object?

       The first question in this section (see DELEGATION) shows  one  way  to
       delegate an option to a component; but this pattern occurs often enough
       that Snit makes it easy.  For example, every tail object has a  -length
       option;  we want to allow the creator of a dog object to set the tail's
       length.  We can do this:

       % snit::type dog {
           delegate option -length to mytail

           constructor {args} {
               install mytail using tail %AUTO% -partof $self
               $self configurelist $args
           }
       }
       ::dog
       % snit::type tail {
           option -partof
           option -length 5
       }
       ::tail
       % dog spot -length 7
       ::spot
       % spot cget -length
       7

       This produces nearly the same result as the oncget and onconfigure han-
       dlers  shown  under  the first question in this section: whenever a dog
       object's -length option  is  set  or  retrieved,  the  underlying  tail
       object's option is set or retrieved in turn.

       Note  that you can define an option name using the option statement, or
       you can define it using delegate; you can't do both.

       Can I delegate to an option with a different name?

       In the previous answer we delegated the dog's -length  option  down  to
       its  tail.   This  is, of course, wrong.  The dog has a length, and the
       tail has a length, and they are different.  What we'd really like to do
       is  give  the  dog  a -taillength option, but delegate it to the tail's
       -length option:

       % snit::type dog {
           delegate option -taillength to mytail as -length

           constructor {args} {
               set mytail [tail %AUTO% -partof $self]
               $self configurelist $args
           }
       }
       ::dog
       % snit::type tail {
           option -partof
           option -length 5
       }
       ::tail
       % dog spot -taillength 7
       ::spot
       % spot cget -taillength
       7

       How can I delegate any unrecognized method or  option  to  a  component
       object?

       It  may happen that a Snit object gets most of its behavior from one of
       its components.  This  often  happens  with  snit::::widgetadaptors,  for
       example, where we wish to slightly the modify the behavior of an exist-
       ing widget.  To carry on with our dog example, however, suppose that we
       have  a  snit::::type  called  animal that implements a variety of animal
       behaviors--moving, eating, sleeping, and so forth.   We  want  our  dog
       objects  to  inherit these same behaviors, while adding dog-like behav-
       iors of its own.  Here's how we can give a dog methods and  options  of
       its  own  while  delegating all other methods and options to its animal
       component:

       % snit::type dog {
           delegate option * to animal
           delegate method * to animal

           option -akc 0

           constructor {args} {
               install animal using animal %AUTO% -name $self
               $self configurelist $args
           }

           method wag {} {
               return "$self wags its tail"
           }
       }
       ::dog

       That's it.  A dog is now an animal which has a -akc option and can  wag
       its tail.

       Note  that  we  don't  need to specify the full list of method names or
       option names which animal will receive.  It gets anything  dog  doesn't
       recognize--and  if it doesn't recognize it either, it will simply throw
       an error, just as it should.

       How can I delegate all but certain methods or options to a component?

       In the previous answer, we said that every dog is an animal by delegat-
       ing  all  unknown methods and options to the animal component. But what
       if the animal type has some methods or options that we'd like  to  sup-
       press?

       One solution is to explicitly delegate all the options and methods, and
       forgo the convenience of delegate method ** and delegate option **.   But
       if we wish to suppress only a few options or methods, there's an easier
       way:

           % snit::type dog {
            delegate option * to animal except -legs
            delegate method * to animal except {fly climb}

            # ...

            constructor {args} {
                install animal using animal %AUTO% -name $self -legs 4
                $self configurelist $args
            }

            # ...
           }
           ::dog
           %

       Dogs have four legs, so we specify that explicitly when we  create  the
       animal  component,  and  explicitly exclude -legs from the set of dele-
       gated options.  Similarly, dogs  can  neither  fly  nor  climb,  so  we
       exclude those animal methods as shown.

WIDGETS
       What is a snit::widget?

       A snit::::widget is the Snit version of what Tcl programmers usually call
       a megawidget: a widget-like object usually consisting of one or more Tk
       widgets all contained within a Tk frame.

       A snit::::widget is also a special kind of snit::::type.  Just about every-
       thing in this FAQ list that relates  to  snit::::types  also  applies  to
       snit::::widgets.

       How do I define a snit::widget?

       snit::::widgets  are  defined  using  the  snit::::widget  command, just as
       snit::::types are defined by the snit::::type command.

       The body of the definition can contain all of the same kinds of  state-
       ments, plus a couple of others which will be mentioned below.

       How do snit::widgets differ from snit::types?


       ]o      The  name  of  an  instance of a snit::::type can be any valid Tcl
              command name, in any namespace.  The name of an  instance  of  a
              snit::::widget must be a valid Tk widget name, and its parent wid-
              get must already exist.

       ]o      An instance of a snit::::type can  be  destroyed  by  calling  its
              destroy  method.   Instances  of  a snit::::widget have no destroy
              method; use the Tk destroy command instead.

       ]o      Every instance of a snit::::widget has  one  predefined  component
              called  its  hull component.  The hull is a Tk frame or toplevel
              widget; any other widgets created as part  of  the  snit::::widget
              will usually be contained within this frame.

       ]o      snit::::widgets can have their options receive default values from
              the Tk option database.

       What is a hull component?

       Snit can't create a Tk widget object; only Tk can do that.  Thus, every
       instance  of a snit::::widget must be wrapped around a genuine Tk widget;
       this Tk widget is called the hull component.  Snit  effectively  piggy-
       backs  the  behavior you define (methods, options, and so forth) on top
       of the hull component so that the whole thing behaves like  a  standard
       Tk widget.

       For  snit::::widgets  the  hull  component must be a Tk frame or toplevel
       widget; any other widgets created as part of the snit::::widget  will  be
       contained within this frame or toplevel.

       snit::::widgetadaptors differ from snit::::widgets chiefly in that any kind
       of widget can be used as the hull component; see WIDGET ADAPTORS.

       How can I set the hull type for a snit::widget?

       A snit::::widget's hull component will usually be a Tk frame widget; how-
       ever,  it may also be a toplevel widget.  You can explicitly choose one
       or the other by including the hulltype command in  the  widget  defini-
       tion:

           snit::widget mytoplevel {
            hulltype toplevel

            # ...
           }

       If no hulltype command appears, the hull will be a frame.

       How should I name widgets which are components of a snit::widget?

       Every  widget, whether a genuine Tk widget or a Snit megawidget, has to
       have a valid Tk window name.  When a snit::::widget is first created, its
       instance  name, self, is a Tk window name; however, if the snit::::widget
       is used as the hull component by  a  snit::::widgetadaptor  its  instance
       name  will  be  changed  to  something  else.   For  this reason, every
       snit::::widget method, constructor, destructor, and so  forth  is  passed
       another  implicit argument, win, which is the window name of the megaw-
       idget.  Any children must be named using win as the root.

       Thus, suppose you're writing a toolbar widget, a frame consisting of  a
       number  of  buttons  placed side-by-side.  It might look something like
       this:

       snit::widget toolbar {
           delegate option * to hull

           constructor {args} {
               button $win.open -text Open -command [mymethod open]
               button $win.save -text Save -command [mymethod save]

               # ....

               $self configurelist $args

           }
       }

       See also the question on renaming objects, toward the top of this file.


WIDGETADAPTORS
       What is a snit::widgetadaptor?

       A  snit::::widgetadaptor is a kind of snit::::widget.  Whereas a snit::::wid-
       get's hull is automatically  created  and  is  always  a  Tk  frame,  a
       snit::::widgetadaptor  can  be  based  on  any  Tk widget--or on any Snit
       megawidget, or even (with luck) on megawidgets defined using some other
       package.

       It's  called a widget adaptor because it allows you to take an existing
       widget and customize its behavior.

       How do I define a snit::widgetadaptor?

       Using the snit::::widgetadaptor command.  The definition for a snit::::wid-
       getadaptor  looks  just  like  that  for  a snit::::type or snit::::widget,
       except that the constructor must create and install the hull component.

       For  example, the following code creates a read-only text widget by the
       simple device of turning its insert and  delete  methods  into  no-ops.
       Then,  we  define  new methods, ins and del, which get delegated to the
       hull component as insert and delete.  Thus, we've adapted the text wid-
       get  and  given  it new behavior while still leaving it fundamentally a
       text widget.

       % ::snit::widgetadaptor rotext {

           constructor {args} {
               # Create the text widget; turn off its insert cursor
               installhull using text -insertwidth 0

               # Apply any options passed at creation time.
               $self configurelist $args
           }

           # Disable the text widget's insert and delete methods, to
           # make this readonly.
           method insert {args} {}
           method delete {args} {}

           # Enable ins and del as synonyms, so the program can insert and
           # delete.
           delegate method ins to hull as insert
           delegate method del to hull as delete

           # Pass all other methods and options to the real text widget, so
           # that the remaining behavior is as expected.
           delegate method * to hull
           delegate option * to hull
       }
       ::rotext

       The most important part is in the  constructor.   Whereas  snit::::widget
       creates the hull for you, snit::::widgetadaptor cannot -- it doesn't know
       what kind of widget you want.  So the first thing the constructor  does
       is  create the hull component (a Tk text widget in this case), and then
       installs it using the installhull command.

       Note: There is no instance command until you create one by installing a
       hull  component.  Any attempt to pass methods to $self prior to calling
       installhull will fail.

       Can I adapt a widget created by someone else?

       Yes.

       At times, it can be convenient to adapt a  widget  created  by  another
       party.   For  example, the Bwidget Pagesanager widget manages a set of
       frame widgets, only one of which is visible at a time.  The application
       chooses which frame is visible.  These frames are created by the Pages-
       anager itself, using its add method.

       In a case like  this,  the  Tk  widget  will  already  exist  when  the
       snit::::widgetadaptor is created.  Snit provides an alternate form of the
       installhull command for this purpose:

           snit::widgetadaptor pageadaptor {
            constructor {args} {
                # The widget already exists; just install it.
                installhull $win

                # ...
            }
           }

THE TK OPTION DATABASE
       What is the Tk option database?

       The Tk option database is a database of  default  option  values  main-
       tained  by Tk itself; every Tk application has one.  The concept of the
       option database derives from something called the  X  Windows  resource
       database;  however, the option database is available in every Tk imple-
       mentation, including those which do not use the X Windows system (e.g.,
       Microsoft Windows).

       Full  details about the Tk option database are beyond the scope of this
       document; both Practical Programming in Tcl and Tk by Welch, Jones, and
       Hobbs,  and  Effective  Tcl/Tk  Programming by Harrison and McClennan.,
       have good introductions to it.

       Snit is implemented so that most of the time  it  will  simply  do  the
       right thing with respect to the option database, provided that the wid-
       get developer does the right thing by Snit.  The body of  this  section
       goes  into  great  deal  about  what Snit requires.  The following is a
       brief statement of the requirements, for reference.


       ]o      If the widget's default widget class is not what is desired, set
              it explicitly using the widgetclass statement in the widget def-
              inition.

       ]o      When defining or delegating options, specify  the  resource  and
              class names explicitly when necessary.

       ]o      Use the installhull using command to create and install the hull
              for snit::::widgetadaptors.

       ]o      Use the install command to create and install all  other  compo-
              nents.

       The  interaction  of  Tk  widgets with the option database is a complex
       thing; the interaction of Snit with the option database  is  even  more
       so, and repays attention to detail.

       Do snit::types use the Tk option database?

       No, they don't; querying the option database requires a Tk window name,
       and snit::::types don't have one.

       Only snit::::widgets and snit::::widgetadaptors query the option  database.

       What is my snit::widget's widget class?

       Every  Tk  widget has a "widget class": a name that is used when adding
       option settings to the database.  For Tk widgets, the widget  class  is
       the same as the widget command name with an initial capital.  For exam-
       ple, the widget class of the Tk button widget is "Button".

       Similarly, the widget class of a snit::::widget defaults to the  unquali-
       fied  type  name  with  the first letter capitalized.  For example, the
       widget class of

           snit::widget ::mylibrary::scrolledText { ... }

       is "ScrolledText".

       The widget class can also  be  set  explicitly  using  the  widgetclass
       statement within the snit::::widget definition:

           snit::widget ::mylibrary::scrolledText {
            widgetclass Text

            # ...
           }

       The  above  definition says that a scrolledText megawidget has the same
       widget class as an ordinary text widget.  This might or might not be  a
       good  idea, depending on how the rest of the megawidget is defined, and
       how its options are delegated.

       What is my snit::widgetadaptor's widget class?

       The widget class of a snit::::widgetadaptor is just the widget  class  of
       its hull widget; Snit has no control over this.

       Note  that  the widget class can be changed only for frame and toplevel
       widgets, which is why these are the valid hull types for snit::::widgets.

       Try  to  use  snit::::widgetadaptors  only to make small modifications to
       another widget's behavior.  Then, it will usually  not  make  sense  to
       change the widget's widget class anyway.

       What are option resource and class names?

       Every  Tk  widget option has three names: the option name, the resource
       name, and the class name.  The option name begins with a hyphen and  is
       all  lowercase; it's used when creating widgets, and with the configure
       and cget commands.

       The resource and class names are used to initialize option default val-
       ues by querying the option database.  The resource name is usually just
       the option name minus the hyphen, but may contain uppercase letters  at
       word  boundaries; the class name is usually just the resource name with
       an initial capital, but not always.  For example, here are the  option,
       resource, and class names for several Tk text widget options:

           -background         background         Background
           -borderwidth        borderWidth        BorderWidth
           -insertborderwidth  insertBorderWidth  BorderWidth
           -padx               padX               Pad

       As  is  easily  seen,  sometimes  the  resource  and class names can be
       inferred from the option name, but not always.

       What are the resource and class names for my megawidget's options?

       For options implicitly delegated to a component using  delegate  option
       **,  the  resource  and class names will be exactly those defined by the
       component.  The configure method returns these names,  along  with  the
       option's default and current values:

           % snit::widget mytext {
            delegate option * to text

            constructor {args} {
                install text using text .text
                # ...
            }

            # ...
           }
           ::mytext
           % mytext .text
           .text
           % .text configure -padx
           -padx padX Pad 1 1
           %

       For  all  other  options  (whether  locally defined or explicitly dele-
       gated), the resource and class names can be defined explicitly, or they
       can be allowed to have default values.

       By default, the resource name is just the option name minus the hyphen;
       the the class name is just the option name with an initial capital let-
       ter.  For example, suppose we explicitly delegate "-padx":

           % snit::widget mytext {
            option -myvalue 5

            delegate option -padx to text
            delegate option * to text

            constructor {args} {
                install text using text .text
                # ...
            }

            # ...
           }
           ::mytext
           % mytext .text
           .text
           % .text configure -mytext
           -mytext mytext Mytext 5 5
           % .text configure -padx
           -padx padx Padx 1 1
           %

       Here  the  resource and class names are chosen using the default rules.
       Often these rules are sufficient, but in the case of "-padx" we'd  most
       likely  prefer  that the option's resource and class names are the same
       as for the built-in Tk widgets.  This is easily done:

           % snit::widget mytext {
            delegate option {-padx padX Pad} to text

            # ...
           }
           ::mytext
           % mytext .text
           .text
           % .text configure -padx
           -padx padX Pad 1 1
           %

       How does Snit initialize my megawidget's locally-defined options?

       The option database is queried for each of  the  megawidget's  locally-
       defined  options,  using  the option's resource and class name.  If the
       result isn't "", then it replaces the default  value  given  in  widget
       definition.   In  either  case,  the  default  can  be overriden by the
       caller.  For example,

           option add *Mywidget.texture pebbled

           snit::widget mywidget {
            option -texture smooth
            # ...
           }

           mywidget .mywidget -texture greasy

       Here, "-texture" would normally default to "smooth", but because of the
       entry  added to the option database it defaults to "pebbled".  However,
       the caller has explicitly overridden the default, and so the new widget
       will be "greasy".

       How does Snit initialize delegated options?

       That  depends  on  whether the options are delegated to the hull, or to
       some other component.

       How does Snit initialize options delegated to the hull?

       A snit::::widget's hull is a widget, and given that its  class  has  been
       set  it  is expected to query the option database for itself.  The only
       exception concerns options that are delegated to it  with  a  different
       name.  Consider the following code:

           option add *Mywidget.borderWidth 5
           option add *Mywidget.relief sunken
           option add *Mywidget.hullbackground red
           option add *Mywidget.background green

           snit::widget mywidget {
            delegate option -borderwidth to hull
            delegate option -hullbackground to hull as -background
            delegate option * to hull
            # ...
           }

           mywidget .mywidget

           set A [.mywidget cget -relief]
           set B [.mywidget cget -hullbackground]
           set C [.mywidget cget -background]
           set D [.mywidget cget -borderwidth]

       The question is, what are the values of variables A, B, C and D?

       The  value  of  A  is  "sunken".  The hull is a Tk frame which has been
       given the widget class "Mywidget";  it  will  automatically  query  the
       option  database  and  pick up this value.  Since the -relief option is
       implicitly delegated to the hull, Snit takes no action.

       The value of B is "red".  The hull will automatically pick up the value
       "green"  for  its  -background option, just as it picked up the -relief
       value.  However, Snit knows  that  -hullbackground  is  mapped  to  the
       hull's  -background  option;  hence, it queries the option database for
       -hullbackground and gets "red" and updates the hull accordingly.

       The value of C is also "red", because -background is  implicitly  dele-
       gated to the hull; thus, retrieving it is the same as retrieving -hull-
       background.  Note that this case is  unusual;  the  -background  option
       should  probably  have  been  excluded  using  the delegate statement's
       "except" clause, or (more likely) delegated to some other component.

       The value of D is "5", but not for the reason you think.  Note that  as
       it  is  defined  above,  the resource name for -borderwidth defaults to
       "borderwidth", whereas the option database entry is  "borderWidth",  in
       accordance  with  the  standard  Tk  naming  for  this option.  As with
       -relief, the hull picks up its own "-borderwidth"  option  before  Snit
       does  anything.   Because  the  option is delegated under its own name,
       Snit assumes that the correct thing has  happened,  and  doesn't  worry
       about  it  any  further.   To  avoid confusion, the -borderwidth option
       should have been delegated like this:

           delegate option {-borderwidth borderWidth BorderWidth} to hull

       For snit::::widgetadaptors, the case is somewhat altered.   Widget  adap-
       tors retain the widget class of their hull, and the hull is not created
       automatically by Snit.   Instead,  the  snit::::widgetadaptor  must  call
       installhull  in  its constructor.  The normal way to do this is as fol-
       lows:

           snit::widgetadaptor mywidget {
            # ...
            constructor {args} {
                # ...
                installhull using text -foreground white
                #
            }
            #...
           }

       In this case, the installhull command will create the hull using a com-
       mand like this:

           set hull [text $win -foreground white]

       The hull is a text widget, so its widget class is "Text".  Just as with
       snit::::widget hulls, Snit assumes that it will pick up all of its normal
       option values automatically, without help from Snit.  Options delegated
       from a different name are initialized from the option database  in  the
       same way as described above.

       In earlier versions of Snit, snit::::widgetadaptors were expected to call
       installhull like this:

           installhull [text $win -foreground white]

       This form still works--but Snit will not query the option  database  as
       described above.

       How does Snit initialize options delegated to other components?

       For  hull  components,  Snit  assumes  that Tk will do most of the work
       automatically.  Hull components are somewhat more complicated,  because
       they are matched against the option database twice.

       A component widget remains a widget still, and is therefore initialized
       from the option database in the usual way.  A  text  widget  remains  a
       text  widget whether it is a component of a megawidget or not, and will
       be created as such.

       But then, the option database is queried for all options  delegated  to
       the  component,  and the component is initialized accordingly--provided
       that the install command is used to create it.

       Before option database support was added to Snit, the usual way to cre-
       ate  a  component was to simply create it in the constructor and assign
       its command name to the component variable:

           snit::widget mywidget {
            delegate option -background to myComp

            constructor {args} {
                set myComp [text $win.text -foreground black]
            }
           }

       The drawback of this method is that Snit has no opportunity to initial-
       ize the component properly.  Hence, the following approach is now used:

           snit::widget mywidget {
            delegate option -background to myComp

            constructor {args} {
                install myComp using text $win.text -foreground black
            }
           }

       The install command does the following:


       ]o      Builds a list of the options explicitly included in the  install
              command--in this case, -foreground.

       ]o      Queries the option database for all options delegated explicitly
              to the named component.

       ]o      Creates the component using the specified command, after insert-
              ing  into  it  a list of options and values read from the option
              database.  Thus, the explicitly  include  options  (-foreground)
              will override anything read from the option database.

       ]o      If  the  widget  definition  implicitly delegated options to the
              component using delegate option **, then  Snit  calls  the  newly
              created component's configure method to receive a list of all of
              the component's options.   From  this  Snit  builds  a  list  of
              options  implicitly  delegated  to  the component which were not
              explicitly included  in  the  install  command.   For  all  such
              options,  Snit  queries  the  option database and configures the
              component accordingly.  You don't really need  to  know  all  of
              this; just use install to install your components, and Snit will
              try to do the right thing.

       What happens if I install a non-widget as a component of widget?

       A snit::::type never queries the option database.  However, a  snit::::wid-
       get  can  have  non-widget components.  And if options are delegated to
       those components, and if the install command is used to  install  those
       components, then they will be initialized from the option database just
       as widget components are.

       However, when used within a megawidget, install assumes that  the  cre-
       ated  component uses a reasonably standard widget-like creation syntax.
       If it doesn't, don't use install.


       Can I adapt widgets from other megawidget packages?

       Yes.

       However, you need to be very careful about making sure the bindtags are
       done  properly.   There's  no way for Snit to take into account all the
       possible weird things other megawidget frameworks might do wrong.

       For example, some widgets in BWidgets place their own  binding
       not  on a separate bind-tag, but on the widget itself. When used as the
       hull of a snit::::widgetadaptor this causes  them  to  be  called  before
       Snit,  removing  the  widget  command.  A  previous version of Snit was
       tripped by this and threw errors because it tried  to  operate  on  and
       with  an  already deleted widget command. Snit is now able to deal with
       this, despite the fact that the ultimate cause is at least  bad  behav-
       iour  of  Bwidget,  possibly even a bug. This however does not preclude
       that there might be other issues lurking.

KEYWORDS
       BWidget, C], Incr Tcl, adaptors, class, mega  widget,  object,  object
       oriented, widget, widget adaptors

COPYRIGHT
       Copyright (c) 2003-2004, by William H. Duquette



snit                                 0.93                           snitfaq(n)
Darwin Mac OS X man pages main menu

Contact us      |       About us      |       Term of use      |       Copyright © 2000-2010 MyWebUniversity.com ™