[comment {-*- tcl -*- doctools manpage}]
[manpage_begin snit n 0.93]
[copyright {2003-2004, by William H. Duquette}]
[moddesc {Snit}]
[titledesc   {Snit's Not Incr Tcl}]
[require Tcl 8.4]
[require snit [opt 0.93]]
[description]
[para]

Snit is yet another pure Tcl object and megawidget system.  It's
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
anything 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 [package Tk] widget, an [package {Incr Tcl}] object,
a [package BWidget] or almost anything else.

[para]

This man page is intended to be a reference only; see the accompanying
[cmd snitfaq] for a gentler, more tutorial introduction to Snit
concepts.


[section REFERENCE]

[emph {The Instance Command}]
[para]

A Snit type or widget's [method create] type method creates objects of
the type; each object has a unique name which is also a Tcl command.
This command is used to access the object's methods and data, and has
this form:

[para]

[list_begin definitions]
[call [cmd {$object}] [arg method] [arg args...]]

The [arg method] can be any of the standard instance methods defined
in the next section, or any instance method defined in the type
definition.

The subsequent [arg args] depend on the specific [arg method] chosen.

[list_end]

[para]
[emph {Standard Instance Methods}]
[para]

In addition to any delegated or locally-defined instance methods in
the type's definition, all Snit objects will have at least the
following methods:

[para]

[list_begin definitions]
[call [cmd {$object}] [method configure] [opt [arg option]] [opt [arg value]] ...]

Assigns new values to one or more options.  If called with one
argument, an [arg option] name, returns a list describing the option,
as Tk widgets do; if called with no arguments, returns a list of lists
describing all options, as Tk widgets do.

[nl]

Warning: This information will be available for delegated options only
if the component to which they are delegated has a [method configure]
method that returns this same kind of information.


[call [cmd {$object}] [method configurelist] [arg optionlist]]

Like [method configure], but takes one argument, a list of options and
their values.  It's mostly useful in the type constructor, but can be
used anywhere.


[call [cmd {$object}] [method cget] [arg option]]

Returns the option's value.


[call [cmd {$object}] [method destroy]]

Destroys the object, calling the [cmd destructor] and freeing all
related memory.

[nl]

[emph Note:]

The [method destroy] method isn't defined for [cmd snit::widget] or
[cmd snit::widgetadaptor] objects; instances of these are destroyed by
calling the [package Tk] [cmd destroy] command, just as a normal
widget is.


[call [cmd {$object}] [method {info type}]]

Returns the instance's type.


[call [cmd {$object}] [method {info vars}] [opt [arg pattern]]]

Returns a list of the object's instance variables (excluding Snit
internal variables).  The names are fully qualified.

[nl]

If [arg pattern] is given, it's used as a [cmd {string match}]
pattern; only names which match the pattern are returned.


[call [cmd {$object}] [method {info typevars}] [opt [arg pattern]]]

Returns a list of the object's type's type variables (excluding Snit
internal variables).  The names are fully qualified.

[nl]

If [arg pattern] is given, it's used as a [cmd {string match}]
pattern; only names which match the pattern are returned.


[call [cmd {$object}] [method {info options}] [opt [arg pattern]]]

Returns a list of the object's option names.  This always includes
local options and explicitly delegated options.  If unknown options
are delegated as well, and if the component to which they are
delegated responds to [cmd {$object configure}] like Tk widgets do,
then the result will include all possible unknown options which could
be delegated to the component.

[nl]

If [arg pattern] is given, it's used as a [cmd {string match}]
pattern; only names which match the pattern are returned.

[nl]

Note that the return value might be different for different instances
of the same type, if component object types can vary from one instance
to another.

[list_end]

[para]
[emph {Commands for use in Object Code}]
[para]

Snit defines the following commands for use in your object code:
that is, for use in type methods, instance methods, constructors,
destructors, onconfigure handlers, oncget handlers, and procs.
They do not reside in the ::snit:: namespace; instead, they are
created with the type, and can be used without qualification.


[list_begin definitions]
[call [cmd varname] [arg name]]

Given an instance variable name, returns the fully qualified name.
Use this if you're passing the variable to some other object, e.g., as
a [option -textvariable] to a Tk label widget.


[call [cmd typevarname] [arg name]]

Given an type variable name, returns the fully qualified name.  Use
this if you're passing the variable to some other object, e.g., as a
[option -textvariable] to a Tk label widget.


[call [cmd codename] [arg name]]

Given the name of a proc (but not a type or instance method), returns
the fully-qualified command name, suitable for passing as a callback.


[call [cmd from] [arg argvName] [arg option] [opt [arg defvalue]]]

The [cmd from] command plucks an option value from a list of options
and their values, such as is passed into a type's [cmd constructor].
[arg argvName] must be the name of a variable containing such a list;
[arg option] is the name of the specific option.

[nl]

[cmd from] looks for [arg option] in the option list.  If it is found,
it and its value are removed from the list, and the value is returned.
If [arg option] doesn't appear in the list, then the [arg defvalue] is
returned.

If the option is a normal (undelegated) option, and [arg defvalue] is
not specified, then the option's default value as specified in the
type definition will be returned instead.

       
[call [cmd variable] [arg name]]

Normally, instance variables are defined in the type definition along
with the options, methods, and so forth; such instance variables are
automatically visible in all instance-specific code.  However,
instance code (e.g., method bodies) can declare such variables
explicitly using the [cmd variable] command, if desired; or, instance
code can use the [cmd variable] command to declare instance variables
that don't appear in the type definition.

[nl]

It's generally best to define all instance variables in the type
definition, and omit declaring them in methods and so forth.

[nl]

Note that this is not the same as the standard Tcl [cmd ::variable]
command.


[call [cmd typevariable] [arg name]]

Normally, type variables are defined in the type definition, along
with the instance variables; such type variables are automatically
visible in all of the type's code.  However, type methods, instance
methods and so forth can use [cmd typevariable] to declare type
variables explicitly, if desired; or, they can use [cmd typevariable]
to declare type variables that don't appear in the type definition.

[nl]

It's generally best to declare all type variables in the type
definition, and omit declaring them in methods, type methods, and so
forth.


[call [cmd install] [arg compName] [const using] [arg objType] [arg objName] [arg args...]]

Creates a new object and installs it as a component, as described
under [emph {Components and Delegation}].

If this is a [cmd snit::type], then the following two commands are
equivalent:

[nl]
[example {
    install myComp using myObjType $self.myComp options...
}]
[nl]
[example {
    set myComp [myObjType $self.myComp options...]
}]
[nl]

Note that whichever method is used, [arg compName] must still be
declared in the type definition using [cmd variable], or must be
referenced in at least one [cmd delegate] statement.

If this is a [cmd snit::widget] or [cmd snit::widgetadaptor], and if
options have been delegated to component [arg compName], then those
options will receive default values from the Tk option database.  Note
that it doesn't matter whether the component to be installed is a
widget or not.  See [emph {The Tk Option Database}] for more
information.


[call [cmd installhull] [const using] [arg widgetType] [arg args...]]
[call [cmd installhull] [arg name]]

The constructor of a [cmd snit::widgetadaptor] must create a widget to
be the object's hull component; the widget is installed as the hull
component using this command.  Note that the installed widget's name
must be [const {$win}].

This command has two forms.

The first form specifies the [arg widgetType] and the [arg args...]
(that is, the hardcoded option list) to use in creating the hull.
Given this form, [cmd installhull] creates the hull widget, and
initializes any options delegated to the hull from the Tk option
database.

In the second form, the hull widget has already been created; note
that its name must be "$win".  In this case, the Tk option database is
[emph not] queried for any options delegated to the hull.

See [emph {The Tk Option Database}] for more information
about [cmd snit::widgetadaptor]s and the option database.

The longer form is preferred; however, the shorter form allows the
programmer to adapt a widget created elsewhere, which is sometimes
useful.  For example, it can be used to adapt a "page" widget created
by a BWidgets tabbed notebook or pages manager widget.

[nl]

The command which creates the hull widget usually just passes its
result to [cmd installhull] as follows:

[nl]
[example {
    installhull [frame $win options....]
}]


[call [cmd mymethod] [arg name] [opt [arg args...]]]

The [cmd mymethod] command is used for formatting callback commands to
be passed to other objects.  It returns a command that when called
will invoke method [arg name] with the specified arguments, plus of
course any arguments added by the caller.  In other words, both of the
following commands will cause my object's [method dosomething] method
to be called when [cmd {$button}] is pressed:

[nl]
[example {
       $button configure -command [list $self dosomething myargument]
       
       $button configure -command [mymethod dosomething myargument]
}]
[nl]

The chief distinction between the two is that the latter form will not
break if the creator of my object renames its object command.

[list_end]
[para]

[emph {Type and Widget Definitions}]
[para]

Snit provides the following commands for defining new types:

[para]
[list_begin definitions]

[call [cmd snit::type] [arg name] [arg definition]]

Defines a new abstract data type called [arg name].  If [arg name] is
not a fully qualified command name, it is assumed to be a name in the
namespace in which the [cmd snit::type] command appears (usually the
global namespace).  It returns the fully qualified type name.

[nl]

The type name is then a command which is used to create objects of the
new type, along with other activities.

[nl]

The [cmd snit::type] [arg definition] block is a script which may
contain the following definitions:

[nl]
[list_begin definitions]
[call [cmd typevariable] [arg name] [opt [arg value]]]

Defines a type variable with the specified [arg name], and optionally
the specified [arg value].  Type variables are shared by all instances
of the type.  This definition can be used to define array variables,
but cannot initialize their elements.

       
[call [cmd typemethod] [arg name] [arg arglist] [arg body]]

Defines a type method with the specified name, argument list, and
body.

The variable [var type] is automatically defined in the [arg body] to
the type's fully-qualified name.

[nl]

The [arg arglist] is a normal Tcl argument list and may contain
default arguments and the [var args] argument; however, it may not
contain the argument names [var type], [var self], [var selfns], or
[var win].

[nl]

Type variables defined in the type [arg definition] are automatically
visible in the [arg body] of every type method.


[call [cmd option] [arg namespec] [opt [arg defaultValue]]]

Defines an option for instances of this type, and optionally gives it
an initial value.  (The option's value defaults to the empty string if
no initial value is specified.)

[nl]

An option defined in this way is said to be [term {locally defined}].

[nl]

The [arg namespec] is a list defining the option's
name, resource name, and class name, e.g.:

[nl]
[example {
    option {-font font Font} {Courier 12}
}]
[nl]

The option name must begin with a hyphen, and must not contain any
upper case letters. The resource name and class name are optional; if
not specified, the resource name defaults to the option name, minus
the hyphen, and the class name defaults to the resource name with the
first letter capitalized.  Thus, the following statement is equivalent
to the previous example:

[nl]
[example {
    option -font {Courier 12}
}]
[nl]

See [emph {The Tk Option Database}] for more information about
resource and class names.

Options are normally set and retrieved using the standard
[method configure] and [method cget] instance methods.

[call [cmd variable] [arg name] [opt [arg value]]]

Defines an instance variable, a private variable associated with each
instance of this type, and optionally its initial value.  This
definition can be used to define array instance variables, but cannot
initialize their elements.

[nl]

Note that the [cmd delegate] statement implicitly defines an instance
variable for the named component.

       
[call [cmd method] [arg name] [arg arglist] [arg body]]

Defines an instance method, a subcommand of each instance of this
type, with the specified name, argument list and body.

The [arg arglist] is a standard Tcl argument list, and may contain
default values and the argument names.

The [arg arglist] is a normal Tcl argument list and may contain
default arguments and the [var args] argument.  In addition, the
method is implicitly passed the following arguments as well:

[var type], which contains the fully-qualified type name; [var self],
which contains the current instance command name; [var selfns], which
contains the name of the instance's private namespace; and [var win],
which contains the original instance name.

Consequently, the [arg arglist] may not contain the argument names
[const type], [const self], [const selfns], or [const win].

[nl]

An instance method defined in this way is said to be

[term {locally defined}].

[nl]

Type and instance variables defined in the type [arg definition] are
automatically visible in all instance methods.  If the type has
locally defined options, the [var options] array is also visible.


[call [cmd constructor] [arg arglist] [arg body]]

The constructor definition specifies a [arg body] of code to be
executed when a new instance is created.

[nl]

The [arg arglist] is a normal Tcl argument list and may contain
default arguments and the [var args] argument.  As with methods, the
arguments [var type], [var self], [var selfns], and [var win], are
defined implicitly.

[nl]

If the constructor is not defined, it defaults to this:

[nl]
[example {
    constructor {args} {
        $self configurelist $args
    }
}]
[nl]

For standard Tk widget behavior (or to achieve the behavior of
previous versions of snit) the argument list should be the single name
[const args], as shown.


[call [cmd destructor] [arg body]]

The destructor is used to code any actions which must take place when
an instance of the type is destroyed: typically, the destruction of
anything created in the constructor.

[nl]

As with arguments, the parameters [var type], [var self],
[var selfns], and [var win], are defined implicitly.


[call [cmd onconfigure] [arg name] [arg arglist] [arg body]]

Every locally-defined option has an [cmd onconfigure] handler which is
called when the option is set to a new value by the [method configure]
or [method configurelist] instance method.

[nl]

The [arg arglist] may contain exactly one argument name. As with
methods, the arguments [var type], [var self], [var selfns], and
[var win], are defined implicitly.

[nl]

If no explicit onconfigure handler is defined for an option, the
handler is defined as follows:

[nl]
[example {
    onconfigure name {value} {
        set options(name) $value
    }
}]
[nl]

If an explicit onconfigure handler is defined, the options array will
be updated with the new value only if the handler so updates it.


[call [cmd oncget] [arg name] [arg body]]

Every locally-defined option has an [cmd oncget] handler which is
called when the option's value is retrieved.

Although there is no explicit argument list, the arguments [var type],
[var self], [var selfns], and [var win], are defined implicitly, just
as they are for methods.

[nl] 

The variables [var type], [var self], [var selfns], and [var win] are
defined as usual in the handler's [arg body].  Whatever the handler
returns will be the return value of the call to the [method cget]
instance method.

[nl]

If no explicit oncget handler is defined for an option, the handler is
defined as follows:

[nl]
[example {
    oncget name {
        return $options(name)
    }
}]


[call [cmd proc] [arg name] [arg args] [arg body]]

Defines a new Tcl procedure in the type's namespace.  The new proc
differs from a normal Tcl proc in that all type variables defined in
the type [arg definition] are automatically visible.

[nl]

Although they are not implicitly defined for procs, the argument names
[const type], [const self], [const selfns], and [const win] should be
avoided.


[call [cmd delegate] [const method] [arg name] [const to] [arg comp]]
[call [cmd delegate] [const method] [arg name] [const to] [arg comp] [const as] [arg target]]
[call [cmd delegate] [const method] [const *] [const to] [arg comp]]
[call [cmd delegate] [const method] [const *] [const to] [arg comp] [const except] [arg exceptions]]


Delegates one or more instance methods to a component of the
object. When a method [arg name] is explicitly stated, it will
automatically be delegated to the named component as though the method
were defined as follows:

[nl]
[example {
    method name {args...} {
        $comp mymethod args...
    }
}]
[nl]

If desired, the delegated method may [arg target] a method with a
different name by using the [const as] clause; the [arg target] may
also include arguments add to the beginning of the argument list.

[nl]

The form "delegate method *" delegates all unknown method names to the
specified [arg comp]onent.  The [const except] clause can be used to
specify a list of [arg exceptions], i.e., method names that will not
be so delegated.

[nl]

A method cannot be both locally defined and delegated.


[call [cmd delegate] [const option] [arg namespec] [const to] [arg comp]]
[call [cmd delegate] [const option] [arg namespec] [const to] [arg comp] [const as] [arg target]]

[call [cmd delegate] [const option] [const *] [const to] [arg comp]]
[call [cmd delegate] [const option] [const *] [const to] [arg comp] [const except] [arg exceptions]]

Defines a delegated option; the [arg namespec] is defined as for the
[cmd option] statement.

When the [method configure], [method configurelist], or [method cget]
instance method is used to set or retrieve the option's value, the
equivalent [method configure] or [method cget] command will be applied
to the component as though these [cmd onconfigure] and [cmd oncget]
handlers were defined, where [arg name] is the option name from the
[arg namespec]:

[nl]
[example {
    onconfigure name {value} {
        $comp configure name $value
    }

    oncget name {
        return [$comp cget name]
    }
}]
[nl]

If the [const as] clause is specified, then the [arg target] option
name is used in place of [arg name].

[nl]

The form "delegate option *" delegates all unknown method names to the
specified [arg comp].  The [const except] clause can be used to
specify a list of [arg exceptions], i.e., option names that will not
be so delegated.

[nl]

Warning: options can only be delegated to a component if it supports
the [method configure] and [method cget] instance methods.

[nl]

Note that an option cannot be both locally defined and delegated.


[call [cmd expose] [arg comp]]
[call [cmd expose] [arg comp] [const as] [arg method]]

Exposes component [arg comp] as a method of the type.  In the first
form, the method name is [arg comp]; in the second form, the method
name is [arg method].

[nl]

This differs from delegation in that it maps an instance method to the
component itself instead of to one of the component's methods.

[nl]

Calling the new instance method is just like calling the component,
except that if the method is called with no arguments it returns the
component.

[list_end]


[call [cmd snit::widget] [arg name] [arg definition]]

This command defines a Snit megawidget type with the specified

[arg name].

The [arg definition] is defined identically to that for

[cmd snit::type]. A [cmd snit::widget] differs from a [cmd snit::type]
in these ways:

[nl]
[list_begin bullet]
[bullet]

Every [cmd snit::widget] instance has an automatically-created
component called [var hull], which is normally a Tk frame widget.
Other widgets created as part of the megawidget will be created within
this widget.

[nl]

The hull component is initially created with the requested widget
name; then Snit does some magic, renaming the hull component and
installing its own instance command in its place.

The hull component's new name is saved in an instance variable called
[var hull].

[bullet]

The name of an instance must be valid Tk window name, and the parent
window must exist.

[list_end]
[nl]

A [cmd snit::widget] definition can include any of statements allowed
in a [cmd snit::type] definition, and may also include these as well:

[nl]

[list_begin definitions]

[call [cmd widgetclass] [arg name]]

Sets the [cmd snit::widget]'s widget class to [arg name], overriding
the default.  See [emph {The Tk Option Database}] for more
information.

[call [cmd hulltype] [arg type]]

Determined the kind of widget used as the [cmd snit::widget]'s hull.
The [arg type] may be [cmd frame] (the default) or [cmd toplevel].

[list_end]

       
[call [cmd snit::widgetadaptor] [arg name] [arg definition]]

This command defines a Snit megawidget type with the specified name.
It differs from [cmd snit::widget] in that the instance's [var hull]
component is not created automatically, but is created in the
constructor and installed using the [cmd installhull] command.  Once
the hull is installed, its instance command is renamed and replaced as
with normal [cmd snit::widget]s.  The original command is again
accessible in the instance variable [var hull].

[nl]

Note that in general it is not possible to change the
[emph {widget class}] of a [cmd snit::widgetadaptor]'s hull widget.
See [emph {The Tk Option Database}] for information on how
[cmd snit::widgetadaptor]s interact with the option database.

[call [cmd snit::typemethod] [arg type] [arg name] [arg arglist] [arg body]]

Defines a new typemethod (or redefines an existing typemethod)
for a previously existing [arg type].

[call [cmd snit::method] [arg type] [arg name] [arg arglist] [arg body]]

Defines a new instance method (or redefines an existing instance
method) for a previously existing [arg type].  Note that delegated
instance methods can't be redefined.

[list_end]

[para]
[emph {The Type Command}]
[para]

A type or widget definition creates a type command, which is used to
create instances of the type.  The type command this form.

[para]
[list_begin definitions]
[call [cmd {$type}] [arg typemethod] [arg args]...]

The [arg typemethod] can be any of the standard type methods defined
in the next section, or any type method defined in the type
definition.

The subsequent [arg args] depend on the specific [arg typemethod]
chosen.

[list_end]

[para]
[emph {Standard Type Methods}]
[para]

In addition to any typemethods in the type's definition, all types and
widgets will have at least the following method:

[para]

[list_begin definitions]

[call [cmd {$type}] [method create] [arg name] [opt "[arg option] [arg value] ..."]]

Creates a new instance of the type, giving it the specified [arg name]
and calling the type's constructor.

[nl]

For [cmd snit::type]s, if [arg name] is not a fully-qualified command
name, it is assumed to be a name in the namespace in which the call to
[cmd snit::type] appears.  The method returns the fully-qualified
instance name.

[nl]

For [cmd snit::widget]s and [cmd snit::widgetadaptor]s, [arg name]
must be a valid widget name; the method returns the widget name.

[nl]

So long as [arg name] does not conflict with any defined type method
name, the [method create] keyword may be omitted.

[nl]

If the [arg name] includes the string [const %AUTO%], it will be
replaced with the string [const {$type$counter}] where [const {$type}] is
the type name and [const {$counter}] is a counter that increments each
time [const %AUTO%] is used for this type.

[nl]

By default, any arguments following the [arg name] will be a list of
[arg option] names and their [arg value]s; however, a type's
constructor can specify a different argument list.


[call [cmd {$type}] [method {info typevars}] [opt [arg pattern]]]

Returns a list of the type's type variables (excluding Snit internal
variables); all variable names are fully-qualified.

[nl]

If [arg pattern] is given, it's used as a [cmd {string match}]
pattern; only names which match the pattern are returned.


[call [cmd {$type}] [method {info instances}] [opt [arg pattern]]]

Returns a list of the type's instances.  For [cmd snit::type]s, it
will be a list of fully-qualified instance names;
for [cmd snit::widget]s, it will be a list of Tk widget names.

[nl]

If [arg pattern] is given, it's used as a [cmd {string match}]
pattern; only names which match the pattern are returned.


[call [cmd {$type}] [method destroy]]

Destroys the type's instances, the type's namespace, and the type
command itself.

This method is defined only for [cmd snit::type]s; [cmd snit::widget]s
use the Tk [cmd destroy] command instead.

[list_end]

[para]
[emph {Components and Delegation}]
[para]

When an object includes other objects, as when a toolbar contains
buttons or a GUI object contains an object that references a database,
the included object is called a component.  The standard way to handle
component objects owned by a Snit object is to assign their names to a
instance variable.  In the following example, a [cmd dog] object has a
[cmd tail] object:

[para]
[example {
    snit::type dog {
        variable mytail
    
        constructor {args} {
            set mytail [tail %AUTO% -partof $self]
            $self configurelist $args
        }
    
        method wag {} {
            $mytail wag
        }
    }
    
    snit::type tail {
        option -length 5
        option -partof
        method wag {} { return "Wag, wag, wag."}
    }
}]
[para]

Because the [cmd tail] object's name is stored in an instance
variable, it's easily accessible in any method.

[para]

As of Snit 0.84, the [cmd install] command provides an alternate way
to create and install the component:

[para]
[example {
snit::type dog {
    variable mytail

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

    method wag {} {
        $mytail wag
    }
}
}]
[para]

For [cmd snit::type]s, the two methods are equivalent; for
[cmd snit::widget]s and [cmd snit::widgetadaptor]s, the [cmd install]
command properly initializes delegated options by querying
[emph {the Tk option database}].

[para]

In the above examples, the [cmd dog] object's [method wag] method
simply calls the [cmd tail] component's [method wag] method.  In OO
circles, this is called delegation.  Snit provides an easier way to do
this, as shown:

[para]
[example {
    snit::type dog {
        delegate method wag to mytail
    
        constructor {args} {
            set mytail [tail %AUTO% -partof $self]
            $self configurelist $args
        }
    }
}]
[para]

The [cmd delegate] statement in the type definition implicitly defines
the instance variable [var mytail] to hold the component's name; it
also defines the [cmd dog] object's [method wag] method, delegating it
to the [var tail] component.

[para]

If desired, all otherwise unknown methods can be delegated to a
specific component:

[para]
[example {
    snit::type dog {
	delegate method * to mytail

	constructor {args} {
	    set mytail [tail %AUTO% -partof $self]
	    $self configurelist $args
	}

	method bark { return "Bark, bark, bark!" }
    }
}]
[para]

In this case, a [cmd dog] object will handle its own [method bark]
method; but [method wag] will be passed along to [cmd mytail].  Any
other method, being recognized by neither [cmd dog] nor [cmd tail],
will simply raise an error.

[para]

Option delegation is similar to method delegation, except for the
interactions with the Tk option database; this is described in the
next section.

[para]
[emph {The Tk Option Database}]
[para]

This section describes how Snit interacts with the Tk option database,
and assumes the reader has a working knowledge of the option database
and its uses.  The book [emph {Practical Programming in Tcl and Tk}]
by Welch et al has a good introduction to the option database, as does
[emph {Effective Tcl/Tk Programming}].

[para]

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
widget 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.

[para]

[list_begin bullet]
[bullet]

If the widget's default widget class is not what is desired, set it
explicitly using [cmd widgetclass] in the widget definition.

[bullet]

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

[bullet]

Use [cmd {installhull using}] to install the hull for
[cmd snit::widgetadaptor]s.

[bullet]

Use [cmd install] to install all other components.

[list_end]
[para]

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.

[para]

[emph {Setting the widget class:}] Every Tk widget has a widget class.
For Tk widgets, the widget class name is the just the widget type name
with an initial capital letter, e.g., the widget class for
[cmd button] widgets is "Button".

[para]

Similarly, the widget class of a [cmd snit::widget] defaults to the
unqualified type name with the first letter capitalized.  For example,
the widget class of

[para]
[example {snit::widget ::mylibrary::scrolledText { ... }}]
[para]

is "ScrolledText".  The widget class can also be set explicitly using
the [cmd widgetclass] statement within the [cmd snit::widget]
definition.

[para]

Note that only [cmd frame] and [cmd toplevel] widgets allow the user
to change the widget class name, which is why they are the allowable
hull types for normal [cmd snit::widget]s.

[para]

The widget class of a [cmd snit::widgetadaptor] is just the widget
class of its hull widget; this cannot be changed unless the hull
widget is a [cmd frame] or [cmd toplevel], in which case it will
usually make more sense to use [cmd snit::widget] rather than
[cmd snit::widgetadaptor].

[para]

[emph {Setting option resource names and classes:}] In Tk, every
option has three names: the option name, the resource name, and the
class name.  The option name begins with a hyphen is all lowercase;
it's used when creating widgets, and with the [cmd configure] and
[cmd cget] commands.

[para]

The resource and class names are used to initialize option default
values by querying the Tk 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
[cmd text] widget options:

[para]
[example {
    -background         background         Background 
    -borderwidth        borderWidth        BorderWidth 
    -insertborderwidth  insertBorderWidth  BorderWidth 
    -padx               padX               Pad 
}]
[para]

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

[para]

Snit options also have a resource name and a class name.  By default,
these names follow the rule given above: the resource name is the
option name without the hyphen, and the class name is the resource
name with an initial capital.  This is true for both locally-defined
options and explicitly delegated options:

[para]
[example {
    snit::widget mywidget {
	option -background
	delegate option -borderwidth to hull
	delegate option * to text
	# ...
    }
}]
[para]

In this case, the widget class name is "Mywidget".  The widget has the
following options: -background, which is locally defined,
-borderwidth, which is explicitly delegated; all other widgets are
delegated to a component called "text", which is probably a Tk

[cmd text] widget.  If so, [cmd mywidget] has all the same options as
a [cmd text] widget.  The option, resource, and class names are as
follows:

[para]
[example {
    -background  background  Background
    -borderwidth borderwidth Borderwidth
    -padx        padX        Pad
}]
[para]

Note that the locally defined option, "-background", happens to have
the same three names as the standard Tk "-background" option; and
"-pad", which is delegated implicitly to the "text" component has the
same three names for [cmd mywidget] as it does for the [cmd text]
widget.  "-borderwidth", on the other hand, has different resource and
class names than usual, because the internal word "width" isn't
capitalized.  For consistency, it should be; this is done as shown:

[para]
[example {
    snit::widget mywidget {
	option -background
	delegate option {-borderwidth borderWidth} to hull
	delegate option * to text
	# ...
    }
}]
[para]

The class name will default to "BorderWidth", as expected.

[para]

Suppose, however, that [cmd mywidget] also delegated "-padx" and
"-pady" to the hull.  In this case, both the resource name and the
class name must be specified explicitly:

[para]
[example {
    snit::widget mywidget {
	option -background
	delegate option {-borderwidth borderWidth} to hull
	delegate option {-padx padX Pad} to hull
	delegate option {-pady padY Pad} to hull
	delegate option * to text
	# ...
    }
}]
[para]

[emph {Querying the option database:}] If you set your widgetclass and
option names as described above, Snit will query the option database
when each instance is created, and will generally do the right thing
when it comes to querying the option database.  The remainder of this
section goes into the gory details.

[para]
[emph {Initializing locally defined options:}]
[para]

When an instance of a snit::widget is created, its locally defined
options are initialized as follows: each option's resource and class
names are used to query the Tk option database.  If the result is
non-empty, it is used as the option's default; otherwise, the default
hardcoded in the type definition is used.  In either case, the default
can be overridden by the caller.  For example,

[para]
[example {
    option add *Mywidget.texture pebbled

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

    mywidget .mywidget -texture greasy
}]
[para]

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".

[para]
[emph {Initializing options delegated to the hull:}]
[para]

A [cmd 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:

[para]
[example {
    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]
}]
[para]

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

[para]

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.

[para]

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.

[para]

The value of C is also "red", because -background is implicitly
delegated to the hull; thus, retrieving it is the same as retrieving
-hullbackground.  Note that this case is unusual; in practice,
-background would probably be explicitly delegated to some other
component.

[para]

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".  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.

[para]

For [cmd snit::widgetadaptor]s, the case is somewhat altered.  Widget
adaptors retain the widget class of their hull, and the hull is not
created automatically by Snit.  Instead, the [cmd snit::widgetadaptor]
must call [cmd installhull] in its constructor.  The normal way to do
this is as follows:

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

In this case, the [cmd installhull] command will create the hull using
a command like this:

[para]
[example {
    set hull [text $win -foreground white]
}]
[para]

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

[para]
[emph {Initializing options delegated to other components:}]
[para]

Non-hull components are matched against the option database in two
ways.  First, a component widget remains a widget still, and therefore
is initialized from the option database in the usual way.

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

[para]

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

[para]
[example {
    snit::widget mywidget {
	delegate option -background to myComp

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

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

[para]
[example {
    snit::widget mywidget {
	delegate option -background to myComp

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

The [cmd install] command does the following:

[para]
[list_begin bullet]
[bullet]

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

[bullet]

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

[bullet]

Creates the component using the specified command, after inserting
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.

[bullet]

If the widget definition implicitly delegated options to the component
using "delegate option *", then Snit calls the newly created
component's [cmd 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 [cmd install] command.  For all such options, Snit
queries the option database and configures the component accordingly.

[list_end]

[para]
[emph {Non-widget components:}] The option database is never queried
for [cmd snit::type]s, since it can only be queried given a Tk widget
name.

However, [cmd snit::widget]s can have non-widget components.  And if
options are delegated to those components, and if the [cmd install]
command is used to install those components, then they will be
initialized from the option database just as widget components are.

[para]


[section CAVEATS]

Please understand that while Snit is already very stable, it is still
early days in Snit's development, and not be too critical. If you have
problems, find bugs, or new ideas you are hereby cordially invited to
submit a report of your problem, bug, or idea at the SourceForge
trackers for tcllib, which can be found at

[uri http://sourceforge.net/projects/tcllib/].

The relevant category is [emph snit].

[para]

One particular area to watch is the interaction of Snit with other
megawidget packages. Some widgets in BWidgets for example place their
own <Destroy> binding not on a separate bind-tag, but on the widget
itself. When used as the hull of a [cmd 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 behaviour of Bwidget, possibly even a
bug. This however does not preclude that there might be other issues
lurking.

[para]

So, if you use a [cmd snit::widgetadaptor] to adapt somebody else's
megawidget,

you need to be very careful about making sure the [cmd 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.


[section {KNOWN BUGS}]

[list_begin bullet]
[bullet]

Error stack traces returned by Snit are extremely ugly and typically
contain far too much information about Snit internals.

[bullet]

Also see the SourceForge Trackers at
[uri http://sourceforge.net/projects/tcllib/], category [emph snit].

[list_end]


[section HISTORY]

During the course of developing Notebook

(See [uri http://www.wjduquette.com/notebook]), my Tcl-based personal
notebook application, I found I was writing it as a collection of
objects.  I wasn't using any particular object-oriented framework; I
was just writing objects in pure Tcl following the guidelines in my
Guide to Object Commands

(See [uri http://www.wjduquette.com/tcl/objects.html]), along with a
few other tricks I'd picked up since.  And it was working very well.
But on the other hand, it was getting tiresome.  Writing objects in
pure Tcl is straightforward, once you figure it out, but there's a
fair amount of boilerplate code to write for each one, especially if
you're trying to create megawidgets or create objects with options,
like Tk widgets have.

[para]

So that was one thing--tedium is a powerful motivator.  But the other
thing I noticed is that I wasn't using inheritance at all, and I
wasn't missing it.  Instead, I was using delegation: objects that
created other objects and delegated methods to them.

[para]

And I said to myself, "This is getting tedious...there has got to be a
better way."  And one afternoon, on a whim, I started working on Snit,
an object system that works the way Tcl works.  Snit doesn't support
inheritance, but it's great at delegation, and it makes creating
megawidgets easy.

[para]

I should add, I'm not particularly down on Incr Tcl.  But "Snit's Not
Incr Tcl" occurred to me while I was casting about for a name, and I
guess there was a certainly inevitability about it.

[para]

If you have any comments or suggestions (or bug reports!) don't
hesitate to send me e-mail at [uri will@wjduquette.com].  In addition,
there's now a Snit mailing list; you can find out more about it at the
Snit home page, see [uri http://www.wjduquette.com/snit].

[para]


[section CREDITS]

Snit has been designed and implemented from the very beginning by
William H. Duquette.  However, much credit belongs to the following
people for using Snit and providing me with valuable feedback: Rolf
Ade, Colin McCormack, Jose Nazario, Jeff Godfrey, Maurice Diamanti,
Egon Pasztor, David S. Cargo, Tom Krehbiel, Michael Cleverly, 
Andreas Kupries, Marty Backe, Andy Goth, Jeff Hobbs, and Brian Griffin.


[keywords class {object oriented} object C++]
[keywords {Incr Tcl} BWidget]
[keywords widget adaptors {widget adaptors} {mega widget}]
[manpage_end]
