		       Auxiliary Library for TK

			    Kevin B. Kenny
			   GE Corporate R&D
			  kennykb@crd.ge.com

========================================================================

	      Copyright 1993 by General Electric Company 
			 All rights reserved.

	      Published only in limited copyright sense.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of General Electric not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

General Electric makes no representations about the suitability of
this software for any purpose.  It is provided ``as is'' without
express or implied warranty.

This work was supported in part by the DARPA Initiative in Concurrent
Engineering (DICE) through DARPA Contracts MDA972-88-C-0047 and
MDA972-92-C-0027.  This work was supported in part by the Tri-Services
Microwave and Millimeter-Wave Advanced Computational Environment
(MMACE) program under Naval Research Laboratory contract
N00014-92-C-2044.

========================================================================

			       PREFACE

There has been a flurry of discussion on the comp.lang.tcl newsgroup
recently about extending the keyboard and mouse bindings for such
things as tab traversal, keyboard operation of buttons, highlighting
of the focused widget (as MOTIF does), arranging for keystrokes to
replace the selection in entry boxes, and other similar look and feel
issues.

I've been working on resolving these issues in Tk for some time now,
and I've put together an auxiliary library, which is designed to
operate in parallel with /usr/local/lib/tk, that addresses some of
them.  I've been enough of a perfectionist, however, that I've been
reluctant to release it.

While the auxiliary library isn't really ready for prime time, the
level of interest generated by the newsgroup discussions shows that
there is a widespread need, and has convinced me to package a beta
release, so that other people can experiment with the concepts.  While
it will undoubtedly be difficult to achieve consensus among the Tk
community about the best way to resolve many of these issues, having a
reference implementation may help serve as a straw man to start
bringing the ideas together.

I'm putting the library code in a file called, `tkauxlib.tar.Z' in the
incoming directory on harbor.ecn.purdue.edu, from whence it should
migrate to the download directories.  I'll look for it in a couple of
days and follow-up once it's in place.

BACKGROUND: THE MODEL-VIEW-CONTROLLER PARADIGM

One popular way to structure user interfaces is the model-view-
controller paradigm used in Smalltalk.  In this scheme, any user
interface is divided into three nearly independent components:

  * the model, which holds the data being manipulated and conducts all
    the computations,

  * the view, which displays the data to the user, and

  * the controller, which responds to all user actions and notifies
    the model and view appropriately.

The model-view-controller scheme is quite versatile, since it allows
for:

  * multiple, simultaneous views of the same data (as a trivial
    example, the same number could simultaneously appear on a scale,
    in an entry box, and as a co-ordinate of something on a canvas),

  * consistency of the user interface -- all widgets that share the
    same controller respond to user input in precisely the same way.

The model-view-controller paradigm is an obvious choice for user
interface design in Tk.  The models, views, and controllers all have
obvious, natural representations in Tcl, and the event management does
all the really difficult tasks of co-ordinating them.

The natural representation of a model is as a cluster of Tcl
variables, together with procedures and commands that manipulate them.
The `trace' mechanism in Tcl, and the corresponding `Tcl_TraceVar'
mechanism in the C library, provide an elegant way for a model to
notify a view when data change and must be redisplayed.  In fact, the
built-in widgets of Tcl, for the most part, use such a mechanism --
all of the widgets that support `-variable' or `-textvariable'
configuration switches arrange to redisplay themselves when the
underlying variables change, using one of the trace mechanisms.

The natural representation of a view, of course, is as a set of Tk
widgets.  Generally speaking, they will be grouped in a single subtree
of the widget tree, particularly as descendants of a frame or toplevel
widget.  The reason for using a frame or toplevel, as opposed to any
other sort of widget such as a canvas, is the fact that the widget
class may be specified using a `-class' configuration option, and
therefore a particular class of view can have its own set of resources
controlling its appearance.

The controller is defined by the set of event bindings applied to the
view using the Tk `bind' command.  In the present implementation of
Tk, the interaction among controllers can get complicated because
multiple bindings cannot be established for a single event -- a
Tcl-level event dispatcher is therefore needed to sort things out.
This situation should be greatly alleviated in Tk 4.0, where the `bind
tags' mechanism should allow greater flexibility in dispatching
events.

The problems of look and feel that the posters have been discussing
are all issues related to the controller.  The Tk widgets do a
reasonable job at providing the views, and the models are perforce
specific to the applications.

CONTROLLER ISSUES

Tk's default bindings already address a number of the requirements for
a shared controller.  Most of the mouse functions are consistent with
the Motif style.  Keyboard bindings (at least rudimentary ones) are
provided for text and entry widgets.  Tk is tantalizing close to
giving a comparatively unskilled programmer full Motif compliance
automatically.  Unfortunately, the remaining functions are not always
that easy to provide.  There are some complex requirements that remain
to be addressed:

  * Non-text-based widgets cannot accept the keyboard focus.  Buttons,
    scales, listboxes, and so on cannot be driven from the keyboard,
    and require the use of a mouse.

  * There is no concept of tab traversal.  The Tab and Shift-Tab keys
    (or Control-Tab and Control-Shift-Tab, if Tab and Shift-Tab have a
    natural meaning) must be bound explicitly for every component of
    the user interface if they are to be used to navigate.  This
    explicit binding imposes a fair amount of bookkeeping on the
    programmer.

  * There is no convenient way to highlight the widget that has the
    keyboard focus.

  * Confining the keyboard focus in support of modal dialogs,
    auxiliary toplevel windows, and so on can require some tricky
    coding.

The auxiliary library begins to handle some of these needs, by
establishing another collection of bindings.  The remainder of this
paper discusses the functions that the auxiliary library provides, and
how to change their behavior.

STARTING THE CONTROLLER

A Tk application that wishes to use the enhanced controller puts it in
place by executing the statement:

	source /usr/local/lib/tkaux/init.tcl

This initialization file loads the rest of the controller.

  * It puts the directory where it resides (normally
    /usr/local/lib/tkaux, but obtained from `info script' so that the
    library can reside anywhere) at the head of `auto-path'.  Most of
    the functions in the controller are auto-loaded on demand, and
    some of them override corresponding ones in `$tk_library', so the
    auxiliary library must preceded $tk_library on the load path.

  * It loads a new `tkerror' function, overriding the system default.
    The primary enhancement to `tkerror' is the ability to save a
    dump of the Tk workspace to a file for analysis, enabling
    postmortem debugging of Tk scripts run at remote sites.

  * It established class bindings to support keyboard actions in a
    variety of widgets.

  * It establishes <FocusIn> and <FocusOut> bindings for the
    application main window and the Toplevel window class.  These
    bindings support a mixed focus model where change of focus among
    top-level windows is handles by the window manager, while focus is
    explicit within top-level windows.  Moreover, each top-level
    window remembers its own focus, allowing them to appear to the
    user as independent applications.

    The controller at present allows only explicit focus within
    composite widgets.  While it would be possible to add a
    focus-follows-mouse model, I personally would find the behavior
    maddening; I dislike having to keep the mouse pointer positioned
    precisely when I type, and focus-follows-mouse would require a
    warp-cursor function for tab traversal to operate correctly.

  * It establishes `all' bindings for Destroy and UnMap events.  These
    bindings allow for multiple widget destructors, giving various
    components a chance to clean up after themselves when a widget is
    destroyed.  This capability allows a variety of the controller's
    procedures to use temporary variables without risking memory
    leaks.  The bindings also allow focus to be redirected when a
    window leaves the screen.

MENU TRAVERSAL

The tk_bindForTraversal procedure in the Tk library does menu
traversal (based on the F10 key) and allows keyboard accelerators
(based on the Alt key).  The auxiliary library's controller includes
these functions without modification, and adds the corresponding
bindings to all widgets that accept the keyboard focus.

TAB TRAVERSAL

The focus_bindForTraversal procedure in the auxiliary library
established Tab traversal binding, and the initialization code invokes
it for all widgets that accept the keyboard focus.  Tab traversal
allows keyboard-based navigation among the widgets.  The following
keyboard actions are supported:

	     Table 1. Keyboard Actions for Tab Traversal

	Key		Action
	------------------------------------------------------------
	Tab		Direct the focus to the next application
			component.

	Shift-Tab	Direct the focus to the previous application
			component.

	Linefeed
	Return
	KP_Enter
	Control-j
	Control-m	Invoke the current `default button.'

Widgets sch as texts, in which keys such as Tab and Return have a
natural meaning, can use an optional parameter, `-controlonly', to
specify that these actions apply only when the Control key is also
depressed; change of focus will then take place only when the user
presses Control-Tab, Control-Shift-Tab, Control-Linefeed, and so on.
Otherwise, the keys are accepted either with or without the Control
key.

The problem of defining the `next' and `previous' application
component is not straightforward.  These must identify the next or
previous object that accepts the keyboard focus.  The object my be
either a widget or a canvas item.  In addition, there must be a
well-defined order among the components.

The most obvious answer is to order the components geometrically,
perhaps top-to-bottom and left-to-right.  Unfortunately, such a
sequence has two problems,  First, it is too inflexible; for instance,
it gives the programmer no way of specifying that a set of widgets is
arranged in columns instead of rows.  Second, the many varieties of
geometry management in Tk (packing, placement, and the `create window'
operation in canvases, with more varieties expected) make it virtually
impossible to determine the geometric sequence of the widgets in a
reliable manner.

Instead, the auxiliary library resorts to defining an arbitrary
sequence.

  * The application root window is the first widget in the sequence.

  * Child widgets follow their parents, in stacking order.  (Those who
    read the library code will note that there is version-dependent
    code throughout focusAux.tcl, since [winfo children] returns the
    child widgets in reverse stacking order in Tk 3.2 and earlier
    releases.)

  * Canvas items follow the canvas, in order of creation.  They
    precede any child widgets of the canvas.  Note that a `window'
    canvas item does not take part in the sequence, since there is no
    way for it to take the keyboard focus -- events are routed instead
    to the corresponding widget.

In addition, the keyboard focus is directed only to those objects that
can use it:

  * A widget receives the keyboard focus only if it handles at least
    one KeyPress or KeyRelease event, that is, only if a `bind'
    command for a KeyPress or KeyRelease event has been executed
    against that widget or its class.  Bindings for all widgets are
    disregarded in this test, since otherwise every widget would
    appear to accept the focus, owing to the binding of the F10 key.
    A widget that supports the `-state' configuration option does not
    receive the keyboard focus if its state is `disabled'.

  * A canvas item receives the keyboard focus only if it handles at
    least one KeyPress or KeyRelease event, that is, only if the
    application has executed a `bind' widget command for a KeyPress or
    KeyRelease event and specified that canvas item or one of its
    tags.  Once again, `all' bindings are disregarded, more for the
    sake of consistency with widgets than for any sound technical
    reason.

  * No widget receives the focus if it is not mapped.  If a widget is
    unmapped (for a reason other than withdrawing its top-level
    window) while it owns the focus, focus is automatically redirected
    to the previous focusable object in the application.  If a widget
    is destroyed while it owns the focus, focus is also redirected.

This behavior can be modified by using the focus_skip command.  The
user can specify that a widget should never receive the keyboard
focus, or that it should receive the keyboard focus even if its state
is `disabled'.

The list of objects that receive focus is circular.  If the user tabs
out of the end of the application, focus returns at the beginning, and
contrarily, if the user Shift-tabs out of the beginning, focus returns
at the end.  This behavior may be made more local by using the
`focus_confine' procedure, which keeps the Tab and Shift-Tab keys from
directing focus beyond a specified subtree of the widget hierarchy.
By default, any top-level window confines the focus within its
substructure: the user cannot use the Tab key to move to another
top-level window.

BUTTONS, RADIOBUTTONS, AND CHECKBUTTONS

The controller contains code that provides for keyboard focus to
buttons, checkbuttons, and radiobuttons.  The buttons handle the
following keys:

		 Table 2.  Key bindings for buttons.

	Key		Function
	------------------------------------------------------------
	space
	Return
	KP_Enter	Invoke the button.

	Tab		Direct focus to the next object.

	Shift-Tab	Direct focus to the previous object.

	F10
	Alt		Control menu traversal.

In addition, for each top-level window, the controller maintains a
record of the `default' button.  This is understood to be the last
button invoked from the keyboard, rather than using the mouse.  The
Return and KP_Enter keys are bound in most widgets so that they invoke
the default button.  In text widgets, where these keys have a natural
meaning, they are replaced by Control-Return and Control-KP_Enter.

The default button, and the button that owns the keyboard focus, need
to be highlighted.  Since Tk does not provide this function with
simple button widgets, the library includes a `focusable' command that
automatically creates a button within a frame so that the frame can be
highlighted.  Thus, instead of saying:

	button .foo.bar -text Quit -command "after 1 exit"

to create a button, most code that uses the auxiliary library will
create buttons with

	focusable button .foo.bar \
		-text Quit -command "after 1 exit"

This operation will create a frame `.foo.bar', with a button
`.foo.bar.b' packed within it.  The frame will normally be flat, and
have the same background color as the button.  Making the button
default will cause the frame to be sunken, and directing keyboard
focus to the button will cause the frame to take on the button's
active background color.

When a new top-level window is created, its default will not be
highlighted automatically.  It may be highlighted, and even changed,
by the command:

	button_setDefault pathName

where pathName is the name of the button or one of its ancestors (If
there are multiple buttons within pathName's widget tree, the first
will be selected).

CANVASES

The controller does not establish any particular set of bindings for
canvases or their items.  The user, however, may choose to do so.  A
common set of bindings that provided for tab traversal and keyboard
traversal of menus can be obtained by invoking

	canvas_bindForTraversal pathName tagOrId ?-controlonly?

where

    pathName
	is the path name of a canvas

    tagOrID
	is a tag or item ID that specifies the canvas item(s) for
	which the bindings should be established.

    -controlonly
	is a flag that, if present, requires the Control key with Tab,
	Shift-Tab, Return, and KP_Enter, allowing them to have natural
	meanings.  This behavior is useful with text items, since
	the user may want to enter tab and newline characters into the
	text.

ENTRIES

The binding set for entry widgets is perhaps the most developed of all
the bindings.  It supports a number of keyboard operations:

  * Plain printing characters replace the selection, instead of being
    inserted at the insertion point, if the insertion point is within
    the selection.  This change supports the Motif-style action of
    stroking out an area of text with the mouse and typing over it.

  * The Insert key works Motif-fashion.  It changes the appearance of
    the insertion cursor and toggles `replace mode'.  In replace mode,
    typing a plain printing character repaces the character to the
    right of the cursor, rather than inserting before it.

  * The Home, Begin, and Control-A keys position the insertion point
    at the left side of the entry box.

  * The Left arrow and Control-B keys move the insertion point one
    position to the left.

  * The Control-C key causes an error with the message `Interrupt',
    which may be caught and processed elsewhere in the application.

  * The DeleteRight and Control-D keys delete the character to the
    right of the insertion point.

  * The End and Control-E keys move the insertion point to the end of
    the entry.

  * The Right arrow and Control-F keys move the insertion point one
    position to the right.

  * The BackSpace and Control-H keys delete the character to the left
    of the insertion point.

  * The Control-K key erases from the insertion point to the end of
    the entry.  (On keyboards that support Erase-EOL, this would be
    a natural binding for the latter key.)

  * The Control-L key centers the insertion point within the visible
    part of the field.

  * The Tab, Shift-Tab Return, KP_Enter, LineFeed, Control-j and
    Control-m keys are bound as described in the preceding sections.

  * The Control-U key clears the entry.  (On keyboards that have a
    Clear Screen key, this would be a natural binding for it).

  * The Control-W key clears the selection.  (Only Emacs users
    probably care about this one.)

  * The Control-Y key pastes the selection into the entry at the
    insertion cursor.

  * The Insert key enters or leaves `replace mode', as described
    above.

  * The Delete key clears the selection if there is one, and otherwise
    erases the character to the left of the insertion cursor.

  * The Escape and Break keys undo the effect of typing in the entry
    and revert it to the contents of its text variable.

In order for the Escape key to operate, code also is added to handle
focusing and defocusing an entry variable.  When an entry has the
keyboard focus, it is temporarily disconnected from its text variable.
When it loses the focus, the text variable is updated from the content
of the entry.  At this time, the content may be validated; the `INPUT
VALIDATION' section below discusses entry validation.

SCALES

The controller allows keyboard focus to scale widgets.  When a scale
widget is focused, its foreground color changes to the active
foreground color.  Scale widgets can accept the following keys:

	    Table 3.  Keyboard Bindings for Scale Widgets

	Key		Function
	------------------------------------------------------------

	Up		The arrow keys push the scale in the
	Down		direction of the arrow.  The orientation
	Left		of the scale is taken into account; if
	Right		the -from value is greater than the -to
			value, the arrow keys still move the scale
			geometrically.

	plus		The plus and minus sign increment and
	minus		decrement the scale's value, irrespective
			of its orientation

	Tab		Keyboard traversal is provided as with all
	Shift-Tab	the other widgets.
	Return
	Linefeed
	KP_Enter
	F10
	Alt

TEXTS

Bindings are added to text widgets to support tab traversal and
default buttons.  The relevant keys (Return, Linefeed, KP_Enter, Tab,
and Shift-Tab) are recognized only in conjunction with the Control
key.

No other bindings beyond the Tk library ones are provided for texts.
An Emacs-like binding set would be a worthwhile addition; the fine one
that Andrew Payne has provided would be a good place to start.  I
would, however, like a way to override the bindings in the application
default file, and doing a parser for the Motif style of specifying
keyboard bindings looks to be a fair amount of work.

TOP-LEVEL WINDOWS

The concept of the default button and the current keyboard focus is
maintained independently for each top-level window, making them appear
to the user almost like separate applications  (The alternative would
be to allow TAB traversal between them, which requires warping the
mouse cursor as well if a consistent look and feel is to be provided).

Bindings are established for FocusIn and FocusOut events on top-level
windows to switch to the currently focused widget when the window
manager directs focus to a toplevel window.

EVENT MANAGEMENT

Several places in the controller require somewhat more sophisticated
event management than Tk currently provides, although somewhat less
than the proposed `bind tags' mechanism.  For this reason, certain
events are trapped by library code and dispatched to multiple
handlers.  These include both window system events and new events
defined by the controller.  They are

		      Table 4. Dispatched Events

	Event		Meaning
	------------------------------------------------------------

	Destroy		A widget has been destroyed.

	GainFocus	A widget is receiving the keyboard focus.

	LoseFocus	A widget is losing the keyboard focus.

			These events are more restrictive than 
			<FocusIn>.  Redirecting the focus among top
			level windows with the window manager does
			not cause GainFocus and LoseFocus events;
			only Tab traversal and mouse events
			do so.

	Unmap		A widget is being unmapped.

	UpdateContent	A widget is requested to bring its content
			into synchronization with its text
			variable.

	Validate	A widget is requested to check that its
			content passes any validation tests.

			The UpdateContent and Validate events are
			discussed further under `INPUT VALIDATION'
			below.

All of these events are processed by the `widget_bind',
`widget_addBinding', and `widget_event' procedures in `widget.tcl'.

INPUT VALIDATION

The controller supports deferred update to entry boxes; generally
speaking, an entry box's content is not reflected in its text variable
until the entry loses the focus.  At that time, the Validate event
will be signalled, requesting that the content be checked for
consistency.

Two validation procedures are supplied by the auxiliary library:
checkInt and checkReal.  The checkInt procedure checks that the entry
contains an integer, and checkReal checks that it contains a real
number.  Either of these can be connected to an entry widget by
executing:

    widget_addBinding .foo.entry Validate "checkReal .foo.entry"

or

    widget_addBinding .foo.entry Validate "checkInteger .foo.entry"

In addition, the programmer can provide other validation procedures.
They should accept the window path name as a parameter, return
normally if the content passes the test, and return an error if the
content fails the test.

Since the actual content of an entry box's text variable is not
updated until it has been validated, commands that are invoked from
buttons and menus may need to force the current focus item to be
synchronized in order to have a complete set of current data.  The
command

	focus_update

performs this synchronization, does all necessary validation, and
returns 0 if it was successful and 1 if it failed.  Thus, many
procedures that handle buttons begin with the code:

	if [focus_update] return

VIEWS

In addition to the controller, the auxiliary library provides a
collection of views that appear to be generally useful.  They include:

choicebox:
	A dialog box with a message, an icon, and a set of buttons
	representing possible actions.

errormessage:
	A choicebox adapted to displaying an error message, with a
	single `Dismiss' button.

fileselect:
	A reasonably complete file selection dialog.

fraction:
	A canvas representing a fraction of something, for example,
	the part of a task that has been completed, by a bar partially
	filling the canvas.

icon:
	A label containing a bitmap that is found by looking for a
	`.xbm' file along the auto_path.  The auxiliary procedure,
	`icon_find', is also provided to look up the bitmap without
	creating a widget, so that the bitmap can be installed in a
	button, canvas item, or other similar place.  A number of
	standard icons come with the library, including:

		bomb			pageup
		error			rtfm
		filltriangled		think
		filltriangler		triangled
		pagedown		triangler

	(Some of these icons are supplied for tk 3.2, and are built in
	in tk 3.3.)

labeledentry:
	A combination widget that has an entry box with optional
	labels to its left and right.  It is handly in combining forms
	with lines that look like:

		Name ______________________
		Age _______________ years

progress:
	A combination widget comprising a message, a fraction, and a
	`quit" button.  It is used for indicating the progress of
	long-running operations.

VIEW MODIFIERS

Several procedures are available to modify the appearance and behavior
of widgets by packing them into frames and establishing various
bindings.  The `focusable' one was discussed under `BUTTONS' above; it
provides Motif-style highlighting for buttons.  Other view modifiers
include:

collapsible:
	Makes a widget that is packed only upon request.  When the
	widget is unpacked  (`collapsed'), it is represented by a
	triangle and a title:

		> My widget

	When the triangle (which is a button) is invoked, it rotates,
	and the actual widget appears below it:

		V    My widget
                  .-----------------.
                  |                 |
                  |                 |
                  `-----------------'

	Judicious use of collapsible widgets can often `neaten' the
	appearance of a user interface.

modalDialog:
	The modalDialog procedure makes a widget a modal dialog.  The
	widget grabs the keyboard and mouse, and handles all
	interaction until it is dismissed (through `modalDialog_end').

transient:
	The transient procedure packs the widget into a top-level
	window, marks it transient, groups it appropriately, and
	centers it relative to another top-level window.  Most
	transient dialog boxes come through `transient' to do their
	window manager interactions.

MISCELLANEOUS SUPPORT PROCEDURES

The library contains a number of service procedures that support it
internally, and are also available to the user.  They include:

debug_traces:
	Find out what traces are active on a variable or set of
	variables.

debug_dump:
	Dump the Tcl workspace to a file for post-mortem analysis
	(This ersatz `core dump' gives a way of analyzing application
	crashes at remote sites).

gensym:
	Produce a unique generated symbol.

int:
	A `greatest integer' function that is compatible with either
	Extended Tcl or Tcl 7.0.

msort:
	A sort with user key comparison.  This procedure is largely
	obsoleted by the new version of `lsort' in Tcl 7.0; the
	`merge' procedure may still be useful.

nop:
	A `no-operation' procedure.  The commonest need for this is to
	configure a `scale' widget -- the `-command' option on scales
	is executed even if the command is the null string.

require:
	A procedure that forces another procedure to be autoloaded
	without the risk of invoking the `unknown' procedure
	recursively.

set?:
	Set a variable if the new value is different from the old one.
	This procedure avoids triggering traces unnecessarily -- some
	trace procedures can be expensive, if tracing is used for
	constraint propagation.

static:
	Define a static variable within a Tcl procedure (This
	procedure appears courtest of Karl Lehenbauer).

trace_action:
	Import a traced variable into the trace handler, and avoid
	`access denied by trace command' errors in Tcl 6.7 and earlier
	releases.

widget_waitVariable:
	Perform the same action as `tkwait variable' but avoid hanging
	the process if the application is destroyed before the
	variable is set.  (Note that this results in returning control
	into the destroyed application, and any Tk command will cause
	a core dump!)

widget_checkAndDestroy:
	Destroys a widget if and only if it still exists.  This
	procedure allows the <Destroy> handler on a child widget to
	destroy the parent widget as well.

winlabel:
	Return the symbolic name of a window for use in error
	messages.

EXPERIMENTAL PROCEDURES

There is an experimental set of procedures, accessed via
`widget_define', that allow the user to define composite widgets that
function as first-class widgets in that they accept widget commands,
have their own configuration flags, and do on.  These procedures are
currently far too inefficient to be useful, owing to the way that they
propagate configuration around the widget tree.  Improving these
procedures is likely to be my next general-purpose Tk project.

BUGS

Entry widgets do not handle double or triple mouse clicks.

Listboxes do not yet support keyboard bindings.

There is no way to configure the file selection dialog to select a
directory instead of a file.

There is no way to control whether the file selection dialog exports
the selection.

The file selection dialog needs a `Rescan' function to rescan the
current directory.

The `winlabel' procedure needs to be generalized.

The `fraction' and `progress' widgets can be made much more efficient
by batching updates.

Eventually, all composite widgets should use `widget_define' to allow
user reconfiguration.

