Index: trunk/kitgen/8.x/blt/Makefile.in
===================================================================
--- trunk/kitgen/8.x/blt/Makefile.in	(revision 175)
+++ trunk/kitgen/8.x/blt/Makefile.in	(revision 175)
@@ -0,0 +1,445 @@
+# Makefile.in --
+#
+#	This file is a Makefile for Sample TEA Extension.  If it has the name
+#	"Makefile.in" then it is a template for a Makefile;  to generate the
+#	actual Makefile, run "./configure", which is a configuration script
+#	generated by the "autoconf" program (constructs like "@foo@" will get
+#	replaced in the actual Makefile.
+#
+# Copyright (c) 1999 Scriptics Corporation.
+# Copyright (c) 2002-2005 ActiveState Corporation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: Makefile.in,v 1.70 2010/09/14 23:22:36 hobbs Exp $
+
+#========================================================================
+# Add additional lines to handle any additional AC_SUBST cases that
+# have been added in a customized configure script.
+#========================================================================
+
+#SAMPLE_NEW_VAR	= @SAMPLE_NEW_VAR@
+
+#========================================================================
+# Nothing of the variables below this line should need to be changed.
+# Please check the TARGETS section below to make sure the make targets
+# are correct.
+#========================================================================
+
+#========================================================================
+# The names of the source files is defined in the configure script.
+# The object files are used for linking into the final library.
+# This will be used when a dist target is added to the Makefile.
+# It is not important to specify the directory, as long as it is the
+# $(srcdir) or in the generic, win or unix subdirectory.
+#========================================================================
+
+PKG_SOURCES	= @PKG_SOURCES@
+PKG_OBJECTS	= @PKG_OBJECTS@
+
+PKG_STUB_SOURCES = @PKG_STUB_SOURCES@
+PKG_STUB_OBJECTS = @PKG_STUB_OBJECTS@
+
+#========================================================================
+# PKG_TCL_SOURCES identifies Tcl runtime files that are associated with
+# this package that need to be installed, if any.
+#========================================================================
+
+PKG_TCL_SOURCES = @PKG_TCL_SOURCES@
+
+#========================================================================
+# This is a list of public header files to be installed, if any.
+#========================================================================
+
+PKG_HEADERS	= @PKG_HEADERS@
+
+#========================================================================
+# "PKG_LIB_FILE" refers to the library (dynamic or static as per
+# configuration options) composed of the named objects.
+#========================================================================
+
+PKG_LIB_FILE	= @PKG_LIB_FILE@
+PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@
+
+lib_BINARIES	= $(PKG_LIB_FILE)
+BINARIES	= $(lib_BINARIES)
+
+SHELL		= @SHELL@
+
+srcdir		= @srcdir@
+prefix		= @prefix@
+exec_prefix	= @exec_prefix@
+
+bindir		= @bindir@
+libdir		= @libdir@
+includedir	= @includedir@
+datarootdir     = @datarootdir@
+datadir		= @datadir@
+mandir		= @mandir@
+
+DESTDIR		=
+
+PKG_DIR		= $(PACKAGE_NAME)$(PACKAGE_VERSION)
+pkgdatadir	= $(datadir)/$(PKG_DIR)
+pkglibdir	= $(libdir)/$(PKG_DIR)
+pkgincludedir	= $(includedir)/$(PKG_DIR)
+
+top_builddir	= .
+
+INSTALL		= @INSTALL@
+INSTALL_PROGRAM	= @INSTALL_PROGRAM@
+INSTALL_LIBRARY	= @INSTALL_PROGRAM@
+INSTALL_DATA	= @INSTALL_DATA@
+INSTALL_SCRIPT	= @INSTALL_SCRIPT@
+
+PACKAGE_NAME	= @PACKAGE_NAME@
+PACKAGE_VERSION	= @PACKAGE_VERSION@
+CC		= @CC@
+CFLAGS_DEFAULT	= @CFLAGS_DEFAULT@
+CFLAGS_WARNING	= @CFLAGS_WARNING@
+EXEEXT		= @EXEEXT@
+LDFLAGS_DEFAULT	= @LDFLAGS_DEFAULT@
+MAKE_LIB	= @MAKE_LIB@
+MAKE_SHARED_LIB	= @MAKE_SHARED_LIB@
+MAKE_STATIC_LIB	= @MAKE_STATIC_LIB@
+MAKE_STUB_LIB	= @MAKE_STUB_LIB@
+OBJEXT		= @OBJEXT@
+RANLIB		= @RANLIB@
+RANLIB_STUB	= @RANLIB_STUB@
+SHLIB_CFLAGS	= @SHLIB_CFLAGS@
+SHLIB_LD	= @SHLIB_LD@
+SHLIB_LD_LIBS	= @SHLIB_LD_LIBS@
+STLIB_LD	= @STLIB_LD@
+#TCL_DEFS	= @TCL_DEFS@
+TCL_BIN_DIR	= @TCL_BIN_DIR@
+TCL_SRC_DIR	= @TCL_SRC_DIR@
+#TK_BIN_DIR	= @TK_BIN_DIR@
+#TK_SRC_DIR	= @TK_SRC_DIR@
+
+# Not used, but retained for reference of what libs Tcl required
+#TCL_LIBS	= @TCL_LIBS@
+
+#========================================================================
+# TCLLIBPATH seeds the auto_path in Tcl's init.tcl so we can test our
+# package without installing.  The other environment variables allow us
+# to test against an uninstalled Tcl.  Add special env vars that you
+# require for testing here (like TCLX_LIBRARY).
+#========================================================================
+
+EXTRA_PATH	= $(top_builddir):$(TCL_BIN_DIR)
+#EXTRA_PATH	= $(top_builddir):$(TCL_BIN_DIR):$(TK_BIN_DIR)
+TCLLIBPATH	= $(top_builddir)
+TCLSH_ENV	= TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library`
+PKG_ENV		= @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \
+		  PATH="$(EXTRA_PATH):$(PATH)" \
+		  TCLLIBPATH="$(TCLLIBPATH)"
+
+TCLSH_PROG	= @TCLSH_PROG@
+TCLSH   	= $(PKG_ENV) $(TCLSH_ENV) $(TCLSH_PROG)
+
+#WISH_ENV	= TK_LIBRARY=`@CYGPATH@ $(TK_SRC_DIR)/library`
+#WISH_PROG	= @WISH_PROG@
+#WISH   	= $(PKG_ENV) $(TCLSH_ENV) $(WISH_ENV) $(WISH_PROG)
+
+SHARED_BUILD	= @SHARED_BUILD@
+
+INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@
+#INCLUDES	= @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@
+
+PKG_CFLAGS	= @PKG_CFLAGS@
+
+# TCL_DEFS is not strictly need here, but if you remove it, then you
+# must make sure that configure.in checks for the necessary components
+# that your library may use.  TCL_DEFS can actually be a problem if
+# you do not compile with a similar machine setup as the Tcl core was
+# compiled with.
+#DEFS		= $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS)
+DEFS		= @DEFS@ $(PKG_CFLAGS)
+
+# Move pkgIndex.tcl to 'BINARIES' var if it is generated in the Makefile
+CONFIG_CLEAN_FILES = Makefile pkgIndex.tcl
+CLEANFILES	= @CLEANFILES@
+
+CPPFLAGS	= @CPPFLAGS@
+LIBS		= @PKG_LIBS@ @LIBS@
+AR		= @AR@
+CFLAGS		= @CFLAGS@
+COMPILE		= $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+#========================================================================
+# Start of user-definable TARGETS section
+#========================================================================
+
+#========================================================================
+# TEA TARGETS.  Please note that the "libraries:" target refers to platform
+# independent files, and the "binaries:" target inclues executable programs and
+# platform-dependent libraries.  Modify these targets so that they install
+# the various pieces of your package.  The make and install rules
+# for the BINARIES that you specified above have already been done.
+#========================================================================
+
+all: binaries libraries doc
+
+#========================================================================
+# The binaries target builds executable programs, Windows .dll's, unix
+# shared/static libraries, and any other platform-dependent files.
+# The list of targets to build for "binaries:" is specified at the top
+# of the Makefile, in the "BINARIES" variable.
+#========================================================================
+
+binaries: $(BINARIES)
+
+libraries:
+
+#========================================================================
+# Your doc target should differentiate from doc builds (by the developer)
+# and doc installs (see install-doc), which just install the docs on the
+# end user machine when building from source.
+#========================================================================
+
+doc:
+	@echo "If you have documentation to create, place the commands to"
+	@echo "build the docs in the 'doc:' target.  For example:"
+	@echo "        xml2nroff sample.xml > sample.n"
+	@echo "        xml2html sample.xml > sample.html"
+
+install: all install-binaries install-libraries install-doc
+
+install-binaries: binaries install-lib-binaries install-bin-binaries
+
+#========================================================================
+# This rule installs platform-independent files, such as header files.
+# The list=...; for p in $$list handles the empty list case x-platform.
+#========================================================================
+
+install-libraries: libraries
+	@mkdir -p $(DESTDIR)$(includedir)
+	@echo "Installing header files in $(DESTDIR)$(includedir)"
+	@list='$(PKG_HEADERS)'; for i in $$list; do \
+	    echo "Installing $(srcdir)/$$i" ; \
+	    $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \
+	done;
+
+#========================================================================
+# Install documentation.  Unix manpages should go in the $(mandir)
+# directory.
+#========================================================================
+
+install-doc: doc
+	@mkdir -p $(DESTDIR)$(mandir)/mann
+	@echo "Installing documentation in $(DESTDIR)$(mandir)"
+	@list='$(srcdir)/doc/*.n'; for i in $$list; do \
+	    echo "Installing $$i"; \
+	    $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \
+	done
+
+test: binaries libraries
+	$(TCLSH) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS)
+
+shell: binaries libraries
+	@$(TCLSH) $(SCRIPT)
+
+gdb:
+	$(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT)
+
+VALGRINDARGS=--tool=memcheck --num-callers=8 --leak-resolution=high --leak-check=yes --show-reachable=yes -v
+
+valgrind: binaries libraries
+	$(TCLSH_ENV) valgrind $(VALGRINDARGS) $(TCLSH_PROG) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS)
+
+valgrindshell: binaries libraries
+	$(TCLSH_ENV) valgrind $(VALGRINDARGS) $(TCLSH_PROG) $(SCRIPT)
+
+depend:
+
+#========================================================================
+# $(PKG_LIB_FILE) should be listed as part of the BINARIES variable
+# mentioned above.  That will ensure that this target is built when you
+# run "make binaries".
+#
+# The $(PKG_OBJECTS) objects are created and linked into the final
+# library.  In most cases these object files will correspond to the
+# source files above.
+#========================================================================
+
+$(PKG_LIB_FILE): $(PKG_OBJECTS)
+	-rm -f $(PKG_LIB_FILE)
+	${MAKE_LIB}
+	$(RANLIB) $(PKG_LIB_FILE)
+
+$(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS)
+	-rm -f $(PKG_STUB_LIB_FILE)
+	${MAKE_STUB_LIB}
+	$(RANLIB_STUB) $(PKG_STUB_LIB_FILE)
+
+#========================================================================
+# We need to enumerate the list of .c to .o lines here.
+#
+# In the following lines, $(srcdir) refers to the toplevel directory
+# containing your extension.  If your sources are in a subdirectory,
+# you will have to modify the paths to reflect this:
+#
+# sample.$(OBJEXT): $(srcdir)/generic/sample.c
+# 	$(COMPILE) -c `@CYGPATH@ $(srcdir)/generic/sample.c` -o $@
+#
+# Setting the VPATH variable to a list of paths will cause the makefile
+# to look into these paths when resolving .c to .obj dependencies.
+# As necessary, add $(srcdir):$(srcdir)/compat:....
+#========================================================================
+
+VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win:$(srcdir)/macosx
+
+.c.@OBJEXT@:
+	$(COMPILE) -c `@CYGPATH@ $<` -o $@
+
+#========================================================================
+# Distribution creation
+# You may need to tweak this target to make it work correctly.
+#========================================================================
+
+#COMPRESS	= tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar
+COMPRESS	= gtar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
+DIST_ROOT	= /tmp/dist
+DIST_DIR	= $(DIST_ROOT)/$(PKG_DIR)
+
+dist-clean:
+	rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.*
+
+dist: dist-clean
+	mkdir -p $(DIST_DIR)
+	cp -p $(srcdir)/ChangeLog $(srcdir)/README* $(srcdir)/license* \
+		$(srcdir)/aclocal.m4 $(srcdir)/configure $(srcdir)/*.in \
+		$(DIST_DIR)/
+	chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4
+	chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.in
+
+	for i in $(srcdir)/*.[ch]; do \
+	    if [ -f $$i ]; then \
+		cp -p $$i $(DIST_DIR)/ ; \
+	    fi; \
+	done;
+
+	mkdir $(DIST_DIR)/tclconfig
+	cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \
+		$(DIST_DIR)/tclconfig/
+	chmod 664 $(DIST_DIR)/tclconfig/tcl.m4
+	chmod +x $(DIST_DIR)/tclconfig/install-sh
+
+	list='demos doc generic library mac tests unix win'; \
+	for p in $$list; do \
+	    if test -d $(srcdir)/$$p ; then \
+		mkdir $(DIST_DIR)/$$p; \
+		cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \
+	    fi; \
+	done
+
+	(cd $(DIST_ROOT); $(COMPRESS);)
+
+#========================================================================
+# End of user-definable section
+#========================================================================
+
+#========================================================================
+# Don't modify the file to clean here.  Instead, set the "CLEANFILES"
+# variable in configure.in
+#========================================================================
+
+clean:  
+	-test -z "$(BINARIES)" || rm -f $(BINARIES)
+	-rm -f *.$(OBJEXT) core *.core
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean: clean
+	-rm -f *.tab.c
+	-rm -f $(CONFIG_CLEAN_FILES)
+	-rm -f config.cache config.log config.status
+
+#========================================================================
+# Install binary object libraries.  On Windows this includes both .dll and
+# .lib files.  Because the .lib files are not explicitly listed anywhere,
+# we need to deduce their existence from the .dll file of the same name.
+# Library files go into the lib directory.
+# In addition, this will generate the pkgIndex.tcl
+# file in the install location (assuming it can find a usable tclsh shell)
+#
+# You should not have to modify this target.
+#========================================================================
+
+install-lib-binaries: binaries
+	@mkdir -p $(DESTDIR)$(pkglibdir)
+	@list='$(lib_BINARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo " $(INSTALL_LIBRARY) $$p $(DESTDIR)$(pkglibdir)/$$p"; \
+	    $(INSTALL_LIBRARY) $$p $(DESTDIR)$(pkglibdir)/$$p; \
+	    stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \
+	    if test "x$$stub" = "xstub"; then \
+		echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \
+		$(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \
+	    else \
+		echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \
+		$(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \
+	    fi; \
+	    ext=`echo $$p|sed -e "s/.*\.//"`; \
+	    if test "x$$ext" = "xdll"; then \
+		lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \
+		if test -f $$lib; then \
+		    echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \
+	            $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \
+		fi; \
+	    fi; \
+	  fi; \
+	done
+	@list='$(PKG_TCL_SOURCES)'; for p in $$list; do \
+	  if test -f $(srcdir)/$$p; then \
+	    destp=`basename $$p`; \
+	    echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \
+	    $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \
+	  fi; \
+	done
+	@if test "x$(SHARED_BUILD)" = "x1"; then \
+	    echo " Install pkgIndex.tcl $(DESTDIR)$(pkglibdir)"; \
+	    $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \
+	fi
+
+#========================================================================
+# Install binary executables (e.g. .exe files and dependent .dll files)
+# This is for files that must go in the bin directory (located next to
+# wish and tclsh), like dependent .dll files on Windows.
+#
+# You should not have to modify this target, except to define bin_BINARIES
+# above if necessary.
+#========================================================================
+
+install-bin-binaries: binaries
+	@mkdir -p $(DESTDIR)$(bindir)
+	@list='$(bin_BINARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \
+	    $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \
+	  fi; \
+	done
+
+.SUFFIXES: .c .$(OBJEXT)
+
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) \
+	  && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+uninstall-binaries:
+	list='$(lib_BINARIES)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+	done
+	list='$(PKG_TCL_SOURCES)'; for p in $$list; do \
+	  p=`basename $$p`; \
+	  rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+	done
+	list='$(bin_BINARIES)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(bindir)/$$p; \
+	done
+
+.PHONY: all binaries clean depend distclean doc install libraries test
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: trunk/kitgen/8.x/blt/aclocal.m4
===================================================================
--- trunk/kitgen/8.x/blt/aclocal.m4	(revision 175)
+++ trunk/kitgen/8.x/blt/aclocal.m4	(revision 175)
@@ -0,0 +1,9 @@
+#
+# Include the TEA standard macro set
+#
+
+builtin(include,tclconfig/tcl.m4)
+
+#
+# Add here whatever m4 macros you want to define for your package
+#
Index: trunk/kitgen/8.x/blt/configure
===================================================================
--- trunk/kitgen/8.x/blt/configure	(revision 175)
+++ trunk/kitgen/8.x/blt/configure	(revision 175)
@@ -0,0 +1,8970 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.65 for BLT 2.4.
+#
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  # We cannot yet assume a decent shell, so we have to provide a
+	# neutralization value for shells without unset; and this also
+	# works around shells that cannot unset nonexistent variables.
+	BASH_ENV=/dev/null
+	ENV=/dev/null
+	(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+	export CONFIG_SHELL
+	exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error ERROR [LINENO LOG_FD]
+# ---------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with status $?, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$?; test $as_status -eq 0 && as_status=1
+  if test "$3"; then
+    as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+  fi
+  $as_echo "$as_me: error: $1" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+	test -d "$1/.";
+      else
+	case $1 in #(
+	-*)set "./$1";;
+	esac;
+	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+	???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='BLT'
+PACKAGE_TARNAME='blt'
+PACKAGE_VERSION='2.4'
+PACKAGE_STRING='BLT 2.4'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+TCLSH_PROG
+VC_MANIFEST_EMBED_EXE
+VC_MANIFEST_EMBED_DLL
+RANLIB_STUB
+MAKE_STUB_LIB
+MAKE_STATIC_LIB
+MAKE_SHARED_LIB
+MAKE_LIB
+TCL_DBGX
+LDFLAGS_DEFAULT
+CFLAGS_DEFAULT
+LD_LIBRARY_PATH_VAR
+SHLIB_CFLAGS
+SHLIB_LD_LIBS
+SHLIB_LD
+STLIB_LD
+CFLAGS_WARNING
+CFLAGS_OPTIMIZE
+CFLAGS_DEBUG
+CELIB_DIR
+AR
+SHARED_BUILD
+TCL_THREADS
+TCL_INCLUDES
+PKG_OBJECTS
+PKG_SOURCES
+MATH_LIBS
+EGREP
+GREP
+RANLIB
+SET_MAKE
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+CPP
+OBJEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+TCL_SHLIB_LD_LIBS
+TCL_LD_FLAGS
+TCL_EXTRA_CFLAGS
+TCL_DEFS
+TCL_LIBS
+CLEANFILES
+TCL_STUB_LIB_SPEC
+TCL_STUB_LIB_FLAG
+TCL_STUB_LIB_FILE
+TCL_LIB_SPEC
+TCL_LIB_FLAG
+TCL_LIB_FILE
+TCL_SRC_DIR
+TCL_BIN_DIR
+TCL_PATCH_LEVEL
+TCL_VERSION
+PKG_CFLAGS
+PKG_LIBS
+PKG_INCLUDES
+PKG_HEADERS
+PKG_TCL_SOURCES
+PKG_STUB_OBJECTS
+PKG_STUB_SOURCES
+PKG_STUB_LIB_FILE
+PKG_LIB_FILE
+EXEEXT
+CYGPATH
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_tcl
+with_tclinclude
+enable_threads
+enable_shared
+enable_64bit
+enable_64bit_vis
+enable_rpath
+enable_wince
+with_celib
+enable_symbols
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=*)	ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *)	ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information."
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures BLT 2.4 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/blt]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of BLT 2.4:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-threads        build with threads
+  --enable-shared         build and link with shared libraries (default: on)
+  --enable-64bit          enable 64bit support (default: off)
+  --enable-64bit-vis      enable 64bit Sparc VIS support (default: off)
+  --disable-rpath         disable rpath support (default: on)
+  --enable-wince          enable Win/CE support (where applicable)
+  --enable-symbols        build with debugging symbols (default: off)
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-tcl              directory containing tcl configuration
+                          (tclConfig.sh)
+  --with-tclinclude       directory containing the public Tcl header files
+  --with-celib=DIR        use Windows/CE support library from DIR
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+BLT configure 2.4
+generated by GNU Autoconf 2.65
+
+Copyright (C) 2009 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } >/dev/null && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 $as_test_x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_header_mongrel
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by BLT $as_me 2.4, which was
+generated by GNU Autoconf 2.65.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+#--------------------------------------------------------------------
+# Call TEA_INIT as the first TEA_ macro to set up initial vars.
+# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
+# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
+# --------------------------------------------------------------------
+
+
+    # TEA extensions pass this us the version of TEA they think they
+    # are compatible with.
+    TEA_VERSION="3.9"
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for correct TEA configuration" >&5
+$as_echo_n "checking for correct TEA configuration... " >&6; }
+    if test x"${PACKAGE_NAME}" = x ; then
+	as_fn_error "
+The PACKAGE_NAME variable must be defined by your TEA configure.in" "$LINENO" 5
+    fi
+    if test x"3.9" = x ; then
+	as_fn_error "
+TEA version not specified." "$LINENO" 5
+    elif test "3.9" != "${TEA_VERSION}" ; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&5
+$as_echo "warning: requested TEA version \"3.9\", have \"${TEA_VERSION}\"" >&6; }
+    else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5
+$as_echo "ok (TEA ${TEA_VERSION})" >&6; }
+    fi
+    case "`uname -s`" in
+	*win32*|*WIN32*|*MINGW32_*)
+	    # Extract the first word of "cygpath", so it can be a program name with args.
+set dummy cygpath; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CYGPATH+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CYGPATH"; then
+  ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CYGPATH="cygpath -w"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo"
+fi
+fi
+CYGPATH=$ac_cv_prog_CYGPATH
+if test -n "$CYGPATH"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYGPATH" >&5
+$as_echo "$CYGPATH" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+	    EXEEXT=".exe"
+	    TEA_PLATFORM="windows"
+	    ;;
+	*CYGWIN_*)
+	    CYGPATH=echo
+	    EXEEXT=".exe"
+	    # TEA_PLATFORM is determined later in LOAD_TCLCONFIG
+	    ;;
+	*)
+	    CYGPATH=echo
+	    # Maybe we are cross-compiling....
+	    case ${host_alias} in
+		*mingw32*)
+		EXEEXT=".exe"
+		TEA_PLATFORM="windows"
+		;;
+	    *)
+		EXEEXT=""
+		TEA_PLATFORM="unix"
+		;;
+	    esac
+	    ;;
+    esac
+
+    # Check if exec_prefix is set. If not use fall back to prefix.
+    # Note when adjusted, so that TEA_PREFIX can correct for this.
+    # This is needed for recursive configures, since autoconf propagates
+    # $prefix, but not $exec_prefix (doh!).
+    if test x$exec_prefix = xNONE ; then
+	exec_prefix_default=yes
+	exec_prefix=$prefix
+    fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&5
+$as_echo "$as_me: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&6;}
+
+
+
+
+    # This package name must be replaced statically for AC_SUBST to work
+
+    # Substitute STUB_LIB_FILE in case package creates a stub library too.
+
+
+    # We AC_SUBST these here to ensure they are subst'ed,
+    # in case the user doesn't call TEA_ADD_...
+
+
+
+
+
+
+
+
+
+ac_aux_dir=
+for ac_dir in tclconfig "$srcdir"/tclconfig; do
+  for ac_t in install-sh install.sh shtool; do
+    if test -f "$ac_dir/$ac_t"; then
+      ac_aux_dir=$ac_dir
+      ac_install_sh="$ac_aux_dir/$ac_t -c"
+      break 2
+    fi
+  done
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error "cannot find install-sh, install.sh, or shtool in tclconfig \"$srcdir\"/tclconfig" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+
+#--------------------------------------------------------------------
+# Load the tclConfig.sh file
+#--------------------------------------------------------------------
+
+
+
+    #
+    # Ok, lets find the tcl configuration
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-tcl
+    #
+
+    if test x"${no_tcl}" = x ; then
+	# we reset no_tcl in case something fails here
+	no_tcl=true
+
+# Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+  withval=$with_tcl; with_tclconfig="${withval}"
+fi
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5
+$as_echo_n "checking for Tcl configuration... " >&6; }
+	if test "${ac_cv_c_tclconfig+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+
+	    # First check to see if --with-tcl was specified.
+	    if test x"${with_tclconfig}" != x ; then
+		case "${with_tclconfig}" in
+		    */tclConfig.sh )
+			if test -f "${with_tclconfig}"; then
+			    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&5
+$as_echo "$as_me: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&2;}
+			    with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`"
+			fi ;;
+		esac
+		if test -f "${with_tclconfig}/tclConfig.sh" ; then
+		    ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`"
+		else
+		    as_fn_error "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5
+		fi
+	    fi
+
+	    # then check for a private Tcl installation
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in \
+			../tcl \
+			`ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+			`ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \
+			`ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \
+			../../tcl \
+			`ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+			`ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \
+			`ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \
+			../../../tcl \
+			`ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+			`ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \
+			`ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # on Darwin, check in Framework installation locations
+	    if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+			`ls -d /Library/Frameworks 2>/dev/null` \
+			`ls -d /Network/Library/Frameworks 2>/dev/null` \
+			`ls -d /System/Library/Frameworks 2>/dev/null` \
+			; do
+		    if test -f "$i/Tcl.framework/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # TEA specific: on Windows, check in common installation locations
+	    if test "${TEA_PLATFORM}" = "windows" \
+		-a x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+			`ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+			; do
+		    if test -f "$i/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few common install locations
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d ${libdir} 2>/dev/null` \
+			`ls -d ${exec_prefix}/lib 2>/dev/null` \
+			`ls -d ${prefix}/lib 2>/dev/null` \
+			`ls -d /usr/local/lib 2>/dev/null` \
+			`ls -d /usr/contrib/lib 2>/dev/null` \
+			`ls -d /usr/lib 2>/dev/null` \
+			`ls -d /usr/lib64 2>/dev/null` \
+			; do
+		    if test -f "$i/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few other private locations
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in \
+			${srcdir}/../tcl \
+			`ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
+			`ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \
+			`ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+fi
+
+
+	if test x"${ac_cv_c_tclconfig}" = x ; then
+	    TCL_BIN_DIR="# no Tcl configs found"
+	    as_fn_error "Can't find Tcl configuration definitions" "$LINENO" 5
+	else
+	    no_tcl=
+	    TCL_BIN_DIR="${ac_cv_c_tclconfig}"
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${TCL_BIN_DIR}/tclConfig.sh" >&5
+$as_echo "found ${TCL_BIN_DIR}/tclConfig.sh" >&6; }
+	fi
+    fi
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for existence of ${TCL_BIN_DIR}/tclConfig.sh" >&5
+$as_echo_n "checking for existence of ${TCL_BIN_DIR}/tclConfig.sh... " >&6; }
+
+    if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: loading" >&5
+$as_echo "loading" >&6; }
+	. "${TCL_BIN_DIR}/tclConfig.sh"
+    else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: could not find ${TCL_BIN_DIR}/tclConfig.sh" >&5
+$as_echo "could not find ${TCL_BIN_DIR}/tclConfig.sh" >&6; }
+    fi
+
+    # eval is required to do the TCL_DBGX substitution
+    eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+    eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+
+    # If the TCL_BIN_DIR is the build directory (not the install directory),
+    # then set the common variable name to the value of the build variables.
+    # For example, the variable TCL_LIB_SPEC will be set to the value
+    # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
+    # instead of TCL_BUILD_LIB_SPEC since it will work with both an
+    # installed and uninstalled version of Tcl.
+    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+        TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}"
+        TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}"
+        TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}"
+    elif test "`uname -s`" = "Darwin"; then
+	# If Tcl was built as a framework, attempt to use the libraries
+	# from the framework at the given location so that linking works
+	# against Tcl.framework installed in an arbitrary location.
+	case ${TCL_DEFS} in
+	    *TCL_FRAMEWORK*)
+		if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then
+		    for i in "`cd "${TCL_BIN_DIR}"; pwd`" \
+			     "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do
+			if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then
+			    TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}"
+			    break
+			fi
+		    done
+		fi
+		if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then
+		    TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}"  | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}"
+		    TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"
+		fi
+		;;
+	esac
+    fi
+
+    # eval is required to do the TCL_DBGX substitution
+    eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+    eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+    eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
+    eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    case "`uname -s`" in
+	*CYGWIN_*)
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cygwin variant" >&5
+$as_echo_n "checking for cygwin variant... " >&6; }
+	    case ${TCL_EXTRA_CFLAGS} in
+		*-mwin32*|*-mno-cygwin*)
+		    TEA_PLATFORM="windows"
+		    CFLAGS="$CFLAGS -mwin32"
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: result: win32" >&5
+$as_echo "win32" >&6; }
+		    ;;
+		*)
+		    TEA_PLATFORM="unix"
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unix" >&5
+$as_echo "unix" >&6; }
+		    ;;
+	    esac
+	    EXEEXT=".exe"
+	    ;;
+	*)
+	    ;;
+    esac
+
+    # The BUILD_$pkg is to define the correct extern storage class
+    # handling when making this package
+
+cat >>confdefs.h <<_ACEOF
+#define BUILD_${PACKAGE_NAME} /**/
+_ACEOF
+
+    # Do this here as we have fully defined TEA_PLATFORM now
+    if test "${TEA_PLATFORM}" = "windows" ; then
+	CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp"
+    fi
+
+    # TEA specific:
+
+
+
+
+
+
+
+
+#--------------------------------------------------------------------
+# Load the tkConfig.sh file if necessary (Tk extension)
+#--------------------------------------------------------------------
+
+#TEA_PATH_TKCONFIG
+#TEA_LOAD_TKCONFIG
+
+#-----------------------------------------------------------------------
+# Handle the --prefix=... option by defaulting to what Tcl gave.
+# Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER.
+#-----------------------------------------------------------------------
+
+
+    if test "${prefix}" = "NONE"; then
+	prefix_default=yes
+	if test x"${TCL_PREFIX}" != x; then
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&5
+$as_echo "$as_me: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&6;}
+	    prefix=${TCL_PREFIX}
+	else
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: --prefix defaulting to /usr/local" >&5
+$as_echo "$as_me: --prefix defaulting to /usr/local" >&6;}
+	    prefix=/usr/local
+	fi
+    fi
+    if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \
+	-o x"${exec_prefix_default}" = x"yes" ; then
+	if test x"${TCL_EXEC_PREFIX}" != x; then
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&5
+$as_echo "$as_me: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&6;}
+	    exec_prefix=${TCL_EXEC_PREFIX}
+	else
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: --exec-prefix defaulting to ${prefix}" >&5
+$as_echo "$as_me: --exec-prefix defaulting to ${prefix}" >&6;}
+	    exec_prefix=$prefix
+	fi
+    fi
+
+
+#-----------------------------------------------------------------------
+# Standard compiler checks.
+# This sets up CC by using the CC env var, or looks for gcc otherwise.
+# This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create
+# the basic setup necessary to compile executables.
+#-----------------------------------------------------------------------
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+    # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE)
+    # in this macro, they need to go into TEA_SETUP_COMPILER instead.
+
+    # If the user did not set CFLAGS, set it now to keep
+    # the AC_PROG_CC macro from adding "-g -O2".
+    if test "${CFLAGS+set}" != "set" ; then
+	CFLAGS=""
+    fi
+
+    ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "no acceptable C compiler found in \$PATH
+See \`config.log' for more details." "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "C compiler cannot create executables
+See \`config.log' for more details." "$LINENO" 5; }; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+    ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+    #--------------------------------------------------------------------
+    # Checks to see if the make program sets the $MAKE variable.
+    #--------------------------------------------------------------------
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+    #--------------------------------------------------------------------
+    # Find ranlib
+    #--------------------------------------------------------------------
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+    #--------------------------------------------------------------------
+    # Determines the correct binary file extension (.o, .obj, .exe etc.)
+    #--------------------------------------------------------------------
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+eval as_val=\$$as_ac_Header
+   if test "x$as_val" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+    # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here.
+
+
+    #------------------------------------------------------------------------
+    # If we're using GCC, see if the compiler understands -pipe. If so, use it.
+    # It makes compiling go faster.  (This is only a performance feature.)
+    #------------------------------------------------------------------------
+
+    if test -z "$no_pipe" -a -n "$GCC"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler understands -pipe" >&5
+$as_echo_n "checking if the compiler understands -pipe... " >&6; }
+if test "${tcl_cv_cc_pipe+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	    hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe"
+	    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_cc_pipe=yes
+else
+  tcl_cv_cc_pipe=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	    CFLAGS=$hold_cflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_pipe" >&5
+$as_echo "$tcl_cv_cc_pipe" >&6; }
+	if test $tcl_cv_cc_pipe = yes; then
+	    CFLAGS="$CFLAGS -pipe"
+	fi
+    fi
+
+    #--------------------------------------------------------------------
+    # Common compiler flag setup
+    #--------------------------------------------------------------------
+
+     { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if test "${ac_cv_c_bigendian+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+	       not a universal capable compiler
+	     #endif
+	     typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+	# Check for potential -arch flags.  It is not universal unless
+	# there are at least two -arch flags with different values.
+	ac_arch=
+	ac_prev=
+	for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+	 if test -n "$ac_prev"; then
+	   case $ac_word in
+	     i?86 | x86_64 | ppc | ppc64)
+	       if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+		 ac_arch=$ac_word
+	       else
+		 ac_cv_c_bigendian=universal
+		 break
+	       fi
+	       ;;
+	   esac
+	   ac_prev=
+	 elif test "x$ac_word" = "x-arch"; then
+	   ac_prev=arch
+	 fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+	     #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+		     && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+		     && LITTLE_ENDIAN)
+	      bogus endian macros
+	     #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+		#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+		 not big endian
+		#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+	      bogus endian macros
+	     #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+		 not big endian
+		#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+short int ascii_mm[] =
+		  { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+		short int ascii_ii[] =
+		  { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+		int use_ascii (int i) {
+		  return ascii_mm[i] + ascii_ii[i];
+		}
+		short int ebcdic_ii[] =
+		  { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+		short int ebcdic_mm[] =
+		  { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+		int use_ebcdic (int i) {
+		  return ebcdic_mm[i] + ebcdic_ii[i];
+		}
+		extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+	      ac_cv_c_bigendian=yes
+	    fi
+	    if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+	      if test "$ac_cv_c_bigendian" = unknown; then
+		ac_cv_c_bigendian=no
+	      else
+		# finding both strings is unlikely to happen, but who knows?
+		ac_cv_c_bigendian=unknown
+	      fi
+	    fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+	     /* Are we little or big endian?  From Harbison&Steele.  */
+	     union
+	     {
+	       long int l;
+	       char c[sizeof (long int)];
+	     } u;
+	     u.l = 1;
+	     return u.c[sizeof (long int) - 1] == 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+    if test "${TEA_PLATFORM}" = "unix" ; then
+
+    #--------------------------------------------------------------------
+    # On a few very rare systems, all of the libm.a stuff is
+    # already in libc.a.  Set compiler flags accordingly.
+    # Also, Linux requires the "ieee" library for math to work
+    # right (and it must appear before "-lm").
+    #--------------------------------------------------------------------
+
+    ac_fn_c_check_func "$LINENO" "sin" "ac_cv_func_sin"
+if test "x$ac_cv_func_sin" = x""yes; then :
+  MATH_LIBS=""
+else
+  MATH_LIBS="-lm"
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lieee" >&5
+$as_echo_n "checking for main in -lieee... " >&6; }
+if test "${ac_cv_lib_ieee_main+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lieee  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+return main ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_ieee_main=yes
+else
+  ac_cv_lib_ieee_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee_main" >&5
+$as_echo "$ac_cv_lib_ieee_main" >&6; }
+if test "x$ac_cv_lib_ieee_main" = x""yes; then :
+  MATH_LIBS="-lieee $MATH_LIBS"
+fi
+
+
+    #--------------------------------------------------------------------
+    # Interactive UNIX requires -linet instead of -lsocket, plus it
+    # needs net/errno.h to define the socket-related error codes.
+    #--------------------------------------------------------------------
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -linet" >&5
+$as_echo_n "checking for main in -linet... " >&6; }
+if test "${ac_cv_lib_inet_main+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-linet  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+return main ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_inet_main=yes
+else
+  ac_cv_lib_inet_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_main" >&5
+$as_echo "$ac_cv_lib_inet_main" >&6; }
+if test "x$ac_cv_lib_inet_main" = x""yes; then :
+  LIBS="$LIBS -linet"
+fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "net/errno.h" "ac_cv_header_net_errno_h" "$ac_includes_default"
+if test "x$ac_cv_header_net_errno_h" = x""yes; then :
+
+
+$as_echo "#define HAVE_NET_ERRNO_H 1" >>confdefs.h
+
+fi
+
+
+
+    #--------------------------------------------------------------------
+    #	Check for the existence of the -lsocket and -lnsl libraries.
+    #	The order here is important, so that they end up in the right
+    #	order in the command line generated by make.  Here are some
+    #	special considerations:
+    #	1. Use "connect" and "accept" to check for -lsocket, and
+    #	   "gethostbyname" to check for -lnsl.
+    #	2. Use each function name only once:  can't redo a check because
+    #	   autoconf caches the results of the last check and won't redo it.
+    #	3. Use -lnsl and -lsocket only if they supply procedures that
+    #	   aren't already present in the normal libraries.  This is because
+    #	   IRIX 5.2 has libraries, but they aren't needed and they're
+    #	   bogus:  they goof up name resolution if used.
+    #	4. On some SVR4 systems, can't use -lsocket without -lnsl too.
+    #	   To get around this problem, check for both libraries together
+    #	   if -lsocket doesn't work by itself.
+    #--------------------------------------------------------------------
+
+    tcl_checkBoth=0
+    ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect"
+if test "x$ac_cv_func_connect" = x""yes; then :
+  tcl_checkSocket=0
+else
+  tcl_checkSocket=1
+fi
+
+    if test "$tcl_checkSocket" = 1; then
+	ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt"
+if test "x$ac_cv_func_setsockopt" = x""yes; then :
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5
+$as_echo_n "checking for setsockopt in -lsocket... " >&6; }
+if test "${ac_cv_lib_socket_setsockopt+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setsockopt ();
+int
+main ()
+{
+return setsockopt ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_socket_setsockopt=yes
+else
+  ac_cv_lib_socket_setsockopt=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5
+$as_echo "$ac_cv_lib_socket_setsockopt" >&6; }
+if test "x$ac_cv_lib_socket_setsockopt" = x""yes; then :
+  LIBS="$LIBS -lsocket"
+else
+  tcl_checkBoth=1
+fi
+
+fi
+
+    fi
+    if test "$tcl_checkBoth" = 1; then
+	tk_oldLibs=$LIBS
+	LIBS="$LIBS -lsocket -lnsl"
+	ac_fn_c_check_func "$LINENO" "accept" "ac_cv_func_accept"
+if test "x$ac_cv_func_accept" = x""yes; then :
+  tcl_checkNsl=0
+else
+  LIBS=$tk_oldLibs
+fi
+
+    fi
+    ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname"
+if test "x$ac_cv_func_gethostbyname" = x""yes; then :
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5
+$as_echo_n "checking for gethostbyname in -lnsl... " >&6; }
+if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname ();
+int
+main ()
+{
+return gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_nsl_gethostbyname=yes
+else
+  ac_cv_lib_nsl_gethostbyname=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5
+$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; }
+if test "x$ac_cv_lib_nsl_gethostbyname" = x""yes; then :
+  LIBS="$LIBS -lnsl"
+fi
+
+fi
+
+
+    # TEA specific: Don't perform the eval of the libraries here because
+    # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS
+
+    TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking dirent.h" >&5
+$as_echo_n "checking dirent.h... " >&6; }
+if test "${tcl_cv_dirent_h+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <dirent.h>
+int
+main ()
+{
+
+#ifndef _POSIX_SOURCE
+#   ifdef __Lynx__
+	/*
+	 * Generate compilation error to make the test fail:  Lynx headers
+	 * are only valid if really in the POSIX environment.
+	 */
+
+	missing_procedure();
+#   endif
+#endif
+DIR *d;
+struct dirent *entryPtr;
+char *p;
+d = opendir("foobar");
+entryPtr = readdir(d);
+p = entryPtr->d_name;
+closedir(d);
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_dirent_h=yes
+else
+  tcl_cv_dirent_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_dirent_h" >&5
+$as_echo "$tcl_cv_dirent_h" >&6; }
+
+    if test $tcl_cv_dirent_h = no; then
+
+$as_echo "#define NO_DIRENT_H 1" >>confdefs.h
+
+    fi
+
+    # TEA specific:
+    ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default"
+if test "x$ac_cv_header_errno_h" = x""yes; then :
+
+else
+
+$as_echo "#define NO_ERRNO_H 1" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_header_mongrel "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default"
+if test "x$ac_cv_header_float_h" = x""yes; then :
+
+else
+
+$as_echo "#define NO_FLOAT_H 1" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_header_mongrel "$LINENO" "values.h" "ac_cv_header_values_h" "$ac_includes_default"
+if test "x$ac_cv_header_values_h" = x""yes; then :
+
+else
+
+$as_echo "#define NO_VALUES_H 1" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = x""yes; then :
+
+$as_echo "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+else
+
+$as_echo "#define NO_LIMITS_H 1" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = x""yes; then :
+  tcl_ok=1
+else
+  tcl_ok=0
+fi
+
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "strtol" >/dev/null 2>&1; then :
+
+else
+  tcl_ok=0
+fi
+rm -f conftest*
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "strtoul" >/dev/null 2>&1; then :
+
+else
+  tcl_ok=0
+fi
+rm -f conftest*
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "strtod" >/dev/null 2>&1; then :
+
+else
+  tcl_ok=0
+fi
+rm -f conftest*
+
+    if test $tcl_ok = 0; then
+
+$as_echo "#define NO_STDLIB_H 1" >>confdefs.h
+
+    fi
+    ac_fn_c_check_header_mongrel "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default"
+if test "x$ac_cv_header_string_h" = x""yes; then :
+  tcl_ok=1
+else
+  tcl_ok=0
+fi
+
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "strstr" >/dev/null 2>&1; then :
+
+else
+  tcl_ok=0
+fi
+rm -f conftest*
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "strerror" >/dev/null 2>&1; then :
+
+else
+  tcl_ok=0
+fi
+rm -f conftest*
+
+
+    # See also memmove check below for a place where NO_STRING_H can be
+    # set and why.
+
+    if test $tcl_ok = 0; then
+
+$as_echo "#define NO_STRING_H 1" >>confdefs.h
+
+    fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_wait_h" = x""yes; then :
+
+else
+
+$as_echo "#define NO_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default"
+if test "x$ac_cv_header_dlfcn_h" = x""yes; then :
+
+else
+
+$as_echo "#define NO_DLFCN_H 1" >>confdefs.h
+
+fi
+
+
+
+    # OS/390 lacks sys/param.h (and doesn't need it, by chance).
+    for ac_header in sys/param.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_PARAM_H 1
+_ACEOF
+
+fi
+
+done
+
+
+	# Let the user call this, because if it triggers, they will
+	# need a compat/strtod.c that is correct.  Users can also
+	# use Tcl_GetDouble(FromObj) instead.
+	#TEA_BUGGY_STRTOD
+    fi
+
+
+#-----------------------------------------------------------------------
+# __CHANGE__
+# Specify the C source files to compile in TEA_ADD_SOURCES,
+# public headers that need to be installed in TEA_ADD_HEADERS,
+# stub library C source files to compile in TEA_ADD_STUB_SOURCES,
+# and runtime Tcl library files in TEA_ADD_TCL_SOURCES.
+# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
+# and PKG_TCL_SOURCES.
+#-----------------------------------------------------------------------
+
+
+    vars="bltAlloc.c bltArrayObj.c bltBind.c bltBitmap.c bltBusy.c bltChain.c bltColor.c bltConfig.c bltGrAxis.c bltGrBar.c bltGrElem.c bltGrGrid.c bltGrHairs.c bltGrLegd.c bltGrLine.c bltGrMarker.c bltGrMisc.c bltGrPen.c bltGrPs.c bltGraph.c bltHash.c bltImage.c bltInit.c bltList.c bltNsUtil.c bltParse.c bltPool.c bltPs.c bltSpline.c bltSwitch.c bltTable.c bltTabnotebook.c bltText.c bltTile.c bltObjConfig.c bltTree.h bltTreeView.h bltTreeViewEdit.c bltObjConfig.h bltTreeCmd.c bltTreeViewCmd.c bltTreeViewStyle.c bltTree.c bltTreeView.c bltTreeViewColumn.c bltUtil.c bltVecCmd.c bltVecObjCmd.c bltVector.c bltWindow.c"
+    for i in $vars; do
+	case $i in
+	    \$*)
+		# allow $-var names
+		PKG_SOURCES="$PKG_SOURCES $i"
+		PKG_OBJECTS="$PKG_OBJECTS $i"
+		;;
+	    *)
+		# check for existence - allows for generic/win/unix VPATH
+		# To add more dirs here (like 'src'), you have to update VPATH
+		# in Makefile.in as well
+		if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+		    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+		    -a ! -f "${srcdir}/macosx/$i" \
+		    ; then
+		    as_fn_error "could not find source file '$i'" "$LINENO" 5
+		fi
+		PKG_SOURCES="$PKG_SOURCES $i"
+		# this assumes it is in a VPATH dir
+		i=`basename $i`
+		# handle user calling this before or after TEA_SETUP_COMPILER
+		if test x"${OBJEXT}" != x ; then
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}"
+		else
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}"
+		fi
+		PKG_OBJECTS="$PKG_OBJECTS $j"
+		;;
+	esac
+    done
+
+
+
+
+    vars="generic/blt.h  generic/bltHash.h  generic/bltPool.h  generic/bltVector.h"
+    for i in $vars; do
+	# check for existence, be strict because it is installed
+	if test ! -f "${srcdir}/$i" ; then
+	    as_fn_error "could not find header file '${srcdir}/$i'" "$LINENO" 5
+	fi
+	PKG_HEADERS="$PKG_HEADERS $i"
+    done
+
+
+
+    vars="-I. -I\"$(${CYGPATH} ${srcdir}/generic)\""
+    for i in $vars; do
+	PKG_INCLUDES="$PKG_INCLUDES $i"
+    done
+
+
+
+    vars=""
+    for i in $vars; do
+	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
+	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
+	    i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'`
+	fi
+	PKG_LIBS="$PKG_LIBS $i"
+    done
+
+
+
+    PKG_CFLAGS="$PKG_CFLAGS "
+
+
+
+    vars=""
+    for i in $vars; do
+	# check for existence - allows for generic/win/unix VPATH
+	if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+	    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+	    -a ! -f "${srcdir}/macosx/$i" \
+	    ; then
+	    as_fn_error "could not find stub source file '$i'" "$LINENO" 5
+	fi
+	PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i"
+	# this assumes it is in a VPATH dir
+	i=`basename $i`
+	# handle user calling this before or after TEA_SETUP_COMPILER
+	if test x"${OBJEXT}" != x ; then
+	    j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}"
+	else
+	    j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}"
+	fi
+	PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j"
+    done
+
+
+
+
+    vars="library/graph.tcl library/tabnotebook.tcl library/treeview.cur library/treeview.xbm library/treeview.tcl library/treeview_m.xbm library/bltCanvEps.pro library/bltGraph.pro"
+    for i in $vars; do
+	# check for existence, be strict because it is installed
+	if test ! -f "${srcdir}/$i" ; then
+	    as_fn_error "could not find tcl source file '${srcdir}/$i'" "$LINENO" 5
+	fi
+	PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i"
+    done
+
+
+
+#--------------------------------------------------------------------
+# __CHANGE__
+#
+# You can add more files to clean if your extension creates any extra
+# files by extending CLEANFILES.
+# Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure
+# and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var.
+#
+# A few miscellaneous platform-specific items:
+# TEA_ADD_* any platform specific compiler/build info here.
+#--------------------------------------------------------------------
+
+CLEANFILES="$CLEANFILES pkgIndex.tcl"
+if test "${TEA_PLATFORM}" = "windows" ; then
+    CLEANFILES="$CLEANFILES *.lib *.dll *.exp *.ilk *.pdb vc*.pch"
+
+    vars="win/bltWinDraw.c win/bltWinImage.c win/bltWinPrnt.c win/bltWinUtil.c"
+    for i in $vars; do
+	case $i in
+	    \$*)
+		# allow $-var names
+		PKG_SOURCES="$PKG_SOURCES $i"
+		PKG_OBJECTS="$PKG_OBJECTS $i"
+		;;
+	    *)
+		# check for existence - allows for generic/win/unix VPATH
+		# To add more dirs here (like 'src'), you have to update VPATH
+		# in Makefile.in as well
+		if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+		    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+		    -a ! -f "${srcdir}/macosx/$i" \
+		    ; then
+		    as_fn_error "could not find source file '$i'" "$LINENO" 5
+		fi
+		PKG_SOURCES="$PKG_SOURCES $i"
+		# this assumes it is in a VPATH dir
+		i=`basename $i`
+		# handle user calling this before or after TEA_SETUP_COMPILER
+		if test x"${OBJEXT}" != x ; then
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}"
+		else
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}"
+		fi
+		PKG_OBJECTS="$PKG_OBJECTS $j"
+		;;
+	esac
+    done
+
+
+
+
+    vars="-I\"$(${CYGPATH} ${srcdir}/win)\""
+    for i in $vars; do
+	PKG_INCLUDES="$PKG_INCLUDES $i"
+    done
+
+
+else
+
+$as_echo "#define NO_PRINTER 1" >>confdefs.h
+
+
+    vars="unix/bltUnixImage.c"
+    for i in $vars; do
+	case $i in
+	    \$*)
+		# allow $-var names
+		PKG_SOURCES="$PKG_SOURCES $i"
+		PKG_OBJECTS="$PKG_OBJECTS $i"
+		;;
+	    *)
+		# check for existence - allows for generic/win/unix VPATH
+		# To add more dirs here (like 'src'), you have to update VPATH
+		# in Makefile.in as well
+		if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+		    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+		    -a ! -f "${srcdir}/macosx/$i" \
+		    ; then
+		    as_fn_error "could not find source file '$i'" "$LINENO" 5
+		fi
+		PKG_SOURCES="$PKG_SOURCES $i"
+		# this assumes it is in a VPATH dir
+		i=`basename $i`
+		# handle user calling this before or after TEA_SETUP_COMPILER
+		if test x"${OBJEXT}" != x ; then
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.${OBJEXT}"
+		else
+		    j="`echo $i | sed -e 's/\.[^.]*$//'`.\${OBJEXT}"
+		fi
+		PKG_OBJECTS="$PKG_OBJECTS $j"
+		;;
+	esac
+    done
+
+
+
+fi
+
+#--------------------------------------------------------------------
+# __CHANGE__
+# Choose which headers you need.  Extension authors should try very
+# hard to only rely on the Tcl public header files.  Internal headers
+# contain private data structures and are subject to change without
+# notice.
+# This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG
+#--------------------------------------------------------------------
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl public headers" >&5
+$as_echo_n "checking for Tcl public headers... " >&6; }
+
+
+# Check whether --with-tclinclude was given.
+if test "${with_tclinclude+set}" = set; then :
+  withval=$with_tclinclude; with_tclinclude=${withval}
+fi
+
+
+    if test "${ac_cv_c_tclh+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	# Use the value from --with-tclinclude, if it was given
+
+	if test x"${with_tclinclude}" != x ; then
+	    if test -f "${with_tclinclude}/tcl.h" ; then
+		ac_cv_c_tclh=${with_tclinclude}
+	    else
+		as_fn_error "${with_tclinclude} directory does not contain tcl.h" "$LINENO" 5
+	    fi
+	else
+	    list=""
+	    if test "`uname -s`" = "Darwin"; then
+		# If Tcl was built as a framework, attempt to use
+		# the framework's Headers directory
+		case ${TCL_DEFS} in
+		    *TCL_FRAMEWORK*)
+			list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`"
+			;;
+		esac
+	    fi
+
+	    # Look in the source dir only if Tcl is not installed,
+	    # and in that situation, look there before installed locations.
+	    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+		list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`"
+	    fi
+
+	    # Check order: pkg --prefix location, Tcl's --prefix location,
+	    # relative to directory of tclConfig.sh.
+
+	    eval "temp_includedir=${includedir}"
+	    list="$list \
+		`ls -d ${temp_includedir}        2>/dev/null` \
+		`ls -d ${TCL_PREFIX}/include     2>/dev/null` \
+		`ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+	    if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+		list="$list /usr/local/include /usr/include"
+		if test x"${TCL_INCLUDE_SPEC}" != x ; then
+		    d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+		    list="$list `ls -d ${d} 2>/dev/null`"
+		fi
+	    fi
+	    for i in $list ; do
+		if test -f "$i/tcl.h" ; then
+		    ac_cv_c_tclh=$i
+		    break
+		fi
+	    done
+	fi
+
+fi
+
+
+    # Print a message based on how we determined the include path
+
+    if test x"${ac_cv_c_tclh}" = x ; then
+	as_fn_error "tcl.h not found.  Please specify its location with --with-tclinclude" "$LINENO" 5
+    else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${ac_cv_c_tclh}" >&5
+$as_echo "${ac_cv_c_tclh}" >&6; }
+    fi
+
+    # Convert to a native path and substitute into the output files.
+
+    INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}`
+
+    TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+
+
+#TEA_PRIVATE_TCL_HEADERS
+
+#TEA_PUBLIC_TK_HEADERS
+#TEA_PRIVATE_TK_HEADERS
+#TEA_PATH_X
+
+#--------------------------------------------------------------------
+# Check whether --enable-threads or --disable-threads was given.
+# This auto-enables if Tcl was compiled threaded.
+#--------------------------------------------------------------------
+
+
+    # Check whether --enable-threads was given.
+if test "${enable_threads+set}" = set; then :
+  enableval=$enable_threads; tcl_ok=$enableval
+else
+  tcl_ok=yes
+fi
+
+
+    if test "${enable_threads+set}" = set; then
+	enableval="$enable_threads"
+	tcl_ok=$enableval
+    else
+	tcl_ok=yes
+    fi
+
+    if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then
+	TCL_THREADS=1
+
+	if test "${TEA_PLATFORM}" != "windows" ; then
+	    # We are always OK on Windows, so check what this platform wants:
+
+	    # USE_THREAD_ALLOC tells us to try the special thread-based
+	    # allocator that significantly reduces lock contention
+
+$as_echo "#define USE_THREAD_ALLOC 1" >>confdefs.h
+
+
+$as_echo "#define _REENTRANT 1" >>confdefs.h
+
+	    if test "`uname -s`" = "SunOS" ; then
+
+$as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+	    fi
+
+$as_echo "#define _THREAD_SAFE 1" >>confdefs.h
+
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthread" >&5
+$as_echo_n "checking for pthread_mutex_init in -lpthread... " >&6; }
+if test "${ac_cv_lib_pthread_pthread_mutex_init+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_init ();
+int
+main ()
+{
+return pthread_mutex_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pthread_pthread_mutex_init=yes
+else
+  ac_cv_lib_pthread_pthread_mutex_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutex_init" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_mutex_init" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_mutex_init" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+	    if test "$tcl_ok" = "no"; then
+		# Check a little harder for __pthread_mutex_init in the same
+		# library, as some systems hide it there until pthread.h is
+		# defined.  We could alternatively do an AC_TRY_COMPILE with
+		# pthread.h, but that will work with libpthread really doesn't
+		# exist, like AIX 4.2.  [Bug: 4359]
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __pthread_mutex_init in -lpthread" >&5
+$as_echo_n "checking for __pthread_mutex_init in -lpthread... " >&6; }
+if test "${ac_cv_lib_pthread___pthread_mutex_init+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char __pthread_mutex_init ();
+int
+main ()
+{
+return __pthread_mutex_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pthread___pthread_mutex_init=yes
+else
+  ac_cv_lib_pthread___pthread_mutex_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread___pthread_mutex_init" >&5
+$as_echo "$ac_cv_lib_pthread___pthread_mutex_init" >&6; }
+if test "x$ac_cv_lib_pthread___pthread_mutex_init" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+	    fi
+
+	    if test "$tcl_ok" = "yes"; then
+		# The space is needed
+		THREADS_LIBS=" -lpthread"
+	    else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lpthreads" >&5
+$as_echo_n "checking for pthread_mutex_init in -lpthreads... " >&6; }
+if test "${ac_cv_lib_pthreads_pthread_mutex_init+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthreads  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_init ();
+int
+main ()
+{
+return pthread_mutex_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pthreads_pthread_mutex_init=yes
+else
+  ac_cv_lib_pthreads_pthread_mutex_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthreads_pthread_mutex_init" >&5
+$as_echo "$ac_cv_lib_pthreads_pthread_mutex_init" >&6; }
+if test "x$ac_cv_lib_pthreads_pthread_mutex_init" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+		if test "$tcl_ok" = "yes"; then
+		    # The space is needed
+		    THREADS_LIBS=" -lpthreads"
+		else
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc" >&5
+$as_echo_n "checking for pthread_mutex_init in -lc... " >&6; }
+if test "${ac_cv_lib_c_pthread_mutex_init+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_init ();
+int
+main ()
+{
+return pthread_mutex_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_c_pthread_mutex_init=yes
+else
+  ac_cv_lib_c_pthread_mutex_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_pthread_mutex_init" >&5
+$as_echo "$ac_cv_lib_c_pthread_mutex_init" >&6; }
+if test "x$ac_cv_lib_c_pthread_mutex_init" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+		    if test "$tcl_ok" = "no"; then
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_init in -lc_r" >&5
+$as_echo_n "checking for pthread_mutex_init in -lc_r... " >&6; }
+if test "${ac_cv_lib_c_r_pthread_mutex_init+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc_r  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_init ();
+int
+main ()
+{
+return pthread_mutex_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_c_r_pthread_mutex_init=yes
+else
+  ac_cv_lib_c_r_pthread_mutex_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_mutex_init" >&5
+$as_echo "$ac_cv_lib_c_r_pthread_mutex_init" >&6; }
+if test "x$ac_cv_lib_c_r_pthread_mutex_init" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+			if test "$tcl_ok" = "yes"; then
+			    # The space is needed
+			    THREADS_LIBS=" -pthread"
+			else
+			    TCL_THREADS=0
+			    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&5
+$as_echo "$as_me: WARNING: Do not know how to find pthread lib on your system - thread support disabled" >&2;}
+			fi
+		    fi
+		fi
+	    fi
+	fi
+    else
+	TCL_THREADS=0
+    fi
+    # Do checking message here to not mess up interleaved configure output
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for building with threads" >&5
+$as_echo_n "checking for building with threads... " >&6; }
+    if test "${TCL_THREADS}" = 1; then
+
+$as_echo "#define TCL_THREADS 1" >>confdefs.h
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (default)" >&5
+$as_echo "yes (default)" >&6; }
+    else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+    # TCL_THREADS sanity checking.  See if our request for building with
+    # threads is the same as the way Tcl was built.  If not, warn the user.
+    case ${TCL_DEFS} in
+	*THREADS=1*)
+	    if test "${TCL_THREADS}" = "0"; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    Building ${PACKAGE_NAME} without threads enabled, but building against Tcl
+    that IS thread-enabled.  It is recommended to use --enable-threads." >&5
+$as_echo "$as_me: WARNING:
+    Building ${PACKAGE_NAME} without threads enabled, but building against Tcl
+    that IS thread-enabled.  It is recommended to use --enable-threads." >&2;}
+	    fi
+	    ;;
+	*)
+	    if test "${TCL_THREADS}" = "1"; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    --enable-threads requested, but building against a Tcl that is NOT
+    thread-enabled.  This is an OK configuration that will also run in
+    a thread-enabled core." >&5
+$as_echo "$as_me: WARNING:
+    --enable-threads requested, but building against a Tcl that is NOT
+    thread-enabled.  This is an OK configuration that will also run in
+    a thread-enabled core." >&2;}
+	    fi
+	    ;;
+    esac
+
+
+
+#--------------------------------------------------------------------
+# The statement below defines a collection of symbols related to
+# building as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to build libraries" >&5
+$as_echo_n "checking how to build libraries... " >&6; }
+    # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+  enableval=$enable_shared; tcl_ok=$enableval
+else
+  tcl_ok=yes
+fi
+
+
+    if test "${enable_shared+set}" = set; then
+	enableval="$enable_shared"
+	tcl_ok=$enableval
+    else
+	tcl_ok=yes
+    fi
+
+    if test "$tcl_ok" = "yes" ; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: shared" >&5
+$as_echo "shared" >&6; }
+	SHARED_BUILD=1
+    else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: static" >&5
+$as_echo "static" >&6; }
+	SHARED_BUILD=0
+
+$as_echo "#define STATIC_BUILD 1" >>confdefs.h
+
+    fi
+
+
+
+#--------------------------------------------------------------------
+# This macro figures out what flags to use with the compiler/linker
+# when building shared/static debug/optimized objects.  This information
+# can be taken from the tclConfig.sh file, but this figures it all out.
+#--------------------------------------------------------------------
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+
+
+    # Step 0.a: Enable 64 bit support?
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit support is requested" >&5
+$as_echo_n "checking if 64bit support is requested... " >&6; }
+    # Check whether --enable-64bit was given.
+if test "${enable_64bit+set}" = set; then :
+  enableval=$enable_64bit; do64bit=$enableval
+else
+  do64bit=no
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bit" >&5
+$as_echo "$do64bit" >&6; }
+
+    # Step 0.b: Enable Solaris 64 bit VIS support?
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if 64bit Sparc VIS support is requested" >&5
+$as_echo_n "checking if 64bit Sparc VIS support is requested... " >&6; }
+    # Check whether --enable-64bit-vis was given.
+if test "${enable_64bit_vis+set}" = set; then :
+  enableval=$enable_64bit_vis; do64bitVIS=$enableval
+else
+  do64bitVIS=no
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $do64bitVIS" >&5
+$as_echo "$do64bitVIS" >&6; }
+    # Force 64bit on with VIS
+    if test "$do64bitVIS" = "yes"; then :
+  do64bit=yes
+fi
+
+    # Step 0.c: Check if visibility support is available. Do this here so
+    # that platform specific alternatives can be used below if this fails.
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler supports visibility \"hidden\"" >&5
+$as_echo_n "checking if compiler supports visibility \"hidden\"... " >&6; }
+if test "${tcl_cv_cc_visibility_hidden+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+	    extern __attribute__((__visibility__("hidden"))) void f(void);
+	    void f(void) {}
+int
+main ()
+{
+f();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_cc_visibility_hidden=yes
+else
+  tcl_cv_cc_visibility_hidden=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+	CFLAGS=$hold_cflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_visibility_hidden" >&5
+$as_echo "$tcl_cv_cc_visibility_hidden" >&6; }
+    if test $tcl_cv_cc_visibility_hidden = yes; then :
+
+
+$as_echo "#define MODULE_SCOPE extern __attribute__((__visibility__(\"hidden\")))" >>confdefs.h
+
+
+fi
+
+    # Step 0.d: Disable -rpath support?
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if rpath support is requested" >&5
+$as_echo_n "checking if rpath support is requested... " >&6; }
+    # Check whether --enable-rpath was given.
+if test "${enable_rpath+set}" = set; then :
+  enableval=$enable_rpath; doRpath=$enableval
+else
+  doRpath=yes
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $doRpath" >&5
+$as_echo "$doRpath" >&6; }
+
+    # TEA specific: Cross-compiling options for Windows/CE builds?
+
+    if test "${TEA_PLATFORM}" = windows; then :
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if Windows/CE build is requested" >&5
+$as_echo_n "checking if Windows/CE build is requested... " >&6; }
+	# Check whether --enable-wince was given.
+if test "${enable_wince+set}" = set; then :
+  enableval=$enable_wince; doWince=$enableval
+else
+  doWince=no
+fi
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $doWince" >&5
+$as_echo "$doWince" >&6; }
+
+fi
+
+    # Set the variable "system" to hold the name and version number
+    # for the system.
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking system version" >&5
+$as_echo_n "checking system version... " >&6; }
+if test "${tcl_cv_sys_version+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	# TEA specific:
+	if test "${TEA_PLATFORM}" = "windows" ; then
+	    tcl_cv_sys_version=windows
+	else
+	    tcl_cv_sys_version=`uname -s`-`uname -r`
+	    if test "$?" -ne 0 ; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: can't find uname command" >&5
+$as_echo "$as_me: WARNING: can't find uname command" >&2;}
+		tcl_cv_sys_version=unknown
+	    else
+		if test "`uname -s`" = "AIX" ; then
+		    tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
+		fi
+	    fi
+	fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_sys_version" >&5
+$as_echo "$tcl_cv_sys_version" >&6; }
+    system=$tcl_cv_sys_version
+
+
+    # Require ranlib early so we can override it in special cases below.
+
+
+
+    # Set configuration options based on system name and version.
+    # This is similar to Tcl's unix/tcl.m4 except that we've added a
+    # "windows" case and removed some core-only vars.
+
+    do64bit_ok=no
+    # default to '{$LIBS}' and set to "" on per-platform necessary basis
+    SHLIB_LD_LIBS='${LIBS}'
+    # When ld needs options to work in 64-bit mode, put them in
+    # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load]
+    # is disabled by the user. [Bug 1016796]
+    LDFLAGS_ARCH=""
+    UNSHARED_LIB_SUFFIX=""
+    # TEA specific: use PACKAGE_VERSION instead of VERSION
+    TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`'
+    ECHO_VERSION='`echo ${PACKAGE_VERSION}`'
+    TCL_LIB_VERSIONS_OK=ok
+    CFLAGS_DEBUG=-g
+    CFLAGS_OPTIMIZE=-O
+    if test "$GCC" = yes; then :
+
+	# TEA specific:
+	CFLAGS_OPTIMIZE=-O2
+	CFLAGS_WARNING="-Wall"
+
+else
+  CFLAGS_WARNING=""
+fi
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AR+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_AR="${ac_tool_prefix}ar"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+  ac_ct_AR=$AR
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_AR="ar"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_AR" = x; then
+    AR=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+else
+  AR="$ac_cv_prog_AR"
+fi
+
+    STLIB_LD='${AR} cr'
+    LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
+    if test "x$SHLIB_VERSION" = x; then :
+  SHLIB_VERSION="1.0"
+fi
+    case $system in
+	# TEA specific:
+	windows)
+	    # This is a 2-stage check to make sure we have the 64-bit SDK
+	    # We have to know where the SDK is installed.
+	    # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs
+	    # MACHINE is IX86 for LINK, but this is used by the manifest,
+	    # which requires x86|amd64|ia64.
+	    MACHINE="X86"
+	    if test "$do64bit" != "no" ; then
+		if test "x${MSSDK}x" = "xx" ; then
+		    MSSDK="C:/Progra~1/Microsoft Platform SDK"
+		fi
+		MSSDK=`echo "$MSSDK" | sed -e  's!\\\!/!g'`
+		PATH64=""
+		case "$do64bit" in
+		    amd64|x64|yes)
+			MACHINE="AMD64" ; # default to AMD64 64-bit build
+			PATH64="${MSSDK}/Bin/Win64/x86/AMD64"
+			;;
+		    ia64)
+			MACHINE="IA64"
+			PATH64="${MSSDK}/Bin/Win64"
+			;;
+		esac
+		if test ! -d "${PATH64}" ; then
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&5
+$as_echo "$as_me: WARNING: Could not find 64-bit $MACHINE SDK to enable 64bit mode" >&2;}
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ensure latest Platform SDK is installed" >&5
+$as_echo "$as_me: WARNING: Ensure latest Platform SDK is installed" >&2;}
+		    do64bit="no"
+		else
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: result:    Using 64-bit $MACHINE mode" >&5
+$as_echo "   Using 64-bit $MACHINE mode" >&6; }
+		    do64bit_ok="yes"
+		fi
+	    fi
+
+	    if test "$doWince" != "no" ; then
+		if test "$do64bit" != "no" ; then
+		    as_fn_error "Windows/CE and 64-bit builds incompatible" "$LINENO" 5
+		fi
+		if test "$GCC" = "yes" ; then
+		    as_fn_error "Windows/CE and GCC builds incompatible" "$LINENO" 5
+		fi
+
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-celib
+
+    if test x"${no_celib}" = x ; then
+	# we reset no_celib in case something fails here
+	no_celib=true
+
+# Check whether --with-celib was given.
+if test "${with_celib+set}" = set; then :
+  withval=$with_celib; with_celibconfig=${withval}
+fi
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Windows/CE celib directory" >&5
+$as_echo_n "checking for Windows/CE celib directory... " >&6; }
+	if test "${ac_cv_c_celibconfig+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	    # First check to see if --with-celibconfig was specified.
+	    if test x"${with_celibconfig}" != x ; then
+		if test -d "${with_celibconfig}/inc" ; then
+		    ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
+		else
+		    as_fn_error "${with_celibconfig} directory doesn't contain inc directory" "$LINENO" 5
+		fi
+	    fi
+
+	    # then check for a celib library
+	    if test x"${ac_cv_c_celibconfig}" = x ; then
+		for i in \
+			../celib-palm-3.0 \
+			../celib \
+			../../celib-palm-3.0 \
+			../../celib \
+			`ls -dr ../celib-*3.[0-9]* 2>/dev/null` \
+			${srcdir}/../celib-palm-3.0 \
+			${srcdir}/../celib \
+			`ls -dr ${srcdir}/../celib-*3.[0-9]* 2>/dev/null` \
+			; do
+		    if test -d "$i/inc" ; then
+			ac_cv_c_celibconfig=`(cd $i; pwd)`
+			break
+		    fi
+		done
+	    fi
+
+fi
+
+	if test x"${ac_cv_c_celibconfig}" = x ; then
+	    as_fn_error "Cannot find celib support library directory" "$LINENO" 5
+	else
+	    no_celib=
+	    CELIB_DIR=${ac_cv_c_celibconfig}
+	    CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: found $CELIB_DIR" >&5
+$as_echo "found $CELIB_DIR" >&6; }
+	fi
+    fi
+
+		# Set defaults for common evc4/PPC2003 setup
+		# Currently Tcl requires 300+, possibly 420+ for sockets
+		CEVERSION=420; 		# could be 211 300 301 400 420 ...
+		TARGETCPU=ARMV4;	# could be ARMV4 ARM MIPS SH3 X86 ...
+		ARCH=ARM;		# could be ARM MIPS X86EM ...
+		PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
+		if test "$doWince" != "yes"; then
+		    # If !yes then the user specified something
+		    # Reset ARCH to allow user to skip specifying it
+		    ARCH=
+		    eval `echo $doWince | awk -F, '{ \
+	    if (length($1)) { printf "CEVERSION=\"%s\"\n", $1; \
+	    if ($1 < 400)   { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
+	    if (length($2)) { printf "TARGETCPU=\"%s\"\n", toupper($2) }; \
+	    if (length($3)) { printf "ARCH=\"%s\"\n", toupper($3) }; \
+	    if (length($4)) { printf "PLATFORM=\"%s\"\n", $4 }; \
+		    }'`
+		    if test "x${ARCH}" = "x" ; then
+			ARCH=$TARGETCPU;
+		    fi
+		fi
+		OSVERSION=WCE$CEVERSION;
+	    	if test "x${WCEROOT}" = "x" ; then
+			WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
+		    if test ! -d "${WCEROOT}" ; then
+			WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
+		    fi
+		fi
+		if test "x${SDKROOT}" = "x" ; then
+		    SDKROOT="C:/Program Files/Windows CE Tools"
+		    if test ! -d "${SDKROOT}" ; then
+			SDKROOT="C:/Windows CE Tools"
+		    fi
+		fi
+		WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
+		SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
+		if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
+		    -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
+		    as_fn_error "could not find PocketPC SDK or target compiler to enable WinCE mode $CEVERSION,$TARGETCPU,$ARCH,$PLATFORM" "$LINENO" 5
+		    doWince="no"
+		else
+		    # We could PATH_NOSPACE these, but that's not important,
+		    # as long as we quote them when used.
+		    CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
+		    if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
+			CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
+		    fi
+		    CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
+    		fi
+	    fi
+
+	    if test "$GCC" != "yes" ; then
+	        if test "${SHARED_BUILD}" = "0" ; then
+		    runtime=-MT
+	        else
+		    runtime=-MD
+	        fi
+
+                if test "$do64bit" != "no" ; then
+		    # All this magic is necessary for the Win64 SDK RC1 - hobbs
+		    CC="\"${PATH64}/cl.exe\""
+		    CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
+		    RC="\"${MSSDK}/bin/rc.exe\""
+		    lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
+		    LINKBIN="\"${PATH64}/link.exe\""
+		    CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
+		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+		    # Avoid 'unresolved external symbol __security_cookie'
+		    # errors, c.f. http://support.microsoft.com/?id=894573
+
+    vars="bufferoverflowU.lib"
+    for i in $vars; do
+	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
+	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
+	    i=`echo "$i" | sed -e 's/^\([^-].*\)\.lib$/-l\1/i'`
+	fi
+	PKG_LIBS="$PKG_LIBS $i"
+    done
+
+
+		elif test "$doWince" != "no" ; then
+		    CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
+		    if test "${TARGETCPU}" = "X86"; then
+			CC="\"${CEBINROOT}/cl.exe\""
+		    else
+			CC="\"${CEBINROOT}/cl${ARCH}.exe\""
+		    fi
+		    CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
+		    RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
+		    arch=`echo ${ARCH} | awk '{print tolower($0)}'`
+		    defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
+		    if test "${SHARED_BUILD}" = "1" ; then
+			# Static CE builds require static celib as well
+		    	defs="${defs} _DLL"
+		    fi
+		    for i in $defs ; do
+
+cat >>confdefs.h <<_ACEOF
+#define $i 1
+_ACEOF
+
+		    done
+
+cat >>confdefs.h <<_ACEOF
+#define _WIN32_WCE $CEVERSION
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define UNDER_CE $CEVERSION
+_ACEOF
+
+		    CFLAGS_DEBUG="-nologo -Zi -Od"
+		    CFLAGS_OPTIMIZE="-nologo -Ox"
+		    lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
+		    lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
+		    LINKBIN="\"${CEBINROOT}/link.exe\""
+
+		else
+		    RC="rc"
+		    lflags="-nologo"
+    		    LINKBIN="link"
+		    CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
+		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+		fi
+	    fi
+
+	    if test "$GCC" = "yes"; then
+		# mingw gcc mode
+		RC="windres"
+		CFLAGS_DEBUG="-g"
+		CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+		SHLIB_LD="$CC -shared"
+		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+		LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
+		LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"
+	    else
+		SHLIB_LD="${LINKBIN} -dll ${lflags}"
+		# link -lib only works when -lib is the first arg
+		STLIB_LD="${LINKBIN} -lib ${lflags}"
+		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib'
+		PATHTYPE=-w
+		# For information on what debugtype is most useful, see:
+		# http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp
+		# and also
+		# http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx
+		# This essentially turns it all on.
+		LDFLAGS_DEBUG="-debug -debugtype:cv"
+		LDFLAGS_OPTIMIZE="-release"
+		if test "$doWince" != "no" ; then
+		    LDFLAGS_CONSOLE="-link ${lflags}"
+		    LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
+		else
+		    LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
+		    LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
+		fi
+	    fi
+
+	    SHLIB_SUFFIX=".dll"
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll'
+
+	    TCL_LIB_VERSIONS_OK=nodots
+    	    ;;
+	AIX-*)
+	    if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"; then :
+
+		# AIX requires the _r compiler when gcc isn't being used
+		case "${CC}" in
+		    *_r|*_r\ *)
+			# ok ...
+			;;
+		    *)
+			# Make sure only first arg gets _r
+		    	CC=`echo "$CC" | sed -e 's/^\([^ ]*\)/\1_r/'`
+			;;
+		esac
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Using $CC for compiling with threads" >&5
+$as_echo "Using $CC for compiling with threads" >&6; }
+
+fi
+	    LIBS="$LIBS -lc"
+	    SHLIB_CFLAGS=""
+	    SHLIB_SUFFIX=".so"
+
+	    LD_LIBRARY_PATH_VAR="LIBPATH"
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    if test "$do64bit" = yes; then :
+
+		if test "$GCC" = yes; then :
+
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;}
+
+else
+
+		    do64bit_ok=yes
+		    CFLAGS="$CFLAGS -q64"
+		    LDFLAGS_ARCH="-q64"
+		    RANLIB="${RANLIB} -X64"
+		    AR="${AR} -X64"
+		    SHLIB_LD_FLAGS="-b64"
+
+fi
+
+fi
+
+	    if test "`uname -m`" = ia64; then :
+
+		# AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC
+		SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+		if test "$GCC" = yes; then :
+
+		    CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+
+else
+
+		    CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}'
+
+fi
+		LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+
+else
+
+		if test "$GCC" = yes; then :
+
+		    SHLIB_LD='${CC} -shared -Wl,-bexpall'
+
+else
+
+		    SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry"
+		    LDFLAGS="$LDFLAGS -brtl"
+
+fi
+		SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}"
+		CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+
+fi
+	    ;;
+	BeOS*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -nostart'
+	    SHLIB_SUFFIX=".so"
+
+	    #-----------------------------------------------------------
+	    # Check for inet_ntoa in -lbind, for BeOS (which also needs
+	    # -lsocket, even if the network functions are in -lnet which
+	    # is always linked to, for compatibility.
+	    #-----------------------------------------------------------
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lbind" >&5
+$as_echo_n "checking for inet_ntoa in -lbind... " >&6; }
+if test "${ac_cv_lib_bind_inet_ntoa+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbind  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_ntoa ();
+int
+main ()
+{
+return inet_ntoa ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_bind_inet_ntoa=yes
+else
+  ac_cv_lib_bind_inet_ntoa=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bind_inet_ntoa" >&5
+$as_echo "$ac_cv_lib_bind_inet_ntoa" >&6; }
+if test "x$ac_cv_lib_bind_inet_ntoa" = x""yes; then :
+  LIBS="$LIBS -lbind -lsocket"
+fi
+
+	    ;;
+	BSD/OS-4.*)
+	    SHLIB_CFLAGS="-export-dynamic -fPIC"
+	    SHLIB_LD='${CC} -shared'
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS="$LDFLAGS -export-dynamic"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	CYGWIN_*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD='${CC} -shared'
+	    SHLIB_SUFFIX=".dll"
+	    EXE_SUFFIX=".exe"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	Haiku*)
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnetwork" >&5
+$as_echo_n "checking for inet_ntoa in -lnetwork... " >&6; }
+if test "${ac_cv_lib_network_inet_ntoa+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnetwork  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_ntoa ();
+int
+main ()
+{
+return inet_ntoa ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_network_inet_ntoa=yes
+else
+  ac_cv_lib_network_inet_ntoa=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_inet_ntoa" >&5
+$as_echo "$ac_cv_lib_network_inet_ntoa" >&6; }
+if test "x$ac_cv_lib_network_inet_ntoa" = x""yes; then :
+  LIBS="$LIBS -lnetwork"
+fi
+
+	    ;;
+	HP-UX-*.11.*)
+	    # Use updated header definitions where possible
+
+$as_echo "#define _XOPEN_SOURCE_EXTENDED 1" >>confdefs.h
+
+	    # TEA specific: Needed by Tcl, but not most extensions
+	    #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?])
+	    #LIBS="$LIBS -lxnet"               # Use the XOPEN network library
+
+	    if test "`uname -m`" = ia64; then :
+
+		SHLIB_SUFFIX=".so"
+		# Use newer C++ library for C++ extensions
+		#if test "$GCC" != "yes" ; then
+		#   CPPFLAGS="-AA"
+		#fi
+
+else
+
+		SHLIB_SUFFIX=".sl"
+
+fi
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_shl_load=yes
+else
+  ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = x""yes; then :
+  tcl_ok=yes
+else
+  tcl_ok=no
+fi
+
+	    if test "$tcl_ok" = yes; then :
+
+		LDFLAGS="$LDFLAGS -Wl,-E"
+		CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
+		LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+		LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+
+fi
+	    if test "$GCC" = yes; then :
+
+		SHLIB_LD='${CC} -shared'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+
+else
+
+		CFLAGS="$CFLAGS -z"
+		# Users may want PA-RISC 1.1/2.0 portable code - needs HP cc
+		#CFLAGS="$CFLAGS +DAportable"
+		SHLIB_CFLAGS="+z"
+		SHLIB_LD="ld -b"
+
+fi
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    if test "$do64bit" = "yes"; then :
+
+		if test "$GCC" = yes; then :
+
+		    case `${CC} -dumpmachine` in
+			hppa64*)
+			    # 64-bit gcc in use.  Fix flags for GNU ld.
+			    do64bit_ok=yes
+			    SHLIB_LD='${CC} -shared'
+			    if test $doRpath = yes; then :
+
+				CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+fi
+			    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+			    ;;
+			*)
+			    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;}
+			    ;;
+		    esac
+
+else
+
+		    do64bit_ok=yes
+		    CFLAGS="$CFLAGS +DD64"
+		    LDFLAGS_ARCH="+DD64"
+
+fi
+
+fi ;;
+	IRIX-6.*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD="ld -n32 -shared -rdata_shared"
+	    SHLIB_SUFFIX=".so"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+fi
+	    if test "$GCC" = yes; then :
+
+		CFLAGS="$CFLAGS -mabi=n32"
+		LDFLAGS="$LDFLAGS -mabi=n32"
+
+else
+
+		case $system in
+		    IRIX-6.3)
+			# Use to build 6.2 compatible binaries on 6.3.
+			CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS"
+			;;
+		    *)
+			CFLAGS="$CFLAGS -n32"
+			;;
+		esac
+		LDFLAGS="$LDFLAGS -n32"
+
+fi
+	    ;;
+	IRIX64-6.*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD="ld -n32 -shared -rdata_shared"
+	    SHLIB_SUFFIX=".so"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+fi
+
+	    # Check to enable 64-bit flags for compiler/linker
+
+	    if test "$do64bit" = yes; then :
+
+	        if test "$GCC" = yes; then :
+
+	            { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported by gcc" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported by gcc" >&2;}
+
+else
+
+	            do64bit_ok=yes
+	            SHLIB_LD="ld -64 -shared -rdata_shared"
+	            CFLAGS="$CFLAGS -64"
+	            LDFLAGS_ARCH="-64"
+
+fi
+
+fi
+	    ;;
+	Linux*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+
+	    # TEA specific:
+	    CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+
+	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+fi
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    if test "`uname -m`" = "alpha"; then :
+  CFLAGS="$CFLAGS -mieee"
+fi
+	    if test $do64bit = yes; then :
+
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -m64 flag" >&5
+$as_echo_n "checking if compiler accepts -m64 flag... " >&6; }
+if test "${tcl_cv_cc_m64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+		    hold_cflags=$CFLAGS
+		    CFLAGS="$CFLAGS -m64"
+		    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_cc_m64=yes
+else
+  tcl_cv_cc_m64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+		    CFLAGS=$hold_cflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_m64" >&5
+$as_echo "$tcl_cv_cc_m64" >&6; }
+		if test $tcl_cv_cc_m64 = yes; then :
+
+		    CFLAGS="$CFLAGS -m64"
+		    do64bit_ok=yes
+
+fi
+
+fi
+
+	    # The combo of gcc + glibc has a bug related to inlining of
+	    # functions like strtod(). The -fno-builtin flag should address
+	    # this problem but it does not work. The -fno-inline flag is kind
+	    # of overkill but it works. Disable inlining only when one of the
+	    # files in compat/*.c is being linked in.
+
+	    if test x"${USE_COMPAT}" != x; then :
+  CFLAGS="$CFLAGS -fno-inline"
+fi
+
+	    ;;
+	GNU*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+
+	    SHLIB_LD='${CC} -shared'
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    if test "`uname -m`" = "alpha"; then :
+  CFLAGS="$CFLAGS -mieee"
+fi
+	    ;;
+	Lynx*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+	    CFLAGS_OPTIMIZE=-02
+	    SHLIB_LD='${CC} -shared'
+	    LD_FLAGS="-Wl,--export-dynamic"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+fi
+	    ;;
+	OpenBSD-*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+	    SHLIB_SUFFIX=".so"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+fi
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ELF" >&5
+$as_echo_n "checking for ELF... " >&6; }
+if test "${tcl_cv_ld_elf+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#ifdef __ELF__
+	yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then :
+  tcl_cv_ld_elf=yes
+else
+  tcl_cv_ld_elf=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_elf" >&5
+$as_echo "$tcl_cv_ld_elf" >&6; }
+	    if test $tcl_cv_ld_elf = yes; then :
+
+		LDFLAGS=-Wl,-export-dynamic
+
+else
+  LDFLAGS=""
+fi
+	    if test "${TCL_THREADS}" = "1"; then :
+
+		# OpenBSD builds and links with -pthread, never -lpthread.
+		LIBS=`echo $LIBS | sed s/-lpthread//`
+		CFLAGS="$CFLAGS -pthread"
+		SHLIB_CFLAGS="$SHLIB_CFLAGS -pthread"
+
+fi
+	    # OpenBSD doesn't do version numbers with dots.
+	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    TCL_LIB_VERSIONS_OK=nodots
+	    ;;
+	NetBSD-*|FreeBSD-[3-4].*)
+	    # FreeBSD 3.* and greater have ELF.
+	    # NetBSD 2.* has ELF and can use 'cc -shared' to build shared libs
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS="$LDFLAGS -export-dynamic"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+fi
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    if test "${TCL_THREADS}" = "1"; then :
+
+		# The -pthread needs to go in the CFLAGS, not LIBS
+		LIBS=`echo $LIBS | sed s/-pthread//`
+		CFLAGS="$CFLAGS -pthread"
+	    	LDFLAGS="$LDFLAGS -pthread"
+
+fi
+	    case $system in
+	    FreeBSD-3.*)
+	    	# FreeBSD-3 doesn't handle version numbers with dots.
+	    	UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    	SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
+	    	TCL_LIB_VERSIONS_OK=nodots
+		;;
+	    esac
+	    ;;
+	FreeBSD-*)
+	    # This configuration from FreeBSD Ports.
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD="${CC} -shared"
+	    TCL_SHLIB_LD_EXTRAS="-soname \$@"
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS=""
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+fi
+	    if test "${TCL_THREADS}" = "1"; then :
+
+		# The -pthread needs to go in the LDFLAGS, not LIBS
+		LIBS=`echo $LIBS | sed s/-pthread//`
+		CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+		LDFLAGS="$LDFLAGS $PTHREAD_LIBS"
+fi
+	    # Version numbers are dot-stripped by system policy.
+	    TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .`
+	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1'
+	    TCL_LIB_VERSIONS_OK=nodots
+	    ;;
+	Darwin-*)
+	    CFLAGS_OPTIMIZE="-Os"
+	    SHLIB_CFLAGS="-fno-common"
+	    # To avoid discrepancies between what headers configure sees during
+	    # preprocessing tests and compiling tests, move any -isysroot and
+	    # -mmacosx-version-min flags from CFLAGS to CPPFLAGS:
+	    CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \
+		awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+		if ($i~/^(isysroot|mmacosx-version-min)/) print "-"$i}'`"
+	    CFLAGS="`echo " ${CFLAGS}" | \
+		awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+		if (!($i~/^(isysroot|mmacosx-version-min)/)) print "-"$i}'`"
+	    if test $do64bit = yes; then :
+
+		case `arch` in
+		    ppc)
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch ppc64 flag" >&5
+$as_echo_n "checking if compiler accepts -arch ppc64 flag... " >&6; }
+if test "${tcl_cv_cc_arch_ppc64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+			    hold_cflags=$CFLAGS
+			    CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+			    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_cc_arch_ppc64=yes
+else
+  tcl_cv_cc_arch_ppc64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+			    CFLAGS=$hold_cflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_ppc64" >&5
+$as_echo "$tcl_cv_cc_arch_ppc64" >&6; }
+			if test $tcl_cv_cc_arch_ppc64 = yes; then :
+
+			    CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+			    do64bit_ok=yes
+
+fi;;
+		    i386)
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler accepts -arch x86_64 flag" >&5
+$as_echo_n "checking if compiler accepts -arch x86_64 flag... " >&6; }
+if test "${tcl_cv_cc_arch_x86_64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+			    hold_cflags=$CFLAGS
+			    CFLAGS="$CFLAGS -arch x86_64"
+			    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_cc_arch_x86_64=yes
+else
+  tcl_cv_cc_arch_x86_64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+			    CFLAGS=$hold_cflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_cc_arch_x86_64" >&5
+$as_echo "$tcl_cv_cc_arch_x86_64" >&6; }
+			if test $tcl_cv_cc_arch_x86_64 = yes; then :
+
+			    CFLAGS="$CFLAGS -arch x86_64"
+			    do64bit_ok=yes
+
+fi;;
+		    *)
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&5
+$as_echo "$as_me: WARNING: Don't know how enable 64-bit on architecture \`arch\`" >&2;};;
+		esac
+
+else
+
+		# Check for combined 32-bit and 64-bit fat build
+		if echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \
+		    && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '; then :
+
+		    fat_32_64=yes
+fi
+
+fi
+	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+	    SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}'
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -single_module flag" >&5
+$as_echo_n "checking if ld accepts -single_module flag... " >&6; }
+if test "${tcl_cv_ld_single_module+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+		hold_ldflags=$LDFLAGS
+		LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module"
+		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int i;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_ld_single_module=yes
+else
+  tcl_cv_ld_single_module=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+		LDFLAGS=$hold_ldflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_single_module" >&5
+$as_echo "$tcl_cv_ld_single_module" >&6; }
+	    if test $tcl_cv_ld_single_module = yes; then :
+
+		SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
+
+fi
+	    # TEA specific: link shlib with current and compatiblity version flags
+	    vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([0-9]\{1,5\}\)\(\(\.[0-9]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
+	    SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
+	    SHLIB_SUFFIX=".dylib"
+	    # Don't use -prebind when building for Mac OS X 10.4 or later only:
+	    if test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int($2)}'`" -lt 4 -a \
+		"`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int($2)}'`" -lt 4; then :
+
+		LDFLAGS="$LDFLAGS -prebind"
+fi
+	    LDFLAGS="$LDFLAGS -headerpad_max_install_names"
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld accepts -search_paths_first flag" >&5
+$as_echo_n "checking if ld accepts -search_paths_first flag... " >&6; }
+if test "${tcl_cv_ld_search_paths_first+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+		hold_ldflags=$LDFLAGS
+		LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int i;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_ld_search_paths_first=yes
+else
+  tcl_cv_ld_search_paths_first=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+		LDFLAGS=$hold_ldflags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_ld_search_paths_first" >&5
+$as_echo "$tcl_cv_ld_search_paths_first" >&6; }
+	    if test $tcl_cv_ld_search_paths_first = yes; then :
+
+		LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+
+fi
+	    if test "$tcl_cv_cc_visibility_hidden" != yes; then :
+
+
+$as_echo "#define MODULE_SCOPE __private_extern__" >>confdefs.h
+
+		tcl_cv_cc_visibility_hidden=yes
+
+fi
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH"
+	    # TEA specific: for combined 32 & 64 bit fat builds of Tk
+	    # extensions, verify that 64-bit build is possible.
+	    if test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"; then :
+
+		if test "${TEA_WINDOWINGSYSTEM}" = x11; then :
+
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit X11" >&5
+$as_echo_n "checking for 64-bit X11... " >&6; }
+if test "${tcl_cv_lib_x11_64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+			done
+			CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include"
+			LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11"
+			cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <X11/Xlib.h>
+int
+main ()
+{
+XrmInitialize();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_lib_x11_64=yes
+else
+  tcl_cv_lib_x11_64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval $v'="$hold_'$v'"'
+			done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_x11_64" >&5
+$as_echo "$tcl_cv_lib_x11_64" >&6; }
+
+fi
+		if test "${TEA_WINDOWINGSYSTEM}" = aqua; then :
+
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit Tk" >&5
+$as_echo_n "checking for 64-bit Tk... " >&6; }
+if test "${tcl_cv_lib_tk_64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+			done
+			CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}"
+			LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}"
+			cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <tk.h>
+int
+main ()
+{
+Tk_InitStubs(NULL, "", 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  tcl_cv_lib_tk_64=yes
+else
+  tcl_cv_lib_tk_64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval $v'="$hold_'$v'"'
+			done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_lib_tk_64" >&5
+$as_echo "$tcl_cv_lib_tk_64" >&6; }
+
+fi
+		# remove 64-bit arch flags from CFLAGS et al. if configuration
+		# does not support 64-bit.
+		if test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no; then :
+
+		    { $as_echo "$as_me:${as_lineno-$LINENO}: Removing 64-bit architectures from compiler & linker flags" >&5
+$as_echo "$as_me: Removing 64-bit architectures from compiler & linker flags" >&6;}
+		    for v in CFLAGS CPPFLAGS LDFLAGS; do
+			eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"'
+		    done
+fi
+
+fi
+	    ;;
+	OS/390-*)
+	    CFLAGS_OPTIMIZE=""		# Optimizer is buggy
+
+$as_echo "#define _OE_SOCKETS 1" >>confdefs.h
+
+	    ;;
+	OSF1-V*)
+	    # Digital OSF/1
+	    SHLIB_CFLAGS=""
+	    if test "$SHARED_BUILD" = 1; then :
+
+	        SHLIB_LD='ld -shared -expect_unresolved "*"'
+
+else
+
+	        SHLIB_LD='ld -non_shared -expect_unresolved "*"'
+
+fi
+	    SHLIB_SUFFIX=".so"
+	    if test $doRpath = yes; then :
+
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+fi
+	    if test "$GCC" = yes; then :
+  CFLAGS="$CFLAGS -mieee"
+else
+
+		CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"
+fi
+	    # see pthread_intro(3) for pthread support on osf1, k.furukawa
+	    if test "${TCL_THREADS}" = 1; then :
+
+		CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
+		CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
+		LIBS=`echo $LIBS | sed s/-lpthreads//`
+		if test "$GCC" = yes; then :
+
+		    LIBS="$LIBS -lpthread -lmach -lexc"
+
+else
+
+		    CFLAGS="$CFLAGS -pthread"
+		    LDFLAGS="$LDFLAGS -pthread"
+
+fi
+
+fi
+	    ;;
+	QNX-6*)
+	    # QNX RTP
+	    # This may work for all QNX, but it was only reported for v6.
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD="ld -Bshareable -x"
+	    SHLIB_LD_LIBS=""
+	    SHLIB_SUFFIX=".so"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	SCO_SV-3.2*)
+	    if test "$GCC" = yes; then :
+
+		SHLIB_CFLAGS="-fPIC -melf"
+		LDFLAGS="$LDFLAGS -melf -Wl,-Bexport"
+
+else
+
+	       SHLIB_CFLAGS="-Kpic -belf"
+	       LDFLAGS="$LDFLAGS -belf -Wl,-Bexport"
+
+fi
+	    SHLIB_LD="ld -G"
+	    SHLIB_LD_LIBS=""
+	    SHLIB_SUFFIX=".so"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	SunOS-5.[0-6])
+	    # Careful to not let 5.10+ fall into this case
+
+	    # Note: If _REENTRANT isn't defined, then Solaris
+	    # won't define thread-safe library routines.
+
+
+$as_echo "#define _REENTRANT 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+
+	    SHLIB_CFLAGS="-KPIC"
+	    SHLIB_SUFFIX=".so"
+	    if test "$GCC" = yes; then :
+
+		SHLIB_LD='${CC} -shared'
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+
+else
+
+		SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+		CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+
+fi
+	    ;;
+	SunOS-5*)
+	    # Note: If _REENTRANT isn't defined, then Solaris
+	    # won't define thread-safe library routines.
+
+
+$as_echo "#define _REENTRANT 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+
+	    SHLIB_CFLAGS="-KPIC"
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    if test "$do64bit" = yes; then :
+
+		arch=`isainfo`
+		if test "$arch" = "sparcv9 sparc"; then :
+
+		    if test "$GCC" = yes; then :
+
+			if test "`${CC} -dumpversion | awk -F. '{print $1}'`" -lt 3; then :
+
+			    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported with GCC < 3.2 on $system" >&2;}
+
+else
+
+			    do64bit_ok=yes
+			    CFLAGS="$CFLAGS -m64 -mcpu=v9"
+			    LDFLAGS="$LDFLAGS -m64 -mcpu=v9"
+			    SHLIB_CFLAGS="-fPIC"
+
+fi
+
+else
+
+			do64bit_ok=yes
+			if test "$do64bitVIS" = yes; then :
+
+			    CFLAGS="$CFLAGS -xarch=v9a"
+			    LDFLAGS_ARCH="-xarch=v9a"
+
+else
+
+			    CFLAGS="$CFLAGS -xarch=v9"
+			    LDFLAGS_ARCH="-xarch=v9"
+
+fi
+			# Solaris 64 uses this as well
+			#LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64"
+
+fi
+
+else
+  if test "$arch" = "amd64 i386"; then :
+
+		    if test "$GCC" = yes; then :
+
+			case $system in
+			    SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*)
+				do64bit_ok=yes
+				CFLAGS="$CFLAGS -m64"
+				LDFLAGS="$LDFLAGS -m64";;
+			    *)
+				{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported with GCC on $system" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported with GCC on $system" >&2;};;
+			esac
+
+else
+
+			do64bit_ok=yes
+			case $system in
+			    SunOS-5.1[1-9]*|SunOS-5.[2-9][0-9]*)
+				CFLAGS="$CFLAGS -m64"
+				LDFLAGS="$LDFLAGS -m64";;
+			    *)
+				CFLAGS="$CFLAGS -xarch=amd64"
+				LDFLAGS="$LDFLAGS -xarch=amd64";;
+			esac
+
+fi
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit mode not supported for $arch" >&5
+$as_echo "$as_me: WARNING: 64bit mode not supported for $arch" >&2;}
+fi
+fi
+
+fi
+
+	    SHLIB_SUFFIX=".so"
+	    if test "$GCC" = yes; then :
+
+		SHLIB_LD='${CC} -shared'
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+		if test "$do64bit_ok" = yes; then :
+
+		    if test "$arch" = "sparcv9 sparc"; then :
+
+			# We need to specify -static-libgcc or we need to
+			# add the path to the sparv9 libgcc.
+			# JH: static-libgcc is necessary for core Tcl, but may
+			# not be necessary for extensions.
+			SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc"
+			# for finding sparcv9 libgcc, get the regular libgcc
+			# path, remove so name and append 'sparcv9'
+			#v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..."
+			#CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir"
+
+else
+  if test "$arch" = "amd64 i386"; then :
+
+			# JH: static-libgcc is necessary for core Tcl, but may
+			# not be necessary for extensions.
+			SHLIB_LD="$SHLIB_LD -m64 -static-libgcc"
+
+fi
+fi
+
+fi
+
+else
+
+		case $system in
+		    SunOS-5.[1-9][0-9]*)
+			# TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+			SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';;
+		    *)
+			SHLIB_LD='/usr/ccs/bin/ld -G -z text';;
+		esac
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+
+fi
+	    ;;
+    esac
+
+    if test "$do64bit" = yes -a "$do64bit_ok" = no; then :
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 64bit support being disabled -- don't know magic for this platform" >&5
+$as_echo "$as_me: WARNING: 64bit support being disabled -- don't know magic for this platform" >&2;}
+
+fi
+
+
+
+    # Add in the arch flags late to ensure it wasn't removed.
+    # Not necessary in TEA, but this is aligned with core
+    LDFLAGS="$LDFLAGS $LDFLAGS_ARCH"
+
+    # If we're running gcc, then change the C flags for compiling shared
+    # libraries to the right flags for gcc, instead of those for the
+    # standard manufacturer compiler.
+
+    if test "$GCC" = yes; then :
+
+	case $system in
+	    AIX-*) ;;
+	    BSD/OS*) ;;
+	    CYGWIN_*) ;;
+	    IRIX*) ;;
+	    NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
+	    Darwin-*) ;;
+	    SCO_SV-3.2*) ;;
+	    windows) ;;
+	    *) SHLIB_CFLAGS="-fPIC" ;;
+	esac
+fi
+
+    if test "$tcl_cv_cc_visibility_hidden" != yes; then :
+
+
+$as_echo "#define MODULE_SCOPE extern" >>confdefs.h
+
+
+$as_echo "#define NO_VIZ /**/" >>confdefs.h
+
+
+fi
+
+    if test "$SHARED_LIB_SUFFIX" = ""; then :
+
+	# TEA specific: use PACKAGE_VERSION instead of VERSION
+	SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'
+fi
+    if test "$UNSHARED_LIB_SUFFIX" = ""; then :
+
+	# TEA specific: use PACKAGE_VERSION instead of VERSION
+	UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+    # These must be called after we do the basic CFLAGS checks and
+    # verify any possible 64-bit or similar switches are necessary
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for required early compiler flags" >&5
+$as_echo_n "checking for required early compiler flags... " >&6; }
+    tcl_flags=""
+
+    if test "${tcl_cv_flag__isoc99_source+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+int
+main ()
+{
+char *p = (char *)strtoll; char *q = (char *)strtoull;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__isoc99_source=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _ISOC99_SOURCE 1
+#include <stdlib.h>
+int
+main ()
+{
+char *p = (char *)strtoll; char *q = (char *)strtoull;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__isoc99_source=yes
+else
+  tcl_cv_flag__isoc99_source=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+    if test "x${tcl_cv_flag__isoc99_source}" = "xyes" ; then
+
+$as_echo "#define _ISOC99_SOURCE 1" >>confdefs.h
+
+	tcl_flags="$tcl_flags _ISOC99_SOURCE"
+    fi
+
+
+    if test "${tcl_cv_flag__largefile64_source+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/stat.h>
+int
+main ()
+{
+struct stat64 buf; int i = stat64("/", &buf);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__largefile64_source=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _LARGEFILE64_SOURCE 1
+#include <sys/stat.h>
+int
+main ()
+{
+struct stat64 buf; int i = stat64("/", &buf);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__largefile64_source=yes
+else
+  tcl_cv_flag__largefile64_source=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+    if test "x${tcl_cv_flag__largefile64_source}" = "xyes" ; then
+
+$as_echo "#define _LARGEFILE64_SOURCE 1" >>confdefs.h
+
+	tcl_flags="$tcl_flags _LARGEFILE64_SOURCE"
+    fi
+
+
+    if test "${tcl_cv_flag__largefile_source64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/stat.h>
+int
+main ()
+{
+char *p = (char *)open64;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__largefile_source64=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _LARGEFILE_SOURCE64 1
+#include <sys/stat.h>
+int
+main ()
+{
+char *p = (char *)open64;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_flag__largefile_source64=yes
+else
+  tcl_cv_flag__largefile_source64=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+    if test "x${tcl_cv_flag__largefile_source64}" = "xyes" ; then
+
+$as_echo "#define _LARGEFILE_SOURCE64 1" >>confdefs.h
+
+	tcl_flags="$tcl_flags _LARGEFILE_SOURCE64"
+    fi
+
+    if test "x${tcl_flags}" = "x" ; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+    else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_flags}" >&5
+$as_echo "${tcl_flags}" >&6; }
+    fi
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit integer type" >&5
+$as_echo_n "checking for 64-bit integer type... " >&6; }
+    if test "${tcl_cv_type_64bit+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	tcl_cv_type_64bit=none
+	# See if the compiler knows natively about __int64
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+__int64 value = (__int64) 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_type_64bit=__int64
+else
+  tcl_type_64bit="long long"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	# See if we should use long anyway  Note that we substitute in the
+	# type that is our current guess for a 64-bit type inside this check
+	# program, so it should be modified only carefully...
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+switch (0) {
+            case 1: case (sizeof(${tcl_type_64bit})==sizeof(long)): ;
+        }
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_type_64bit=${tcl_type_64bit}
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+    if test "${tcl_cv_type_64bit}" = none ; then
+
+$as_echo "#define TCL_WIDE_INT_IS_LONG 1" >>confdefs.h
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using long" >&5
+$as_echo "using long" >&6; }
+    elif test "${tcl_cv_type_64bit}" = "__int64" \
+		-a "${TEA_PLATFORM}" = "windows" ; then
+	# TEA specific: We actually want to use the default tcl.h checks in
+	# this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER*
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using Tcl header defaults" >&5
+$as_echo "using Tcl header defaults" >&6; }
+    else
+
+cat >>confdefs.h <<_ACEOF
+#define TCL_WIDE_INT_TYPE ${tcl_cv_type_64bit}
+_ACEOF
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${tcl_cv_type_64bit}" >&5
+$as_echo "${tcl_cv_type_64bit}" >&6; }
+
+	# Now check for auxiliary declarations
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct dirent64" >&5
+$as_echo_n "checking for struct dirent64... " >&6; }
+if test "${tcl_cv_struct_dirent64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/dirent.h>
+int
+main ()
+{
+struct dirent64 p;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_struct_dirent64=yes
+else
+  tcl_cv_struct_dirent64=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_dirent64" >&5
+$as_echo "$tcl_cv_struct_dirent64" >&6; }
+	if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
+
+$as_echo "#define HAVE_STRUCT_DIRENT64 1" >>confdefs.h
+
+	fi
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct stat64" >&5
+$as_echo_n "checking for struct stat64... " >&6; }
+if test "${tcl_cv_struct_stat64+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/stat.h>
+int
+main ()
+{
+struct stat64 p;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_struct_stat64=yes
+else
+  tcl_cv_struct_stat64=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_struct_stat64" >&5
+$as_echo "$tcl_cv_struct_stat64" >&6; }
+	if test "x${tcl_cv_struct_stat64}" = "xyes" ; then
+
+$as_echo "#define HAVE_STRUCT_STAT64 1" >>confdefs.h
+
+	fi
+
+	for ac_func in open64 lseek64
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+eval as_val=\$$as_ac_var
+   if test "x$as_val" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for off64_t" >&5
+$as_echo_n "checking for off64_t... " >&6; }
+	if test "${tcl_cv_type_off64_t+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+	    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+int
+main ()
+{
+off64_t offset;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  tcl_cv_type_off64_t=yes
+else
+  tcl_cv_type_off64_t=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+			if test "x${tcl_cv_type_off64_t}" = "xyes" && \
+	        test "x${ac_cv_func_lseek64}" = "xyes" && \
+	        test "x${ac_cv_func_open64}" = "xyes" ; then
+
+$as_echo "#define HAVE_TYPE_OFF64_T 1" >>confdefs.h
+
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	else
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	fi
+    fi
+
+
+
+#--------------------------------------------------------------------
+# Set the default compiler switches based on the --enable-symbols option.
+#--------------------------------------------------------------------
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for build with symbols" >&5
+$as_echo_n "checking for build with symbols... " >&6; }
+    # Check whether --enable-symbols was given.
+if test "${enable_symbols+set}" = set; then :
+  enableval=$enable_symbols; tcl_ok=$enableval
+else
+  tcl_ok=no
+fi
+
+    DBGX=""
+    if test "$tcl_ok" = "no"; then
+	CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE}"
+	LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    else
+	CFLAGS_DEFAULT="${CFLAGS_DEBUG}"
+	LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}"
+	if test "$tcl_ok" = "yes"; then
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (standard debugging)" >&5
+$as_echo "yes (standard debugging)" >&6; }
+	fi
+    fi
+    # TEA specific:
+    if test "${TEA_PLATFORM}" != "windows" ; then
+	LDFLAGS_DEFAULT="${LDFLAGS}"
+    fi
+
+
+
+
+    if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then
+
+$as_echo "#define TCL_MEM_DEBUG 1" >>confdefs.h
+
+    fi
+
+    if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then
+	if test "$tcl_ok" = "all"; then
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled symbols mem debugging" >&5
+$as_echo "enabled symbols mem debugging" >&6; }
+	else
+	    { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled $tcl_ok debugging" >&5
+$as_echo "enabled $tcl_ok debugging" >&6; }
+	fi
+    fi
+
+
+#--------------------------------------------------------------------
+# Everyone should be linking against the Tcl stub library.  If you
+# can't for some reason, remove this definition.  If you aren't using
+# stubs, you also need to modify the SHLIB_LD_LIBS setting below to
+# link against the non-stubbed Tcl library.  Add Tk too if necessary.
+#--------------------------------------------------------------------
+
+
+$as_echo "#define USE_TCL_STUBS 1" >>confdefs.h
+
+#AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
+
+#--------------------------------------------------------------------
+# This macro generates a line to use when building a library.  It
+# depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS,
+# and TEA_LOAD_TCLCONFIG macros above.
+#--------------------------------------------------------------------
+
+
+    if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
+	MAKE_STATIC_LIB="\${STLIB_LD} -out:\$@ \$(PKG_OBJECTS)"
+	MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\$@ \$(PKG_OBJECTS)"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+print("manifest needed")
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "manifest needed" >/dev/null 2>&1; then :
+
+	# Could do a CHECK_PROG for mt, but should always be with MSVC8+
+	VC_MANIFEST_EMBED_DLL="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;2 ; fi"
+	VC_MANIFEST_EMBED_EXE="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest -outputresource:\$@\;1 ; fi"
+	MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}"
+
+    CLEANFILES="$CLEANFILES *.manifest"
+
+
+fi
+rm -f conftest*
+
+	MAKE_STUB_LIB="\${STLIB_LD} -out:\$@ \$(PKG_STUB_OBJECTS)"
+    else
+	MAKE_STATIC_LIB="\${STLIB_LD} \$@ \$(PKG_OBJECTS)"
+	MAKE_SHARED_LIB="\${SHLIB_LD} -o \$@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
+	MAKE_STUB_LIB="\${STLIB_LD} \$@ \$(PKG_STUB_OBJECTS)"
+    fi
+
+    if test "${SHARED_BUILD}" = "1" ; then
+	MAKE_LIB="${MAKE_SHARED_LIB} "
+    else
+	MAKE_LIB="${MAKE_STATIC_LIB} "
+    fi
+
+    #--------------------------------------------------------------------
+    # Shared libraries and static libraries have different names.
+    # Use the double eval to make sure any variables in the suffix is
+    # substituted. (@@@ Might not be necessary anymore)
+    #--------------------------------------------------------------------
+
+    if test "${TEA_PLATFORM}" = "windows" ; then
+	if test "${SHARED_BUILD}" = "1" ; then
+	    # We force the unresolved linking of symbols that are really in
+	    # the private libraries of Tcl and Tk.
+	    SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
+	    if test x"${TK_BIN_DIR}" != x ; then
+		SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\""
+	    fi
+	    eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+	else
+	    eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+	fi
+	# Some packages build their own stubs libraries
+	eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+	if test "$GCC" = "yes"; then
+	    PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
+	fi
+	# These aren't needed on Windows (either MSVC or gcc)
+	RANLIB=:
+	RANLIB_STUB=:
+    else
+	RANLIB_STUB="${RANLIB}"
+	if test "${SHARED_BUILD}" = "1" ; then
+	    SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}"
+	    if test x"${TK_BIN_DIR}" != x ; then
+		SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
+	    fi
+	    eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+	    RANLIB=:
+	else
+	    eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+	fi
+	# Some packages build their own stubs libraries
+	eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+    fi
+
+    # These are escaped so that only CFLAGS is picked up at configure time.
+    # The other values will be substituted at make time.
+    CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}"
+    if test "${SHARED_BUILD}" = "1" ; then
+	CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}"
+    fi
+
+
+
+
+
+
+
+
+
+
+#--------------------------------------------------------------------
+# Determine the name of the tclsh and/or wish executables in the
+# Tcl and Tk build directories or the location they were installed
+# into. These paths are used to support running test cases only,
+# the Makefile should not be making use of these paths to generate
+# a pkgIndex.tcl file or anything else at extension build time.
+#--------------------------------------------------------------------
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tclsh" >&5
+$as_echo_n "checking for tclsh... " >&6; }
+    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+        # tclConfig.sh is in Tcl build directory
+        if test "${TEA_PLATFORM}" = "windows"; then
+            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+        else
+            TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
+        fi
+    else
+        # tclConfig.sh is in install location
+        if test "${TEA_PLATFORM}" = "windows"; then
+            TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+        else
+            TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}"
+        fi
+        list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
+              `ls -d ${TCL_BIN_DIR}/..     2>/dev/null` \
+              `ls -d ${TCL_PREFIX}/bin     2>/dev/null`"
+        for i in $list ; do
+            if test -f "$i/${TCLSH_PROG}" ; then
+                REAL_TCL_BIN_DIR="`cd "$i"; pwd`/"
+                break
+            fi
+        done
+        TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}"
+    fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${TCLSH_PROG}" >&5
+$as_echo "${TCLSH_PROG}" >&6; }
+
+
+#TEA_PROG_WISH
+
+#--------------------------------------------------------------------
+# Finally, substitute all of the various values into the Makefile.
+# You may alternatively have a special pkgIndex.tcl.in or other files
+# which require substituting th AC variables in.  Include these here.
+#--------------------------------------------------------------------
+
+ac_config_files="$ac_config_files Makefile pkgIndex.tcl"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[	 `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+	g
+	s/^\n//
+	s/\n/ /g
+	p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS=""
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error ERROR [LINENO LOG_FD]
+# ---------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with status $?, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$?; test $as_status -eq 0 && as_status=1
+  if test "$3"; then
+    as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+  fi
+  $as_echo "$as_me: error: $1" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+	test -d "$1/.";
+      else
+	case $1 in #(
+	-*)set "./$1";;
+	esac;
+	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+	???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by BLT $as_me 2.4, which was
+generated by GNU Autoconf 2.65.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+BLT config.status 2.4
+configured by $0, generated by GNU Autoconf 2.65,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2009 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h |  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "pkgIndex.tcl") CONFIG_FILES="$CONFIG_FILES pkgIndex.tcl" ;;
+
+  *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp=
+  trap 'exit_status=$?
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+  || as_fn_error "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[	 ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X "  :F $CONFIG_FILES      "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$tmp/stdin" \
+      || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+  || as_fn_error "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&2;}
+
+  rm -f "$tmp/stdin"
+  case $ac_file in
+  -) cat "$tmp/out" && rm -f "$tmp/out";;
+  *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit $?
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
Index: trunk/kitgen/8.x/blt/configure.in
===================================================================
--- trunk/kitgen/8.x/blt/configure.in	(revision 175)
+++ trunk/kitgen/8.x/blt/configure.in	(revision 175)
@@ -0,0 +1,185 @@
+#!/bin/bash -norc
+dnl	This file is an input file used by the GNU "autoconf" program to
+dnl	generate the file "configure", which is run during Tcl installation
+dnl	to configure the system for the local environment.
+#
+# RCS: @(#) $Id: configure.in,v 1.48 2008/11/05 00:13:00 hobbs Exp $
+
+#-----------------------------------------------------------------------
+# Sample configure.in for Tcl Extensions.  The only places you should
+# need to modify this file are marked by the string __CHANGE__
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# __CHANGE__
+# Set your package name and version numbers here.
+#
+# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
+# set as provided.  These will also be added as -D defs in your Makefile
+# so you can encode the package version directly into the source files.
+# This will also define a special symbol for Windows (BUILD_sample in
+# this case) so that we create the export library with the dll.
+#-----------------------------------------------------------------------
+
+AC_INIT([BLT], [2.4])
+
+#--------------------------------------------------------------------
+# Call TEA_INIT as the first TEA_ macro to set up initial vars.
+# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
+# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
+# --------------------------------------------------------------------
+
+TEA_INIT([3.9])
+
+AC_CONFIG_AUX_DIR(tclconfig)
+
+#--------------------------------------------------------------------
+# Load the tclConfig.sh file
+#--------------------------------------------------------------------
+
+TEA_PATH_TCLCONFIG
+TEA_LOAD_TCLCONFIG
+
+#--------------------------------------------------------------------
+# Load the tkConfig.sh file if necessary (Tk extension)
+#--------------------------------------------------------------------
+
+#TEA_PATH_TKCONFIG
+#TEA_LOAD_TKCONFIG
+
+#-----------------------------------------------------------------------
+# Handle the --prefix=... option by defaulting to what Tcl gave.
+# Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER.
+#-----------------------------------------------------------------------
+
+TEA_PREFIX
+
+#-----------------------------------------------------------------------
+# Standard compiler checks.
+# This sets up CC by using the CC env var, or looks for gcc otherwise.
+# This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create
+# the basic setup necessary to compile executables.
+#-----------------------------------------------------------------------
+
+TEA_SETUP_COMPILER
+
+#-----------------------------------------------------------------------
+# __CHANGE__
+# Specify the C source files to compile in TEA_ADD_SOURCES,
+# public headers that need to be installed in TEA_ADD_HEADERS,
+# stub library C source files to compile in TEA_ADD_STUB_SOURCES,
+# and runtime Tcl library files in TEA_ADD_TCL_SOURCES.
+# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
+# and PKG_TCL_SOURCES.
+#-----------------------------------------------------------------------
+
+TEA_ADD_SOURCES([bltAlloc.c bltArrayObj.c bltBind.c bltBitmap.c bltBusy.c bltChain.c bltColor.c bltConfig.c bltGrAxis.c bltGrBar.c bltGrElem.c bltGrGrid.c bltGrHairs.c bltGrLegd.c bltGrLine.c bltGrMarker.c bltGrMisc.c bltGrPen.c bltGrPs.c bltGraph.c bltHash.c bltImage.c bltInit.c bltList.c bltNsUtil.c bltParse.c bltPool.c bltPs.c bltSpline.c bltSwitch.c bltTable.c bltTabnotebook.c bltText.c bltTile.c bltObjConfig.c bltTree.h bltTreeView.h bltTreeViewEdit.c bltObjConfig.h bltTreeCmd.c bltTreeViewCmd.c bltTreeViewStyle.c bltTree.c bltTreeView.c bltTreeViewColumn.c bltUtil.c bltVecCmd.c bltVecObjCmd.c bltVector.c bltWindow.c])
+TEA_ADD_HEADERS([generic/blt.h  generic/bltHash.h  generic/bltPool.h  generic/bltVector.h])
+TEA_ADD_INCLUDES([-I. -I\"$(${CYGPATH} ${srcdir}/generic)\"])
+TEA_ADD_LIBS([])
+TEA_ADD_CFLAGS([])
+TEA_ADD_STUB_SOURCES([])
+TEA_ADD_TCL_SOURCES([library/graph.tcl library/tabnotebook.tcl library/treeview.cur library/treeview.xbm library/treeview.tcl library/treeview_m.xbm library/bltCanvEps.pro library/bltGraph.pro])
+
+#--------------------------------------------------------------------
+# __CHANGE__
+#
+# You can add more files to clean if your extension creates any extra
+# files by extending CLEANFILES.
+# Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure
+# and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var.
+#
+# A few miscellaneous platform-specific items:
+# TEA_ADD_* any platform specific compiler/build info here.
+#--------------------------------------------------------------------
+
+CLEANFILES="$CLEANFILES pkgIndex.tcl"
+if test "${TEA_PLATFORM}" = "windows" ; then
+    CLEANFILES="$CLEANFILES *.lib *.dll *.exp *.ilk *.pdb vc*.pch"
+    TEA_ADD_SOURCES([win/bltWinDraw.c win/bltWinImage.c win/bltWinPrnt.c win/bltWinUtil.c])
+    TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"])
+else
+    AC_DEFINE(NO_PRINTER, 1, [No printer support for unix systems])
+    TEA_ADD_SOURCES([unix/bltUnixImage.c])
+fi
+
+#--------------------------------------------------------------------
+# __CHANGE__
+# Choose which headers you need.  Extension authors should try very
+# hard to only rely on the Tcl public header files.  Internal headers
+# contain private data structures and are subject to change without
+# notice.
+# This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG
+#--------------------------------------------------------------------
+
+TEA_PUBLIC_TCL_HEADERS
+#TEA_PRIVATE_TCL_HEADERS
+
+#TEA_PUBLIC_TK_HEADERS
+#TEA_PRIVATE_TK_HEADERS
+#TEA_PATH_X
+
+#--------------------------------------------------------------------
+# Check whether --enable-threads or --disable-threads was given.
+# This auto-enables if Tcl was compiled threaded.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_THREADS
+
+#--------------------------------------------------------------------
+# The statement below defines a collection of symbols related to
+# building as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_SHARED
+
+#--------------------------------------------------------------------
+# This macro figures out what flags to use with the compiler/linker
+# when building shared/static debug/optimized objects.  This information
+# can be taken from the tclConfig.sh file, but this figures it all out.
+#--------------------------------------------------------------------
+
+TEA_CONFIG_CFLAGS
+
+#--------------------------------------------------------------------
+# Set the default compiler switches based on the --enable-symbols option.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_SYMBOLS
+
+#--------------------------------------------------------------------
+# Everyone should be linking against the Tcl stub library.  If you
+# can't for some reason, remove this definition.  If you aren't using
+# stubs, you also need to modify the SHLIB_LD_LIBS setting below to
+# link against the non-stubbed Tcl library.  Add Tk too if necessary.
+#--------------------------------------------------------------------
+
+AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs])
+#AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
+
+#--------------------------------------------------------------------
+# This macro generates a line to use when building a library.  It
+# depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS,
+# and TEA_LOAD_TCLCONFIG macros above.
+#--------------------------------------------------------------------
+
+TEA_MAKE_LIB
+
+#--------------------------------------------------------------------
+# Determine the name of the tclsh and/or wish executables in the
+# Tcl and Tk build directories or the location they were installed
+# into. These paths are used to support running test cases only,
+# the Makefile should not be making use of these paths to generate
+# a pkgIndex.tcl file or anything else at extension build time.
+#--------------------------------------------------------------------
+
+TEA_PROG_TCLSH
+#TEA_PROG_WISH
+
+#--------------------------------------------------------------------
+# Finally, substitute all of the various values into the Makefile.
+# You may alternatively have a special pkgIndex.tcl.in or other files
+# which require substituting th AC variables in.  Include these here.
+#--------------------------------------------------------------------
+
+AC_OUTPUT([Makefile pkgIndex.tcl])
Index: trunk/kitgen/8.x/blt/function_plotting.tcl
===================================================================
--- trunk/kitgen/8.x/blt/function_plotting.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/function_plotting.tcl	(revision 175)
@@ -0,0 +1,52 @@
+package require BLT
+namespace import ::blt::*
+
+proc compute args {
+puts $::a
+puts $::b
+puts $::c
+puts $::d
+#puts [vector expr {($::a * (::x ^ 3))  + ($::b * (::x ^ 2)) + ($::c * ::x) +  $::d}]
+puts [vector expr {::x}]
+
+}
+
+label .l -text "Function: ax^3 + bx^2 + cx + d"        ; table . .l 0,0
+
+graph .g -width 700 -height 600 -bd 2 -relief groove   ; table . .g 1,0 -fill both
+Blt_ZoomStack .g
+
+vector ::x
+::x seq -10 10 0.5
+vector ::y
+
+set items {label variable to from digits resolution tickinterval gridpos}
+set scale_cfg {
+       a ::a  -0.31  0.3 5 0.001 0.1   1,1
+       b ::b  -1.01  1.0 5 0.002 0.5   1,2
+       c ::c  -4.01  4.0 4 0.01  2.0   1,3
+       d ::d -10.01 10.0 3 0.1 5.0     1,4
+}
+
+foreach $items  $scale_cfg {
+       set $variable 1.0
+       trace variable $variable w compute
+
+       set w .sc$label
+       set label [ string totitle $label ]
+       scale $w -label $label \
+               -variable $variable -to $to -from $from \
+               -digits $digits -resolution $resolution \
+               -tickinterval $tickinterval \
+               -bd 2 -relief groove
+       table . $w $gridpos -fill y
+}
+
+# fill vector ::y
+compute
+
+.g element create Function -xdata ::x -ydata ::y -pixels 3
+.g axis configure y -min -20 -max 20
+.g grid configure -hide no -dashes { 2 2 }
+
+# ready
Index: trunk/kitgen/8.x/blt/generic/blt.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/blt.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/blt.h	(revision 175)
@@ -0,0 +1,70 @@
+/*
+ * blt.h --
+ *
+ * Copyright 1991-1998 by Bell Labs Innovations for Lucent
+ * Technologies.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_H
+#define _BLT_H
+
+#define BLT_MAJOR_VERSION 	2
+#define BLT_MINOR_VERSION 	4
+#define BLT_VERSION		"2.4"
+#define BLT_PATCH_LEVEL		"2.4z"
+#define BLT_RELEASE_SERIAL	0
+
+#include <tcl.h>
+
+#ifndef EXPORT
+#define EXPORT
+#endif
+
+#undef EXTERN
+
+#ifdef __cplusplus
+#   define EXTERN extern "C" EXPORT
+#else
+#   define EXTERN extern EXPORT
+#endif
+
+#ifndef _ANSI_ARGS_
+#   define _ANSI_ARGS_(x)       ()
+#endif
+
+#include "bltVector.h"
+#include "bltHash.h"
+
+typedef char *Blt_Uid;
+
+EXTERN Blt_Uid Blt_GetUid _ANSI_ARGS_((char *string));
+EXTERN void Blt_FreeUid _ANSI_ARGS_((Blt_Uid uid));
+EXTERN Blt_Uid Blt_FindUid _ANSI_ARGS_((char *string));
+
+#if (TCL_MAJOR_VERSION >= 8)
+EXTERN int Blt_GetArrayFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, Blt_HashTable **tablePtrPtr));
+EXTERN Tcl_Obj *Blt_NewArrayObj _ANSI_ARGS_((int objc, Tcl_Obj *objv[]));
+EXTERN void Blt_RegisterArrayObj _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Blt_IsArrayObj _ANSI_ARGS_((Tcl_Obj *obj));
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+#endif /*_BLT_H*/
Index: trunk/kitgen/8.x/blt/generic/bltAlloc.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltAlloc.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltAlloc.c	(revision 175)
@@ -0,0 +1,69 @@
+#include "bltInt.h"
+
+#ifndef linux
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+#endif
+
+/*
+ * Blt_MallocProcPtr, Blt_FreeProcPtr --
+ *
+ *	These global variables allow you to override the default
+ *	memory allocation/deallocation routines, simply by setting the
+ *	pointers to your own C functions.  By default, we try to use
+ *	the same memory allocation scheme that Tcl is using: generally
+ *	that's Tcl_Alloc and Tcl_Free.
+ */
+
+EXTERN Blt_MallocProc TclpAlloc;
+EXTERN Blt_FreeProc TclpFree;
+EXTERN Blt_ReallocProc TclpRealloc;
+
+Blt_MallocProc *Blt_MallocProcPtr = TclpAlloc;
+Blt_FreeProc *Blt_FreeProcPtr = TclpFree;
+Blt_ReallocProc *Blt_ReallocProcPtr = TclpRealloc;
+
+void *
+Blt_Calloc(nElems, sizeOfElem)
+    unsigned int nElems; 
+    size_t sizeOfElem;
+{
+    char *ptr;
+    size_t size;
+
+    size = nElems * sizeOfElem;
+    ptr = Blt_Malloc(size);
+    if (ptr != NULL) {
+	memset(ptr, 0, size);
+    }
+    return ptr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_Strdup --
+ *
+ *      Create a copy of the string from heap storage.
+ *
+ * Results:
+ *      Returns a pointer to the need string copy.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_Strdup(string)
+    CONST char *string;
+{
+    size_t size;
+    char *ptr;
+
+    size = strlen(string) + 1;
+    ptr = Blt_Malloc(size * sizeof(char));
+    if (ptr != NULL) {
+	strcpy(ptr, string);
+    }
+    return ptr;
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltArrayObj.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltArrayObj.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltArrayObj.c	(revision 175)
@@ -0,0 +1,247 @@
+
+/*
+ * bltArrayObj.c --
+ *
+ * Copyright 2000 Silicon Metrics, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The array Tcl object was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_ARRAY
+#include "bltHash.h"
+
+static Tcl_DupInternalRepProc DupArrayInternalRep;
+static Tcl_FreeInternalRepProc FreeArrayInternalRep;
+static Tcl_UpdateStringProc UpdateStringOfArray;
+static Tcl_SetFromAnyProc SetArrayFromAny;
+
+static Tcl_ObjType arrayObjType = {
+    "array",
+    FreeArrayInternalRep,	/* Called when an object is freed. */
+    DupArrayInternalRep,	/* Copies an internal representation 
+				 * from one object to another. */
+    UpdateStringOfArray,	/* Creates string representation from
+				 * an object's internal representation. */
+    SetArrayFromAny,		/* Creates valid internal representation 
+				 * from an object's string representation. */
+};
+
+static int
+SetArrayFromAny(interp, objPtr)
+    Tcl_Interp *interp;
+    Tcl_Obj *objPtr;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    Tcl_Obj *elemObjPtr;
+    Tcl_ObjType *oldTypePtr = objPtr->typePtr;
+    char **elemArr;
+    char *string;
+    int isNew;
+    int nElem;
+    register int i;
+
+    if (objPtr->typePtr == &arrayObjType) {
+	return TCL_OK;
+    }
+    /*
+     * Get the string representation. Make it up-to-date if necessary.
+     */
+    string = Tcl_GetString(objPtr);
+    if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+    assert(tablePtr);
+    Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
+    for (i = 0; i < nElem; i += 2) {
+	hPtr = Blt_CreateHashEntry(tablePtr, elemArr[i], &isNew);
+	elemObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
+	Blt_SetHashValue(hPtr, elemObjPtr);
+
+	/* Make sure we increment the reference count */
+	Tcl_IncrRefCount(elemObjPtr);
+    }
+    
+    if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+	oldTypePtr->freeIntRepProc(objPtr);
+    }
+    objPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
+    objPtr->typePtr = &arrayObjType;
+    Blt_Free(elemArr);
+
+    return TCL_OK;
+}
+
+static void
+DupArrayInternalRep(srcPtr, destPtr)
+    Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */
+    Tcl_Obj *destPtr;		/* Object with internal rep to set. */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_HashTable *srcTablePtr, *destTablePtr;
+    Tcl_Obj *valueObjPtr;
+    char *key;
+    int isNew;
+
+    srcTablePtr = (Blt_HashTable *)srcPtr->internalRep.otherValuePtr;
+    destTablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+    assert(destTablePtr);
+    Blt_InitHashTable(destTablePtr, BLT_STRING_KEYS);
+    for (hPtr = Blt_FirstHashEntry(srcTablePtr, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	key = Blt_GetHashKey(srcTablePtr, hPtr);
+	Blt_CreateHashEntry(destTablePtr, key, &isNew);
+	valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+	Blt_SetHashValue(hPtr, valueObjPtr);
+
+	/* Make sure we increment the reference count now that both
+	 * array objects are using the same elements. */
+	Tcl_IncrRefCount(valueObjPtr);
+    }
+    Tcl_InvalidateStringRep(destPtr);
+    destPtr->internalRep.otherValuePtr = (VOID *)destTablePtr;
+    destPtr->typePtr = &arrayObjType;
+}
+
+static void
+UpdateStringOfArray(objPtr)
+    Tcl_Obj *objPtr;		/* Array object whose string rep to update. */
+{
+    Tcl_DString dString;
+    Blt_HashTable *tablePtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Tcl_Obj *elemObjPtr;
+
+    tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+    Tcl_DStringInit(&dString);
+    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+	Tcl_DStringAppendElement(&dString, Blt_GetHashKey(tablePtr, hPtr));
+	Tcl_DStringAppendElement(&dString, Tcl_GetString(elemObjPtr));
+    }
+    objPtr->bytes = Blt_Strdup(Tcl_DStringValue(&dString));
+    objPtr->length = strlen(Tcl_DStringValue(&dString));
+    Tcl_DStringFree(&dString);
+}
+
+static void
+FreeArrayInternalRep(objPtr)
+    Tcl_Obj *objPtr;		/* Array object to release. */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_HashTable *tablePtr;
+    Tcl_Obj *elemObjPtr;
+    
+    Tcl_InvalidateStringRep(objPtr);
+    tablePtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	elemObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+	Tcl_DecrRefCount(elemObjPtr);
+    }
+    Blt_DeleteHashTable(tablePtr);
+    Blt_Free(tablePtr);
+}
+
+int
+Blt_GetArrayFromObj(interp, objPtr, tablePtrPtr) 
+    Tcl_Interp *interp;
+    Tcl_Obj *objPtr;
+    Blt_HashTable **tablePtrPtr;
+{
+    if (objPtr->typePtr == &arrayObjType) {
+	*tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+	return TCL_OK;
+    }
+    if (SetArrayFromAny(interp, objPtr) == TCL_OK) {
+	*tablePtrPtr = (Blt_HashTable *)objPtr->internalRep.otherValuePtr;
+	return TCL_OK;
+    }
+    return TCL_ERROR;
+}
+    
+Tcl_Obj *
+Blt_NewArrayObj(objc, objv) 
+    int objc;
+    Tcl_Obj *objv[];
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    Tcl_Obj *arrayObjPtr, *objPtr;
+    int isNew;
+    register int i;
+
+    tablePtr = Blt_Malloc(sizeof(Blt_HashTable));
+    assert(tablePtr);
+    Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
+
+    for (i = 0; i < objc; i += 2) {
+	hPtr = Blt_CreateHashEntry(tablePtr, Tcl_GetString(objv[i]), &isNew);
+	if ((i + 1) == objc) {
+	    objPtr = bltEmptyStringObjPtr;
+	} else {
+	    objPtr = objv[i+1];
+	}
+	Tcl_IncrRefCount(objPtr);
+	if (!isNew) {
+	    Tcl_Obj *oldObjPtr;
+
+	    oldObjPtr = Blt_GetHashValue(hPtr);
+	    Tcl_DecrRefCount(oldObjPtr);
+	}
+	Blt_SetHashValue(hPtr, objPtr);
+    }
+    arrayObjPtr = Tcl_NewObj(); 
+    /* 
+     * Reference counts for entry objects are initialized to 0. They
+     * are incremented as they are inserted into the tree via the 
+     * Blt_TreeSetValue call. 
+     */
+    arrayObjPtr->refCount = 0;	
+    arrayObjPtr->internalRep.otherValuePtr = (VOID *)tablePtr;
+    arrayObjPtr->bytes = NULL;
+    arrayObjPtr->length = 0; 
+    arrayObjPtr->typePtr = &arrayObjType;
+    return arrayObjPtr;
+}
+
+int
+Blt_IsArrayObj(objPtr)
+    Tcl_Obj *objPtr;
+{
+    return (objPtr->typePtr == &arrayObjType);
+}
+
+/*ARGSUSED*/
+void
+Blt_RegisterArrayObj(interp)
+    Tcl_Interp *interp;		/* Not used. */
+{
+    Tcl_RegisterObjType(&arrayObjType);
+}
+#endif /* NO_ARRAY */
Index: trunk/kitgen/8.x/blt/generic/bltBind.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltBind.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltBind.c	(revision 175)
@@ -0,0 +1,667 @@
+/*
+ * bltBind.c --
+ *
+ *	This module implements object binding procedures for the BLT
+ *	toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltBind.h"
+
+static Tk_EventProc BindProc;
+
+/*
+ * Binding table procedures.
+ */
+#define REPICK_IN_PROGRESS (1<<0)
+#define LEFT_GRABBED_ITEM  (1<<1)
+
+#define ALL_BUTTONS_MASK \
+	(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)
+
+#ifndef VirtualEventMask
+#define VirtualEventMask    (1L << 30)
+#endif
+
+#define ALL_VALID_EVENTS_MASK \
+	(ButtonMotionMask | Button1MotionMask | Button2MotionMask | \
+	 Button3MotionMask | Button4MotionMask | Button5MotionMask | \
+	 ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
+	 LeaveWindowMask | KeyPressMask | KeyReleaseMask | \
+	 PointerMotionMask | VirtualEventMask)
+
+static int buttonMasks[] =
+{
+    0,				/* No buttons pressed */
+    Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
+};
+
+
+/*
+ * How to make drag&drop work?
+ *
+ *	Right now we generate pseudo <Enter> <Leave> events within 
+ *	button grab on an object.  They're marked NotifyVirtual instead 
+ *	of NotifyAncestor.  A better solution: generate new-style 
+ *	virtual <<DragEnter>> <<DragMotion>> <<DragLeave>> events.  
+ *	These virtual events don't have to exist as "real" event 
+ *	sequences, like virtual events do now.  
+ */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoEvent --
+ *
+ *	This procedure is called to invoke binding processing
+ *	for a new event that is associated with the current item
+ *	for a legend.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Depends on the bindings for the legend.  A binding script
+ *	could delete an entry, so callers should protect themselves
+ *	with Tcl_Preserve and Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+DoEvent(bindPtr, eventPtr, item, context)
+    struct Blt_BindTableStruct *bindPtr; /* Binding information for widget in
+				 * which event occurred. */
+    XEvent *eventPtr;		/* Real or simulated X event that
+				 * is to be processed. */
+    ClientData item;		/* Item picked. */
+    ClientData context;		/* Context of item.  */
+{
+    Blt_List bindIds;
+    int nIds;
+
+    if ((bindPtr->tkwin == NULL) || (bindPtr->bindingTable == NULL)) {
+	return;
+    }
+    if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
+	item = bindPtr->focusItem;
+	context = bindPtr->focusContext;
+    }
+    if (item == NULL) {
+	return;
+    }
+    /*
+     * Invoke the binding system.
+     */
+
+    bindIds = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+    if (bindPtr->tagProc == NULL) {
+	Blt_ListAppend(bindIds, (char *)Tk_GetUid("all"), 0);
+	Blt_ListAppend(bindIds, (char *)item, 0);
+    } else {
+	(*bindPtr->tagProc) (bindPtr, item, context, bindIds);
+    }
+    nIds = Blt_ListGetLength(bindIds);
+    if (nIds > 0) {
+	ClientData *idArray;
+	ClientData tags[32];
+	register Blt_ListNode node;
+	
+	idArray = tags;
+	if (nIds >= 32) {
+	    idArray = Blt_Malloc(sizeof(ClientData) * nIds);
+	    
+	} 
+	nIds = 0;
+	for (node = Blt_ListFirstNode(bindIds); node != NULL;
+	     node = Blt_ListNextNode(node)) {
+	    idArray[nIds++] = (ClientData)Blt_ListGetKey(node);
+	}
+	Tk_BindEvent(bindPtr->bindingTable, eventPtr, bindPtr->tkwin, nIds, 
+		idArray);
+	if (nIds >= 32) {
+	    Blt_Free(idArray);
+	}
+    }
+    Blt_ListDestroy(bindIds);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PickCurrentItem --
+ *
+ *	Find the topmost item in a legend that contains a given
+ *	location and mark the the current item.  If the current
+ *	item has changed, generate a fake exit event on the old
+ *	current item and a fake enter event on the new current
+ *	item.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The current item may change.  If it does, then the commands
+ *	associated with item entry and exit could do just about 
+ *	anything.  A binding script could delete the legend, so 
+ *	callers should protect themselves with Tcl_Preserve and 
+ *	Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+PickCurrentItem(bindPtr, eventPtr)
+    struct Blt_BindTableStruct *bindPtr;	/* Binding table information. */
+    XEvent *eventPtr;		/* Event describing location of
+				 * mouse cursor.  Must be EnterWindow,
+				 * LeaveWindow, ButtonRelease, or
+				 * MotionNotify. */
+{
+    int buttonDown;
+    ClientData newItem;
+    ClientData newContext;
+
+    /*
+     * Check whether or not a button is down.  If so, we'll log entry
+     * and exit into and out of the current item, but not entry into
+     * any other item.  This implements a form of grabbing equivalent
+     * to what the X server does for windows.
+     */
+    buttonDown = (bindPtr->state & ALL_BUTTONS_MASK);
+    if (!buttonDown) {
+	bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+    }
+    /*
+     * Save information about this event in the widget.  The event in
+     * the widget is used for two purposes:
+     *
+     * 1. Event bindings: if the current item changes, fake events are
+     *    generated to allow item-enter and item-leave bindings to trigger.
+     * 2. Reselection: if the current item gets deleted, can use the
+     *    saved event to find a new current item.
+     * Translate MotionNotify events into EnterNotify events, since that's
+     * what gets reported to item handlers.
+     */
+
+    if (eventPtr != &bindPtr->pickEvent) {
+	if ((eventPtr->type == MotionNotify) ||
+	    (eventPtr->type == ButtonRelease)) {
+	    bindPtr->pickEvent.xcrossing.type = EnterNotify;
+	    bindPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
+	    bindPtr->pickEvent.xcrossing.send_event =
+		eventPtr->xmotion.send_event;
+	    bindPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
+	    bindPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
+	    bindPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
+	    bindPtr->pickEvent.xcrossing.subwindow = None;
+	    bindPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
+	    bindPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
+	    bindPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
+	    bindPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
+	    bindPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
+	    bindPtr->pickEvent.xcrossing.mode = NotifyNormal;
+	    bindPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
+	    bindPtr->pickEvent.xcrossing.same_screen
+		= eventPtr->xmotion.same_screen;
+	    bindPtr->pickEvent.xcrossing.focus = False;
+	    bindPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
+	} else {
+	    bindPtr->pickEvent = *eventPtr;
+	}
+    }
+    bindPtr->activePick = TRUE;
+
+    /*
+     * If this is a recursive call (there's already a partially completed
+     * call pending on the stack;  it's in the middle of processing a
+     * Leave event handler for the old current item) then just return;
+     * the pending call will do everything that's needed.
+     */
+    if (bindPtr->flags & REPICK_IN_PROGRESS) {
+	return;
+    }
+    /*
+     * A LeaveNotify event automatically means that there's no current
+     * item, so the check for closest item can be skipped.
+     */
+    newContext = NULL;
+    if (bindPtr->pickEvent.type != LeaveNotify) {
+	int x, y;
+
+	x = bindPtr->pickEvent.xcrossing.x;
+	y = bindPtr->pickEvent.xcrossing.y;
+	newItem = (*bindPtr->pickProc) (bindPtr->clientData, x, y, &newContext);
+    } else {
+	newItem = NULL;
+    }
+    if (((newItem == bindPtr->currentItem) && 
+	 (newContext == bindPtr->currentContext)) && 
+	(!(bindPtr->flags & LEFT_GRABBED_ITEM))) {
+	/*
+	 * Nothing to do:  the current item hasn't changed.
+	 */
+	return;
+    }
+#ifndef FULLY_SIMULATE_GRAB
+    if (((newItem != bindPtr->currentItem) || 
+	 (newContext != bindPtr->currentContext)) && 
+	(buttonDown)) {
+	bindPtr->flags |= LEFT_GRABBED_ITEM;
+	return;
+    }
+#endif
+    /*
+     * Simulate a LeaveNotify event on the previous current item and
+     * an EnterNotify event on the new current item.  Remove the "current"
+     * tag from the previous current item and place it on the new current
+     * item.
+     */
+    if ((bindPtr->currentItem != NULL) &&
+	((newItem != bindPtr->currentItem) || 
+	 (newContext != bindPtr->currentContext)) && 
+	!(bindPtr->flags & LEFT_GRABBED_ITEM)) {
+	XEvent event;
+
+	event = bindPtr->pickEvent;
+	event.type = LeaveNotify;
+	/*
+	 * If the event's detail happens to be NotifyInferior the
+	 * binding mechanism will discard the event.  To be consistent,
+	 * always use NotifyAncestor.
+	 */
+	event.xcrossing.detail = NotifyAncestor;
+
+	bindPtr->flags |= REPICK_IN_PROGRESS;
+	DoEvent(bindPtr, &event, bindPtr->currentItem, bindPtr->currentContext);
+	bindPtr->flags &= ~REPICK_IN_PROGRESS;
+
+	/*
+	 * Note:  during DoEvent above, it's possible that
+	 * bindPtr->newItem got reset to NULL because the
+	 * item was deleted.
+	 */
+    }
+    if (((newItem != bindPtr->currentItem) || 
+	 (newContext != bindPtr->currentContext)) && 
+	(buttonDown)) {
+	XEvent event;
+
+	bindPtr->flags |= LEFT_GRABBED_ITEM;
+	event = bindPtr->pickEvent;
+	if ((newItem != bindPtr->newItem) || 
+	    (newContext != bindPtr->newContext)) {
+	    ClientData savedItem;
+	    ClientData savedContext;
+
+	    /*
+	     * Generate <Enter> and <Leave> events for objects during
+	     * button grabs.  This isn't standard. But for example, it
+	     * allows one to provide balloon help on the individual
+	     * entries of the Hierbox widget.
+	     */
+	    savedItem = bindPtr->currentItem;
+	    savedContext = bindPtr->currentContext;
+	    if (bindPtr->newItem != NULL) {
+		event.type = LeaveNotify;
+		event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+		bindPtr->currentItem = bindPtr->newItem;
+		DoEvent(bindPtr, &event, bindPtr->newItem, bindPtr->newContext);
+	    }
+	    bindPtr->newItem = newItem;
+	    bindPtr->newContext = newContext;
+	    if (newItem != NULL) {
+		event.type = EnterNotify;
+		event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+		bindPtr->currentItem = newItem;
+		DoEvent(bindPtr, &event, newItem, newContext);
+	    }
+	    bindPtr->currentItem = savedItem;
+	    bindPtr->currentContext = savedContext;
+	}
+	return;
+    }
+    /*
+     * Special note:  it's possible that
+     *		bindPtr->newItem == bindPtr->currentItem
+     * here.  This can happen, for example, if LEFT_GRABBED_ITEM was set.
+     */
+
+    bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+    bindPtr->currentItem = bindPtr->newItem = newItem;
+    bindPtr->currentContext = bindPtr->newContext = newContext;
+    if (bindPtr->currentItem != NULL) {
+	XEvent event;
+
+	event = bindPtr->pickEvent;
+	event.type = EnterNotify;
+	event.xcrossing.detail = NotifyAncestor;
+	DoEvent(bindPtr, &event, newItem, newContext);
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BindProc --
+ *
+ *	This procedure is invoked by the Tk dispatcher to handle
+ *	events associated with bindings on items.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Depends on the command invoked as part of the binding
+ *	(if there was any).
+ *
+ *--------------------------------------------------------------
+ */
+static void
+BindProc(clientData, eventPtr)
+    ClientData clientData;	/* Pointer to widget structure. */
+    XEvent *eventPtr;		/* Pointer to X event that just
+					 * happened. */
+{
+    struct Blt_BindTableStruct *bindPtr = clientData;
+    int mask;
+
+    Tcl_Preserve(bindPtr->clientData);
+
+    /*
+     * This code below keeps track of the current modifier state in
+     * bindPtr->state.  This information is used to defer repicks of
+     * the current item while buttons are down.
+     */
+    switch (eventPtr->type) {
+    case ButtonPress:
+    case ButtonRelease:
+	mask = 0;
+	if ((eventPtr->xbutton.button >= Button1) &&
+	    (eventPtr->xbutton.button <= Button5)) {
+	    mask = buttonMasks[eventPtr->xbutton.button];
+	}
+	/*
+	 * For button press events, repick the current item using the
+	 * button state before the event, then process the event.  For
+	 * button release events, first process the event, then repick
+	 * the current item using the button state *after* the event
+	 * (the button has logically gone up before we change the
+	 * current item).
+	 */
+
+	if (eventPtr->type == ButtonPress) {
+
+	    /*
+	     * On a button press, first repick the current item using
+	     * the button state before the event, the process the event.
+	     */
+
+	    bindPtr->state = eventPtr->xbutton.state;
+	    PickCurrentItem(bindPtr, eventPtr);
+	    bindPtr->state ^= mask;
+	    DoEvent(bindPtr, eventPtr, bindPtr->currentItem, 
+		bindPtr->currentContext);
+
+	} else {
+
+	    /*
+	     * Button release: first process the event, with the button
+	     * still considered to be down.  Then repick the current
+	     * item under the assumption that the button is no longer down.
+	     */
+	    bindPtr->state = eventPtr->xbutton.state;
+	    DoEvent(bindPtr, eventPtr, bindPtr->currentItem, 
+		bindPtr->currentContext);
+	    eventPtr->xbutton.state ^= mask;
+	    bindPtr->state = eventPtr->xbutton.state;
+	    PickCurrentItem(bindPtr, eventPtr);
+	    eventPtr->xbutton.state ^= mask;
+	}
+	break;
+
+    case EnterNotify:
+    case LeaveNotify:
+	bindPtr->state = eventPtr->xcrossing.state;
+	PickCurrentItem(bindPtr, eventPtr);
+	break;
+
+    case MotionNotify:
+	bindPtr->state = eventPtr->xmotion.state;
+	PickCurrentItem(bindPtr, eventPtr);
+	DoEvent(bindPtr, eventPtr, bindPtr->currentItem, 
+		bindPtr->currentContext);
+	break;
+
+    case KeyPress:
+    case KeyRelease:
+	bindPtr->state = eventPtr->xkey.state;
+	PickCurrentItem(bindPtr, eventPtr);
+	DoEvent(bindPtr, eventPtr, bindPtr->currentItem, 
+		bindPtr->currentContext);
+	break;
+    }
+    Tcl_Release(bindPtr->clientData);
+}
+
+int
+Blt_ConfigureBindings(interp, bindPtr, item, argc, argv)
+    Tcl_Interp *interp;
+    struct Blt_BindTableStruct *bindPtr;
+    ClientData item;
+    int argc;
+    char **argv;
+{
+    char *command;
+    unsigned long mask;
+    char *seq;
+
+    if (argc == 0) {
+	Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+	return TCL_OK;
+    }
+    if (argc == 1) {
+	command = Tk_GetBinding(interp, bindPtr->bindingTable, item, argv[0]);
+	if (command == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_SetResult(interp, command, TCL_VOLATILE);
+	return TCL_OK;
+    }
+
+    seq = argv[0];
+    command = argv[1];
+
+    if (command[0] == '\0') {
+	return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+    }
+
+    if (command[0] == '+') {
+	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+		command + 1, TRUE);
+    } else {
+	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+		command, FALSE);
+    }
+    if (mask == 0) {
+	return TCL_ERROR;
+    }
+    if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+	Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+	Tcl_ResetResult(interp);
+	Tcl_AppendResult(interp, "requested illegal events; ",
+		 "only key, button, motion, enter, leave, and virtual ",
+		 "events may be used", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+#if (TCL_MAJOR_VERSION >= 8)
+
+int
+Blt_ConfigureBindingsFromObj(interp, bindPtr, item, objc, objv)
+    Tcl_Interp *interp;
+    struct Blt_BindTableStruct *bindPtr;
+    ClientData item;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    char *command;
+    unsigned long mask;
+    char *seq;
+    char *string;
+
+    if (objc == 0) {
+	Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+	return TCL_OK;
+    }
+    string = Tcl_GetString(objv[0]);
+    if (objc == 1) {
+	command = Tk_GetBinding(interp, bindPtr->bindingTable, item, string);
+	if (command == NULL) {
+	    Tcl_ResetResult(interp);
+	    Tcl_AppendResult(interp, "invalid binding event \"", string, "\"", 
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	Tcl_SetResult(interp, command, TCL_VOLATILE);
+	return TCL_OK;
+    }
+
+    seq = string;
+    command = Tcl_GetString(objv[1]);
+
+    if (command[0] == '\0') {
+	return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+    }
+
+    if (command[0] == '+') {
+	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+		command + 1, TRUE);
+    } else {
+	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+		command, FALSE);
+    }
+    if (mask == 0) {
+	return TCL_ERROR;
+    }
+    if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+	Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+	Tcl_ResetResult(interp);
+	Tcl_AppendResult(interp, "requested illegal events; ",
+		 "only key, button, motion, enter, leave, and virtual ",
+		 "events may be used", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+#endif
+
+Blt_BindTable
+Blt_CreateBindingTable(interp, tkwin, clientData, pickProc, tagProc)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    ClientData clientData;
+    Blt_BindPickProc *pickProc;
+    Blt_BindTagProc *tagProc;
+{
+    unsigned int mask;
+    struct Blt_BindTableStruct *bindPtr;
+
+    bindPtr = Blt_Calloc(1, sizeof(struct Blt_BindTableStruct));
+    assert(bindPtr);
+    bindPtr->clientData = clientData;
+    bindPtr->pickProc = pickProc;
+    bindPtr->tagProc = tagProc;
+    bindPtr->tkwin = tkwin;
+    bindPtr->bindingTable = Tk_CreateBindingTable(interp);
+    mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+	PointerMotionMask);
+    Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+    return bindPtr;
+}
+
+void
+Blt_DestroyBindingTable(bindPtr)
+    struct Blt_BindTableStruct *bindPtr;
+{
+    unsigned int mask;
+
+    Tk_DeleteBindingTable(bindPtr->bindingTable);
+    mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+	PointerMotionMask);
+    Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+    Blt_Free(bindPtr);
+}
+
+void
+Blt_PickCurrentItem(bindPtr)
+    struct Blt_BindTableStruct *bindPtr;
+{
+    if (bindPtr->activePick) {
+	PickCurrentItem(bindPtr, &(bindPtr->pickEvent));
+    }
+}
+
+void
+Blt_DeleteBindings(bindPtr, object)
+    struct Blt_BindTableStruct *bindPtr;
+    ClientData object;
+{
+    Tk_DeleteAllBindings(bindPtr->bindingTable, object);
+
+    /*
+     * If this is the object currently picked, we need to repick one.
+     */
+    if (bindPtr->currentItem == object) {
+	bindPtr->currentItem = NULL;
+	bindPtr->currentContext = NULL;
+    }
+    if (bindPtr->newItem == object) {
+	bindPtr->newItem = NULL;
+	bindPtr->newContext = NULL;
+    }
+    if (bindPtr->focusItem == object) {
+	bindPtr->focusItem = NULL;
+	bindPtr->focusContext = NULL;
+    }
+}
+
+void
+Blt_MoveBindingTable(bindPtr, tkwin)
+    struct Blt_BindTableStruct *bindPtr;
+    Tk_Window tkwin;
+{
+    unsigned int mask;
+
+    mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+	PointerMotionMask);
+    if (bindPtr->tkwin != NULL) {
+	Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+    }
+    Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+    bindPtr->tkwin = tkwin;
+}
Index: trunk/kitgen/8.x/blt/generic/bltBind.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltBind.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltBind.h	(revision 175)
@@ -0,0 +1,124 @@
+/*
+ * bltBind.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_BIND_H
+#define _BLT_BIND_H
+
+#include "bltList.h"
+
+typedef struct Blt_BindTableStruct *Blt_BindTable;
+
+typedef ClientData (Blt_BindPickProc) _ANSI_ARGS_((ClientData clientData,
+	int x, int y, ClientData *contextPtr));
+
+typedef void (Blt_BindTagProc) _ANSI_ARGS_((Blt_BindTable bindTable, 
+	ClientData object, ClientData context, Blt_List list));
+
+
+/*
+ *  Binding structure information:
+ */
+
+struct Blt_BindTableStruct {
+    unsigned int flags;
+    Tk_BindingTable bindingTable;
+				/* Table of all bindings currently defined.
+				 * NULL means that no bindings exist, so the
+				 * table hasn't been created.  Each "object"
+				 * used for this table is either a Tk_Uid for
+				 * a tag or the address of an item named by
+				 * id. */
+
+    ClientData currentItem;	/* The item currently containing the mouse
+				 * pointer, or NULL if none. */
+    ClientData currentContext;	/* One word indicating what kind of object
+				 * was picked. */
+
+    ClientData newItem;		/* The item that is about to become the
+				 * current one, or NULL.  This field is
+				 * used to detect deletions of the new
+				 * current item pointer that occur during
+				 * Leave processing of the previous current
+				 * tab.  */
+    ClientData newContext;	/* One-word indicating what kind of object 
+				 * was just picked. */
+
+    ClientData focusItem;
+    ClientData focusContext;
+
+    XEvent pickEvent;		/* The event upon which the current choice
+				 * of the current tab is based.  Must be saved
+				 * so that if the current item is deleted,
+				 * we can pick another. */
+    int activePick;		/* The pick event has been initialized so
+				 * that we can repick it */
+
+    int state;			/* Last known modifier state.  Used to
+				 * defer picking a new current object
+				 * while buttons are down. */
+
+    ClientData clientData;
+    Tk_Window tkwin;
+    Blt_BindPickProc *pickProc;	/* Routine to report the item the mouse is
+				 * currently over. */
+    Blt_BindTagProc *tagProc;	/* Routine to report tags picked items. */
+};
+
+EXTERN void Blt_DestroyBindingTable _ANSI_ARGS_((Blt_BindTable table));
+
+EXTERN Blt_BindTable Blt_CreateBindingTable _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, ClientData clientData, Blt_BindPickProc *pickProc,
+	Blt_BindTagProc *tagProc));
+
+EXTERN int Blt_ConfigureBindings _ANSI_ARGS_((Tcl_Interp *interp,
+	Blt_BindTable table, ClientData item, int argc, char **argv));
+
+#if (TCL_MAJOR_VERSION >= 8)
+EXTERN int Blt_ConfigureBindingsFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	Blt_BindTable table, ClientData item, int objc, Tcl_Obj *CONST *objv));
+#endif
+
+EXTERN void Blt_PickCurrentItem _ANSI_ARGS_((Blt_BindTable table));
+
+EXTERN void Blt_DeleteBindings _ANSI_ARGS_((Blt_BindTable table,
+	ClientData object));
+
+EXTERN void Blt_MoveBindingTable _ANSI_ARGS_((Blt_BindTable table, 
+	Tk_Window tkwin));
+
+#define Blt_SetFocusItem(bindPtr, object, context) \
+	((bindPtr)->focusItem = (ClientData)(object),\
+	 (bindPtr)->focusContext = (ClientData)(context))
+
+#define Blt_SetCurrentItem(bindPtr, object, context) \
+	((bindPtr)->currentItem = (ClientData)(object),\
+	 (bindPtr)->currentContext = (ClientData)(context))
+
+#define Blt_GetCurrentItem(bindPtr)  ((bindPtr)->currentItem)
+#define Blt_GetCurrentContext(bindPtr)  ((bindPtr)->currentContext)
+#define Blt_GetLatestItem(bindPtr)  ((bindPtr)->newItem)
+
+#define Blt_GetBindingData(bindPtr)  ((bindPtr)->clientData)
+
+#endif /*_BLT_BIND_H*/
Index: trunk/kitgen/8.x/blt/generic/bltBitmap.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltBitmap.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltBitmap.c	(revision 175)
@@ -0,0 +1,1406 @@
+
+/*
+ * bltBitmap.c --
+ *
+ *	This module implements Tcl bitmaps for the Tk toolkit.
+ *
+ *	Much of the code is taken from XRdBitF.c and XWrBitF.c
+ *	from the MIT X11R5 distribution.
+ *
+ * Copyright, 1987, Massachusetts Institute of Technology Permission
+ * to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee,
+ * 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 M.I.T. not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "bitmap" command created by George Howlett.  */
+
+/*
+  Predefined table holds bitmap info (source width, height)
+  Name table holds bitmap names  
+  Id table hold bitmap ids
+  Both id and name tables get you the actual bitmap.
+ */
+#include "bltInt.h"
+
+#ifndef NO_BITMAP
+#include "bltHash.h"
+#include <X11/Xutil.h>
+
+#define BITMAP_THREAD_KEY	"BLT Bitmap Data"
+
+/* 
+ * BitmapInterpData --
+ *
+ *	Tk's routine to create a bitmap, Tk_DefineBitmap, assumes that
+ *	the source (bit array) is always statically allocated.  This
+ *	isn't true here (we dynamically allocate the arrays), so we have 
+ *	to save them in a hashtable and cleanup after the interpreter 
+ *	is deleted.
+ */
+typedef struct {
+    Blt_HashTable bitmapTable;	/* Hash table of bitmap data keyed by 
+				 * the name of the bitmap. */
+    Tcl_Interp *interp;
+    Display *display;		/* Display of interpreter. */
+    Tk_Window tkwin;		/* Main window of interpreter. */
+} BitmapInterpData;
+
+#define MAX_SIZE 255
+
+/* 
+ * BitmapInfo --
+ */
+typedef struct {
+    double rotate;		/* Rotation of text string */
+    double scale;		/* Scaling factor */
+    Tk_Font font;		/* Font pointer */
+    Tk_Justify justify;		/* Justify text */
+    Blt_Pad padX, padY;		/* Padding around the text */
+} BitmapInfo;
+
+/* 
+ * BitmapData --
+ */
+typedef struct {
+    int width, height;		/* Dimension of image */
+    unsigned char *bits;	/* Data array for bitmap image */
+    int arraySize;		/* Number of bytes in data array */
+} BitmapData;
+
+#define DEF_BITMAP_FONT		STD_FONT
+#define DEF_BITMAP_PAD		"4"
+#define DEF_BITMAP_ROTATE	"0.0"
+#define DEF_BITMAP_SCALE	"1.0"
+#define DEF_BITMAP_JUSTIFY	"center"
+
+#define ROTATE_0	0
+#define ROTATE_90	1
+#define ROTATE_180	2
+#define ROTATE_270	3
+
+
+extern Tk_CustomOption bltPadOption;
+
+static Tk_ConfigSpec composeConfigSpecs[] =
+{
+    {TK_CONFIG_FONT, "-font", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_FONT, Tk_Offset(BitmapInfo, font), 0},
+    {TK_CONFIG_JUSTIFY, "-justify", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_JUSTIFY, Tk_Offset(BitmapInfo, justify),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_PAD, Tk_Offset(BitmapInfo, padX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_PAD, Tk_Offset(BitmapInfo, padY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_DOUBLE, "-rotate", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_ROTATE, Tk_Offset(BitmapInfo, rotate),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-scale", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_SCALE, Tk_Offset(BitmapInfo, scale),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+static Tk_ConfigSpec defineConfigSpecs[] =
+{
+    {TK_CONFIG_DOUBLE, "-rotate", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_ROTATE, Tk_Offset(BitmapInfo, rotate),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-scale", (char *)NULL, (char *)NULL,
+	DEF_BITMAP_SCALE, Tk_Offset(BitmapInfo, scale),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+/* Shared data for the image read/parse logic */
+static char hexTable[256];	/* conversion value */
+static int initialized = 0;	/* easier to fill in at run time */
+
+#define blt_width 40
+#define blt_height 40
+static unsigned char blt_bits[] =
+{
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x04,
+    0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0xe4, 0x33, 0x3f,
+    0x01, 0x00, 0x64, 0x36, 0x0c, 0x01, 0x00, 0x64, 0x36, 0x8c, 0x00, 0x00,
+    0xe4, 0x33, 0x8c, 0x00, 0x00, 0x64, 0x36, 0x8c, 0x00, 0x00, 0x64, 0x36,
+    0x0c, 0x01, 0x00, 0xe4, 0xf3, 0x0d, 0x01, 0x00, 0x04, 0x00, 0x00, 0x02,
+    0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x0c,
+    0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xf8, 0xff,
+    0x03, 0x80, 0xed, 0x07, 0x00, 0x04, 0xe0, 0x0c, 0x00, 0x20, 0x09, 0x10,
+    0x0c, 0x00, 0x00, 0x12, 0x10, 0x0c, 0x00, 0x00, 0x10, 0x30, 0x00, 0x00,
+    0x00, 0x19, 0xd0, 0x03, 0x00, 0x00, 0x14, 0xb0, 0xfe, 0xff, 0xff, 0x1b,
+    0x50, 0x55, 0x55, 0x55, 0x0d, 0xe8, 0xaa, 0xaa, 0xaa, 0x16, 0xe4, 0xff,
+    0xff, 0xff, 0x2f, 0xf4, 0xff, 0xff, 0xff, 0x27, 0xd8, 0xae, 0xaa, 0xbd,
+    0x2d, 0x6c, 0x5f, 0xd5, 0x67, 0x1b, 0xbc, 0xf3, 0x7f, 0xd0, 0x36, 0xf8,
+    0x01, 0x10, 0xcc, 0x1f, 0xe0, 0x45, 0x8e, 0x92, 0x0f, 0xb0, 0x32, 0x41,
+    0x43, 0x0b, 0xd0, 0xcf, 0x3c, 0x7c, 0x0d, 0xb0, 0xaa, 0xc2, 0xab, 0x0a,
+    0x60, 0x55, 0x55, 0x55, 0x05, 0xc0, 0xff, 0xab, 0xaa, 0x03, 0x00, 0x00,
+    0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define bigblt_width 64
+#define bigblt_height 64
+static unsigned char bigblt_bits[] =
+{
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00,
+    0x00, 0x00, 0xe2, 0x0f, 0xc7, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x1f,
+    0xc7, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00,
+    0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x38,
+    0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x1f, 0x07, 0x1c, 0x04, 0x00,
+    0x00, 0x00, 0xe2, 0x1f, 0x07, 0x1c, 0x04, 0x00, 0x00, 0x00, 0xe2, 0x38,
+    0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00,
+    0x00, 0x00, 0xe2, 0x38, 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x1f,
+    0xff, 0x1c, 0x10, 0x00, 0x00, 0x00, 0xe2, 0x0f, 0xff, 0x1c, 0x10, 0x00,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00,
+    0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x06, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xc0, 0xff, 0xff, 0x07, 0x00,
+    0x00, 0xe0, 0xf6, 0x3f, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1c, 0x06, 0x00,
+    0x00, 0x00, 0xc0, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0xc0, 0x08, 0x03,
+    0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x06, 0x00,
+    0x00, 0x00, 0x40, 0x04, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04,
+    0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0xc0, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x0c, 0x06, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+    0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x06, 0x40, 0x55, 0xff, 0xff,
+    0xff, 0xff, 0x7f, 0x05, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x06,
+    0x80, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x03, 0x40, 0xab, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0x01, 0x70, 0x57, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x04,
+    0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0b, 0xd8, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0x14, 0xd0, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13,
+    0xf0, 0xda, 0xbf, 0xaa, 0xba, 0xfd, 0xd6, 0x0b, 0x70, 0xed, 0x77, 0x55,
+    0x57, 0xe5, 0xad, 0x07, 0xb8, 0xf7, 0xab, 0xaa, 0xaa, 0xd2, 0x5b, 0x0f,
+    0xf8, 0xfb, 0x54, 0x55, 0x75, 0x94, 0xf7, 0x1e, 0xf0, 0x7b, 0xfa, 0xff,
+    0x9f, 0xa9, 0xef, 0x1f, 0xc0, 0xbf, 0x00, 0x20, 0x40, 0x54, 0xfe, 0x0f,
+    0x00, 0x1f, 0x92, 0x00, 0x04, 0xa9, 0xfc, 0x01, 0xc0, 0x5f, 0x41, 0xf9,
+    0x04, 0x21, 0xfd, 0x00, 0xc0, 0x9b, 0x28, 0x04, 0xd8, 0x0a, 0x9a, 0x03,
+    0x40, 0x5d, 0x08, 0x40, 0x44, 0x44, 0x62, 0x03, 0xc0, 0xaa, 0x67, 0xe2,
+    0x03, 0x64, 0xba, 0x02, 0x40, 0x55, 0xd5, 0x55, 0xfd, 0xdb, 0x55, 0x03,
+    0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x01, 0x00, 0x57, 0x55, 0x55,
+    0x55, 0x55, 0xd5, 0x00, 0x00, 0xac, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x00,
+    0x00, 0xf0, 0xff, 0x57, 0x55, 0x55, 0x1d, 0x00, 0x00, 0x00, 0x00, 0xf8,
+    0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static Tcl_CmdProc BitmapCmd;
+static Tcl_InterpDeleteProc BitmapInterpDeleteProc;
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetHexValue --
+ *
+ *	Converts the hexadecimal string into an unsigned integer
+ *	value.  The hexadecimal string need not have a leading "0x".
+ *
+ * Results:
+ *	Returns a standard TCL result. If the conversion was
+ *	successful, TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side Effects:
+ * 	If the conversion fails, interp->result is filled with an
+ *	error message.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+GetHexValue(interp, string, valuePtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *valuePtr;
+{
+    register int c;
+    register char *s;
+    register int value;
+
+    s = string;
+    if ((s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) {
+	s += 2;
+    }
+    if (s[0] == '\0') {
+	Tcl_AppendResult(interp, "expecting hex value: got \"", string, "\"",
+	    (char *)NULL);
+	return TCL_ERROR;	/* Only found "0x"  */
+    }
+    value = 0;
+    for ( /*empty*/ ; *s != '\0'; s++) {
+	/* Trim high bits, check type and accumulate */
+	c = *s & 0xff;
+	if (!isxdigit(c)) {
+	    Tcl_AppendResult(interp, "expecting hex value: got \"", string,
+		"\"", (char *)NULL);
+	    return TCL_ERROR;	/* Not a hexadecimal number */
+	}
+	value = (value << 4) + hexTable[c];
+    }
+    *valuePtr = value;
+    return TCL_OK;
+}
+
+#ifdef WIN32
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapToData --
+ *
+ *	Converts a bitmap into an data array.
+ *
+ * Results:
+ *	Returns the number of bytes in an data array representing the bitmap.
+ *
+ * Side Effects:
+ *	Memory is allocated for the data array. Caller must free
+ *	array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+BitmapToData(
+    Tk_Window tkwin,		/* Main window of interpreter */
+    Pixmap bitmap,		/* Bitmap to be queried */
+    int width, int height,	/* Dimensions of the bitmap */
+    unsigned char **bitsPtr)	/* Pointer to converted array of data */
+{
+    int value, bitMask;
+    unsigned long pixel;
+    register int x, y;
+    int count;
+    int arraySize, bytes_per_line;
+    unsigned char *bits;
+    unsigned char *srcPtr, *srcBits;
+    int bytesPerRow;
+
+    *bitsPtr = NULL;
+    srcBits = Blt_GetBitmapData(Tk_Display(tkwin), bitmap, width, height,
+	&bytesPerRow);
+    if (srcBits == NULL) {
+        OutputDebugString("BitmapToData: Can't get bitmap data");
+	return 0;
+    }
+    bytes_per_line = (width + 7) / 8;
+    arraySize = height * bytes_per_line;
+    bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+    assert(bits);
+    count = 0;
+    for (y = height - 1; y >= 0; y--) {
+	srcPtr = srcBits + (bytesPerRow * y);
+	value = 0, bitMask = 1;
+	for (x = 0; x < width; /* empty */ ) {
+	    pixel = (*srcPtr & (0x80 >> (x % 8)));
+	    if (pixel) {
+		value |= bitMask;
+	    }
+	    bitMask <<= 1;
+	    x++;
+	    if (!(x & 7)) {
+		bits[count++] = (unsigned char)value;
+		value = 0, bitMask = 1;
+		srcPtr++;
+	    }
+	}
+	if (x & 7) {
+	    bits[count++] = (unsigned char)value;
+	}
+    }
+    *bitsPtr = bits;
+    return count;
+}
+
+#else
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapToData --
+ *
+ *	Converts a bitmap into an data array.
+ *
+ * Results:
+ *	Returns the number of bytes in an data array representing the bitmap.
+ *
+ * Side Effects:
+ *	Memory is allocated for the data array. Caller must free
+ *	array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+BitmapToData(tkwin, bitmap, width, height, bitsPtr)
+    Tk_Window tkwin;		/* Main window of interpreter */
+    Pixmap bitmap;		/* Bitmap to be queried */
+    int width, height;		/* Dimensions of the bitmap */
+    unsigned char **bitsPtr;	/* Pointer to converted array of data */
+{
+    int value, bitMask;
+    unsigned long pixel;
+    register int x, y;
+    int count;
+    int arraySize, bytes_per_line;
+    Display *display;
+    XImage *imagePtr;
+    unsigned char *bits;
+
+    display = Tk_Display(tkwin);
+    /* Convert the bitmap to an image */
+    imagePtr = XGetImage(display, bitmap, 0, 0, width, height, 1L, XYPixmap);
+    /*
+     * The slow but robust brute force method of converting an image:
+     */
+    bytes_per_line = (width + 7) / 8;
+    arraySize = height * bytes_per_line;
+    bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+    assert(bits);
+    count = 0;
+    for (y = 0; y < height; y++) {
+	value = 0, bitMask = 1;
+	for (x = 0; x < width; /*empty*/ ) {
+	    pixel = XGetPixel(imagePtr, x, y);
+	    if (pixel) {
+		value |= bitMask;
+	    }
+	    bitMask <<= 1;
+	    x++;
+	    if (!(x & 7)) {
+		bits[count++] = (unsigned char)value;
+		value = 0, bitMask = 1;
+	    }
+	}
+	if (x & 7) {
+	    bits[count++] = (unsigned char)value;
+	}
+    }
+    XDestroyImage(imagePtr);
+    *bitsPtr = bits;
+    return count;
+}
+
+#endif
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AsciiToData --
+ *
+ *	Converts a Tcl list of ASCII values into a data array.
+ *
+ * Results:
+ *	A standard TCL result.
+ *
+ * Side Effects:
+ * 	If an error occurs while processing the data, interp->result
+ * 	is filled with a corresponding error message.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AsciiToData(interp, elemList, width, height, bitsPtr)
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    char *elemList;		/* List of of hex numbers representing
+				 * bitmap data */
+    int width, height;		/* Height and width */
+    unsigned char **bitsPtr;	/* data array (output) */
+{
+    int arraySize;		/* Number of bytes of data */
+    int value;			/* from an input line */
+    int padding;		/* to handle alignment */
+    int bytesPerLine;		/* per scanline of data */
+    unsigned char *bits;
+    register int count;
+    enum Formats {
+	V10, V11
+    } format;
+    register int i;		/*  */
+    char **valueArr;
+    int nValues;
+
+    /* First time through initialize the ascii->hex translation table */
+    if (!initialized) {
+	Blt_InitHexTable(hexTable);
+	initialized = 1;
+    }
+    if (Tcl_SplitList(interp, elemList, &nValues, &valueArr) != TCL_OK) {
+	return -1;
+    }
+    bytesPerLine = (width + 7) / 8;
+    arraySize = bytesPerLine * height;
+    if (nValues == arraySize) {
+	format = V11;
+    } else if (nValues == (arraySize / 2)) {
+	format = V10;
+    } else {
+	Tcl_AppendResult(interp, "bitmap has wrong # of data values",
+	    (char *)NULL);
+	goto error;
+    }
+    padding = 0;
+    if (format == V10) {
+	padding = ((width % 16) && ((width % 16) < 9));
+	if (padding) {
+	    bytesPerLine = (width + 7) / 8 + padding;
+	    arraySize = bytesPerLine * height;
+	}
+    }
+    bits = Blt_Calloc(sizeof(unsigned char), arraySize);
+    if (bits == NULL) {
+	Tcl_AppendResult(interp, "can't allocate memory for bitmap",
+	    (char *)NULL);
+	goto error;
+    }
+    count = 0;
+    for (i = 0; i < nValues; i++) {
+	if (GetHexValue(interp, valueArr[i], &value) != TCL_OK) {
+	    Blt_Free(bits);
+	    goto error;
+	}
+	bits[count++] = (unsigned char)value;
+	if (format == V10) {
+	    if ((!padding) || (((i * 2) + 2) % bytesPerLine)) {
+		bits[count++] = value >> 8;
+	    }
+	}
+    }
+    Blt_Free(valueArr);
+    *bitsPtr = bits;
+    return count;
+  error:
+    Blt_Free(valueArr);
+    return -1;
+}
+
+
+static int
+ParseListData(interp, string, widthPtr, heightPtr, bitsPtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *widthPtr;
+    int *heightPtr;
+    unsigned char **bitsPtr;
+{
+    register char *p;
+    char **elemArr;
+    int nElem;
+    int width, height;
+    int result;
+    int arraySize;
+
+    arraySize = -1;
+    if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	return -1;
+    }
+    if (nElem == 2) {
+	char **dimArr;
+	int nDim;
+	
+	if (Tcl_SplitList(interp, elemArr[0], &nDim, &dimArr) != TCL_OK) {
+	    goto error;
+	}
+	if (nDim != 2) {
+	    Tcl_AppendResult(interp, "wrong # of bitmap dimensions: ",
+			     "should be \"width height\"", (char *)NULL);
+	    result = TCL_ERROR;
+	} else {
+	    result = ((Tcl_GetInt(interp, dimArr[0], &width) == TCL_OK) &&
+		      (Tcl_GetInt(interp, dimArr[1], &height) == TCL_OK));
+	}
+	Blt_Free(dimArr);
+	if (!result) {
+	    goto error;
+	}
+	string = elemArr[1];
+    } else if (nElem == 3) {
+	if ((Tcl_GetInt(interp, elemArr[0], &width) != TCL_OK) ||
+	    (Tcl_GetInt(interp, elemArr[1], &height) != TCL_OK)) {
+	    goto error;
+	}
+	string = elemArr[2];
+    } else {
+	Tcl_AppendResult(interp, "wrong # of bitmap data components: ",
+			 "should be \"dimensions sourceData\"", (char *)NULL);
+	goto error;
+    }
+    if ((width < 1) || (height < 1)) {
+	Tcl_AppendResult(interp, "bad bitmap dimensions", (char *)NULL);
+	goto error;
+    }
+    /* Convert commas to blank spaces */
+    
+    for (p = string; *p != '\0'; p++) {
+	if (*p == ',') {
+	    *p = ' ';
+	}
+    }
+    arraySize = AsciiToData(interp, string, width, height, bitsPtr);
+    *widthPtr = width;
+    *heightPtr = height;
+ error:
+    Blt_Free(elemArr);
+    return arraySize;
+}
+
+/*
+ * Parse the lines that define the dimensions of the bitmap,
+ * plus the first line that defines the bitmap data (it declares
+ * the name of a data variable but doesn't include any actual
+ * data).  These lines look something like the following:
+ *
+ *		#define foo_width 16
+ *		#define foo_height 16
+ *		#define foo_x_hot 3
+ *		#define foo_y_hot 3
+ *		static char foo_bits[] = {
+ *
+ * The x_hot and y_hot lines may or may not be present.  It's
+ * important to check for "char" in the last line, in order to
+ * reject old X10-style bitmaps that used shorts.
+ */
+
+static int
+ParseStructData(interp, string, widthPtr, heightPtr, bitsPtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *widthPtr;
+    int *heightPtr;
+    unsigned char **bitsPtr;
+{
+    int width, height;
+    int hotX, hotY;
+    char *line, *nextline;
+    register char *p;
+    Tcl_RegExp re;
+    char *name, *value, *data;
+    int len;
+    int arraySize;
+
+    width = height = 0;
+    hotX = hotY = -1;
+    data = NULL;
+    nextline = string;
+    for (line = string; nextline != NULL; line = nextline + 1) {
+	nextline = strchr(line, '\n');
+	if ((nextline == NULL) || (line == nextline)) {
+	    continue;		/* Empty line */
+	}
+	*nextline = '\0';
+	re = Tcl_RegExpCompile(interp, " *# *define +");
+	if (Tcl_RegExpExec(interp, re, line, line)) {
+	    char *start, *end;
+
+	    Tcl_RegExpRange(re, 0, &start, &end);
+	    name = strtok(end, " \t"); 
+	    value = strtok(NULL, " \t");
+	    if ((name == NULL) || (value == NULL)) {
+		return TCL_ERROR;
+	    }
+	    len = strlen(name);
+	    if ((len >= 6) && (name[len-6] == '_') && 
+		(strcmp(name+len-6, "_width") == 0)) {
+		if (Tcl_GetInt(interp, value, &width) != TCL_OK) {
+		    return -1;
+		}
+	    } else if ((len >= 7) && (name[len-7] == '_') && 
+		       (strcmp(name+len-7, "_height") == 0)) {
+		if (Tcl_GetInt(interp, value, &height) != TCL_OK) {
+		    return -1;
+		}
+	    } else if ((len >= 6) && (name[len-6] == '_') && 
+		       (strcmp(name+len-6, "_x_hot") == 0)) {
+		if (Tcl_GetInt(interp, value, &hotX) != TCL_OK) {
+		    return -1;
+		}
+	    } else if ((len >= 6) && (name[len-6] == '_') && 
+		       (strcmp(name+len-6, "_y_hot") == 0)) {
+		if (Tcl_GetInt(interp, value, &hotY) != TCL_OK) {
+		    return -1;
+		}
+	    } 
+	} else {
+	    re = Tcl_RegExpCompile(interp, " *static +.*char +");
+	    if (Tcl_RegExpExec(interp, re, line, line)) {
+		/* Find the { */
+	        /* Repair the string so we can search the entire string. */
+ 	        *nextline = ' ';   
+		p = strchr(line, '{');
+		if (p == NULL) {
+		    return -1;
+		}
+		data = p + 1;
+		break;
+	    } else {
+		Tcl_AppendResult(interp, "unknown bitmap format: ",
+		 "obsolete X10 bitmap file?", (char *) NULL);
+		return -1;
+	    }
+	}
+    }
+    /*
+     * Now we've read everything but the data.  Allocate an array
+     * and read in the data.
+     */
+    if ((width <= 0) || (height <= 0)) {
+	Tcl_AppendResult(interp, "invalid bitmap dimensions \"", (char *)NULL);
+	Tcl_AppendResult(interp, Blt_Itoa(width), " x ", (char *)NULL);
+	Tcl_AppendResult(interp, Blt_Itoa(height), "\"", (char *)NULL);
+	return -1;
+    }
+    *widthPtr = width;
+    *heightPtr = height;
+    for (p = data; *p != '\0'; p++) {
+	if ((*p == ',') || (*p == ';') || (*p == '}')) {
+	    *p = ' ';
+	}
+    }
+    arraySize = AsciiToData(interp, data, width, height, bitsPtr);
+    return arraySize;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ScaleRotateData --
+ *
+ *	Creates a new data array of the rotated and scaled bitmap.
+ *
+ * Results:
+ *	A standard Tcl result. If the bitmap data is rotated
+ *	successfully, TCL_OK is returned.  But if memory could not be
+ *	allocated for the new data array, TCL_ERROR is returned and an
+ *	error message is left in interp->result.
+ *
+ * Side Effects:
+ *	Memory is allocated for rotated, scaled data array. Caller
+ *	must free array later.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+ScaleRotateData(
+    Tcl_Interp *interp,		/* Interpreter to report results to */
+    BitmapData *srcPtr,		/* Source bitmap to transform. */
+    double theta,		/* Number of degrees to rotate the bitmap. */
+    double scale,		/* Factor to scale the bitmap. */
+    BitmapData *destPtr)	/* Destination bitmap. */
+{
+    register int x, y, sx, sy;
+    double srcX, srcY, destX, destY;	/* Origins of source and destination
+					 * bitmaps */
+    double dx, dy;
+    double sinTheta, cosTheta;
+    double rotWidth, rotHeight;
+    double radians;
+    unsigned char *bits;
+    int arraySize;
+    int pixel, ipixel;
+    int srcBytesPerLine, destBytesPerLine;
+
+    srcBytesPerLine = (srcPtr->width + 7) / 8;
+    Blt_GetBoundingBox(srcPtr->width, srcPtr->height, theta, &rotWidth, 
+	&rotHeight, (Point2D *)NULL);
+    destPtr->width = (int)(rotWidth * scale + 0.5) ;
+    destPtr->height = (int)(rotHeight * scale + 0.5);
+
+    destBytesPerLine = (destPtr->width + 7) / 8;
+    arraySize = destPtr->height * destBytesPerLine;
+    bits = Blt_Calloc(arraySize, sizeof(unsigned char));
+    if (bits == NULL) {
+	Tcl_AppendResult(interp, "can't allocate bitmap data array",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    scale = 1.0 / scale;
+    destPtr->bits = bits;
+    destPtr->arraySize = arraySize;
+
+    radians = (theta / 180.0) * M_PI;
+    sinTheta = sin(radians);
+    cosTheta = cos(radians);
+
+    /*
+     * Coordinates of the centers of the source and destination rectangles
+     */
+    srcX = srcPtr->width * 0.5;
+    srcY = srcPtr->height * 0.5;
+    destX = rotWidth * 0.5;
+    destY = rotHeight * 0.5;
+
+    /*
+     * Rotate each pixel of dest image, placing results in source image
+     */
+    for (y = 0; y < destPtr->height; y++) {
+	for (x = 0; x < destPtr->width; x++) {
+	    dx = scale * (double)x;
+	    dy = scale * (double)y;
+	    if (theta == 270.0) {
+		sx = (int)dy, sy = (int)(rotWidth - dx) - 1;
+	    } else if (theta == 180.0) {
+		sx = (int)(rotWidth - dx) - 1, sy = (int)(rotHeight - dy) - 1;
+	    } else if (theta == 90.0) {
+		sx = (int)(rotHeight - dy) - 1, sy = (int)dx;
+	    } else if (theta == 0.0) {
+		sx = (int)dx, sy = (int)dy;
+	    } else {
+		double transX, transY, rotX, rotY;
+		/* Translate origin to center of destination image */
+
+		transX = dx - destX;
+		transY = dy - destY;
+
+		/* Rotate the coordinates about the origin */
+
+		rotX = (transX * cosTheta) - (transY * sinTheta);
+		rotY = (transX * sinTheta) + (transY * cosTheta);
+
+		/* Translate back to the center of the source image */
+		rotX += srcX;
+		rotY += srcY;
+
+		sx = ROUND(rotX);
+		sy = ROUND(rotY);
+
+		/*
+		 * Verify the coordinates, since the destination image
+		 * can be bigger than the source.
+		 */
+
+		if ((sx >= srcPtr->width) || (sx < 0) ||
+		    (sy >= srcPtr->height) || (sy < 0)) {
+		    continue;
+		}
+	    }
+	    ipixel = (srcBytesPerLine * sy) + (sx / 8);
+	    pixel = srcPtr->bits[ipixel] & (1 << (sx % 8));
+	    if (pixel) {
+		ipixel = (destBytesPerLine * y) + (x / 8);
+		bits[ipixel] |= (1 << (x % 8));
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapDataToString --
+ *
+ *	Returns a list of hex values corresponding to the data
+ *	bits of the bitmap given.
+ *
+ *	Converts the unsigned character value into a two character
+ *	hexadecimal string.  A separator is also added, which may
+ *	either a newline or space according the the number of bytes
+ *	already output.
+ *
+ * Results:
+ *	Returns TCL_ERROR if a data array can't be generated
+ *	from the bitmap (memory allocation failure), otherwise TCL_OK.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+BitmapDataToString(tkwin, bitmap, resultPtr)
+    Tk_Window tkwin;		/* Main window of interpreter */
+    Pixmap bitmap;		/* Bitmap to be queried */
+    Tcl_DString *resultPtr;	/* Dynamic string to output results to */
+{
+    unsigned char *bits;
+    char *separator;
+    int arraySize;
+    register int i;
+    char string[200];
+    int width, height;
+
+    /* Get the dimensions of the bitmap */
+    Tk_SizeOfBitmap(Tk_Display(tkwin), bitmap, &width, &height);
+    arraySize = BitmapToData(tkwin, bitmap, width, height, &bits);
+#define BYTES_PER_OUTPUT_LINE 24
+    for (i = 0; i < arraySize; i++) {
+	separator = (i % BYTES_PER_OUTPUT_LINE) ? " " : "\n    ";
+	sprintf(string, "%s%02x", separator, bits[i]);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if (bits != NULL) {
+        Blt_Free(bits);
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ComposeOp --
+ *
+ *	Converts the text string into an internal bitmap.
+ *
+ *	There's a lot of extra (read unnecessary) work going on here,
+ *	but I don't (right now) think that it matters much.  The
+ *	rotated bitmap (formerly an image) is converted back to an
+ *	image just so we can convert it to a data array for
+ *	Tk_DefineBitmap.
+ *
+ * Results:
+ *	A standard TCL result.
+ *
+ * Side Effects:
+ * 	If an error occurs while processing the data, interp->result
+ * 	is filled with a corresponding error message.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+ComposeOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Number of arguments */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    int width, height;		/* Dimensions of bitmap */
+    Pixmap bitmap;		/* Text bitmap */
+    unsigned char *bits;	/* Data array derived from text bitmap */
+    int arraySize;
+    BitmapInfo info;		/* Text rotation and font information */
+    int result;
+    double theta;
+    TextStyle ts;
+    TextLayout *textPtr;
+    Tk_Window tkwin;		/* Main window of interpreter */
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    tkwin = dataPtr->tkwin;
+    bitmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[2]));
+    Tcl_ResetResult(interp);
+    if (bitmap != None) {
+	Tk_FreeBitmap(dataPtr->display, bitmap);
+	return TCL_OK;
+    }
+    /* Initialize info and process flags */
+    info.justify = TK_JUSTIFY_CENTER;
+    info.rotate = 0.0;		/* No rotation or scaling by default */
+    info.scale = 1.0;
+    info.padLeft = info.padRight = 0;
+    info.padTop = info.padBottom = 0;
+    info.font = (Tk_Font)NULL;	/* Initialized by Tk_ConfigureWidget */
+    if (Tk_ConfigureWidget(interp, tkwin, composeConfigSpecs,
+	    argc - 4, argv + 4, (char *)&info, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    theta = FMOD(info.rotate, (double)360.0);
+    if (theta < 0.0) {
+	theta += 360.0;
+    }
+    Blt_InitTextStyle(&ts);
+    ts.font = info.font;
+    ts.theta = 0.0;
+    ts.justify = info.justify;
+    ts.padX = info.padX;
+    ts.padY = info.padY;
+    ts.leader = 0;
+    ts.anchor = TK_ANCHOR_CENTER;
+
+    textPtr = Blt_GetTextLayout(argv[3], &ts);
+    bitmap = Blt_CreateTextBitmap(tkwin, textPtr, &ts, &width, &height);
+    Blt_Free(textPtr);
+    if (bitmap == None) {
+	Tcl_AppendResult(interp, "can't create bitmap", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Free the font structure, since we don't need it anymore */
+    Tk_FreeOptions(composeConfigSpecs, (char *)&info, dataPtr->display, 0);
+
+    /* Convert bitmap back to a data array */
+    arraySize = BitmapToData(tkwin, bitmap, width, height, &bits);
+    Tk_FreePixmap(dataPtr->display, bitmap);
+    if (arraySize == 0) {
+	Tcl_AppendResult(interp, "can't get bitmap data", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* If bitmap is to be rotated or scale, do it here */
+    if ((theta != 0.0) || (info.scale != 1.0)) {
+	BitmapData srcData, destData;
+
+	srcData.bits = bits;
+	srcData.width = width;
+	srcData.height = height;
+	srcData.arraySize = arraySize;
+
+	result = ScaleRotateData(interp, &srcData, theta, info.scale, 
+		 &destData);
+	Blt_Free(bits);		/* Free the un-transformed data array. */
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	bits = destData.bits;
+	width = destData.width;
+	height = destData.height;
+    }
+    /* Create the bitmap again, this time using Tk's bitmap facilities */
+    result = Tk_DefineBitmap(interp, Tk_GetUid(argv[2]), (char *)bits,
+	width, height);
+    if (result != TCL_OK) {
+	Blt_Free(bits);
+    }
+    hPtr = Blt_CreateHashEntry(&(dataPtr->bitmapTable), argv[2], &isNew);
+    Blt_SetHashValue(hPtr, bits);
+    return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DefineOp --
+ *
+ *	Converts the dataList into an internal bitmap.
+ *
+ * Results:
+ *	A standard TCL result.
+ *
+ * Side Effects:
+ * 	If an error occurs while processing the data, interp->result
+ *	is filled with a corresponding error message.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+DefineOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Number of arguments */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    int width, height;		/* Dimensions of bitmap */
+    unsigned char *bits;	/* working variable */
+    register char *p;
+    char *defn;			/* Definition of bitmap. */
+    BitmapInfo info;		/* Not used. */
+    int arraySize;
+    int result;
+    double theta;
+    Pixmap bitmap;
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    Tcl_ResetResult(interp);
+    if (bitmap != None) {
+	Tk_FreeBitmap(dataPtr->display, bitmap);
+	return TCL_OK;
+    }
+    /* Initialize info and then process flags */
+    info.rotate = 0.0;		/* No rotation by default */
+    info.scale = 1.0;		/* No scaling by default */
+    if (Tk_ConfigureWidget(interp, dataPtr->tkwin, defineConfigSpecs,
+	    argc - 4, argv + 4, (char *)&info, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Skip leading spaces. */
+    for (p = argv[3]; isspace(UCHAR(*p)); p++) {
+	/*empty*/
+    }
+    defn = Blt_Strdup(p);
+    bits = NULL;
+    if (*p == '#') {
+	arraySize = ParseStructData(interp, defn, &width, &height, &bits);
+    } else {
+	arraySize = ParseListData(interp, defn, &width, &height, &bits);
+    }
+    Blt_Free(defn);
+    if (arraySize <= 0) {
+	return TCL_ERROR;
+    }
+    theta = FMOD(info.rotate, 360.0);
+    if (theta < 0.0) {
+	theta += 360.0;
+    }
+    /* If bitmap is to be rotated or scale, do it here */
+    if ((theta != 0.0) || (info.scale != 1.0)) { 
+	BitmapData srcData, destData;
+
+	srcData.bits = bits;
+	srcData.width = width;
+	srcData.height = height;
+	srcData.arraySize = arraySize;
+
+	result = ScaleRotateData(interp, &srcData, theta, info.scale, 
+		 &destData);
+	Blt_Free(bits);		/* Free the array of un-transformed data. */
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	bits = destData.bits;
+	width = destData.width;
+	height = destData.height;
+    }
+    result = Tk_DefineBitmap(interp, Tk_GetUid(argv[2]), (char *)bits,
+	width, height);
+    if (result != TCL_OK) {
+	Blt_Free(bits);
+    }
+    hPtr = Blt_CreateHashEntry(&(dataPtr->bitmapTable), argv[2], &isNew);
+    Blt_SetHashValue(hPtr, bits);
+    return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExistOp --
+ *
+ *	Indicates if the named bitmap exists.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Not used. */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    Pixmap bitmap;
+
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    Tcl_ResetResult(interp);
+    if (bitmap != None) {
+	Tk_FreeBitmap(dataPtr->display, bitmap);
+    }
+    Blt_SetBooleanResult(interp, (bitmap != None));
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * HeightOp --
+ *
+ *	Returns the height of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+HeightOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Not used. */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    int width, height;
+    Pixmap bitmap;
+    
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    if (bitmap == None) {
+	return TCL_ERROR;
+    }
+    Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+    Tcl_SetResult(interp, Blt_Itoa(height), TCL_VOLATILE);
+    Tk_FreeBitmap(dataPtr->display, bitmap);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * WidthOp --
+ *
+ *	Returns the width of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+WidthOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Not used. */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    int width, height;
+    Pixmap bitmap;
+
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    if (bitmap == None) {
+	return TCL_ERROR;
+    }
+    Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+    Tcl_SetResult(interp, Blt_Itoa(width), TCL_VOLATILE);
+    Tk_FreeBitmap(dataPtr->display, bitmap);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SourceOp --
+ *
+ *	Returns the data array (excluding width and height)
+ *	of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SourceOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Not used. */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    Pixmap bitmap;
+    Tcl_DString dString;
+
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    if (bitmap == None) {
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    BitmapDataToString(dataPtr->tkwin, bitmap, &dString);
+    Tk_FreeBitmap(dataPtr->display, bitmap);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DataOp --
+ *
+ *	Returns the data array, including width and height,
+ *	of the named bitmap.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DataOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Not used. */
+    char **argv;		/* Argument list */
+{
+    BitmapInterpData *dataPtr = clientData;
+    Pixmap bitmap;
+    int width, height;
+    Tcl_DString dString;
+
+    bitmap = Tk_GetBitmap(interp, dataPtr->tkwin, Tk_GetUid(argv[2]));
+    if (bitmap == None) {
+	return TCL_ERROR;
+    }
+    Tk_SizeOfBitmap(dataPtr->display, bitmap, &width, &height);
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppendElement(&dString, Blt_Itoa(width));
+    Tcl_DStringAppendElement(&dString, Blt_Itoa(height));
+    Tcl_DStringStartSublist(&dString);
+    BitmapDataToString(dataPtr->tkwin, bitmap, &dString);
+    Tcl_DStringEndSublist(&dString);
+    Tk_FreeBitmap(dataPtr->display, bitmap);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BLT Sub-command specification:
+ *
+ *	- Name of the sub-command.
+ *	- Minimum number of characters needed to unambiguously
+ *        recognize the sub-command.
+ *	- Pointer to the function to be called for the sub-command.
+ *	- Minimum number of arguments accepted.
+ *	- Maximum number of arguments accepted.
+ *	- String to be displayed for usage.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec bitmapOps[] =
+{
+    {"compose", 1, (Blt_Op)ComposeOp, 4, 0, "bitmapName text ?flags?",},
+    {"data", 2, (Blt_Op)DataOp, 3, 3, "bitmapName",},
+    {"define", 2, (Blt_Op)DefineOp, 4, 0, "bitmapName data ?flags?",},
+    {"exists", 1, (Blt_Op)ExistsOp, 3, 3, "bitmapName",},
+    {"height", 1, (Blt_Op)HeightOp, 3, 3, "bitmapName",},
+    {"source", 1, (Blt_Op)SourceOp, 3, 3, "bitmapName",},
+    {"width", 1, (Blt_Op)WidthOp, 3, 3, "bitmapName",},
+};
+static int nBitmapOps = sizeof(bitmapOps) / sizeof(Blt_OpSpec);
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BitmapCmd --
+ *
+ *	This procedure is invoked to process the Tcl command
+ *	that corresponds to bitmaps managed by this module.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BitmapCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Thread-specific data for bitmaps. */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nBitmapOps, bitmapOps, BLT_OP_ARG1, argc, argv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (clientData, interp, argc, argv);
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BitmapInterpDeleteProc --
+ *
+ *	This is called when the interpreter is deleted. All the tiles
+ *	are specific to that interpreter are destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys the tile table.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BitmapInterpDeleteProc(clientData, interp)
+    ClientData clientData;	/* Thread-specific data. */
+    Tcl_Interp *interp;
+{
+    BitmapInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    unsigned char *bits;
+    Blt_HashSearch cursor;
+    
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->bitmapTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	bits = (unsigned char *)Blt_GetHashValue(hPtr);
+	Blt_Free(bits);
+    }
+    Blt_DeleteHashTable(&(dataPtr->bitmapTable));
+    Tcl_DeleteAssocData(interp, BITMAP_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+static BitmapInterpData *
+GetBitmapInterpData(interp)
+    Tcl_Interp *interp;
+{
+    BitmapInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (BitmapInterpData *)
+	Tcl_GetAssocData(interp, BITMAP_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(BitmapInterpData));
+	assert(dataPtr);
+	dataPtr->interp = interp;
+	dataPtr->tkwin = Tk_MainWindow(interp);
+	dataPtr->display = Tk_Display(dataPtr->tkwin);
+	Tcl_SetAssocData(interp, BITMAP_THREAD_KEY, BitmapInterpDeleteProc, 
+		 dataPtr);
+	Blt_InitHashTable(&(dataPtr->bitmapTable), BLT_STRING_KEYS);
+    }
+    return dataPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_BitmapInit --
+ *
+ *	This procedure is invoked to initialize the bitmap command.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Adds the command to the interpreter and sets an array variable
+ *	which its version number.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_BitmapInit(interp)
+    Tcl_Interp *interp;
+{
+    BitmapInterpData *dataPtr;
+    static Blt_CmdSpec cmdSpec =
+    {"bitmap", BitmapCmd};
+
+    /* Define the BLT logo bitmaps */
+
+    dataPtr = GetBitmapInterpData(interp);
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    Tk_DefineBitmap(interp, Tk_GetUid("bigBLT"), (char *)bigblt_bits,
+	bigblt_width, bigblt_height);
+    Tk_DefineBitmap(interp, Tk_GetUid("BLT"), (char *)blt_bits,
+	blt_width, blt_height);
+    Tcl_ResetResult(interp);
+    return TCL_OK;
+}
+
+#endif /* NO_BITMAP */
Index: trunk/kitgen/8.x/blt/generic/bltBusy.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltBusy.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltBusy.c	(revision 175)
@@ -0,0 +1,1196 @@
+/*
+ * bltBusy.c --
+ *
+ *	This module implements busy windows for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "busy" command was created by George Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_BUSY
+#include "bltHash.h"
+
+#define BUSYDEBUG 0
+
+#ifndef TK_REPARENTED
+#define TK_REPARENTED 0
+#endif
+
+#define BUSY_THREAD_KEY	"BLT Busy Data"
+
+typedef struct {
+    Display *display;		/* Display of busy window */
+    Tcl_Interp *interp;		/* Interpreter where "busy" command was
+				 * created. It's used to key the
+				 * searches in the window hierarchy. See the
+				 * "windows" command. */
+
+    Tk_Window tkBusy;		/* Busy window: Transparent window used
+				 * to block delivery of events to windows
+				 * underneath it. */
+
+    Tk_Window tkParent;		/* Parent window of the busy
+				 * window. It may be the reference
+				 * window (if the reference is a
+				 * toplevel) or a mutual ancestor of
+				 * the reference window */
+
+    Tk_Window tkRef;		/* Reference window of the busy window.
+				 * It's is used to manage the size and
+				 * position of the busy window. */
+
+
+    int x, y;			/* Position of the reference window */
+
+    int width, height;		/* Size of the reference window. Retained to
+				 * know if the reference window has been
+				 * reconfigured to a new size. */
+
+    int isBusy;			/* Indicates whether the transparent
+				 * window should be displayed. This
+				 * can be different from what
+				 * Tk_IsMapped says because the a
+				 * sibling reference window may be
+				 * unmapped, forcing the busy window
+				 * to be also hidden. */
+
+    int menuBar;		/* Menu bar flag. */
+    Tk_Cursor cursor;		/* Cursor for the busy window. */
+
+    Blt_HashEntry *hashPtr;	/* Used the delete the busy window entry 
+				 * out of the global hash table. */
+    Blt_HashTable *tablePtr;
+} Busy;
+
+#ifdef WIN32
+#define DEF_BUSY_CURSOR "wait"
+#else
+#define DEF_BUSY_CURSOR "watch"
+#endif
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor",
+	DEF_BUSY_CURSOR, Tk_Offset(Busy, cursor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef struct {
+    Blt_HashTable busyTable;	/* Hash table of busy window
+				 * structures keyed by the address of
+				 * the reference Tk window */
+} BusyInterpData;
+
+static void BusyGeometryProc _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin));
+static void BusyCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin));
+
+static Tk_GeomMgr busyMgrInfo =
+{
+    "busy",			/* Name of geometry manager used by winfo */
+    BusyGeometryProc,		/* Procedure to for new geometry requests */
+    BusyCustodyProc,		/* Procedure when window is taken away */
+};
+
+/* Forward declarations */
+static void DestroyBusy _ANSI_ARGS_((DestroyData dataPtr));
+static void BusyEventProc _ANSI_ARGS_((ClientData clientData,
+	XEvent *eventPtr));
+
+static Tk_EventProc RefWinEventProc;
+static Tcl_CmdProc BusyCmd;
+static Tcl_InterpDeleteProc BusyInterpDeleteProc;
+
+static void
+ShowBusyWindow(busyPtr) 
+    Busy *busyPtr;
+{
+    if (busyPtr->tkBusy != NULL) {
+	Tk_MapWindow(busyPtr->tkBusy);
+	/* 
+	 * Always raise the busy window just in case new sibling
+	 * windows have been created in the meantime. Can't use
+	 * Tk_RestackWindow because it doesn't work under Win32.
+	 */
+	XRaiseWindow(Tk_Display(busyPtr->tkBusy), 
+	     Tk_WindowId(busyPtr->tkBusy));
+    }
+#ifdef WIN32
+    {
+	POINT point;
+	/*
+	 * Under Win32, cursors aren't associated with windows.  Tk
+	 * fakes this by watching Motion events on its windows.  So Tk
+	 * will automatically change the cursor when the pointer
+	 * enters the Busy window.  But Windows doesn't immediately
+	 * change the cursor; it waits for the cursor position to
+	 * change or a system call.  We need to change the cursor
+ 	 * before the application starts processing, so set the cursor
+	 * position redundantly back to the current position.
+	 */
+	GetCursorPos(&point);
+	SetCursorPos(point.x, point.y);
+    }
+#endif /* WIN32 */
+}
+
+static void
+HideBusyWindow(busyPtr)
+    Busy *busyPtr;
+{
+    if (busyPtr->tkBusy != NULL) {
+	Tk_UnmapWindow(busyPtr->tkBusy); 
+    }
+#ifdef WIN32
+    {
+	POINT point;
+	/*
+	 * Under Win32, cursors aren't associated with windows.  Tk
+	 * fakes this by watching Motion events on its windows.  So Tk
+	 * will automatically change the cursor when the pointer
+	 * enters the Busy window.  But Windows doesn't immediately
+	 * change the cursor: it waits for the cursor position to
+	 * change or a system call.  We need to change the cursor
+	 * before the application starts processing, so set the cursor
+	 * position redundantly back to the current position.
+	 */
+	GetCursorPos(&point);
+	SetCursorPos(point.x, point.y);
+    }
+#endif /* WIN32 */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BusyEventProc --
+ *
+ *	This procedure is invoked by the Tk dispatcher for events on
+ *	the busy window itself.  We're only concerned with destroy
+ *	events.
+ *
+ *	It might be necessary (someday) to watch resize events.  Right
+ *	now, I don't think there's any point in it.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When a busy window is destroyed, all internal structures
+ * 	associated with it released at the next idle point.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+BusyEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Busy window record */
+    XEvent *eventPtr;		/* Event which triggered call to routine */
+{
+    Busy *busyPtr = clientData;
+
+    if (eventPtr->type == DestroyNotify) {
+	busyPtr->tkBusy = NULL;
+	Tcl_EventuallyFree(busyPtr, DestroyBusy);
+    }
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BusyCustodyProc --
+ *
+ *	This procedure is invoked when the busy window has been stolen
+ *	by another geometry manager.  The information and memory
+ *	associated with the busy window is released. I don't know why
+ *	anyone would try to pack a busy window, but this should keep
+ *	everything sane, if it is.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The Busy structure is freed at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyCustodyProc(clientData, tkwin)
+    ClientData clientData;	/* Information about the busy window. */
+    Tk_Window tkwin;		/* Not used. */
+{
+    Busy *busyPtr = clientData;
+
+    Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc, 
+	busyPtr);
+    HideBusyWindow(busyPtr);
+    busyPtr->tkBusy = NULL;
+    Tcl_EventuallyFree(busyPtr, DestroyBusy);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BusyGeometryProc --
+ *
+ *	This procedure is invoked by Tk_GeometryRequest for busy
+ *	windows.  Busy windows never request geometry, so it's
+ *	unlikely that this routine will ever be called.  The routine
+ *	exists simply as a place holder for the GeomProc in the
+ *	Geometry Manager structure.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyGeometryProc(clientData, tkwin)
+    ClientData clientData;	/* Information about window that got new
+				 * preferred geometry.  */
+    Tk_Window tkwin;		/* Other Tk-related information about the
+			         * window. */
+{
+    /* Should never get here */
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * RefWinEventProc --
+ *
+ *	This procedure is invoked by the Tk dispatcher for the
+ *	following events on the reference window.  If the reference and
+ *	parent windows are the same, only the first event is
+ *	important.
+ *
+ *	   1) ConfigureNotify  - The reference window has been resized or
+ *				 moved.  Move and resize the busy window
+ *				 to be the same size and position of the
+ *				 reference window.
+ *
+ *	   2) DestroyNotify    - The reference window was destroyed. Destroy
+ *				 the busy window and the free resources
+ *				 used.
+ *
+ *	   3) MapNotify	       - The reference window was (re)shown. Map the
+ *				 busy window again.
+ *
+ *	   4) UnmapNotify      - The reference window was hidden. Unmap the
+ *				 busy window.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the reference window gets deleted, internal structures get
+ *	cleaned up.  When it gets resized, the busy window is resized
+ *	accordingly. If it's displayed, the busy window is displayed. And
+ *	when it's hidden, the busy window is unmapped.
+ *
+ * -------------------------------------------------------------------
+ */
+static void
+RefWinEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Busy window record */
+    register XEvent *eventPtr;	/* Event which triggered call to routine */
+{
+    register Busy *busyPtr = clientData;
+
+    switch (eventPtr->type) {
+    case ReparentNotify:
+    case DestroyNotify:
+
+	/*
+	 * Arrange for the busy structure to be removed at a proper time.
+	 */
+
+	Tcl_EventuallyFree(busyPtr, DestroyBusy);
+	break;
+
+    case ConfigureNotify:
+	if ((busyPtr->width != Tk_Width(busyPtr->tkRef)) ||
+	    (busyPtr->height != Tk_Height(busyPtr->tkRef)) ||
+	    (busyPtr->x != Tk_X(busyPtr->tkRef)) ||
+	    (busyPtr->y != Tk_Y(busyPtr->tkRef))) {
+	    int x, y;
+
+	    busyPtr->width = Tk_Width(busyPtr->tkRef);
+	    busyPtr->height = Tk_Height(busyPtr->tkRef);
+	    busyPtr->x = Tk_X(busyPtr->tkRef);
+	    busyPtr->y = Tk_Y(busyPtr->tkRef);
+
+	    x = y = 0;
+
+	    if (busyPtr->tkParent != busyPtr->tkRef) {
+		Tk_Window tkwin;
+
+		for (tkwin = busyPtr->tkRef; (tkwin != NULL) &&
+			 (!Tk_IsTopLevel(tkwin)); tkwin = Tk_Parent(tkwin)) {
+		    if (tkwin == busyPtr->tkParent) {
+			break;
+		    }
+		    x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+		    y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+		}
+	    }
+#if BUSYDEBUG
+	    PurifyPrintf("menubar2: width=%d, height=%d\n", 
+		busyPtr->width, busyPtr->height);
+#endif
+	    if (busyPtr->tkBusy != NULL) {
+#if BUSYDEBUG
+		fprintf(stderr, "busy window %s is at %d,%d %dx%d\n", 
+			Tk_PathName(busyPtr->tkBusy),
+			x, y, busyPtr->width, busyPtr->height);
+#endif
+		Tk_MoveResizeWindow(busyPtr->tkBusy, x, y, busyPtr->width,
+		    busyPtr->height);
+		if (busyPtr->isBusy) {
+		    ShowBusyWindow(busyPtr);
+		}
+	    }
+	}
+	break;
+
+    case MapNotify:
+	if ((busyPtr->tkParent != busyPtr->tkRef) && (busyPtr->isBusy)) {
+	    ShowBusyWindow(busyPtr);
+	}
+	break;
+
+    case UnmapNotify:
+	if (busyPtr->tkParent != busyPtr->tkRef) {
+	    HideBusyWindow(busyPtr);
+	}
+	break;
+    }
+}
+
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ConfigureBusy --
+ *
+ *	This procedure is called from the Tk event dispatcher. It
+ * 	releases X resources and memory used by the busy window and
+ *	updates the internal hash table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory and resources are released and the Tk event handler
+ *	is removed.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ConfigureBusy(interp, busyPtr, argc, argv)
+    Tcl_Interp *interp;
+    Busy *busyPtr;
+    int argc;
+    char **argv;
+{
+    Tk_Cursor oldCursor;
+
+    oldCursor = busyPtr->cursor;
+    if (Tk_ConfigureWidget(interp, busyPtr->tkRef, configSpecs, argc, argv,
+	    (char *)busyPtr, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (busyPtr->cursor != oldCursor) {
+	if (busyPtr->cursor == None) {
+	    Tk_UndefineCursor(busyPtr->tkBusy);
+	} else {
+	    Tk_DefineCursor(busyPtr->tkBusy, busyPtr->cursor);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * CreateBusy --
+ *
+ *	Creates a child transparent window that obscures its parent
+ *	window thereby effectively blocking device events.  The size
+ *	and position of the busy window is exactly that of the reference
+ *	window.
+ *
+ *	We want to create sibling to the window to be blocked.  If the
+ *	busy window is a child of the window to be blocked, Enter/Leave
+ *	events can sneak through.  Futhermore under WIN32, messages of
+ *	transparent windows are sent directly to the parent.  The only
+ *	exception to this are toplevels, since we can't make a sibling.
+ *	Fortunately, toplevel windows rarely receive events that need
+ *	blocking.
+ *
+ * Results:
+ *	Returns a pointer to the new busy window structure.
+ *
+ * Side effects:
+ *	When the busy window is eventually displayed, it will screen
+ *	device events (in the area of the reference window) from reaching
+ *	its parent window and its children.  User feed back can be
+ *	achieved by changing the cursor.
+ *
+ * -------------------------------------------------------------------
+ */
+static Busy *
+CreateBusy(interp, tkRef)
+    Tcl_Interp *interp;		/* Interpreter to report error to */
+    Tk_Window tkRef;		/* Window hosting the busy window */
+{
+    Busy *busyPtr;
+    int length;
+    char *fmt, *name;
+    Tk_Window tkBusy;
+    Window parent;
+    Tk_Window tkChild, tkParent;
+    Tk_FakeWin *winPtr;
+    int x, y;
+
+    busyPtr = Blt_Calloc(1, sizeof(Busy));
+    assert(busyPtr);
+    x = y = 0;
+    length = strlen(Tk_Name(tkRef));
+    name = Blt_Malloc(length + 6);
+    if (Tk_IsTopLevel(tkRef)) {
+	fmt = "_Busy";		/* Child */
+	tkParent = tkRef;
+    } else {
+	Tk_Window tkwin;
+
+	fmt = "%s_Busy";	/* Sibling */
+	tkParent = Tk_Parent(tkRef);
+	for (tkwin = tkRef; (tkwin != NULL) && (!Tk_IsTopLevel(tkwin)); 
+	     tkwin = Tk_Parent(tkwin)) {
+	    if (tkwin == tkParent) {
+		break;
+	    }
+	    x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+	    y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+	}
+    }
+    for (tkChild = Blt_FirstChild(tkParent); tkChild != NULL; 
+	 tkChild = Blt_NextChild(tkChild)) {
+	Tk_MakeWindowExist(tkChild);
+    }
+    sprintf(name, fmt, Tk_Name(tkRef));
+    tkBusy = Tk_CreateWindow(interp, tkParent, name, (char *)NULL);
+    Blt_Free(name);
+
+    if (tkBusy == NULL) {
+	return NULL;
+    }
+    Tk_MakeWindowExist(tkRef);
+    busyPtr->display = Tk_Display(tkRef);
+    busyPtr->interp = interp;
+    busyPtr->tkRef = tkRef;
+    busyPtr->tkParent = tkParent;
+    busyPtr->tkBusy = tkBusy;
+    busyPtr->width = Tk_Width(tkRef);
+    busyPtr->height = Tk_Height(tkRef);
+    busyPtr->x = Tk_X(tkRef);
+    busyPtr->y = Tk_Y(tkRef);
+    busyPtr->cursor = None;
+    busyPtr->isBusy = FALSE;
+    Tk_SetClass(tkBusy, "Busy");
+#if (TK_MAJOR_VERSION > 4)
+    Blt_SetWindowInstanceData(tkBusy, busyPtr);
+#endif
+    winPtr = (Tk_FakeWin *) tkRef;
+    if (winPtr->flags & TK_REPARENTED) {
+	/*
+	 * This works around a bug in the implementation of menubars
+	 * for non-MacIntosh window systems (Win32 and X11).  Tk
+	 * doesn't reset the pointers to the parent window when the
+	 * menu is reparented (winPtr->parentPtr points to the
+	 * wrong window). We get around this by determining the parent
+	 * via the native API calls.
+	 */
+#ifdef WIN32
+	{
+	    HWND hWnd;
+	    RECT rect;
+
+	    hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkRef)));
+	    parent = (Window) hWnd;
+	    if (GetWindowRect(hWnd, &rect)) {
+		busyPtr->width = rect.right - rect.left;
+		busyPtr->height = rect.bottom - rect.top;
+#if BUSYDEBUG
+		PurifyPrintf("menubar: width=%d, height=%d\n",
+		    busyPtr->width, busyPtr->height);
+#endif
+	    }
+	}
+#else
+	parent = Blt_GetParent(Tk_Display(tkRef), Tk_WindowId(tkRef));
+#endif
+    } else {
+	parent = Tk_WindowId(tkParent);
+#ifdef WIN32
+	parent = (Window) Tk_GetHWND(parent);
+#endif
+    }
+    Blt_MakeTransparentWindowExist(tkBusy, parent, TRUE);
+
+#if BUSYDEBUG
+    PurifyPrintf("menubar1: width=%d, height=%d\n", busyPtr->width, 
+	busyPtr->height);
+    fprintf(stderr, "busy window %s is at %d,%d %dx%d\n", Tk_PathName(tkBusy),
+	    x, y, busyPtr->width, busyPtr->height);
+#endif
+    Tk_MoveResizeWindow(tkBusy, x, y, busyPtr->width, busyPtr->height);
+
+    /* Only worry if the busy window is destroyed.  */
+    Tk_CreateEventHandler(tkBusy, StructureNotifyMask, BusyEventProc, busyPtr);
+    /*
+     * Indicate that the busy window's geometry is being managed.
+     * This will also notify us if the busy window is ever packed.
+     */
+    Tk_ManageGeometry(tkBusy, &busyMgrInfo, busyPtr);
+    if (busyPtr->cursor != None) {
+	Tk_DefineCursor(tkBusy, busyPtr->cursor);
+    }
+    /* Track the reference window to see if it is resized or destroyed.  */
+    Tk_CreateEventHandler(tkRef, StructureNotifyMask, RefWinEventProc, busyPtr);
+    return busyPtr;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * DestroyBusy --
+ *
+ *	This procedure is called from the Tk event dispatcher. It
+ *	releases X resources and memory used by the busy window and
+ *	updates the internal hash table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory and resources are released and the Tk event handler
+ *	is removed.
+ *
+ * -------------------------------------------------------------------
+ */
+static void
+DestroyBusy(data)
+    DestroyData data;		/* Busy window structure record */
+{
+    Busy *busyPtr = (Busy *)data;
+
+    Tk_FreeOptions(configSpecs, (char *)busyPtr, busyPtr->display, 0);
+    if (busyPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(busyPtr->tablePtr, busyPtr->hashPtr);
+    }
+    Tk_DeleteEventHandler(busyPtr->tkRef, StructureNotifyMask,
+	RefWinEventProc, busyPtr);
+    if (busyPtr->tkBusy != NULL) {
+	Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask,
+	    BusyEventProc, busyPtr);
+	Tk_ManageGeometry(busyPtr->tkBusy, NULL, busyPtr);
+	Tk_DestroyWindow(busyPtr->tkBusy);
+    }
+    Blt_Free(busyPtr);
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * GetBusy --
+ *
+ *	Returns the busy window structure associated with the reference
+ *	window, keyed by its path name.  The clientData argument is
+ *	the main window of the interpreter, used to search for the
+ *	reference window in its own window hierarchy.
+ *
+ * Results:
+ *	If path name represents a reference window with a busy window, a
+ *	pointer to the busy window structure is returned. Otherwise,
+ *	NULL is returned and an error message is left in
+ *	interp->result.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+GetBusy(dataPtr, interp, pathName, busyPtrPtr)
+    BusyInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    char *pathName;		/* Path name of parent window */
+    Busy **busyPtrPtr;		/* Will contain address of busy window if
+				 * found. */
+{
+    Blt_HashEntry *hPtr;
+    Tk_Window tkwin;
+
+    tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_FindHashEntry(&dataPtr->busyTable, (char *)tkwin);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    *busyPtrPtr = ((Busy *)Blt_GetHashValue(hPtr));
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * HoldBusy --
+ *
+ *	Creates (if necessary) and maps a busy window, thereby
+ *	preventing device events from being be received by the parent
+ *	window and its children.
+ *
+ * Results:
+ *	Returns a standard TCL result. If path name represents a busy
+ *	window, it is unmapped and TCL_OK is returned. Otherwise,
+ *	TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side effects:
+ *	The busy window is created and displayed, blocking events from
+ *	the parent window and its children.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+HoldBusy(dataPtr, interp, argc, argv)
+    BusyInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;		/* Window name and option pairs */
+{
+    Tk_Window tkwin;
+    Blt_HashEntry *hPtr;
+    Busy *busyPtr;
+    int isNew;
+    int result;
+
+    tkwin = Tk_NameToWindow(interp, argv[0], Tk_MainWindow(interp));
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_CreateHashEntry(&dataPtr->busyTable, (char *)tkwin, &isNew);
+    if (isNew) {
+	busyPtr = (Busy *)CreateBusy(interp, tkwin);
+	if (busyPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	Blt_SetHashValue(hPtr, (char *)busyPtr);
+	busyPtr->hashPtr = hPtr;
+    } else {
+	busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+    }
+    busyPtr->tablePtr = &dataPtr->busyTable;
+    result = ConfigureBusy(interp, busyPtr, argc - 1, argv + 1);
+
+    /* 
+     * Don't map the busy window unless the reference window is also
+     * currently displayed.
+     */
+    if (Tk_IsMapped(busyPtr->tkRef)) {
+	ShowBusyWindow(busyPtr);
+    } else {
+	HideBusyWindow(busyPtr);
+    }
+    busyPtr->isBusy = TRUE;
+    return result;
+}
+
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * StatusOp --
+ *
+ *	Returns the status of the busy window; whether it's blocking
+ *	events or not.
+ *
+ * Results:
+ *	Returns a standard TCL result. If path name represents a busy
+ *	window, the status is returned via interp->result and TCL_OK
+ *	is returned. Otherwise, TCL_ERROR is returned and an error
+ *	message is left in interp->result.
+ *
+ * -------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StatusOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report error to */
+    int argc;			/* Not used. */
+    char **argv;
+{
+    BusyInterpData *dataPtr = clientData;
+    Busy *busyPtr;
+
+    if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(busyPtr);
+    Blt_SetBooleanResult(interp, busyPtr->isBusy);
+    Tcl_Release(busyPtr);
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ForgetOp --
+ *
+ *	Destroys the busy window associated with the reference window and
+ *	arranges for internal resources to the released when they're
+ *	not being used anymore.
+ *
+ * Results:
+ *	Returns a standard TCL result. If path name represents a busy
+ *	window, it is destroyed and TCL_OK is returned. Otherwise,
+ *	TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side effects:
+ *	The busy window is removed.  Other related memory and resources
+ *	are eventually released by the Tk dispatcher.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ForgetOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;
+{
+    BusyInterpData *dataPtr = clientData;
+    Busy *busyPtr;
+    register int i;
+
+    for (i = 2; i < argc; i++) {
+	if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	/* Unmap the window even though it will be soon destroyed */
+	HideBusyWindow(busyPtr);
+	Tcl_EventuallyFree(busyPtr, DestroyBusy);
+    }
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * ReleaseOp --
+ *
+ *	Unmaps the busy window, thereby permitting device events
+ *	to be received by the parent window and its children.
+ *
+ * Results:
+ *	Returns a standard TCL result. If path name represents a busy
+ *	window, it is unmapped and TCL_OK is returned. Otherwise,
+ *	TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side effects:
+ *	The busy window is hidden, allowing the parent window and
+ *	its children to receive events again.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+ReleaseOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;
+{
+    BusyInterpData *dataPtr = clientData;
+    Busy *busyPtr;
+    int i;
+
+    for (i = 2; i < argc; i++) {
+	if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	HideBusyWindow(busyPtr);
+	busyPtr->isBusy = FALSE;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ *	Reports the names of all widgets with busy windows attached to
+ *	them, matching a given pattern.  If no pattern is given, all
+ *	busy widgets are listed.
+ *
+ * Results:
+ *	Returns a TCL list of the names of the widget with busy windows
+ *	attached to them, regardless if the widget is currently busy
+ *	or not.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+NamesOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;
+{
+    BusyInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Busy *busyPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+	if ((argc == 2) ||
+	    (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
+	    Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * BusyOp --
+ *
+ *	Reports the names of all widgets with busy windows attached to
+ *	them, matching a given pattern.  If no pattern is given, all
+ *	busy widgets are listed.
+ *
+ * Results:
+ *	Returns a TCL list of the names of the widget with busy windows
+ *	attached to them, regardless if the widget is currently busy
+ *	or not.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+BusyOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;
+{
+    BusyInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Busy *busyPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+	if (!busyPtr->isBusy) {
+	    continue;
+	}
+	if ((argc == 2) || 
+	    (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
+	    Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------
+ *
+ * HoldOp --
+ *
+ *	Creates (if necessary) and maps a busy window, thereby
+ *	preventing device events from being be received by the parent
+ *      window and its children. The argument vector may contain
+ *	option-value pairs of configuration options to be set.
+ *
+ * Results:
+ *	Returns a standard TCL result.
+ *
+ * Side effects:
+ *	The busy window is created and displayed, blocking events from the
+ *	parent window and its children.
+ *
+ * -------------------------------------------------------------------
+ */
+static int
+HoldOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;		/* Window name and option pairs */
+{
+    BusyInterpData *dataPtr = clientData;
+    register int i, count;
+
+    if ((argv[1][0] == 'h') && (strcmp(argv[1], "hold") == 0)) {
+	argc--, argv++;		/* Command used "hold" keyword */
+    }
+    for (i = 1; i < argc; i++) {
+	/*
+	 * Find the end of the option-value pairs for this window.
+	 */
+	for (count = i + 1; count < argc; count += 2) {
+	    if (argv[count][0] != '-') {
+		break;
+	    }
+	}
+	if (count > argc) {
+	    count = argc;
+	}
+	if (HoldBusy(dataPtr, interp, count - i, argv + i) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	i = count;
+    }
+    return TCL_OK;
+}
+
+/* ARGSUSED*/
+static int
+CgetOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;		/* Widget pathname and option switch */
+{
+    BusyInterpData *dataPtr = clientData;
+    Busy *busyPtr;
+    int result;
+
+    if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(busyPtr);
+    result = Tk_ConfigureValue(interp, busyPtr->tkRef, configSpecs,
+	(char *)busyPtr, argv[3], 0);
+    Tcl_Release(busyPtr);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	This procedure is called to process an argv/argc list in order
+ *	to configure (or reconfigure) a busy window.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information get set for busyPtr; old resources
+ *	get freed, if there were any.  The busy window destroyed and
+ *	recreated in a new parent window.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;		/* Reference window path name and options */
+{
+    BusyInterpData *dataPtr = clientData;
+    Busy *busyPtr;
+    int result;
+
+    if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (argc == 3) {
+	result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
+	    (char *)busyPtr, (char *)NULL, 0);
+    } else if (argc == 4) {
+	result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
+	    (char *)busyPtr, argv[3], 0);
+    } else {
+	Tcl_Preserve(busyPtr);
+	result = ConfigureBusy(interp, busyPtr, argc - 3, argv + 3);
+	Tcl_Release(busyPtr);
+    }
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BusyInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting the "busy" command 
+ *	is destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys all the hash table managing the busy windows.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+BusyInterpDeleteProc(clientData, interp)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+{
+    BusyInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Busy *busyPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	busyPtr = (Busy *)Blt_GetHashValue(hPtr);
+	busyPtr->hashPtr = NULL;
+	DestroyBusy((DestroyData)busyPtr);
+    }
+    Blt_DeleteHashTable(&dataPtr->busyTable);
+    Tcl_DeleteAssocData(interp, BUSY_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Busy Sub-command specification:
+ *
+ *	- Name of the sub-command.
+ *	- Minimum number of characters needed to unambiguously
+ *        recognize the sub-command.
+ *	- Pointer to the function to be called for the sub-command.
+ *	- Minimum number of arguments accepted.
+ *	- Maximum number of arguments accepted.
+ *	- String to be displayed for usage (arguments only).
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec busyOps[] =
+{
+    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "window option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "window ?options?...",},
+    {"forget", 1, (Blt_Op)ForgetOp, 2, 0, "?window?...",},
+    {"hold", 3, (Blt_Op)HoldOp, 3, 0, 
+	"window ?options?... ?window options?...",},
+    {"isbusy", 1, (Blt_Op)BusyOp, 2, 3, "?pattern?",},
+    {"names", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+    {"release", 1, (Blt_Op)ReleaseOp, 2, 0, "?window?...",},
+    {"status", 1, (Blt_Op)StatusOp, 3, 3, "window",},
+    {"windows", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+};
+static int nBusyOps = sizeof(busyOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BusyCmd --
+ *
+ *	This procedure is invoked to process the "busy" Tcl command.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BusyCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter associated with command */
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+    
+    if ((argc > 1) && (argv[1][0] == '.')) {
+	return HoldOp(clientData, interp, argc, argv);
+    }
+    proc = Blt_GetOp(interp, nBusyOps, busyOps, BLT_OP_ARG1, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (clientData, interp, argc, argv);
+    return result;
+}
+
+static BusyInterpData *
+GetBusyInterpData(interp)
+     Tcl_Interp *interp;
+{
+    BusyInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (BusyInterpData *)
+	Tcl_GetAssocData(interp, BUSY_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(BusyInterpData));
+	assert(dataPtr);
+	Tcl_SetAssocData(interp, BUSY_THREAD_KEY, BusyInterpDeleteProc, 
+		dataPtr);
+	Blt_InitHashTable(&dataPtr->busyTable, BLT_ONE_WORD_KEYS);
+    }
+    return dataPtr;
+}
+
+int
+Blt_BusyInit(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpec = {"busy", BusyCmd, };
+    BusyInterpData *dataPtr;
+
+    dataPtr = GetBusyInterpData(interp);
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+#endif /* NO_BUSY */
Index: trunk/kitgen/8.x/blt/generic/bltChain.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltChain.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltChain.c	(revision 175)
@@ -0,0 +1,445 @@
+/*
+ * bltChain.c --
+ *
+ *	The module implements a generic linked list package.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+
+#ifndef ALIGN
+#define ALIGN(a) \
+	(((size_t)a + (sizeof(double) - 1)) & (~(sizeof(double) - 1)))
+#endif /* ALIGN */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainCreate --
+ *
+ *	Creates a new linked list (chain) structure and initializes 
+ *	its pointers;
+ *
+ * Results:
+ *	Returns a pointer to the newly created chain structure.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Chain *
+Blt_ChainCreate()
+{
+    Blt_Chain *chainPtr;
+
+    chainPtr = Blt_Malloc(sizeof(Blt_Chain));
+    if (chainPtr != NULL) {
+	Blt_ChainInit(chainPtr);
+    }
+    return chainPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainAllocLink --
+ *
+ *	Creates a new chain link.  Unlink Blt_ChainNewLink, this 
+ *	routine also allocates extra memory in the node for data.
+ *
+ * Results:
+ *	The return value is the pointer to the newly created entry.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainAllocLink(extraSize)
+    unsigned int extraSize;
+{
+    Blt_ChainLink *linkPtr;
+    unsigned int linkSize;
+
+    linkSize = ALIGN(sizeof(Blt_ChainLink));
+    linkPtr = Blt_Calloc(1, linkSize + extraSize);
+    assert(linkPtr);
+    if (extraSize > 0) {
+	/* Point clientData at the memory beyond the normal structure. */
+	linkPtr->clientData = (ClientData)((char *)linkPtr + linkSize);
+    }
+    return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainNewLink --
+ *
+ *	Creates a new link.
+ *
+ * Results:
+ *	The return value is the pointer to the newly created link.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainNewLink()
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_Malloc(sizeof(Blt_ChainLink));
+    assert(linkPtr);
+    linkPtr->clientData = NULL;
+    linkPtr->nextPtr = linkPtr->prevPtr = NULL;
+    return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainReset --
+ *
+ *	Removes all the links from the chain, freeing the memory for
+ *	each link.  Memory pointed to by the link (clientData) is not
+ *	freed.  It's the caller's responsibility to deallocate it.
+ *
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+void
+Blt_ChainReset(chainPtr)
+    Blt_Chain *chainPtr;	/* Chain to clear */
+{
+    if (chainPtr != NULL) {
+	Blt_ChainLink *oldPtr;
+	Blt_ChainLink *linkPtr = chainPtr->headPtr;
+
+	while (linkPtr != NULL) {
+	    oldPtr = linkPtr;
+	    linkPtr = linkPtr->nextPtr;
+	    Blt_Free(oldPtr);
+	}
+	Blt_ChainInit(chainPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainDestroy
+ *
+ *     Frees all the nodes from the chain and deallocates the memory
+ *     allocated for the chain structure itself.  It's assumed that
+ *     the chain was previous allocated by Blt_ChainCreate.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainDestroy(chainPtr)
+    Blt_Chain *chainPtr;
+{
+    if (chainPtr != NULL) {
+	Blt_ChainReset(chainPtr);
+	Blt_Free(chainPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainInit --
+ *
+ *	Initializes a linked list.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainInit(chainPtr)
+    Blt_Chain *chainPtr;
+{
+    chainPtr->nLinks = 0;
+    chainPtr->headPtr = chainPtr->tailPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainLinkAfter --
+ *
+ *	Inserts an entry following a given entry.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainLinkAfter(chainPtr, linkPtr, afterPtr)
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr, *afterPtr;
+{
+    if (chainPtr->headPtr == NULL) {
+	chainPtr->tailPtr = chainPtr->headPtr = linkPtr;
+    } else {
+	if (afterPtr == NULL) {
+	    /* Prepend to the front of the chain */
+	    linkPtr->nextPtr = chainPtr->headPtr;
+	    linkPtr->prevPtr = NULL;
+	    chainPtr->headPtr->prevPtr = linkPtr;
+	    chainPtr->headPtr = linkPtr;
+	} else {
+	    linkPtr->nextPtr = afterPtr->nextPtr;
+	    linkPtr->prevPtr = afterPtr;
+	    if (afterPtr == chainPtr->tailPtr) {
+		chainPtr->tailPtr = linkPtr;
+	    } else {
+		afterPtr->nextPtr->prevPtr = linkPtr;
+	    }
+	    afterPtr->nextPtr = linkPtr;
+	}
+    }
+    chainPtr->nLinks++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainLinkBefore --
+ *
+ *	Inserts a link preceding a given link.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainLinkBefore(chainPtr, linkPtr, beforePtr)
+    Blt_Chain *chainPtr;	/* Chain to contain new entry */
+    Blt_ChainLink *linkPtr;	/* New entry to be inserted */
+    Blt_ChainLink *beforePtr;	/* Entry to link before */
+{
+    if (chainPtr->headPtr == NULL) {
+	chainPtr->tailPtr = chainPtr->headPtr = linkPtr;
+    } else {
+	if (beforePtr == NULL) {
+	    /* Append to the end of the chain. */
+	    linkPtr->nextPtr = NULL;
+	    linkPtr->prevPtr = chainPtr->tailPtr;
+	    chainPtr->tailPtr->nextPtr = linkPtr;
+	    chainPtr->tailPtr = linkPtr;
+	} else {
+	    linkPtr->prevPtr = beforePtr->prevPtr;
+	    linkPtr->nextPtr = beforePtr;
+	    if (beforePtr == chainPtr->headPtr) {
+		chainPtr->headPtr = linkPtr;
+	    } else {
+		beforePtr->prevPtr->nextPtr = linkPtr;
+	    }
+	    beforePtr->prevPtr = linkPtr;
+	}
+    }
+    chainPtr->nLinks++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainUnlinkLink --
+ *
+ *	Unlinks a link from the chain. The link is not deallocated, 
+ *	but only removed from the chain.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainUnlinkLink(chainPtr, linkPtr)
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr;
+{
+    int unlinked;		/* Indicates if the link is actually
+				 * removed from the chain. */
+
+    unlinked = FALSE;
+    if (chainPtr->headPtr == linkPtr) {
+	chainPtr->headPtr = linkPtr->nextPtr;
+	unlinked = TRUE;
+    }
+    if (chainPtr->tailPtr == linkPtr) {
+	chainPtr->tailPtr = linkPtr->prevPtr;
+	unlinked = TRUE;
+    }
+    if (linkPtr->nextPtr != NULL) {
+	linkPtr->nextPtr->prevPtr = linkPtr->prevPtr;
+	unlinked = TRUE;
+    }
+    if (linkPtr->prevPtr != NULL) {
+	linkPtr->prevPtr->nextPtr = linkPtr->nextPtr;
+	unlinked = TRUE;
+    }
+    if (unlinked) {
+	chainPtr->nLinks--;
+    }
+    linkPtr->prevPtr = linkPtr->nextPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainDeleteLink --
+ *
+ *	Unlinks and also frees the given link.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainDeleteLink(chainPtr, linkPtr)
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr;
+{
+    Blt_ChainUnlinkLink(chainPtr, linkPtr);
+    Blt_Free(linkPtr);
+}
+
+Blt_ChainLink *
+Blt_ChainAppend(chainPtr, clientData)
+    Blt_Chain *chainPtr;
+    ClientData clientData;
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_ChainNewLink();
+    Blt_ChainLinkBefore(chainPtr, linkPtr, (Blt_ChainLink *)NULL);
+    Blt_ChainSetValue(linkPtr, clientData);
+    return linkPtr;
+}
+
+Blt_ChainLink *
+Blt_ChainPrepend(chainPtr, clientData)
+    Blt_Chain *chainPtr;
+    ClientData clientData;
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_ChainNewLink();
+    Blt_ChainLinkAfter(chainPtr, linkPtr, (Blt_ChainLink *)NULL);
+    Blt_ChainSetValue(linkPtr, clientData);
+    return linkPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainGetNthLink --
+ *
+ *	Find the link at the given position in the chain.
+ *
+ * Results:
+ *	Returns the pointer to the link, if that numbered link
+ *	exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ChainLink *
+Blt_ChainGetNthLink(chainPtr, position)
+    Blt_Chain *chainPtr;	/* Chain to traverse */
+    int position;		/* Index of link to select from front
+				 * or back of the chain. */
+{
+    Blt_ChainLink *linkPtr;
+
+    if (chainPtr != NULL) {
+	for (linkPtr = chainPtr->headPtr; linkPtr != NULL;
+	    linkPtr = linkPtr->nextPtr) {
+	    if (position == 0) {
+		return linkPtr;
+	    }
+	    position--;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ChainSort --
+ *
+ *	Sorts the chain according to the given comparison routine.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The chain is reordered.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ChainSort(chainPtr, proc)
+    Blt_Chain *chainPtr;	/* Chain to traverse */
+    Blt_ChainCompareProc *proc;
+{
+    Blt_ChainLink **linkArr;
+    register Blt_ChainLink *linkPtr;
+    register int i;
+
+    if (chainPtr->nLinks < 2) {
+	return;
+    }
+    linkArr = Blt_Malloc(sizeof(Blt_ChainLink *) * (chainPtr->nLinks + 1));
+    if (linkArr == NULL) {
+	return;			/* Out of memory. */
+    }
+    i = 0;
+    for (linkPtr = chainPtr->headPtr; linkPtr != NULL; 
+	 linkPtr = linkPtr->nextPtr) { 
+	linkArr[i++] = linkPtr;
+    }
+    qsort((char *)linkArr, chainPtr->nLinks, sizeof(Blt_ChainLink *),
+	(QSortCompareProc *)proc);
+
+    /* Rethread the chain. */
+    linkPtr = linkArr[0];
+    chainPtr->headPtr = linkPtr;
+    linkPtr->prevPtr = NULL;
+    for (i = 1; i < chainPtr->nLinks; i++) {
+	linkPtr->nextPtr = linkArr[i];
+	linkPtr->nextPtr->prevPtr = linkPtr;
+	linkPtr = linkPtr->nextPtr;
+    }
+    chainPtr->tailPtr = linkPtr;
+    linkPtr->nextPtr = NULL;
+    Blt_Free(linkArr);
+}
Index: trunk/kitgen/8.x/blt/generic/bltChain.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltChain.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltChain.h	(revision 175)
@@ -0,0 +1,85 @@
+/*
+ * bltChain.h --
+ *
+ * Copyright 1993-2000 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#ifndef _BLT_CHAIN_H
+#define _BLT_CHAIN_H
+
+typedef struct Blt_ChainLinkStruct Blt_ChainLink;
+
+/*
+ * A Blt_ChainLink is the container structure for the Blt_Chain.
+ */
+
+struct Blt_ChainLinkStruct {
+    Blt_ChainLink *prevPtr;	/* Link to the previous link */
+    Blt_ChainLink *nextPtr;	/* Link to the next link */
+    ClientData clientData;	/* Pointer to the data object */
+};
+
+typedef int (Blt_ChainCompareProc) _ANSI_ARGS_((Blt_ChainLink **l1PtrPtr, 
+	Blt_ChainLink **l2PtrPtr));
+
+/*
+ * A Blt_Chain is a doubly chained list structure.
+ */
+typedef struct {
+    Blt_ChainLink *headPtr;	/* Pointer to first element in chain */
+    Blt_ChainLink *tailPtr;	/* Pointer to last element in chain */
+    int nLinks;			/* Number of elements in chain */
+} Blt_Chain;
+
+extern void Blt_ChainInit _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern Blt_Chain *Blt_ChainCreate _ANSI_ARGS_(());
+extern void Blt_ChainDestroy _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern Blt_ChainLink *Blt_ChainNewLink _ANSI_ARGS_((void));
+extern Blt_ChainLink *Blt_ChainAllocLink _ANSI_ARGS_((unsigned int size));
+extern Blt_ChainLink *Blt_ChainAppend _ANSI_ARGS_((Blt_Chain * chainPtr,
+	ClientData clientData));
+extern Blt_ChainLink *Blt_ChainPrepend _ANSI_ARGS_((Blt_Chain * chainPtr,
+	ClientData clientData));
+extern void Blt_ChainReset _ANSI_ARGS_((Blt_Chain * chainPtr));
+extern void Blt_ChainLinkAfter _ANSI_ARGS_((Blt_Chain * chainPtr,
+	Blt_ChainLink * linkPtr, Blt_ChainLink * afterLinkPtr));
+extern void Blt_ChainLinkBefore _ANSI_ARGS_((Blt_Chain * chainPtr,
+	Blt_ChainLink * linkPtr, Blt_ChainLink * beforeLinkPtr));
+extern void Blt_ChainUnlinkLink _ANSI_ARGS_((Blt_Chain * chainPtr,
+	Blt_ChainLink * linkPtr));
+extern void Blt_ChainDeleteLink _ANSI_ARGS_((Blt_Chain * chainPtr,
+	Blt_ChainLink * linkPtr));
+extern Blt_ChainLink *Blt_ChainGetNthLink _ANSI_ARGS_((Blt_Chain * chainPtr, int n));
+extern void Blt_ChainSort _ANSI_ARGS_((Blt_Chain * chainPtr,
+	Blt_ChainCompareProc * proc));
+
+#define Blt_ChainGetLength(c)	(((c) == NULL) ? 0 : (c)->nLinks)
+#define Blt_ChainFirstLink(c)	(((c) == NULL) ? NULL : (c)->headPtr)
+#define Blt_ChainLastLink(c)	(((c) == NULL) ? NULL : (c)->tailPtr)
+#define Blt_ChainPrevLink(l)	((l)->prevPtr)
+#define Blt_ChainNextLink(l) 	((l)->nextPtr)
+#define Blt_ChainGetValue(l)  	((l)->clientData)
+#define Blt_ChainSetValue(l, value) ((l)->clientData = (ClientData)(value))
+#define Blt_ChainAppendLink(c, l) \
+	(Blt_ChainLinkBefore((c), (l), (Blt_ChainLink *)NULL))
+#define Blt_ChainPrependLink(c, l) \
+	(Blt_ChainLinkAfter((c), (l), (Blt_ChainLink *)NULL))
+
+#endif /* _BLT_CHAIN_H */
Index: trunk/kitgen/8.x/blt/generic/bltColor.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltColor.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltColor.c	(revision 175)
@@ -0,0 +1,981 @@
+
+/*
+ * bltColor.c --
+ *
+ *	This module contains routines for color allocation strategies
+ *	used with color images in the BLT toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * Color strategies of 8-bit visuals:
+ *
+ * Try to "best" represent an N-color image into 8-bit (256 color)
+ * colormap.  The simplest method is use the high bits of each RGB
+ * value (3 bits for red and green, 2 bits for blue).  But this
+ * produces lots of contouring since the distribution of colors tends
+ * to be clustered.  Other problems: probably can't allocate even 256
+ * colors. Other applications will have already taken some color
+ * slots.  Furthermore, we might be displaying several images, and we
+ * can't assume that all images are representative of the colors used.
+ *
+ * If we use a private colormap, we may want to allocate some number
+ * of colors from the default colormap to prevent flashing when
+ * colormaps are switched.
+ *
+ * Switches:
+ *
+ *	-exact boolean		Try to match the colors of the image rather
+ *				then generating a "best" color ramp.
+ *
+ *	-threshold value	Maximum average error. Indicates how far
+ *				to reduce the quantized color palette.
+ *
+ *	-tolerance value	Allow colors within this distance to match.
+ *				This will weight allocation towards harder
+ *				to match colors, rather than the most
+ *				frequent.
+ *
+ *	-mincolors number	Minimum number of reduced quantized colors.
+ *				or color ramp.
+ *
+ *	-dither	boolean		Turn on/off Floyd-Steinberg dithering.
+ *
+ *	-keep number		Hint to keep the first N colors in the
+ *				in the default colormap.  This only applies to
+ *				private colormaps.
+ *
+ *	-ramp number		Number of colors to use in a linear ramp.
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef WIN32
+
+#include "bltHash.h"
+#include "bltImage.h"
+
+#define NCOLORS		256
+
+
+static void
+GetPaletteSizes(nColors, nRedsPtr, nGreensPtr, nBluesPtr)
+    int nColors;		/* Number of colors requested. */
+    unsigned int *nRedsPtr;	/* (out) Number of red components. */
+    unsigned int *nGreensPtr;	/* (out) Number of green components. */
+    unsigned int *nBluesPtr;	/* (out) Number of blue components. */
+{
+    unsigned int nBlues, nReds, nGreens;
+
+    assert(nColors > 1); 
+    nBlues = nReds = nGreens = 0;
+    while ((nBlues * nBlues * nBlues) <= nColors) {
+	nBlues++;
+    }
+    nBlues--;
+    while ((nReds * nReds * nBlues) <= nColors) {
+	nReds++;
+    }
+    nReds--;
+    nGreens = nColors / (nBlues * nReds);
+
+    *nRedsPtr = nReds;
+    *nGreensPtr = nGreens;
+    *nBluesPtr = nBlues;
+}
+
+static void
+BuildColorRamp(palettePtr, nColors)
+    Pix32 *palettePtr;
+    int nColors;
+{
+    register unsigned int r, g, b;
+    unsigned int short red, green, blue;
+    unsigned int nReds, nGreens, nBlues;
+
+    GetPaletteSizes(nColors, &nReds, &nGreens, &nBlues);
+    for (r = 0; r < nReds; r++) {
+	red = (r * USHRT_MAX) / (nReds - 1);
+	for (g = 0; g < nGreens; g++) {
+	    green = (g * USHRT_MAX) / (nGreens - 1);
+	    for (b = 0; b < nBlues; b++) {
+		blue = (b * USHRT_MAX) / (nBlues - 1);
+		palettePtr->Red = red;
+		palettePtr->Green = green;
+		palettePtr->Blue = blue;
+		palettePtr++;
+	    }
+	}
+    }
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * QueryColormap --
+ *
+ *	This is for psuedo-color displays only.  Fills an array or
+ *	XColors with the color values (RGB and pixel) currently
+ *	allocated in the colormap.
+ *
+ * Results:
+ *	The number of colors allocated is returned. The array "colorArr"
+ *	will contain the XColor values of each color in the colormap.
+ *
+ *---------------------------------------------------------------------- 
+ */
+
+static int
+QueryColormap(display, colorMap, mapColors, numMapColorsPtr)
+    Display *display;
+    Colormap colorMap;
+    XColor mapColors[];
+    int *numMapColorsPtr;
+{
+    unsigned long int pixelValues[NCOLORS];
+    int numAvail, numMapColors;
+    register int i;
+    register XColor *colorPtr;
+    register unsigned long *indexPtr;
+    int inUse[NCOLORS];
+
+    /* Initially, we assume all color cells are allocated. */
+    memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+
+    /*
+     * Start allocating color cells.  This will tell us which color cells
+     * haven't already been allocated in the colormap.  We'll release the
+     * cells as soon as we find out how many there are.
+     */
+    numAvail = 0;
+    for (indexPtr = pixelValues, i = 0; i < NCOLORS; i++, indexPtr++) {
+	if (!XAllocColorCells(display, colorMap, False, NULL, 0, indexPtr, 1)) {
+	    break;
+	}
+	inUse[*indexPtr] = TRUE;/* Indicate the cell is unallocated */
+	numAvail++;
+    }
+    XFreeColors(display, colorMap, pixelValues, numAvail, 0);
+
+    /*
+     * Put the indices of the cells already allocated into a color array.
+     * We'll use the array to query the RGB values of the allocated colors.
+     */
+    numMapColors = 0;
+    colorPtr = mapColors;
+    for (i = 0; i < NCOLORS; i++) {
+	if (!inUse[i]) {
+	    colorPtr->pixel = i;
+	    colorPtr->flags = (DoRed | DoGreen | DoBlue);
+	    colorPtr++, numMapColors++;
+	}
+    }
+    XQueryColors(display, colorMap, mapColors, numMapColors);
+    *numMapColorsPtr = numMapColors;
+#ifdef notdef
+    fprintf(stderr, "Number of colors (allocated/free) %d/%d\n", numMapColors,
+	numAvail);
+#endif
+    return numAvail;
+}
+
+static void
+FindClosestColor(colorPtr, mapColors, numMapColors)
+    ColorInfo *colorPtr;
+    XColor mapColors[];
+    int numMapColors;
+{
+    double r, g, b;
+    register int i;
+    double dist, min;
+    XColor *lastMatch;
+    register XColor *mapColorPtr;
+
+    min = DBL_MAX;	/* Any color is closer. */
+    lastMatch = NULL;
+
+    /* Linear search of color */
+
+    mapColorPtr = mapColors;
+    for (i = 0; i < numMapColors; i++, mapColorPtr++) {
+	r = (double)mapColorPtr->red - (double)colorPtr->exact.red;
+	g = (double)mapColorPtr->green - (double)colorPtr->exact.green;
+	b = (double)mapColorPtr->blue - (double)colorPtr->exact.blue;
+
+	dist = (r * r) + (b * b) + (g * g);
+	if (dist < min) {
+	    min = dist;
+	    lastMatch = mapColorPtr;
+	}
+    }
+    colorPtr->best = *lastMatch;
+    colorPtr->best.flags = (DoRed | DoGreen | DoBlue);
+    colorPtr->error = (float)sqrt(min);
+}
+
+static int
+CompareColors(a, b)
+    void *a, *b;
+{
+    ColorInfo *i1Ptr, *i2Ptr;
+
+    i1Ptr = *(ColorInfo **) a;
+    i2Ptr = *(ColorInfo **) b;
+    if (i2Ptr->error > i1Ptr->error) {
+	return 1;
+    } else if (i2Ptr->error < i1Ptr->error) {
+	return -1;
+    }
+    return 0;
+}
+
+static float
+MatchColors(colorTabPtr, rgbPtr, numColors, numAvailColors, numMapColors,
+    mapColors)
+    struct ColorTableStruct *colorTabPtr;
+    Pix32 *rgbPtr;
+    int numColors;
+    int numAvailColors;
+    int numMapColors;
+    XColor mapColors[NCOLORS];
+{
+    int numMatched;
+    float sum;
+    register int i;
+    register ColorInfo *colorPtr;
+
+    /*
+     * For each quantized color, compute and store the error (i.e
+     * the distance from a color that's already been allocated).
+     * We'll use this information to sort the colors based upon how
+     * badly they match and their frequency to the color image.
+     */
+    colorPtr = colorTabPtr->colorInfo;
+    for (i = 0; i < numColors; i++, colorPtr++, rgbPtr++) {
+	colorPtr->index = i;
+	colorTabPtr->sortedColors[i] = colorPtr;
+	colorPtr->exact.red = rgbPtr->Red;
+	colorPtr->exact.green = rgbPtr->Green;
+	colorPtr->exact.blue = rgbPtr->Blue;
+	colorPtr->exact.flags = (DoRed | DoGreen | DoBlue);
+	FindClosestColor(colorPtr, mapColors, numMapColors);
+    }
+
+    /* Sort the colors, first by frequency (most to least), then by
+     * matching error (worst to best).
+     */
+    qsort(colorTabPtr->sortedColors, numColors, sizeof(ColorInfo *),
+	(QSortCompareProc *)CompareColors);
+
+    for (i = 0; i < numColors; i++) {
+	colorPtr = colorTabPtr->sortedColors[i];
+	fprintf(stderr, "%d. %04x%04x%04x / %04x%04x%04x = %f (%d)\n", i,
+	    colorPtr->exact.red, colorPtr->exact.green, colorPtr->exact.blue,
+	    colorPtr->best.red, colorPtr->best.green, colorPtr->best.blue,
+	    colorPtr->error, colorPtr->freq);
+    }
+    sum = 0.0;
+    numMatched = 0;
+    for (i = numAvailColors; i < numColors; i++) {
+	colorPtr = colorTabPtr->sortedColors[i];
+	sum += colorPtr->error;
+	numMatched++;
+    }
+    if (numMatched > 0) {
+	sum /= numMatched;
+    }
+    return sum;
+}
+
+    
+static int
+AllocateColors(nImageColors, colorTabPtr, matchOnly)
+    int nImageColors;
+    struct ColorTableStruct *colorTabPtr;
+    int matchOnly;
+{
+    register int i;
+    register ColorInfo *colorPtr;
+    unsigned long int pixelValue;
+
+    for (i = 0; i < nImageColors; i++) {
+	colorPtr = colorTabPtr->sortedColors[i];
+	if (matchOnly) {
+	    XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+		&colorPtr->best);
+	    pixelValue = colorPtr->best.pixel;
+	} else {
+	    colorPtr->allocated = XAllocColor(colorTabPtr->display,
+		colorTabPtr->colorMap, &colorPtr->exact);
+
+	    if (colorPtr->allocated) {
+		pixelValue = colorPtr->exact.pixel;
+	    } else {
+		XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+		    &colorPtr->best);
+		pixelValue = colorPtr->best.pixel;
+	    }
+	}
+	colorTabPtr->pixelValues[colorPtr->index] = pixelValue;
+    }
+    colorTabPtr->nPixels = nImageColors;
+    return 1;
+}
+
+ColorTable
+Blt_CreateColorTable(tkwin)
+    Tk_Window tkwin;
+{
+    XVisualInfo visualInfo, *visualInfoPtr;
+    int nVisuals;
+    Visual *visualPtr;
+    Display *display;
+    struct ColorTableStruct *colorTabPtr;
+
+    display = Tk_Display(tkwin);
+    visualPtr = Tk_Visual(tkwin);
+
+    colorTabPtr = Blt_Calloc(1, sizeof(struct ColorTableStruct));
+    assert(colorTabPtr);
+    colorTabPtr->display = Tk_Display(tkwin);
+    colorTabPtr->colorMap = Tk_Colormap(tkwin);
+
+    visualInfo.screen = Tk_ScreenNumber(tkwin);
+    visualInfo.visualid = XVisualIDFromVisual(visualPtr);
+    visualInfoPtr = XGetVisualInfo(display, VisualScreenMask | VisualIDMask,
+	&visualInfo, &nVisuals);
+
+    colorTabPtr->visualInfo = *visualInfoPtr;
+    XFree(visualInfoPtr);
+
+    return colorTabPtr;
+}
+
+void
+Blt_FreeColorTable(colorTabPtr)
+    struct ColorTableStruct *colorTabPtr;
+{
+    if (colorTabPtr == NULL) {
+	return;
+    }
+    if (colorTabPtr->nPixels > 0) {
+	XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+	    colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+    }
+    Blt_Free(colorTabPtr);
+}
+
+extern int redAdjust, greenAdjust, blueAdjust;
+extern int redMaskShift, greenMaskShift, blueMaskShift;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DirectColorTable --
+ *
+ *	Creates a color table using the DirectColor visual.  We try
+ *	to allocate colors across the color spectrum.
+ *
+ * Results:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+ColorTable
+Blt_DirectColorTable(interp, tkwin, image)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    Blt_ColorImage image;
+{
+    struct ColorTableStruct *colorTabPtr;
+    Visual *visualPtr;
+    Display *display;
+    XColor color;
+    int nr, ng, nb;
+    int rBand, gBand, bBand;
+    int rLast, gLast, bLast;
+    unsigned int r, g, b;
+    unsigned int value;
+    register int i;
+
+    display = Tk_Display(tkwin);
+    visualPtr = Tk_Visual(tkwin);
+
+    colorTabPtr = Blt_CreateColorTable(tkwin);
+    /*
+     * Compute the number of distinct colors in each band
+     */
+    nr = ((unsigned int)visualPtr->red_mask >> redMaskShift) + 1;
+    ng = ((unsigned int)visualPtr->green_mask >> greenMaskShift) + 1;
+    nb = ((unsigned int)visualPtr->blue_mask >> blueMaskShift) + 1;
+
+#ifdef notdef
+    assert((nr <= visualPtr->map_entries) && (ng <= visualPtr->map_entries) &&
+	(nb <= visualPtr->map_entries));
+#endif
+    rBand = NCOLORS / nr;
+    gBand = NCOLORS / ng;
+    bBand = NCOLORS / nb;
+
+  retry:
+    color.flags = (DoRed | DoGreen | DoBlue);
+    rLast = gLast = bLast = 0;
+    r = g = b = 0;
+    for (i = 0; i < visualPtr->map_entries; i++) {
+	if (rLast < NCOLORS) {
+	    r = rLast + rBand;
+	    if (r > NCOLORS) {
+		r = NCOLORS;
+	    }
+	}
+	if (gLast < NCOLORS) {
+	    g = gLast + gBand;
+	    if (g > NCOLORS) {
+		g = NCOLORS;
+	    }
+	}
+	if (bLast < NCOLORS) {
+	    b = bLast + bBand;
+	    if (b > NCOLORS) {
+		b = NCOLORS;
+	    }
+	}
+	color.red = (r - 1) * (NCOLORS + 1);
+	color.green = (g - 1) * (NCOLORS + 1);
+	color.blue = (b - 1) * (NCOLORS + 1);
+
+	if (!XAllocColor(display, colorTabPtr->colorMap, &color)) {
+	    XFreeColors(display, colorTabPtr->colorMap,
+		colorTabPtr->pixelValues, i, 0);
+	    if ((colorTabPtr->flags & PRIVATE_COLORMAP) == 0) {
+		/*
+		 * If we can't allocate a color in the default
+		 * colormap, try again, this time with a private
+		 * colormap.
+		 */
+		fprintf(stderr, "Need to allocate private colormap\n");
+		colorTabPtr->colorMap = Tk_GetColormap(interp, tkwin, ".");
+
+		XSetWindowColormap(display, Tk_WindowId(tkwin),
+		    colorTabPtr->colorMap);
+		colorTabPtr->flags |= PRIVATE_COLORMAP;
+		goto retry;
+	    }
+#ifdef notdef
+	    fprintf(stderr, "Failed to allocate after %d colors\n", i);
+#endif
+	    Blt_Free(colorTabPtr);
+	    return NULL;	/* Ran out of colors in private map? */
+	}
+	colorTabPtr->pixelValues[i] = color.pixel;
+	/*
+	 * Fill in pixel values for each band at this intensity
+	 */
+	value = color.pixel & visualPtr->red_mask;
+	while (rLast < r) {
+	    colorTabPtr->red[rLast++] = value;
+	}
+	value = color.pixel & visualPtr->green_mask;
+	while (gLast < g) {
+	    colorTabPtr->green[gLast++] = value;
+	}
+	value = color.pixel & visualPtr->blue_mask;
+	while (bLast < b) {
+	    colorTabPtr->blue[bLast++] = value;
+	}
+    }
+    colorTabPtr->nPixels = i;
+    return colorTabPtr;
+}
+
+/*
+ * First attempt:
+ *	Allocate colors all the colors in the image (up to NCOLORS). Bail out
+ *	on the first failure or if we need more than NCOLORS.
+ */
+static int
+GetUniqueColors(image)
+    Blt_ColorImage image;
+{
+    register int i, nColors;
+    register Pix32 *pixelPtr;
+    Pix32 color;
+    Blt_HashEntry *hPtr;
+    int isNew, nPixels;
+    int refCount;
+    Blt_HashTable colorTable;
+
+    Blt_InitHashTable(&colorTable, BLT_ONE_WORD_KEYS);
+
+    nPixels = Blt_ColorImageWidth(image) * Blt_ColorImageHeight(image);
+    nColors = 0;
+    pixelPtr = Blt_ColorImageBits(image);
+    for (i = 0; i < nPixels; i++, pixelPtr++) {
+	color.value = pixelPtr->value;
+	color.Alpha = 0xFF;	/* Ignore alpha-channel values */
+	hPtr = Blt_CreateHashEntry(&colorTable, (char *)color.value, &isNew);
+	if (isNew) {
+	    refCount = 1;
+	    nColors++;
+	} else {
+	    refCount = (int)Blt_GetHashValue(hPtr);
+	    refCount++;
+	}
+	Blt_SetHashValue(hPtr, (ClientData)refCount);
+    }
+    Blt_DeleteHashTable(&colorTable);
+    return nColors;
+}
+
+#define Blt_DefaultColormap(tkwin)  \
+	DefaultColormap(Tk_Display(tkwin), Tk_ScreenNumber(tkwin))
+
+
+static void
+PrivateColormap(interp, colorTabPtr, image, tkwin)
+    Tcl_Interp *interp;
+    struct ColorTableStruct *colorTabPtr;
+    Blt_ColorImage image;
+    Tk_Window tkwin;
+{
+    int keepColors = 0;
+    register int i;
+    XColor usedColors[NCOLORS];
+    int nFreeColors, nUsedColors;
+    Colormap colorMap;
+    int inUse[NCOLORS];
+    XColor *colorPtr;
+    XColor *imageColors;
+
+    /*
+     * Create a private colormap if one doesn't already exist for the
+     * window.
+     */
+
+    colorTabPtr->colorMap = colorMap = Tk_Colormap(tkwin);
+
+    nUsedColors = 0;		/* Number of colors allocated */
+
+    if (colorTabPtr->nPixels > 0) {
+	XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+	    colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+    }
+    nFreeColors = QueryColormap(colorTabPtr->display, colorMap, usedColors,
+	&nUsedColors);
+    memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+    if ((nUsedColors == 0) && (keepColors > 0)) {
+
+	/*
+	 * We're starting with a clean colormap so find out what colors
+	 * have been used in the default colormap.
+	 */
+
+	nFreeColors = QueryColormap(colorTabPtr->display,
+	    Blt_DefaultColormap(tkwin), usedColors, &nUsedColors);
+
+	/*
+	 * Copy a number of colors from the default colormap into the private
+	 * colormap.  We can assume that this is the working set from most
+	 * (non-image related) applications. While this doesn't stop our
+	 * image from flashing and looking dumb when colormaps are swapped
+	 * in and out, at least everything else should remain unaffected.
+	 */
+
+	if (nUsedColors > keepColors) {
+	    nUsedColors = keepColors;
+	}
+	/*
+	 * We want to allocate colors in the same ordering as the old colormap,
+	 * and we can't assume that the colors in the old map were contiguous.
+	 * So mark the colormap locations (i.e. pixels) that we find in use.
+	 */
+
+    }
+    for (colorPtr = usedColors, i = 0; i < nUsedColors; i++, colorPtr++) {
+	inUse[colorPtr->pixel] = TRUE;
+    }
+
+    /*
+     * In an "exact" colormap, we try to allocate as many of colors from the
+     * image as we can fit.  If necessary, we'll cheat and reduce the number
+     * of colors by quantizing.
+     */
+    imageColors = usedColors + nUsedColors;
+
+    Tk_SetWindowColormap(tkwin, colorMap);
+}
+
+ColorTable
+Blt_PseudoColorTable(interp, tkwin, image)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    Blt_ColorImage image;
+{
+    struct ColorTableStruct *colorTabPtr;
+    Colormap defColorMap;
+    int usePrivate;
+
+    colorTabPtr = Blt_CreateColorTable(tkwin);
+    defColorMap = DefaultColormap(colorTabPtr->display, Tk_ScreenNumber(tkwin));
+    if (colorTabPtr->colorMap == defColorMap) {
+	fprintf(stderr, "Using default colormap\n");
+    }
+    /* All other visuals use an 8-bit colormap */
+    colorTabPtr->lut = Blt_Malloc(sizeof(unsigned int) * 33 * 33 * 33);
+    assert(colorTabPtr->lut);
+
+    usePrivate = TRUE;
+    if (usePrivate) {
+	PrivateColormap(interp, colorTabPtr, image, tkwin);
+    } else {
+#ifdef notdef
+	ReadOnlyColormap(colorTabPtr, image, tkwin);
+#endif
+    }
+    return colorTabPtr;
+}
+
+#ifdef notdef
+
+static void
+ConvoleColorImage(srcImage, destImage, kernelPtr)
+    Blt_ColorImage srcImage, destImage;
+    ConvoleKernel *kernelPtr;
+{
+    Pix32 *srcPtr, *destPtr;
+    Pix32 *src[MAXROWS];
+    register int x, y, i, j;
+    int red, green, blue;
+
+    /* i = 0 case, ignore left column of pixels */
+
+    srcPtr = Blt_ColorImageBits(srcImage);
+    destPtr = Blt_ColorImageBits(destImage);
+
+    width = Blt_ColorImageWidth(srcImage);
+    height = Blt_ColorImageHeight(srcImage);
+
+    yOffset = kernelPtr->height / 2;
+    xOffset = kernelPtr->width / 2;
+    for (y = yOffset; y < (height - yOffset); y++) {
+	/* Set up pointers to individual rows */
+	for (i = 0; i < kernelPtr->height; i++) {
+	    src[i] = srcPtr + (i * width);
+	}
+	for (x = xOffset; x < (width - xOffset); x++) {
+	    red = green = blue = 0;
+	    kernPtr = kernelPtr->values;
+	    for (i = 0; i < kernelPtr->height; i++) {
+		for (j = 0; j < kernelPtr->width; j++) {
+		    red += *valuePtr * src[i][j].Red;
+		    green += *valuePtr * src[i][j].Green;
+		    blue += *valuePtr * src[i][j].Blue;
+		    valuePtr++;
+		}
+	    }
+	    destPtr->Red = red / kernelPtr->sum;
+	    destPtr->Green = green / kernelPtr->sum;
+	    destPtr->Blue = blue / kernelPtr->sum;
+	    destPtr++;
+	}
+	srcPtr += width;
+    }
+    sum = bot[0].Red +
+	red = bot[0].Red + bot[1].Red + mid[1].Red + top[0].Red + top[1].Red;
+    green = bot[0].Green + bot[1].Green + mid[1].Green + top[0].Green +
+	top[1].Green;
+    blue = bot[0].Blue + bot[1].Blue + mid[1].Blue + top[0].Blue + top[1].Blue;
+    error = (red / 5) - mid[0].Red;
+    redVal = mid[0].Red - (error * blend / blend_divisor);
+    error = (green / 5) - mid[0].Green;
+    greenVal = mid[0].Green - (error * blend / blend_divisor);
+    error = (blue / 5) - mid[0].Blue;
+    blueVal = mid[0].Blue - (error * blend / blend_divisor);
+
+    out[0].Red = CLAMP(redVal);
+    out[0].Green = CLAMP(greenVal);
+    out[0].Blue = CLAMP(blueVal);
+
+    for (i = 1; i < (width - 1); i++) {
+	for (chan = 0; chan < 3; chan++) {
+	    total = bot[chan][i - 1] + bot[chan][i] + bot[chan][i + 1] +
+		mid[chan][i - 1] + mid[chan][i + 1] +
+		top[chan][i - 1] + top[chan][i] + top[chan][i + 1];
+	    avg = total >> 3;	/* divide by 8 */
+	    diff = avg - mid[chan][i];
+	    result = mid[chan][i] - (diff * blend / blend_divisor);
+	    out[chan][i] = CLAMP(result);
+	}
+    }
+    /* i = in_hdr.xmax case, ignore right column of pixels */
+    for (chan = 0; chan < 3; chan++) {
+	total = bot[chan][i - 1] + bot[chan][i] +
+	    mid[chan][i - 1] +
+	    top[chan][i - 1] + top[chan][i];
+	avg = total / 5;
+	diff = avg - mid[chan][i];
+	result = mid[chan][i] - (diff * blend / blend_divisor);
+	out[chan][i] = CLAMP(result);
+    }
+}
+
+static void
+DitherRow(srcImage, destImage, lastRow, curRow)
+    Blt_ColorImage srcImage, destImage;
+    int width, height;
+    int bottom, top;
+{
+    int width, height;
+
+    width = Blt_ColorImageWidth(srcImage);
+    topPtr = Blt_ColorImageBits(destPtr) + (width * row);
+    rowPtr = topPtr + width;
+    botPtr = rowPtr + width;
+
+    for (x = 0; x < width; x++) {
+
+	/* Clamp current error entry */
+
+	midPtr->red = CLAMP(midPtr->red);
+	midPtr->blue = CLAMP(midPtr->blue);
+	midPtr->green = CLAMP(midPtr->green);
+
+	r = (midPtr->red >> 3) + 1;
+	g = (midPtr->green >> 3) + 1;
+	b = (midPtr->blue >> 3) + 1;
+	index = colorTabPtr->lut[r][g][b];
+
+	redVal = midPtr->red * (NCOLORS + 1);
+	greenVal = midPtr->green * (NCOLORS + 1);
+	blueVal = midPtr->blue * (NCOLORS + 1);
+
+	error = colorVal - colorMap[index].red;
+	if (x < 511) {
+	    currRow[x + 1].Red = currRow[x + 1].Red + 7 * error / 16;
+	    nextRow[x + 1].Red = nextRow[x + 1].Red + error / 16;
+	}
+	nextRow[x].Red = nextRow[x].Red + 5 * error / 16;
+	if (x > 0) {
+	    nextRow[x - 1].Red = nextRow[x - 1].Red + 3 * error / 16;
+	}
+	error = row[x][c] - colormap[index][c];
+
+	value = srcPtr->channel[i] * error[i];
+	value = CLAMP(value);
+	destPtr->channel[i] = value;
+
+	/* Closest pixel */
+	pixel = PsuedoColorPixel();
+	error[RED] = colorPtr->Red - srcPtr->Red * (NCOLORS + 1);
+
+	/* translate pixel to colorInfoPtr to get error */
+	colorTabPtr->lut[r][g][b];
+	colorPtr = PixelToColorInfo(pixel);
+	error = colorPtr->error;
+
+	register rle_pixel *optr;
+	register int j;
+	register short *thisptr, *nextptr = NULL;
+	int chan;
+	static int nchan = 0;
+	int lastline = 0, lastpixel;
+	static int *cval = 0;
+	static rle_pixel *pixel = 0;
+
+	if (nchan != in_hdr->ncolors)
+	    if (cval) {
+		Blt_Free(cval);
+		Blt_Free(pixel);
+	    }
+	nchan = in_hdr->ncolors;
+	if (!cval) {
+	    if ((cval = Blt_Malloc(nchan * sizeof(int))) == 0)
+		    malloc_ERR;
+	    if ((pixel = Blt_Malloc(nchan * sizeof(rle_pixel))) == 0)
+		malloc_ERR;
+	}
+	optr = outrow[RLE_RED];
+
+	thisptr = row_top;
+	if (row_bottom)
+	    nextptr = row_bottom;
+	else
+	    lastline = 1;
+
+	for (x = 0; x < width; x++) {
+	    int cmap_index = 0;
+
+	    lastpixel = (x == (width - 1));
+	    val = srcPtr->Red;
+
+	    for (chan = 0; chan < 3; chan++) {
+		cval[chan] = *thisptr++;
+
+		/*
+		 * Current channel value has been accumulating error,
+		 * it could be out of range.
+		 */
+		if (cval[chan] < 0)
+		    cval[chan] = 0;
+		else if (cval[chan] > 255)
+		    cval[chan] = 255;
+
+		pixel[chan] = cval[chan];
+	    }
+
+	    /* find closest color */
+	    find_closest(map, nchan, maplen, pixel, &cmap_index);
+	    *optr++ = cmap_index;
+
+	    /* thisptr is now looking at pixel to the right of current pixel
+	     * nextptr is looking at pixel below current pixel
+	     * So, increment thisptr as stuff gets stored.  nextptr gets moved
+	     * by one, and indexing is done +/- nchan.
+	     */
+	    for (chan = 0; chan < nchan; chan++) {
+		cval[chan] -= map[chan][cmap_index];
+
+		if (!lastpixel) {
+		    thisptr[chan] += cval[chan] * 7 / 16;
+		}
+		if (!lastline) {
+		    if (j != 0) {
+			nextptr[-nchan] += cval[chan] * 3 / 16;
+		    }
+		    nextptr[0] += cval[chan] * 5 / 16;
+		    if (!lastpixel) {
+			nextptr[nchan] += cval[chan] / 16;
+		    }
+		    nextptr++;
+		}
+	    }
+	}
+    }
+}
+
+/********************************************/
+static Blt_ColorImage
+DoColorDither(pic24, pic8, w, h, rmap, gmap, bmap, rdisp, gdisp, bdisp, maplen)
+    byte *pic24, *pic8, *rmap, *gmap, *bmap, *rdisp, *gdisp, *bdisp;
+    int w, h, maplen;
+{
+    /* takes a 24 bit picture, of size w*h, dithers with the colors in
+       rdisp, gdisp, bdisp (which have already been allocated),
+       and generates an 8-bit w*h image, which it returns.
+       ignores input value 'pic8'
+       returns NULL on error
+
+       note: the rdisp,gdisp,bdisp arrays should be the 'displayed' colors,
+       not the 'desired' colors
+
+       if pic24 is NULL, uses the passed-in pic8 (an 8-bit image) as
+       the source, and the rmap,gmap,bmap arrays as the desired colors */
+
+    byte *np, *ep, *newpic;
+    short *cache;
+    int r2, g2, b2;
+    int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
+    int i, j, rerr, gerr, berr, pwide3;
+    int imax, jmax;
+    int key;
+    long cnt1, cnt2;
+    int error[512];		/* -255 .. 0 .. +255 */
+
+    /* compute somewhat non-linear floyd-steinberg error mapping table */
+    for (i = j = 0; i <= 0x40; i++, j++) {
+	error[256 + i] = j;
+	error[256 - i] = -j;
+    }
+    for ( /*empty*/ ; i < 0x80; i++, j += !(i & 1) ? 1 : 0) {
+	error[256 + i] = j;
+	error[256 - i] = -j;
+    }
+    for ( /*empty*/ ; i <= 0xff; i++) {
+	error[256 + i] = j;
+	error[256 - i] = -j;
+    }
+
+    cnt1 = cnt2 = 0;
+    pwide3 = w * 3;
+    imax = h - 1;
+    jmax = w - 1;
+    ep = (pic24) ? pic24 : pic8;
+
+    /* attempt to malloc things */
+    newpic = Blt_Malloc((size_t) (w * h));
+    cache = Blt_Calloc((size_t) (2 << 14), sizeof(short));
+    thisline = Blt_Malloc(pwide3 * sizeof(int));
+    nextline = Blt_Malloc(pwide3 * sizeof(int));
+    if (!cache || !newpic || !thisline || !nextline) {
+	if (newpic)
+	    Blt_Free(newpic);
+	if (cache)
+	    Blt_Free(cache);
+	if (thisline)
+	    Blt_Free(thisline);
+	if (nextline)
+	    Blt_Free(nextline);
+	return (byte *) NULL;
+    }
+    np = newpic;
+
+    /* Get first line of picture in reverse order. */
+
+    srcPtr = Blt_ColorImageBits(image), tempPtr = tempArr;
+    for (x = 0; x < width; x++, tempPtr++, srcPtr--) {
+	*tempPtr = *srcPtr;
+    }
+
+    for (y = 0; y < height; y++) {
+	tempPtr = curRowPtr, curRowPtr = nextRowPtr, nextRowPtr = tempPtr;
+
+	if (y != (height - 1)) {/* get next line */
+	    for (x = 0; x < width; x++, tempPtr++, srcPtr--)
+		*tempPtr = *srcPtr;
+	}
+    }
+
+
+    Blt_Free(thisline);
+    Blt_Free(nextline);
+    Blt_Free(cache);
+
+    return newpic;
+}
+
+
+static void
+DitherImage(image)
+    Blt_ColorImage image;
+{
+    int width, height;
+
+
+
+}
+
+#endif
+
+#endif /* WIN32 */
Index: trunk/kitgen/8.x/blt/generic/bltConfig.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltConfig.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltConfig.c	(revision 175)
@@ -0,0 +1,1370 @@
+/*
+ * bltConfig.c --
+ *
+ *	This module implements custom configuration options for the BLT
+ *	toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "bltTile.h"
+
+static int StringToFill _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+static char *FillToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltFillOption =
+{
+    StringToFill, FillToString, (ClientData)0
+};
+
+static int StringToPad _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *PadToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+	char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltPadOption =
+{
+    StringToPad, PadToString, (ClientData)0
+};
+
+static int StringToDistance _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+static char *DistanceToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltDistanceOption =
+{
+    StringToDistance, DistanceToString, (ClientData)PIXELS_NONNEGATIVE
+};
+
+Tk_CustomOption bltPositiveDistanceOption =
+{
+    StringToDistance, DistanceToString, (ClientData)PIXELS_POSITIVE
+};
+
+Tk_CustomOption bltAnyDistanceOption =
+{
+    StringToDistance, DistanceToString, (ClientData)PIXELS_ANY
+};
+
+static int StringToCount _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+static char *CountToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltCountOption =
+{
+    StringToCount, CountToString, (ClientData)COUNT_NONNEGATIVE
+};
+
+Tk_CustomOption bltPositiveCountOption =
+{
+    StringToCount, CountToString, (ClientData)COUNT_POSITIVE
+};
+
+static int StringToDashes _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window,
+	char *, char *, int));
+static char *DashesToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltDashesOption =
+{
+    StringToDashes, DashesToString, (ClientData)0
+};
+
+static int StringToShadow _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+	Tk_Window tkwin, char *string, char *widgRec, int offset));
+static char *ShadowToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+	char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltShadowOption =
+{
+    StringToShadow, ShadowToString, (ClientData)0
+};
+
+static int StringToUid _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+static char *UidToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltUidOption =
+{
+    StringToUid, UidToString, (ClientData)0
+};
+
+static int StringToState _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+static char *StateToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltStateOption =
+{
+    StringToState, StateToString, (ClientData)0
+};
+
+static int StringToList _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+	Tk_Window tkwin, char *string, char *widgRec, int flags));
+static char *ListToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+Tk_CustomOption bltListOption =
+{
+    StringToList, ListToString, (ClientData)0
+};
+
+static int StringToTile _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+	Tk_Window tkwin, char *value, char *widgRec, int flags));
+static char *TileToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
+	char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
+
+Tk_CustomOption bltTileOption =
+{
+    StringToTile, TileToString, (ClientData)0
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfFill --
+ *
+ *	Converts the integer representing the fill direction into a string.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_NameOfFill(fill)
+    int fill;
+{
+    switch (fill) {
+    case FILL_X:
+	return "x";
+    case FILL_Y:
+	return "y";
+    case FILL_NONE:
+	return "none";
+    case FILL_BOTH:
+	return "both";
+    default:
+	return "unknown value";
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFill --
+ *
+ *	Converts the fill style string into its numeric representation.
+ *
+ *	Valid style strings are:
+ *
+ *	  "none"   Use neither plane.
+ * 	  "x"	   X-coordinate plane.
+ *	  "y"	   Y-coordinate plane.
+ *	  "both"   Use both coordinate planes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFill(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Fill style string */
+    char *widgRec;		/* Cubicle structure record */
+    int offset;			/* Offset of style in record */
+{
+    int *fillPtr = (int *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+	*fillPtr = FILL_NONE;
+    } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) {
+	*fillPtr = FILL_X;
+    } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) {
+	*fillPtr = FILL_Y;
+    } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+	*fillPtr = FILL_BOTH;
+    } else {
+	Tcl_AppendResult(interp, "bad argument \"", string,
+	    "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FillToString --
+ *
+ *	Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ *	The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FillToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of fill in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int fill = *(int *)(widgRec + offset);
+
+    return Blt_NameOfFill(fill);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToFlag --
+ *
+ *	Converts the fill style string into its numeric representation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToFlag(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Bit mask to be tested in status word */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Fill style string */
+    char *widgRec;		/* Cubicle structure record */
+    int offset;			/* Offset of style in record */
+{
+    unsigned int mask = (unsigned int)clientData;	/* Bit to be tested */
+    int *flagPtr = (int *)(widgRec + offset);
+    int bool;
+
+    if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (bool) {
+	*flagPtr |= mask;
+    } else {
+	*flagPtr &= ~mask;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FlagToString --
+ *
+ *	Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ *	The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_FlagToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Bit mask to be test in status word */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of fill in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not Used. */
+{
+    unsigned int mask = (unsigned int)clientData;	/* Bit to be tested */
+    unsigned int bool = *(unsigned int *)(widgRec + offset);
+
+    return (bool & mask) ? "1" : "0";
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPixels --
+ *
+ *	Like Tk_GetPixels, but checks for negative, zero.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetPixels(interp, tkwin, string, check, valuePtr)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    char *string;
+    int check;			/* Can be PIXELS_POSITIVE, PIXELS_NONNEGATIVE,
+				 * or PIXELS_ANY, */
+    int *valuePtr;
+{
+    int length;
+
+    if (Tk_GetPixels(interp, tkwin, string, &length) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (length >= SHRT_MAX) {
+	Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+	    "too big to represent", (char *)NULL);
+	return TCL_ERROR;
+    }
+    switch (check) {
+    case PIXELS_NONNEGATIVE:
+	if (length < 0) {
+	    Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+		"can't be negative", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+    case PIXELS_POSITIVE:
+	if (length <= 0) {
+	    Tcl_AppendResult(interp, "bad distance \"", string, "\": ",
+		"must be positive", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+    case PIXELS_ANY:
+	break;
+    }
+    *valuePtr = length;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDistance --
+ *
+ *	Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ *	values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDistance(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Indicated how to check distance */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window */
+    char *string;		/* Pixel value string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of pixel size in record */
+{
+    int *valuePtr = (int *)(widgRec + offset);
+    return Blt_GetPixels(interp, tkwin, string, (int)clientData, valuePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DistanceToString --
+ *
+ *	Returns the string representing the positive pixel size.
+ *
+ * Results:
+ *	The pixel size string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DistanceToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int value = *(int *)(widgRec + offset);
+    char *result;
+
+    result = Blt_Strdup(Blt_Itoa(value));
+    assert(result);
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+int
+Blt_GetInt(interp, string, check, valuePtr)
+    Tcl_Interp *interp;
+    char *string;
+    int check;			/* Can be COUNT_POSITIVE, COUNT_NONNEGATIVE,
+				 * or COUNT_ANY, */
+    int *valuePtr;
+{
+    int count;
+
+    if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    switch (check) {
+    case COUNT_NONNEGATIVE:
+	if (count < 0) {
+	    Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+		"can't be negative", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+    case COUNT_POSITIVE:
+	if (count <= 0) {
+	    Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+		"must be positive", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+    case COUNT_ANY:
+	break;
+    }
+    *valuePtr = count;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToCount --
+ *
+ *	Like TK_CONFIG_PIXELS, but adds an extra check for negative
+ *	values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToCount(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Indicated how to check distance */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Pixel value string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of pixel size in record */
+{
+    int *valuePtr = (int *)(widgRec + offset);
+    return Blt_GetInt(interp, string, (int)clientData, valuePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CountToString --
+ *
+ *	Returns the string representing the positive pixel size.
+ *
+ * Results:
+ *	The pixel size string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CountToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int value = *(int *)(widgRec + offset);
+    char *result;
+
+    result = Blt_Strdup(Blt_Itoa(value));
+    assert(result);
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPad --
+ *
+ *	Convert a string into two pad values.  The string may be in one of
+ *	the following forms:
+ *
+ *	    n    - n is a non-negative integer. This sets both
+ *		   pad values to n.
+ *	  {n m}  - both n and m are non-negative integers. side1
+ *		   is set to n, side2 is set to m.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side Effects:
+ *	The padding structure passed is updated with the new values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPad(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window */
+    char *string;		/* Pixel value string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of pad in widget */
+{
+    Blt_Pad *padPtr = (Blt_Pad *)(widgRec + offset);
+    int nElem;
+    int pad, result;
+    char **padArr;
+
+    if (Tcl_SplitList(interp, string, &nElem, &padArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    result = TCL_ERROR;
+    if ((nElem < 1) || (nElem > 2)) {
+	Tcl_AppendResult(interp, "wrong # elements in padding list",
+	    (char *)NULL);
+	goto error;
+    }
+    if (Blt_GetPixels(interp, tkwin, padArr[0], PIXELS_NONNEGATIVE, &pad)
+	!= TCL_OK) {
+	goto error;
+    }
+    padPtr->side1 = pad;
+    if ((nElem > 1) &&
+	(Blt_GetPixels(interp, tkwin, padArr[1], PIXELS_NONNEGATIVE, &pad)
+	    != TCL_OK)) {
+	goto error;
+    }
+    padPtr->side2 = pad;
+    result = TCL_OK;
+
+  error:
+    Blt_Free(padArr);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PadToString --
+ *
+ *	Converts the two pad values into a Tcl list.  Each pad has two
+ *	pixel values.  For vertical pads, they represent the top and bottom
+ *	margins.  For horizontal pads, they're the left and right margins.
+ *	All pad values are non-negative integers.
+ *
+ * Results:
+ *	The padding list is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PadToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Structure record */
+    int offset;			/* Offset of pad in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Blt_Pad *padPtr = (Blt_Pad *)(widgRec + offset);
+    char *result;
+    char string[200];
+
+    sprintf(string, "%d %d", padPtr->side1, padPtr->side2);
+    result = Blt_Strdup(string);
+    if (result == NULL) {
+	return "out of memory";
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToShadow --
+ *
+ *	Convert a string into two pad values.  The string may be in one of
+ *	the following forms:
+ *
+ *	    n    - n is a non-negative integer. This sets both
+ *		   pad values to n.
+ *	  {n m}  - both n and m are non-negative integers. side1
+ *		   is set to n, side2 is set to m.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side Effects:
+ *	The padding structure passed is updated with the new values.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToShadow(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window */
+    char *string;		/* Pixel value string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of pad in widget */
+{
+    Shadow *shadowPtr = (Shadow *) (widgRec + offset);
+    XColor *colorPtr;
+    int dropOffset;
+
+    colorPtr = NULL;
+    dropOffset = 0;
+    if ((string != NULL) && (string[0] != '\0')) {
+	int nElem;
+	char **elemArr;
+
+	if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((nElem < 1) || (nElem > 2)) {
+	    Tcl_AppendResult(interp, "wrong # elements in drop shadow value",
+		(char *)NULL);
+	    Blt_Free(elemArr);
+	    return TCL_ERROR;
+	}
+	colorPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(elemArr[0]));
+	if (colorPtr == NULL) {
+	    Blt_Free(elemArr);
+	    return TCL_ERROR;
+	}
+	dropOffset = 1;
+	if (nElem == 2) {
+	    if (Blt_GetPixels(interp, tkwin, elemArr[1], PIXELS_NONNEGATIVE,
+		    &dropOffset) != TCL_OK) {
+		Tk_FreeColor(colorPtr);
+		Blt_Free(elemArr);
+		return TCL_ERROR;
+	    }
+	}
+	Blt_Free(elemArr);
+    }
+    if (shadowPtr->color != NULL) {
+	Tk_FreeColor(shadowPtr->color);
+    }
+    shadowPtr->color = colorPtr;
+    shadowPtr->offset = dropOffset;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShadowToString --
+ *
+ *	Converts the two pad values into a Tcl list.  Each pad has two
+ *	pixel values.  For vertical pads, they represent the top and bottom
+ *	margins.  For horizontal pads, they're the left and right margins.
+ *	All pad values are non-negative integers.
+ *
+ * Results:
+ *	The padding list is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ShadowToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Structure record */
+    int offset;			/* Offset of pad in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Shadow *shadowPtr = (Shadow *) (widgRec + offset);
+    char *result;
+
+    result = "";
+    if (shadowPtr->color != NULL) {
+	char string[200];
+
+	sprintf(string, "%s %d", Tk_NameOfColor(shadowPtr->color),
+	    shadowPtr->offset);
+	result = Blt_Strdup(string);
+	*freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    }
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetDashes --
+ *
+ *	Converts a Tcl list of dash values into a dash list ready for
+ *	use with XSetDashes.
+ *
+ * 	A valid list dash values can have zero through 11 elements
+ *	(PostScript limit).  Values must be between 1 and 255. Although
+ *	a list of 0 (like the empty string) means no dashes.
+ *
+ * Results:
+ *	A standard Tcl result. If the list represented a valid dash
+ *	list TCL_OK is returned and *dashesPtr* will contain the
+ *	valid dash list. Otherwise, TCL_ERROR is returned and
+ *	interp->result will contain an error message.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetDashes(interp, string, dashesPtr)
+    Tcl_Interp *interp;
+    char *string;
+    Blt_Dashes *dashesPtr;
+{
+    if ((string == NULL) || (*string == '\0')) {
+	dashesPtr->values[0] = 0;
+    } else if (strcmp(string, "dash") == 0) {	/* 5 2 */
+	dashesPtr->values[0] = 5;
+	dashesPtr->values[1] = 2;
+	dashesPtr->values[2] = 0;
+    } else if (strcmp(string, "dot") == 0) {	/* 1 */
+	dashesPtr->values[0] = 1;
+	dashesPtr->values[1] = 0;
+    } else if (strcmp(string, "dashdot") == 0) {	/* 2 4 2 */
+	dashesPtr->values[0] = 2;
+	dashesPtr->values[1] = 4;
+	dashesPtr->values[2] = 2;
+	dashesPtr->values[3] = 0;
+    } else if (strcmp(string, "dashdotdot") == 0) {	/* 2 4 2 2 */
+	dashesPtr->values[0] = 2;
+	dashesPtr->values[1] = 4;
+	dashesPtr->values[2] = 2;
+	dashesPtr->values[3] = 2;
+	dashesPtr->values[4] = 0;
+    } else {
+	int nValues;
+	char **strArr;
+	long int value;
+	register int i;
+
+	if (Tcl_SplitList(interp, string, &nValues, &strArr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (nValues > 11) {	/* This is the postscript limit */
+	    Tcl_AppendResult(interp, "too many values in dash list \"", 
+			     string, "\"", (char *)NULL);
+	    Blt_Free(strArr);
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < nValues; i++) {
+	    if (Tcl_ExprLong(interp, strArr[i], &value) != TCL_OK) {
+		Blt_Free(strArr);
+		return TCL_ERROR;
+	    }
+	    /*
+	     * Backward compatibility:
+	     * Allow list of 0 to turn off dashes
+	     */
+	    if ((value == 0) && (nValues == 1)) {
+		break;
+	    }
+	    if ((value < 1) || (value > 255)) {
+		Tcl_AppendResult(interp, "dash value \"", strArr[i],
+		    "\" is out of range", (char *)NULL);
+		Blt_Free(strArr);
+		return TCL_ERROR;
+	    }
+	    dashesPtr->values[i] = (unsigned char)value;
+	}
+	/* Make sure the array ends with a NUL byte  */
+	dashesPtr->values[i] = 0;
+	Blt_Free(strArr);
+    }
+    return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDashes --
+ *
+ *	Convert the list of dash values into a dashes array.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	The Dashes structure is updated with the new dash list.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDashes(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* New dash value list */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset to Dashes structure */
+{
+    Blt_Dashes *dashesPtr = (Blt_Dashes *)(widgRec + offset);
+
+    return GetDashes(interp, string, dashesPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DashesToString --
+ *
+ *	Convert the dashes array into a list of values.
+ *
+ * Results:
+ *	The string representing the dashes returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DashesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of Dashes in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Blt_Dashes *dashesPtr = (Blt_Dashes *)(widgRec + offset);
+    Tcl_DString dString;
+    unsigned char *p;
+    char *result;
+
+    if (dashesPtr->values[0] == 0) {
+	return "";
+    }
+    Tcl_DStringInit(&dString);
+    for (p = dashesPtr->values; *p != 0; p++) {
+	Tcl_DStringAppendElement(&dString, Blt_Itoa(*p));
+    }
+    result = Tcl_DStringValue(&dString);
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToUid --
+ *
+ *	Converts the string to a BLT Uid. Blt Uid's are hashed, reference
+ *	counted strings.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToUid(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Fill style string */
+    char *widgRec;		/* Cubicle structure record */
+    int offset;			/* Offset of style in record */
+{
+    Blt_Uid *uidPtr = (Blt_Uid *)(widgRec + offset);
+    Blt_Uid newId;
+
+    newId = NULL;
+    if ((string != NULL) && (*string != '\0')) {
+	newId = Blt_GetUid(string);
+    }
+    if (*uidPtr != NULL) {
+	Blt_FreeUid(*uidPtr);
+    }
+    *uidPtr = newId;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UidToString --
+ *
+ *	Returns the fill style string based upon the fill flags.
+ *
+ * Results:
+ *	The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+UidToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of fill in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Blt_Uid uid = *(Blt_Uid *)(widgRec + offset);
+
+    return (uid == NULL) ? "" : uid;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToState --
+ *
+ *	Converts the string to a state value. Valid states are
+ *	disabled, normal.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToState(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representation of option value */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of field in record */
+{
+    int *statePtr = (int *)(widgRec + offset);
+
+    if (strcmp(string, "normal") == 0) {
+	*statePtr = STATE_NORMAL;
+    } else if (strcmp(string, "disabled") == 0) {
+	*statePtr = STATE_DISABLED;
+    } else if (strcmp(string, "active") == 0) {
+	*statePtr = STATE_ACTIVE;
+    } else {
+	Tcl_AppendResult(interp, "bad state \"", string,
+	    "\": should be normal, active, or disabled", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StateToString --
+ *
+ *	Returns the string representation of the state configuration field
+ *
+ * Results:
+ *	The string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+StateToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of fill in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int state = *(int *)(widgRec + offset);
+
+    switch (state) {
+    case STATE_ACTIVE:
+	return "active";
+    case STATE_DISABLED:
+	return "disabled";
+    case STATE_NORMAL:
+	return "normal";
+    default:
+	return "???";
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToList --
+ *
+ *	Converts the string to a list.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToList(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representation of option value */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of field in record */
+{
+    char ***listPtr = (char ***)(widgRec + offset);
+    char **elemArr;
+    int nElem;
+
+    if (*listPtr != NULL) {
+	Blt_Free(*listPtr);
+	*listPtr = NULL;
+    }
+    if ((string == NULL) || (*string == '\0')) {
+	return TCL_OK;
+    }
+    if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (nElem > 0) {
+	*listPtr = elemArr;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListToString --
+ *
+ *	Returns the string representation of the state configuration field
+ *
+ * Results:
+ *	The string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ListToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record. */
+    int offset;			/* Offset of fill in widget record. */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    char **list = *(char ***)(widgRec + offset);
+    register char **p;
+    char *result;
+    Tcl_DString dString;
+
+    if (list == NULL) {
+	return "";
+    }
+    Tcl_DStringInit(&dString);
+    for (p = list; *p != NULL; p++) {
+	Tcl_DStringAppendElement(&dString, *p);
+    }
+    result = Tcl_DStringValue(&dString);
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    Tcl_DStringFree(&dString);
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToTile --
+ *
+ *	Converts the name of an image into a tile.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToTile(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window on same display as tile */
+    char *string;		/* Name of image */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of tile in record */
+{
+    Blt_Tile *tilePtr = (Blt_Tile *)(widgRec + offset);
+    Blt_Tile tile, oldTile;
+
+    oldTile = *tilePtr;
+    tile = NULL;
+    if ((string != NULL) && (*string != '\0')) {
+	if (Blt_GetTile(interp, tkwin, string, &tile) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    /* Don't delete the information for the old tile, until we know
+     * that we successfully allocated a new one. */
+    if (oldTile != NULL) {
+	Blt_FreeTile(oldTile);
+    }
+    *tilePtr = tile;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileToString --
+ *
+ *	Returns the name of the tile.
+ *
+ * Results:
+ *	The name of the tile is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+TileToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record */
+    int offset;			/* Offset of tile in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Blt_Tile tile = *(Blt_Tile *)(widgRec + offset);
+
+    return Blt_NameOfTile(tile);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigModified --
+ *
+ *      Given the configuration specifications and one or more option
+ *	patterns (terminated by a NULL), indicate if any of the matching
+ *	configuration options has been reset.
+ *
+ * Results:
+ *      Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int Blt_ConfigModified
+TCL_VARARGS_DEF(Tk_ConfigSpec *, arg1)
+{
+    va_list argList;
+    Tk_ConfigSpec *specs;
+    register Tk_ConfigSpec *specPtr;
+    register char *option;
+
+    specs = TCL_VARARGS_START(Tk_ConfigSpec *, arg1, argList);
+    while ((option = va_arg(argList, char *)) != NULL) {
+	for (specPtr = specs; specPtr->type != TK_CONFIG_END; specPtr++) {
+	    if ((Tcl_StringMatch(specPtr->argvName, option)) &&
+		(specPtr->specFlags & TK_CONFIG_OPTION_SPECIFIED)) {
+		va_end(argList);
+		return 1;
+	    }
+	}
+    }
+    va_end(argList);
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureWidgetComponent --
+ *
+ *	Configures a component of a widget.  This is useful for
+ *	widgets that have multiple components which aren't uniquely
+ *	identified by a Tk_Window. It allows us, for example, set
+ *	resources for axes of the graph widget. The graph really has
+ *	only one window, but its convenient to specify components in a
+ *	hierarchy of options.
+ *
+ *		*graph.x.logScale yes
+ *		*graph.Axis.logScale yes
+ *		*graph.temperature.scaleSymbols yes
+ *		*graph.Element.scaleSymbols yes
+ *
+ *	This is really a hack to work around the limitations of the Tk
+ *	resource database.  It creates a temporary window, needed to
+ *	call Tk_ConfigureWidget, using the name of the component.
+ *
+ * Results:
+ *      A standard Tcl result.
+ *
+ * Side Effects:
+ *	A temporary window is created merely to pass to Tk_ConfigureWidget.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureWidgetComponent(interp, parent, resName, className, specsPtr, 
+	argc, argv, widgRec, flags)
+    Tcl_Interp *interp;
+    Tk_Window parent;		/* Window to associate with component */
+    char resName[];		/* Name of component */
+    char className[];
+    Tk_ConfigSpec *specsPtr;
+    int argc;
+    char *argv[];
+    char *widgRec;
+    int flags;
+{
+    Tk_Window tkwin;
+    int result;
+    char *tempName;
+    int isTemporary = FALSE;
+
+    tempName = Blt_Strdup(resName);
+
+    /* Window name can't start with an upper case letter */
+    tempName[0] = tolower(resName[0]);
+
+    /*
+     * Create component if a child window by the component's name
+     * doesn't already exist.
+     */
+    tkwin = Blt_FindChild(parent, tempName);
+    if (tkwin == NULL) {
+	tkwin = Tk_CreateWindow(interp, parent, tempName, (char *)NULL);
+	isTemporary = TRUE;
+    }
+    if (tkwin == NULL) {
+	Tcl_AppendResult(interp, "can't find window in \"", 
+		 Tk_PathName(parent), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    assert(Tk_Depth(tkwin) == Tk_Depth(parent));
+    Blt_Free(tempName);
+
+    Tk_SetClass(tkwin, className);
+    result = Tk_ConfigureWidget(interp, tkwin, specsPtr, argc, argv, widgRec,
+	flags);
+    if (isTemporary) {
+	Tk_DestroyWindow(tkwin);
+    }
+    return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToEnum --
+ *
+ *	Converts the string into its enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToEnum(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Vectors of valid strings. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String to match. */
+    char *widgRec;		/* Widget record. */
+    int offset;			/* Offset of field in record */
+{
+    int *enumPtr = (int *)(widgRec + offset);
+    char c;
+    register char **p;
+    register int i;
+    int count;
+
+    c = string[0];
+    count = 0;
+    for (p = (char **)clientData; *p != NULL; p++) {
+	if ((c == p[0][0]) && (strcmp(string, *p) == 0)) {
+	    *enumPtr = count;
+	    return TCL_OK;
+	}
+	count++;
+    }
+    *enumPtr = -1;
+
+    Tcl_AppendResult(interp, "bad value \"", string, "\": should be ",
+		(char *)NULL);
+    p = (char **)clientData; 
+    if (count > 0) {
+	Tcl_AppendResult(interp, p[0], (char *)NULL);
+    }
+    for (i = 1; i < (count - 1); i++) {
+	Tcl_AppendResult(interp, " ", p[i], ", ", (char *)NULL);
+    }
+    if (count > 1) {
+	Tcl_AppendResult(interp, " or ", p[count - 1], ".", (char *)NULL);
+    }
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EnumToString --
+ *
+ *	Returns the string associated with the enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_EnumToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* List of strings. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int value = *(int *)(widgRec + offset);
+    char **p;
+    int count;
+
+    count = 0;
+    for (p = (char **)clientData; *p != NULL; p++) {
+	count++;
+    }
+    if ((value >= count) || (value < 0)) {
+	return "unknown value";
+    }
+    p = (char **)clientData;
+    return p[value];
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltConfig.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltConfig.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltConfig.h	(revision 175)
@@ -0,0 +1,140 @@
+/* src/bltConfig.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#undef WORDS_BIGENDIAN
+
+/* Define if DBL_EPSILON is not defined in float.h */
+#undef BLT_DBL_EPSILON
+
+/* Define if drand48 isn't declared in math.h. */
+#undef NEED_DECL_DRAND48
+
+/* Define if srand48 isn't declared in math.h. */
+#undef NEED_DECL_SRAND48
+
+/* Define if strdup isn't declared in a standard header file. */
+#undef NEED_DECL_STRDUP
+
+/* Define if j1 isn't declared in a standard header file. */
+#undef NEED_DECL_J1
+
+/* Define if union wait type is defined incorrectly.  */
+#undef HAVE_UNION_WAIT
+
+/* Define if isfinite is found in libm.  */
+#undef HAVE_ISFINITE
+
+/* The number of bytes in a int.  */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long.  */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a long long.  */
+#undef SIZEOF_LONG_LONG
+
+/* The number of bytes in a void *.  */
+#undef SIZEOF_VOID_P
+
+/* Define if you have the XExtendedMaxRequestSize function.  */
+#undef HAVE_XEXTENDEDMAXREQUESTSIZE
+
+/* Define if you have the drand48 function.  */
+#undef HAVE_DRAND48
+
+/* Define if you have the finite function.  */
+#undef HAVE_FINITE
+
+/* Define if you have the isnan function.  */
+#undef HAVE_ISNAN
+
+/* Define if you have the srand48 function.  */
+#undef HAVE_SRAND48
+
+/* Define if you have the strcasecmp function.  */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strdup function.  */
+#undef HAVE_STRDUP
+
+/* Define if you have the strncasecmp function.  */
+#undef HAVE_STRNCASECMP
+
+/* Define if you have the <ctype.h> header file.  */
+#undef HAVE_CTYPE_H
+
+/* Define if you have the <errno.h> header file.  */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <float.h> header file.  */
+#undef HAVE_FLOAT_H
+
+/* Define if you have the <ieeefp.h> header file.  */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <inttypes.h> header file.  */
+#undef HAVE_INTTYPES_H
+
+/* Define if you have the <jpeglib.h> header file.  */
+#undef HAVE_JPEGLIB_H
+
+/* Define if you have the <limits.h> header file.  */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <malloc.h> header file.  */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <math.h> header file.  */
+#undef HAVE_MATH_H
+
+/* Define if you have the <memory.h> header file.  */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <setjmp.h> header file.  */
+#undef HAVE_SETJMP_H
+
+/* Define if you have the <stdlib.h> header file.  */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/param.h> header file.  */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/wait.h> header file.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <waitflags.h> header file.  */
+#undef HAVE_WAITFLAGS_H
+
+/* Define if you have the m library (-lm).  */
+#undef HAVE_LIBM
+
+/* Define if you have the nsl library (-lnsl).  */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket).  */
+#undef HAVE_LIBSOCKET
Index: trunk/kitgen/8.x/blt/generic/bltGrAxis.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrAxis.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrAxis.c	(revision 175)
@@ -0,0 +1,4537 @@
+
+/*
+ * bltGrAxis.c --
+ *
+ *	This module implements coordinate axes for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltGrElem.h"
+#include <X11/Xutil.h>
+
+#define DEF_NUM_TICKS		4	/* Each minor tick is 20% */
+#define STATIC_TICK_SPACE	10
+
+#define TICK_LABEL_SIZE		200
+#define MAXTICKS		10001
+
+#define CLAMP(val,low,high)	\
+	(((val) < (low)) ? (low) : ((val) > (high)) ? (high) : (val))
+
+/*
+ * Round x in terms of units
+ */
+#define UROUND(x,u)		(Round((x)/(u))*(u))
+#define UCEIL(x,u)		(ceil((x)/(u))*(u))
+#define UFLOOR(x,u)		(floor((x)/(u))*(u))
+
+#define LENGTH_MAJOR_TICK 	0.030	/* Length of a major tick */
+#define LENGTH_MINOR_TICK 	0.015	/* Length of a minor (sub)tick */
+#define LENGTH_LABEL_TICK 	0.040	/* Distance from graph to start of the
+					 * label */
+#define NUMDIGITS		15	/* Specifies the number of
+					 * digits of accuracy used when
+					 * outputting axis tick labels. */
+#define AVG_TICK_NUM_CHARS	16	/* Assumed average tick label size */
+
+#define TICK_RANGE_TIGHT	0
+#define TICK_RANGE_LOOSE	1
+#define TICK_RANGE_ALWAYS_LOOSE	2
+
+#define AXIS_TITLE_PAD		2	/* Padding for axis title. */
+#define AXIS_LINE_PAD		1	/* Padding for axis line. */
+
+#define HORIZMARGIN(m)	(!((m)->site & 0x1))	/* Even sites are horizontal */
+
+typedef enum AxisComponents {
+    MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
+} AxisComponent;
+
+
+typedef struct {
+    int axis;		/* Length of the axis.  */
+    int t1;		/* Length of a major tick (in pixels). */
+    int t2;		/* Length of a minor tick (in pixels). */
+    int label;		/* Distance from axis to tick label.  */
+} AxisInfo;
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltListOption;
+
+static Tk_OptionParseProc StringToLimit;
+static Tk_OptionPrintProc LimitToString;
+static Tk_OptionParseProc StringToTicks;
+static Tk_OptionPrintProc TicksToString;
+static Tk_OptionParseProc StringToAxis;
+static Tk_OptionPrintProc AxisToString;
+static Tk_OptionParseProc StringToAnyAxis;
+static Tk_OptionParseProc StringToFormat;
+static Tk_OptionPrintProc FormatToString;
+static Tk_OptionParseProc StringToLoose;
+static Tk_OptionPrintProc LooseToString;
+
+static Tk_CustomOption limitOption =
+{
+    StringToLimit, LimitToString, (ClientData)0
+};
+
+static Tk_CustomOption majorTicksOption =
+{
+    StringToTicks, TicksToString, (ClientData)AXIS_CONFIG_MAJOR,
+};
+static Tk_CustomOption minorTicksOption =
+{
+    StringToTicks, TicksToString, (ClientData)AXIS_CONFIG_MINOR,
+};
+Tk_CustomOption bltXAxisOption =
+{
+    StringToAxis, AxisToString, (ClientData)&bltXAxisUid
+};
+Tk_CustomOption bltYAxisOption =
+{
+    StringToAxis, AxisToString, (ClientData)&bltYAxisUid
+};
+Tk_CustomOption bltAnyXAxisOption =
+{
+    StringToAnyAxis, AxisToString, (ClientData)&bltXAxisUid
+};
+Tk_CustomOption bltAnyYAxisOption =
+{
+    StringToAnyAxis, AxisToString, (ClientData)&bltYAxisUid
+};
+static Tk_CustomOption formatOption =
+{
+    StringToFormat, FormatToString, (ClientData)0,
+};
+static Tk_CustomOption looseOption =
+{
+    StringToLoose, LooseToString, (ClientData)0,
+};
+
+/* Axis flags: */
+
+#define DEF_AXIS_COMMAND		(char *)NULL
+#define DEF_AXIS_DESCENDING		"no"
+#define DEF_AXIS_FOREGROUND		RGB_BLACK
+#define DEF_AXIS_FG_MONO		RGB_BLACK
+#define DEF_AXIS_HIDE			"no"
+#define DEF_AXIS_JUSTIFY		"center"
+#define DEF_AXIS_LIMITS_FORMAT	        (char *)NULL
+#define DEF_AXIS_LINE_WIDTH		"1"
+#define DEF_AXIS_LOGSCALE		"no"
+#define DEF_AXIS_LOOSE			"no"
+#define DEF_AXIS_RANGE			"0.0"
+#define DEF_AXIS_ROTATE			"0.0"
+#define DEF_AXIS_SCROLL_INCREMENT 	"10"
+#define DEF_AXIS_SHIFTBY		"0.0"
+#define DEF_AXIS_SHOWTICKS		"yes"
+#define DEF_AXIS_STEP			"0.0"
+#define DEF_AXIS_STEP			"0.0"
+#define DEF_AXIS_SUBDIVISIONS		"2"
+#define DEF_AXIS_TAGS			"all"
+#define DEF_AXIS_TICKS			"0"
+#ifdef WIN32
+#define DEF_AXIS_TICK_FONT		"{Arial Narrow} 8"
+#else
+#define DEF_AXIS_TICK_FONT		"*-Helvetica-Medium-R-Normal-*-10-*"
+#endif
+#define DEF_AXIS_TICK_LENGTH		"8"
+#define DEF_AXIS_TITLE_ALTERNATE	"0"
+#define DEF_AXIS_TITLE_FG		RGB_BLACK
+#define DEF_AXIS_TITLE_FONT		STD_FONT
+#define DEF_AXIS_X_STEP_BARCHART	"1.0"
+#define DEF_AXIS_X_SUBDIVISIONS_BARCHART "0"
+#define DEF_AXIS_BACKGROUND		(char *)NULL
+#define DEF_AXIS_BORDERWIDTH		"0"
+#define DEF_AXIS_RELIEF			"flat"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_DOUBLE, "-autorange", "autoRange", "AutoRange",
+	DEF_AXIS_RANGE, Tk_Offset(Axis, windowSize),
+        ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT}, 
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_AXIS_BACKGROUND, Tk_Offset(Axis, border),
+	ALL_GRAPHS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_AXIS_TAGS, Tk_Offset(Axis, tags),
+	ALL_GRAPHS | TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, 
+        (char *)NULL, 0, ALL_GRAPHS},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_AXIS_BORDERWIDTH, Tk_Offset(Axis, borderWidth),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_AXIS_FOREGROUND, Tk_Offset(Axis, tickTextStyle.color),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_AXIS_FG_MONO, Tk_Offset(Axis, tickTextStyle.color),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_STRING, "-command", "command", "Command",
+	DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
+	TK_CONFIG_NULL_OK | ALL_GRAPHS},
+    {TK_CONFIG_BOOLEAN, "-descending", "descending", "Descending",
+	DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_AXIS_HIDE, Tk_Offset(Axis, hidden),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+	DEF_AXIS_JUSTIFY, Tk_Offset(Axis, titleTextStyle.justify),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-labeloffset", "labelOffset", "LabelOffset",
+        (char *)NULL, Tk_Offset(Axis, labelOffset), ALL_GRAPHS}, 
+    {TK_CONFIG_COLOR, "-limitscolor", "limitsColor", "Color",
+	DEF_AXIS_FOREGROUND, Tk_Offset(Axis, limitsTextStyle.color),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_COLOR, "-limitscolor", "limitsColor", "Color",
+	DEF_AXIS_FG_MONO, Tk_Offset(Axis, limitsTextStyle.color),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_FONT, "-limitsfont", "limitsFont", "Font",
+	DEF_AXIS_TICK_FONT, Tk_Offset(Axis, limitsTextStyle.font), ALL_GRAPHS},
+    {TK_CONFIG_CUSTOM, "-limitsformat", "limitsFormat", "LimitsFormat",
+        (char *)NULL, Tk_Offset(Axis, limitsFormats),
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &formatOption},
+    {TK_CONFIG_CUSTOM, "-limitsshadow", "limitsShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, limitsTextStyle.shadow),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-limitsshadow", "limitsShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, limitsTextStyle.shadow),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+	DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-logscale", "logScale", "LogScale",
+	DEF_AXIS_LOGSCALE, Tk_Offset(Axis, logScale),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-loose", "loose", "Loose",
+	DEF_AXIS_LOOSE, 0, ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT,
+	&looseOption},
+    {TK_CONFIG_CUSTOM, "-majorticks", "majorTicks", "MajorTicks",
+	(char *)NULL, Tk_Offset(Axis, t1Ptr),
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &majorTicksOption},
+    {TK_CONFIG_CUSTOM, "-max", "max", "Max",
+	(char *)NULL, Tk_Offset(Axis, reqMax), 
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &limitOption},
+    {TK_CONFIG_CUSTOM, "-min", "min", "Min",
+	(char *)NULL, Tk_Offset(Axis, reqMin), 
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &limitOption},
+    {TK_CONFIG_CUSTOM, "-minorticks", "minorTicks", "MinorTicks",
+	(char *)NULL, Tk_Offset(Axis, t2Ptr),
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &minorTicksOption},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_AXIS_RELIEF, Tk_Offset(Axis, relief), 
+        ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+	DEF_AXIS_ROTATE, Tk_Offset(Axis, tickTextStyle.theta),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
+	(char *)NULL, Tk_Offset(Axis, scrollCmdPrefix),
+	ALL_GRAPHS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement", "ScrollIncrement",
+	DEF_AXIS_SCROLL_INCREMENT, Tk_Offset(Axis, scrollUnits),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+    {TK_CONFIG_CUSTOM, "-scrollmax", "scrollMax", "ScrollMax",
+	(char *)NULL, Tk_Offset(Axis, scrollMax), 
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &limitOption},
+    {TK_CONFIG_CUSTOM, "-scrollmin", "scrollMin", "ScrollMin",
+	(char *)NULL, Tk_Offset(Axis, scrollMin), 
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &limitOption},
+    {TK_CONFIG_DOUBLE, "-shiftby", "shiftBy", "ShiftBy",
+	DEF_AXIS_SHIFTBY, Tk_Offset(Axis, shiftBy),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-showticks", "showTicks", "ShowTicks",
+	DEF_AXIS_SHOWTICKS, Tk_Offset(Axis, showTicks),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-stepsize", "stepSize", "StepSize",
+	DEF_AXIS_STEP, Tk_Offset(Axis, reqStep),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-tickdivider", "tickDivider", "TickDivider",
+	DEF_AXIS_STEP, Tk_Offset(Axis, tickZoom),
+	ALL_GRAPHS | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_INT, "-subdivisions", "subdivisions", "Subdivisions",
+	DEF_AXIS_SUBDIVISIONS, Tk_Offset(Axis, reqNumMinorTicks),
+	ALL_GRAPHS},
+    {TK_CONFIG_FONT, "-tickfont", "tickFont", "Font",
+	DEF_AXIS_TICK_FONT, Tk_Offset(Axis, tickTextStyle.font), ALL_GRAPHS},
+    {TK_CONFIG_PIXELS, "-ticklength", "tickLength", "TickLength",
+	DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_GRAPHS},
+    {TK_CONFIG_CUSTOM, "-tickshadow", "tickShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, tickTextStyle.shadow),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-tickshadow", "tickShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, tickTextStyle.shadow),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_STRING, "-title", "title", "Title",
+	(char *)NULL, Tk_Offset(Axis, title),
+	TK_CONFIG_DONT_SET_DEFAULT | TK_CONFIG_NULL_OK | ALL_GRAPHS},
+    {TK_CONFIG_BOOLEAN, "-titlealternate", "titleAlternate", "TitleAlternate",
+	DEF_AXIS_TITLE_ALTERNATE, Tk_Offset(Axis, titleAlternate),
+	TK_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+    {TK_CONFIG_COLOR, "-titlecolor", "titleColor", "Color",
+	DEF_AXIS_FOREGROUND, Tk_Offset(Axis, titleTextStyle.color),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_COLOR, "-titlecolor", "titleColor", "TitleColor",
+	DEF_AXIS_FG_MONO, Tk_Offset(Axis, titleTextStyle.color),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+	DEF_AXIS_TITLE_FONT, Tk_Offset(Axis, titleTextStyle.font), ALL_GRAPHS},
+    {TK_CONFIG_CUSTOM, "-titleshadow", "titleShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, titleTextStyle.shadow),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-titleshadow", "titleShadow", "Shadow",
+	(char *)NULL, Tk_Offset(Axis, titleTextStyle.shadow),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS, &bltShadowOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+static void DestroyAxis _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr));
+static int GetAxis _ANSI_ARGS_((Graph *graphPtr, char *name, Blt_Uid classUid,
+	Axis **axisPtrPtr));
+static void FreeAxis _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr));
+
+INLINE static int
+Round(register double x)
+{
+    return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+static void
+SetAxisRange(AxisRange *rangePtr, double min, double max)
+{
+    rangePtr->min = min;
+    rangePtr->max = max;
+    rangePtr->range = max - min;
+    if (FABS(rangePtr->range) < DBL_EPSILON) {
+	rangePtr->range = 1.0;
+    }
+    rangePtr->scale = 1.0 / rangePtr->range;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ *	Determines if a value lies within a given range.
+ *
+ *	The value is normalized and compared against the interval
+ *	[0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ *	DBL_EPSILON is the smallest number that can be represented
+ *	on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ *	Please note, *max* can't equal *min*.
+ *
+ * Results:
+ *	If the value is within the interval [min..max], 1 is 
+ *	returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(x, rangePtr)
+    register double x;
+    AxisRange *rangePtr;
+{
+    if (rangePtr->range < DBL_EPSILON) {
+#ifdef notdef
+	return (((rangePtr->max - x) >= (FABS(x) * DBL_EPSILON)) &&
+		((x - rangePtr->min) >= (FABS(x) * DBL_EPSILON)));
+#endif
+	return (FABS(rangePtr->max - x) >= DBL_EPSILON);
+    } else {
+	double norm;
+
+	norm = (x - rangePtr->min) * rangePtr->scale;
+	return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+    }
+}
+
+INLINE static int
+AxisIsHorizontal(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    return ((axisPtr->classUid == bltYAxisUid) == graphPtr->inverted);
+}
+
+
+
+/* ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAnyAxis --
+ *
+ *	Converts the name of an axis to a pointer to its axis structure.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The axis flags are
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAnyAxis(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Class identifier of the type of 
+				 * axis we are looking for. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to. */
+    Tk_Window tkwin;		/* Used to look up pointer to graph. */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+    Blt_Uid classUid = *(Blt_Uid *)clientData;
+    Graph *graphPtr;
+    Axis *axisPtr;
+
+    graphPtr = Blt_GetGraphFromWindowData(tkwin);
+    if (*axisPtrPtr != NULL) {
+	FreeAxis(graphPtr, *axisPtrPtr);
+    }
+    if (string[0] == '\0') {
+	axisPtr = NULL;
+    } else if (GetAxis(graphPtr, string, classUid, &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    *axisPtrPtr = axisPtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAxis --
+ *
+ *	Converts the name of an axis to a pointer to its axis structure.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The axis flags are
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAxis(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Class identifier of the type of 
+				 * axis we are looking for. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to. */
+    Tk_Window tkwin;		/* Used to look up pointer to graph. */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+    Blt_Uid classUid = *(Blt_Uid *)clientData;
+    Graph *graphPtr;
+
+    graphPtr = Blt_GetGraphFromWindowData(tkwin);
+    if (*axisPtrPtr != NULL) {
+	FreeAxis(graphPtr, *axisPtrPtr);
+    }
+    if (GetAxis(graphPtr, string, classUid, axisPtrPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AxisToString --
+ *
+ *	Convert the window coordinates into a string.
+ *
+ * Results:
+ *	The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+AxisToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Pointer to structure record .*/
+    int offset;			/* Offset of field in structure. */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Axis *axisPtr = *(Axis **)(widgRec + offset);
+
+    if (axisPtr == NULL) {
+	return "";
+    }
+    return axisPtr->name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ *	Convert the name of virtual axis to an pointer.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The axis flags are
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to. */
+    Tk_Window tkwin;		/* Used to look up pointer to graph */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    Axis *axisPtr = (Axis *)(widgRec);
+    char **argv;
+    int argc;
+
+    if (axisPtr->limitsFormats != NULL) {
+	Blt_Free(axisPtr->limitsFormats);
+    }
+    axisPtr->limitsFormats = NULL;
+    axisPtr->nFormats = 0;
+
+    if ((string == NULL) || (*string == '\0')) {
+	return TCL_OK;
+    }
+    if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (argc > 2) {
+	Tcl_AppendResult(interp, "too many elements in limits format list \"",
+	    string, "\"", (char *)NULL);
+	Blt_Free(argv);
+	return TCL_ERROR;
+    }
+    axisPtr->limitsFormats = argv;
+    axisPtr->nFormats = argc;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatToString --
+ *
+ *	Convert the window coordinates into a string.
+ *
+ * Results:
+ *	The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of limits field */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Axis *axisPtr = (Axis *)(widgRec);
+    
+    if (axisPtr->nFormats == 0) {
+	return "";
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return Tcl_Merge(axisPtr->nFormats, axisPtr->limitsFormats); 
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToLimit --
+ *
+ *	Convert the string representation of an axis limit into its numeric
+ *	form.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The symbol type is
+ *	written into the widget record.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToLimit(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Either AXIS_CONFIG_MIN or AXIS_CONFIG_MAX.
+				 * Indicates which axis limit to set. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    double *limitPtr = (double *)(widgRec + offset);
+
+    if ((string == NULL) || (*string == '\0')) {
+	*limitPtr = VALUE_UNDEFINED;
+    } else if (Tcl_ExprDouble(interp, string, limitPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LimitToString --
+ *
+ *	Convert the floating point axis limits into a string.
+ *
+ * Results:
+ *	The string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+LimitToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Either LMIN or LMAX */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* */
+    int offset;
+    Tcl_FreeProc **freeProcPtr;
+{
+    double limit = *(double *)(widgRec + offset);
+    char *result;
+
+    result = "";
+    if (DEFINED(limit)) {
+	char string[TCL_DOUBLE_SPACE + 1];
+	Graph *graphPtr;
+
+	graphPtr = Blt_GetGraphFromWindowData(tkwin);
+	Tcl_PrintDouble(graphPtr->interp, limit, string);
+	result = Blt_Strdup(string);
+	if (result == NULL) {
+	    return "";
+	}
+	*freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    }
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToTicks --
+ *
+ *
+ * Results:
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToTicks(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    unsigned int mask = (unsigned int)clientData;
+    Axis *axisPtr = (Axis *)widgRec;
+    Ticks **ticksPtrPtr = (Ticks **) (widgRec + offset);
+    int nTicks;
+    Ticks *ticksPtr;
+
+    nTicks = 0;
+    ticksPtr = NULL;
+    if ((string != NULL) && (*string != '\0')) {
+	int nExprs;
+	char **exprArr;
+
+	if (Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (nExprs > 0) {
+	    register int i;
+	    int result = TCL_ERROR;
+	    double value;
+
+	    ticksPtr = Blt_Malloc(sizeof(Ticks) + (nExprs * sizeof(double)));
+	    assert(ticksPtr);
+	    for (i = 0; i < nExprs; i++) {
+		result = Tcl_ExprDouble(interp, exprArr[i], &value);
+		if (result != TCL_OK) {
+		    break;
+		}
+		ticksPtr->values[i] = value;
+	    }
+	    Blt_Free(exprArr);
+	    if (result != TCL_OK) {
+		Blt_Free(ticksPtr);
+		return TCL_ERROR;
+	    }
+	    nTicks = nExprs;
+	}
+    }
+    axisPtr->flags &= ~mask;
+    if (ticksPtr != NULL) {
+	axisPtr->flags |= mask;
+	ticksPtr->nTicks = nTicks;
+    }
+    if (*ticksPtrPtr != NULL) {
+	Blt_Free(*ticksPtrPtr);
+    }
+    *ticksPtrPtr = ticksPtr;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TicksToString --
+ *
+ *	Convert array of tick coordinates to a list.
+ *
+ * Results:
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+TicksToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* */
+    int offset;
+    Tcl_FreeProc **freeProcPtr;
+{
+    Ticks *ticksPtr = *(Ticks **) (widgRec + offset);
+    char string[TCL_DOUBLE_SPACE + 1];
+    register int i;
+    char *result;
+    Tcl_DString dString;
+    Graph *graphPtr;
+
+    if (ticksPtr == NULL) {
+	return "";
+    }
+    Tcl_DStringInit(&dString);
+    graphPtr = Blt_GetGraphFromWindowData(tkwin);
+    for (i = 0; i < ticksPtr->nTicks; i++) {
+	Tcl_PrintDouble(graphPtr->interp, ticksPtr->values[i], string);
+	Tcl_DStringAppendElement(&dString, string);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    result = Blt_Strdup(Tcl_DStringValue(&dString));
+    Tcl_DStringFree(&dString);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToLoose --
+ *
+ *	Convert a string to one of three values.
+ *		0 - false, no, off
+ *		1 - true, yes, on
+ *		2 - always
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToLoose(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing new value. */
+    char *widgRec;		/* Pointer to structure record. */
+    int offset;			/* Offset of field in structure. */
+{
+    Axis *axisPtr = (Axis *)(widgRec);
+    register int i;
+    int argc;
+    char **argv;
+    int values[2];
+
+    if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((argc < 1) || (argc > 2)) {
+	Tcl_AppendResult(interp, "wrong # elements in loose value \"",
+	    string, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (i = 0; i < argc; i++) {
+	if ((argv[i][0] == 'a') && (strcmp(argv[i], "always") == 0)) {
+	    values[i] = TICK_RANGE_ALWAYS_LOOSE;
+	} else {
+	    int bool;
+
+	    if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+		Blt_Free(argv);
+		return TCL_ERROR;
+	    }
+	    values[i] = bool;
+	}
+    }
+    axisPtr->looseMin = axisPtr->looseMax = values[0];
+    if (argc > 1) {
+	axisPtr->looseMax = values[1];
+    }
+    Blt_Free(argv);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LooseToString --
+ *
+ * Results:
+ *	The string representation of the auto boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+LooseToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of flags field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Axis *axisPtr = (Axis *)widgRec;
+    Tcl_DString dString;
+    char *result;
+
+    Tcl_DStringInit(&dString);
+    if (axisPtr->looseMin == TICK_RANGE_TIGHT) {
+	Tcl_DStringAppendElement(&dString, "0");
+    } else if (axisPtr->looseMin == TICK_RANGE_LOOSE) {
+	Tcl_DStringAppendElement(&dString, "1");
+    } else if (axisPtr->looseMin == TICK_RANGE_ALWAYS_LOOSE) {
+	Tcl_DStringAppendElement(&dString, "always");
+    }
+    if (axisPtr->looseMin != axisPtr->looseMax) {
+	if (axisPtr->looseMax == TICK_RANGE_TIGHT) {
+	    Tcl_DStringAppendElement(&dString, "0");
+	} else if (axisPtr->looseMax == TICK_RANGE_LOOSE) {
+	    Tcl_DStringAppendElement(&dString, "1");
+	} else if (axisPtr->looseMax == TICK_RANGE_ALWAYS_LOOSE) {
+	    Tcl_DStringAppendElement(&dString, "always");
+	}
+    }
+    result = Blt_Strdup(Tcl_DStringValue(&dString));
+    Tcl_DStringFree(&dString);
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+static void
+FreeLabels(chainPtr)
+    Blt_Chain *chainPtr;
+{
+    Blt_ChainLink *linkPtr;
+    TickLabel *labelPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	labelPtr = Blt_ChainGetValue(linkPtr);
+	Blt_Free(labelPtr);
+    }
+    Blt_ChainReset(chainPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MakeLabel --
+ *
+ *	Converts a floating point tick value to a string to be used as its
+ *	label.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Returns a new label in the string character buffer.  The formatted
+ *	tick label will be displayed on the graph.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+static TickLabel *
+MakeLabel(graphPtr, axisPtr, value)
+    Graph *graphPtr;
+    Axis *axisPtr;		/* Axis structure */
+    double value;		/* Value to be convert to a decimal string */
+{
+    char string[TICK_LABEL_SIZE + 1];
+    TickLabel *labelPtr;
+
+    /* Generate a default tick label based upon the tick value.  */
+    if (axisPtr->logScale) {
+	sprintf(string, "1E%d", ROUND(value));
+    } else {
+	sprintf(string, "%.*g", NUMDIGITS, value);
+    }
+
+    if (axisPtr->formatCmd != NULL) {
+	Tcl_Interp *interp = graphPtr->interp;
+	Tk_Window tkwin = graphPtr->tkwin;
+
+	/*
+	 * A Tcl proc was designated to format tick labels. Append the path
+	 * name of the widget and the default tick label as arguments when
+	 * invoking it. Copy and save the new label from interp->result.
+	 */
+	Tcl_ResetResult(interp);
+	if (Tcl_VarEval(interp, axisPtr->formatCmd, " ", Tk_PathName(tkwin),
+		" ", string, (char *)NULL) != TCL_OK) {
+	    Tcl_BackgroundError(interp);
+	} else {
+	    /* 
+	     * The proc could return a string of any length, so arbitrarily 
+	     * limit it to what will fit in the return string. 
+	     */
+	    strncpy(string, Tcl_GetStringResult(interp), TICK_LABEL_SIZE);
+	    string[TICK_LABEL_SIZE] = '\0';
+	    
+	    Tcl_ResetResult(interp); /* Clear the interpreter's result. */
+	}
+    }
+    labelPtr = Blt_Malloc(sizeof(TickLabel) + strlen(string));
+    assert(labelPtr);
+    strcpy(labelPtr->string, string);
+    labelPtr->anchorPos.x = labelPtr->anchorPos.y = DBL_MAX;
+    return labelPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InvHMap --
+ *
+ *	Maps the given screen coordinate back to a graph coordinate.
+ *	Called by the graph locater routine.
+ *
+ * Results:
+ *	Returns the graph coordinate value at the given window
+ *	y-coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+double
+Blt_InvHMap(graphPtr, axisPtr, x)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double x;
+{
+    double value;
+
+    x = (double)(x - graphPtr->hOffset) * graphPtr->hScale;
+    if (axisPtr->descending) {
+	x = 1.0 - x;
+    }
+    value = (x * axisPtr->axisRange.range) + axisPtr->axisRange.min;
+    if (axisPtr->logScale) {
+	value = EXP10(value);
+    }
+    return value;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InvVMap --
+ *
+ *	Maps the given window y-coordinate back to a graph coordinate
+ *	value. Called by the graph locater routine.
+ *
+ * Results:
+ *	Returns the graph coordinate value at the given window
+ *	y-coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+double
+Blt_InvVMap(graphPtr, axisPtr, y)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double y;
+{
+    double value;
+
+    y = (double)(y - graphPtr->vOffset) * graphPtr->vScale;
+    if (axisPtr->descending) {
+	y = 1.0 - y;
+    }
+    value = ((1.0 - y) * axisPtr->axisRange.range) + axisPtr->axisRange.min;
+    if (axisPtr->logScale) {
+	value = EXP10(value);
+    }
+    return value;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_HMap --
+ *
+ *	Map the given graph coordinate value to its axis, returning a window
+ *	position.
+ *
+ * Results:
+ *	Returns a double precision number representing the window coordinate
+ *	position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+double
+Blt_HMap(graphPtr, axisPtr, x)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double x;
+{
+    if ((axisPtr->logScale) && (x != 0.0)) {
+	x = log10(FABS(x));
+    }
+    /* Map graph coordinate to normalized coordinates [0..1] */
+    x = (x - axisPtr->axisRange.min) * axisPtr->axisRange.scale;
+    if (axisPtr->descending) {
+	x = 1.0 - x;
+    }
+    return (x * graphPtr->hRange + graphPtr->hOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VMap --
+ *
+ *	Map the given graph coordinate value to its axis, returning a window
+ *	position.
+ *
+ * Results:
+ *	Returns a double precision number representing the window coordinate
+ *	position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+double
+Blt_VMap(graphPtr, axisPtr, y)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double y;
+{
+    if ((axisPtr->logScale) && (y != 0.0)) {
+	y = log10(FABS(y));
+    }
+    /* Map graph coordinate to normalized coordinates [0..1] */
+    y = (y - axisPtr->axisRange.min) * axisPtr->axisRange.scale;
+    if (axisPtr->descending) {
+	y = 1.0 - y;
+    }
+    return (((1.0 - y) * graphPtr->vRange) + graphPtr->vOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Map2D --
+ *
+ *	Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ *	Returns a XPoint structure containing the window coordinates of
+ *	the given graph x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+Point2D
+Blt_Map2D(graphPtr, x, y, axesPtr)
+    Graph *graphPtr;
+    double x, y;		/* Graph x and y coordinates */
+    Axis2D *axesPtr;		/* Specifies which axes to use */
+{
+    Point2D point;
+
+    if (graphPtr->inverted) {
+	point.x = Blt_HMap(graphPtr, axesPtr->y, y);
+	point.y = Blt_VMap(graphPtr, axesPtr->x, x);
+    } else {
+	point.x = Blt_HMap(graphPtr, axesPtr->x, x);
+	point.y = Blt_VMap(graphPtr, axesPtr->y, y);
+    }
+    return point;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InvMap2D --
+ *
+ *	Maps the given window x,y coordinates to graph values.
+ *
+ * Results:
+ *	Returns a structure containing the graph coordinates of
+ *	the given window x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+Point2D
+Blt_InvMap2D(graphPtr, x, y, axesPtr)
+    Graph *graphPtr;
+    double x, y;		/* Window x and y coordinates */
+    Axis2D *axesPtr;		/* Specifies which axes to use */
+{
+    Point2D point;
+
+    if (graphPtr->inverted) {
+	point.x = Blt_InvVMap(graphPtr, axesPtr->x, y);
+	point.y = Blt_InvHMap(graphPtr, axesPtr->y, x);
+    } else {
+	point.x = Blt_InvHMap(graphPtr, axesPtr->x, x);
+	point.y = Blt_InvVMap(graphPtr, axesPtr->y, y);
+    }
+    return point;
+}
+
+
+static void
+GetDataLimits(axisPtr, min, max)
+    Axis *axisPtr;
+    double min, max;
+{
+    if (axisPtr->valueRange.min > min) {
+	axisPtr->valueRange.min = min;
+    }
+    if (axisPtr->valueRange.max < max) {
+	axisPtr->valueRange.max = max;
+    }
+}
+
+static void
+FixAxisRange(axisPtr)
+    Axis *axisPtr;
+{
+    double min, max;
+    /*
+     * When auto-scaling, the axis limits are the bounds of the element
+     * data.  If no data exists, set arbitrary limits (wrt to log/linear
+     * scale).
+     */
+    min = axisPtr->valueRange.min;
+    max = axisPtr->valueRange.max;
+
+    if (min == DBL_MAX) {
+	if (DEFINED(axisPtr->reqMin)) {
+	    min = axisPtr->reqMin;
+	} else {
+	    min = (axisPtr->logScale) ? 0.001 : 0.0;
+	}
+    }
+    if (max == -DBL_MAX) {
+	if (DEFINED(axisPtr->reqMax)) {
+	    max = axisPtr->reqMax;
+	} else {
+	    max = 1.0;
+	}
+    }
+    if (min >= max) {
+	double value;
+
+	/*
+	 * There is no range of data (i.e. min is not less than max), 
+	 * so manufacture one.
+	 */
+	value = min;
+	if (value == 0.0) {
+	    min = -0.1, max = 0.1;
+	} else {
+	    double x;
+
+	    x = FABS(value) * 0.1;
+	    min = value - x, max = value + x;
+	}
+    }
+    SetAxisRange(&axisPtr->valueRange, min, max);
+
+    /*   
+     * The axis limits are either the current data range or overridden
+     * by the values selected by the user with the -min or -max
+     * options.
+     */
+    axisPtr->min = min;
+    axisPtr->max = max;
+    if (DEFINED(axisPtr->reqMin)) {
+	axisPtr->min = axisPtr->reqMin;
+    }
+    if (DEFINED(axisPtr->reqMax)) { 
+	axisPtr->max = axisPtr->reqMax;
+    }
+
+    if (axisPtr->max < axisPtr->min) {
+
+	/*   
+	 * If the limits still don't make sense, it's because one
+	 * limit configuration option (-min or -max) was set and the
+	 * other default (based upon the data) is too small or large.
+	 * Remedy this by making up a new min or max from the
+	 * user-defined limit.
+	 */
+
+	if (!DEFINED(axisPtr->reqMin)) {
+	    axisPtr->min = axisPtr->max - (FABS(axisPtr->max) * 0.1);
+	}
+	if (!DEFINED(axisPtr->reqMax)) {
+	    axisPtr->max = axisPtr->min + (FABS(axisPtr->max) * 0.1);
+	}
+    }
+    /* 
+     * If a window size is defined, handle auto ranging by shifting
+     * the axis limits. 
+     */
+    if ((axisPtr->windowSize > 0.0) && 
+	(!DEFINED(axisPtr->reqMin)) && (!DEFINED(axisPtr->reqMax))) {
+	if (axisPtr->shiftBy < 0.0) {
+	    axisPtr->shiftBy = 0.0;
+	}
+	max = axisPtr->min + axisPtr->windowSize;
+	if (axisPtr->max >= max) {
+	    if (axisPtr->shiftBy > 0.0) {
+		max = UCEIL(axisPtr->max, axisPtr->shiftBy);
+	    }
+	    axisPtr->min = max - axisPtr->windowSize;
+	}
+	axisPtr->max = max;
+    }
+    if ((axisPtr->max != axisPtr->prevMax) ||
+	(axisPtr->min != axisPtr->prevMin)) {
+	/* Indicate if the axis limits have changed */
+	axisPtr->flags |= AXIS_DIRTY;
+	/* and save the previous minimum and maximum values */
+	axisPtr->prevMin = axisPtr->min;
+	axisPtr->prevMax = axisPtr->max;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NiceNum --
+ *
+ *	Reference: Paul Heckbert, "Nice Numbers for Graph Labels",
+ *		   Graphics Gems, pp 61-63.  
+ *
+ *	Finds a "nice" number approximately equal to x.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+NiceNum(x, round)
+    double x;
+    int round;			/* If non-zero, round. Otherwise take ceiling
+				 * of value. */
+{
+    double expt;		/* Exponent of x */
+    double frac;		/* Fractional part of x */
+    double nice;		/* Nice, rounded fraction */
+
+    expt = floor(log10(x));
+    frac = x / EXP10(expt);	/* between 1 and 10 */
+    if (round) {
+	if (frac < 1.5) {
+	    nice = 1.0;
+	} else if (frac < 3.0) {
+	    nice = 2.0;
+	} else if (frac < 7.0) {
+	    nice = 5.0;
+	} else {
+	    nice = 10.0;
+	}
+    } else {
+	if (frac <= 1.0) {
+	    nice = 1.0;
+	} else if (frac <= 2.0) {
+	    nice = 2.0;
+	} else if (frac <= 5.0) {
+	    nice = 5.0;
+	} else {
+	    nice = 10.0;
+	}
+    }
+    return nice * EXP10(expt);
+}
+
+static Ticks *
+GenerateTicks(sweepPtr)
+    TickSweep *sweepPtr;
+{
+    Ticks *ticksPtr;
+    register int i;
+
+    ticksPtr = Blt_Malloc(sizeof(Ticks) + (sweepPtr->nSteps * sizeof(double)));
+    assert(ticksPtr);
+
+    if (sweepPtr->step == 0.0) { 
+	static double logTable[] = /* Precomputed log10 values [1..10] */
+	{
+	    0.0, 
+	    0.301029995663981, 0.477121254719662, 
+	    0.602059991327962, 0.698970004336019, 
+	    0.778151250383644, 0.845098040014257,
+	    0.903089986991944, 0.954242509439325, 
+	    1.0
+	};
+	/* Hack: A zero step indicates to use log values. */
+	for (i = 0; i < sweepPtr->nSteps; i++) {
+	    ticksPtr->values[i] = logTable[i];
+	}
+    } else {
+	double value;
+
+	value = sweepPtr->initial; /* Start from smallest axis tick */
+	for (i = 0; i < sweepPtr->nSteps; i++) {
+	    value = UROUND(value, sweepPtr->step);
+	    ticksPtr->values[i] = value;
+	    value += sweepPtr->step;
+	}
+    }
+    ticksPtr->nTicks = sweepPtr->nSteps;
+    return ticksPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LogScaleAxis --
+ *
+ * 	Determine the range and units of a log scaled axis.
+ *
+ * 	Unless the axis limits are specified, the axis is scaled
+ * 	automatically, where the smallest and largest major ticks encompass
+ * 	the range of actual data values.  When an axis limit is specified,
+ * 	that value represents the smallest(min)/largest(max) value in the
+ * 	displayed range of values.
+ *
+ * 	Both manual and automatic scaling are affected by the step used.  By
+ * 	default, the step is the largest power of ten to divide the range in
+ * 	more than one piece.
+ *
+ *	Automatic scaling:
+ *	Find the smallest number of units which contain the range of values.
+ *	The minimum and maximum major tick values will be represent the
+ *	range of values for the axis. This greatest number of major ticks
+ *	possible is 10.
+ *
+ * 	Manual scaling:
+ *   	Make the minimum and maximum data values the represent the range of
+ *   	the values for the axis.  The minimum and maximum major ticks will be
+ *   	inclusive of this range.  This provides the largest area for plotting
+ *   	and the expected results when the axis min and max values have be set
+ *   	by the user (.e.g zooming).  The maximum number of major ticks is 20.
+ *
+ *   	For log scale, there's the possibility that the minimum and
+ *   	maximum data values are the same magnitude.  To represent the
+ *   	points properly, at least one full decade should be shown.
+ *   	However, if you zoom a log scale plot, the results should be
+ *   	predictable. Therefore, in that case, show only minor ticks.
+ *   	Lastly, there should be an appropriate way to handle numbers
+ *   	<=0.
+ *
+ *          maxY
+ *            |    units = magnitude (of least significant digit)
+ *            |    high  = largest unit tick < max axis value
+ *      high _|    low   = smallest unit tick > min axis value
+ *            |
+ *            |    range = high - low
+ *            |    # ticks = greatest factor of range/units
+ *           _|
+ *        U   |
+ *        n   |
+ *        i   |
+ *        t  _|
+ *            |
+ *            |
+ *            |
+ *       low _|
+ *            |
+ *            |_minX________________maxX__
+ *            |   |       |      |       |
+ *     minY  low                        high
+ *           minY
+ *
+ *
+ * 	numTicks = Number of ticks
+ * 	min = Minimum value of axis
+ * 	max = Maximum value of axis
+ * 	range    = Range of values (max - min)
+ *
+ * 	If the number of decades is greater than ten, it is assumed
+ *	that the full set of log-style ticks can't be drawn properly.
+ *
+ * Results:
+ *	None
+ *
+ * ---------------------------------------------------------------------- */
+static void
+LogScaleAxis(axisPtr, min, max)
+    Axis *axisPtr;
+    double min, max;
+{
+    double range;
+    double tickMin, tickMax;
+    double majorStep, minorStep;
+    int nMajor, nMinor;
+
+    nMajor = nMinor = 0;
+    majorStep = minorStep = 0.0;
+    if (min < max) {
+	min = (min != 0.0) ? log10(FABS(min)) : 0.0;
+	max = (max != 0.0) ? log10(FABS(max)) : 1.0;
+	
+	tickMin = floor(min);
+	tickMax = ceil(max);
+	range = tickMax - tickMin;
+	
+	if (range > 10) {
+	    /* There are too many decades to display a major tick at every
+	     * decade.  Instead, treat the axis as a linear scale.  */
+	    range = NiceNum(range, 0);
+	    majorStep = NiceNum(range / DEF_NUM_TICKS, 1);
+	    tickMin = UFLOOR(tickMin, majorStep);
+	    tickMax = UCEIL(tickMax, majorStep);
+	    nMajor = (int)((tickMax - tickMin) / majorStep) + 1;
+	    minorStep = EXP10(floor(log10(majorStep)));
+	    if (minorStep == majorStep) {
+		nMinor = 4, minorStep = 0.2;
+	    } else {
+		nMinor = Round(majorStep / minorStep) - 1;
+	    }
+	} else {
+	    if (tickMin == tickMax) {
+		tickMax++;
+	    }
+	    majorStep = 1.0;
+	    nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */
+	    
+	    minorStep = 0.0;	/* This is a special hack to pass
+				 * information to the GenerateTicks
+				 * routine. An interval of 0.0 tells
+				 *	1) this is a minor sweep and 
+				 *	2) the axis is log scale.  
+				 */
+	    nMinor = 10;
+	}
+	if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+	    ((axisPtr->looseMin == TICK_RANGE_LOOSE) && 
+	     (DEFINED(axisPtr->reqMin)))) {
+	    tickMin = min;
+	    nMajor++;
+	}
+	if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+	    ((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+	     (DEFINED(axisPtr->reqMax)))) {
+	    tickMax = max;
+	}
+    }
+    axisPtr->majorSweep.step = majorStep;
+    axisPtr->majorSweep.initial = floor(tickMin);
+    axisPtr->majorSweep.nSteps = nMajor;
+    axisPtr->minorSweep.initial = axisPtr->minorSweep.step = minorStep;
+    axisPtr->minorSweep.nSteps = nMinor;
+    SetAxisRange(&axisPtr->axisRange, tickMin, tickMax);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LinearScaleAxis --
+ *
+ * 	Determine the units of a linear scaled axis.
+ *
+ *	The axis limits are either the range of the data values mapped
+ *	to the axis (autoscaled), or the values specified by the -min
+ *	and -max options (manual).
+ *
+ *	If autoscaled, the smallest and largest major ticks will
+ *	encompass the range of data values.  If the -loose option is
+ *	selected, the next outer ticks are choosen.  If tight, the
+ *	ticks are at or inside of the data limits are used.
+ *
+ * 	If manually set, the ticks are at or inside the data limits
+ * 	are used.  This makes sense for zooming.  You want the
+ * 	selected range to represent the next limit, not something a
+ * 	bit bigger.
+ *
+ *	Note: I added an "always" value to the -loose option to force
+ *	      the manually selected axes to be loose. It's probably
+ *	      not a good idea.
+ *
+ *          maxY
+ *            |    units = magnitude (of least significant digit)
+ *            |    high  = largest unit tick < max axis value
+ *      high _|    low   = smallest unit tick > min axis value
+ *            |
+ *            |    range = high - low
+ *            |    # ticks = greatest factor of range/units
+ *           _|
+ *        U   |
+ *        n   |
+ *        i   |
+ *        t  _|
+ *            |
+ *            |
+ *            |
+ *       low _|
+ *            |
+ *            |_minX________________maxX__
+ *            |   |       |      |       |
+ *     minY  low                        high
+ *           minY
+ *
+ * 	numTicks = Number of ticks
+ * 	min = Minimum value of axis
+ * 	max = Maximum value of axis
+ * 	range    = Range of values (max - min)
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The axis tick information is set.  The actual tick values will
+ *	be generated later.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+LinearScaleAxis(axisPtr, min, max)
+    Axis *axisPtr;
+    double min, max;
+{
+    double range, step;
+    double tickMin, tickMax;
+    double axisMin, axisMax;
+    int nTicks;
+
+    nTicks = 0;
+    tickMin = tickMax = 0.0;
+    if (min < max) {
+	range = max - min;
+	
+	/* Calculate the major tick stepping. */
+	if (axisPtr->reqStep > 0.0) {
+	    /* An interval was designated by the user.  Keep scaling it
+	     * until it fits comfortably within the current range of the
+	     * axis.  */
+	    step = axisPtr->reqStep;
+	    while ((2 * step) >= range) {
+		step *= 0.5;
+	    }
+	} else {
+	    range = NiceNum(range, 0);
+	    step = NiceNum(range / DEF_NUM_TICKS, 1);
+	}
+	
+	/* Find the outer tick values. Add 0.0 to prevent getting -0.0. */
+	axisMin = tickMin = floor(min / step) * step + 0.0;
+	axisMax = tickMax = ceil(max / step) * step + 0.0;
+	
+	nTicks = Round((tickMax - tickMin) / step) + 1;
+    }
+    axisPtr->majorSweep.step = step;
+    axisPtr->majorSweep.initial = tickMin;
+    axisPtr->majorSweep.nSteps = nTicks;
+    
+    /*
+     * The limits of the axis are either the range of the data
+     * ("tight") or at the next outer tick interval ("loose").  The
+     * looseness or tightness has to do with how the axis fits the
+     * range of data values.  This option is overridden when
+     * the user sets an axis limit (by either -min or -max option).
+     * The axis limit is always at the selected limit (otherwise we
+     * assume that user would have picked a different number).
+     */
+    if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+	((axisPtr->looseMin == TICK_RANGE_LOOSE) &&
+	 (DEFINED(axisPtr->reqMin)))) {
+	axisMin = min;
+    }
+    if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+	((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+	 (DEFINED(axisPtr->reqMax)))) {
+	axisMax = max;
+    }
+    SetAxisRange(&axisPtr->axisRange, axisMin, axisMax);
+    
+    /* Now calculate the minor tick step and number. */
+    
+    if ((axisPtr->reqNumMinorTicks > 0) && 
+	((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0)) {
+	nTicks = axisPtr->reqNumMinorTicks - 1;
+	step = 1.0 / (nTicks + 1);
+    } else {
+	nTicks = 0;		/* No minor ticks. */
+	step = 0.5;		/* Don't set the minor tick interval
+				 * to 0.0. It makes the GenerateTicks
+				 * routine create minor log-scale tick
+				 * marks.  */
+    }
+    axisPtr->minorSweep.initial = axisPtr->minorSweep.step = step;
+    axisPtr->minorSweep.nSteps = nTicks;
+}
+
+static void
+SweepTicks(axisPtr)
+    Axis *axisPtr;
+{
+    if ((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0) {
+	if (axisPtr->t1Ptr != NULL) {
+	    Blt_Free(axisPtr->t1Ptr);
+	}
+	axisPtr->t1Ptr = GenerateTicks(&axisPtr->majorSweep);
+    }
+    if ((axisPtr->flags & AXIS_CONFIG_MINOR) == 0) {
+	if (axisPtr->t2Ptr != NULL) {
+	    Blt_Free(axisPtr->t2Ptr);
+	}
+	axisPtr->t2Ptr = GenerateTicks(&axisPtr->minorSweep);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_ResetAxes --
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_ResetAxes(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+    Axis *axisPtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Extents2D exts;
+    double min, max;
+
+    /* FIXME: This should be called whenever the display list of
+     *	      elements change. Maybe yet another flag INIT_STACKS to
+     *	      indicate that the element display list has changed.
+     *	      Needs to be done before the axis limits are set.
+     */
+    Blt_InitFreqTable(graphPtr);
+    if ((graphPtr->mode == MODE_STACKED) && (graphPtr->nStacks > 0)) {
+	Blt_ComputeStacks(graphPtr);
+    }
+    /*
+     * Step 1:  Reset all axes. Initialize the data limits of the axis to
+     *		impossible values.
+     */
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	axisPtr->min = axisPtr->valueRange.min = DBL_MAX;
+	axisPtr->max = axisPtr->valueRange.max = -DBL_MAX;
+    }
+
+    /*
+     * Step 2:  For each element that's to be displayed, get the smallest
+     *		and largest data values mapped to each X and Y-axis.  This
+     *		will be the axis limits if the user doesn't override them 
+     *		with -min and -max options.
+     */
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (!elemPtr->hidden) {
+	    (*elemPtr->procsPtr->extentsProc) (elemPtr, &exts);
+	    GetDataLimits(elemPtr->axes.x, exts.left, exts.right);
+	    GetDataLimits(elemPtr->axes.y, exts.top, exts.bottom);
+	}
+    }
+    /*
+     * Step 3:  Now that we know the range of data values for each axis,
+     *		set axis limits and compute a sweep to generate tick values.
+     */
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	FixAxisRange(axisPtr);
+
+	/* Calculate min/max tick (major/minor) layouts */
+	min = axisPtr->min;
+	max = axisPtr->max;
+	if ((DEFINED(axisPtr->scrollMin)) && (min < axisPtr->scrollMin)) {
+	    min = axisPtr->scrollMin;
+	}
+	if ((DEFINED(axisPtr->scrollMax)) && (max > axisPtr->scrollMax)) {
+	    max = axisPtr->scrollMax;
+	}
+	if (axisPtr->logScale) {
+	    LogScaleAxis(axisPtr, min, max);
+	} else {
+	    LinearScaleAxis(axisPtr, min, max);
+	}
+
+	if ((axisPtr->flags & (AXIS_DIRTY | AXIS_ONSCREEN)) ==
+	    (AXIS_DIRTY | AXIS_ONSCREEN)) {
+	    graphPtr->flags |= REDRAW_BACKING_STORE;
+	}
+    }
+
+    graphPtr->flags &= ~RESET_AXES;
+
+    /*
+     * When any axis changes, we need to layout the entire graph.
+     */
+    graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | 
+			MAP_ALL | REDRAW_WORLD);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetTextStyles --
+ *
+ *	Configures axis attributes (font, line width, label, etc) and
+ *	allocates a new (possibly shared) graphics context.  Line cap
+ *	style is projecting.  This is for the problem of when a tick
+ *	sits directly at the end point of the axis.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Axis resources are allocated (GC, font). Axis layout is
+ *	deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetTextStyles(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    Blt_ResetTextStyle(graphPtr->tkwin, &axisPtr->titleTextStyle);
+    Blt_ResetTextStyle(graphPtr->tkwin, &axisPtr->tickTextStyle);
+    Blt_ResetTextStyle(graphPtr->tkwin, &axisPtr->limitsTextStyle);
+
+    gcMask = (GCForeground | GCLineWidth | GCCapStyle);
+    gcValues.foreground = axisPtr->tickTextStyle.color->pixel;
+    gcValues.line_width = LineWidth(axisPtr->lineWidth);
+    gcValues.cap_style = CapProjecting;
+
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (axisPtr->tickGC != NULL) {
+	Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+    }
+    axisPtr->tickGC = newGC;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyAxis --
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Resources (font, color, gc, labels, etc.) associated with the
+ *	axis are deallocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyAxis(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    int flags;
+
+    flags = Blt_GraphType(graphPtr);
+    Tk_FreeOptions(configSpecs, (char *)axisPtr, graphPtr->display, flags);
+    if (graphPtr->bindTable != NULL) {
+	Blt_DeleteBindings(graphPtr->bindTable, axisPtr);
+    }
+    if (axisPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(axisPtr->chainPtr, axisPtr->linkPtr);
+    }
+    if (axisPtr->name != NULL) {
+	Blt_Free(axisPtr->name);
+    }
+    if (axisPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&graphPtr->axes.table, axisPtr->hashPtr);
+    }
+    Blt_FreeTextStyle(graphPtr->display, &axisPtr->titleTextStyle);
+    Blt_FreeTextStyle(graphPtr->display, &axisPtr->limitsTextStyle);
+    Blt_FreeTextStyle(graphPtr->display, &axisPtr->tickTextStyle);
+
+    if (axisPtr->tickGC != NULL) {
+	Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+    }
+    if (axisPtr->t1Ptr != NULL) {
+	Blt_Free(axisPtr->t1Ptr);
+    }
+    if (axisPtr->t2Ptr != NULL) {
+	Blt_Free(axisPtr->t2Ptr);
+    }
+    if (axisPtr->limitsFormats != NULL) {
+	Blt_Free(axisPtr->limitsFormats);
+    }
+    FreeLabels(axisPtr->tickLabels);
+    Blt_ChainDestroy(axisPtr->tickLabels);
+    if (axisPtr->segments != NULL) {
+	Blt_Free(axisPtr->segments);
+    }
+    if (axisPtr->tags != NULL) {
+	Blt_Free(axisPtr->tags);
+    }
+    Blt_Free(axisPtr);
+}
+
+static double titleRotate[4] =	/* Rotation for each axis title */
+{
+    0.0, 90.0, 0.0, 270.0
+};
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * AxisOffsets --
+ *
+ *	Determines the sites of the axis, major and minor ticks,
+ *	and title of the axis.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+AxisOffsets(graphPtr, axisPtr, margin, axisOffset, infoPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int margin;
+    int axisOffset;
+    AxisInfo *infoPtr;
+{
+    int pad;			/* Offset of axis from interior region. This
+				 * includes a possible border and the axis
+				 * line width. */
+    int p;
+    int majorOffset, minorOffset, labelOffset;
+    int offset;
+    int x, y;
+
+    axisPtr->titleTextStyle.theta = titleRotate[margin];
+
+    majorOffset = minorOffset = 0;
+    labelOffset = AXIS_TITLE_PAD;
+    if (axisPtr->lineWidth > 0) {
+	majorOffset = ABS(axisPtr->tickLength);
+	minorOffset = 10 * majorOffset / 15;
+	labelOffset = majorOffset + AXIS_TITLE_PAD + axisPtr->lineWidth / 2;
+    }
+    /* Adjust offset for the interior border width and the line width */
+    pad = axisPtr->lineWidth + 1;
+    if (graphPtr->plotBorderWidth > 0) {
+	pad += graphPtr->plotBorderWidth + 1;
+    }
+    offset = axisOffset + 1 + pad;
+    if ((margin == MARGIN_LEFT) || (margin == MARGIN_TOP)) {
+	majorOffset = -majorOffset;
+	minorOffset = -minorOffset;
+	labelOffset = -labelOffset;
+    }
+    /*
+     * Pre-calculate the x-coordinate positions of the axis, tick labels, and
+     * the individual major and minor ticks.
+     */
+    p = 0;		/* Suppress compiler warning */
+    
+    switch (margin) {
+    case MARGIN_TOP:
+	p = graphPtr->top - axisOffset - pad;
+	if (axisPtr->titleAlternate) {
+	    x = graphPtr->right + AXIS_TITLE_PAD;
+	    y = graphPtr->top - axisOffset - (axisPtr->height  / 2);
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_W;
+	} else {
+	    x = (graphPtr->right + graphPtr->left) / 2;
+	    y = graphPtr->top - axisOffset - axisPtr->height - AXIS_TITLE_PAD;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_N;
+	}
+	axisPtr->tickTextStyle.anchor = TK_ANCHOR_S;
+	offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+	axisPtr->region.left = graphPtr->hOffset - offset - 2;
+	axisPtr->region.right = graphPtr->hOffset + graphPtr->hRange + 
+	    offset - 1;
+	axisPtr->region.top = p + labelOffset - 1;
+	axisPtr->region.bottom = p;
+	axisPtr->titlePos.x = x;
+	axisPtr->titlePos.y = y;
+	break;
+
+    case MARGIN_BOTTOM:
+	p = graphPtr->bottom + axisOffset + pad;
+	if (axisPtr->titleAlternate) {
+	    x = graphPtr->right + AXIS_TITLE_PAD;
+	    y = graphPtr->bottom + axisOffset + (axisPtr->height / 2);
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_W; 
+	} else {
+	    x = (graphPtr->right + graphPtr->left) / 2;
+	    y = graphPtr->bottom + axisOffset + axisPtr->height + 
+		AXIS_TITLE_PAD;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_S; 
+	}
+	axisPtr->tickTextStyle.anchor = TK_ANCHOR_N;
+	offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+	axisPtr->region.left = graphPtr->hOffset - offset - 2;
+	axisPtr->region.right = graphPtr->hOffset + graphPtr->hRange + 
+	    offset - 1;
+
+	axisPtr->region.top = graphPtr->bottom + axisOffset + 
+	    axisPtr->lineWidth - axisPtr->lineWidth / 2;
+	axisPtr->region.bottom = graphPtr->bottom + axisOffset + 
+	    axisPtr->lineWidth + labelOffset + 1;
+	axisPtr->titlePos.x = x;
+	axisPtr->titlePos.y = y;
+	break;
+
+    case MARGIN_LEFT:
+	p = graphPtr->left - axisOffset - pad;
+	if (axisPtr->titleAlternate) {
+	    x = graphPtr->left - axisOffset - (axisPtr->width / 2);
+	    y = graphPtr->top - AXIS_TITLE_PAD;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_SW; 
+	} else {
+	    x = graphPtr->left - axisOffset - axisPtr->width -
+		graphPtr->plotBorderWidth;
+	    y = (graphPtr->bottom + graphPtr->top) / 2;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_W; 
+	}
+	axisPtr->tickTextStyle.anchor = TK_ANCHOR_E;
+	axisPtr->region.left = graphPtr->left - offset + labelOffset - 1;
+	axisPtr->region.right = graphPtr->left - offset + 2;
+
+	offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+	axisPtr->region.top = graphPtr->vOffset - offset - 2;
+	axisPtr->region.bottom = graphPtr->vOffset + graphPtr->vRange + 
+	    offset - 1;
+	axisPtr->titlePos.x = x;
+	axisPtr->titlePos.y = y;
+	break;
+
+    case MARGIN_RIGHT:
+	p = graphPtr->right + axisOffset + pad;
+	if (axisPtr->titleAlternate) {
+	    x = graphPtr->right + axisOffset + (axisPtr->width / 2);
+	    y = graphPtr->top - AXIS_TITLE_PAD;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_SE; 
+	} else {
+	    x = graphPtr->right + axisOffset + axisPtr->width + 
+		AXIS_TITLE_PAD;
+	    y = (graphPtr->bottom + graphPtr->top) / 2;
+	    axisPtr->titleTextStyle.anchor = TK_ANCHOR_E;
+	}
+	axisPtr->tickTextStyle.anchor = TK_ANCHOR_W;
+
+	axisPtr->region.left = graphPtr->right + axisOffset + 
+	    axisPtr->lineWidth - axisPtr->lineWidth / 2;
+	axisPtr->region.right = graphPtr->right + axisOffset + 
+	    labelOffset + axisPtr->lineWidth + 1;
+
+	offset = axisPtr->borderWidth + axisPtr->lineWidth / 2;
+	axisPtr->region.top = graphPtr->vOffset - offset - 2;
+	axisPtr->region.bottom = graphPtr->vOffset + graphPtr->vRange + 
+	    offset - 1;
+	axisPtr->titlePos.x = x;
+	axisPtr->titlePos.y = y;
+	break;
+
+    case MARGIN_NONE:
+	break;
+    }
+    infoPtr->axis = p - (axisPtr->lineWidth / 2);
+    infoPtr->t1 = p + majorOffset;
+    infoPtr->t2 = p + minorOffset;
+    infoPtr->label = p + labelOffset;
+
+    if (axisPtr->tickLength < 0) {
+	int hold;
+	
+	hold = infoPtr->t1;
+	infoPtr->t1 = infoPtr->axis;
+	infoPtr->axis = hold;
+    }
+}
+
+static void
+MakeAxisLine(graphPtr, axisPtr, line, segPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;		/* Axis information */
+    int line;
+    Segment2D *segPtr;
+{
+    double min, max;
+
+    min = axisPtr->axisRange.min;
+    max = axisPtr->axisRange.max;
+    if (axisPtr->logScale) {
+	min = EXP10(min);
+	max = EXP10(max);
+    }
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	segPtr->p.x = Blt_HMap(graphPtr, axisPtr, min);
+	segPtr->q.x = Blt_HMap(graphPtr, axisPtr, max);
+	segPtr->p.y = segPtr->q.y = line;
+    } else {
+	segPtr->q.x = segPtr->p.x = line;
+	segPtr->p.y = Blt_VMap(graphPtr, axisPtr, min);
+	segPtr->q.y = Blt_VMap(graphPtr, axisPtr, max);
+    }
+}
+
+
+static void
+MakeTick(graphPtr, axisPtr, value, tick, line, segPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double value;
+    int tick, line;		/* Lengths of tick and axis line. */
+    Segment2D *segPtr;
+{
+    if (axisPtr->logScale) {
+	value = EXP10(value);
+    }
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	segPtr->p.x = segPtr->q.x = Blt_HMap(graphPtr, axisPtr, value);
+	segPtr->p.y = line;
+	segPtr->q.y = tick;
+    } else {
+	segPtr->p.x = line;
+	segPtr->p.y = segPtr->q.y = Blt_VMap(graphPtr, axisPtr, value);
+	segPtr->q.x = tick;
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * MapAxis --
+ *
+ *	Pre-calculates positions of the axis, ticks, and labels (to be
+ *	used later when displaying the axis).  Calculates the values
+ *	for each major and minor tick and checks to see if they are in
+ *	range (the outer ticks may be outside of the range of plotted
+ *	values).
+ *
+ *	Line segments for the minor and major ticks are saved into one
+ *	XSegment array so that they can be drawn by a single
+ *	XDrawSegments call. The positions of the tick labels are also
+ *	computed and saved.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Line segments and tick labels are saved and used later to draw
+ *	the axis.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+MapAxis(graphPtr, axisPtr, offset, margin)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int offset;
+    int margin;
+{
+    int arraySize;
+    int nMajorTicks, nMinorTicks;
+    AxisInfo info;
+    Segment2D *segments;
+    Segment2D *segPtr;
+
+    AxisOffsets(graphPtr, axisPtr, margin, offset, &info);
+
+    /* Save all line coordinates in an array of line segments. */
+
+    if (axisPtr->segments != NULL) {
+	Blt_Free(axisPtr->segments);
+    }
+    nMajorTicks = nMinorTicks = 0;
+    if (axisPtr->t1Ptr != NULL) {
+	nMajorTicks = axisPtr->t1Ptr->nTicks;
+    }
+    if (axisPtr->t2Ptr != NULL) {
+	nMinorTicks = axisPtr->t2Ptr->nTicks;
+    }
+    arraySize = 1 + (nMajorTicks * (nMinorTicks + 1));
+    segments = Blt_Malloc(arraySize * sizeof(Segment2D));
+    assert(segments);
+
+    segPtr = segments;
+    if (axisPtr->lineWidth > 0) {
+	/* Axis baseline */
+	MakeAxisLine(graphPtr, axisPtr, info.axis, segPtr);
+	segPtr++;
+    }
+    if (axisPtr->showTicks) {
+	double t1, t2;
+	double labelPos;
+	register int i, j;
+	int isHoriz;
+	TickLabel *labelPtr;
+	Blt_ChainLink *linkPtr;
+	Segment2D seg;
+
+	isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+	for (i = 0; i < axisPtr->t1Ptr->nTicks; i++) {
+	    t1 = axisPtr->t1Ptr->values[i];
+	    /* Minor ticks */
+	    for (j = 0; j < axisPtr->t2Ptr->nTicks; j++) {
+		t2 = t1 +
+		    (axisPtr->majorSweep.step * axisPtr->t2Ptr->values[j]);
+		if (InRange(t2, &axisPtr->axisRange)) {
+		    MakeTick(graphPtr, axisPtr, t2, info.t2, info.axis, 
+			     segPtr);
+		    segPtr++;
+		}
+	    }
+	    if (!InRange(t1, &axisPtr->axisRange)) {
+		continue;
+	    }
+	    /* Major tick */
+	    MakeTick(graphPtr, axisPtr, t1, info.t1, info.axis, segPtr);
+	    segPtr++;
+	}
+
+	linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels);
+	labelPos = (double)info.label;
+
+	for (i = 0; i < axisPtr->t1Ptr->nTicks; i++) {
+	    t1 = axisPtr->t1Ptr->values[i];
+	    if (axisPtr->labelOffset) {
+		t1 += axisPtr->majorSweep.step * 0.5;
+	    }
+	    if (!InRange(t1, &axisPtr->axisRange)) {
+		continue;
+	    }
+	    labelPtr = Blt_ChainGetValue(linkPtr);
+	    linkPtr = Blt_ChainNextLink(linkPtr);
+	    MakeTick(graphPtr, axisPtr, t1, info.t1, info.axis, &seg);
+	    /* Save tick label X-Y position. */
+	    if (isHoriz) {
+		labelPtr->anchorPos.x = seg.p.x;
+		labelPtr->anchorPos.y = labelPos;
+	    } else {
+		labelPtr->anchorPos.x = labelPos;
+		labelPtr->anchorPos.y = seg.p.y;
+	    }
+	}
+    }
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	axisPtr->width = graphPtr->right - graphPtr->left;
+    } else {
+	axisPtr->height = graphPtr->bottom - graphPtr->top;
+    }
+    axisPtr->segments = segments;
+    axisPtr->nSegments = segPtr - segments;
+    assert(axisPtr->nSegments <= arraySize);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustViewport --
+ *
+ *	Adjusts the offsets of the viewport according to the scroll mode.
+ *	This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ *	"canvas"	The viewport scrolls within the range of world
+ *			coordinates.  This way the viewport always displays
+ *			a full page of the world.  If the world is smaller
+ *			than the viewport, then (bizarrely) the world and
+ *			viewport are inverted so that the world moves up
+ *			and down within the viewport.
+ *
+ *	"listbox"	The viewport can scroll beyond the range of world
+ *			coordinates.  Every entry can be displayed at the
+ *			top of the viewport.  This also means that the
+ *			scrollbar thumb weirdly shrinks as the last entry
+ *			is scrolled upward.
+ *
+ * Results:
+ *	The corrected offset is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+AdjustViewport(offset, windowSize)
+    double offset, windowSize;
+{
+    /*
+     * Canvas-style scrolling allows the world to be scrolled
+     * within the window.
+     */
+    if (windowSize > 1.0) {
+	if (windowSize < (1.0 - offset)) {
+	    offset = 1.0 - windowSize;
+	}
+	if (offset > 0.0) {
+	    offset = 0.0;
+	}
+    } else {
+	if ((offset + windowSize) > 1.0) {
+	    offset = 1.0 - windowSize;
+	}
+	if (offset < 0.0) {
+	    offset = 0.0;
+	}
+    }
+    return offset;
+}
+
+static int
+GetAxisScrollInfo(interp, argc, argv, offsetPtr, windowSize, scrollUnits)
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    double *offsetPtr;
+    double windowSize;
+    double scrollUnits;
+{
+    char c;
+    unsigned int length;
+    double offset;
+    int count;
+    double fract;
+
+    offset = *offsetPtr;
+    c = argv[0][0];
+    length = strlen(argv[0]);
+    if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
+	assert(argc == 3);
+	/* scroll number unit/page */
+	if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	c = argv[2][0];
+	length = strlen(argv[2]);
+	if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
+	    fract = (double)count * scrollUnits;
+	} else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
+	    /* A page is 90% of the view-able window. */
+	    fract = (double)count * windowSize * 0.9;
+	} else {
+	    Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
+		"\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	offset += fract;
+    } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
+	assert(argc == 2);
+	/* moveto fraction */
+	if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	offset = fract;
+    } else {
+	/* Treat like "scroll units" */
+	if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	fract = (double)count * scrollUnits;
+	offset += fract;
+	/* CHECK THIS: return TCL_OK; */
+    }
+    *offsetPtr = AdjustViewport(offset, windowSize);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawAxis --
+ *
+ *	Draws the axis, ticks, and labels onto the canvas.
+ *
+ *	Initializes and passes text attribute information through
+ *	TextStyle structure.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Axis gets drawn on window.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawAxis(graphPtr, drawable, axisPtr)
+    Graph *graphPtr;
+    Drawable drawable;
+    Axis *axisPtr;
+{
+    if (axisPtr->border != NULL) {
+	Blt_Fill3DRectangle(graphPtr->tkwin, drawable, axisPtr->border,
+		axisPtr->region.left + graphPtr->plotBorderWidth, 
+		axisPtr->region.top + graphPtr->plotBorderWidth, 
+		axisPtr->region.right - axisPtr->region.left, 
+		axisPtr->region.bottom - axisPtr->region.top, 
+		axisPtr->borderWidth, axisPtr->relief);
+    }
+    if (axisPtr->title != NULL) {
+	Blt_DrawText(graphPtr->tkwin, drawable, axisPtr->title,
+		&axisPtr->titleTextStyle, (int)axisPtr->titlePos.x, 
+		(int)axisPtr->titlePos.y);
+    }
+    if (axisPtr->scrollCmdPrefix != NULL) {
+	double viewWidth, viewMin, viewMax;
+	double worldWidth, worldMin, worldMax;
+	double fract;
+	int isHoriz;
+
+	worldMin = axisPtr->valueRange.min;
+	worldMax = axisPtr->valueRange.max;
+	if (DEFINED(axisPtr->scrollMin)) {
+	    worldMin = axisPtr->scrollMin;
+	}
+	if (DEFINED(axisPtr->scrollMax)) {
+	    worldMax = axisPtr->scrollMax;
+	}
+	viewMin = axisPtr->min;
+	viewMax = axisPtr->max;
+	if (viewMin < worldMin) {
+	    viewMin = worldMin;
+	}
+	if (viewMax > worldMax) {
+	    viewMax = worldMax;
+	}
+	if (axisPtr->logScale) {
+	    worldMin = log10(worldMin);
+	    worldMax = log10(worldMax);
+	    viewMin = log10(viewMin);
+	    viewMax = log10(viewMax);
+	}
+	worldWidth = worldMax - worldMin;	
+	viewWidth = viewMax - viewMin;
+	isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+
+	if (isHoriz != axisPtr->descending) {
+	    fract = (viewMin - worldMin) / worldWidth;
+	} else {
+	    fract = (worldMax - viewMax) / worldWidth;
+	}
+	fract = AdjustViewport(fract, viewWidth / worldWidth);
+
+	if (isHoriz != axisPtr->descending) {
+	    viewMin = (fract * worldWidth);
+	    axisPtr->min = viewMin + worldMin;
+	    axisPtr->max = axisPtr->min + viewWidth;
+	    viewMax = viewMin + viewWidth;
+	    if (axisPtr->logScale) {
+		axisPtr->min = EXP10(axisPtr->min);
+		axisPtr->max = EXP10(axisPtr->max);
+	    }
+	    Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdPrefix,
+		(viewMin / worldWidth), (viewMax / worldWidth));
+	} else {
+	    viewMax = (fract * worldWidth);
+	    axisPtr->max = worldMax - viewMax;
+	    axisPtr->min = axisPtr->max - viewWidth;
+	    viewMin = viewMax + viewWidth;
+	    if (axisPtr->logScale) {
+		axisPtr->min = EXP10(axisPtr->min);
+		axisPtr->max = EXP10(axisPtr->max);
+	    }
+	    Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdPrefix,
+		(viewMax / worldWidth), (viewMin / worldWidth));
+	}
+    }
+    if (axisPtr->showTicks) {
+	register Blt_ChainLink *linkPtr;
+	TickLabel *labelPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {	
+	    /* Draw major tick labels */
+	    labelPtr = Blt_ChainGetValue(linkPtr);
+	    Blt_DrawText(graphPtr->tkwin, drawable, labelPtr->string,
+		&axisPtr->tickTextStyle, (int)labelPtr->anchorPos.x, 
+		(int)labelPtr->anchorPos.y);
+	}
+    }
+    if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {	
+	/* Draw the tick marks and axis line. */
+	Blt_Draw2DSegments(graphPtr->display, drawable, axisPtr->tickGC,
+	    axisPtr->segments, axisPtr->nSegments);
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * AxisToPostScript --
+ *
+ *	Generates PostScript output to draw the axis, ticks, and
+ *	labels.
+ *
+ *	Initializes and passes text attribute information through
+ *	TextStyle structure.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	PostScript output is left in graphPtr->interp->result;
+ *
+ * -----------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+AxisToPostScript(psToken, axisPtr)
+    PsToken psToken;
+    Axis *axisPtr;
+{
+    if (axisPtr->title != NULL) {
+	Blt_TextToPostScript(psToken, axisPtr->title, &axisPtr->titleTextStyle, 
+		axisPtr->titlePos.x, axisPtr->titlePos.y);
+    }
+    if (axisPtr->showTicks) {
+	register Blt_ChainLink *linkPtr;
+	TickLabel *labelPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    labelPtr = Blt_ChainGetValue(linkPtr);
+	    Blt_TextToPostScript(psToken, labelPtr->string, 
+		&axisPtr->tickTextStyle, labelPtr->anchorPos.x, 
+		labelPtr->anchorPos.y);
+	}
+    }
+    if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {
+	Blt_LineAttributesToPostScript(psToken, axisPtr->tickTextStyle.color,
+	    axisPtr->lineWidth, (Blt_Dashes *)NULL, CapButt, JoinMiter);
+	Blt_2DSegmentsToPostScript(psToken, axisPtr->segments, 
+	   axisPtr->nSegments);
+    }
+}
+
+static void
+MakeGridLine(graphPtr, axisPtr, value, segPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double value;
+    Segment2D *segPtr;
+{
+    if (axisPtr->logScale) {
+	value = EXP10(value);
+    }
+    /* Grid lines run orthogonally to the axis */
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	segPtr->p.y = graphPtr->top;
+	segPtr->q.y = graphPtr->bottom;
+	segPtr->p.x = segPtr->q.x = Blt_HMap(graphPtr, axisPtr, value);
+    } else {
+	segPtr->p.x = graphPtr->left;
+	segPtr->q.x = graphPtr->right;
+	segPtr->p.y = segPtr->q.y = Blt_VMap(graphPtr, axisPtr, value);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetAxisSegments --
+ *
+ *	Assembles the grid lines associated with an axis. Generates
+ *	tick positions if necessary (this happens when the axis is
+ *	not a logical axis too).
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GetAxisSegments(graphPtr, axisPtr, segPtrPtr, nSegmentsPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    Segment2D **segPtrPtr;
+    int *nSegmentsPtr;
+{
+    int needed;
+    Ticks *t1Ptr, *t2Ptr;
+    register int i;
+    double value;
+    Segment2D *segments, *segPtr;
+
+    *nSegmentsPtr = 0;
+    *segPtrPtr = NULL;
+    if (axisPtr == NULL) {
+	return;
+    }
+    t1Ptr = axisPtr->t1Ptr;
+    if (t1Ptr == NULL) {
+	t1Ptr = GenerateTicks(&axisPtr->majorSweep);
+    }
+    t2Ptr = axisPtr->t2Ptr;
+    if (t2Ptr == NULL) {
+	t2Ptr = GenerateTicks(&axisPtr->minorSweep);
+    }
+
+    needed = t1Ptr->nTicks;
+    if (graphPtr->gridPtr->minorGrid) {
+	needed += (t1Ptr->nTicks * t2Ptr->nTicks);
+    }
+    if (needed == 0) {
+	return;			
+    }
+    segments = Blt_Malloc(sizeof(Segment2D) * needed);
+    if (segments == NULL) {
+	return;			/* Can't allocate memory for grid. */
+    }
+
+    segPtr = segments;
+    for (i = 0; i < t1Ptr->nTicks; i++) {
+	value = t1Ptr->values[i];
+	if (graphPtr->gridPtr->minorGrid) {
+	    register int j;
+	    double subValue;
+
+	    for (j = 0; j < t2Ptr->nTicks; j++) {
+		subValue = value +
+		    (axisPtr->majorSweep.step * t2Ptr->values[j]);
+		if (InRange(subValue, &axisPtr->axisRange)) {
+		    MakeGridLine(graphPtr, axisPtr, subValue, segPtr);
+		    segPtr++;
+		}
+	    }
+	}
+	if (InRange(value, &axisPtr->axisRange)) {
+	    MakeGridLine(graphPtr, axisPtr, value, segPtr);
+	    segPtr++;
+	}
+    }
+
+    if (t1Ptr != axisPtr->t1Ptr) {
+	Blt_Free(t1Ptr);	/* Free generated ticks. */
+    }
+    if (t2Ptr != axisPtr->t2Ptr) {
+	Blt_Free(t2Ptr);	/* Free generated ticks. */
+    }
+    *nSegmentsPtr = segPtr - segments;
+    assert(*nSegmentsPtr <= needed);
+    *segPtrPtr = segments;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetAxisGeometry --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetAxisGeometry(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    int height;
+
+    FreeLabels(axisPtr->tickLabels);
+    height = 0;
+    if (axisPtr->lineWidth > 0) {
+	/* Leave room for axis baseline (and pad) */
+	height += axisPtr->lineWidth + 2;
+    }
+    if (axisPtr->showTicks) {
+	int pad;
+	register int i, nLabels;
+	int lw, lh;
+	double x, x2;
+	int maxWidth, maxHeight;
+	TickLabel *labelPtr;
+
+	SweepTicks(axisPtr);
+	
+	if (axisPtr->t1Ptr->nTicks < 0) {
+	    fprintf(stderr, "%s major ticks can't be %d\n",
+		    axisPtr->name, axisPtr->t1Ptr->nTicks);
+	    abort();
+	}
+	if (axisPtr->t1Ptr->nTicks > MAXTICKS) {
+	    fprintf(stderr, "too big, %s major ticks can't be %d\n",
+		    axisPtr->name, axisPtr->t1Ptr->nTicks);
+	    abort();
+	}
+	
+	maxHeight = maxWidth = 0;
+	nLabels = 0;
+	for (i = 0; i < axisPtr->t1Ptr->nTicks; i++) {
+	    x2 = x = axisPtr->t1Ptr->values[i];
+	    if (axisPtr->labelOffset) {
+		x2 += axisPtr->majorSweep.step * 0.5;
+	    }
+	    if (!InRange(x2, &axisPtr->axisRange)) {
+		continue;
+	    }
+	    labelPtr = MakeLabel(graphPtr, axisPtr, x);
+	    Blt_ChainAppend(axisPtr->tickLabels, labelPtr);
+	    nLabels++;
+	    /* 
+	     * Get the dimensions of each tick label.  
+	     * Remember tick labels can be multi-lined and/or rotated. 
+	     */
+	    Blt_GetTextExtents(&axisPtr->tickTextStyle, labelPtr->string, 
+	       &lw, &lh);
+	    labelPtr->width = lw;
+	    labelPtr->height = lh;
+
+	    if (axisPtr->tickTextStyle.theta > 0.0) {
+		double rotWidth, rotHeight;
+
+		Blt_GetBoundingBox(lw, lh, axisPtr->tickTextStyle.theta, 
+			&rotWidth, &rotHeight, (Point2D *)NULL);
+		lw = ROUND(rotWidth);
+		lh = ROUND(rotHeight);
+	    }
+	    if (maxWidth < lw) {
+		maxWidth = lw;
+	    }
+	    if (maxHeight < lh) {
+		maxHeight = lh;
+	    }
+	}
+	assert(nLabels <= axisPtr->t1Ptr->nTicks);
+	
+	/* Because the axis cap style is "CapProjecting", we need to
+	 * account for an extra 1.5 linewidth at the end of each
+	 * line.  */
+
+	pad = ((axisPtr->lineWidth * 15) / 10);
+	
+	if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	    height += maxHeight + pad;
+	} else {
+	    height += maxWidth + pad;
+	}
+	if (axisPtr->lineWidth > 0) {
+	    /* Distance from axis line to tick label. */
+	    height += AXIS_TITLE_PAD;
+	    height += ABS(axisPtr->tickLength);
+	}
+    }
+
+    if (axisPtr->title != NULL) {
+	if (axisPtr->titleAlternate) {
+	    if (height < axisPtr->titleHeight) {
+		height = axisPtr->titleHeight;
+	    }
+	} else {
+	    height += axisPtr->titleHeight + AXIS_TITLE_PAD;
+	}
+    }
+
+    /* Correct for orientation of the axis. */
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	axisPtr->height = height;
+    } else {
+	axisPtr->width = height;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMarginGeometry --
+ *
+ *	Examines all the axes in the given margin and determines the 
+ *	area required to display them.  
+ *
+ *	Note: For multiple axes, the titles are displayed in another
+ *	      margin. So we must keep track of the widest title.
+ *	
+ * Results:
+ *	Returns the width or height of the margin, depending if it
+ *	runs horizontally along the graph or vertically.
+ *
+ * Side Effects:
+ *	The area width and height set in the margin.  Note again that
+ *	this may be corrected later (mulitple axes) to adjust for
+ *	the longest title in another margin.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetMarginGeometry(graphPtr, marginPtr)
+    Graph *graphPtr;
+    Margin *marginPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Axis *axisPtr;
+    int width, height;
+    int isHoriz;
+    int length, count;
+
+    isHoriz = HORIZMARGIN(marginPtr);
+    /* Count the number of visible axes. */
+    count = 0;
+    length = width = height = 0;
+    for (linkPtr = Blt_ChainFirstLink(marginPtr->axes); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	axisPtr = Blt_ChainGetValue(linkPtr);
+	if ((!axisPtr->hidden) && (axisPtr->flags & AXIS_ONSCREEN)) {
+	    count++;
+	    if (graphPtr->flags & GET_AXIS_GEOMETRY) {
+		GetAxisGeometry(graphPtr, axisPtr);
+	    }
+	    if ((axisPtr->titleAlternate) && (length < axisPtr->titleWidth)) {
+		length = axisPtr->titleWidth;
+	    }
+	    if (isHoriz) {
+		height += axisPtr->height;
+	    } else {
+		width += axisPtr->width;
+	    }
+	}
+    }
+    /* Enforce a minimum size for margins. */
+    if (width < 3) {
+	width = 3;
+    }
+    if (height < 3) {
+	height = 3;
+    }
+    marginPtr->nAxes = count;
+    marginPtr->axesTitleLength = length;
+    marginPtr->width = width;
+    marginPtr->height = height;
+    marginPtr->axesOffset = (HORIZMARGIN(marginPtr)) ? height : width;
+    return marginPtr->axesOffset;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeMargins --
+ *
+ *	Computes the size of the margins and the plotting area.  We
+ *	first compute the space needed for the axes in each margin.
+ *	Then how much space the legend will occupy.  Finally, if the
+ *	user has requested a margin size, we override the computed
+ *	value.
+ *
+ * Results:
+ *
+ *---------------------------------------------------------------------- */
+static void
+ComputeMargins(graphPtr)
+    Graph *graphPtr;
+{
+    int left, right, top, bottom;
+    int width, height;
+    int insets;
+
+    /* 
+     * Step 1:	Compute the amount of space needed to display the
+     *		axes (there many be 0 or more) associated with the
+     *		margin.
+     */
+    top = GetMarginGeometry(graphPtr, &graphPtr->topMargin);
+    bottom = GetMarginGeometry(graphPtr, &graphPtr->bottomMargin);
+    left = GetMarginGeometry(graphPtr, &graphPtr->leftMargin);
+    right = GetMarginGeometry(graphPtr, &graphPtr->rightMargin);
+
+    /* 
+     * Step 2:  Add the graph title height to the top margin. 
+     */
+    if (graphPtr->title != NULL) {
+	top += graphPtr->titleTextStyle.height;
+    }
+    insets = 2 * (graphPtr->inset + graphPtr->plotBorderWidth);
+
+    /* 
+     * Step 3:  Use the current estimate of the plot area to compute 
+     *		the legend size.  Add it to the proper margin.
+     */
+    width = graphPtr->width - (insets + left + right);
+    height = graphPtr->height - (insets + top + bottom);
+    Blt_MapLegend(graphPtr->legend, width, height);
+    if (!Blt_LegendIsHidden(graphPtr->legend)) {
+	switch (Blt_LegendSite(graphPtr->legend)) {
+	case LEGEND_RIGHT:
+	    right += Blt_LegendWidth(graphPtr->legend) + 2;
+	    break;
+	case LEGEND_LEFT:
+	    left += Blt_LegendWidth(graphPtr->legend) + 2;
+	    break;
+	case LEGEND_TOP:
+	    top += Blt_LegendHeight(graphPtr->legend) + 2;
+	    break;
+	case LEGEND_BOTTOM:
+	    bottom += Blt_LegendHeight(graphPtr->legend) + 2;
+	    break;
+	case LEGEND_XY:
+	case LEGEND_PLOT:
+	case LEGEND_WINDOW:
+	    /* Do nothing. */
+	    break;
+	}
+    }
+
+    /* 
+     * Recompute the plotarea, now accounting for the legend. 
+     */
+    width = graphPtr->width - (insets + left + right);
+    height = graphPtr->height - (insets + top + bottom);
+
+    /*
+     * Step 5:	If necessary, correct for the requested plot area 
+     *		aspect ratio.
+     */
+    if (graphPtr->aspect > 0.0) {
+	double ratio;
+
+	/* 
+	 * Shrink one dimension of the plotarea to fit the requested
+	 * width/height aspect ratio.  
+	 */
+	ratio = (double)width / (double)height;
+	if (ratio > graphPtr->aspect) {
+	    int scaledWidth;
+
+	    /* Shrink the width. */
+	    scaledWidth = (int)(height * graphPtr->aspect);
+	    if (scaledWidth < 1) {
+		scaledWidth = 1;
+	    }
+	    right += (width - scaledWidth); /* Add the difference to
+					     * the right margin. */
+	    /* CHECK THIS: width = scaledWidth; */
+	} else {
+	    int scaledHeight;
+
+	    /* Shrink the height. */
+	    scaledHeight = (int)(width / graphPtr->aspect);
+	    if (scaledHeight < 1) {
+		scaledHeight = 1;
+	    }
+	    top += (height - scaledHeight); /* Add the difference to
+					    * the top margin. */
+	    /* CHECK THIS: height = scaledHeight; */
+	}
+    }
+
+    /* 
+     * Step 6:	If there's multiple axes in a margin, the axis 
+     *		titles will be displayed in the adjoining marging.
+     *		Make sure there's room for the longest axis titles. 
+     */
+
+    if (top < graphPtr->leftMargin.axesTitleLength) {
+	top = graphPtr->leftMargin.axesTitleLength;
+    }
+    if (right < graphPtr->bottomMargin.axesTitleLength) {
+	right = graphPtr->bottomMargin.axesTitleLength;
+    }
+    if (top < graphPtr->rightMargin.axesTitleLength) {
+	top = graphPtr->rightMargin.axesTitleLength;
+    }
+    if (right < graphPtr->topMargin.axesTitleLength) {
+	right = graphPtr->topMargin.axesTitleLength;
+    }
+
+    /* 
+     * Step 7:  Override calculated values with requested margin 
+     *		sizes. 
+     */
+
+    graphPtr->leftMargin.width = left;
+    graphPtr->rightMargin.width = right;
+    graphPtr->topMargin.height =  top;
+    graphPtr->bottomMargin.height = bottom;
+	    
+    if (graphPtr->leftMargin.reqSize > 0) {
+	graphPtr->leftMargin.width = graphPtr->leftMargin.reqSize;
+    }
+    if (graphPtr->rightMargin.reqSize > 0) {
+	graphPtr->rightMargin.width = graphPtr->rightMargin.reqSize;
+    }
+    if (graphPtr->topMargin.reqSize > 0) {
+	graphPtr->topMargin.height = graphPtr->topMargin.reqSize;
+    }
+    if (graphPtr->bottomMargin.reqSize > 0) {
+	graphPtr->bottomMargin.height = graphPtr->bottomMargin.reqSize;
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LayoutMargins --
+ *
+ * 	Calculate the layout of the graph.  Based upon the data,
+ *	axis limits, X and Y titles, and title height, determine
+ *	the cavity left which is the plotting surface.  The first
+ *	step get the data and axis limits for calculating the space
+ *	needed for the top, bottom, left, and right margins.
+ *
+ * 	1) The LEFT margin is the area from the left border to the
+ *	   Y axis (not including ticks). It composes the border
+ *	   width, the width an optional Y axis label and its padding,
+ *	   and the tick numeric labels. The Y axis label is rotated
+ *	   90 degrees so that the width is the font height.
+ *
+ * 	2) The RIGHT margin is the area from the end of the graph
+ *	   to the right window border. It composes the border width,
+ *	   some padding, the font height (this may be dubious. It
+ *	   appears to provide a more even border), the max of the
+ *	   legend width and 1/2 max X tick number. This last part is
+ *	   so that the last tick label is not clipped.
+ *
+ *           Window Width
+ *      ___________________________________________________________
+ *      |          |                               |               |
+ *      |          |   TOP  height of title        |               |
+ *      |          |                               |               |
+ *      |          |           x2 title            |               |
+ *      |          |                               |               |
+ *      |          |        height of x2-axis      |               |
+ *      |__________|_______________________________|_______________|  W
+ *      |          | -plotpady                     |               |  i
+ *      |__________|_______________________________|_______________|  n
+ *      |          | top                   right   |               |  d
+ *      |          |                               |               |  o
+ *      |   LEFT   |                               |     RIGHT     |  w
+ *      |          |                               |               |
+ *      | y        |     Free area = 104%          |      y2       |  H
+ *      |          |     Plotting surface = 100%   |               |  e
+ *      | t        |     Tick length = 2 + 2%      |      t        |  i
+ *      | i        |                               |      i        |  g
+ *      | t        |                               |      t  legend|  h
+ *      | l        |                               |      l   width|  t
+ *      | e        |                               |      e        |
+ *      |    height|                               |height         |
+ *      |       of |                               | of            |
+ *      |    y-axis|                               |y2-axis        |
+ *      |          |                               |               |
+ *      |          |origin 0,0                     |               |
+ *      |__________|_left___________________bottom___|_______________|
+ *      |          |-plotpady                      |               |
+ *      |__________|_______________________________|_______________|
+ *      |          | (xoffset, yoffset)            |               |
+ *      |          |                               |               |
+ *      |          |       height of x-axis        |               |
+ *      |          |                               |               |
+ *      |          |   BOTTOM   x title            |               |
+ *      |__________|_______________________________|_______________|
+ *
+ * 3) The TOP margin is the area from the top window border to the top
+ *    of the graph. It composes the border width, twice the height of
+ *    the title font (if one is given) and some padding between the
+ *    title.
+ *
+ * 4) The BOTTOM margin is area from the bottom window border to the
+ *    X axis (not including ticks). It composes the border width, the height
+ *    an optional X axis label and its padding, the height of the font
+ *    of the tick labels.
+ *
+ * The plotting area is between the margins which includes the X and Y axes
+ * including the ticks but not the tick numeric labels. The length of
+ * the ticks and its padding is 5% of the entire plotting area.  Hence the
+ * entire plotting area is scaled as 105% of the width and height of the
+ * area.
+ *
+ * The axis labels, ticks labels, title, and legend may or may not be
+ * displayed which must be taken into account.
+ *
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LayoutMargins(graphPtr)
+    Graph *graphPtr;
+{
+    int width, height;
+    int titleY;
+    int left, right, top, bottom;
+
+    ComputeMargins(graphPtr);
+    left = graphPtr->leftMargin.width + graphPtr->inset + 
+	graphPtr->plotBorderWidth;
+    right = graphPtr->rightMargin.width + graphPtr->inset + 
+	graphPtr->plotBorderWidth;
+    top = graphPtr->topMargin.height + graphPtr->inset + 
+	graphPtr->plotBorderWidth;
+    bottom = graphPtr->bottomMargin.height + graphPtr->inset + 
+	graphPtr->plotBorderWidth;
+
+    /* Based upon the margins, calculate the space left for the graph. */
+    width = graphPtr->width - (left + right);
+    height = graphPtr->height - (top + bottom);
+    if (width < 1) {
+	width = 1;
+    }
+    if (height < 1) {
+	height = 1;
+    }
+    graphPtr->left = left;
+    graphPtr->right = left + width;
+    graphPtr->bottom = top + height;
+    graphPtr->top = top;
+
+    graphPtr->vOffset = top + graphPtr->padTop;
+    graphPtr->vRange = height - PADDING(graphPtr->padY);
+    graphPtr->hOffset = left + graphPtr->padLeft;
+    graphPtr->hRange = width - PADDING(graphPtr->padX);
+
+    if (graphPtr->vRange < 1) {
+	graphPtr->vRange = 1;
+    }
+    if (graphPtr->hRange < 1) {
+	graphPtr->hRange = 1;
+    }
+    graphPtr->hScale = 1.0 / (double)graphPtr->hRange;
+    graphPtr->vScale = 1.0 / (double)graphPtr->vRange;
+
+    /*
+     * Calculate the placement of the graph title so it is centered within the
+     * space provided for it in the top margin
+     */
+    titleY = graphPtr->titleTextStyle.height;
+    graphPtr->titleY = (titleY / 2) + graphPtr->inset;
+    graphPtr->titleX = (graphPtr->right + graphPtr->left) / 2;
+
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureAxis --
+ *
+ *	Configures axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Axis layout is deferred until the height and width of the
+ *	window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static int
+ConfigureAxis(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    char errMsg[200];
+
+    /* Check the requested axis limits. Can't allow -min to be greater
+     * than -max, or have undefined log scale limits.  */
+    if (((DEFINED(axisPtr->reqMin)) && (DEFINED(axisPtr->reqMax))) &&
+	(axisPtr->reqMin >= axisPtr->reqMax)) {
+	sprintf(errMsg, "impossible limits (min %g >= max %g) for axis \"%s\"",
+	    axisPtr->reqMin, axisPtr->reqMax, axisPtr->name);
+	Tcl_AppendResult(graphPtr->interp, errMsg, (char *)NULL);
+	/* Bad values, turn on axis auto-scaling */
+	axisPtr->reqMin = axisPtr->reqMax = VALUE_UNDEFINED;
+	return TCL_ERROR;
+    }
+    if ((axisPtr->logScale) && (DEFINED(axisPtr->reqMin)) &&
+	(axisPtr->reqMin <= 0.0)) {
+	sprintf(errMsg, "bad logscale limits (min=%g,max=%g) for axis \"%s\"",
+	    axisPtr->reqMin, axisPtr->reqMax, axisPtr->name);
+	Tcl_AppendResult(graphPtr->interp, errMsg, (char *)NULL);
+	/* Bad minimum value, turn on auto-scaling */
+	axisPtr->reqMin = VALUE_UNDEFINED;
+	return TCL_ERROR;
+    }
+    axisPtr->tickTextStyle.theta = FMOD(axisPtr->tickTextStyle.theta, 360.0);
+    if (axisPtr->tickTextStyle.theta < 0.0) {
+	axisPtr->tickTextStyle.theta += 360.0;
+    }
+    ResetTextStyles(graphPtr, axisPtr);
+
+    axisPtr->titleWidth = axisPtr->titleHeight = 0;
+    if (axisPtr->title != NULL) {
+	int w, h;
+
+	Blt_GetTextExtents(&axisPtr->titleTextStyle, axisPtr->title, &w, &h);
+	axisPtr->titleWidth = (short int)w;
+	axisPtr->titleHeight = (short int)h;
+    }
+
+    /* 
+     * Don't bother to check what configuration options have changed.
+     * Almost every option changes the size of the plotting area
+     * (except for -color and -titlecolor), requiring the graph and
+     * its contents to be completely redrawn.
+     *
+     * Recompute the scale and offset of the axis in case -min, -max
+     * options have changed.  
+     */
+    graphPtr->flags |= REDRAW_WORLD;
+    if (!Blt_ConfigModified(configSpecs, "-*color", "-background", "-bg",
+		    (char *)NULL)) {
+	graphPtr->flags |= (MAP_WORLD | RESET_AXES);
+	axisPtr->flags |= AXIS_DIRTY;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateAxis --
+ *
+ *	Create and initialize a structure containing information to
+ * 	display a graph axis.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Axis *
+CreateAxis(graphPtr, name, margin)
+    Graph *graphPtr;
+    char *name;			/* Identifier for axis. */
+    int margin;
+{
+    Axis *axisPtr;
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    if (name[0] == '-') {
+	Tcl_AppendResult(graphPtr->interp, "name of axis \"", name, 
+			 "\" can't start with a '-'", (char *)NULL);
+	return NULL;
+    }
+    hPtr = Blt_CreateHashEntry(&graphPtr->axes.table, name, &isNew);
+    if (!isNew) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	if (!axisPtr->deletePending) {
+	    Tcl_AppendResult(graphPtr->interp, "axis \"", name,
+		"\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+		(char *)NULL);
+	    return NULL;
+	}
+	axisPtr->deletePending = FALSE;
+    } else {
+	axisPtr = Blt_Calloc(1, sizeof(Axis));
+	assert(axisPtr);
+
+	axisPtr->name = Blt_Strdup(name);
+	axisPtr->hashPtr = hPtr;
+	axisPtr->classUid = NULL;
+	axisPtr->looseMin = axisPtr->looseMax = TICK_RANGE_TIGHT;
+	axisPtr->reqNumMinorTicks = 2;
+	axisPtr->scrollUnits = 10;
+	axisPtr->showTicks = TRUE;
+	axisPtr->reqMin = axisPtr->reqMax = VALUE_UNDEFINED;
+	axisPtr->scrollMin = axisPtr->scrollMax = VALUE_UNDEFINED;
+
+	if ((graphPtr->classUid == bltBarElementUid) && 
+	    ((margin == MARGIN_TOP) || (margin == MARGIN_BOTTOM))) {
+	    axisPtr->reqStep = 1.0;
+	    axisPtr->reqNumMinorTicks = 0;
+	} 
+	if ((margin == MARGIN_RIGHT) || (margin == MARGIN_TOP)) {
+	    axisPtr->hidden = TRUE;
+	}
+	Blt_InitTextStyle(&axisPtr->titleTextStyle);
+	Blt_InitTextStyle(&axisPtr->limitsTextStyle);
+	Blt_InitTextStyle(&axisPtr->tickTextStyle);
+	axisPtr->tickLabels = Blt_ChainCreate();
+	axisPtr->lineWidth = 1;
+	axisPtr->tickTextStyle.padX.side1 = 2;
+	axisPtr->tickTextStyle.padX.side2 = 2;
+	Blt_SetHashValue(hPtr, axisPtr);
+    }
+    return axisPtr;
+}
+
+static int
+NameToAxis(graphPtr, name, axisPtrPtr)
+    Graph *graphPtr;		/* Graph widget record. */
+    char *name;			/* Name of the axis to be searched for. */
+    Axis **axisPtrPtr;		/* (out) Pointer to found axis structure. */
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&graphPtr->axes.table, name);
+    if (hPtr != NULL) {
+	Axis *axisPtr;
+
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	if (!axisPtr->deletePending) {
+	    *axisPtrPtr = axisPtr;
+	    return TCL_OK;
+	}
+    }
+    Tcl_AppendResult(graphPtr->interp, "can't find axis \"", name,
+	    "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+    *axisPtrPtr = NULL;
+    return TCL_ERROR;
+}
+
+static int
+GetAxis(graphPtr, axisName, classUid, axisPtrPtr)
+    Graph *graphPtr;
+    char *axisName;
+    Blt_Uid classUid;
+    Axis **axisPtrPtr;
+{
+    Axis *axisPtr;
+
+    if (NameToAxis(graphPtr, axisName, &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (classUid != NULL) {
+	if ((axisPtr->refCount == 0) || (axisPtr->classUid == NULL)) {
+	    /* Set the axis type on the first use of it. */
+	    axisPtr->classUid = classUid;
+	} else if (axisPtr->classUid != classUid) {
+	    Tcl_AppendResult(graphPtr->interp, "axis \"", axisName,
+		"\" is already in use on an opposite ", axisPtr->classUid,
+	        "-axis", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	axisPtr->refCount++;
+    }
+    *axisPtrPtr = axisPtr;
+    return TCL_OK;
+}
+
+static void
+FreeAxis(graphPtr, axisPtr)
+    Graph *graphPtr;
+    Axis *axisPtr;
+{
+    axisPtr->refCount--;
+    if ((axisPtr->deletePending) && (axisPtr->refCount == 0)) {
+	DestroyAxis(graphPtr, axisPtr);
+    }
+}
+
+
+void
+Blt_DestroyAxes(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Axis *axisPtr;
+    int i;
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	axisPtr->hashPtr = NULL;
+	DestroyAxis(graphPtr, axisPtr);
+    }
+    Blt_DeleteHashTable(&graphPtr->axes.table);
+    for (i = 0; i < 4; i++) {
+	Blt_ChainDestroy(graphPtr->axisChain[i]);
+    }
+    Blt_DeleteHashTable(&graphPtr->axes.tagTable);
+    Blt_ChainDestroy(graphPtr->axes.displayList);
+}
+
+int
+Blt_DefaultAxes(graphPtr)
+    Graph *graphPtr;
+{
+    register int i;
+    Axis *axisPtr;
+    Blt_Chain *chainPtr;
+    static char *axisNames[4] = { "x", "y", "x2", "y2" } ;
+    int flags;
+
+    flags = Blt_GraphType(graphPtr);
+    for (i = 0; i < 4; i++) {
+	chainPtr = Blt_ChainCreate();
+	graphPtr->axisChain[i] = chainPtr;
+
+	/* Create a default axis for each chain. */
+	axisPtr = CreateAxis(graphPtr, axisNames[i], i);
+	if (axisPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	axisPtr->refCount = 1;	/* Default axes are assumed in use. */
+	axisPtr->classUid = (i & 1) ? bltYAxisUid : bltXAxisUid;
+	axisPtr->flags |= AXIS_ONSCREEN;
+
+	/*
+	 * Blt_ConfigureWidgetComponent creates a temporary child window 
+	 * by the name of the axis.  It's used so that the Tk routines
+	 * that access the X resource database can describe a single 
+	 * component and not the entire graph.
+	 */
+	if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+		axisPtr->name, "Axis", configSpecs, 0, (char **)NULL,
+		(char *)axisPtr, flags) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	axisPtr->linkPtr = Blt_ChainAppend(chainPtr, axisPtr);
+	axisPtr->chainPtr = chainPtr;
+    }
+    return TCL_OK;
+}
+
+
+/*----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *    .g axis bind axisName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BindOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int argc;
+    char **argv;
+{
+    Tcl_Interp *interp = graphPtr->interp;
+
+    return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+          Blt_MakeAxisTag(graphPtr, axisPtr->name), argc, argv);
+}
+          
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *	Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	Return value is a standard Tcl result.  If querying configuration
+ *	values, interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int argc;			/* Not used. */
+    char *argv[];
+{
+    return Tk_ConfigureValue(graphPtr->interp, graphPtr->tkwin, configSpecs,
+	(char *)axisPtr, argv[0], Blt_GraphType(graphPtr));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	Return value is a standard Tcl result.  If querying configuration
+ *	values, interp->result will contain the results.
+ *
+ * Side Effects:
+ *	Axis resources are possibly allocated (GC, font). Axis layout is
+ *	deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int argc;
+    char *argv[];
+{
+    int flags;
+
+    flags = TK_CONFIG_ARGV_ONLY | Blt_GraphType(graphPtr);
+    if (argc == 0) {
+	return Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin, configSpecs,
+	    (char *)axisPtr, (char *)NULL, flags);
+    } else if (argc == 1) {
+	return Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin, configSpecs,
+	    (char *)axisPtr, argv[0], flags);
+    }
+    if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
+	    argc, argv, (char *)axisPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (axisPtr->flags & AXIS_ONSCREEN) {
+	if (!Blt_ConfigModified(configSpecs, "-*color", "-background", "-bg",
+				(char *)NULL)) {
+	    graphPtr->flags |= REDRAW_BACKING_STORE;
+	}
+	graphPtr->flags |= DRAW_MARGINS;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *    Returns the name of the picked axis (using the axis
+ *    bind operation).  Right now, the only name accepted is
+ *    "current".
+ *
+ * Results:
+ *    A standard Tcl result.  The interpreter result will contain
+ *    the name of the axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;                 /* Not used. */
+    char *argv[];
+{
+    Tcl_Interp *interp = graphPtr->interp;
+    register Axis *axisPtr;
+
+    axisPtr = (Axis *)Blt_GetCurrentItem(graphPtr->bindTable);
+    /* Report only on axes. */
+    if ((axisPtr != NULL) && 
+	((axisPtr->classUid == bltXAxisUid) ||
+	 (axisPtr->classUid == bltYAxisUid) || 
+	 (axisPtr->classUid == NULL))) {
+	char c;
+	
+	c = argv[3][0];
+	if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
+	    Tcl_SetResult(interp, axisPtr->name, TCL_VOLATILE);
+	} else if ((c == 'd') && (strcmp(argv[3], "detail") == 0)) {
+	    Tcl_SetResult(interp, axisPtr->detail, TCL_VOLATILE);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LimitsOp --
+ *
+ *	This procedure returns a string representing the axis limits
+ *	of the graph.  The format of the string is { left top right bottom}.
+ *
+ * Results:
+ *	Always returns TCL_OK.  The interp->result field is
+ *	a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LimitsOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+
+{
+    Tcl_Interp *interp = graphPtr->interp;
+    double min, max;
+
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    if (axisPtr->logScale) {
+	min = EXP10(axisPtr->axisRange.min);
+	max = EXP10(axisPtr->axisRange.max);
+    } else {
+	min = axisPtr->axisRange.min;
+	max = axisPtr->axisRange.max;
+    }
+    Tcl_AppendElement(interp, Blt_Dtoa(interp, min));
+    Tcl_AppendElement(interp, Blt_Dtoa(interp, max));
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvTransformOp --
+ *
+ *	Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the axis value. If an error occurred, TCL_ERROR is returned
+ *	and interp->result will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvTransformOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int x;			/* Integer window coordinate*/
+    double y;			/* Real graph coordinate */
+
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    if (Tcl_GetInt(graphPtr->interp, argv[0], &x) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /*
+     * Is the axis vertical or horizontal?
+     *
+     * Check the site where the axis was positioned.  If the axis is
+     * virtual, all we have to go on is how it was mapped to an
+     * element (using either -mapx or -mapy options).  
+     */
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	y = Blt_InvHMap(graphPtr, axisPtr, (double)x);
+    } else {
+	y = Blt_InvVMap(graphPtr, axisPtr, (double)x);
+    }
+    Tcl_AppendElement(graphPtr->interp, Blt_Dtoa(graphPtr->interp, y));
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ *	Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the window coordinate. If an error occurred, TCL_ERROR
+ *	is returned and interp->result will contain an error
+ *	message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;		/* Axis */
+    int argc;			/* Not used. */
+    char **argv;
+{
+    double x;
+
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    if (Tcl_ExprDouble(graphPtr->interp, argv[0], &x) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (AxisIsHorizontal(graphPtr, axisPtr)) {
+	x = Blt_HMap(graphPtr, axisPtr, x);
+    } else {
+	x = Blt_VMap(graphPtr, axisPtr, x);
+    }
+    Tcl_SetResult(graphPtr->interp, Blt_Itoa((int)x), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * UseOp --
+ *
+ *	Changes the virtual axis used by the logical axis.
+ *
+ * Results:
+ *	A standard Tcl result.  If the named axis doesn't exist
+ *	an error message is put in interp->result.
+ *
+ * .g xaxis use "abc def gah"
+ * .g xaxis use [lappend abc [.g axis use]]
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UseOp(graphPtr, axisPtr, argc, argv)
+    Graph *graphPtr;
+    Axis *axisPtr;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Blt_Chain *chainPtr;
+    int nNames;
+    char **names;
+    Blt_ChainLink *linkPtr;
+    int i;
+    Blt_Uid classUid;
+    int margin;
+
+    margin = (int)argv[-1];
+    chainPtr = graphPtr->margins[margin].axes;
+    if (argc == 0) {
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr!= NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    axisPtr = Blt_ChainGetValue(linkPtr);
+	    Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+	}
+	return TCL_OK;
+    }
+    if ((margin == MARGIN_BOTTOM) || (margin == MARGIN_TOP)) {
+	classUid = (graphPtr->inverted) ? bltYAxisUid : bltXAxisUid;
+    } else {
+	classUid = (graphPtr->inverted) ? bltXAxisUid : bltYAxisUid;
+    }
+    if (Tcl_SplitList(graphPtr->interp, argv[0], &nNames, &names) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr!= NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	axisPtr = Blt_ChainGetValue(linkPtr);
+	axisPtr->linkPtr = NULL;
+	axisPtr->flags &= ~AXIS_ONSCREEN;
+	/* Clear the axis type if it's not currently used.*/
+	if (axisPtr->refCount == 0) {
+	    axisPtr->classUid = NULL; 
+	}
+    }
+    Blt_ChainReset(chainPtr);
+    for (i = 0; i < nNames; i++) {
+	if (NameToAxis(graphPtr, names[i], &axisPtr) != TCL_OK) {
+	    Blt_Free(names);
+	    return TCL_ERROR;
+	}
+	if (axisPtr->classUid == NULL) {
+	    axisPtr->classUid = classUid; 
+	} else if (axisPtr->classUid != classUid) {
+	    Tcl_AppendResult(graphPtr->interp, "wrong type axis \"", 
+		     axisPtr->name, "\": can't use ", classUid, " type axis.", 
+		     (char *)NULL);			     
+	    Blt_Free(names);
+	    return TCL_ERROR;
+	}
+	if (axisPtr->linkPtr != NULL) {
+	    /* Move the axis from the old margin's "use" list to the new. */
+	    Blt_ChainUnlinkLink(axisPtr->chainPtr, axisPtr->linkPtr);
+	    Blt_ChainAppendLink(chainPtr, axisPtr->linkPtr);
+	} else {
+	    axisPtr->linkPtr = Blt_ChainAppend(chainPtr, axisPtr);
+	}
+	axisPtr->chainPtr = chainPtr;
+	axisPtr->flags |= AXIS_ONSCREEN;
+    }
+    graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+    /* When any axis changes, we need to layout the entire graph.  */
+    graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+    Blt_EventuallyRedrawGraph(graphPtr);
+    
+    Blt_Free(names);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateVirtualOp --
+ *
+ *	Creates a new axis.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CreateVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Axis *axisPtr;
+    int flags;
+
+    axisPtr = CreateAxis(graphPtr, argv[3], MARGIN_NONE);
+    if (axisPtr == NULL) {
+	return TCL_ERROR;
+    }
+    flags = Blt_GraphType(graphPtr);
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+	    axisPtr->name, "Axis", configSpecs, argc - 4, argv + 4,
+	    (char *)axisPtr, flags) != TCL_OK) {
+	goto error;
+    }
+    if (ConfigureAxis(graphPtr, axisPtr) != TCL_OK) {
+	goto error;
+    }
+    Tcl_SetResult(graphPtr->interp, axisPtr->name, TCL_VOLATILE);
+    return TCL_OK;
+  error:
+    DestroyAxis(graphPtr, axisPtr);
+    return TCL_ERROR;
+}
+
+/*----------------------------------------------------------------------
+ *
+ * BindVirtualOp --
+ *
+ *    .g axis bind axisName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Tcl_Interp *interp = graphPtr->interp;
+
+    if (argc == 3) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	char *tagName;
+
+	for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.tagTable, &cursor);
+	     hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tagName = Blt_GetHashKey(&graphPtr->axes.tagTable, hPtr);
+	    Tcl_AppendElement(interp, tagName);
+	}
+	return TCL_OK;
+    }
+    return Blt_ConfigureBindings(interp, graphPtr->bindTable, 
+	 Blt_MakeAxisTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetVirtualOp --
+ *
+ *	Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	Return value is a standard Tcl result.  If querying configuration
+ *	values, interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char *argv[];
+{
+    Axis *axisPtr;
+
+    if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return CgetOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureVirtualOp --
+ *
+ *	Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	Return value is a standard Tcl result.  If querying configuration
+ *	values, interp->result will contain the results.
+ *
+ * Side Effects:
+ *	Axis resources are possibly allocated (GC, font). Axis layout is
+ *	deferred until the height and width of the window are known.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char *argv[];
+{
+    Axis *axisPtr;
+    int nNames, nOpts;
+    char **options;
+    register int i;
+
+    /* Figure out where the option value pairs begin */
+    argc -= 3;
+    argv += 3;
+    for (i = 0; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    break;
+	}
+	if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    nNames = i;			/* Number of pen names specified */
+    nOpts = argc - i;		/* Number of options specified */
+    options = argv + i;		/* Start of options in argv  */
+
+    for (i = 0; i < nNames; i++) {
+	if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (ConfigureOp(graphPtr, axisPtr, nOpts, options) != TCL_OK) {
+	    break;
+	}
+    }
+    if (i < nNames) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteVirtualOp --
+ *
+ *	Deletes one or more axes.  The actual removal may be deferred
+ *	until the axis is no longer used by any element. The axis
+ *	can't be referenced by its name any longer and it may be
+ *	recreated.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    register int i;
+    Axis *axisPtr;
+
+    for (i = 3; i < argc; i++) {
+	if (NameToAxis(graphPtr, argv[i], &axisPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	axisPtr->deletePending = TRUE;
+	if (axisPtr->refCount == 0) {
+	    DestroyAxis(graphPtr, axisPtr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InvTransformVirtualOp --
+ *
+ *	Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the axis value. If an error occurred, TCL_ERROR is returned
+ *	and interp->result will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvTransformVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Axis *axisPtr;
+
+    if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return InvTransformOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LimitsVirtualOp --
+ *
+ *	This procedure returns a string representing the axis limits
+ *	of the graph.  The format of the string is { left top right bottom}.
+ *
+ * Results:
+ *	Always returns TCL_OK.  The interp->result field is
+ *	a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LimitsVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+
+{
+    Axis *axisPtr;
+
+    if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return LimitsOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesVirtualOp --
+ *
+ *	Return a list of the names of all the axes.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+NamesVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Axis *axisPtr;
+    register int i;
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	if (axisPtr->deletePending) {
+	    continue;
+	}
+	if (argc == 3) {
+	    Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+	    continue;
+	}
+	for (i = 3; i < argc; i++) {
+	    if (Tcl_StringMatch(axisPtr->name, argv[i])) {
+		Tcl_AppendElement(graphPtr->interp, axisPtr->name);
+		break;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TransformVirtualOp --
+ *
+ *	Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the window coordinate. If an error occurred, TCL_ERROR
+ *	is returned and interp->result will contain an error
+ *	message.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformVirtualOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Axis *axisPtr;
+
+    if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TransformOp(graphPtr, axisPtr, argc - 4, argv + 4);
+}
+
+static int
+ViewOp(graphPtr, argc, argv)
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Axis *axisPtr;
+    Tcl_Interp *interp = graphPtr->interp;
+    double axisOffset, scrollUnits;
+    double fract;
+    double viewMin, viewMax, worldMin, worldMax;
+    double viewWidth, worldWidth;
+
+    if (NameToAxis(graphPtr, argv[3], &axisPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    worldMin = axisPtr->valueRange.min;
+    worldMax = axisPtr->valueRange.max;
+    /* Override data dimensions with user-selected limits. */
+    if (DEFINED(axisPtr->scrollMin)) {
+	worldMin = axisPtr->scrollMin;
+    }
+    if (DEFINED(axisPtr->scrollMax)) {
+	worldMax = axisPtr->scrollMax;
+    }
+    viewMin = axisPtr->min;
+    viewMax = axisPtr->max;
+    /* Bound the view within scroll region. */ 
+    if (viewMin < worldMin) {
+	viewMin = worldMin;
+    } 
+    if (viewMax > worldMax) {
+	viewMax = worldMax;
+    }
+    if (axisPtr->logScale) {
+	worldMin = log10(worldMin);
+	worldMax = log10(worldMax);
+	viewMin = log10(viewMin);
+	viewMax = log10(viewMax);
+    }
+    worldWidth = worldMax - worldMin;
+    viewWidth = viewMax - viewMin;
+
+    /* Unlike horizontal axes, vertical axis values run opposite of
+     * the scrollbar first/last values.  So instead of pushing the
+     * axis minimum around, we move the maximum instead. */
+
+    if (AxisIsHorizontal(graphPtr, axisPtr) != axisPtr->descending) {
+	axisOffset = viewMin - worldMin;
+	scrollUnits = (double)axisPtr->scrollUnits * graphPtr->hScale;
+    } else {
+	axisOffset = worldMax - viewMax;
+	scrollUnits = (double)axisPtr->scrollUnits * graphPtr->vScale;
+    }
+    if (argc == 4) {
+	/* Note: Bound the fractions between 0.0 and 1.0 to support
+	 * "canvas"-style scrolling. */
+	fract = axisOffset / worldWidth;
+	Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+	fract = (axisOffset + viewWidth) / worldWidth;
+	Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+	return TCL_OK;
+    }
+    fract = axisOffset / worldWidth;
+    if (GetAxisScrollInfo(interp, argc - 4, argv + 4, &fract, 
+		viewWidth / worldWidth, scrollUnits) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (AxisIsHorizontal(graphPtr, axisPtr) != axisPtr->descending) {
+	axisPtr->reqMin = (fract * worldWidth) + worldMin;
+	axisPtr->reqMax = axisPtr->reqMin + viewWidth;
+    } else {
+	axisPtr->reqMax = worldMax - (fract * worldWidth);
+	axisPtr->reqMin = axisPtr->reqMax - viewWidth;
+    }
+    if (axisPtr->logScale) {
+	axisPtr->reqMin = EXP10(axisPtr->reqMin);
+	axisPtr->reqMax = EXP10(axisPtr->reqMax);
+    }
+    graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+int
+Blt_VirtualAxisOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+    static Blt_OpSpec axisOps[] =
+    {
+	{"bind", 1, (Blt_Op)BindVirtualOp, 3, 6, 
+	     "axisName sequence command",},
+	{"cget", 2, (Blt_Op)CgetVirtualOp, 5, 5, "axisName option",},
+	{"configure", 2, (Blt_Op)ConfigureVirtualOp, 4, 0,
+	    "axisName ?axisName?... ?option value?...",},
+	{"create", 2, (Blt_Op)CreateVirtualOp, 4, 0,
+	    "axisName ?option value?...",},
+	{"delete", 1, (Blt_Op)DeleteVirtualOp, 3, 0, "?axisName?...",},
+	{"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+	{"invtransform", 1, (Blt_Op)InvTransformVirtualOp, 5, 5,
+	    "axisName value",},
+	{"limits", 1, (Blt_Op)LimitsVirtualOp, 4, 4, "axisName",},
+	{"names", 1, (Blt_Op)NamesVirtualOp, 3, 0, "?pattern?...",},
+	{"transform", 1, (Blt_Op)TransformVirtualOp, 5, 5, "axisName value",},
+	{"view", 1, (Blt_Op)ViewOp, 4, 7,
+	    "axisName ?moveto fract? ?scroll number what?",},
+    };
+    static int nAxisOps = sizeof(axisOps) / sizeof(Blt_OpSpec);
+
+    proc = Blt_GetOp(interp, nAxisOps, axisOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (graphPtr, argc, argv);
+    return result;
+}
+
+int
+Blt_AxisOp(graphPtr, margin, argc, argv)
+    Graph *graphPtr;
+    int margin;
+    int argc;
+    char **argv;
+{
+    int result;
+    Blt_Op proc;
+    Axis *axisPtr;
+    static Blt_OpSpec axisOps[] =
+    {
+	{"bind", 1, (Blt_Op)BindOp, 2, 5, "sequence command",},
+	{"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+	{"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+	{"invtransform", 1, (Blt_Op)InvTransformOp, 4, 4, "value",},
+	{"limits", 1, (Blt_Op)LimitsOp, 3, 3, "",},
+	{"transform", 1, (Blt_Op)TransformOp, 4, 4, "value",},
+	{"use", 1, (Blt_Op)UseOp, 3, 4, "?axisName?",},
+    };
+    static int nAxisOps = sizeof(axisOps) / sizeof(Blt_OpSpec);
+
+    proc = Blt_GetOp(graphPtr->interp, nAxisOps, axisOps, BLT_OP_ARG2, 
+	argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    argv[2] = (char *)margin; /* Hack. Slide a reference to the margin in 
+			       * the argument list. Needed only for UseOp.
+			       */
+    axisPtr = Blt_GetFirstAxis(graphPtr->margins[margin].axes);
+    result = (*proc)(graphPtr, axisPtr, argc - 3, argv + 3);
+    return result;
+}
+
+void
+Blt_MapAxes(graphPtr)
+    Graph *graphPtr;
+{
+    Axis *axisPtr;
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr;
+    register int margin;
+    int offset;
+    
+    for (margin = 0; margin < 4; margin++) {
+	chainPtr = graphPtr->margins[margin].axes;
+	offset = 0;
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    axisPtr = Blt_ChainGetValue(linkPtr);
+	    if ((!axisPtr->hidden) && (axisPtr->flags & AXIS_ONSCREEN)) {
+		MapAxis(graphPtr, axisPtr, offset, margin);
+		if (AxisIsHorizontal(graphPtr, axisPtr)) {
+		    offset += axisPtr->height;
+		} else {
+		    offset += axisPtr->width;
+		}
+	    }
+	}
+    }
+}
+
+void
+Blt_DrawAxes(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;
+{
+    Axis *axisPtr;
+    Blt_ChainLink *linkPtr;
+    register int i;
+
+    for (i = 0; i < 4; i++) {
+	for (linkPtr = Blt_ChainFirstLink(graphPtr->margins[i].axes); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    axisPtr = Blt_ChainGetValue(linkPtr);
+	    if ((!axisPtr->hidden) && (axisPtr->flags & AXIS_ONSCREEN)) {
+		DrawAxis(graphPtr, drawable, axisPtr);
+	    }
+	}
+    }
+}
+
+void
+Blt_AxesToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    Axis *axisPtr;
+    Blt_ChainLink *linkPtr;
+    register int i;
+
+    for (i = 0; i < 4; i++) {
+	for (linkPtr = Blt_ChainFirstLink(graphPtr->margins[i].axes); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    axisPtr = Blt_ChainGetValue(linkPtr);
+	    if ((!axisPtr->hidden) && (axisPtr->flags & AXIS_ONSCREEN)) {
+		AxisToPostScript(psToken, axisPtr);
+	    }
+	}
+    }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_DrawAxisLimits --
+ *
+ *	Draws the min/max values of the axis in the plotting area. 
+ *	The text strings are formatted according to the "sprintf"
+ *	format descriptors in the limitsFormats array.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws the numeric values of the axis limits into the outer
+ *	regions of the plotting area.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_DrawAxisLimits(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;
+{
+    Axis *axisPtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Dim2D textDim;
+    int isHoriz;
+    char *minPtr, *maxPtr;
+    char *minFormat, *maxFormat;
+    char minString[200], maxString[200];
+    int vMin, hMin, vMax, hMax;
+
+#define SPACING 8
+    vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+    hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2;	/* Offsets */
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+
+	if (axisPtr->nFormats == 0) {
+	    continue;
+	}
+	isHoriz = AxisIsHorizontal(graphPtr, axisPtr);
+	minPtr = maxPtr = NULL;
+	minFormat = maxFormat = axisPtr->limitsFormats[0];
+	if (axisPtr->nFormats > 1) {
+	    maxFormat = axisPtr->limitsFormats[1];
+	}
+	if (minFormat[0] != '\0') {
+	    minPtr = minString;
+	    sprintf(minString, minFormat, axisPtr->axisRange.min);
+	}
+	if (maxFormat[0] != '\0') {
+	    maxPtr = maxString;
+	    sprintf(maxString, maxFormat, axisPtr->axisRange.max);
+	}
+	if (axisPtr->descending) {
+	    char *tmp;
+
+	    tmp = minPtr, minPtr = maxPtr, maxPtr = tmp;
+	}
+	if (maxPtr != NULL) {
+	    if (isHoriz) {
+		axisPtr->limitsTextStyle.theta = 90.0;
+		axisPtr->limitsTextStyle.anchor = TK_ANCHOR_SE;
+		Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+		    &axisPtr->limitsTextStyle, graphPtr->right, hMax, &textDim);
+		hMax -= (textDim.height + SPACING);
+	    } else {
+		axisPtr->limitsTextStyle.theta = 0.0;
+		axisPtr->limitsTextStyle.anchor = TK_ANCHOR_NW;
+		Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+		    &axisPtr->limitsTextStyle, vMax, graphPtr->top, &textDim);
+		vMax += (textDim.width + SPACING);
+	    }
+	}
+	if (minPtr != NULL) {
+	    axisPtr->limitsTextStyle.anchor = TK_ANCHOR_SW;
+	    if (isHoriz) {
+		axisPtr->limitsTextStyle.theta = 90.0;
+		Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+		    &axisPtr->limitsTextStyle, graphPtr->left, hMin, &textDim);
+		hMin -= (textDim.height + SPACING);
+	    } else {
+		axisPtr->limitsTextStyle.theta = 0.0;
+		Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+		    &axisPtr->limitsTextStyle, vMin, graphPtr->bottom, &textDim);
+		vMin += (textDim.width + SPACING);
+	    }
+	}
+    }				/* Loop on axes */
+}
+
+void
+Blt_AxisLimitsToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    Axis *axisPtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    double vMin, hMin, vMax, hMax;
+    char string[200];
+    int textWidth, textHeight;
+    char *minFmt, *maxFmt;
+
+#define SPACING 8
+    vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+    hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2;	/* Offsets */
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+
+	if (axisPtr->nFormats == 0) {
+	    continue;
+	}
+	minFmt = maxFmt = axisPtr->limitsFormats[0];
+	if (axisPtr->nFormats > 1) {
+	    maxFmt = axisPtr->limitsFormats[1];
+	}
+	if (*maxFmt != '\0') {
+	    sprintf(string, maxFmt, axisPtr->axisRange.max);
+	    Blt_GetTextExtents(&axisPtr->tickTextStyle, string, &textWidth,
+		&textHeight);
+	    if ((textWidth > 0) && (textHeight > 0)) {
+		if (axisPtr->classUid == bltXAxisUid) {
+		    axisPtr->limitsTextStyle.theta = 90.0;
+		    axisPtr->limitsTextStyle.anchor = TK_ANCHOR_SE;
+		    Blt_TextToPostScript(psToken, string, 
+					 &axisPtr->limitsTextStyle, 
+					 (double)graphPtr->right, hMax);
+		    hMax -= (textWidth + SPACING);
+		} else {
+		    axisPtr->limitsTextStyle.theta = 0.0;
+		    axisPtr->limitsTextStyle.anchor = TK_ANCHOR_NW;
+		    Blt_TextToPostScript(psToken, string, 
+			 &axisPtr->limitsTextStyle, vMax, (double)graphPtr->top);
+		    vMax += (textWidth + SPACING);
+		}
+	    }
+	}
+	if (*minFmt != '\0') {
+	    sprintf(string, minFmt, axisPtr->axisRange.min);
+	    Blt_GetTextExtents(&axisPtr->tickTextStyle, string, &textWidth,
+		&textHeight);
+	    if ((textWidth > 0) && (textHeight > 0)) {
+		axisPtr->limitsTextStyle.anchor = TK_ANCHOR_SW;
+		if (axisPtr->classUid == bltXAxisUid) {
+		    axisPtr->limitsTextStyle.theta = 90.0;
+		    Blt_TextToPostScript(psToken, string, 
+					 &axisPtr->limitsTextStyle, 
+					 (double)graphPtr->left, hMin);
+		    hMin -= (textWidth + SPACING);
+		} else {
+		    axisPtr->limitsTextStyle.theta = 0.0;
+		    Blt_TextToPostScript(psToken, string, 
+					 &axisPtr->limitsTextStyle, 
+					 vMin, (double)graphPtr->bottom);
+		    vMin += (textWidth + SPACING);
+		}
+	    }
+	}
+    }
+}
+
+Axis *
+Blt_GetFirstAxis(chainPtr)
+    Blt_Chain *chainPtr;
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_ChainFirstLink(chainPtr);
+    if (linkPtr == NULL) {
+	return NULL;
+    }
+    return Blt_ChainGetValue(linkPtr);
+}
+
+Axis *
+Blt_NearestAxis(graphPtr, x, y)
+    Graph *graphPtr;
+    int x, y;                 /* Point to be tested */
+{
+    register Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Axis *axisPtr;
+    int width, height;
+    double rotWidth, rotHeight;
+    Point2D bbox[5];
+    
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	axisPtr = (Axis *)Blt_GetHashValue(hPtr);
+	if ((axisPtr->hidden) || (!(axisPtr->flags & AXIS_ONSCREEN))) {
+	    continue;		/* Don't check hidden axes or axes
+				 * that are virtual. */
+	}
+	if (axisPtr->showTicks) {
+	    register Blt_ChainLink *linkPtr;
+	    TickLabel *labelPtr;
+	    Point2D t;
+
+	    for (linkPtr = Blt_ChainFirstLink(axisPtr->tickLabels); 
+		 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {	
+		labelPtr = Blt_ChainGetValue(linkPtr);
+		Blt_GetBoundingBox(labelPtr->width, labelPtr->height, 
+		    axisPtr->tickTextStyle.theta, &rotWidth, &rotHeight, bbox);
+		width = ROUND(rotWidth);
+		height = ROUND(rotHeight);
+		t = Blt_TranslatePoint(&labelPtr->anchorPos, width, height, 
+			axisPtr->tickTextStyle.anchor);
+		t.x = x - t.x - (width * 0.5);
+		t.y = y - t.y - (height * 0.5);
+
+		bbox[4] = bbox[0];
+		if (Blt_PointInPolygon(&t, bbox, 5)) {
+		    axisPtr->detail = "label";
+		    return axisPtr;
+		}
+	    }
+	}
+	if (axisPtr->title != NULL) { /* and then the title string. */
+	    Point2D t;
+
+	    Blt_GetTextExtents(&axisPtr->titleTextStyle, axisPtr->title,&width,
+		 &height);
+	    Blt_GetBoundingBox(width, height, axisPtr->titleTextStyle.theta, 
+		&rotWidth, &rotHeight, bbox);
+	    width = ROUND(rotWidth);
+	    height = ROUND(rotHeight);
+	    t = Blt_TranslatePoint(&axisPtr->titlePos, width, height, 
+		axisPtr->titleTextStyle.anchor);
+	    /* Translate the point so that the 0,0 is the upper left 
+	     * corner of the bounding box.  */
+	    t.x = x - t.x - (width / 2);
+	    t.y = y - t.y - (height / 2);
+	    
+	    bbox[4] = bbox[0];
+	    if (Blt_PointInPolygon(&t, bbox, 5)) {
+		axisPtr->detail = "title";
+		return axisPtr;
+	    }
+	}
+	if (axisPtr->lineWidth > 0) { /* Check for the axis region */
+	    if (PointInRegion(&axisPtr->region, x, y)) {
+		axisPtr->detail = "line";
+		return axisPtr;
+	    }
+	}
+    }
+    return NULL;
+}
+ 
+ClientData
+Blt_MakeAxisTag(graphPtr, tagName)
+    Graph *graphPtr;
+    char *tagName;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    hPtr = Blt_CreateHashEntry(&graphPtr->axes.tagTable, tagName, &isNew);
+    assert(hPtr);
+    return Blt_GetHashKey(&graphPtr->axes.tagTable, hPtr);
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrAxis.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrAxis.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrAxis.h	(revision 175)
@@ -0,0 +1,306 @@
+/*
+ * bltGrAxis.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_AXIS_H
+#define _BLT_GR_AXIS_H
+
+#include "bltList.h"
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * AxisRange --
+ *
+ *	Designates a range of values by a minimum and maximum limit.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    double min, max, range, scale;
+} AxisRange;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TickLabel --
+ *
+ * 	Structure containing the X-Y screen coordinates of the tick
+ * 	label (anchored at its center).
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    Point2D anchorPos;
+    int width, height;
+    char string[1];
+} TickLabel;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Ticks --
+ *
+ * 	Structure containing information where the ticks (major or
+ *	minor) will be displayed on the graph.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    int nTicks;			/* # of ticks on axis */
+    double values[1];		/* Array of tick values (malloc-ed). */
+} Ticks;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TickSweep --
+ *
+ * 	Structure containing information where the ticks (major or
+ *	minor) will be displayed on the graph.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    double initial;		/* Initial value */
+    double step;		/* Size of interval */
+    int nSteps;			/* Number of intervals. */
+} TickSweep;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Axis --
+ *
+ * 	Structure contains options controlling how the axis will be
+ * 	displayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier to refer the element.
+				 * Used in the "insert", "delete", or
+				 * "show", commands. */
+
+    Blt_Uid classUid;		/* Type of axis. */
+
+    Graph *graphPtr;		/* Graph widget of element*/
+
+    unsigned int flags;		/* Set bit field definitions below */
+
+    /*
+     * AXIS_DRAWN		Axis is designated as a logical axis
+     * AXIS_DIRTY
+     *
+     * AXIS_CONFIG_MAJOR	User specified major ticks.
+     * AXIS_CONFIG_MINOR	User specified minor ticks.
+     */
+
+    char **tags;
+
+    char *detail;
+
+    int deletePending;		/* Indicates that the axis was
+				 * scheduled for deletion. The actual
+				 * deletion may be deferred until the
+				 * axis is no longer in use.  */
+
+    int refCount;		/* Number of elements referencing this
+				 * axis. */
+
+    Blt_HashEntry *hashPtr;	/* Points to axis entry in hash
+				 * table. Used to quickly remove axis
+				 * entries. */
+
+    int logScale;		/* If non-zero, scale the axis values
+				 * logarithmically. */
+
+    int hidden;			/* If non-zero, don't display the
+				 * axis title, ticks, or line. */
+
+    int showTicks;		/* If non-zero, display tick marks and
+				 * labels. */
+
+    int descending;		/* If non-zero, display the range of
+				 * values on the axis in descending
+				 * order, from high to low. */
+
+    int looseMin, looseMax;	/* If non-zero, axis range extends to
+				 * the outer major ticks, otherwise at
+				 * the limits of the data values. This
+				 * is overriddened by setting the -min
+				 * and -max options.  */
+
+    char *title;		/* Title of the axis. */
+
+    TextStyle titleTextStyle;	/* Text attributes (color, font,
+				 * rotation, etc.)  of the axis
+				 * title. */
+
+    int titleAlternate;		/* Indicates whether to position the
+				 * title above/left of the axis. */
+
+    Point2D titlePos;		/* Position of the title */
+
+    unsigned short int titleWidth, titleHeight;	
+
+    int lineWidth;		/* Width of lines representing axis
+				 * (including ticks).  If zero, then
+				 * no axis lines or ticks are
+				 * drawn. */
+
+    char **limitsFormats;	/* One or two strings of sprintf-like
+				 * formats describing how to display
+				 * virtual axis limits. If NULL,
+				 * display no limits. */
+    int nFormats;
+
+    TextStyle limitsTextStyle;	/* Text attributes (color, font,
+				 * rotation, etc.)  of the limits. */
+
+    double windowSize;		/* Size of a sliding window of values
+				 * used to scale the axis automatically
+				 * as new data values are added. The axis
+				 * will always display the latest values
+				 * in this range. */
+
+    double shiftBy;		/* Shift maximum by this interval. */
+
+    int tickLength;		/* Length of major ticks in pixels */
+
+    TextStyle tickTextStyle;	/* Text attributes (color, font, rotation, 
+				 * etc.) for labels at each major tick. */
+
+    char *formatCmd;		/* Specifies a Tcl command, to be invoked
+				 * by the axis whenever it has to generate 
+				 * tick labels. */
+
+    char *scrollCmdPrefix;
+    int scrollUnits;
+
+    double min, max;		/* The actual axis range. */
+
+    double reqMin, reqMax;	/* Requested axis bounds. Consult the
+				 * axisPtr->flags field for
+				 * AXIS_CONFIG_MIN and AXIS_CONFIG_MAX
+				 * to see if the requested bound have
+				 * been set.  They override the
+				 * computed range of the axis
+				 * (determined by auto-scaling). */
+
+    double scrollMin, scrollMax;/* Defines the scrolling reqion of the axis.
+				 * Normally the region is determined from 
+				 * the data limits. If specified, these 
+				 * values override the data-range. */
+
+    AxisRange valueRange;	/* Range of data values of elements mapped 
+				 * to this axis. This is used to auto-scale 
+				 * the axis in "tight" mode. */
+
+    AxisRange axisRange;	/* Smallest and largest major tick values
+				 * for the axis.  The tick values lie outside
+				 * the range of data values.  This is used to
+				 * auto-scale the axis in "loose" mode. */
+
+    double prevMin, prevMax;
+
+    double reqStep;		/* If > 0.0, overrides the computed major 
+				 * tick interval.  Otherwise a stepsize 
+				 * is automatically calculated, based 
+				 * upon the range of elements mapped to the 
+				 * axis. The default value is 0.0. */
+
+    double tickZoom;		/* If > 0.0, overrides the computed major 
+				 * tick interval.  Otherwise a stepsize 
+				 * is automatically calculated, based 
+				 * upon the range of elements mapped to the 
+				 * axis. The default value is 0.0. */
+
+
+    GC tickGC;			/* Graphics context for axis and tick labels */
+
+    Ticks *t1Ptr;		/* Array of major tick positions. May be
+				 * set by the user or generated from the 
+				 * major sweep below. */
+
+    Ticks *t2Ptr;		/* Array of minor tick positions. May be
+				 * set by the user or generated from the
+				 * minor sweep below. */
+
+    TickSweep minorSweep, majorSweep;
+
+    int reqNumMinorTicks;	/* If non-zero, represents the
+				 * requested the number of minor ticks
+				 * to be uniformally displayed along
+				 * each major tick. */
+
+
+    int labelOffset;		/* If non-zero, indicates that the tick
+				 * label should be offset to sit in the
+				 * middle of the next interval. */
+
+    /* The following fields are specific to logical axes */
+
+    Blt_ChainLink *linkPtr;	/* Axis link in margin list. */
+    Blt_Chain *chainPtr;
+
+    short int width, height;	/* Extents of axis */
+
+    Segment2D *segments;	/* Array of line segments representing
+				 * the major and minor ticks, but also
+				 * the axis line itself. The segment
+				 * coordinates are relative to the
+				 * axis. */
+
+    int nSegments;		/* Number of segments in the above array. */
+
+    Blt_Chain *tickLabels;	/* Contains major tick label strings 
+				 * and their offsets along the axis. */
+    Region2D region;
+
+    Tk_3DBorder border;
+    int borderWidth;
+    int relief;
+} Axis;
+
+#define AXIS_CONFIG_MAJOR (1<<4) /* User specified major tick intervals. */
+#define AXIS_CONFIG_MINOR (1<<5) /* User specified minor tick intervals. */
+#define AXIS_ONSCREEN	  (1<<6) /* Axis is displayed on the screen via
+				  * the "use" operation */
+#define AXIS_DIRTY	  (1<<7)
+#define AXIS_ALLOW_NULL   (1<<12)
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Axis2D --
+ *
+ *	The pair of axes mapping a point onto the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    Axis *x, *y;
+} Axis2D;
+
+#endif /* _BLT_GR_AXIS_H */
Index: trunk/kitgen/8.x/blt/generic/bltGrBar.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrBar.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrBar.c	(revision 175)
@@ -0,0 +1,2324 @@
+
+/*
+ * bltGrBar.c --
+ *
+ *	This module implements barchart elements for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+typedef struct {
+    char *name;			/* Pen style identifier.  If NULL pen
+				 * was statically allocated. */
+    Blt_Uid classUid;		/* Type of pen */
+    char *typeId;		/* String token identifying the type of pen */
+    unsigned int flags;		/* Indicates if the pen element is active or
+				 * normal */
+    int refCount;		/* Reference count for elements using
+				 * this pen. */
+    Blt_HashEntry *hashPtr;
+    Tk_ConfigSpec *specsPtr;	/* Configuration specifications */
+
+    PenConfigureProc *configProc;
+    PenDestroyProc *destroyProc;
+
+    XColor *fgColor;		/* Foreground color of bar */
+    Tk_3DBorder border;		/* 3D border and background color */
+    int borderWidth;		/* 3D border width of bar */
+    int relief;			/* Relief of the bar */
+    Pixmap stipple;		/* Stipple */
+    GC gc;			/* Graphics context */
+
+    /* Error bar attributes. */
+    int errorBarShow;		/* Describes which error bars to
+				 * display: none, x, y, or * both. */
+
+    int errorBarLineWidth;	/* Width of the error bar segments. */
+
+    int errorBarCapWidth;
+    XColor *errorBarColor;	/* Color of the error bar. */
+
+    GC errorBarGC;		/* Error bar graphics context. */
+
+    /* Show value attributes. */
+    int valueShow;		/* Indicates whether to display data value.
+				 * Values are x, y, or none. */
+
+    char *valueFormat;		/* A printf format string. */
+    TextStyle valueStyle;	/* Text attributes (color, font,
+				 * rotation, etc.) of the value. */
+    
+} BarPen;
+
+typedef struct {
+    Weight weight;		/* Weight range where this pen is valid. */
+
+    BarPen *penPtr;		/* Pen to draw */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int errorBarCapWidth;	/* Length of the cap ends on each
+				 * error bar. */
+
+    int symbolSize;		/* Size of the pen's symbol scaled to the
+				 * current graph size. */
+
+    /* Bar chart specific data. */
+    XRectangle *rectangles;	/* Indicates starting location in bar
+				 * array for this pen. */
+    int nRects;			/* Number of bar segments for this pen. */
+
+} BarPenStyle;
+
+typedef struct {
+    char *name;			/* Identifier to refer the
+				 * element. Used in the "insert",
+				 * "delete", or "show", commands. */
+
+    Blt_Uid classUid;		/* Type of element; either 
+				 * bltBarElementUid, bltLineElementUid, or
+				 * bltStripElementUid. */
+
+    Graph *graphPtr;		/* Graph widget of element*/
+    unsigned int flags;		/* Indicates if the entire element is
+				 * active, or if coordinates need to
+				 * be calculated */
+    char **tags;
+    int hidden;			/* If non-zero, don't display the element. */
+
+    Blt_HashEntry *hashPtr;
+    char *label;		/* Label displayed in legend */
+    int labelRelief;		/* Relief of label in legend. */
+
+    Axis2D axes;
+    ElemVector x, y, w;		/* Contains array of numeric values */
+
+    ElemVector xError;		/* Relative/symmetric X error values. */
+    ElemVector yError;		/* Relative/symmetric Y error values. */
+    ElemVector xHigh, xLow;	/* Absolute/asymmetric X-coordinate high/low
+				   error values. */
+    ElemVector yHigh, yLow;	/* Absolute/asymmetric Y-coordinate high/low
+				   error values. */
+
+    int *activeIndices;		/* Array of indices (malloc-ed) that
+				 * indicate the data points have been
+				 * selected as active (drawn with
+				 * "active" colors). */
+
+    int nActiveIndices;		/* Number of active data points. Special
+				 * case: if nActiveIndices < 0 and the
+				 * active bit is set in "flags", then all
+				 * data points are drawn active. */
+
+    ElementProcs *procsPtr;	/* Class information for bar elements */
+    Tk_ConfigSpec *specsPtr;	/* Configuration specifications */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int *xErrorToData;		/* Maps individual error bar segments back
+				 * to the data point associated with it. */
+    int *yErrorToData;		/* Maps individual error bar segments back
+				 * to the data point associated with it. */
+
+    int errorBarCapWidth;	/* Length of cap on error bars */
+
+    BarPen *activePenPtr;	/* Standard Pens */
+    BarPen *normalPenPtr;
+
+    Blt_Chain *palette;		/* Chain of pen style information. */
+
+    /* Symbol scaling */
+    int scaleSymbols;		/* If non-zero, the symbols will scale
+				 * in size as the graph is zoomed
+				 * in/out.  */
+
+    double xRange, yRange;	/* Initial X-axis and Y-axis ranges:
+				 * used to scale the size of element's
+				 * symbol. */
+    int state;
+    /*
+     * Bar specific attributes
+     */
+    BarPen builtinPen;
+
+    int *rectToData;
+    XRectangle *rectangles;	/* Array of rectangles comprising the bar
+				 * segments of the element. */
+    int nRects;			/* # of visible bar segments for element */
+
+    int padX;			/* Spacing on either side of bar */
+    double barWidth;
+    int nActive;
+
+    XRectangle *activeRects;
+    int *activeToData;
+} Bar;
+
+extern Tk_CustomOption bltBarPenOption;
+extern Tk_CustomOption bltDataOption;
+extern Tk_CustomOption bltDataPairsOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltColorOption;
+extern Tk_CustomOption bltStateOption;
+
+extern Tk_OptionParseProc Blt_StringToStyles;
+extern Tk_OptionPrintProc Blt_StylesToString;
+static Tk_OptionParseProc StringToBarMode;
+static Tk_OptionPrintProc BarModeToString;
+
+static Tk_CustomOption stylesOption =
+{
+    Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(BarPenStyle)
+};
+
+Tk_CustomOption bltBarModeOption =
+{
+    StringToBarMode, BarModeToString, (ClientData)0
+};
+
+#define DEF_BAR_ACTIVE_PEN		"activeBar"
+#define DEF_BAR_AXIS_X			"x"
+#define DEF_BAR_AXIS_Y			"y"
+#define DEF_BAR_BACKGROUND		"navyblue"
+#define DEF_BAR_BG_MONO			BLACK
+#define DEF_BAR_BORDERWIDTH		"2"
+#define DEF_BAR_DATA			(char *)NULL
+#define DEF_BAR_ERRORBAR_COLOR		"defcolor"
+#define DEF_BAR_ERRORBAR_LINE_WIDTH	"1"
+#define DEF_BAR_ERRORBAR_CAP_WIDTH	"1"
+#define DEF_BAR_FOREGROUND		"blue"
+#define DEF_BAR_FG_MONO			WHITE
+#define DEF_BAR_HIDE			"no"
+#define DEF_BAR_LABEL			(char *)NULL
+#define DEF_BAR_LABEL_RELIEF		"flat"
+#define DEF_BAR_NORMAL_STIPPLE		""
+#define DEF_BAR_RELIEF			"raised"
+#define DEF_BAR_SHOW_ERRORBARS		"both"
+#define DEF_BAR_STATE			"normal"
+#define DEF_BAR_STYLES			""
+#define DEF_BAR_TAGS			"all"
+#define DEF_BAR_WIDTH			"0.0"
+#define DEF_BAR_DATA			(char *)NULL
+
+#define DEF_PEN_ACTIVE_BACKGROUND		"red"
+#define DEF_PEN_ACTIVE_BG_MONO		WHITE
+#define DEF_PEN_ACTIVE_FOREGROUND     	"pink"
+#define DEF_PEN_ACTIVE_FG_MONO		BLACK
+#define DEF_PEN_BORDERWIDTH		"2"
+#define DEF_PEN_NORMAL_BACKGROUND	"navyblue"
+#define DEF_PEN_NORMAL_BG_MONO		BLACK
+#define DEF_PEN_NORMAL_FOREGROUND	"blue"
+#define DEF_PEN_NORMAL_FG_MONO		WHITE
+#define DEF_PEN_RELIEF			"raised"
+#define DEF_PEN_STIPPLE			""
+#define DEF_PEN_TYPE			"bar"
+#define	DEF_PEN_VALUE_ANCHOR		"s"
+#define	DEF_PEN_VALUE_COLOR		RGB_BLACK
+#define	DEF_PEN_VALUE_FONT		STD_FONT_SMALL
+#define	DEF_PEN_VALUE_FORMAT		"%g"
+#define	DEF_PEN_VALUE_ROTATE		(char *)NULL
+#define	DEF_PEN_VALUE_SHADOW		(char *)NULL
+#define DEF_PEN_SHOW_VALUES		"no"
+
+static Tk_ConfigSpec barPenConfigSpecs[] =
+{
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_PEN_ACTIVE_BACKGROUND, Tk_Offset(BarPen, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_PEN_ACTIVE_BACKGROUND, Tk_Offset(BarPen, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_PEN_NORMAL_BACKGROUND, Tk_Offset(BarPen, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_PEN_NORMAL_BACKGROUND, Tk_Offset(BarPen, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | NORMAL_PEN},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+	(char *)NULL, 0, ALL_PENS},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+	(char *)NULL, 0, ALL_PENS},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_PEN_BORDERWIDTH, Tk_Offset(BarPen, borderWidth), ALL_PENS,
+	&bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+	DEF_BAR_ERRORBAR_COLOR, Tk_Offset(BarPen, errorBarColor), 
+	ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+	DEF_BAR_ERRORBAR_LINE_WIDTH, Tk_Offset(BarPen, errorBarLineWidth),
+        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", 
+	DEF_BAR_ERRORBAR_CAP_WIDTH, Tk_Offset(BarPen, errorBarCapWidth),
+        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+	(char *)NULL, 0, ALL_PENS},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_PEN_ACTIVE_FOREGROUND, Tk_Offset(BarPen, fgColor),
+	ACTIVE_PEN | TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_PEN_ACTIVE_FOREGROUND, Tk_Offset(BarPen, fgColor),
+	ACTIVE_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_PEN_NORMAL_FOREGROUND, Tk_Offset(BarPen, fgColor),
+	NORMAL_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_PEN_NORMAL_FOREGROUND, Tk_Offset(BarPen, fgColor),
+	NORMAL_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_PEN_RELIEF, Tk_Offset(BarPen, relief), ALL_PENS},
+    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+	DEF_BAR_SHOW_ERRORBARS, Tk_Offset(BarPen, errorBarShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+	DEF_PEN_SHOW_VALUES, Tk_Offset(BarPen, valueShow),
+        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+	DEF_PEN_STIPPLE, Tk_Offset(BarPen, stipple),
+	ALL_PENS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
+	DEF_PEN_TYPE, Tk_Offset(BarPen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+	DEF_PEN_VALUE_ANCHOR, Tk_Offset(BarPen, valueStyle.anchor), ALL_PENS},
+    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+	DEF_PEN_VALUE_COLOR, Tk_Offset(BarPen, valueStyle.color), ALL_PENS},
+    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+	DEF_PEN_VALUE_FONT, Tk_Offset(BarPen, valueStyle.font), ALL_PENS},
+    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+	DEF_PEN_VALUE_FORMAT, Tk_Offset(BarPen, valueFormat),
+	ALL_PENS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+	DEF_PEN_VALUE_ROTATE, Tk_Offset(BarPen, valueStyle.theta), ALL_PENS},
+    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+	DEF_PEN_VALUE_SHADOW, Tk_Offset(BarPen, valueStyle.shadow),
+	ALL_PENS, &bltShadowOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec barElemConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+	DEF_BAR_ACTIVE_PEN, Tk_Offset(Bar, activePenPtr),
+	TK_CONFIG_NULL_OK, &bltBarPenOption},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_BAR_BACKGROUND, Tk_Offset(Bar, builtinPen.border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_BAR_BACKGROUND, Tk_Offset(Bar, builtinPen.border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
+	DEF_BAR_WIDTH, Tk_Offset(Bar, barWidth),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_BAR_TAGS, Tk_Offset(Bar, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_BAR_BORDERWIDTH, Tk_Offset(Bar, builtinPen.borderWidth),
+	0, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+	DEF_BAR_ERRORBAR_COLOR, Tk_Offset(Bar, builtinPen.errorBarColor), 
+	0, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+	DEF_BAR_ERRORBAR_LINE_WIDTH, 
+	Tk_Offset(Bar, builtinPen.errorBarLineWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", 
+	DEF_BAR_ERRORBAR_CAP_WIDTH, Tk_Offset(Bar, builtinPen.errorBarCapWidth),
+        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+	(char *)NULL, 0, 0, &bltDataPairsOption},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_BAR_FOREGROUND, Tk_Offset(Bar, builtinPen.fgColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_BAR_FOREGROUND, Tk_Offset(Bar, builtinPen.fgColor),
+        TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_STRING, "-label", "label", "Label",
+	DEF_BAR_LABEL, Tk_Offset(Bar, label), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+	DEF_BAR_LABEL_RELIEF, Tk_Offset(Bar, labelRelief),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_BAR_HIDE, Tk_Offset(Bar, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_BAR_AXIS_X, Tk_Offset(Bar, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_BAR_AXIS_Y, Tk_Offset(Bar, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+	(char *)NULL, Tk_Offset(Bar, normalPenPtr),
+	TK_CONFIG_NULL_OK, &bltBarPenOption},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_BAR_RELIEF, Tk_Offset(Bar, builtinPen.relief), 0},
+    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+	DEF_BAR_SHOW_ERRORBARS, Tk_Offset(Bar, builtinPen.errorBarShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+	DEF_PEN_SHOW_VALUES, Tk_Offset(Bar, builtinPen.valueShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_BAR_STATE, Tk_Offset(Bar, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+	DEF_BAR_NORMAL_STIPPLE, Tk_Offset(Bar, builtinPen.stipple),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+	DEF_BAR_STYLES, Tk_Offset(Bar, palette), 
+        TK_CONFIG_NULL_OK, &stylesOption},
+    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+	DEF_PEN_VALUE_ANCHOR, Tk_Offset(Bar, builtinPen.valueStyle.anchor), 0},
+    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+	DEF_PEN_VALUE_COLOR, Tk_Offset(Bar, builtinPen.valueStyle.color), 0},
+    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+	DEF_PEN_VALUE_FONT, Tk_Offset(Bar, builtinPen.valueStyle.font), 0},
+    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+	DEF_PEN_VALUE_FORMAT, Tk_Offset(Bar, builtinPen.valueFormat),
+        TK_CONFIG_NULL_OK},
+    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+	DEF_PEN_VALUE_ROTATE, Tk_Offset(Bar, builtinPen.valueStyle.theta), 0},
+    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+	DEF_PEN_VALUE_SHADOW, Tk_Offset(Bar, builtinPen.valueStyle.shadow),
+	0, &bltShadowOption},
+
+    {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+	(char *)NULL, Tk_Offset(Bar, w), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-x", "xdata", "Xdata",
+	DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-y", "ydata", "Ydata",
+	DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xdata", "xdata", "Xdata",
+	DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ydata", "ydata", "Ydata",
+	DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError",
+	DEF_BAR_DATA, Tk_Offset(Bar, xError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh",
+	DEF_BAR_DATA, Tk_Offset(Bar, xHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow",
+	DEF_BAR_DATA, Tk_Offset(Bar, xLow), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError",
+	DEF_BAR_DATA, Tk_Offset(Bar, yError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh",
+	DEF_BAR_DATA, Tk_Offset(Bar, yHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow",
+	DEF_BAR_DATA, Tk_Offset(Bar, yLow), 0, &bltDataOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+static PenConfigureProc ConfigurePen;
+static PenDestroyProc DestroyPen;
+static ElementClosestProc ClosestBar;
+static ElementConfigProc ConfigureBar;
+static ElementDestroyProc DestroyBar;
+static ElementDrawProc DrawActiveBar;
+static ElementDrawProc DrawNormalBar;
+static ElementDrawSymbolProc DrawSymbol;
+static ElementExtentsProc GetBarExtents;
+static ElementToPostScriptProc ActiveBarToPostScript;
+static ElementToPostScriptProc NormalBarToPostScript;
+static ElementSymbolToPostScriptProc SymbolToPostScript;
+static ElementMapProc MapBar;
+
+INLINE static int
+Round(x)
+    register double x;
+{
+    return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NameOfBarMode --
+ *
+ *	Converts the integer representing the mode style into a string.
+ *
+ * ----------------------------------------------------------------------
+ */
+static char *
+NameOfBarMode(mode)
+    BarMode mode;
+{
+    switch (mode) {
+    case MODE_INFRONT:
+	return "infront";
+    case MODE_OVERLAP:
+	return "overlap";
+    case MODE_STACKED:
+	return "stacked";
+    case MODE_ALIGNED:
+	return "aligned";
+    default:
+	return "unknown mode value";
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToMode --
+ *
+ *	Converts the mode string into its numeric representation.
+ *
+ *	Valid mode strings are:
+ *
+ *      "infront"   Draw a full bar at each point in the element.
+ *
+ * 	"stacked"   Stack bar segments vertically. Each stack is defined
+ *		    by each ordinate at a particular abscissa. The height
+ *		    of each segment is represented by the sum the previous
+ *		    ordinates.
+ *
+ *	"aligned"   Align bar segments as smaller slices one next to
+ *		    the other.  Like "stacks", aligned segments are
+ *		    defined by each ordinate at a particular abscissa.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToBarMode(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Mode style string */
+    char *widgRec;		/* Cubicle structure record */
+    int offset;			/* Offset of style in record */
+{
+    BarMode *modePtr = (BarMode *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'n') && (strncmp(string, "normal", length) == 0)) {
+	*modePtr = MODE_INFRONT;
+    } else if ((c == 'i') && (strncmp(string, "infront", length) == 0)) {
+	*modePtr = MODE_INFRONT;
+    } else if ((c == 's') && (strncmp(string, "stacked", length) == 0)) {
+	*modePtr = MODE_STACKED;
+    } else if ((c == 'a') && (strncmp(string, "aligned", length) == 0)) {
+	*modePtr = MODE_ALIGNED;
+    } else if ((c == 'o') && (strncmp(string, "overlap", length) == 0)) {
+	*modePtr = MODE_OVERLAP;
+    } else {
+	Tcl_AppendResult(interp, "bad mode argument \"", string,
+	    "\": should be \"infront\", \"stacked\", \"overlap\", or \"aligned\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BarModeToString --
+ *
+ *	Returns the mode style string based upon the mode flags.
+ *
+ * Results:
+ *	The mode style string is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+BarModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Row/column structure record */
+    int offset;			/* Offset of mode in Partition record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    BarMode mode = *(BarMode *)(widgRec + offset);
+
+    return NameOfBarMode(mode);
+}
+
+
+/* 
+ * Zero out the style's number of rectangles and errorbars. 
+ */
+static void
+ClearPalette(palette)
+    Blt_Chain *palette;
+{
+    register BarPenStyle *stylePtr;
+    Blt_ChainLink *linkPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = stylePtr->nRects = 0;
+    }
+}
+
+static int
+ConfigurePen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    BarPen *bpPtr = (BarPen *)penPtr;
+    XGCValues gcValues;
+    unsigned long gcMask;
+    int fillStyle;
+    GC newGC;
+    long defColor;
+
+    Blt_ResetTextStyle(graphPtr->tkwin, &(bpPtr->valueStyle));
+    gcMask = GCForeground;
+    if (bpPtr->fgColor != NULL) {
+	defColor = bpPtr->fgColor->pixel;
+	gcValues.foreground = bpPtr->fgColor->pixel;
+    } else if (bpPtr->border != NULL) {
+	defColor = Tk_3DBorderColor(bpPtr->border)->pixel;
+	gcValues.foreground = Tk_3DBorderColor(bpPtr->border)->pixel;
+    } else {
+	defColor = BlackPixel(graphPtr->display, 
+			      Tk_ScreenNumber(graphPtr->tkwin));
+    }
+    if ((bpPtr->fgColor != NULL) && (bpPtr->border != NULL)) {
+	gcMask |= GCBackground;
+	gcValues.background = Tk_3DBorderColor(bpPtr->border)->pixel;
+	fillStyle = FillOpaqueStippled;
+    } else {
+	fillStyle = FillStippled;
+    }
+    if (bpPtr->stipple != None) {
+	gcValues.stipple = bpPtr->stipple;
+	gcValues.fill_style = fillStyle;
+	gcMask |= (GCStipple | GCFillStyle);
+    }
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (bpPtr->gc != NULL) {
+	Tk_FreeGC(graphPtr->display, bpPtr->gc);
+    }
+    bpPtr->gc = newGC;
+
+    gcMask = GCForeground | GCLineWidth;
+    if (bpPtr->errorBarColor == COLOR_DEFAULT) {
+	gcValues.foreground = defColor;
+    } else {
+	gcValues.foreground = bpPtr->errorBarColor->pixel;
+    }
+    gcValues.line_width = LineWidth(bpPtr->errorBarLineWidth);
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (bpPtr->errorBarGC != NULL) {
+	Tk_FreeGC(graphPtr->display, bpPtr->errorBarGC);
+    }
+    bpPtr->errorBarGC = newGC;
+
+    return TCL_OK;
+}
+
+static void
+DestroyPen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    BarPen *bpPtr = (BarPen *)penPtr;
+
+    Blt_FreeTextStyle(graphPtr->display, &(bpPtr->valueStyle));
+    if (bpPtr->gc != NULL) {
+	Tk_FreeGC(graphPtr->display, bpPtr->gc);
+    }
+    if (bpPtr->errorBarGC != NULL) {
+	Tk_FreeGC(graphPtr->display, bpPtr->errorBarGC);
+    }
+}
+
+static void
+InitPen(penPtr)
+    BarPen *penPtr;
+{
+    Blt_InitTextStyle(&(penPtr->valueStyle));
+    penPtr->specsPtr = barPenConfigSpecs;
+    penPtr->configProc = ConfigurePen;
+    penPtr->destroyProc = DestroyPen;
+    penPtr->relief = TK_RELIEF_RAISED;
+    penPtr->flags = NORMAL_PEN;
+    penPtr->errorBarShow = SHOW_BOTH;
+    penPtr->valueShow = SHOW_NONE;
+    penPtr->borderWidth = 2;
+}
+
+Pen *
+Blt_BarPen(penName)
+    char *penName;
+{
+    BarPen *penPtr;
+
+    penPtr = Blt_Calloc(1, sizeof(BarPen));
+    assert(penPtr);
+    InitPen(penPtr);
+    penPtr->name = Blt_Strdup(penName);
+    if (strcmp(penName, "activeBar") == 0) {
+	penPtr->flags = ACTIVE_PEN;
+    }
+    return (Pen *) penPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CheckStacks --
+ *
+ *	Check that the data limits are not superseded by the heights
+ *	of stacked bar segments.  The heights are calculated by
+ *	Blt_ComputeStacks.
+ *
+ * Results:
+ *	If the y-axis limits need to be adjusted for stacked segments,
+ *	*minPtr* or *maxPtr* are updated.
+ *
+ * Side effects:
+ *	Autoscaling of the y-axis is affected.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+CheckStacks(graphPtr, pairPtr, minPtr, maxPtr)
+    Graph *graphPtr;
+    Axis2D *pairPtr;
+    double *minPtr, *maxPtr;	/* Current minimum maximum for y-axis */
+{
+    FreqInfo *infoPtr;
+    register int i;
+
+    if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
+	return;
+    }
+    infoPtr = graphPtr->freqArr;
+    for (i = 0; i < graphPtr->nStacks; i++) {
+	if ((infoPtr->axes.x == pairPtr->x) && 
+	    (infoPtr->axes.y == pairPtr->y)) {
+	    /*
+
+	     * Check if any of the y-values (because of stacking) are
+	     * greater than the current limits of the graph.
+	     */
+	    if (infoPtr->sum < 0.0) {
+		if (*minPtr > infoPtr->sum) {
+		    *minPtr = infoPtr->sum;
+		}
+	    } else {
+		if (*maxPtr < infoPtr->sum) {
+		    *maxPtr = infoPtr->sum;
+		}
+	    }
+	}
+	infoPtr++;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureBar --
+ *
+ *	Sets up the appropriate configuration parameters in the GC.
+ *      It is assumed the parameters have been previously set by
+ *	a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information such as bar foreground/background
+ *	color and stipple etc. get set in a new GC.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureBar(graphPtr, elemPtr)
+    Graph *graphPtr;
+    register Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (ConfigurePen(graphPtr, (Pen *)&(barPtr->builtinPen)) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /*
+     * Point to the static normal pen if no external pens have
+     * been selected.
+     */
+    if (barPtr->normalPenPtr == NULL) {
+	barPtr->normalPenPtr = &(barPtr->builtinPen);
+    }
+    linkPtr = Blt_ChainFirstLink(barPtr->palette);
+    if (linkPtr != NULL) {
+	BarPenStyle *stylePtr;
+
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->penPtr = barPtr->normalPenPtr;
+    }
+    if (Blt_ConfigModified(barPtr->specsPtr, "-barwidth", "-*data",
+	    "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
+	barPtr->flags |= MAP_ITEM;
+    }
+    return TCL_OK;
+}
+
+static void
+GetBarExtents(elemPtr, extsPtr)
+    Element *elemPtr;
+    Extents2D *extsPtr;
+{
+    Graph *graphPtr = elemPtr->graphPtr;
+    Bar *barPtr = (Bar *)elemPtr;
+    double middle, barWidth;
+    int nPoints;
+
+    extsPtr->top = extsPtr->left = DBL_MAX;
+    extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+    nPoints = NumberOfPoints(barPtr);
+    if (nPoints < 1) {
+	return;			/* No data points */
+    }
+    barWidth = graphPtr->barWidth;
+    if (barPtr->barWidth > 0.0) {
+	barWidth = barPtr->barWidth;
+    }
+    middle = barWidth * 0.5;
+    extsPtr->left = barPtr->x.min - middle;
+    extsPtr->right = barPtr->x.max + middle;
+
+    extsPtr->top = barPtr->y.min;
+    extsPtr->bottom = barPtr->y.max;
+    if (extsPtr->bottom < graphPtr->baseline) {
+	extsPtr->bottom = graphPtr->baseline;
+    }
+    /*
+     * Handle "stacked" bar elements specially.
+     *
+     * If element is stacked, the sum of its ordinates may be outside
+     * the minimum/maximum limits of the element's data points.
+     */
+    if ((graphPtr->mode == MODE_STACKED) && (graphPtr->nStacks > 0)) {
+	CheckStacks(graphPtr, &(elemPtr->axes), &(extsPtr->top), 
+		&(extsPtr->bottom));
+    }
+    /* Warning: You get what you deserve if the x-axis is logScale */
+    if (elemPtr->axes.x->logScale) {
+	extsPtr->left = Blt_FindElemVectorMinimum(&(barPtr->x), DBL_MIN) + 
+	    middle;
+    }
+    /* Fix y-min limits for barchart */
+    if (elemPtr->axes.y->logScale) {
+ 	if ((extsPtr->top <= 0.0) || (extsPtr->top > 1.0)) {
+	    extsPtr->top = 1.0;
+	}
+    } else {
+	if (extsPtr->top > 0.0) {
+	    extsPtr->top = 0.0;
+	}
+    }
+    /* Correct the extents for error bars if they exist. */
+    if (elemPtr->xError.nValues > 0) {
+	register int i;
+	double x;
+	
+	/* Correct the data limits for error bars */
+	nPoints = MIN(elemPtr->xError.nValues, nPoints);
+	for (i = 0; i < nPoints; i++) {
+	    x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
+	    if (x > extsPtr->right) {
+		extsPtr->right = x;
+	    }
+	    x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
+	    if (elemPtr->axes.x->logScale) {
+		if (x < 0.0) {
+		    x = -x;	/* Mirror negative values, instead
+				 * of ignoring them. */
+		}
+		if ((x > DBL_MIN) && (x < extsPtr->left)) {
+		    extsPtr->left = x;
+		}
+	    } else if (x < extsPtr->left) {
+		extsPtr->left = x;
+	    }
+	}		     
+    } else {
+	if ((elemPtr->xHigh.nValues > 0) && 
+	    (elemPtr->xHigh.max > extsPtr->right)) {
+	    extsPtr->right = elemPtr->xHigh.max;
+	}
+	if (elemPtr->xLow.nValues > 0) {
+	    double left;
+	    
+	    if ((elemPtr->xLow.min <= 0.0) && 
+		(elemPtr->axes.x->logScale)) {
+		left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
+	    } else {
+		left = elemPtr->xLow.min;
+	    }
+	    if (left < extsPtr->left) {
+		extsPtr->left = left;
+	    }
+	}
+    }
+    if (elemPtr->yError.nValues > 0) {
+	register int i;
+	double y;
+	
+	nPoints = MIN(elemPtr->yError.nValues, nPoints);
+	for (i = 0; i < nPoints; i++) {
+	    y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
+	    if (y > extsPtr->bottom) {
+		extsPtr->bottom = y;
+	    }
+	    y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
+	    if (elemPtr->axes.y->logScale) {
+		if (y < 0.0) {
+		    y = -y;	/* Mirror negative values, instead
+				 * of ignoring them. */
+		}
+		if ((y > DBL_MIN) && (y < extsPtr->left)) {
+		    extsPtr->top = y;
+		}
+	    } else if (y < extsPtr->top) {
+		extsPtr->top = y;
+	    }
+	}		     
+    } else {
+	if ((elemPtr->yHigh.nValues > 0) && 
+	    (elemPtr->yHigh.max > extsPtr->bottom)) {
+	    extsPtr->bottom = elemPtr->yHigh.max;
+	}
+	if (elemPtr->yLow.nValues > 0) {
+	    double top;
+	    
+	    if ((elemPtr->yLow.min <= 0.0) && 
+		(elemPtr->axes.y->logScale)) {
+		top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
+	    } else {
+		top = elemPtr->yLow.min;
+	    }
+	    if (top < extsPtr->top) {
+		extsPtr->top = top;
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ClosestBar --
+ *
+ *	Find the bar segment closest to the window coordinates	point
+ *	specified.
+ *
+ *	Note:  This does not return the height of the stacked segment
+ *	       (in graph coordinates) properly.
+ *
+ * Results:
+ *	Returns 1 if the point is width any bar segment, otherwise 0.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ClosestBar(graphPtr, elemPtr, searchPtr)
+    Graph *graphPtr;		/* Graph widget record */
+    Element *elemPtr;		/* Bar element */
+    ClosestSearch *searchPtr;	/* Info of closest point in element */
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    Point2D *pointPtr, *endPtr;
+    Point2D t, outline[5];
+    XRectangle *rectPtr;
+    double left, right, top, bottom;
+    double minDist, dist;
+    int imin;
+    register int i;
+
+    minDist = searchPtr->dist;
+    imin = 0;
+    
+    rectPtr = barPtr->rectangles;
+    for (i = 0; i < barPtr->nRects; i++) {
+	if (PointInRectangle(rectPtr, searchPtr->x, searchPtr->y)) {
+	    imin = barPtr->rectToData[i];
+	    minDist = 0.0;
+	    break;
+	}
+	left = rectPtr->x, top = rectPtr->y;
+	right = (double)(rectPtr->x + rectPtr->width);
+	bottom = (double)(rectPtr->y + rectPtr->height);
+	outline[4].x = outline[3].x = outline[0].x = left;
+	outline[4].y = outline[1].y = outline[0].y = top;
+	outline[2].x = outline[1].x = right;
+	outline[3].y = outline[2].y = bottom;
+
+	for (pointPtr = outline, endPtr = outline + 4; pointPtr < endPtr; 
+	     pointPtr++) {
+	    t = Blt_GetProjection(searchPtr->x, searchPtr->y,
+				  pointPtr, pointPtr + 1);
+	    if (t.x > right) {
+		t.x = right;
+	    } else if (t.x < left) {
+		t.x = left;
+	    }
+	    if (t.y > bottom) {
+		t.y = bottom;
+	    } else if (t.y < top) {
+		t.y = top;
+	    }
+	    dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y));
+	    if (dist < minDist) {
+		minDist = dist;
+		imin = barPtr->rectToData[i];
+	    }
+	}
+	rectPtr++;
+    }
+    if (minDist < searchPtr->dist) {
+	searchPtr->elemPtr = (Element *)elemPtr;
+	searchPtr->dist = minDist;
+	searchPtr->index = imin;
+	searchPtr->point.x = (double)barPtr->x.valueArr[imin];
+	searchPtr->point.y = (double)barPtr->y.valueArr[imin];
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ *	Reorders the both arrays of points and errorbars to merge pens.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The old arrays are freed and new ones allocated containing
+ *	the reordered points and errorbars.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MergePens(barPtr, dataToStyle)
+    Bar *barPtr;
+    PenStyle **dataToStyle;
+{
+    BarPenStyle *stylePtr;
+    Blt_ChainLink *linkPtr;
+
+    if (Blt_ChainGetLength(barPtr->palette) < 2) {
+	linkPtr = Blt_ChainFirstLink(barPtr->palette);
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->nRects = barPtr->nRects;
+	stylePtr->rectangles = barPtr->rectangles;
+	stylePtr->symbolSize = barPtr->rectangles->width / 2;
+	stylePtr->xErrorBarCnt = barPtr->xErrorBarCnt;
+	stylePtr->xErrorBars = barPtr->xErrorBars;
+	stylePtr->yErrorBarCnt = barPtr->yErrorBarCnt;
+	stylePtr->yErrorBars = barPtr->yErrorBars;
+	return;
+    }
+    /* We have more than one style. Group bar segments of like pen
+     * styles together.  */
+
+    if (barPtr->nRects > 0) {
+	XRectangle *rectangles;
+	int *rectToData;
+	int dataIndex;
+	register XRectangle *rectPtr;
+	register int *indexPtr;
+	register int i;
+
+	rectangles = Blt_Malloc(barPtr->nRects * sizeof(XRectangle));
+	rectToData = Blt_Malloc(barPtr->nRects * sizeof(int));
+	assert(rectangles && rectToData);
+
+	rectPtr = rectangles, indexPtr = rectToData;
+	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->symbolSize = rectPtr->width / 2;
+	    stylePtr->rectangles = rectPtr;
+	    for (i = 0; i < barPtr->nRects; i++) {
+		dataIndex = barPtr->rectToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *rectPtr++ = barPtr->rectangles[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->nRects = rectPtr - stylePtr->rectangles;
+	}
+	Blt_Free(barPtr->rectangles);
+	barPtr->rectangles = rectangles;
+	Blt_Free(barPtr->rectToData);
+	barPtr->rectToData = rectToData;
+    }
+    if (barPtr->xErrorBarCnt > 0) {
+	Segment2D *errorBars, *segPtr;
+	int *errorToData, *indexPtr;
+	int dataIndex;
+	register int i;
+
+	errorBars = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(Segment2D));
+	errorToData = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(int));
+	assert(errorBars);
+	segPtr = errorBars, indexPtr = errorToData;
+	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->xErrorBars = segPtr;
+	    for (i = 0; i < barPtr->xErrorBarCnt; i++) {
+		dataIndex = barPtr->xErrorToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *segPtr++ = barPtr->xErrorBars[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
+	}
+	Blt_Free(barPtr->xErrorBars);
+	barPtr->xErrorBars = errorBars;
+	Blt_Free(barPtr->xErrorToData);
+	barPtr->xErrorToData = errorToData;
+    }
+    if (barPtr->yErrorBarCnt > 0) {
+	Segment2D *errorBars, *segPtr;
+	int *errorToData, *indexPtr;
+	int dataIndex;
+	register int i;
+
+	errorBars = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(Segment2D));
+	errorToData = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(int));
+	assert(errorBars);
+	segPtr = errorBars, indexPtr = errorToData;
+	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->yErrorBars = segPtr;
+	    for (i = 0; i < barPtr->yErrorBarCnt; i++) {
+		dataIndex = barPtr->yErrorToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *segPtr++ = barPtr->yErrorBars[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
+	}
+	Blt_Free(barPtr->yErrorBars);
+	barPtr->yErrorBars = errorBars;
+	Blt_Free(barPtr->yErrorToData);
+	barPtr->yErrorToData = errorToData;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapActiveBars --
+ *
+ *	Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is freed and allocated for the active point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapActiveBars(barPtr)
+    Bar *barPtr;
+{
+    if (barPtr->activeRects != NULL) {
+	Blt_Free(barPtr->activeRects);
+	barPtr->activeRects = NULL;
+    }
+    if (barPtr->activeToData != NULL) {
+	Blt_Free(barPtr->activeToData);
+	barPtr->activeToData = NULL;
+    }
+    barPtr->nActive = 0;
+
+    if (barPtr->nActiveIndices > 0) {
+	XRectangle *activeRects;
+	int *activeToData;
+	register int i, n;
+	register int count;
+
+	activeRects = Blt_Malloc(sizeof(XRectangle) * barPtr->nActiveIndices);
+	assert(activeRects);
+	activeToData = Blt_Malloc(sizeof(int) * barPtr->nActiveIndices);
+	assert(activeToData);
+	count = 0;
+	for (i = 0; i < barPtr->nRects; i++) {
+	    for (n = 0; n < barPtr->nActiveIndices; n++) {
+		if (barPtr->rectToData[i] == barPtr->activeIndices[n]) {
+		    activeRects[count] = barPtr->rectangles[i];
+		    activeToData[count] = i;
+		    count++;
+		}
+	    }
+	}
+	barPtr->nActive = count;
+	barPtr->activeRects = activeRects;
+	barPtr->activeToData = activeToData;
+    }
+    barPtr->flags &= ~ACTIVE_PENDING;
+}
+
+static void
+ResetBar(barPtr)
+    Bar *barPtr;
+{
+    /* Release any storage associated with the display of the bar */
+    ClearPalette(barPtr->palette);
+    if (barPtr->activeRects != NULL) {
+	Blt_Free(barPtr->activeRects);
+    }
+    if (barPtr->activeToData != NULL) {
+	Blt_Free(barPtr->activeToData);
+    }
+    if (barPtr->xErrorBars != NULL) {
+	Blt_Free(barPtr->xErrorBars);
+    }
+    if (barPtr->xErrorToData != NULL) {
+	Blt_Free(barPtr->xErrorToData);
+    }
+    if (barPtr->yErrorBars != NULL) {
+	Blt_Free(barPtr->yErrorBars);
+    }
+    if (barPtr->yErrorToData != NULL) {
+	Blt_Free(barPtr->yErrorToData);
+    }
+    if (barPtr->rectangles != NULL) {
+	Blt_Free(barPtr->rectangles);
+    }
+    if (barPtr->rectToData != NULL) {
+	Blt_Free(barPtr->rectToData);
+    }
+    barPtr->activeToData = barPtr->xErrorToData = barPtr->yErrorToData = 
+	barPtr->rectToData = NULL;
+    barPtr->activeRects = barPtr->rectangles = NULL;
+    barPtr->xErrorBars = barPtr->yErrorBars = NULL;
+    barPtr->nActive = barPtr->xErrorBarCnt = barPtr->yErrorBarCnt = 
+	barPtr->nRects = 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapBar --
+ *
+ *	Calculates the actual window coordinates of the bar element.
+ *	The window coordinates are saved in the bar element structure.
+ *
+ * Results:
+ *	None.
+ *
+ * Notes:
+ *	A bar can have multiple segments (more than one x,y pairs).
+ *	In this case, the bar can be represented as either a set of
+ *	non-contiguous bars or a single multi-segmented (stacked) bar.
+ *
+ *	The x-axis layout for a barchart may be presented in one of
+ *	two ways.  If abscissas are used, the bars are placed at those
+ *	coordinates.  Otherwise, the range will represent the number
+ *	of values.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapBar(graphPtr, elemPtr)
+    Graph *graphPtr;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    FreqKey key;
+    PenStyle **dataToStyle;
+    Point2D c1, c2;		/* Two opposite corners of the rectangle
+				 * in graph coordinates. */
+    double *x, *y;
+    double barWidth, barOffset;
+    double baseline;
+    double dx, dy;
+    int *rectToData;		/* Maps rectangles to data point indices */
+    int height;
+    int invertBar;
+    int nPoints, count;
+    register XRectangle *rectPtr, *rectangles;
+    register int i;
+    int size;
+    Blt_ChainLink *linkPtr;
+    BarPenStyle *stylePtr;
+
+    ResetBar(barPtr);
+    nPoints = NumberOfPoints(barPtr);
+    if (nPoints < 1) {
+	return;			/* No data points */
+    }
+    barWidth = graphPtr->barWidth;
+    if (barPtr->barWidth > 0.0) {
+	barWidth = barPtr->barWidth;
+    }
+    baseline = (barPtr->axes.y->logScale) ? 1.0 : graphPtr->baseline;
+    barOffset = barWidth * 0.5;
+
+    /*
+     * Create an array of rectangles representing the screen coordinates
+     * of all the segments in the bar.
+     */
+    rectPtr = rectangles = Blt_Malloc(nPoints * sizeof(XRectangle));
+    assert(rectangles);
+    rectToData = Blt_Calloc(nPoints, sizeof(int));
+    assert(rectToData);
+
+    x = barPtr->x.valueArr, y = barPtr->y.valueArr;
+    count = 0;
+    for (i = 0; i < nPoints; i++) {
+	if (((x[i] - barWidth) > barPtr->axes.x->axisRange.max) ||
+	    ((x[i] + barWidth) < barPtr->axes.x->axisRange.min)) {
+	    continue;		/* Abscissa is out of range of the x-axis */
+	}
+	c1.x = x[i] - barOffset;
+	c1.y = y[i];
+	c2.x = c1.x + barWidth;
+	c2.y = baseline;
+
+	/*
+	 * If the mode is "aligned" or "stacked" we need to adjust the
+	 * x or y coordinates of the two corners.
+	 */
+
+	if ((graphPtr->nStacks > 0) && (graphPtr->mode != MODE_INFRONT)) {
+	    Blt_HashEntry *hPtr;
+
+	    key.value = x[i];
+	    key.axes = barPtr->axes;
+	    hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
+	    if (hPtr != NULL) {
+		FreqInfo *infoPtr;
+		double slice, width;
+
+		infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
+		switch (graphPtr->mode) {
+		case MODE_STACKED:
+		    c2.y = infoPtr->lastY;
+		    c1.y += c2.y;
+		    infoPtr->lastY = c1.y;
+		    break;
+
+		case MODE_ALIGNED:
+		    infoPtr->count++;
+		    slice = barWidth / (double)infoPtr->freq;
+		    c1.x += slice * (infoPtr->freq - infoPtr->count);
+		    c2.x = c1.x + slice;
+		    break;
+
+		case MODE_OVERLAP:
+		    infoPtr->count++;
+		    slice = barWidth / (double)(infoPtr->freq * 2);
+		    width = slice * (infoPtr->freq + 1);
+		    c1.x += slice * (infoPtr->freq - infoPtr->count);
+		    c2.x = c1.x + width;
+		    break;
+		case MODE_INFRONT:
+		    break;
+		}
+	    }
+	}
+	invertBar = FALSE;
+	if (c1.y < c2.y) {
+	    double temp;
+
+	    /* Handle negative bar values by swapping ordinates */
+	    temp = c1.y, c1.y = c2.y, c2.y = temp;
+	    invertBar = TRUE;
+	}
+	/*
+	 * Get the two corners of the bar segment and compute the rectangle
+	 */
+	c1 = Blt_Map2D(graphPtr, c1.x, c1.y, &barPtr->axes);
+	c2 = Blt_Map2D(graphPtr, c2.x, c2.y, &barPtr->axes);
+
+	/* Bound the bars vertically by the size of the graph window */
+	if (c1.y < 0.0) {
+	    c1.y = 0.0;
+	} else if (c1.y > (double)graphPtr->height) {
+	    c1.y = (double)graphPtr->height;
+	}
+	if (c2.y < 0.0) {
+	    c2.y = 0.0;
+	} else if (c2.y > (double)graphPtr->height) {
+	    c2.y = (double)graphPtr->height;
+	}
+	dx = c1.x - c2.x;
+	dy = c1.y - c2.y;
+	height = (int)Round(FABS(dy));
+	if (invertBar) {
+	    rectPtr->y = (short int)MIN(c1.y, c2.y);
+	} else {
+	    rectPtr->y = (short int)(MAX(c1.y, c2.y)) - height;
+	}
+	rectPtr->x = (short int)MIN(c1.x, c2.x);
+	rectPtr->width = (short int)Round(FABS(dx)) + 1;
+	if (rectPtr->width < 1) {
+	    rectPtr->width = 1;
+	}
+	rectPtr->height = height + 1;
+	if (rectPtr->height < 1) {
+	    rectPtr->height = 1;
+	}
+	rectToData[count] = i;	/* Save the data index corresponding to the
+				 * rectangle */
+	rectPtr++;
+	count++;
+    }
+    barPtr->nRects = count;
+    barPtr->rectangles = rectangles;
+    barPtr->rectToData = rectToData;
+    if (barPtr->nActiveIndices > 0) {
+	MapActiveBars(barPtr);
+    }
+	
+    size = 20;
+    if (count > 0) {
+	size = rectangles->width;
+    }
+    /* Set the symbol size of all the pen styles. */
+    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->symbolSize = size;
+	stylePtr->errorBarCapWidth = (stylePtr->penPtr->errorBarCapWidth > 0) 
+	    ? stylePtr->penPtr->errorBarCapWidth : (int)(size * 0.6666666);
+	stylePtr->errorBarCapWidth /= 2;
+    }
+    dataToStyle = Blt_StyleMap((Element *)barPtr);
+    if (((barPtr->yHigh.nValues > 0) && (barPtr->yLow.nValues > 0)) ||
+	((barPtr->xHigh.nValues > 0) && (barPtr->xLow.nValues > 0)) ||
+	(barPtr->xError.nValues > 0) || (barPtr->yError.nValues > 0)) {
+	Blt_MapErrorBars(graphPtr, (Element *)barPtr, dataToStyle);
+    }
+    MergePens(barPtr, dataToStyle);
+    Blt_Free(dataToStyle);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbol --
+ *
+ * 	Draw a symbol centered at the given x,y window coordinate
+ *	based upon the element symbol type and size.
+ *
+ * Results:
+ *	None.
+ *
+ * Problems:
+ *	Most notable is the round-off errors generated when
+ *	calculating the centered position of the symbol.
+ * -----------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Element *elemPtr;
+    int x, y;
+    int size;
+{
+    BarPen *penPtr = ((Bar *)elemPtr)->normalPenPtr;
+    int radius;
+
+    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
+	return;
+    }
+    radius = (size / 2);
+    size--;
+
+    x -= radius;
+    y -= radius;
+    XSetTSOrigin(graphPtr->display, penPtr->gc, x, y);
+    XFillRectangle(graphPtr->display, drawable, penPtr->gc, x, y, 
+		   size, size);
+    XSetTSOrigin(graphPtr->display, penPtr->gc, 0, 0);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawBarSegments --
+ *
+ * 	Draws each of the rectangular segments for the element.
+ *
+ * Results:
+ *	None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawBarSegments(
+    Graph *graphPtr,
+    Drawable drawable,		/* Pixmap or window to draw into */
+    BarPen *penPtr,
+    XRectangle *rectangles,
+    int nRects)
+{
+    register XRectangle *rectPtr;
+
+    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
+	return;
+    }
+    XFillRectangles(graphPtr->display, drawable, penPtr->gc, rectangles, 
+		    nRects);
+    if ((penPtr->border != NULL) && (penPtr->borderWidth > 0) && 
+	(penPtr->relief != TK_RELIEF_FLAT)) {
+	XRectangle *endPtr;
+
+	for (rectPtr = rectangles, endPtr = rectangles + nRects; 
+	     rectPtr < endPtr; rectPtr++) {
+	    Blt_Draw3DRectangle(graphPtr->tkwin, drawable, penPtr->border,
+		rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+		penPtr->borderWidth, penPtr->relief);
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawBarValues --
+ *
+ * 	Draws the numeric value of the bar.
+ *
+ * Results:
+ *	None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawBarValues(
+    Graph *graphPtr, 
+    Drawable drawable, 
+    Bar *barPtr,
+    BarPen *penPtr,
+    XRectangle *rectangles,
+    int nRects,
+    int *rectToData)
+{
+    XRectangle *rectPtr, *endPtr;
+    int count;
+    char *fmt;
+    char string[TCL_DOUBLE_SPACE * 2 + 2];
+    double x, y;
+    Point2D anchorPos;
+    
+    count = 0;
+    fmt = penPtr->valueFormat;
+    if (fmt == NULL) {
+	fmt = "%g";
+    }
+    for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr; 
+	 rectPtr++) {
+	x = barPtr->x.valueArr[rectToData[count]];
+	y = barPtr->y.valueArr[rectToData[count]];
+	count++;
+	if (penPtr->valueShow == SHOW_X) {
+	    sprintf(string, fmt, x); 
+	} else if (penPtr->valueShow == SHOW_Y) {
+	    sprintf(string, fmt, y); 
+	} else if (penPtr->valueShow == SHOW_BOTH) {
+	    sprintf(string, fmt, x);
+	    strcat(string, ",");
+	    sprintf(string + strlen(string), fmt, y);
+	}
+	if (graphPtr->inverted) {
+	    anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
+	    anchorPos.x = rectPtr->x + rectPtr->width;
+	    if (y < graphPtr->baseline) {
+		anchorPos.x -= rectPtr->width;
+	    } 
+	} else {
+	    anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
+	    anchorPos.y = rectPtr->y;
+	    if (y < graphPtr->baseline) {			
+		anchorPos.y += rectPtr->height;
+	    }
+	}
+	Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle), 
+		     (int)anchorPos.x, (int)anchorPos.y);
+    }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawNormalBar --
+ *
+ *	Draws the rectangle representing the bar element.  If the
+ *	relief option is set to "raised" or "sunken" and the bar
+ *	borderwidth is set (borderwidth > 0), a 3D border is drawn
+ *	around the bar.
+ *
+ *	Don't draw bars that aren't visible (i.e. within the limits
+ *	of the axis).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	X drawing commands are output.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawNormalBar(graphPtr, drawable, elemPtr)
+    Graph *graphPtr;
+    Drawable drawable;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    int count;
+    Blt_ChainLink *linkPtr;
+    register BarPenStyle *stylePtr;
+    BarPen *penPtr;
+
+    count = 0;
+    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	penPtr = stylePtr->penPtr;
+	if (stylePtr->nRects > 0) {
+	    DrawBarSegments(graphPtr, drawable, penPtr, stylePtr->rectangles, 
+		stylePtr->nRects);
+	}
+	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
+	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
+		       stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
+	}
+	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
+		       stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
+			stylePtr->rectangles, stylePtr->nRects, 
+			barPtr->rectToData + count);
+	}
+	count += stylePtr->nRects;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawActiveBar --
+ *
+ *	Draws rectangles representing the active segments of the
+ *	bar element.  If the -relief option is set (other than "flat")
+ *	and the borderwidth is greater than 0, a 3D border is drawn
+ *	around the each bar segment.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	X drawing commands are output.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawActiveBar(graphPtr, drawable, elemPtr)
+    Graph *graphPtr;
+    Drawable drawable;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+
+    if (barPtr->activePenPtr != NULL) {
+	BarPen *penPtr = barPtr->activePenPtr;
+
+	if (barPtr->nActiveIndices > 0) {
+	    if (barPtr->flags & ACTIVE_PENDING) {
+		MapActiveBars(barPtr);
+	    }
+	    DrawBarSegments(graphPtr, drawable, penPtr, barPtr->activeRects, 
+			 barPtr->nActive);
+	    if (penPtr->valueShow != SHOW_NONE) {
+		DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
+			   barPtr->activeRects, barPtr->nActive, 
+			   barPtr->activeToData);
+	    }
+	} else if (barPtr->nActiveIndices < 0) {
+	    DrawBarSegments(graphPtr, drawable, penPtr, barPtr->rectangles, 
+			 barPtr->nRects);
+	    if (penPtr->valueShow != SHOW_NONE) {
+		DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
+			barPtr->rectangles, barPtr->nRects, barPtr->rectToData);
+	    }
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * 	Draw a symbol centered at the given x,y window coordinate
+ *	based upon the element symbol type and size.
+ *
+ * Results:
+ *	None.
+ *
+ * Problems:
+ *	Most notable is the round-off errors generated when
+ *	calculating the centered position of the symbol.
+ *
+ * -----------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
+    Graph *graphPtr;
+    PsToken psToken;
+    Element *elemPtr;
+    int size;
+    double x, y;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    BarPen *bpPtr = barPtr->normalPenPtr;
+
+    if ((bpPtr->border == NULL) && (bpPtr->fgColor == NULL)) {
+	return;
+    }
+    /*
+     * Build a PostScript procedure to draw the fill and outline of
+     * the symbol after the path of the symbol shape has been formed
+     */
+    Blt_AppendToPostScript(psToken, "\n",
+	"/DrawSymbolProc {\n",
+	"  gsave\n    ", (char *)NULL);
+    if (bpPtr->stipple != None) {
+	if (bpPtr->border != NULL) {
+	    Blt_BackgroundToPostScript(psToken,Tk_3DBorderColor(bpPtr->border));
+	    Blt_AppendToPostScript(psToken, "    Fill\n    ", (char *)NULL);
+	}
+	if (bpPtr->fgColor != NULL) {
+	    Blt_ForegroundToPostScript(psToken, bpPtr->fgColor);
+	} else {
+	    Blt_ForegroundToPostScript(psToken,Tk_3DBorderColor(bpPtr->border));
+	}
+	Blt_StippleToPostScript(psToken, graphPtr->display, bpPtr->stipple);
+    } else if (bpPtr->fgColor != NULL) {
+	Blt_ForegroundToPostScript(psToken, bpPtr->fgColor);
+	Blt_AppendToPostScript(psToken, "    fill\n", (char *)NULL);
+    }
+    Blt_AppendToPostScript(psToken, "  grestore\n", (char *)NULL);
+    Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
+    Blt_FormatToPostScript(psToken, "%g %g %d Sq\n", x, y, size);
+}
+
+static void
+SegmentsToPostScript(graphPtr, psToken, penPtr, rectPtr, nRects)
+    Graph *graphPtr;
+    PsToken psToken;
+    BarPen *penPtr;
+    register XRectangle *rectPtr;
+    int nRects;
+{
+    XRectangle *endPtr;
+
+    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
+	return;
+    }
+    for (endPtr = rectPtr + nRects; rectPtr < endPtr; rectPtr++) {
+	if ((rectPtr->width < 1) || (rectPtr->height < 1)) {
+	    continue;
+	}
+	if (penPtr->stipple != None) {
+	    Blt_RegionToPostScript(psToken, 
+		(double)rectPtr->x, (double)rectPtr->y,
+		(int)rectPtr->width - 1, (int)rectPtr->height - 1);
+	    if (penPtr->border != NULL) {
+		Blt_BackgroundToPostScript(psToken, 
+			Tk_3DBorderColor(penPtr->border));
+		Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+	    }
+	    if (penPtr->fgColor != NULL) {
+		Blt_ForegroundToPostScript(psToken, penPtr->fgColor);
+	    } else {
+		Blt_ForegroundToPostScript(psToken, 
+					   Tk_3DBorderColor(penPtr->border));
+	    }
+	    Blt_StippleToPostScript(psToken, graphPtr->display, 
+				    penPtr->stipple);
+	} else if (penPtr->fgColor != NULL) {
+	    Blt_ForegroundToPostScript(psToken, penPtr->fgColor);
+	    Blt_RectangleToPostScript(psToken, 
+		(double)rectPtr->x, (double)rectPtr->y,
+		(int)rectPtr->width - 1, (int)rectPtr->height - 1);
+	}
+	if ((penPtr->border != NULL) && (penPtr->borderWidth > 0) && 
+	    (penPtr->relief != TK_RELIEF_FLAT)) {
+	    Blt_Draw3DRectangleToPostScript(psToken, penPtr->border, 
+		(double)rectPtr->x, (double)rectPtr->y, 
+		(int)rectPtr->width, (int)rectPtr->height,
+		penPtr->borderWidth, penPtr->relief);
+	}
+    }
+}
+
+static void
+BarValuesToPostScript(
+    Graph *graphPtr,
+    PsToken psToken,
+    Bar *barPtr,
+    BarPen *penPtr,
+    XRectangle *rectangles,
+    int nRects,
+    int *rectToData)
+{
+    XRectangle *rectPtr, *endPtr;
+    int count;
+    char *fmt;
+    char string[TCL_DOUBLE_SPACE * 2 + 2];
+    double x, y;
+    Point2D anchorPos;
+    
+    count = 0;
+    fmt = penPtr->valueFormat;
+    if (fmt == NULL) {
+	fmt = "%g";
+    }
+    for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr; 
+	 rectPtr++) {
+	x = barPtr->x.valueArr[rectToData[count]];
+	y = barPtr->y.valueArr[rectToData[count]];
+	count++;
+	if (penPtr->valueShow == SHOW_X) {
+	    sprintf(string, fmt, x); 
+	} else if (penPtr->valueShow == SHOW_Y) {
+	    sprintf(string, fmt, y); 
+	} else if (penPtr->valueShow == SHOW_BOTH) {
+	    sprintf(string, fmt, x);
+	    strcat(string, ",");
+	    sprintf(string + strlen(string), fmt, y);
+	}
+	if (graphPtr->inverted) {
+	    anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
+	    anchorPos.x = rectPtr->x + rectPtr->width;
+	    if (y < graphPtr->baseline) {
+		anchorPos.x -= rectPtr->width;
+	    } 
+	} else {
+	    anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
+	    anchorPos.y = rectPtr->y;
+	    if (y < graphPtr->baseline) {			
+		anchorPos.y += rectPtr->height;
+	    }
+	}
+	Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle), 
+		     anchorPos.x, anchorPos.y);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ActiveBarToPostScript --
+ *
+ *	Similar to the NormalBarToPostScript procedure, generates
+ *	PostScript commands to display the rectangles representing the
+ *	active bar segments of the element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript pen width, dashes, and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ActiveBarToPostScript(graphPtr, psToken, elemPtr)
+    Graph *graphPtr;
+    PsToken psToken;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+
+    if (barPtr->activePenPtr != NULL) {
+	BarPen *penPtr = barPtr->activePenPtr;
+	
+	if (barPtr->nActiveIndices > 0) {
+	    if (barPtr->flags & ACTIVE_PENDING) {
+		MapActiveBars(barPtr);
+	    }
+	    SegmentsToPostScript(graphPtr, psToken, penPtr,
+				 barPtr->activeRects, barPtr->nActive);
+	    if (penPtr->valueShow != SHOW_NONE) {
+		BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
+		   barPtr->activeRects, barPtr->nActive, barPtr->activeToData);
+	    }
+	} else if (barPtr->nActiveIndices < 0) {
+	    SegmentsToPostScript(graphPtr, psToken, penPtr, 
+				 barPtr->rectangles, barPtr->nRects);
+	    if (penPtr->valueShow != SHOW_NONE) {
+		BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
+		   barPtr->rectangles, barPtr->nRects, barPtr->rectToData);
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NormalBarToPostScript --
+ *
+ *	Generates PostScript commands to form the rectangles
+ *	representing the segments of the bar element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript pen width, dashes, and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+NormalBarToPostScript(graphPtr, psToken, elemPtr)
+    Graph *graphPtr;
+    PsToken psToken;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+    Blt_ChainLink *linkPtr;
+    register BarPenStyle *stylePtr;
+    int count;
+    BarPen *penPtr;
+    XColor *colorPtr;
+
+    count = 0;
+    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	penPtr = stylePtr->penPtr;
+	if (stylePtr->nRects > 0) {
+	    SegmentsToPostScript(graphPtr, psToken, penPtr, 
+		stylePtr->rectangles, stylePtr->nRects);
+	}
+	colorPtr = penPtr->errorBarColor;
+	if (colorPtr == COLOR_DEFAULT) {
+	    colorPtr = penPtr->fgColor;
+	}
+	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
+	    Blt_LineAttributesToPostScript(psToken, colorPtr, 
+		penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
+	    Blt_2DSegmentsToPostScript(psToken, stylePtr->xErrorBars,
+		stylePtr->xErrorBarCnt);
+	}
+	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+	    Blt_LineAttributesToPostScript(psToken, colorPtr, 
+		penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
+	    Blt_2DSegmentsToPostScript(psToken, stylePtr->yErrorBars,
+		stylePtr->yErrorBarCnt);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
+		stylePtr->rectangles, stylePtr->nRects, 
+		barPtr->rectToData + count);
+	}
+	count += stylePtr->nRects;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyBar --
+ *
+ *	Release memory and resources allocated for the bar element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the bar element is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+#define FreeElemVector(v) \
+    if ((v).clientId != NULL) { \
+	Blt_FreeVectorId((v).clientId); \
+    } else if ((v).valueArr != NULL) { \
+	Blt_Free((v).valueArr); \
+    } 
+
+static void
+DestroyBar(graphPtr, elemPtr)
+    Graph *graphPtr;
+    Element *elemPtr;
+{
+    Bar *barPtr = (Bar *)elemPtr;
+
+    if (barPtr->normalPenPtr != &(barPtr->builtinPen)) {
+	Blt_FreePen(graphPtr, (Pen *)barPtr->normalPenPtr);
+    }
+    DestroyPen(graphPtr, (Pen *)&(barPtr->builtinPen));
+    if (barPtr->activePenPtr != NULL) {
+	Blt_FreePen(graphPtr, (Pen *)barPtr->activePenPtr);
+    }
+    FreeElemVector(barPtr->x);
+    FreeElemVector(barPtr->y);
+    FreeElemVector(barPtr->w);
+    FreeElemVector(barPtr->xHigh);
+    FreeElemVector(barPtr->xLow);
+    FreeElemVector(barPtr->xError);
+    FreeElemVector(barPtr->yHigh);
+    FreeElemVector(barPtr->yLow);
+    FreeElemVector(barPtr->yError);
+
+    ResetBar(barPtr);
+    if (barPtr->activeIndices != NULL) {
+	Blt_Free(barPtr->activeIndices);
+    }
+    if (barPtr->palette != NULL) {
+	Blt_FreePalette(graphPtr, barPtr->palette);
+	Blt_ChainDestroy(barPtr->palette);
+    }
+    if (barPtr->tags != NULL) {
+	Blt_Free(barPtr->tags);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_BarElement --
+ *
+ *	Allocate memory and initialize methods for the new bar element.
+ *
+ * Results:
+ *	The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the bar element structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static ElementProcs barProcs =
+{
+    ClosestBar,
+    ConfigureBar,
+    DestroyBar,
+    DrawActiveBar,
+    DrawNormalBar,
+    DrawSymbol,
+    GetBarExtents,
+    ActiveBarToPostScript,
+    NormalBarToPostScript,
+    SymbolToPostScript,
+    MapBar,
+};
+
+
+Element *
+Blt_BarElement(graphPtr, name, type)
+    Graph *graphPtr;
+    char *name;
+    Blt_Uid type;
+{
+    register Bar *barPtr;
+
+    barPtr = Blt_Calloc(1, sizeof(Bar));
+    assert(barPtr);
+    barPtr->normalPenPtr = &(barPtr->builtinPen);
+    barPtr->procsPtr = &barProcs;
+    barPtr->specsPtr = barElemConfigSpecs;
+    barPtr->labelRelief = TK_RELIEF_FLAT;
+    barPtr->classUid = type;
+    /* By default, an element's name and label are the same. */
+    barPtr->label = Blt_Strdup(name);
+    barPtr->name = Blt_Strdup(name);
+
+    barPtr->graphPtr = graphPtr;
+    barPtr->hidden = FALSE;
+
+    InitPen(barPtr->normalPenPtr);
+    barPtr->palette = Blt_ChainCreate();
+    return (Element *)barPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_InitFreqTable --
+ *
+ *	Generate a table of abscissa frequencies.  Duplicate
+ *	x-coordinates (depending upon the bar drawing mode) indicate
+ *	that something special should be done with each bar segment
+ *	mapped to the same abscissa (i.e. it should be stacked,
+ *	aligned, or overlay-ed with other segments)
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is allocated for the bar element structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_InitFreqTable(graphPtr)
+    Graph *graphPtr;
+{
+    register Element *elemPtr;
+    Blt_ChainLink *linkPtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Bar *barPtr;
+    int isNew, count;
+    int nStacks, nSegs;
+    int nPoints;
+    FreqKey key;
+    Blt_HashTable freqTable;
+    register int i;
+    double *xArr;
+    /*
+     * Free resources associated with a previous frequency table. This
+     * includes the array of frequency information and the table itself
+     */
+    if (graphPtr->freqArr != NULL) {
+	Blt_Free(graphPtr->freqArr);
+	graphPtr->freqArr = NULL;
+    }
+    if (graphPtr->nStacks > 0) {
+	Blt_DeleteHashTable(&(graphPtr->freqTable));
+	graphPtr->nStacks = 0;
+    }
+    if (graphPtr->mode == MODE_INFRONT) {
+	return;			/* No frequency table is needed for
+				 * "infront" mode */
+    }
+    Blt_InitHashTable(&(graphPtr->freqTable), sizeof(FreqKey) / sizeof(int));
+
+    /*
+     * Initialize a hash table and fill it with unique abscissas.
+     * Keep track of the frequency of each x-coordinate and how many
+     * abscissas have duplicate mappings.
+     */
+    Blt_InitHashTable(&freqTable, sizeof(FreqKey) / sizeof(int));
+    nSegs = nStacks = 0;
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
+	    continue;
+	}
+	nSegs++;
+	barPtr = (Bar *)elemPtr;
+	xArr = barPtr->x.valueArr;
+	nPoints = NumberOfPoints(barPtr);
+	for (i = 0; i < nPoints; i++) {
+	    key.value = xArr[i];
+	    key.axes = barPtr->axes;
+	    hPtr = Blt_CreateHashEntry(&freqTable, (char *)&key, &isNew);
+	    assert(hPtr != NULL);
+	    if (isNew) {
+		count = 1;
+	    } else {
+		count = (int)Blt_GetHashValue(hPtr);
+		if (count == 1) {
+		    nStacks++;
+		}
+		count++;
+	    }
+	    Blt_SetHashValue(hPtr, (ClientData)count);
+	}
+    }
+    if (nSegs == 0) {
+	return;			/* No bar elements to be displayed */
+    }
+    if (nStacks > 0) {
+	FreqInfo *infoPtr;
+	FreqKey *keyPtr;
+	Blt_HashEntry *h2Ptr;
+
+	graphPtr->freqArr = Blt_Calloc(nStacks, sizeof(FreqInfo));
+	assert(graphPtr->freqArr);
+	infoPtr = graphPtr->freqArr;
+	for (hPtr = Blt_FirstHashEntry(&freqTable, &cursor); hPtr != NULL;
+	    hPtr = Blt_NextHashEntry(&cursor)) {
+	    count = (int)Blt_GetHashValue(hPtr);
+	    keyPtr = (FreqKey *)Blt_GetHashKey(&freqTable, hPtr);
+	    if (count > 1) {
+		h2Ptr = Blt_CreateHashEntry(&(graphPtr->freqTable),
+		    (char *)keyPtr, &isNew);
+		count = (int)Blt_GetHashValue(hPtr);
+		infoPtr->freq = count;
+		infoPtr->axes = keyPtr->axes;
+		Blt_SetHashValue(h2Ptr, infoPtr);
+		infoPtr++;
+	    }
+	}
+    }
+    Blt_DeleteHashTable(&freqTable);
+    graphPtr->nStacks = nStacks;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_ComputeStacks --
+ *
+ *	Determine the height of each stack of bar segments.  A stack
+ *	is created by designating two or more points with the same
+ *	abscissa.  Each ordinate defines the height of a segment in
+ *	the stack.  This procedure simply looks at all the data points
+ *	summing the heights of each stacked segment. The sum is saved
+ *	in the frequency information table.  This value will be used
+ *	to calculate the y-axis limits (data limits aren't sufficient).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The heights of each stack is computed. CheckStacks will
+ *	use this information to adjust the y-axis limits if necessary.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_ComputeStacks(graphPtr)
+    Graph *graphPtr;
+{
+    Element *elemPtr;
+    Bar *barPtr;
+    FreqKey key;
+    Blt_ChainLink *linkPtr;
+    Blt_HashEntry *hPtr;
+    int nPoints;
+    register int i;
+    register FreqInfo *infoPtr;
+    double *xArr, *yArr;
+
+    if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
+	return;
+    }
+    /* Reset the sums for all duplicate values to zero. */
+
+    infoPtr = graphPtr->freqArr;
+    for (i = 0; i < graphPtr->nStacks; i++) {
+	infoPtr->sum = 0.0;
+	infoPtr++;
+    }
+
+    /* Look at each bar point, adding the ordinates of duplicate abscissas */
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
+	    continue;
+	}
+	barPtr = (Bar *)elemPtr;
+	xArr = barPtr->x.valueArr;
+	yArr = barPtr->y.valueArr;
+	nPoints = NumberOfPoints(barPtr);
+	for (i = 0; i < nPoints; i++) {
+	    key.value = xArr[i];
+	    key.axes = barPtr->axes;
+	    hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
+	    if (hPtr == NULL) {
+		continue;
+	    }
+	    infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
+	    infoPtr->sum += yArr[i];
+	}
+    }
+}
+
+void
+Blt_ResetStacks(graphPtr)
+    Graph *graphPtr;
+{
+    register FreqInfo *infoPtr, *endPtr;
+
+    for (infoPtr = graphPtr->freqArr, 
+	     endPtr = graphPtr->freqArr + graphPtr->nStacks;
+	 infoPtr < endPtr; infoPtr++) {
+	infoPtr->lastY = 0.0;
+	infoPtr->count = 0;
+    }
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltGrElem.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrElem.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrElem.c	(revision 175)
@@ -0,0 +1,2262 @@
+
+/*
+ * bltGrElem.c --
+ *
+ *	This module implements generic elements for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltVecInt.h"
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+
+
+static Tk_OptionParseProc StringToData;
+static Tk_OptionPrintProc DataToString;
+static Tk_OptionParseProc StringToDataPairs;
+static Tk_OptionPrintProc DataPairsToString;
+static Tk_OptionParseProc StringToAlong;
+static Tk_OptionPrintProc AlongToString;
+static Tk_CustomOption alongOption =
+{
+    StringToAlong, AlongToString, (ClientData)0
+};
+Tk_CustomOption bltDataOption =
+{
+    StringToData, DataToString, (ClientData)0
+};
+Tk_CustomOption bltDataPairsOption =
+{
+    StringToDataPairs, DataPairsToString, (ClientData)0
+};
+extern Tk_CustomOption bltDistanceOption;
+
+
+
+static int counter;
+
+#include "bltGrElem.h"
+
+extern Element *Blt_BarElement();
+extern Element *Blt_LineElement();
+
+static Blt_VectorChangedProc VectorChangedProc;
+
+EXTERN int Blt_VectorExists2 _ANSI_ARGS_((Tcl_Interp *interp, char *vecName));
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+static int
+GetPenStyle(graphPtr, string, type, stylePtr)
+    Graph *graphPtr;
+    char *string;
+    Blt_Uid type;
+    PenStyle *stylePtr;
+{
+    Pen *penPtr;
+    Tcl_Interp *interp = graphPtr->interp;
+    char **elemArr;
+    int nElem;
+
+    elemArr = NULL;
+    if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((nElem != 1) && (nElem != 3)) {
+	Tcl_AppendResult(interp, "bad style \"", string, "\": should be ", 
+		 "\"penName\" or \"penName min max\"", (char *)NULL);
+	if (elemArr != NULL) {
+	    Blt_Free(elemArr);
+	}
+	return TCL_ERROR;
+    }
+    if (Blt_GetPen(graphPtr, elemArr[0], type, &penPtr) != TCL_OK) {
+	Blt_Free(elemArr);
+	return TCL_ERROR;
+    }
+    if (nElem == 3) {
+	double min, max;
+
+	if ((Tcl_GetDouble(interp, elemArr[1], &min) != TCL_OK) ||
+	    (Tcl_GetDouble(interp, elemArr[2], &max) != TCL_OK)) {
+	    Blt_Free(elemArr);
+	    return TCL_ERROR;
+	}
+	SetWeight(stylePtr->weight, min, max);
+    }
+    stylePtr->penPtr = penPtr;
+    Blt_Free(elemArr);
+    return TCL_OK;
+}
+
+double
+Blt_VecMin(vecPtr)
+    Blt_Vector *vecPtr;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (!FINITE(vPtr->min)) {
+        double min;
+        register int i;
+
+        min = bltNaN;
+        for (i = 0; i < vPtr->length; i++) {
+            if (FINITE(vPtr->valueArr[i])) {
+                min = vPtr->valueArr[i];
+                break;
+            }
+        }
+        for (/* empty */; i < vPtr->length; i++) {
+            if (FINITE(vPtr->valueArr[i])) {
+                if (min > vPtr->valueArr[i]) {
+                    min = vPtr->valueArr[i];
+                }
+            }
+        }
+        vPtr->min = min;
+    }
+    return vPtr->min;
+}
+
+double
+Blt_VecMax(vecPtr)
+    Blt_Vector *vecPtr;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (!FINITE(vPtr->max)) {
+        double max;
+        register int i;
+
+        max = bltNaN;
+        for (i = 0; i < vPtr->length; i++) {
+            if (FINITE(vPtr->valueArr[i])) {
+                max = vPtr->valueArr[i];
+                break;
+            }
+        }
+        for (/* empty */; i < vPtr->length; i++) {
+            if (FINITE(vPtr->valueArr[i])) {
+                if (max < vPtr->valueArr[i]) {
+                    max = vPtr->valueArr[i];
+                }
+            }
+        }
+        vPtr->max = max;
+    }
+    return vPtr->max;
+}
+
+static void
+SyncElemVector(vPtr)
+    ElemVector *vPtr;
+{
+    vPtr->nValues = Blt_VecLength(vPtr->vecPtr);
+    vPtr->valueArr = Blt_VecData(vPtr->vecPtr);
+    vPtr->min = Blt_VecMin(vPtr->vecPtr);
+    vPtr->max = Blt_VecMax(vPtr->vecPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindRange --
+ *
+ *	Find the minimum, positive minimum, and maximum values in a
+ *	given vector and store the results in the vector structure.
+ *
+ * Results:
+ *     	None.
+ *
+ * Side Effects:
+ *	Minimum, positive minimum, and maximum values are stored in
+ *	the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FindRange(vPtr)
+    ElemVector *vPtr;
+{
+    register int i;
+    register double *x;
+    register double min, max;
+
+    if ((vPtr->nValues < 1) || (vPtr->valueArr == NULL)) {
+	return;			/* This shouldn't ever happen. */
+    }
+    x = vPtr->valueArr;
+
+    min = DBL_MAX, max = -DBL_MAX;
+    for(i = 0; i < vPtr->nValues; i++) {
+	if (FINITE(x[i])) {
+	    min = max = x[i];
+	    break;
+	}
+    }
+    /*  Initialize values to track the vector range */
+    for (/* empty */; i < vPtr->nValues; i++) {
+	if (FINITE(x[i])) {
+	    if (x[i] < min) {
+		min = x[i];
+	    } else if (x[i] > max) {
+		max = x[i];
+	    }
+	}
+    }
+    vPtr->min = min, vPtr->max = max;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindElemVectorMinimum --
+ *
+ *	Find the minimum, positive minimum, and maximum values in a
+ *	given vector and store the results in the vector structure.
+ *
+ * Results:
+ *     	None.
+ *
+ * Side Effects:
+ *	Minimum, positive minimum, and maximum values are stored in
+ *	the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+double
+Blt_FindElemVectorMinimum(vPtr, minLimit)
+    ElemVector *vPtr;
+    double minLimit;
+{
+    register int i;
+    register double *arr;
+    register double min, x;
+
+    min = DBL_MAX;
+    arr = vPtr->valueArr;
+    for (i = 0; i < vPtr->nValues; i++) {
+	x = arr[i];
+	if (x < 0.0) {
+	    /* What do you do about negative values when using log
+	     * scale values seems like a grey area.  Mirror. */
+	    x = -x;
+	}
+	if ((x > minLimit) && (min > x)) {
+	    min = x;
+	}
+    }
+    if (min == DBL_MAX) {
+	min = minLimit;
+    }
+    return min;
+}
+
+static void
+FreeDataVector(vPtr)
+    ElemVector *vPtr;
+{
+    if (vPtr->clientId != NULL) {
+	Blt_FreeVectorId(vPtr->clientId);	/* Free the old vector */
+	vPtr->clientId = NULL;
+    } else if (vPtr->valueArr != NULL) {
+	Blt_Free(vPtr->valueArr);
+    }
+    vPtr->valueArr = NULL;
+    vPtr->nValues = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorChangedProc --
+ *
+ *
+ * Results:
+ *     	None.
+ *
+ * Side Effects:
+ *	Graph is redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+VectorChangedProc(interp, clientData, notify)
+    Tcl_Interp *interp;
+    ClientData clientData;
+    Blt_VectorNotify notify;
+{
+    ElemVector *vPtr = clientData;
+    Element *elemPtr = vPtr->elemPtr;
+    Graph *graphPtr = elemPtr->graphPtr;
+
+    switch (notify) {
+    case BLT_VECTOR_NOTIFY_DESTROY:
+	vPtr->clientId = NULL;
+	vPtr->valueArr = NULL;
+	vPtr->nValues = 0;
+	break;
+
+    case BLT_VECTOR_NOTIFY_UPDATE:
+    default:
+	Blt_GetVectorById(interp, vPtr->clientId, &vPtr->vecPtr);
+	SyncElemVector(vPtr);
+	break;
+    }
+    graphPtr->flags |= RESET_AXES;
+    elemPtr->flags |= MAP_ITEM;
+    if (!elemPtr->hidden) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+}
+
+static int
+EvalExprList(interp, list, nElemPtr, arrayPtr)
+    Tcl_Interp *interp;
+    char *list;
+    int *nElemPtr;
+    double **arrayPtr;
+{
+    int nElem;
+    char **elemArr;
+    double *array;
+    int result;
+
+    result = TCL_ERROR;
+    elemArr = NULL;
+    if (Tcl_SplitList(interp, list, &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    array = NULL;
+    if (nElem > 0) {
+	register double *valuePtr;
+	register int i;
+
+	counter++;
+	array = Blt_Malloc(sizeof(double) * nElem);
+	if (array == NULL) {
+	    Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL);
+	    goto badList;
+	}
+	valuePtr = array;
+	for (i = 0; i < nElem; i++) {
+	    if (Tcl_ExprDouble(interp, elemArr[i], valuePtr) != TCL_OK) {
+		goto badList;
+	    }
+	    valuePtr++;
+	}
+    }
+    result = TCL_OK;
+
+  badList:
+    Blt_Free(elemArr);
+    *arrayPtr = array;
+    *nElemPtr = nElem;
+    if (result != TCL_OK) {
+	Blt_Free(array);
+    }
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToData --
+ *
+ *	Given a Tcl list of numeric expression representing the element
+ *	values, convert into an array of double precision values. In
+ *	addition, the minimum and maximum values are saved.  Since
+ *	elastic values are allow (values which translate to the
+ *	min/max of the graph), we must try to get the non-elastic
+ *	minimum and maximum.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The vector is passed
+ *	back via the vPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToData(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Type of axis vector to fill */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Tcl list of expressions */
+    char *widgRec;		/* Element record */
+    int offset;			/* Offset of vector in Element record */
+{
+    Element *elemPtr = (Element *)(widgRec);
+    ElemVector *vPtr = (ElemVector *)(widgRec + offset);
+
+    FreeDataVector(vPtr);
+    if (Blt_VectorExists2(interp, string)) {
+	Blt_VectorId clientId;
+
+	clientId = Blt_AllocVectorId(interp, string);
+	if (Blt_GetVectorById(interp, clientId, &vPtr->vecPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Blt_SetVectorChangedProc(clientId, VectorChangedProc, vPtr);
+	vPtr->elemPtr = elemPtr;
+	vPtr->clientId = clientId;
+	SyncElemVector(vPtr);
+	elemPtr->flags |= MAP_ITEM;
+    } else {
+	double *newArr;
+	int nValues;
+
+	if (EvalExprList(interp, string, &nValues, &newArr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (nValues > 0) {
+	    vPtr->valueArr = newArr;
+	}
+	vPtr->nValues = nValues;
+	FindRange(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataToString --
+ *
+ *	Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ *	The string representation of the vector is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DataToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Type of axis vector to print */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element record */
+    int offset;			/* Offset of vector in Element record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    ElemVector *vPtr = (ElemVector *)(widgRec + offset);
+    Element *elemPtr = (Element *)(widgRec);
+    Tcl_DString dString;
+    char *result;
+    char string[TCL_DOUBLE_SPACE + 1];
+    double *p, *endPtr; 
+
+    if (vPtr->clientId != NULL) {
+	return Blt_NameOfVectorId(vPtr->clientId);
+    }
+    if (vPtr->nValues == 0) {
+	return "";
+    }
+    Tcl_DStringInit(&dString);
+    endPtr = vPtr->valueArr + vPtr->nValues;
+    for (p = vPtr->valueArr; p < endPtr; p++) {
+	Tcl_PrintDouble(elemPtr->graphPtr->interp, *p, string);
+	Tcl_DStringAppendElement(&dString, string);
+    }
+    result = Tcl_DStringValue(&dString);
+
+    /*
+     * If memory wasn't allocated for the dynamic string, do it here (it's
+     * currently on the stack), so that Tcl can free it normally.
+     */
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToDataPairs --
+ *
+ *	This procedure is like StringToData except that it interprets
+ *	the list of numeric expressions as X Y coordinate pairs.  The
+ *	minimum and maximum for both the X and Y vectors are
+ *	determined.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The vectors are
+ *	passed back via the widget record (elemPtr).
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToDataPairs(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Tcl list of numeric expressions */
+    char *widgRec;		/* Element record */
+    int offset;			/* Not used. */
+{
+    Element *elemPtr = (Element *)widgRec;
+    int nElem;
+    unsigned int newSize;
+    double *newArr;
+
+    if (EvalExprList(interp, string, &nElem, &newArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (nElem & 1) {
+	Tcl_AppendResult(interp, "odd number of data points", (char *)NULL);
+	Blt_Free(newArr);
+	return TCL_ERROR;
+    }
+    nElem /= 2;
+    newSize = nElem * sizeof(double);
+
+    FreeDataVector(&elemPtr->x);
+    FreeDataVector(&elemPtr->y);
+
+    elemPtr->x.valueArr = Blt_Malloc(newSize);
+    elemPtr->y.valueArr = Blt_Malloc(newSize);
+    assert(elemPtr->x.valueArr && elemPtr->y.valueArr);
+    elemPtr->x.nValues = elemPtr->y.nValues = nElem;
+
+    if (newSize > 0) {
+	register double *dataPtr;
+	register int i;
+
+	for (dataPtr = newArr, i = 0; i < nElem; i++) {
+	    elemPtr->x.valueArr[i] = *dataPtr++;
+	    elemPtr->y.valueArr[i] = *dataPtr++;
+	}
+	Blt_Free(newArr);
+	FindRange(&elemPtr->x);
+	FindRange(&elemPtr->y);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataPairsToString --
+ *
+ *	Convert pairs of floating point values in the X and Y arrays
+ *	into a Tcl list.
+ *
+ * Results:
+ *	The return value is a string (Tcl list).
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+DataPairsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Not used. */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Element *elemPtr = (Element *)widgRec;
+    Tcl_Interp *interp = elemPtr->graphPtr->interp;
+    int i;
+    int length;
+    char *result;
+    char string[TCL_DOUBLE_SPACE + 1];
+    Tcl_DString dString;
+
+    length = NumberOfPoints(elemPtr);
+    if (length < 1) {
+	return "";
+    }
+    Tcl_DStringInit(&dString);
+    for (i = 0; i < length; i++) {
+	Tcl_PrintDouble(interp, elemPtr->x.valueArr[i], string);
+	Tcl_DStringAppendElement(&dString, string);
+	Tcl_PrintDouble(interp, elemPtr->y.valueArr[i], string);
+	Tcl_DStringAppendElement(&dString, string);
+    }
+    result = Tcl_DStringValue(&dString);
+
+    /*
+     * If memory wasn't allocated for the dynamic string, do it here
+     * (it's currently on the stack), so that Tcl can free it
+     * normally.
+     */
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToAlong --
+ *
+ *	Given a Tcl list of numeric expression representing the element
+ *	values, convert into an array of double precision values. In
+ *	addition, the minimum and maximum values are saved.  Since
+ *	elastic values are allow (values which translate to the
+ *	min/max of the graph), we must try to get the non-elastic
+ *	minimum and maximum.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The vector is passed
+ *	back via the vPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToAlong(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representation of value. */
+    char *widgRec;		/* Widget record. */
+    int offset;			/* Offset of field in widget record. */
+{
+    int *intPtr = (int *)(widgRec + offset);
+
+    if ((string[0] == 'x') && (string[1] == '\0')) {
+	*intPtr = SEARCH_X;
+    } else if ((string[0] == 'y') && (string[1] == '\0')) { 
+	*intPtr = SEARCH_Y;
+    } else if ((string[0] == 'b') && (strcmp(string, "both") == 0)) {
+	*intPtr = SEARCH_BOTH;
+    } else {
+	Tcl_AppendResult(interp, "bad along value \"", string, "\"",
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AlongToString --
+ *
+ *	Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ *	The string representation of the vector is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+AlongToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in widget record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    int along = *(int *)(widgRec + offset);
+
+    switch (along) {
+    case SEARCH_X:
+	return "x";
+    case SEARCH_Y:
+	return "y";
+    case SEARCH_BOTH:
+	return "both";
+    default:
+	return "unknown along value";
+    }
+}
+
+void
+Blt_FreePalette(graphPtr, palette)
+    Graph *graphPtr;
+    Blt_Chain *palette;
+{
+    Blt_ChainLink *linkPtr;
+
+    /* Skip the first slot. It contains the built-in "normal" pen of
+     * the element.  */
+    linkPtr = Blt_ChainFirstLink(palette);
+    if (linkPtr != NULL) {
+	register PenStyle *stylePtr;
+	Blt_ChainLink *nextPtr;
+
+	for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL; 
+	     linkPtr = nextPtr) {
+	    nextPtr =  Blt_ChainNextLink(linkPtr);
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    Blt_FreePen(graphPtr, stylePtr->penPtr);
+	    Blt_ChainDeleteLink(palette, linkPtr);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToStyles --
+ *
+ *	Parse the list of style names.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_StringToStyles(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing style list */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of symbol type field in record */
+{
+    Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr = (Element *)(widgRec);
+    PenStyle *stylePtr;
+    char **elemArr;
+    int nStyles;
+    register int i;
+    size_t size = (size_t)clientData;
+
+    elemArr = NULL;
+    Blt_FreePalette(elemPtr->graphPtr, palette);
+    if ((string == NULL) || (*string == '\0')) {
+	nStyles = 0;
+    } else if (Tcl_SplitList(interp, string, &nStyles, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Reserve the first entry for the "normal" pen. We'll set the
+     * style later */
+    linkPtr = Blt_ChainFirstLink(palette);
+    if (linkPtr == NULL) {
+	linkPtr = Blt_ChainAllocLink(size);
+	Blt_ChainLinkBefore(palette, linkPtr, NULL);
+    }
+    stylePtr = Blt_ChainGetValue(linkPtr);
+    stylePtr->penPtr = elemPtr->normalPenPtr;
+
+    for (i = 0; i < nStyles; i++) {
+	linkPtr = Blt_ChainAllocLink(size);
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->weight.min = (double)i;
+	stylePtr->weight.max = (double)i + 1.0;
+	stylePtr->weight.range = 1.0;
+	if (GetPenStyle(elemPtr->graphPtr, elemArr[i], elemPtr->classUid,
+	    (PenStyle *)stylePtr) != TCL_OK) {
+	    Blt_Free(elemArr);
+	    Blt_FreePalette(elemPtr->graphPtr, palette);
+	    return TCL_ERROR;
+	}
+	Blt_ChainLinkBefore(palette, linkPtr, NULL);
+    }
+    if (elemArr != NULL) {
+	Blt_Free(elemArr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StylesToString --
+ *
+ *	Convert the style information into a string.
+ *
+ * Results:
+ *	The string representing the style information is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+char *
+Blt_StylesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Not used. */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset);
+    Tcl_DString dString;
+    char *result;
+    Blt_ChainLink *linkPtr;
+
+    Tcl_DStringInit(&dString);
+    linkPtr = Blt_ChainFirstLink(palette);
+    if (linkPtr != NULL) {
+	Element *elemPtr = (Element *)(widgRec);
+	char string[TCL_DOUBLE_SPACE];
+	Tcl_Interp *interp;
+	PenStyle *stylePtr;
+
+	interp = elemPtr->graphPtr->interp;
+	for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    Tcl_DStringStartSublist(&dString);
+	    Tcl_DStringAppendElement(&dString, stylePtr->penPtr->name);
+	    Tcl_PrintDouble(interp, stylePtr->weight.min, string);
+	    Tcl_DStringAppendElement(&dString, string);
+	    Tcl_PrintDouble(interp, stylePtr->weight.max, string);
+	    Tcl_DStringAppendElement(&dString, string);
+	    Tcl_DStringEndSublist(&dString);
+	}
+    }
+    result = Blt_Strdup(Tcl_DStringValue(&dString));
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StyleMap --
+ *
+ *	Creates an array of style indices and fills it based on the weight
+ *	of each data point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+
+PenStyle **
+Blt_StyleMap(elemPtr)
+    Element *elemPtr;
+{
+    register int i;
+    int nWeights;		/* Number of weights to be examined.
+				 * If there are more data points than
+				 * weights, they will default to the
+				 * normal pen. */
+
+    PenStyle **dataToStyle;	/* Directory of styles.  Each array
+				 * element represents the style for
+				 * the data point at that index */
+    Blt_ChainLink *linkPtr;
+    PenStyle *stylePtr;
+    double *w;			/* Weight vector */
+    int nPoints;
+
+    nPoints = NumberOfPoints(elemPtr);
+    nWeights = MIN(elemPtr->w.nValues, nPoints);
+    w = elemPtr->w.valueArr;
+    linkPtr = Blt_ChainFirstLink(elemPtr->palette);
+    stylePtr = Blt_ChainGetValue(linkPtr);
+
+    /* 
+     * Create a style mapping array (data point index to style), 
+     * initialized to the default style.
+     */
+    dataToStyle = Blt_Malloc(nPoints * sizeof(PenStyle *));
+    assert(dataToStyle);
+    for (i = 0; i < nPoints; i++) {
+	dataToStyle[i] = stylePtr;
+    }
+
+    for (i = 0; i < nWeights; i++) {
+	for (linkPtr = Blt_ChainLastLink(elemPtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+
+	    if (stylePtr->weight.range > 0.0) {
+		double norm;
+
+		norm = (w[i] - stylePtr->weight.min) / stylePtr->weight.range;
+		if (((norm - 1.0) <= DBL_EPSILON) && 
+		    (((1.0 - norm) - 1.0) <= DBL_EPSILON)) {
+		    dataToStyle[i] = stylePtr;
+		    break;		/* Done: found range that matches. */
+		}
+	    }
+	}
+    }
+    return dataToStyle;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapErrorBars --
+ *
+ *	Creates two arrays of points and pen indices, filled with
+ *	the screen coordinates of the visible
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapErrorBars(graphPtr, elemPtr, dataToStyle)
+    Graph *graphPtr;
+    Element *elemPtr;
+    PenStyle **dataToStyle;
+{
+    int n, nPoints;
+    Extents2D exts;
+    PenStyle *stylePtr;
+
+    Blt_GraphExtents(graphPtr, &exts);
+    nPoints = NumberOfPoints(elemPtr);
+    if (elemPtr->xError.nValues > 0) {
+	n = MIN(elemPtr->xError.nValues, nPoints);
+    } else {
+	n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, nPoints);
+    }
+    if (n > 0) {
+	Segment2D *errorBars;
+	Segment2D *segPtr;
+	double high, low;
+	double x, y;
+	int *errorToData;
+	int *indexPtr;
+	register int i;
+		
+	segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
+	indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
+	for (i = 0; i < n; i++) {
+	    x = elemPtr->x.valueArr[i];
+	    y = elemPtr->y.valueArr[i];
+	    stylePtr = dataToStyle[i];
+	    if ((FINITE(x)) && (FINITE(y))) {
+		if (elemPtr->xError.nValues > 0) {
+		    high = x + elemPtr->xError.valueArr[i];
+		    low = x - elemPtr->xError.valueArr[i];
+		} else {
+		    high = elemPtr->xHigh.valueArr[i];
+		    low = elemPtr->xLow.valueArr[i];
+		}
+		if ((FINITE(high)) && (FINITE(low)))  {
+		    Point2D p, q;
+
+		    p = Blt_Map2D(graphPtr, high, y, &elemPtr->axes);
+		    q = Blt_Map2D(graphPtr, low, y, &elemPtr->axes);
+		    segPtr->p = p;
+		    segPtr->q = q;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		    /* Left cap */
+		    segPtr->p.x = segPtr->q.x = p.x;
+		    segPtr->p.y = p.y - stylePtr->errorBarCapWidth;
+		    segPtr->q.y = p.y + stylePtr->errorBarCapWidth;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		    /* Right cap */
+		    segPtr->p.x = segPtr->q.x = q.x;
+		    segPtr->p.y = q.y - stylePtr->errorBarCapWidth;
+		    segPtr->q.y = q.y + stylePtr->errorBarCapWidth;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		}
+	    }
+	}
+	elemPtr->xErrorBars = errorBars;
+	elemPtr->xErrorBarCnt = segPtr - errorBars;
+	elemPtr->xErrorToData = errorToData;
+    }
+    if (elemPtr->yError.nValues > 0) {
+	n = MIN(elemPtr->yError.nValues, nPoints);
+    } else {
+	n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, nPoints);
+    }
+    if (n > 0) {
+	Segment2D *errorBars;
+	Segment2D *segPtr;
+	double high, low;
+	double x, y;
+	int *errorToData;
+	int *indexPtr;
+	register int i;
+		
+	segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D));
+	indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int));
+	for (i = 0; i < n; i++) {
+	    x = elemPtr->x.valueArr[i];
+	    y = elemPtr->y.valueArr[i];
+	    stylePtr = dataToStyle[i];
+	    if ((FINITE(x)) && (FINITE(y))) {
+		if (elemPtr->yError.nValues > 0) {
+		    high = y + elemPtr->yError.valueArr[i];
+		    low = y - elemPtr->yError.valueArr[i];
+		} else {
+		    high = elemPtr->yHigh.valueArr[i];
+		    low = elemPtr->yLow.valueArr[i];
+		}
+		if ((FINITE(high)) && (FINITE(low)))  {
+		    Point2D p, q;
+		    
+		    p = Blt_Map2D(graphPtr, x, high, &elemPtr->axes);
+		    q = Blt_Map2D(graphPtr, x, low, &elemPtr->axes);
+		    segPtr->p = p;
+		    segPtr->q = q;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		    /* Top cap. */
+		    segPtr->p.y = segPtr->q.y = p.y;
+		    segPtr->p.x = p.x - stylePtr->errorBarCapWidth;
+		    segPtr->q.x = p.x + stylePtr->errorBarCapWidth;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		    /* Bottom cap. */
+		    segPtr->p.y = segPtr->q.y = q.y;
+		    segPtr->p.x = q.x - stylePtr->errorBarCapWidth;
+		    segPtr->q.x = q.x + stylePtr->errorBarCapWidth;
+		    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+			segPtr++;
+			*indexPtr++ = i;
+		    }
+		}
+	    }
+	}
+	elemPtr->yErrorBars = errorBars;
+	elemPtr->yErrorBarCnt = segPtr - errorBars;
+	elemPtr->yErrorToData = errorToData;
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetIndex --
+ *
+ *	Given a string representing the index of a pair of x,y
+ *	coordinates, return the numeric index.
+ *
+ * Results:
+ *     	A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetIndex(interp, elemPtr, string, indexPtr)
+    Tcl_Interp *interp;
+    Element *elemPtr;
+    char *string;
+    int *indexPtr;
+{
+    long ielem;
+    int last;
+
+    last = NumberOfPoints(elemPtr) - 1;
+    if ((*string == 'e') && (strcmp("end", string) == 0)) {
+	ielem = last;
+    } else if (Tcl_ExprLong(interp, string, &ielem) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    *indexPtr = (int)ielem;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameToElement --
+ *
+ *	Find the element represented the given name,  returning
+ *	a pointer to its data structure via elemPtrPtr.
+ *
+ * Results:
+ *     	A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NameToElement(graphPtr, name, elemPtrPtr)
+    Graph *graphPtr;
+    char *name;
+    Element **elemPtrPtr;
+{
+    Blt_HashEntry *hPtr;
+
+    if (name == NULL) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_FindHashEntry(&graphPtr->elements.table, name);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(graphPtr->interp, "can't find element \"", name,
+	    "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *elemPtrPtr = (Element *)Blt_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyElement --
+ *
+ *	Add a new element to the graph.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyElement(graphPtr, elemPtr)
+    Graph *graphPtr;
+    Element *elemPtr;
+{
+    Blt_ChainLink *linkPtr;
+
+    Blt_DeleteBindings(graphPtr->bindTable, elemPtr);
+    Blt_LegendRemoveElement(graphPtr->legend, elemPtr);
+
+    Tk_FreeOptions(elemPtr->specsPtr, (char *)elemPtr, graphPtr->display, 0);
+    /*
+     * Call the element's own destructor to release the memory and
+     * resources allocated for it.
+     */
+    (*elemPtr->procsPtr->destroyProc) (graphPtr, elemPtr);
+
+    /* Remove it also from the element display list */
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+	    Blt_ChainDeleteLink(graphPtr->elements.displayList, linkPtr);
+	    if (!elemPtr->hidden) {
+		graphPtr->flags |= RESET_WORLD;
+		Blt_EventuallyRedrawGraph(graphPtr);
+	    }
+	    break;
+	}
+    }
+    /* Remove the element for the graph's hash table of elements */
+    if (elemPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&graphPtr->elements.table, elemPtr->hashPtr);
+    }
+    if (elemPtr->name != NULL) {
+	Blt_Free(elemPtr->name);
+    }
+    Blt_Free(elemPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateElement --
+ *
+ *	Add a new element to the graph.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateElement(graphPtr, interp, argc, argv, classUid)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    Blt_Uid classUid;
+{
+    Element *elemPtr;
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    if (argv[3][0] == '-') {
+	Tcl_AppendResult(graphPtr->interp, "name of element \"", argv[3], 
+			 "\" can't start with a '-'", (char *)NULL);
+	return TCL_ERROR;
+    }
+    hPtr = Blt_CreateHashEntry(&graphPtr->elements.table, argv[3], &isNew);
+    if (!isNew) {
+	Tcl_AppendResult(interp, "element \"", argv[3],
+	    "\" already exists in \"", argv[0], "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (classUid == bltBarElementUid) {
+	elemPtr = Blt_BarElement(graphPtr, argv[3], classUid);
+    } else { 
+	/* Stripcharts are line graphs with some options enabled. */	
+	elemPtr = Blt_LineElement(graphPtr, argv[3], classUid);
+    }
+    elemPtr->hashPtr = hPtr;
+    Blt_SetHashValue(hPtr, elemPtr);
+
+    if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, elemPtr->name,
+	    "Element", elemPtr->specsPtr, argc - 4, argv + 4, 
+		(char *)elemPtr, 0) != TCL_OK) {
+	DestroyElement(graphPtr, elemPtr);
+	return TCL_ERROR;
+    }
+    (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr);
+    Blt_ChainPrepend(graphPtr->elements.displayList, elemPtr);
+
+    if (!elemPtr->hidden) {
+	/* If the new element isn't hidden then redraw the graph.  */
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+    elemPtr->flags |= MAP_ITEM;
+    graphPtr->flags |= RESET_AXES;
+    Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildDisplayList --
+ *
+ *	Given a Tcl list of element names, this procedure rebuilds the
+ *	display list, ignoring invalid element names. This list describes
+ *	not only only which elements to draw, but in what order.  This is
+ *	only important for bar and pie charts.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  Only if the Tcl list
+ *	can not be split, a TCL_ERROR is returned and interp->result contains
+ *	an error message.
+ *
+ * Side effects:
+ *	The graph is eventually redrawn using the new display list.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RebuildDisplayList(graphPtr, newList)
+    Graph *graphPtr;		/* Graph widget record */
+    char *newList;		/* Tcl list of element names */
+{
+    int nNames;			/* Number of names found in Tcl name list */
+    char **nameArr;		/* Broken out array of element names */
+    register int i;
+    Element *elemPtr;		/* Element information record */
+
+    if (Tcl_SplitList(graphPtr->interp, newList, &nNames, &nameArr) != TCL_OK) {
+	Tcl_AppendResult(graphPtr->interp, "can't split name list \"", newList,
+	    "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Clear the display list and mark all elements as hidden.  */
+    Blt_ChainReset(graphPtr->elements.displayList);
+
+    /* Rebuild the display list, checking that each name it exists
+     * (currently ignoring invalid element names).  */
+    for (i = 0; i < nNames; i++) {
+	if (NameToElement(graphPtr, nameArr[i], &elemPtr) == TCL_OK) {
+	    Blt_ChainAppend(graphPtr->elements.displayList, elemPtr);
+	}
+    }
+    Blt_Free(nameArr);
+    graphPtr->flags |= RESET_WORLD;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    Tcl_ResetResult(graphPtr->interp);
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyElements --
+ *
+ *	Removes all the graph's elements. This routine is called when
+ *	the graph is destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory allocated for the graph's elements is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyElements(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Element *elemPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	elemPtr = (Element *)Blt_GetHashValue(hPtr);
+	elemPtr->hashPtr = NULL;
+	DestroyElement(graphPtr, elemPtr);
+    }
+    Blt_DeleteHashTable(&graphPtr->elements.table);
+    Blt_DeleteHashTable(&graphPtr->elements.tagTable);
+    Blt_ChainDestroy(graphPtr->elements.displayList);
+}
+
+void
+Blt_MapElements(graphPtr)
+    Graph *graphPtr;
+{
+    Element *elemPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (graphPtr->mode != MODE_INFRONT) {
+	Blt_ResetStacks(graphPtr);
+    }
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (elemPtr->hidden) {
+	    continue;
+	}
+	if ((graphPtr->flags & MAP_ALL) || (elemPtr->flags & MAP_ITEM)) {
+	    (*elemPtr->procsPtr->mapProc) (graphPtr, elemPtr);
+	    elemPtr->flags &= ~MAP_ITEM;
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawElements --
+ *
+ *	Calls the individual element drawing routines for each
+ *	element.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Elements are drawn into the drawable (pixmap) which will
+ *	eventually be displayed in the graph window.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawElements(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (!elemPtr->hidden) {
+	    (*elemPtr->procsPtr->drawNormalProc) (graphPtr, drawable, elemPtr);
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawActiveElements --
+ *
+ *	Calls the individual element drawing routines to display
+ *	the active colors for each element.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Elements are drawn into the drawable (pixmap) which will
+ *	eventually be displayed in the graph window.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawActiveElements(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+	    (*elemPtr->procsPtr->drawActiveProc) (graphPtr, drawable, elemPtr);
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_ElementsToPostScript --
+ *
+ *	Generates PostScript output for each graph element in the
+ *	element display list.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_ElementsToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (!elemPtr->hidden) {
+	    /* Comment the PostScript to indicate the start of the element */
+	    Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n", 
+		elemPtr->name);
+	    (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken, elemPtr);
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_ActiveElementsToPostScript --
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_ActiveElementsToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+	    Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n",
+		elemPtr->name);
+	    (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken, elemPtr);
+	}
+    }
+}
+
+int
+Blt_GraphUpdateNeeded(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (elemPtr->hidden) {
+	    continue;
+	}
+	/* Check if the x or y vectors have notifications pending */
+	if ((Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+	    (Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ *	Marks data points of elements (given by their index) as active.
+ *
+ * Results:
+ *	Returns TCL_OK if no errors occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ActivateOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;			/* Number of element names */
+    char **argv;		/* List of element names */
+{
+    Element *elemPtr;
+    register int i;
+    int *activeArr;
+    int nActiveIndices;
+
+    if (argc == 3) {
+	register Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+
+	/* List all the currently active elements */
+	for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    elemPtr = (Element *)Blt_GetHashValue(hPtr);
+	    if (elemPtr->flags & ELEM_ACTIVE) {
+		Tcl_AppendElement(graphPtr->interp, elemPtr->name);
+	    }
+	}
+	return TCL_OK;
+    }
+    if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+	return TCL_ERROR;	/* Can't find named element */
+    }
+    elemPtr->flags |= ELEM_ACTIVE | ACTIVE_PENDING;
+
+    activeArr = NULL;
+    nActiveIndices = -1;
+    if (argc > 4) {
+	register int *activePtr;
+
+	nActiveIndices = argc - 4;
+	activePtr = activeArr = Blt_Malloc(sizeof(int) * nActiveIndices);
+	assert(activeArr);
+	for (i = 4; i < argc; i++) {
+	    if (GetIndex(interp, elemPtr, argv[i], activePtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    activePtr++;
+	}
+    }
+    if (elemPtr->activeIndices != NULL) {
+	Blt_Free(elemPtr->activeIndices);
+    }
+    elemPtr->nActiveIndices = nActiveIndices;
+    elemPtr->activeIndices = activeArr;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+ClientData
+Blt_MakeElementTag(graphPtr, tagName)
+    Graph *graphPtr;
+    char *tagName;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    hPtr = Blt_CreateHashEntry(&graphPtr->elements.tagTable, tagName, &isNew);
+    assert(hPtr);
+    return Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *	.g element bind elemName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    if (argc == 3) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	char *tagName;
+
+	for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+	    Tcl_AppendElement(interp, tagName);
+	}
+	return TCL_OK;
+    }
+    return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+	Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ *	Add a new element to the graph (using the default type of the
+ *	graph).
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateOp(graphPtr, interp, argc, argv, type)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    Blt_Uid type;
+{
+    return CreateElement(graphPtr, interp, argc, argv, type);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char *argv[];
+{
+    Element *elemPtr;
+
+    if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+	return TCL_ERROR;	/* Can't find named element */
+    }
+    if (Tk_ConfigureValue(interp, graphPtr->tkwin, elemPtr->specsPtr,
+	    (char *)elemPtr, argv[4], 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestOp --
+ *
+ *	Find the element closest to the specified screen coordinates.
+ *	Options:
+ *	-halo		Consider points only with this maximum distance
+ *			from the picked coordinate.
+ *	-interpolate	Find closest point along element traces, not just
+ *			data points.
+ *	-along
+ *
+ * Results:
+ *	A standard Tcl result. If an element could be found within
+ *	the halo distance, the interpreter result is "1", otherwise
+ *	"0".  If a closest element exists, the designated Tcl array
+ *	variable will be set with the following information:
+ *
+ *	1) the element name,
+ *	2) the index of the closest point,
+ *	3) the distance (in screen coordinates) from the picked X-Y
+ *	   coordinate and the closest point,
+ *	4) the X coordinate (graph coordinate) of the closest point,
+ *	5) and the Y-coordinate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tk_ConfigSpec closestSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-halo", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(ClosestSearch, halo), 0, &bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-interpolate", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(ClosestSearch, mode), 0 }, 
+    {TK_CONFIG_CUSTOM, "-along", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(ClosestSearch, along), 0, &alongOption},
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+static int
+ClosestOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget */
+    Tcl_Interp *interp;		/* Interpreter to report results to */
+    int argc;			/* Number of element names */
+    char **argv;		/* List of element names */
+{
+    Element *elemPtr;
+    ClosestSearch search;
+    int i, x, y;
+    int flags = TCL_LEAVE_ERR_MSG;
+    int found;
+
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &x) != TCL_OK) {
+	Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Tk_GetPixels(interp, graphPtr->tkwin, argv[4], &y) != TCL_OK) {
+	Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (graphPtr->inverted) {
+	int temp;
+
+	temp = x, x = y, y = temp;
+    }
+    for (i = 6; i < argc; i += 2) {	/* Count switches-value pairs */
+	if ((argv[i][0] != '-') || 
+	    ((argv[i][1] == '-') && (argv[i][2] == '\0'))) {
+	    break;
+	}
+    }
+    if (i > argc) {
+	i = argc;
+    }
+
+    search.mode = SEARCH_POINTS;
+    search.halo = graphPtr->halo;
+    search.index = -1;
+    search.along = SEARCH_BOTH;
+    search.x = x;
+    search.y = y;
+
+    if (Tk_ConfigureWidget(interp, graphPtr->tkwin, closestSpecs, i - 6,
+	    argv + 6, (char *)&search, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	return TCL_ERROR;	/* Error occurred processing an option. */
+    }
+    if ((i < argc) && (argv[i][0] == '-')) {
+	i++;			/* Skip "--" */
+    }
+    search.dist = (double)(search.halo + 1);
+
+    if (i < argc) {
+	Blt_ChainLink *linkPtr;
+
+	for ( /* empty */ ; i < argc; i++) {
+	    if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+		return TCL_ERROR;	/* Can't find named element */
+	    }
+	    found = FALSE;
+	    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+		 linkPtr == NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+		if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+		    found = TRUE;
+		    break;
+		}
+	    }
+	    if ((!found) || (elemPtr->hidden)) {
+		Tcl_AppendResult(interp, "element \"", argv[i], "\" is hidden",
+			(char *)NULL);
+		return TCL_ERROR;	/* Element isn't visible */
+	    }
+	    /* Check if the X or Y vectors have notifications pending */
+	    if ((elemPtr->flags & MAP_ITEM) ||
+		(Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+		(Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+		continue;
+	    }
+	    (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+	}
+    } else {
+	Blt_ChainLink *linkPtr;
+
+	/* 
+	 * Find the closest point from the set of displayed elements,
+	 * searching the display list from back to front.  That way if
+	 * the points from two different elements overlay each other
+	 * exactly, the last one picked will be the topmost.  
+	 */
+	for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+	    linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	    elemPtr = Blt_ChainGetValue(linkPtr);
+	    /* Check if the X or Y vectors have notifications pending */
+	    if ((elemPtr->hidden) || 
+		(elemPtr->flags & MAP_ITEM) ||
+		(Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+		(Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+		continue;
+	    }
+	    (*elemPtr->procsPtr->closestProc)(graphPtr, elemPtr, &search);
+	}
+
+    }
+    if (search.dist < (double)search.halo) {
+	char string[200];
+	/*
+	 *  Return an array of 5 elements
+	 */
+	if (Tcl_SetVar2(interp, argv[5], "name",
+		search.elemPtr->name, flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	sprintf(string, "%d", search.index);
+	if (Tcl_SetVar2(interp, argv[5], "index", string, flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_PrintDouble(interp, search.point.x, string);
+	if (Tcl_SetVar2(interp, argv[5], "x", string, flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_PrintDouble(interp, search.point.y, string);
+	if (Tcl_SetVar2(interp, argv[5], "y", string, flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_PrintDouble(interp, search.dist, string);
+	if (Tcl_SetVar2(interp, argv[5], "dist", string, flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_SetResult(interp, "1", TCL_STATIC);
+    } else {
+	if (Tcl_SetVar2(interp, argv[5], "name", "", flags) == NULL) {
+	    return TCL_ERROR;
+	}
+	Tcl_SetResult(interp, "0", TCL_STATIC);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Sets the element specifications by the given the command line
+ *	arguments and calls the element specification configuration
+ *	routine. If zero or one command line options are given, only
+ *	information about the option(s) is returned in interp->result.
+ *	If the element configuration has changed and the element is
+ *	currently displayed, the axis limits are updated and
+ *	recomputed.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new display list.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char *argv[];
+{
+    Element *elemPtr;
+    int flags;
+    int numNames, numOpts;
+    char **options;
+    register int i;
+
+    /* Figure out where the option value pairs begin */
+    argc -= 3;
+    argv += 3;
+    for (i = 0; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    break;
+	}
+	if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+	    return TCL_ERROR;	/* Can't find named element */
+	}
+    }
+    numNames = i;		/* Number of element names specified */
+    numOpts = argc - i;		/* Number of options specified */
+    options = argv + numNames;	/* Start of options in argv  */
+
+    for (i = 0; i < numNames; i++) {
+	NameToElement(graphPtr, argv[i], &elemPtr);
+	flags = TK_CONFIG_ARGV_ONLY;
+	if (numOpts == 0) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
+		elemPtr->specsPtr, (char *)elemPtr, (char *)NULL, flags);
+	} else if (numOpts == 1) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
+		elemPtr->specsPtr, (char *)elemPtr, options[0], flags);
+	}
+	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->specsPtr, 
+		numOpts, options, (char *)elemPtr, flags) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((*elemPtr->procsPtr->configProc) (graphPtr, elemPtr) != TCL_OK) {
+	    return TCL_ERROR;	/* Failed to configure element */
+	}
+	if (Blt_ConfigModified(elemPtr->specsPtr, "-hide", (char *)NULL)) {
+	    graphPtr->flags |= RESET_AXES;
+	    elemPtr->flags |= MAP_ITEM;
+	}
+	/* If data points or axes have changed, reset the axes (may
+	 * affect autoscaling) and recalculate the screen points of
+	 * the element. */
+
+	if (Blt_ConfigModified(elemPtr->specsPtr, "-*data", "-map*", "-x",
+		       "-y", (char *)NULL)) {
+	    graphPtr->flags |= RESET_WORLD;
+	    elemPtr->flags |= MAP_ITEM;
+	}
+	/* The new label may change the size of the legend */
+	if (Blt_ConfigModified(elemPtr->specsPtr, "-label", (char *)NULL)) {
+	    graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+	}
+    }
+    /* Update the pixmap if any configuration option changed */
+    graphPtr->flags |= (REDRAW_BACKING_STORE | DRAW_MARGINS);
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeactivateOp --
+ *
+ *	Clears the active bit for the named elements.
+ *
+ * Results:
+ *	Returns TCL_OK if no errors occurred.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeactivateOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget */
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;			/* Number of element names */
+    char **argv;		/* List of element names */
+{
+    Element *elemPtr;
+    register int i;
+
+    for (i = 3; i < argc; i++) {
+	if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+	    return TCL_ERROR;	/* Can't find named element */
+	}
+	elemPtr->flags &= ~ELEM_ACTIVE;
+	if (elemPtr->activeIndices != NULL) {
+	    Blt_Free(elemPtr->activeIndices);
+	    elemPtr->activeIndices = NULL;
+	}
+	elemPtr->nActiveIndices = 0;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Delete the named elements from the graph.
+ *
+ * Results:
+ *	TCL_ERROR is returned if any of the named elements can not be
+ *	found.  Otherwise TCL_OK is returned;
+ *
+ * Side Effects:
+ *	If the element is currently displayed, the plotting area of
+ *	the graph is redrawn. Memory and resources allocated by the
+ *	elements are released.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget */
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;			/* Number of element names */
+    char **argv;		/* List of element names */
+{
+    Element *elemPtr;
+    register int i;
+
+    for (i = 3; i < argc; i++) {
+	if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+	    return TCL_ERROR;	/* Can't find named element */
+	}
+	DestroyElement(graphPtr, elemPtr);
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ *	Indicates if the named element exists in the graph.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The interpreter
+ *	result will contain "1" or "0".
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ExistsOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&graphPtr->elements.table, argv[3]);
+    Blt_SetBooleanResult(interp, (hPtr != NULL));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * 	Returns the name of the picked element (using the element
+ *	bind operation).  Right now, the only name accepted is
+ *	"current".
+ *
+ * Results:
+ *	A standard Tcl result.  The interpreter result will contain
+ *	the name of the element.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char *argv[];
+{
+    register Element *elemPtr;
+
+    if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
+	elemPtr = (Element *)Blt_GetCurrentItem(graphPtr->bindTable);
+	/* Report only on elements. */
+	if ((elemPtr != NULL) && 
+	    ((elemPtr->classUid == bltBarElementUid) ||
+	    (elemPtr->classUid == bltLineElementUid) ||
+	    (elemPtr->classUid == bltStripElementUid))) {
+	    Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ *	Returns the names of the elements is the graph matching
+ *	one of more patterns provided.  If no pattern arguments
+ *	are given, then all element names will be returned.
+ *
+ * Results:
+ *	The return value is a standard Tcl result. The interpreter
+ *	result will contain a Tcl list of the element names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NamesOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Element *elemPtr;
+    Blt_HashSearch cursor;
+    register Blt_HashEntry *hPtr;
+    register int i;
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	elemPtr = (Element *)Blt_GetHashValue(hPtr);
+	if (argc == 3) {
+	    Tcl_AppendElement(graphPtr->interp, elemPtr->name);
+	    continue;
+	}
+	for (i = 3; i < argc; i++) {
+	    if (Tcl_StringMatch(elemPtr->name, argv[i])) {
+		Tcl_AppendElement(interp, elemPtr->name);
+		break;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ *	Queries or resets the element display list.
+ *
+ * Results:
+ *	The return value is a standard Tcl result. The interpreter
+ *	result will contain the new display list of element names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShowOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Element *elemPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (argc == 4) {
+	if (RebuildDisplayList(graphPtr, argv[3]) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	Tcl_AppendElement(interp, elemPtr->name);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ *	Returns the name of the type of the element given by some
+ *	element name.
+ *
+ * Results:
+ *	A standard Tcl result. Returns the type of the element in
+ *	interp->result. If the identifier given doesn't represent an
+ *	element, then an error message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;		/* Element name */
+{
+    Element *elemPtr;
+
+    if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) {
+	return TCL_ERROR;	/* Can't find named element */
+    }
+    Tcl_SetResult(interp, elemPtr->classUid, TCL_STATIC);
+    return TCL_OK;
+}
+
+/*
+ * Global routines:
+ */
+static Blt_OpSpec elemOps[] =
+{
+    {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?elemName? ?index...?",},
+    {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
+    {"cget", 2, (Blt_Op)CgetOp, 5, 5, "elemName option",},
+    {"closest", 2, (Blt_Op)ClosestOp, 6, 0,
+	"x y varName ?option value?... ?elemName?...",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+	"elemName ?elemName?... ?option value?...",},
+    {"create", 2, (Blt_Op)CreateOp, 4, 0, "elemName ?option value?...",},
+    {"deactivate", 3, (Blt_Op)DeactivateOp, 3, 0, "?elemName?...",},
+    {"delete", 3, (Blt_Op)DeleteOp, 3, 0, "?elemName?...",},
+    {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "elemName",},
+    {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+    {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+    {"show", 1, (Blt_Op)ShowOp, 3, 4, "?elemList?",},
+    {"type", 1, (Blt_Op)TypeOp, 4, 4, "elemName",},
+};
+static int numElemOps = sizeof(elemOps) / sizeof(Blt_OpSpec);
+
+
+/*
+ * ----------------------------------------------------------------
+ *
+ * Blt_ElementOp --
+ *
+ *	This procedure is invoked to process the Tcl command that
+ *	corresponds to a widget managed by this module.  See the user
+ *	documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * ----------------------------------------------------------------
+ */
+int
+Blt_ElementOp(graphPtr, interp, argc, argv, type)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* # arguments */
+    char **argv;		/* Argument list */
+    Blt_Uid type;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, numElemOps, elemOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    if (proc == CreateOp) {
+	result = CreateOp(graphPtr, interp, argc, argv, type);
+    } else {
+	result = (*proc) (graphPtr, interp, argc, argv);
+    }
+    return result;
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrElem.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrElem.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrElem.h	(revision 175)
@@ -0,0 +1,295 @@
+/*
+ * bltGrElem.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_ELEM_H
+#define _BLT_GR_ELEM_H
+
+#define SEARCH_X	0
+#define SEARCH_Y	1
+#define SEARCH_BOTH	2
+
+#define SHOW_NONE	0
+#define SHOW_X		1
+#define SHOW_Y		2
+#define SHOW_BOTH	3
+
+#define SEARCH_POINTS	0	/* Search for closest data point. */
+#define SEARCH_TRACES	1	/* Search for closest point on trace. 
+				 * Interpolate the connecting line segments
+				 * if necessary. */
+#define SEARCH_AUTO	2	/* Automatically determine whether to search
+				 * for data points or traces.  Look for
+				 * traces if the linewidth is > 0 and if 
+				 * there is more than one data point. */
+
+#define	ELEM_ACTIVE	(1<<8)	/* Non-zero indicates that the element
+				 * should be drawn in its active
+				 * foreground and background
+				 * colors. */
+#define	ACTIVE_PENDING	(1<<7)
+
+#define	LABEL_ACTIVE 	(1<<9)	/* Non-zero indicates that the
+				 * element's entry in the legend
+				 * should be drawn in its active
+				 * foreground and background
+				 * colors. */
+#define SCALE_SYMBOL	(1<<10)
+
+#define NumberOfPoints(e)	MIN((e)->x.nValues, (e)->y.nValues)
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Weight --
+ *
+ *	Designates a range of values by a minimum and maximum limit.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    double min, max, range;
+} Weight;
+
+#define SetRange(l) \
+	((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON)
+#define SetScale(l) \
+	((l).scale = 1.0 / (l).range)
+#define SetWeight(l, lo, hi) \
+	((l).min = (lo), (l).max = (hi), SetRange(l))
+
+/* 
+ * An element has one or more vectors plus several attributes, such as
+ * line style, thickness, color, and symbol type.  It has an
+ * identifier which distinguishes it among the list of all elements.  
+ */
+typedef struct {
+    Weight weight;		/* Weight range where this pen is valid. */
+
+    Pen *penPtr;		/* Pen to use. */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int errorBarCapWidth;	/* Length of the cap ends on each
+				 * error bar. */
+
+    int symbolSize;		/* Size of the pen's symbol scaled to
+				 * the current graph size. */
+} PenStyle;
+
+
+typedef struct {
+    XColor *color;		/* Color of error bar */
+    int lineWidth;		/* Width of the error bar segments. */
+    GC gc;
+    int show;			/* Flags for errorbars: none, x, y, or both */
+
+} ErrorBarAttributes;
+
+typedef struct {
+    int halo;			/* Maximal distance a candidate point
+				 * can be from the sample window
+				 * coordinate */
+
+    int mode;			/* Indicates whether to find the closest 
+				 * data point or the closest point on the 
+				 * trace by interpolating the line segments.
+				 * Can also be SEARCH_AUTO, indicating to 
+				 * choose how to search.*/
+
+    int x, y;			/* Screen coordinates of test point */
+
+    int along;			/* Indicates to let search run along a 
+				 * particular axis: x, y, or both. */
+
+    /* Output */
+    Element *elemPtr;		/* Name of the closest element */
+
+    Point2D point;		/* Graph coordinates of closest point */
+
+    int index;			/* Index of closest data point */
+
+    double dist;		/* Distance in screen coordinates */
+
+} ClosestSearch;
+
+typedef void (ElementDrawProc) _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+	Element *elemPtr));
+typedef void (ElementToPostScriptProc) _ANSI_ARGS_((Graph *graphPtr, 
+	PsToken psToken, Element *elemPtr));
+typedef void (ElementDestroyProc) _ANSI_ARGS_((Graph *graphPtr, 
+	Element *elemPtr));
+typedef int (ElementConfigProc) _ANSI_ARGS_((Graph *graphPtr, 
+	Element *elemPtr));
+typedef void (ElementMapProc) _ANSI_ARGS_((Graph *graphPtr,
+	Element *elemPtr));
+typedef void (ElementExtentsProc) _ANSI_ARGS_((Element *elemPtr,
+	Extents2D *extsPtr));
+typedef void (ElementClosestProc) _ANSI_ARGS_((Graph *graphPtr, 
+	Element *elemPtr, ClosestSearch *searchPtr));
+typedef void (ElementDrawSymbolProc) _ANSI_ARGS_((Graph *graphPtr,
+	Drawable drawable, Element *elemPtr, int x, int y, int symbolSize));
+typedef void (ElementSymbolToPostScriptProc) _ANSI_ARGS_((Graph *graphPtr,
+	PsToken psToken, Element *elemPtr, double x, double y, int symSize));
+
+typedef struct {
+    ElementClosestProc *closestProc;
+    ElementConfigProc *configProc;
+    ElementDestroyProc *destroyProc;
+    ElementDrawProc *drawActiveProc;
+    ElementDrawProc *drawNormalProc;
+    ElementDrawSymbolProc *drawSymbolProc;
+    ElementExtentsProc *extentsProc;
+    ElementToPostScriptProc *printActiveProc;
+    ElementToPostScriptProc *printNormalProc;
+    ElementSymbolToPostScriptProc *printSymbolProc;
+    ElementMapProc *mapProc;
+} ElementProcs;
+
+/* 
+ * The data structure below contains information pertaining to a line
+ * vector.  It consists of an array of floating point data values and
+ * for convenience, the number and minimum/maximum values.  
+ */
+
+typedef struct {
+    Blt_Vector *vecPtr;
+
+    double *valueArr;
+
+    int nValues;
+
+    int arraySize;
+
+    double min, max;
+
+    Blt_VectorId clientId;	/* If non-NULL, a client token identifying the
+				 * external vector. */
+
+    Element *elemPtr;		/* Element associated with vector. */
+
+} ElemVector;
+
+
+struct ElementStruct {
+    char *name;			/* Identifier to refer the element.
+				 * Used in the "insert", "delete", or
+				 * "show", commands. */
+
+    Blt_Uid classUid;		/* Type of element */
+
+    Graph *graphPtr;		/* Graph widget of element*/
+
+    unsigned int flags;		/* Indicates if the entire element is
+				 * active, or if coordinates need to
+				 * be calculated */
+
+    char **tags;
+
+    int hidden;			/* If non-zero, don't display the element. */
+
+    Blt_HashEntry *hashPtr;
+
+    char *label;		/* Label displayed in legend */
+
+    int labelRelief;		/* Relief of label in legend. */
+
+    Axis2D axes;		/* X-axis and Y-axis mapping the element */
+
+    ElemVector x, y, w;		/* Contains array of floating point
+				 * graph coordinate values. Also holds
+				 * min/max and the number of
+				 * coordinates */
+
+    ElemVector xError;		/* Relative/symmetric X error values. */
+    ElemVector yError;		/* Relative/symmetric Y error values. */
+    ElemVector xHigh, xLow;	/* Absolute/asymmetric X-coordinate high/low
+				   error values. */
+    ElemVector yHigh, yLow;	/* Absolute/asymmetric Y-coordinate high/low
+				   error values. */
+
+    int *activeIndices;		/* Array of indices (malloc-ed) which
+				 * indicate which data points are
+				 * active (drawn with "active"
+				 * colors). */
+
+    int nActiveIndices;		/* Number of active data points.
+				 * Special case: if nActiveIndices < 0
+				 * and the active bit is set in
+				 * "flags", then all data points are
+				 * drawn active. */
+
+    ElementProcs *procsPtr;
+
+    Tk_ConfigSpec *specsPtr;	/* Configuration specifications. */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int *xErrorToData;		/* Maps error bar segments back to the data
+				 * point. */
+    int *yErrorToData;		/* Maps error bar segments back to the data
+				 * point. */
+
+    int errorBarCapWidth;	/* Length of cap on error bars */
+
+    Pen *activePenPtr;		/* Standard Pens */
+    Pen *normalPenPtr;
+
+    Blt_Chain *palette;		/* Palette of pens. */
+
+    /* Symbol scaling */
+    int scaleSymbols;		/* If non-zero, the symbols will scale
+				 * in size as the graph is zoomed
+				 * in/out.  */
+
+    double xRange, yRange;	/* Initial X-axis and Y-axis ranges:
+				 * used to scale the size of element's
+				 * symbol. */
+    int state;
+};
+
+
+extern double Blt_FindElemVectorMinimum _ANSI_ARGS_((ElemVector *vecPtr,
+	double minLimit));
+extern void Blt_ResizeStatusArray _ANSI_ARGS_((Element *elemPtr, int nPoints));
+extern int Blt_GetPenStyle _ANSI_ARGS_((Graph *graphPtr, char *name,
+	Blt_Uid classUid, PenStyle *stylePtr));
+extern void Blt_FreePalette _ANSI_ARGS_((Graph *graphPtr, Blt_Chain *palette));
+extern PenStyle **Blt_StyleMap _ANSI_ARGS_((Element *elemPtr));
+extern void Blt_MapErrorBars _ANSI_ARGS_((Graph *graphPtr, Element *elemPtr, 
+	       PenStyle **dataToStyle));
+
+#endif /* _BLT_GR_ELEM_H */
Index: trunk/kitgen/8.x/blt/generic/bltGrGrid.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrGrid.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrGrid.c	(revision 175)
@@ -0,0 +1,517 @@
+
+/*
+ * bltGrGrid.c --
+ *
+ *	This module implements grid lines for the BLT graph widget.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Graph widget created by Sani Nassif and George Howlett.
+ */
+
+#include "bltGraph.h"
+
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltAnyXAxisOption;
+extern Tk_CustomOption bltAnyYAxisOption;
+
+
+#define DEF_GRID_DASHES		"dot"
+#define DEF_GRID_FOREGROUND	RGB_GREY64
+#define DEF_GRID_FG_MONO	RGB_BLACK
+#define DEF_GRID_LINE_WIDTH	"0"
+#define DEF_GRID_HIDE_BARCHART	"no"
+#define DEF_GRID_HIDE_GRAPH	"yes"
+#define DEF_GRID_MINOR		"yes"
+#define DEF_GRID_MAP_X_GRAPH	"x"
+#define DEF_GRID_MAP_X_BARCHART	(char *)NULL
+#define DEF_GRID_MAP_Y		"y"
+#define DEF_GRID_POSITION	(char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_GRID_FOREGROUND, Tk_Offset(Grid, colorPtr),
+	TK_CONFIG_COLOR_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_COLOR, "-color", "color", "color",
+	DEF_GRID_FG_MONO, Tk_Offset(Grid, colorPtr),
+	TK_CONFIG_MONO_ONLY | ALL_GRAPHS},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_GRID_DASHES, Tk_Offset(Grid, dashes),
+	TK_CONFIG_NULL_OK | ALL_GRAPHS, &bltDashesOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_GRID_HIDE_BARCHART, Tk_Offset(Grid, hidden), BARCHART},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_GRID_HIDE_GRAPH, Tk_Offset(Grid, hidden), GRAPH | STRIPCHART},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "Linewidth",
+	DEF_GRID_LINE_WIDTH, Tk_Offset(Grid, lineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_GRID_MAP_X_GRAPH, Tk_Offset(Grid, axes.x),
+	GRAPH | STRIPCHART, &bltAnyXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_GRID_MAP_X_BARCHART, Tk_Offset(Grid, axes.x),
+	BARCHART, &bltAnyXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_GRID_MAP_Y, Tk_Offset(Grid, axes.y),
+	ALL_GRAPHS, &bltAnyYAxisOption},
+    {TK_CONFIG_BOOLEAN, "-minor", "minor", "Minor",
+	DEF_GRID_MINOR, Tk_Offset(Grid, minorGrid),
+	TK_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureGrid --
+ *
+ *	Configures attributes of the grid such as line width,
+ *	dashes, and position.  The grid are first turned off
+ *	before any of the attributes changes.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureGrid(graphPtr, gridPtr)
+    Graph *graphPtr;
+    Grid *gridPtr;
+{
+    XGCValues gcValues;
+    unsigned long gcMask;
+    GC newGC;
+
+    gcValues.background = gcValues.foreground = gridPtr->colorPtr->pixel;
+    gcValues.line_width = LineWidth(gridPtr->lineWidth);
+    gcMask = (GCForeground | GCBackground | GCLineWidth);
+    if (LineIsDashed(gridPtr->dashes)) {
+	gcValues.line_style = LineOnOffDash;
+	gcMask |= GCLineStyle;
+    }
+    newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (LineIsDashed(gridPtr->dashes)) {
+	Blt_SetDashes(graphPtr->display, newGC, &(gridPtr->dashes));
+    }
+    if (gridPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, gridPtr->gc);
+    }
+    gridPtr->gc = newGC;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapGrid --
+ *
+ *	Determines the coordinates of the line segments corresponding
+ *	to the grid lines for each axis.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapGrid(graphPtr)
+    Graph *graphPtr;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+    int nSegments;
+    Segment2D *segments;
+
+    if (gridPtr->x.segments != NULL) {
+	Blt_Free(gridPtr->x.segments);
+	gridPtr->x.segments = NULL;
+    }
+    if (gridPtr->y.segments != NULL) {
+	Blt_Free(gridPtr->y.segments);
+	gridPtr->y.segments = NULL;
+    }
+    gridPtr->x.nSegments = gridPtr->y.nSegments = 0;
+    /*
+     * Generate line segments to represent the grid.  Line segments
+     * are calculated from the major tick intervals of each axis mapped.
+     */
+    Blt_GetAxisSegments(graphPtr, gridPtr->axes.x, &segments, &nSegments);
+    if (nSegments > 0) {
+	gridPtr->x.nSegments = nSegments;
+	gridPtr->x.segments = segments;
+    }
+    Blt_GetAxisSegments(graphPtr, gridPtr->axes.y, &segments, &nSegments);
+    if (nSegments > 0) {
+	gridPtr->y.nSegments = nSegments;
+	gridPtr->y.segments = segments;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawGrid --
+ *
+ *	Draws the grid lines associated with each axis.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DrawGrid(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    if (gridPtr->hidden) {
+	return;
+    }
+    if (gridPtr->x.nSegments > 0) {
+	Blt_Draw2DSegments(graphPtr->display, drawable, gridPtr->gc, 
+			 gridPtr->x.segments, gridPtr->x.nSegments);
+    }
+    if (gridPtr->y.nSegments > 0) {
+	Blt_Draw2DSegments(graphPtr->display, drawable, gridPtr->gc,
+	    gridPtr->y.segments, gridPtr->y.nSegments);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GridToPostScript --
+ *
+ *	Prints the grid lines associated with each axis.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GridToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    if (gridPtr->hidden) {
+	return;
+    }
+    Blt_LineAttributesToPostScript(psToken, gridPtr->colorPtr,
+	gridPtr->lineWidth, &(gridPtr->dashes), CapButt, JoinMiter);
+    if (gridPtr->x.nSegments > 0) {
+	Blt_2DSegmentsToPostScript(psToken, gridPtr->x.segments, 
+			gridPtr->x.nSegments);
+    }
+    if (gridPtr->y.nSegments > 0) {
+	Blt_2DSegmentsToPostScript(psToken, gridPtr->y.segments, 
+			gridPtr->y.nSegments);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyGrid --
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Grid GC is released.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyGrid(graphPtr)
+    Graph *graphPtr;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    Tk_FreeOptions(configSpecs, (char *)gridPtr, graphPtr->display,
+	Blt_GraphType(graphPtr));
+    if (gridPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, gridPtr->gc);
+    }
+    if (gridPtr->x.segments != NULL) {
+	Blt_Free(gridPtr->x.segments);
+    }
+    if (gridPtr->y.segments != NULL) {
+	Blt_Free(gridPtr->y.segments);
+    }
+    Blt_Free(gridPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateGrid --
+ *
+ *	Creates and initializes a new grid structure.
+ *
+ * Results:
+ *	Returns TCL_ERROR if the configuration failed, otherwise TCL_OK.
+ *
+ * Side Effects:
+ *	Memory for grid structure is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreateGrid(graphPtr)
+    Graph *graphPtr;
+{
+    Grid *gridPtr;
+
+    gridPtr = Blt_Calloc(1, sizeof(Grid));
+    assert(gridPtr);
+    gridPtr->minorGrid = TRUE;
+    graphPtr->gridPtr = gridPtr;
+
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin, "grid",
+	    "Grid", configSpecs, 0, (char **)NULL, (char *)gridPtr,
+	    Blt_GraphType(graphPtr)) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    ConfigureGrid(graphPtr, gridPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *	Queries configuration attributes of the grid such as line
+ *	width, dashes, and position.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+	(char *)gridPtr, argv[3], Blt_GraphType(graphPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Queries or resets configuration attributes of the grid
+ * 	such as line width, dashes, and position.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Grid attributes are reset.  The graph is redrawn at the
+ *	next idle point.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+    int flags;
+
+    flags = Blt_GraphType(graphPtr) | TK_CONFIG_ARGV_ONLY;
+    if (argc == 3) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+	    (char *)gridPtr, (char *)NULL, flags);
+    } else if (argc == 4) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+	    (char *)gridPtr, argv[3], flags);
+    }
+    if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
+	    argc - 3, argv + 3, (char *)gridPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    ConfigureGrid(graphPtr, gridPtr);
+    graphPtr->flags |= REDRAW_BACKING_STORE;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ *	Maps the grid.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Grid attributes are reset and the graph is redrawn if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    if (gridPtr->hidden) {
+	gridPtr->hidden = FALSE;/* Changes "-hide" configuration option */
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ *	Maps or unmaps the grid (off or on).
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Grid attributes are reset and the graph is redrawn if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UnmapOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    if (!gridPtr->hidden) {
+	gridPtr->hidden = TRUE;	/* Changes "-hide" configuration option */
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ToggleOp --
+ *
+ *	Toggles the state of the grid shown/hidden.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Grid is hidden/displayed. The graph is redrawn at the next
+ *	idle time.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ToggleOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Grid *gridPtr = (Grid *)graphPtr->gridPtr;
+
+    gridPtr->hidden = (!gridPtr->hidden);
+    graphPtr->flags |= REDRAW_BACKING_STORE;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+
+static Blt_OpSpec gridOps[] =
+{
+    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?options...?",},
+    {"off", 2, (Blt_Op)UnmapOp, 3, 3, "",},
+    {"on", 2, (Blt_Op)MapOp, 3, 3, "",},
+    {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "",},
+};
+static int nGridOps = sizeof(gridOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GridOp --
+ *
+ *	User routine to configure grid lines.  Grids are drawn
+ *	at major tick intervals across the graph.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Grid may be drawn in the plotting area.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GridOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+
+    proc = Blt_GetOp(interp, nGridOps, gridOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (graphPtr, interp, argc, argv);
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrHairs.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrHairs.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrHairs.c	(revision 175)
@@ -0,0 +1,544 @@
+
+/*
+ * bltGrHairs.c --
+ *
+ *	This module implements crosshairs for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Graph widget created by Sani Nassif and George Howlett.
+*/
+
+#include "bltGraph.h"
+
+extern Tk_CustomOption bltPointOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltDashesOption;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ *	Contains the line segments positions and graphics context used
+ *	to simulate crosshairs (by XORing) on the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+
+struct CrosshairsStruct {
+
+    XPoint hotSpot;		/* Hot spot for crosshairs */
+    int visible;		/* Internal state of crosshairs. If non-zero,
+				 * crosshairs are displayed. */
+    int hidden;			/* If non-zero, crosshairs are not displayed.
+				 * This is not necessarily consistent with the
+				 * internal state variable.  This is true when
+				 * the hot spot is off the graph.  */
+    Blt_Dashes dashes;		/* Dashstyle of the crosshairs. This represents
+				 * an array of alternatingly drawn pixel
+				 * values. If NULL, the hairs are drawn as a
+				 * solid line */
+    int lineWidth;		/* Width of the simulated crosshair lines */
+    XSegment segArr[2];		/* Positions of line segments representing the
+				 * simulated crosshairs. */
+    XColor *colorPtr;		/* Foreground color of crosshairs */
+    GC gc;			/* Graphics context for crosshairs. Set to
+				 * GXxor to not require redraws of graph */
+};
+
+#define DEF_HAIRS_DASHES	(char *)NULL
+#define DEF_HAIRS_FOREGROUND	RGB_BLACK
+#define DEF_HAIRS_FG_MONO	RGB_BLACK
+#define DEF_HAIRS_LINE_WIDTH	"0"
+#define DEF_HAIRS_HIDE		"yes"
+#define DEF_HAIRS_POSITION	(char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_HAIRS_FOREGROUND, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_HAIRS_FG_MONO, Tk_Offset(Crosshairs, colorPtr), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_HAIRS_DASHES, Tk_Offset(Crosshairs, dashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_HAIRS_HIDE, Tk_Offset(Crosshairs, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "Linewidth",
+	DEF_HAIRS_LINE_WIDTH, Tk_Offset(Crosshairs, lineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-position", "position", "Position",
+	DEF_HAIRS_POSITION, Tk_Offset(Crosshairs, hotSpot),
+	0, &bltPointOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TurnOffHairs --
+ *
+ *	XOR's the existing line segments (representing the crosshairs),
+ *	thereby erasing them.  The internal state of the crosshairs is
+ *	tracked.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Crosshairs are erased.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TurnOffHairs(tkwin, chPtr)
+    Tk_Window tkwin;
+    Crosshairs *chPtr;
+{
+    if (Tk_IsMapped(tkwin) && (chPtr->visible)) {
+	XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin), chPtr->gc,
+	    chPtr->segArr, 2);
+	chPtr->visible = FALSE;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TurnOnHairs --
+ *
+ *	Draws (by XORing) new line segments, creating the effect of
+ *	crosshairs. The internal state of the crosshairs is tracked.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Crosshairs are displayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TurnOnHairs(graphPtr, chPtr)
+    Graph *graphPtr;
+    Crosshairs *chPtr;
+{
+    if (Tk_IsMapped(graphPtr->tkwin) && (!chPtr->visible)) {
+	if (!PointInGraph(graphPtr, chPtr->hotSpot.x, chPtr->hotSpot.y)) {
+	    return;		/* Coordinates are off the graph */
+	}
+	XDrawSegments(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+	    chPtr->gc, chPtr->segArr, 2);
+	chPtr->visible = TRUE;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureCrosshairs --
+ *
+ *	Configures attributes of the crosshairs such as line width,
+ *	dashes, and position.  The crosshairs are first turned off
+ *	before any of the attributes changes.
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ConfigureCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    XGCValues gcValues;
+    unsigned long gcMask;
+    GC newGC;
+    long colorValue;
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    /*
+     * Turn off the crosshairs temporarily. This is in case the new
+     * configuration changes the size, style, or position of the lines.
+     */
+    TurnOffHairs(graphPtr->tkwin, chPtr);
+
+    gcValues.function = GXxor;
+
+    if (graphPtr->plotBg == NULL) {
+	/* The graph's color option may not have been set yet */
+	colorValue = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+    } else {
+	colorValue = graphPtr->plotBg->pixel;
+    }
+    gcValues.background = colorValue;
+    gcValues.foreground = (colorValue ^ chPtr->colorPtr->pixel);
+
+    gcValues.line_width = LineWidth(chPtr->lineWidth);
+    gcMask = (GCForeground | GCBackground | GCFunction | GCLineWidth);
+    if (LineIsDashed(chPtr->dashes)) {
+	gcValues.line_style = LineOnOffDash;
+	gcMask |= GCLineStyle;
+    }
+    newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (LineIsDashed(chPtr->dashes)) {
+	Blt_SetDashes(graphPtr->display, newGC, &(chPtr->dashes));
+    }
+    if (chPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+    }
+    chPtr->gc = newGC;
+
+    /*
+     * Are the new coordinates on the graph?
+     */
+    chPtr->segArr[0].x2 = chPtr->segArr[0].x1 = chPtr->hotSpot.x;
+    chPtr->segArr[0].y1 = graphPtr->bottom;
+    chPtr->segArr[0].y2 = graphPtr->top;
+    chPtr->segArr[1].y2 = chPtr->segArr[1].y1 = chPtr->hotSpot.y;
+    chPtr->segArr[1].x1 = graphPtr->left;
+    chPtr->segArr[1].x2 = graphPtr->right;
+
+    if (!chPtr->hidden) {
+	TurnOnHairs(graphPtr, chPtr);
+    }
+}
+
+void
+Blt_EnableCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    if (!graphPtr->crosshairs->hidden) {
+	TurnOnHairs(graphPtr, graphPtr->crosshairs);
+    }
+}
+
+void
+Blt_DisableCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    if (!graphPtr->crosshairs->hidden) {
+	TurnOffHairs(graphPtr->tkwin, graphPtr->crosshairs);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateCrosshairs --
+ *
+ *	Update the length of the hairs (not the hot spot).
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UpdateCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    chPtr->segArr[0].y1 = graphPtr->bottom;
+    chPtr->segArr[0].y2 = graphPtr->top;
+    chPtr->segArr[1].x1 = graphPtr->left;
+    chPtr->segArr[1].x2 = graphPtr->right;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyCrosshairs --
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    Tk_FreeOptions(configSpecs, (char *)chPtr, graphPtr->display, 0);
+    if (chPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+    }
+    Blt_Free(chPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCrosshairs --
+ *
+ *	Creates and initializes a new crosshair structure.
+ *
+ * Results:
+ *	Returns TCL_ERROR if the crosshair structure can't be created,
+ *	otherwise TCL_OK.
+ *
+ * Side Effects:
+ *	Crosshair GC is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreateCrosshairs(graphPtr)
+    Graph *graphPtr;
+{
+    Crosshairs *chPtr;
+
+    chPtr = Blt_Calloc(1, sizeof(Crosshairs));
+    assert(chPtr);
+    chPtr->hidden = TRUE;
+    chPtr->hotSpot.x = chPtr->hotSpot.y = -1;
+    graphPtr->crosshairs = chPtr;
+
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+	    "crosshairs", "Crosshairs", configSpecs, 0, (char **)NULL,
+	    (char *)chPtr, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *	Queries configuration attributes of the crosshairs such as
+ *	line width, dashes, and position.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+	    (char *)chPtr, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Queries or resets configuration attributes of the crosshairs
+ * 	such as line width, dashes, and position.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Crosshairs are reset.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    if (argc == 3) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)chPtr, (char *)NULL, 0);
+    } else if (argc == 4) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)chPtr, argv[3], 0);
+    }
+    if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+	    argv + 3, (char *)chPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_ConfigureCrosshairs(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OnOp --
+ *
+ *	Maps the crosshairs.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Crosshairs are reset if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OnOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    if (chPtr->hidden) {
+	TurnOnHairs(graphPtr, chPtr);
+	chPtr->hidden = FALSE;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OffOp --
+ *
+ *	Unmaps the crosshairs.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Crosshairs are reset if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    if (!chPtr->hidden) {
+	TurnOffHairs(graphPtr->tkwin, chPtr);
+	chPtr->hidden = TRUE;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ToggleOp --
+ *
+ *	Toggles the state of the crosshairs.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Crosshairs are reset.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ToggleOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Crosshairs *chPtr = graphPtr->crosshairs;
+
+    chPtr->hidden = (chPtr->hidden == 0);
+    if (chPtr->hidden) {
+	TurnOffHairs(graphPtr->tkwin, chPtr);
+    } else {
+	TurnOnHairs(graphPtr, chPtr);
+    }
+    return TCL_OK;
+}
+
+
+static Blt_OpSpec xhairOps[] =
+{
+    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?options...?",},
+    {"off", 2, (Blt_Op)OffOp, 3, 3, "",},
+    {"on", 2, (Blt_Op)OnOp, 3, 3, "",},
+    {"toggle", 1, (Blt_Op)ToggleOp, 3, 3, "",},
+};
+static int nXhairOps = sizeof(xhairOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CrosshairsOp --
+ *
+ *	User routine to configure crosshair simulation.  Crosshairs
+ *	are simulated by drawing line segments parallel to both axes
+ *	using the XOR drawing function. The allows the lines to be
+ *	erased (by drawing them again) without redrawing the entire
+ *	graph.  Care must be taken to erase crosshairs before redrawing
+ *	the graph and redraw them after the graph is redraw.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Crosshairs may be drawn in the plotting area.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CrosshairsOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+
+    proc = Blt_GetOp(interp, nXhairOps, xhairOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (graphPtr, interp, argc, argv);
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrLegd.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrLegd.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrLegd.c	(revision 175)
@@ -0,0 +1,1516 @@
+
+/*
+ * bltGrLegd.c --
+ *
+ *	This module implements the legend for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltGrElem.h"
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Legend --
+ *
+ * 	Contains information specific to how the legend will be
+ *	displayed.
+ *
+ *
+ * -------------------------------------------------------------------
+ */
+struct LegendStruct {
+    unsigned int flags;
+    Blt_Uid classUid;		/* Type: Element or Marker. */
+
+    int hidden;			/* If non-zero, don't display the legend. */
+
+    int raised;			/* If non-zero, draw the legend last, above
+				 * everything else. */
+
+    int nEntries;		/* Number of element entries in table. */
+
+    short int width, height;	/* Dimensions of the legend */
+
+    short int nColumns, nRows;	/* Number of columns and rows in legend */
+
+    int site;
+    Point2D anchorPos;		/* Says how to position the legend. Indicates 
+				 * the site and/or x-y screen coordinates of 
+				 * the legend.  Used in conjunction with the 
+				 * anchor to determine its location. */
+
+    Tk_Anchor anchor;		/* Anchor of legend. Used to interpret the
+				 * positioning point of the legend in the
+				 * graph*/
+
+    int x, y;			/* Computed origin of legend. */
+
+    Graph *graphPtr;
+    Tcl_Command cmdToken;	/* Token for graph's widget command. */
+    int reqColumns, reqRows;
+
+    Blt_Pad ipadX, ipadY;	/* # of pixels padding around legend entries */
+    Blt_Pad padX, padY;		/* # of pixels padding to exterior of legend */
+
+    Tk_Window tkwin;		/* Optional external window to draw legend. */
+
+    TextStyle style;
+
+    int maxSymSize;		/* Size of largest symbol to be displayed.
+				 * Used to calculate size of legend */
+
+    Tk_3DBorder activeBorder;	/* Active legend entry background color. */
+    int activeRelief;		/* 3-D effect on active entry. */
+    int entryBorderWidth;	/* Border width around each entry in legend. */
+
+    Tk_3DBorder border;		/* 3-D effect of legend. */
+    int borderWidth;		/* Width of legend 3-D border */
+    int relief;			/* 3-d effect of border around the legend:
+				 * TK_RELIEF_RAISED etc. */
+
+    Blt_BindTable bindTable;
+};
+
+#define padLeft  	padX.side1
+#define padRight  	padX.side2
+#define padTop		padY.side1
+#define padBottom	padY.side2
+#define PADDING(x)	((x).side1 + (x).side2)
+
+#define DEF_LEGEND_ACTIVE_BACKGROUND 	STD_ACTIVE_BACKGROUND
+#define DEF_LEGEND_ACTIVE_BG_MONO	STD_ACTIVE_BG_MONO
+#define DEF_LEGEND_ACTIVE_BORDERWIDTH  "2"
+#define DEF_LEGEND_ACTIVE_FOREGROUND	STD_ACTIVE_FOREGROUND
+#define DEF_LEGEND_ACTIVE_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_LEGEND_ACTIVE_RELIEF	"flat"
+#define DEF_LEGEND_ANCHOR	   	"n"
+#define DEF_LEGEND_BACKGROUND	   	(char *)NULL
+#define DEF_LEGEND_BG_MONO		(char *)NULL
+#define DEF_LEGEND_BORDERWIDTH 	STD_BORDERWIDTH
+#define DEF_LEGEND_FOREGROUND		STD_NORMAL_FOREGROUND
+#define DEF_LEGEND_FG_MONO		STD_NORMAL_FG_MONO
+#define DEF_LEGEND_FONT			STD_FONT_SMALL
+#define DEF_LEGEND_HIDE			"no"
+#define DEF_LEGEND_IPAD_X		"1"
+#define DEF_LEGEND_IPAD_Y		"1"
+#define DEF_LEGEND_PAD_X		"1"
+#define DEF_LEGEND_PAD_Y		"1"
+#define DEF_LEGEND_POSITION		"rightmargin"
+#define DEF_LEGEND_RAISED       	"no"
+#define DEF_LEGEND_RELIEF		"sunken"
+#define DEF_LEGEND_SHADOW_COLOR		(char *)NULL
+#define DEF_LEGEND_SHADOW_MONO		(char *)NULL
+#define DEF_LEGEND_ROWS			"0"
+#define DEF_LEGEND_COLUMNS		"0"
+
+static Tk_OptionParseProc StringToPosition;
+static Tk_OptionPrintProc PositionToString;
+static Tk_CustomOption legendPositionOption =
+{
+    StringToPosition, PositionToString, (ClientData)0
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltCountOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+	"ActiveBackground", DEF_LEGEND_ACTIVE_BACKGROUND,
+	Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+	"ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
+	Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-activeborderwidth", "activeBorderWidth",
+	"BorderWidth", DEF_LEGEND_BORDERWIDTH, 
+	Tk_Offset(Legend, entryBorderWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+	"ActiveForeground", DEF_LEGEND_ACTIVE_FOREGROUND,
+	Tk_Offset(Legend, style.activeColor), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+	"ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
+	Tk_Offset(Legend, style.activeColor), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+	DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_LEGEND_BACKGROUND, Tk_Offset(Legend, border),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_LEGEND_BORDERWIDTH, Tk_Offset(Legend, borderWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-columns", "columns", "columns",
+	DEF_LEGEND_COLUMNS, Tk_Offset(Legend, reqColumns),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+    {TK_CONFIG_FONT, "-font", "font", "Font",
+	DEF_LEGEND_FONT, Tk_Offset(Legend, style.font), 0},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_LEGEND_FOREGROUND, Tk_Offset(Legend, style.color),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_LEGEND_FG_MONO, Tk_Offset(Legend, style.color),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_LEGEND_HIDE, Tk_Offset(Legend, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "Pad",
+	DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "Pad",
+	DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-padx", "padX", "Pad",
+	DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", "padY", "Pad",
+	DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-position", "position", "Position",
+	DEF_LEGEND_POSITION, 0, 
+        TK_CONFIG_DONT_SET_DEFAULT, &legendPositionOption},
+    {TK_CONFIG_BOOLEAN, "-raised", "raised", "Raised",
+	DEF_LEGEND_RAISED, Tk_Offset(Legend, raised),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-rows", "rows", "rows",
+	DEF_LEGEND_ROWS, Tk_Offset(Legend, reqRows),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltCountOption},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_LEGEND_SHADOW_COLOR, Tk_Offset(Legend, style.shadow),
+	TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_LEGEND_SHADOW_MONO, Tk_Offset(Legend, style.shadow),
+	TK_CONFIG_MONO_ONLY, &bltShadowOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tcl_IdleProc DisplayLegend;
+static Blt_BindPickProc PickLegendEntry;
+static Tk_EventProc LegendEventProc;
+
+extern Tcl_CmdProc Blt_GraphInstCmdProc;
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EventuallyRedrawLegend --
+ *
+ *	Tells the Tk dispatcher to call the graph display routine at
+ *	the next idle point.  This request is made only if the window
+ *	is displayed and no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ *	The window is eventually redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+EventuallyRedrawLegend(legendPtr)
+    Legend *legendPtr;		/* Legend record */
+{
+    if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
+	Tcl_DoWhenIdle(DisplayLegend, legendPtr);
+	legendPtr->flags |= REDRAW_PENDING;
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * LegendEventProc --
+ *
+ *	This procedure is invoked by the Tk dispatcher for various
+ *	events on graphs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the window gets deleted, internal structures get
+ *	cleaned up.  When it gets exposed, the graph is eventually
+ *	redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+LegendEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Legend record */
+    register XEvent *eventPtr;	/* Event which triggered call to routine */
+{
+    Legend *legendPtr = clientData;
+
+    if (eventPtr->type == Expose) {
+	if (eventPtr->xexpose.count == 0) {
+	    EventuallyRedrawLegend(legendPtr);
+	}
+    } else if (eventPtr->type == DestroyNotify) {
+	Graph *graphPtr = legendPtr->graphPtr;
+
+	if (legendPtr->tkwin != graphPtr->tkwin) {
+	    Blt_DeleteWindowInstanceData(legendPtr->tkwin);
+	    if (legendPtr->cmdToken != NULL) {
+		Tcl_DeleteCommandFromToken(graphPtr->interp, 
+					   legendPtr->cmdToken);
+		legendPtr->cmdToken = NULL;
+	    }
+	    legendPtr->tkwin = graphPtr->tkwin;
+	}
+	if (legendPtr->flags & REDRAW_PENDING) {
+	    Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+	    legendPtr->flags &= ~REDRAW_PENDING;
+	}
+	legendPtr->site = LEGEND_RIGHT;
+	graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+	Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
+	Blt_EventuallyRedrawGraph(graphPtr);
+    } else if (eventPtr->type == ConfigureNotify) {
+	EventuallyRedrawLegend(legendPtr);
+    }
+}
+
+static int
+CreateLegendWindow(interp, legendPtr, pathName)
+    Tcl_Interp *interp;
+    Legend *legendPtr;
+    char *pathName;
+{
+    Tk_Window tkwin;
+
+    tkwin = Tk_MainWindow(interp);
+    tkwin = Tk_CreateWindowFromPath(interp, tkwin, pathName, NULL);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    Blt_SetWindowInstanceData(tkwin, legendPtr);
+    Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+	  LegendEventProc, legendPtr);
+    /* Move the legend's binding table to the new window. */
+    Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
+    if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+	Tk_DestroyWindow(legendPtr->tkwin);
+    }
+    legendPtr->cmdToken = Tcl_CreateCommand(interp, pathName, 
+	Blt_GraphInstCmdProc, legendPtr->graphPtr, NULL);
+    legendPtr->tkwin = tkwin;
+    legendPtr->site = LEGEND_WINDOW;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPosition --
+ *
+ *	Convert the string representation of a legend XY position into
+ *	window coordinates.  The form of the string must be "@x,y" or
+ *	none.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The symbol type is
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPosition(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* New legend position string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset to XPoint structure */
+{
+    Legend *legendPtr = (Legend *)widgRec;
+    char c;
+    unsigned int length;
+
+    c = string[0];
+    length = strlen(string);
+
+    if ((string == NULL) || (*string == '\0')) {
+	legendPtr->site = LEGEND_RIGHT;
+    } else if ((c == 'l') && (strncmp(string, "leftmargin", length) == 0)) {
+	legendPtr->site = LEGEND_LEFT;
+    } else if ((c == 'r') && (strncmp(string, "rightmargin", length) == 0)) {
+	legendPtr->site = LEGEND_RIGHT;
+    } else if ((c == 't') && (strncmp(string, "topmargin", length) == 0)) {
+	legendPtr->site = LEGEND_TOP;
+    } else if ((c == 'b') && (strncmp(string, "bottommargin", length) == 0)) {
+	legendPtr->site = LEGEND_BOTTOM;
+    } else if ((c == 'p') && (strncmp(string, "plotarea", length) == 0)) {
+	legendPtr->site = LEGEND_PLOT;
+    } else if (c == '@') {
+	char *comma;
+	long x, y;
+	int result;
+	
+	comma = strchr(string + 1, ',');
+	if (comma == NULL) {
+	    Tcl_AppendResult(interp, "bad screen position \"", string,
+			     "\": should be @x,y", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	x = y = 0;
+	*comma = '\0';
+	result = ((Tcl_ExprLong(interp, string + 1, &x) == TCL_OK) &&
+		  (Tcl_ExprLong(interp, comma + 1, &y) == TCL_OK));
+	*comma = ',';
+	if (!result) {
+	    return TCL_ERROR;
+	}
+	legendPtr->anchorPos.x = (int)x;
+	legendPtr->anchorPos.y = (int)y;
+	legendPtr->site = LEGEND_XY;
+    } else if (c == '.') {
+	if (legendPtr->tkwin != legendPtr->graphPtr->tkwin) {
+	    Tk_DestroyWindow(legendPtr->tkwin);
+	    legendPtr->tkwin = legendPtr->graphPtr->tkwin;
+	}
+	if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	legendPtr->site = LEGEND_WINDOW;
+    } else {
+	Tcl_AppendResult(interp, "bad position \"", string, "\": should be  \
+\"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
+\"plotarea\", .window or @x,y", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PositionToString --
+ *
+ *	Convert the window coordinates into a string.
+ *
+ * Results:
+ *	The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PositionToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of XPoint in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Legend *legendPtr = (Legend *)widgRec;
+
+    switch (legendPtr->site) {
+    case LEGEND_LEFT:
+	return "leftmargin";
+    case LEGEND_RIGHT:
+	return "rightmargin";
+    case LEGEND_TOP:
+	return "topmargin";
+    case LEGEND_BOTTOM:
+	return "bottommargin";
+    case LEGEND_PLOT:
+	return "plotarea";
+    case LEGEND_WINDOW:
+	return Tk_PathName(legendPtr->tkwin);
+    case LEGEND_XY:
+	{
+	    char string[200];
+	    char *result;
+
+	    sprintf(string, "@%d,%d", (int)legendPtr->anchorPos.x, 
+		    (int)legendPtr->anchorPos.y);
+	    result = Blt_Strdup(string);
+	    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+	    return result;
+	}
+    default:
+	return "unknown legend position";
+    }
+}
+
+static void
+SetLegendOrigin(legendPtr)
+    Legend *legendPtr;
+{
+    Graph *graphPtr;
+    int x, y, width, height;
+
+    graphPtr = legendPtr->graphPtr;
+    x = y = width = height = 0;		/* Suppress compiler warning. */
+    switch (legendPtr->site) {
+    case LEGEND_RIGHT:
+	width = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
+	height = graphPtr->bottom - graphPtr->top;
+	x = graphPtr->width - (width + graphPtr->inset);
+	y = graphPtr->top;
+	break;
+    case LEGEND_LEFT:
+	width = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
+	height = graphPtr->bottom - graphPtr->top;
+	x = graphPtr->inset;
+	y = graphPtr->top;
+	break;
+    case LEGEND_TOP:
+	width = graphPtr->right - graphPtr->left;
+	height = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
+	if (graphPtr->title != NULL) {
+	    height -= graphPtr->titleTextStyle.height;
+	}
+	x = graphPtr->left;
+	y = graphPtr->inset;
+	if (graphPtr->title != NULL) {
+	    y += graphPtr->titleTextStyle.height;
+	}
+	break;
+    case LEGEND_BOTTOM:
+	width = graphPtr->right - graphPtr->left;
+	height = graphPtr->bottomMargin.height - 
+	    graphPtr->bottomMargin.axesOffset;
+	x = graphPtr->left;
+	y = graphPtr->height - (height + graphPtr->inset);
+	break;
+    case LEGEND_PLOT:
+	width = graphPtr->right - graphPtr->left;
+	height = graphPtr->bottom - graphPtr->top;
+	x = graphPtr->left;
+	y = graphPtr->top;
+	break;
+    case LEGEND_XY:
+	width = legendPtr->width;
+	height = legendPtr->height;
+	x = (int)legendPtr->anchorPos.x;
+	y = (int)legendPtr->anchorPos.y;
+	if (x < 0) {
+	    x += graphPtr->width;
+	}
+	if (y < 0) {
+	    y += graphPtr->height;
+	}
+	break;
+    case LEGEND_WINDOW:
+	legendPtr->anchor = TK_ANCHOR_NW;
+	legendPtr->x = legendPtr->y = 0;
+	return;
+    }
+    width = legendPtr->width - width;
+    height = legendPtr->height - height;
+    Blt_TranslateAnchor(x, y, width, height, legendPtr->anchor, &x, &y);
+
+    legendPtr->x = x + legendPtr->padLeft;
+    legendPtr->y = y + legendPtr->padTop;
+}
+
+
+/*ARGSUSED*/
+static ClientData
+PickLegendEntry(clientData, x, y, contextPtr)
+    ClientData clientData;
+    int x, y;			/* Point to be tested */
+    ClientData *contextPtr;	/* Not used. */
+{
+    Graph *graphPtr = clientData;
+    Legend *legendPtr;
+    int width, height;
+
+    legendPtr = graphPtr->legend;
+    width = legendPtr->width;
+    height = legendPtr->height;
+
+    x -= legendPtr->x + legendPtr->borderWidth;
+    y -= legendPtr->y + legendPtr->borderWidth;
+    width -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
+    height -= 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
+
+    if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
+	int row, column;
+	int n;
+
+	/*
+	 * It's in the bounding box, so compute the index.
+	 */
+	row = y / legendPtr->style.height;
+	column = x / legendPtr->style.width;
+	n = (column * legendPtr->nRows) + row;
+	if (n < legendPtr->nEntries) {
+	    Blt_ChainLink *linkPtr;
+	    Element *elemPtr;
+	    int count;
+
+	    /* Legend entries are stored in reverse. */
+	    count = 0;
+	    for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+		 linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+		elemPtr = Blt_ChainGetValue(linkPtr);
+		if (elemPtr->label != NULL) {
+		    if (count == n) {
+			return elemPtr;
+		    }
+		    count++;
+		}
+	    }	      
+	    if (linkPtr != NULL) {
+		return Blt_ChainGetValue(linkPtr);
+	    }	
+	}
+    }
+    return NULL;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_MapLegend --
+ *
+ * 	Calculates the dimensions (width and height) needed for
+ *	the legend.  Also determines the number of rows and columns
+ *	necessary to list all the valid element labels.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *   	The following fields of the legend are calculated and set.
+ *
+ * 	nEntries   - number of valid labels of elements in the
+ *		      display list.
+ * 	nRows	    - number of rows of entries
+ * 	nColumns    - number of columns of entries
+ * 	style.height - height of each entry
+ * 	style.width  - width of each entry
+ * 	height	    - width of legend (includes borders and padding)
+ * 	width	    - height of legend (includes borders and padding)
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_MapLegend(legendPtr, plotWidth, plotHeight)
+    Legend *legendPtr;
+    int plotWidth;		/* Maximum width available in window
+				 * to draw the legend. Will calculate number
+				 * of columns from this. */
+    int plotHeight;		/* Maximum height available in window
+				 * to draw the legend. Will calculate number
+				 * of rows from this. */
+{
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+    int nRows, nColumns, nEntries;
+    int legendWidth, legendHeight;
+    int entryWidth, entryHeight;
+    int symbolWidth;
+    Tk_FontMetrics fontMetrics;
+
+    /* Initialize legend values to default (no legend displayed) */
+
+    legendPtr->style.width = legendPtr->style.height = 0;
+    legendPtr->nRows = legendPtr->nColumns = 0;
+    legendPtr->nEntries = 0;
+    legendPtr->height = legendPtr->width = 0;
+
+    if (legendPtr->site == LEGEND_WINDOW) {
+	if (Tk_Width(legendPtr->tkwin) > 1) {
+	    plotWidth = Tk_Width(legendPtr->tkwin);
+	}
+	if (Tk_Height(legendPtr->tkwin) > 1) {
+	    plotHeight = Tk_Height(legendPtr->tkwin);
+	}
+    }
+    if ((legendPtr->hidden) || (plotWidth < 1) || (plotHeight < 1)) {
+	return;			/* Legend is not being displayed */
+    }
+
+    /*   
+     * Count the number of legend entries and determine the widest and
+     * tallest label.  The number of entries would normally be the
+     * number of elements, but 1) elements can be hidden and 2)
+     * elements can have no legend entry (-label "").  
+     */
+    nEntries = 0;
+    entryWidth = entryHeight = 0;
+    for (linkPtr = Blt_ChainLastLink(legendPtr->graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	int width, height;
+
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (elemPtr->label == NULL) {
+	    continue;		/* Element has no legend entry. */
+	}
+	Blt_GetTextExtents(&legendPtr->style, elemPtr->label, &width, &height);
+	if (entryWidth < width) {
+	    entryWidth = width;
+	}
+	if (entryHeight < height) {
+	    entryHeight = height;
+	}
+	nEntries++;
+    }
+
+    if (nEntries == 0) {
+	return;			/* No legend entries. */
+    }
+
+
+    Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+    symbolWidth = 2 * fontMetrics.ascent;
+
+    entryWidth += 2 * legendPtr->entryBorderWidth + PADDING(legendPtr->ipadX) +
+	5 + symbolWidth;
+    entryHeight += 2 * legendPtr->entryBorderWidth + PADDING(legendPtr->ipadY);
+
+    legendWidth = plotWidth - 2 * legendPtr->borderWidth - 
+	PADDING(legendPtr->padX);
+    legendHeight = plotHeight - 2 * legendPtr->borderWidth - 
+	PADDING(legendPtr->padY);
+
+    /*
+     * The number of rows and columns is computed as one of the following:
+     *
+     *	both options set		User defined. 
+     *  -rows				Compute columns from rows.
+     *  -columns			Compute rows from columns.
+     *	neither set			Compute rows and columns from
+     *					size of plot.  
+     */
+    if (legendPtr->reqRows > 0) {
+	nRows = legendPtr->reqRows; 
+	if (nRows > nEntries) {
+	    nRows = nEntries;	
+	}
+	if (legendPtr->reqColumns > 0) {
+	    nColumns = legendPtr->reqColumns;
+	    if (nColumns > nEntries) {
+		nColumns = nEntries; /* Both -rows, -columns set. */
+	    }
+	} else {
+	    nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */
+	}
+    } else if (legendPtr->reqColumns > 0) { /* Only -columns. */
+	nColumns = legendPtr->reqColumns;
+	if (nColumns > nEntries) {
+	    nColumns = nEntries;
+	}
+	nRows = ((nEntries - 1) / nColumns) + 1;
+    } else {			
+	/* Compute # of rows and columns from the legend size. */
+	nRows = legendHeight / entryHeight;
+	nColumns = legendWidth / entryWidth;
+	
+	if (nRows > nEntries) {
+	    nRows = nEntries;
+	} else if (nRows < 1) {
+	    nRows = 1;
+	} 
+	if (nColumns > nEntries) {
+	    nColumns = nEntries;
+	} else if (nColumns < 1) {
+	    nColumns = 1;
+	}
+	if ((legendPtr->site == LEGEND_TOP) || 
+	    (legendPtr->site == LEGEND_BOTTOM)) {
+	    nRows = ((nEntries - 1) / nColumns) + 1;
+	} else {
+	    nColumns = ((nEntries - 1) / nRows) + 1;
+	}
+    }
+    if (nRows < 1) {
+	nRows = 1;
+    }
+    if (nColumns < 1) {
+	nColumns = 1;
+    }
+    legendWidth = 2 * legendPtr->borderWidth + PADDING(legendPtr->padX);
+    legendHeight = 2 * legendPtr->borderWidth + PADDING(legendPtr->padY);
+    legendHeight += nRows * entryHeight;
+    legendWidth += nColumns * entryWidth;
+
+    legendPtr->height = legendHeight;
+    legendPtr->width = legendWidth;
+    legendPtr->nRows = nRows;
+    legendPtr->nColumns = nColumns;
+    legendPtr->nEntries = nEntries;
+    legendPtr->style.height = entryHeight;
+    legendPtr->style.width = entryWidth;
+
+    if ((legendPtr->tkwin != legendPtr->graphPtr->tkwin) &&
+	((Tk_ReqWidth(legendPtr->tkwin) != legendWidth) ||
+	 (Tk_ReqHeight(legendPtr->tkwin) != legendHeight))) {
+	Tk_GeometryRequest(legendPtr->tkwin, legendWidth, legendHeight);
+    }
+}
+
+void
+Blt_DrawLegend(legendPtr, drawable)
+    Legend *legendPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Graph *graphPtr;
+    Blt_ChainLink *linkPtr;
+    Pixmap pixmap;
+    Tk_3DBorder border;
+    Tk_FontMetrics fontMetrics;
+    Tk_Window tkwin;
+    int count;
+    int labelX, startY, symbolX, symbolY;
+    int symbolSize, midX, midY;
+    int width, height;
+    int x, y;
+    register Element *elemPtr;
+
+    graphPtr = legendPtr->graphPtr;
+    graphPtr->flags &= ~DRAW_LEGEND;
+    if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+	return;
+    }
+    SetLegendOrigin(legendPtr);
+
+    if (legendPtr->tkwin != graphPtr->tkwin) {
+	tkwin = legendPtr->tkwin;
+	width = Tk_Width(tkwin);
+	if (width < 1) {
+	    width = legendPtr->width;
+	}
+	height = Tk_Height(tkwin);
+	if (height < 1) {
+	    height = legendPtr->height;
+	}
+    } else {
+	width = legendPtr->width;
+	height = legendPtr->height;
+    }
+    Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+
+    symbolSize = fontMetrics.ascent;
+    midX = symbolSize + 1 + legendPtr->entryBorderWidth;
+    midY = (symbolSize / 2) + 1 + legendPtr->entryBorderWidth;
+    labelX = 2 * symbolSize + legendPtr->entryBorderWidth + 
+	legendPtr->ipadX.side1 + 5;
+    symbolY = midY + legendPtr->ipadY.side1;
+    symbolX = midX + legendPtr->ipadX.side1;
+
+    pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(legendPtr->tkwin), 
+	width, height, Tk_Depth(legendPtr->tkwin));
+
+    if (legendPtr->border != NULL) {
+	/* Background color and relief. */
+	Blt_Fill3DRectangle(legendPtr->tkwin, pixmap, legendPtr->border, 0, 0, 
+		width, height, 0, TK_RELIEF_FLAT);
+    } else if (legendPtr->site & LEGEND_IN_PLOT) {
+	/* 
+	 * Legend background is transparent and is positioned over the
+	 * the plot area.  Either copy the part of the background from
+	 * the backing store pixmap or (if no backing store exists)
+	 * just fill it with the background color of the plot.
+	 */
+	if (graphPtr->backPixmap != None) {
+	    XCopyArea(graphPtr->display, graphPtr->backPixmap, pixmap, 
+		graphPtr->drawGC, legendPtr->x, legendPtr->y, width, height, 
+		0, 0);
+        } else {
+	    XFillRectangle(graphPtr->display, pixmap, graphPtr->plotFillGC,
+		0, 0, width, height);
+ 	}
+    } else {
+	/* 
+	 * The legend is positioned in one of the margins or the
+	 * external window.  Draw either the solid or tiled background
+	 * with the the border.
+	 */
+	if (graphPtr->tile != NULL) {
+	    Blt_SetTileOrigin(legendPtr->tkwin, graphPtr->tile, legendPtr->x, 
+			      legendPtr->y);
+	    Blt_TileRectangle(legendPtr->tkwin, pixmap, graphPtr->tile, 0, 0, 
+				width, height);
+	} else {
+	    XFillRectangle(graphPtr->display, pixmap, graphPtr->fillGC, 0, 0, 
+			   width, height);
+ 	}
+    }
+    x = legendPtr->padLeft + legendPtr->borderWidth;
+    y = legendPtr->padTop + legendPtr->borderWidth;
+    count = 0;
+    startY = y;
+    for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (elemPtr->label == NULL) {
+	    continue;		/* Skip this entry */
+	}
+	if (elemPtr->flags & LABEL_ACTIVE) {
+	    legendPtr->style.state |= STATE_ACTIVE;
+	    Blt_Fill3DRectangle(legendPtr->tkwin, pixmap, 
+		legendPtr->activeBorder, x, y, 
+		legendPtr->style.width, legendPtr->style.height, 
+		legendPtr->entryBorderWidth, legendPtr->activeRelief);
+	} else {
+	    legendPtr->style.state &= ~STATE_ACTIVE;
+	    if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+		Blt_Draw3DRectangle(legendPtr->tkwin, pixmap, graphPtr->border,
+		    x, y, legendPtr->style.width, legendPtr->style.height,
+		    legendPtr->entryBorderWidth, elemPtr->labelRelief);
+	    }
+	}
+	(*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
+		x + symbolX, y + symbolY, symbolSize);
+	Blt_DrawText(legendPtr->tkwin, pixmap, elemPtr->label, 
+		&legendPtr->style, x + labelX, 
+		y + legendPtr->entryBorderWidth + legendPtr->ipadY.side1);
+	count++;
+
+	/* Check when to move to the next column */
+	if ((count % legendPtr->nRows) > 0) {
+	    y += legendPtr->style.height;
+	} else {
+	    x += legendPtr->style.width;
+	    y = startY;
+	}
+    }
+    /*
+     * Draw the border and/or background of the legend.
+     */
+    border = legendPtr->border;
+    if (border == NULL) {
+	border = graphPtr->border;
+    }
+    Blt_Draw3DRectangle(legendPtr->tkwin, pixmap, border, 0, 0, width, height, 
+	legendPtr->borderWidth, legendPtr->relief);
+
+    XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0, 
+	width, height, legendPtr->x, legendPtr->y);
+    Tk_FreePixmap(graphPtr->display, pixmap);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LegendToPostScript --
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LegendToPostScript(legendPtr, psToken)
+    Legend *legendPtr;
+    PsToken psToken;
+{
+    Graph *graphPtr;
+    double x, y, startY;
+    Element *elemPtr;
+    int labelX, symbolX, symbolY;
+    int count;
+    Blt_ChainLink *linkPtr;
+    int symbolSize, midX, midY;
+    int width, height;
+    Tk_FontMetrics fontMetrics;
+
+    if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+	return;
+    }
+    SetLegendOrigin(legendPtr);
+
+    x = legendPtr->x, y = legendPtr->y;
+    width = legendPtr->width - PADDING(legendPtr->padX);
+    height = legendPtr->height - PADDING(legendPtr->padY);
+
+    graphPtr = legendPtr->graphPtr;
+    if (graphPtr->postscript->decorations) {
+	if (legendPtr->border != NULL) {
+	    Blt_Fill3DRectangleToPostScript(psToken, legendPtr->border, x, y,
+		width, height, legendPtr->borderWidth, legendPtr->relief);
+	} else {
+	    Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, x, y,
+		width, height, legendPtr->borderWidth, legendPtr->relief);
+	}
+    } else {
+	Blt_ClearBackgroundToPostScript(psToken);
+	Blt_RectangleToPostScript(psToken, x, y, width, height);
+    }
+    x += legendPtr->borderWidth;
+    y += legendPtr->borderWidth;
+
+    Tk_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+    symbolSize = fontMetrics.ascent;
+    midX = symbolSize + 1 + legendPtr->entryBorderWidth;
+    midY = (symbolSize / 2) + 1 + legendPtr->entryBorderWidth;
+    labelX = 2 * symbolSize + legendPtr->entryBorderWidth + 
+	legendPtr->ipadX.side1 + 5;
+    symbolY = midY + legendPtr->ipadY.side1;
+    symbolX = midX + legendPtr->ipadX.side1;
+
+    count = 0;
+    startY = y;
+    for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	elemPtr = Blt_ChainGetValue(linkPtr);
+	if (elemPtr->label == NULL) {
+	    continue;		/* Skip this label */
+	}
+	if (elemPtr->flags & LABEL_ACTIVE) {
+	    legendPtr->style.state |= STATE_ACTIVE;
+	    Blt_Fill3DRectangleToPostScript(psToken, legendPtr->activeBorder,
+		    x, y, legendPtr->style.width, legendPtr->style.height,
+		    legendPtr->entryBorderWidth, legendPtr->activeRelief);
+	} else {
+	    legendPtr->style.state &= ~STATE_ACTIVE;
+	    if (elemPtr->labelRelief != TK_RELIEF_FLAT) {
+		Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border,
+		    x, y, legendPtr->style.width, legendPtr->style.height,
+		    legendPtr->entryBorderWidth, elemPtr->labelRelief);
+	    }
+	}
+	(*elemPtr->procsPtr->printSymbolProc) (graphPtr, psToken, elemPtr,
+	    x + symbolX, y + symbolY, symbolSize);
+	Blt_TextToPostScript(psToken, elemPtr->label, &(legendPtr->style),
+		x + labelX, 
+		y + legendPtr->entryBorderWidth + legendPtr->ipadY.side1);
+	count++;
+	if ((count % legendPtr->nRows) > 0) {
+	    y += legendPtr->style.height;
+	} else {
+	    x += legendPtr->style.width;
+	    y = startY;
+	}
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DisplayLegend --
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DisplayLegend(clientData)
+    ClientData clientData;
+{
+    Legend *legendPtr = clientData;
+    int width, height;
+
+    legendPtr->flags &= ~REDRAW_PENDING;
+
+    if (legendPtr->tkwin == NULL) {
+	return;			/* Window has been destroyed. */
+    }
+    if (legendPtr->site == LEGEND_WINDOW) {
+	width = Tk_Width(legendPtr->tkwin);
+	height = Tk_Height(legendPtr->tkwin);
+	if ((width <= 1) || (height <= 1)) {
+	    return;
+	}
+	if ((width != legendPtr->width) || (height != legendPtr->height)) {
+	    Blt_MapLegend(legendPtr, width, height);
+	}
+    }
+    if (!Tk_IsMapped(legendPtr->tkwin)) {
+	return;
+    }
+    Blt_DrawLegend(legendPtr, Tk_WindowId(legendPtr->tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLegend --
+ *
+ * 	Routine to configure the legend.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureLegend(graphPtr, legendPtr)
+    Graph *graphPtr;
+    Legend *legendPtr;
+{
+    Blt_ResetTextStyle(graphPtr->tkwin, &(legendPtr->style));
+
+    if (legendPtr->site == LEGEND_WINDOW) {
+	EventuallyRedrawLegend(legendPtr);
+    } else {
+	/*
+	 *  Update the layout of the graph (and redraw the elements) if
+	 *  any of the following legend options (all of which affect the
+	 *	size of the legend) have changed.
+	 *
+	 *		-activeborderwidth, -borderwidth
+	 *		-border
+	 *		-font
+	 *		-hide
+	 *		-ipadx, -ipady, -padx, -pady
+	 *		-rows
+	 *
+	 *  If the position of the legend changed to/from the default
+	 *  position, also indicate that a new layout is needed.
+	 *
+	 */
+	if (Blt_ConfigModified(configSpecs, "-*border*", "-*pad?",
+		"-position", "-hide", "-font", "-rows", (char *)NULL)) {
+	    graphPtr->flags |= MAP_WORLD;
+	}
+	graphPtr->flags |= (REDRAW_WORLD | REDRAW_BACKING_STORE);
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyLegend --
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Resources associated with the legend are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyLegend(graphPtr)
+    Graph *graphPtr;
+{
+    Legend *legendPtr = graphPtr->legend;
+
+    Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
+    Blt_FreeTextStyle(graphPtr->display, &(legendPtr->style));
+    Blt_DestroyBindingTable(legendPtr->bindTable);
+    if (legendPtr->tkwin != graphPtr->tkwin) {
+	Tk_Window tkwin;
+
+	/* The graph may be in the process of being torn down */
+	if (legendPtr->cmdToken != NULL) {
+	    Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
+	}
+	if (legendPtr->flags & REDRAW_PENDING) {
+	    Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+	    legendPtr->flags &= ~REDRAW_PENDING;
+	}
+	tkwin = legendPtr->tkwin;
+	legendPtr->tkwin = NULL;
+	if (tkwin != NULL) {
+	    Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask, 
+				  LegendEventProc, legendPtr);
+	    Blt_DeleteWindowInstanceData(tkwin);
+	    Tk_DestroyWindow(tkwin);
+	}
+    }
+    Blt_Free(legendPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateLegend --
+ *
+ * 	Creates and initializes a legend structure with default settings
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_CreateLegend(graphPtr)
+    Graph *graphPtr;
+{
+    Legend *legendPtr;
+
+    legendPtr = Blt_Calloc(1, sizeof(Legend));
+    assert(legendPtr);
+    graphPtr->legend = legendPtr;
+    legendPtr->graphPtr = graphPtr;
+    legendPtr->tkwin = graphPtr->tkwin;
+    legendPtr->hidden = FALSE;
+    legendPtr->anchorPos.x = legendPtr->anchorPos.y = -SHRT_MAX;
+    legendPtr->relief = TK_RELIEF_SUNKEN;
+    legendPtr->activeRelief = TK_RELIEF_FLAT;
+    legendPtr->entryBorderWidth = legendPtr->borderWidth = 2;
+    legendPtr->ipadX.side1 = legendPtr->ipadX.side2 = 1;
+    legendPtr->ipadY.side1 = legendPtr->ipadY.side2 = 1;
+    legendPtr->padX.side1 = legendPtr->padX.side2 = 1;
+    legendPtr->padY.side1 = legendPtr->padY.side2 = 1;
+    legendPtr->anchor = TK_ANCHOR_N;
+    legendPtr->site = LEGEND_RIGHT;
+    Blt_InitTextStyle(&(legendPtr->style));
+    legendPtr->style.justify = TK_JUSTIFY_LEFT;
+    legendPtr->style.anchor = TK_ANCHOR_NW;
+    legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
+	graphPtr->tkwin, graphPtr, PickLegendEntry, Blt_GraphTags);
+
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+	    "legend", "Legend", configSpecs, 0, (char **)NULL,
+	    (char *)legendPtr, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    ConfigureLegend(graphPtr, legendPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * 	Find the legend entry from the given argument.  The argument
+ *	can be either a screen position "@x,y" or the name of an
+ *	element.
+ *
+ *	I don't know how useful it is to test with the name of an
+ *	element.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char *argv[];
+{
+    register Element *elemPtr;
+    Legend *legendPtr = graphPtr->legend;
+    int x, y;
+    char c;
+
+    if ((legendPtr->hidden) || (legendPtr->nEntries == 0)) {
+	return TCL_OK;
+    }
+    elemPtr = NULL;
+    c = argv[3][0];
+    if ((c == 'c') && (strcmp(argv[3], "current") == 0)) {
+	elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
+    } else if ((c == '@') &&
+       (Blt_GetXY(interp, graphPtr->tkwin, argv[3], &x, &y) == TCL_OK)) { 
+	elemPtr = (Element *)PickLegendEntry(graphPtr, x, y, NULL);
+    }
+    if (elemPtr != NULL) {
+	Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * 	Activates a particular label in the legend.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ActivateOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char *argv[];
+{
+    Legend *legendPtr = graphPtr->legend;
+    Element *elemPtr;
+    unsigned int active, redraw;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    register int i;
+
+    active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
+    redraw = 0;
+    for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	elemPtr = Blt_GetHashValue(hPtr);
+	for (i = 3; i < argc; i++) {
+	    if (Tcl_StringMatch(elemPtr->name, argv[i])) {
+		break;
+	    }
+	}
+	if ((i < argc) && (active != (elemPtr->flags & LABEL_ACTIVE))) {
+	    elemPtr->flags ^= LABEL_ACTIVE;
+	    if (elemPtr->label != NULL) {
+		redraw++;
+	    }
+	}
+    }
+    if ((redraw) && (!legendPtr->hidden)) {
+	/*
+	 * See if how much we need to draw. If the graph is already
+	 * schedule for a redraw, just make sure the right flags are
+	 * set.  Otherwise redraw only the legend: it's either in an
+	 * external window or it's the only thing that need updating.
+	 */
+	if (graphPtr->flags & REDRAW_PENDING) {
+	    if (legendPtr->site & LEGEND_IN_PLOT) {
+		graphPtr->flags |= REDRAW_BACKING_STORE;
+	    }
+	    graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
+	} else {
+	    EventuallyRedrawLegend(legendPtr);
+	}
+    }
+    /* Return the names of all the active legend entries */
+    for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.table), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	elemPtr = Blt_GetHashValue(hPtr);
+	if (elemPtr->flags & LABEL_ACTIVE) {
+	    Tcl_AppendElement(interp, elemPtr->name);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *	  .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    if (argc == 3) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	char *tagName;
+
+	for (hPtr = Blt_FirstHashEntry(&(graphPtr->elements.tagTable), &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tagName = Blt_GetHashKey(&(graphPtr->elements.tagTable), hPtr);
+	    Tcl_AppendElement(interp, tagName);
+	}
+	return TCL_OK;
+    }
+    return Blt_ConfigureBindings(interp, graphPtr->legend->bindTable,
+	Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * 	Queries or resets options for the legend.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+	    (char *)graphPtr->legend, argv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * 	Queries or resets options for the legend.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int flags = TK_CONFIG_ARGV_ONLY;
+    Legend *legendPtr;
+
+    legendPtr = graphPtr->legend;
+    if (argc == 3) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)legendPtr, (char *)NULL, flags);
+    } else if (argc == 4) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)legendPtr, argv[3], flags);
+    }
+    if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+	    argv + 3, (char *)legendPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    ConfigureLegend(graphPtr, legendPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LegendOp --
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Legend is possibly redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec legendOps[] =
+{
+    {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+    {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",},
+    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+    {"deactivate", 1, (Blt_Op)ActivateOp, 3, 0, "?pattern?...",},
+    {"get", 1, (Blt_Op)GetOp, 4, 4, "index",},
+};
+static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_LegendOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nLegendOps, legendOps, BLT_OP_ARG2, argc, argv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (graphPtr, interp, argc, argv);
+    return result;
+}
+
+int 
+Blt_LegendSite(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->site;
+}
+
+int 
+Blt_LegendWidth(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->width;
+}
+
+int 
+Blt_LegendHeight(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->height;
+}
+
+int 
+Blt_LegendIsHidden(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->hidden;
+}
+
+int 
+Blt_LegendIsRaised(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->raised;
+}
+
+int 
+Blt_LegendX(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->x;
+}
+
+int 
+Blt_LegendY(legendPtr)
+    Legend *legendPtr;
+{
+    return legendPtr->y;
+}
+
+void
+Blt_LegendRemoveElement(legendPtr, elemPtr)
+    Legend *legendPtr;
+    Element *elemPtr;
+{
+    Blt_DeleteBindings(legendPtr->bindTable, elemPtr);
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrLegd.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrLegd.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrLegd.h	(revision 175)
@@ -0,0 +1,56 @@
+/*
+ * bltGrLegd.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GR_LEGEND_H
+#define _BLT_GR_LEGEND_H
+
+#define LEGEND_RIGHT	(1<<0)	/* Right margin */
+#define LEGEND_LEFT	(1<<1)	/* Left margin */
+#define LEGEND_BOTTOM	(1<<2)	/* Bottom margin */
+#define LEGEND_TOP	(1<<3)	/* Top margin, below the graph title. */
+#define LEGEND_PLOT	(1<<4)	/* Plot area */
+#define LEGEND_XY	(1<<5)	/* Screen coordinates in the plotting 
+				 * area. */
+#define LEGEND_WINDOW	(1<<6)	/* External window. */
+#define LEGEND_IN_MARGIN \
+	(LEGEND_RIGHT | LEGEND_LEFT | LEGEND_BOTTOM | LEGEND_TOP)
+#define LEGEND_IN_PLOT  (LEGEND_PLOT | LEGEND_XY)
+
+extern int Blt_CreateLegend _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyLegend _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DrawLegend _ANSI_ARGS_((Legend *legendPtr, Drawable drawable));
+extern void Blt_MapLegend _ANSI_ARGS_((Legend *legendPtr, int width,
+	int height));
+extern int Blt_LegendOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_LegendSite _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendWidth _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendHeight _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendIsHidden _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendIsRaised _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendX _ANSI_ARGS_((Legend *legendPtr));
+extern int Blt_LegendY _ANSI_ARGS_((Legend *legendPtr));
+extern void Blt_LegendRemoveElement _ANSI_ARGS_((Legend *legendPtr, 
+	Element *elemPtr));
+#endif /* BLT_GR_LEGEND_H */
Index: trunk/kitgen/8.x/blt/generic/bltGrLine.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrLine.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrLine.c	(revision 175)
@@ -0,0 +1,5235 @@
+
+/*
+ * bltGrLine.c --
+ *
+ *	This module implements line graph and stripchart elements for
+ *	the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+#define COLOR_DEFAULT	(XColor *)1
+#define PATTERN_SOLID	((Pixmap)1)
+
+#define PEN_INCREASING  1	/* Draw line segments for only those
+				 * data points whose abscissas are
+				 * monotonically increasing in
+				 * order */
+#define PEN_DECREASING  2	/* Lines will be drawn between only
+				 * those points whose abscissas are
+				 * decreasing in order */
+
+#define PEN_BOTH_DIRECTIONS	(PEN_INCREASING | PEN_DECREASING)
+ /* Lines will be drawn between points regardless of the ordering of
+  * the abscissas */
+
+#define BROKEN_TRACE(dir,last,next) \
+    (((((dir) & PEN_DECREASING) == 0) && ((next) < (last))) || \
+     ((((dir) & PEN_INCREASING) == 0) && ((next) > (last))))
+
+#define DRAW_SYMBOL(linePtr) \
+	(((linePtr)->symbolCounter % (linePtr)->symbolInterval) == 0)
+
+typedef enum { 
+    PEN_SMOOTH_NONE,		/* Line segments */
+    PEN_SMOOTH_STEP,		/* Step-and-hold */
+    PEN_SMOOTH_NATURAL,		/* Natural cubic spline */
+    PEN_SMOOTH_QUADRATIC,	/* Quadratic spline */
+    PEN_SMOOTH_CATROM,		/* Catrom parametric spline */
+    PEN_SMOOTH_LAST		/* Sentinel */
+} Smoothing;
+
+typedef struct {
+    char *name;
+    Smoothing value;
+} SmoothingInfo;
+
+static SmoothingInfo smoothingInfo[] = {
+    { "linear",		PEN_SMOOTH_NONE },
+    { "step",		PEN_SMOOTH_STEP },
+    { "natural",	PEN_SMOOTH_NATURAL },
+    { "cubic",		PEN_SMOOTH_NATURAL },
+    { "quadratic",	PEN_SMOOTH_QUADRATIC },
+    { "catrom",		PEN_SMOOTH_CATROM },
+    { (char *)NULL,	PEN_SMOOTH_LAST }
+};
+
+
+typedef struct {
+    Point2D *screenPts;		/* Array of transformed coordinates */
+    int nScreenPts;		/* Number of coordinates */
+    int *dataToStyle;		/* Index of pen styles  */
+    int *indices;		/* Maps segments/traces to data points */
+
+} MapInfo;
+
+/*
+ * Symbol types for line elements
+ */
+typedef enum {
+    SYMBOL_NONE,
+    SYMBOL_SQUARE,
+    SYMBOL_CIRCLE,
+    SYMBOL_DIAMOND,
+    SYMBOL_PLUS,
+    SYMBOL_CROSS,
+    SYMBOL_SPLUS,
+    SYMBOL_SCROSS,
+    SYMBOL_TRIANGLE,
+    SYMBOL_ARROW,
+    SYMBOL_BITMAP
+} SymbolType;
+
+typedef struct {
+    SymbolType type;		/* Type of symbol to be drawn/printed */
+
+    int size;			/* Requested size of symbol in pixels */
+
+    XColor *outlineColor;	/* Outline color */
+
+    int outlineWidth;		/* Width of the outline */
+
+    GC outlineGC;		/* Outline graphics context */
+
+    XColor *fillColor;		/* Normal fill color */
+
+    GC fillGC;			/* Fill graphics context */
+
+    /* The last two fields are used only for bitmap symbols. */
+
+    Pixmap bitmap;		/* Bitmap to determine foreground/background
+				 * pixels of the symbol */
+
+    Pixmap mask;		/* Bitmap representing the transparent
+				 * pixels of the symbol */
+
+} Symbol;
+
+typedef struct {
+    int start;			/* Index into the X-Y coordinate
+				 * arrays indicating where trace
+				 * starts. */
+
+    int nScreenPts;		/* Number of points in the continuous
+				 * trace */
+
+    Point2D *screenPts;		/* Array of screen coordinates
+				 * (malloc-ed) representing the
+				 * trace. */
+
+    int *symbolToData;		/* Reverse mapping of screen
+				 * coordinate indices back to their
+				 * data coordinates */
+} Trace;
+
+typedef struct {
+    char *name;			/* Name of pen style. If the pen was
+				 * statically allocated the name will
+				 * be NULL. */
+
+    Blt_Uid classUid;		/* Type of pen */
+
+    char *typeId;		/* String token identifying the type
+				 * of pen */
+
+    unsigned int flags;		/* Indicates if the pen element is
+				 * active or normal */
+
+    int refCount;		/* Reference count for elements using
+				 * this pen. */
+    Blt_HashEntry *hashPtr;
+
+    Tk_ConfigSpec *configSpecs;	/* Configuration specifications */
+
+    PenConfigureProc *configProc;
+    PenDestroyProc *destroyProc;
+
+    /* Symbol attributes. */
+    Symbol symbol;		/* Element symbol type */
+
+    /* Trace attributes. */
+    int traceWidth;		/* Width of the line segments. If
+				 * lineWidth is 0, no line will be
+				 * drawn, only symbols. */
+
+    Blt_Dashes traceDashes;	/* Dash on-off list value */
+
+    XColor *traceColor;		/* Line segment color */
+
+    XColor *traceOffColor;	/* Line segment dash gap color */
+
+    GC traceGC;			/* Line segment graphics context */
+    
+    /* Error bar attributes. */
+    int errorBarShow;		/* Describes which error bars to
+				 * display: none, x, y, or * both. */
+
+    int errorBarLineWidth;	/* Width of the error bar segments. */
+
+    int errorBarCapWidth;	/* Width of the cap on error bars. */
+
+    XColor *errorBarColor;	/* Color of the error bar. */
+
+    GC errorBarGC;		/* Error bar graphics context. */
+
+    /* Show value attributes. */
+    int valueShow;		/* Indicates whether to display data
+				 * value.  Values are x, y, both, or 
+				 * none. */
+    char *valueFormat;		/* A printf format string. */
+
+    TextStyle valueStyle;	/* Text attributes (color, font,
+				 * rotation, etc.) of the value. */
+
+} LinePen;
+
+typedef struct {
+    Weight weight;		/* Weight range where this pen is valid. */
+
+    LinePen *penPtr;		/* Pen used to draw symbols, traces, error 
+				 * bars, segments, etc. */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int errorBarCapWidth;	/* Length of the cap ends on each
+				 * error bar. */
+
+    int symbolSize;		/* Size of the pen's symbol scaled to the
+				 * current graph size. */
+
+    /* Graph specific data. */
+
+    Point2D *symbolPts;		/* Points to start of array for this pen. */
+
+    int nSymbolPts;		/* # of points for this pen. */
+
+    /* The last two fields are used only for stripcharts. */
+
+    Segment2D *strips;		/* Points to start of the line segments
+				 * for this pen. */
+
+    int nStrips;		/* # of line segments for this pen. */
+
+} LinePenStyle;
+
+typedef struct {
+    char *name;			/* Identifier used to refer the
+				 * element. Used in the "insert",
+				 * "delete", or "show", operations. */
+
+    Blt_Uid classUid;		/* Type of element */
+
+    Graph *graphPtr;		/* Graph widget of element*/
+
+    unsigned int flags;		/* Indicates if the entire element is
+				 * active, or if coordinates need to
+				 * be calculated */
+
+    char **tags;
+
+    int hidden;			/* If non-zero, don't display the
+				 * element. */
+
+    Blt_HashEntry *hashPtr;
+
+    char *label;		/* Label displayed in legend */
+
+    int labelRelief;		/* Relief of label in legend. */
+
+    Axis2D axes;
+
+    ElemVector x, y, w;		/* Contains array of numeric values */
+
+    ElemVector xError;		/* Relative/symmetric X error values. */
+    ElemVector yError;		/* Relative/symmetric Y error values. */
+    ElemVector xHigh, xLow;	/* Absolute/asymmetric X-coordinate high/low
+				   error values. */
+    ElemVector yHigh, yLow;	/* Absolute/asymmetric Y-coordinate high/low
+				   error values. */
+
+    int *activeIndices;		/* Array of indices (malloc-ed) that
+				 * indicate the data points are active
+				 * (drawn with "active" colors). */
+
+    int nActiveIndices;		/* Number of active data points.
+				 * Special case: if < 0 then all data
+				 * points are drawn active. */
+
+    ElementProcs *procsPtr;
+    Tk_ConfigSpec *configSpecs;	/* Configuration specifications */
+
+    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
+				 * segments in the element's array. */
+    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
+				 * segments in the element's array. */
+    int xErrorBarCnt;		/* # of error bars for this pen. */
+    int yErrorBarCnt;		/* # of error bars for this pen. */
+
+    int *xErrorToData;		/* Maps individual error bar segments back
+				 * to the data point associated with it. */
+    int *yErrorToData;		/* Maps individual error bar segments back
+				 * to the data point associated with it. */
+
+    int errorBarCapWidth;	/* Length of cap on error bars */
+
+    LinePen *activePenPtr;	/* Pen to draw "active" elements. */
+    LinePen *normalPenPtr;	/* Pen to draw elements normally. */
+
+    Blt_Chain *palette;		/* Array of pen styles: pens are associated
+				 * with specific ranges of data.*/
+
+    /* Symbol scaling */
+    int scaleSymbols;		/* If non-zero, the symbols will scale
+				 * in size as the graph is zoomed
+				 * in/out.  */
+
+    double xRange, yRange;	/* Initial X-axis and Y-axis ranges:
+				 * used to scale the size of element's
+				 * symbol. */
+
+    int state;
+    /*
+     * Line specific configurable attributes
+     */
+    LinePen builtinPen;
+
+    /* Line smoothing */
+    Smoothing reqSmooth;	/* Requested smoothing function to use
+				 * for connecting the data points */
+
+    Smoothing smooth;		/* Smoothing function used. */
+
+    double rTolerance;		/* Tolerance to reduce the number of
+				 * points displayed. */
+    /*
+     * Drawing related data structures.
+     */
+
+    /* Area-under-curve fill attributes. */
+    XColor *fillFgColor;
+    XColor *fillBgColor;
+    GC fillGC;
+
+    Blt_Tile fillTile;		/* Tile for fill area. */
+    Pixmap fillStipple;		/* Stipple for fill area. */
+
+    int nFillPts;
+    Point2D *fillPts;		/* Array of points used to draw
+				 * polygon to fill area under the
+				 * curve */
+
+    /* Symbol points */
+    Point2D *symbolPts;		/* Holds the screen coordinates of all
+				 * the data points for the element. */
+    int nSymbolPts;		/* Number of points */
+
+    int *symbolToData;		/* Contains indices of data points.
+				 * It's first used to map pens to the
+				 * visible points to sort them by pen
+				 * style, and later to find data
+				 * points from the index of a visible
+				 * point. */
+
+    /* Active symbol points */
+    Point2D *activePts;		/* Array of indices representing the
+				 * "active" points. */
+    int nActivePts;		/* Number of indices in the above array. */
+
+    int *activeToData;		/* Contains indices of data points.
+				 * It's first used to map pens to the
+				 * visible points to sort them by pen
+				 * style, and later to find data
+				 * points from the index of a visible
+				 * point. */
+
+    int reqMaxSymbols;
+    int symbolInterval;
+    int symbolCounter;
+
+    /* X-Y graph-specific fields */
+
+    int penDir;			/* Indicates if a change in the pen
+				 * direction should be considered a
+				 * retrace (line segment is not
+				 * drawn). */
+
+    Blt_Chain *traces;	/* List of traces (a trace is a series
+				 * of contiguous line segments).  New
+				 * traces are generated when either
+				 * the next segment changes the pen
+				 * direction, or the end point is
+				 * clipped by the plotting area. */
+
+    /* Stripchart-specific fields */
+
+    Segment2D *strips;		/* Holds the the line segments of the
+				 * element trace. The segments are
+				 * grouped by pen style. */
+    int nStrips;		/* Number of line segments to be drawn. */
+    int *stripToData;		/* Pen to visible line segment mapping. */
+
+} Line;
+
+static Tk_OptionParseProc StringToPattern;
+static Tk_OptionPrintProc PatternToString;
+static Tk_OptionParseProc StringToSmooth;
+static Tk_OptionPrintProc SmoothToString;
+extern Tk_OptionParseProc Blt_StringToStyles;
+extern Tk_OptionPrintProc Blt_StylesToString;
+static Tk_OptionParseProc StringToPenDir;
+static Tk_OptionPrintProc PenDirToString;
+static Tk_OptionParseProc StringToSymbol;
+static Tk_OptionPrintProc SymbolToString;
+
+static Tk_CustomOption patternOption =
+{
+    StringToPattern, PatternToString, (ClientData)0
+};
+static Tk_CustomOption smoothOption =
+{
+    StringToSmooth, SmoothToString, (ClientData)0
+};
+static Tk_CustomOption stylesOption =
+{
+    Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(LinePenStyle)
+};
+static Tk_CustomOption penDirOption =
+{
+    StringToPenDir, PenDirToString, (ClientData)0
+};
+static Tk_CustomOption symbolOption =
+{
+    StringToSymbol, SymbolToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDataOption;
+extern Tk_CustomOption bltDataPairsOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltLinePenOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltStateOption;
+
+#define DEF_LINE_ACTIVE_PEN		"activeLine"
+#define DEF_LINE_AXIS_X			"x"
+#define DEF_LINE_AXIS_Y			"y"
+#define DEF_LINE_DASHES			(char *)NULL
+#define DEF_LINE_DATA			(char *)NULL
+#define DEF_LINE_FILL_COLOR    		"defcolor"
+#define DEF_LINE_FILL_MONO		"defcolor"
+#define DEF_LINE_HIDE			"no"
+#define DEF_LINE_LABEL			(char *)NULL
+#define DEF_LINE_LABEL_RELIEF		"flat"
+#define DEF_LINE_MAX_SYMBOLS		"0"
+#define DEF_LINE_OFFDASH_COLOR    	(char *)NULL
+#define DEF_LINE_OFFDASH_MONO		(char *)NULL
+#define DEF_LINE_OUTLINE_COLOR		"defcolor"
+#define DEF_LINE_OUTLINE_MONO		"defcolor"
+#define DEF_LINE_OUTLINE_WIDTH 		"1"
+#define DEF_LINE_PATTERN		(char *)NULL
+#define DEF_LINE_PATTERN_BG		"white"
+#define DEF_LINE_PATTERN_FG		"black"
+#define DEF_LINE_PATTERN_TILE		(char *)NULL
+#define DEF_LINE_PEN_COLOR		RGB_NAVYBLUE
+#define DEF_LINE_PEN_DIRECTION		"both"
+#define DEF_LINE_PEN_MONO		RGB_BLACK
+#define DEF_LINE_PEN_WIDTH		"1"
+#define DEF_LINE_PIXELS			"0.125i"
+#define DEF_LINE_REDUCE			"0.0"
+#define DEF_LINE_SCALE_SYMBOLS		"yes"
+#define DEF_LINE_SMOOTH			"linear"
+#define DEF_LINE_STATE			"normal"
+#define DEF_LINE_STIPPLE		(char *)NULL
+#define DEF_LINE_STYLES			""
+#define DEF_LINE_SYMBOL			"circle"
+#define DEF_LINE_TAGS			"all"
+#define DEF_LINE_X_DATA			(char *)NULL
+#define DEF_LINE_Y_DATA			(char *)NULL
+
+#define DEF_LINE_ERRORBAR_COLOR		"defcolor"
+#define DEF_LINE_ERRORBAR_LINE_WIDTH	"1"
+#define DEF_LINE_ERRORBAR_CAP_WIDTH	"1"
+#define DEF_LINE_SHOW_ERRORBARS		"both"
+
+#define DEF_PEN_ACTIVE_COLOR		RGB_BLUE
+#define DEF_PEN_ACTIVE_MONO		RGB_BLACK
+#define DEF_PEN_DASHES			(char *)NULL
+#define DEF_PEN_FILL_COLOR    		"defcolor"
+#define DEF_PEN_FILL_MONO		"defcolor"
+#define DEF_PEN_LINE_WIDTH		"1"
+#define DEF_PEN_NORMAL_COLOR		RGB_NAVYBLUE
+#define DEF_PEN_NORMAL_MONO		RGB_BLACK
+#define DEF_PEN_OFFDASH_COLOR    	(char *)NULL
+#define DEF_PEN_OFFDASH_MONO		(char *)NULL
+#define DEF_PEN_OUTLINE_COLOR		"defcolor"
+#define DEF_PEN_OUTLINE_MONO		"defcolor"
+#define DEF_PEN_OUTLINE_WIDTH 		"1"
+#define DEF_PEN_PIXELS			"0.125i"
+#define DEF_PEN_SYMBOL			"circle"
+#define DEF_PEN_TYPE			"line"
+#define	DEF_PEN_VALUE_ANCHOR		"s"
+#define	DEF_PEN_VALUE_COLOR		RGB_BLACK
+#define	DEF_PEN_VALUE_FONT		STD_FONT_SMALL
+#define	DEF_PEN_VALUE_FORMAT		"%g"
+#define	DEF_PEN_VALUE_ROTATE		(char *)NULL
+#define	DEF_PEN_VALUE_SHADOW		(char *)NULL
+#define DEF_PEN_SHOW_VALUES		"no"
+
+static Tk_ConfigSpec lineElemConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+	DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+	TK_CONFIG_NULL_OK, &bltLinePenOption},
+    {TK_CONFIG_CUSTOM, "-areapattern", "areaPattern", "AreaPattern",
+        DEF_LINE_PATTERN, Tk_Offset(Line, fillStipple), 
+        TK_CONFIG_NULL_OK, &patternOption},
+    {TK_CONFIG_COLOR, "-areaforeground", "areaForeground", "areaForeground",
+	DEF_LINE_PATTERN_FG, Tk_Offset(Line, fillFgColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-areabackground", "areaBackground", "areaBackground",
+	DEF_LINE_PATTERN_BG, Tk_Offset(Line, fillBgColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-areatile", "areaTile", "AreaTile",
+	DEF_LINE_PATTERN_TILE, Tk_Offset(Line, fillTile), 
+	TK_CONFIG_NULL_OK, &bltTileOption},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_LINE_TAGS, Tk_Offset(Line, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+	DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+	DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorBarColor), 
+	0, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+	DEF_LINE_ERRORBAR_LINE_WIDTH, 
+	Tk_Offset(Line, builtinPen.errorBarLineWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", 
+	DEF_LINE_ERRORBAR_CAP_WIDTH, 
+	Tk_Offset(Line, builtinPen.errorBarCapWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-label", "label", "Label",
+	(char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+	DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+	TK_CONFIG_DONT_SET_DEFAULT}, 
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+	DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+        DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+	DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+	TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+	TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+	DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+	(char *)NULL, Tk_Offset(Line, normalPenPtr),
+	TK_CONFIG_NULL_OK, &bltLinePenOption},
+    {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+	DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size),
+	GRAPH | STRIPCHART, &bltDistanceOption},
+    {TK_CONFIG_DOUBLE, "-reduce", "reduce", "Reduce",
+	DEF_LINE_REDUCE, Tk_Offset(Line, rTolerance),
+	GRAPH | STRIPCHART | TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+	DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+	DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorBarShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+	DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+	DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+	TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_LINE_STATE, Tk_Offset(Line, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+	DEF_LINE_STYLES, Tk_Offset(Line, palette), 
+        TK_CONFIG_NULL_OK, &stylesOption},
+    {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+	DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+	TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+    {TK_CONFIG_CUSTOM, "-trace", "trace", "Trace",
+	DEF_LINE_PEN_DIRECTION, Tk_Offset(Line, penDir),
+	TK_CONFIG_DONT_SET_DEFAULT, &penDirOption},
+    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+	DEF_PEN_VALUE_ANCHOR, 
+        Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+	DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+	DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+	DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+	DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+	DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow),
+	0, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+	(char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+	(char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+	(char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError", 
+        (char *)NULL, Tk_Offset(Line, xError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", 
+        (char *)NULL, Tk_Offset(Line, xHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", 
+        (char *)NULL, Tk_Offset(Line, xLow), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-y", "yData", "YData", 
+	(char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+	(char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError", 
+        (char *)NULL, Tk_Offset(Line, yError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", 
+        (char *)NULL, Tk_Offset(Line, yHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", 
+        (char *)NULL, Tk_Offset(Line, yLow), 0, &bltDataOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec stripElemConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+	DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+	TK_CONFIG_NULL_OK, &bltLinePenOption},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_LINE_TAGS, Tk_Offset(Line, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+	DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+	DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorBarColor), 
+	0, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+	DEF_LINE_ERRORBAR_LINE_WIDTH, 
+	Tk_Offset(Line, builtinPen.errorBarLineWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", 
+	"ErrorBarCap", DEF_LINE_ERRORBAR_CAP_WIDTH, 
+	Tk_Offset(Line, builtinPen.errorBarCapWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-label", "label", "Label",
+	(char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+	DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+        DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth), 
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+	DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+	TK_CONFIG_COLOR_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+	TK_CONFIG_MONO_ONLY, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+	DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+	(char *)NULL, Tk_Offset(Line, normalPenPtr), 
+        TK_CONFIG_NULL_OK, &bltLinePenOption},
+    {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+        DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size), 0,
+	&bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+	DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+	DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorBarShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+	DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+	DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+	TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+    {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+	DEF_LINE_STYLES, Tk_Offset(Line, palette), 
+	TK_CONFIG_NULL_OK, &stylesOption},
+    {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+	DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+	TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+	DEF_PEN_VALUE_ANCHOR, 
+        Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+	DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+	DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+	DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+	DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+	DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow), 0,
+	&bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+	(char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+	(char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+	(char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-y", "yData", "YData",
+	(char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL, 
+	Tk_Offset(Line, xError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+	(char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL, 
+	Tk_Offset(Line, yError), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL, 
+	Tk_Offset(Line, xHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL, 
+	Tk_Offset(Line, xLow), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL, 
+	Tk_Offset(Line, xHigh), 0, &bltDataOption},
+    {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL, 
+	Tk_Offset(Line, yLow), 0, &bltDataOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tk_ConfigSpec linePenConfigSpecs[] =
+{
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_PEN_ACTIVE_COLOR, Tk_Offset(LinePen, traceColor),
+	TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_PEN_ACTIVE_MONO, Tk_Offset(LinePen, traceColor),
+	TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_PEN_NORMAL_COLOR, Tk_Offset(LinePen, traceColor),
+	TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
+    {TK_CONFIG_COLOR, "-color", "color", "Color",
+	DEF_PEN_NORMAL_MONO, Tk_Offset(LinePen, traceColor),
+	TK_CONFIG_MONO_ONLY | NORMAL_PEN},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_PEN_DASHES, Tk_Offset(LinePen, traceDashes),
+	TK_CONFIG_NULL_OK | ALL_PENS, &bltDashesOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+	DEF_LINE_ERRORBAR_COLOR, Tk_Offset(LinePen, errorBarColor), 
+	ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+	DEF_LINE_ERRORBAR_LINE_WIDTH, Tk_Offset(LinePen, errorBarLineWidth),
+        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", 
+	"ErrorBarCap", DEF_LINE_ERRORBAR_CAP_WIDTH, 
+	Tk_Offset(LinePen, errorBarCapWidth),
+        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_PEN_FILL_COLOR, Tk_Offset(LinePen, symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_PEN_FILL_MONO, Tk_Offset(LinePen, symbol.fillColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+	(char *)NULL, Tk_Offset(LinePen, traceWidth), 
+	ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_PEN_OFFDASH_COLOR, Tk_Offset(LinePen, traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+	DEF_PEN_OFFDASH_MONO, Tk_Offset(LinePen, traceOffColor),
+	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_PEN_OUTLINE_COLOR, Tk_Offset(LinePen, symbol.outlineColor),
+	TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_PEN_OUTLINE_MONO, Tk_Offset(LinePen, symbol.outlineColor),
+	TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+    {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+	DEF_PEN_OUTLINE_WIDTH, Tk_Offset(LinePen, symbol.outlineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+	DEF_PEN_PIXELS, Tk_Offset(LinePen, symbol.size),
+	ALL_PENS, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+	DEF_LINE_SHOW_ERRORBARS, Tk_Offset(LinePen, errorBarShow),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+	DEF_PEN_SHOW_VALUES, Tk_Offset(LinePen, valueShow),
+	ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+	DEF_PEN_SYMBOL, Tk_Offset(LinePen, symbol),
+	TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &symbolOption},
+    {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
+	DEF_PEN_TYPE, Tk_Offset(Pen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+	DEF_PEN_VALUE_ANCHOR, Tk_Offset(LinePen, valueStyle.anchor), ALL_PENS},
+    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+	DEF_PEN_VALUE_COLOR, Tk_Offset(LinePen, valueStyle.color), ALL_PENS},
+    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+	DEF_PEN_VALUE_FONT, Tk_Offset(LinePen, valueStyle.font), ALL_PENS},
+    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+	DEF_PEN_VALUE_FORMAT, Tk_Offset(LinePen, valueFormat),
+	ALL_PENS | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+	DEF_PEN_VALUE_ROTATE, Tk_Offset(LinePen, valueStyle.theta), ALL_PENS},
+    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+	DEF_PEN_VALUE_SHADOW, Tk_Offset(LinePen, valueStyle.shadow),
+	ALL_PENS, &bltShadowOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef double (DistanceProc) _ANSI_ARGS_((int x, int y, Point2D *p, 
+	Point2D *q, Point2D *t));
+
+/* Forward declarations */
+static PenConfigureProc ConfigurePen;
+static PenDestroyProc DestroyPen;
+static ElementClosestProc ClosestLine;
+static ElementConfigProc ConfigureLine;
+static ElementDestroyProc DestroyLine;
+static ElementDrawProc DrawActiveLine;
+static ElementDrawProc DrawNormalLine;
+static ElementDrawSymbolProc DrawSymbol;
+static ElementExtentsProc GetLineExtents;
+static ElementToPostScriptProc ActiveLineToPostScript;
+static ElementToPostScriptProc NormalLineToPostScript;
+static ElementSymbolToPostScriptProc SymbolToPostScript;
+static ElementMapProc MapLine;
+static DistanceProc DistanceToY;
+static DistanceProc DistanceToX;
+static DistanceProc DistanceToLine;
+static Blt_TileChangedProc TileChangedProc;
+
+#ifdef WIN32
+
+static int tkpWinRopModes[] =
+{
+    R2_BLACK,			/* GXclear */
+    R2_MASKPEN,			/* GXand */
+    R2_MASKPENNOT,		/* GXandReverse */
+    R2_COPYPEN,			/* GXcopy */
+    R2_MASKNOTPEN,		/* GXandInverted */
+    R2_NOT,			/* GXnoop */
+    R2_XORPEN,			/* GXxor */
+    R2_MERGEPEN,		/* GXor */
+    R2_NOTMERGEPEN,		/* GXnor */
+    R2_NOTXORPEN,		/* GXequiv */
+    R2_NOT,			/* GXinvert */
+    R2_MERGEPENNOT,		/* GXorReverse */
+    R2_NOTCOPYPEN,		/* GXcopyInverted */
+    R2_MERGENOTPEN,		/* GXorInverted */
+    R2_NOTMASKPEN,		/* GXnand */
+    R2_WHITE			/* GXset */
+};
+
+#endif
+
+INLINE static int
+Round(x)
+    register double x;
+{
+    return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * 	Custom configuration option (parse and print) routines
+ * ----------------------------------------------------------------------
+ */
+
+static int
+StringToBitmap(interp, tkwin, symbolPtr, string)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    Symbol *symbolPtr;
+    char *string;
+{
+    Pixmap bitmap, mask;
+    char **elemArr;
+    int nElems;
+    int result;
+
+    if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+
+    if (nElems > 2) {
+	Tcl_AppendResult(interp, "too many elements in bitmap list \"", string,
+		 "\": should be \"bitmap mask\"", (char *)NULL);
+	result = TCL_ERROR;
+	goto error;
+    }
+    mask = None;
+    bitmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[0]));
+    if (bitmap == None) {
+	result = TCL_BREAK;
+	Tcl_ResetResult(interp);
+	goto error;
+    }
+    if ((nElems > 1) && (elemArr[1][0] != '\0')) {
+	mask = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[1]));
+	if (mask == None) {
+	    Tk_FreeBitmap(Tk_Display(tkwin), bitmap);
+	    result = TCL_ERROR;
+	    goto error;
+	}
+    }
+    Blt_Free(elemArr);
+    if (symbolPtr->bitmap != None) {
+	Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->bitmap);
+    }
+    symbolPtr->bitmap = bitmap;
+    if (symbolPtr->mask != None) {
+	Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->mask);
+    }
+    symbolPtr->mask = mask;
+    return TCL_OK;
+  error:
+    Blt_Free(elemArr);
+    return result;
+}
+
+/*ARGSUSED*/
+static char *
+PatternToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Pixmap stipple = *(Pixmap *)(widgRec + offset);
+
+    if (stipple == None) {
+	return "";
+    } 
+    if (stipple == PATTERN_SOLID) {
+	return "solid";
+    } 
+    return Tk_NameOfBitmap(Tk_Display(tkwin), stipple);
+}
+
+/*ARGSUSED*/
+static int
+StringToPattern(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing field */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of field in record */
+{
+    Pixmap *stipplePtr = (Pixmap *)(widgRec + offset);
+    Pixmap stipple;
+
+    if ((string == NULL) || (string[0] == '\0')) {
+	stipple = None;
+    } else if (strcmp(string, "solid") == 0) {
+	stipple = PATTERN_SOLID;
+    } else {
+	stipple = Tk_GetBitmap(interp, tkwin, Tk_GetUid(string));
+	if (stipple == None) {
+	    return TCL_ERROR;
+	}
+    }
+    if ((*stipplePtr != None) && (*stipplePtr != PATTERN_SOLID)) {
+	Tk_FreeBitmap(Tk_Display(tkwin), *stipplePtr);
+    }
+    *stipplePtr = stipple;
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSymbol --
+ *
+ *	Converts the symbol value into its string representation.
+ *
+ * Results:
+ *	The static string representing the symbol type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSymbol(symbolPtr)
+    Symbol *symbolPtr;
+{
+    switch (symbolPtr->type) {
+    case SYMBOL_NONE:
+	return "none";
+    case SYMBOL_SQUARE:
+	return "square";
+    case SYMBOL_CIRCLE:
+	return "circle";
+    case SYMBOL_DIAMOND:
+	return "diamond";
+    case SYMBOL_PLUS:
+	return "plus";
+    case SYMBOL_CROSS:
+	return "cross";
+    case SYMBOL_SPLUS:
+	return "splus";
+    case SYMBOL_SCROSS:
+	return "scross";
+    case SYMBOL_TRIANGLE:
+	return "triangle";
+    case SYMBOL_ARROW:
+	return "arrow";
+    case SYMBOL_BITMAP:
+	return "bitmap";
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSymbol --
+ *
+ *	Convert the string representation of a line style or symbol name
+ *	into its numeric form.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The symbol type is
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSymbol(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing symbol type */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of symbol type field in record */
+{
+    Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if (c == '\0') {
+	symbolPtr->type = SYMBOL_NONE;
+    } else if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+	symbolPtr->type = SYMBOL_NONE;
+    } else if ((c == 'c') && (length > 1) &&
+	(strncmp(string, "circle", length) == 0)) {
+	symbolPtr->type = SYMBOL_CIRCLE;
+    } else if ((c == 's') && (length > 1) &&
+	(strncmp(string, "square", length) == 0)) {
+	symbolPtr->type = SYMBOL_SQUARE;
+    } else if ((c == 'd') && (strncmp(string, "diamond", length) == 0)) {
+	symbolPtr->type = SYMBOL_DIAMOND;
+    } else if ((c == 'p') && (strncmp(string, "plus", length) == 0)) {
+	symbolPtr->type = SYMBOL_PLUS;
+    } else if ((c == 'c') && (length > 1) &&
+	(strncmp(string, "cross", length) == 0)) {
+	symbolPtr->type = SYMBOL_CROSS;
+    } else if ((c == 's') && (length > 1) &&
+	(strncmp(string, "splus", length) == 0)) {
+	symbolPtr->type = SYMBOL_SPLUS;
+    } else if ((c == 's') && (length > 1) &&
+	(strncmp(string, "scross", length) == 0)) {
+	symbolPtr->type = SYMBOL_SCROSS;
+    } else if ((c == 't') && (strncmp(string, "triangle", length) == 0)) {
+	symbolPtr->type = SYMBOL_TRIANGLE;
+    } else if ((c == 'a') && (strncmp(string, "arrow", length) == 0)) {
+	symbolPtr->type = SYMBOL_ARROW;
+    } else {
+	int result;
+
+	result = StringToBitmap(interp, tkwin, symbolPtr, string);
+	if (result != TCL_OK) {
+	    if (result != TCL_ERROR) {
+		Tcl_AppendResult(interp, "bad symbol \"", string, 
+"\": should be \"none\", \"circle\", \"square\", \"diamond\", \"plus\", \
+\"cross\", \"splus\", \"scross\", \"triangle\", \"arrow\" \
+or the name of a bitmap", (char *)NULL);
+	    }
+	    return TCL_ERROR;
+	}
+	symbolPtr->type = SYMBOL_BITMAP;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SymbolToString --
+ *
+ *	Convert the symbol value into a string.
+ *
+ * Results:
+ *	The string representing the symbol type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SymbolToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of symbol type field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+    char *result;
+
+    if (symbolPtr->type == SYMBOL_BITMAP) {
+	Tcl_DString dString;
+
+	Tcl_DStringInit(&dString);
+	Tcl_DStringAppendElement(&dString,
+	    Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->bitmap));
+	Tcl_DStringAppendElement(&dString, (symbolPtr->mask == None) ? "" :
+	    Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->mask));
+	result = Blt_Strdup(Tcl_DStringValue(&dString));
+	Tcl_DStringFree(&dString);
+	*freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    } else {
+	result = NameOfSymbol(symbolPtr);
+    }
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSmooth --
+ *
+ *	Converts the smooth value into its string representation.
+ *
+ * Results:
+ *	The static string representing the smooth type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSmooth(value)
+    Smoothing value;
+{
+    if ((value < 0) || (value >= PEN_SMOOTH_LAST)) {
+	return "unknown smooth value";
+    }
+    return smoothingInfo[value].name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSmooth --
+ *
+ *	Convert the string representation of a line style or smooth name
+ *	into its numeric form.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The smooth type is
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSmooth(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing smooth type */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of smooth type field in record */
+{
+    Smoothing *valuePtr = (Smoothing *)(widgRec + offset);
+    register SmoothingInfo *siPtr;
+
+    for (siPtr = smoothingInfo; siPtr->name != NULL; siPtr++) {
+	if (strcmp(string, siPtr->name) == 0) {
+	    *valuePtr = siPtr->value;
+	    return TCL_OK;
+	}
+    }
+    Tcl_AppendResult(interp, "bad smooth value \"", string, "\": should be \
+linear, step, natural, or quadratic", (char *)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SmoothToString --
+ *
+ *	Convert the smooth value into a string.
+ *
+ * Results:
+ *	The string representing the smooth type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SmoothToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of smooth type field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int smooth = *(int *)(widgRec + offset);
+
+    return NameOfSmooth(smooth);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPenDir --
+ *
+ *	Convert the string representation of a line style or symbol name
+ *	into its numeric form.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The symbol type is
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPenDir(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing pen direction */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of pen direction field in record */
+{
+    int *penDirPtr = (int *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'i') && (strncmp(string, "increasing", length) == 0)) {
+	*penDirPtr = PEN_INCREASING;
+    } else if ((c == 'd') && (strncmp(string, "decreasing", length) == 0)) {
+	*penDirPtr = PEN_DECREASING;
+    } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+	*penDirPtr = PEN_BOTH_DIRECTIONS;
+    } else {
+	Tcl_AppendResult(interp, "bad trace value \"", string,
+	    "\" : should be \"increasing\", \"decreasing\", or \"both\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfPenDir --
+ *
+ *	Convert the pen direction into a string.
+ *
+ * Results:
+ *	The static string representing the pen direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfPenDir(penDir)
+    int penDir;			/* Direction for pen drawing between points */
+{
+    switch (penDir) {
+    case PEN_INCREASING:
+	return "increasing";
+    case PEN_DECREASING:
+	return "decreasing";
+    case PEN_BOTH_DIRECTIONS:
+	return "both";
+    default:
+	return "unknown trace direction";
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PenDirToString --
+ *
+ *	Convert the pen direction into a string.
+ *
+ * Results:
+ *	The string representing the pen drawing direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PenDirToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of pen direction field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int penDir = *(int *)(widgRec + offset);
+
+    return NameOfPenDir(penDir);
+}
+
+
+/*
+ * Clear the number of points and segments, in case there are no
+ * segments or points
+ */
+static void
+ClearPalette(palette)
+    Blt_Chain *palette;
+{
+    register LinePenStyle *stylePtr;
+    Blt_ChainLink *linkPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->nStrips = stylePtr->nSymbolPts = 0;
+	stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = 0;
+    }
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigurePen --
+ *
+ *	Sets up the appropriate configuration parameters in the GC.
+ *      It is assumed the parameters have been previously set by
+ *	a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information such as line width, line style, color
+ *	etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    LinePen *lpPtr = (LinePen *)penPtr;
+    unsigned long gcMask;
+    GC newGC;
+    XGCValues gcValues;
+    XColor *colorPtr;
+
+    Blt_ResetTextStyle(graphPtr->tkwin, &(lpPtr->valueStyle));
+    /*
+     * Set the outline GC for this pen: GCForeground is outline color.
+     * GCBackground is the fill color (only used for bitmap symbols).
+     */
+    gcMask = (GCLineWidth | GCForeground);
+    colorPtr = lpPtr->symbol.outlineColor;
+    if (colorPtr == COLOR_DEFAULT) {
+	colorPtr = lpPtr->traceColor;
+    }
+    gcValues.foreground = colorPtr->pixel;
+    if (lpPtr->symbol.type == SYMBOL_BITMAP) {
+	colorPtr = lpPtr->symbol.fillColor;
+	if (colorPtr == COLOR_DEFAULT) {
+	    colorPtr = lpPtr->traceColor;
+	}
+	/*
+	 * Set a clip mask if either
+	 *	1) no background color was designated or
+	 *	2) a masking bitmap was specified.
+	 *
+	 * These aren't necessarily the bitmaps we'll be using for
+	 * clipping. But this makes it unlikely that anyone else will
+	 * be sharing this GC when we set the clip origin (at the time
+	 * the bitmap is drawn).
+	 */
+	if (colorPtr != NULL) {
+	    gcValues.background = colorPtr->pixel;
+	    gcMask |= GCBackground;
+	    if (lpPtr->symbol.mask != None) {
+		gcValues.clip_mask = lpPtr->symbol.mask;
+		gcMask |= GCClipMask;
+	    }
+	} else {
+	    gcValues.clip_mask = lpPtr->symbol.bitmap;
+	    gcMask |= GCClipMask;
+	}
+    }
+    gcValues.line_width = LineWidth(lpPtr->symbol.outlineWidth);
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (lpPtr->symbol.outlineGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+    }
+    lpPtr->symbol.outlineGC = newGC;
+
+    /* Fill GC for symbols: GCForeground is fill color */
+
+    gcMask = (GCLineWidth | GCForeground);
+    colorPtr = lpPtr->symbol.fillColor;
+    if (colorPtr == COLOR_DEFAULT) {
+	colorPtr = lpPtr->traceColor;
+    }
+    newGC = NULL;
+    if (colorPtr != NULL) {
+	gcValues.foreground = colorPtr->pixel;
+	newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    }
+    if (lpPtr->symbol.fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+    }
+    lpPtr->symbol.fillGC = newGC;
+
+    /* Line segments */
+
+    gcMask = (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle |
+	GCJoinStyle);
+    gcValues.cap_style = CapButt;
+    gcValues.join_style = JoinRound;
+    gcValues.line_style = LineSolid;
+    gcValues.line_width = LineWidth(lpPtr->traceWidth);
+
+    colorPtr = lpPtr->traceOffColor;
+    if (colorPtr == COLOR_DEFAULT) {
+	colorPtr = lpPtr->traceColor;
+    }
+    if (colorPtr != NULL) {
+	gcMask |= GCBackground;
+	gcValues.background = colorPtr->pixel;
+    }
+    gcValues.foreground = lpPtr->traceColor->pixel;
+    if (LineIsDashed(lpPtr->traceDashes)) {
+	gcValues.line_width = lpPtr->traceWidth;
+	gcValues.line_style = 
+	    (colorPtr == NULL) ? LineOnOffDash : LineDoubleDash;
+    }
+    newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (lpPtr->traceGC != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+    }
+    if (LineIsDashed(lpPtr->traceDashes)) {
+	lpPtr->traceDashes.offset = lpPtr->traceDashes.values[0] / 2;
+	Blt_SetDashes(graphPtr->display, newGC, &(lpPtr->traceDashes));
+    }
+    lpPtr->traceGC = newGC;
+
+    gcMask = (GCLineWidth | GCForeground);
+    colorPtr = lpPtr->errorBarColor;
+    if (colorPtr == COLOR_DEFAULT) {
+	colorPtr = lpPtr->traceColor;
+    }
+    gcValues.line_width = LineWidth(lpPtr->errorBarLineWidth);
+    gcValues.foreground = colorPtr->pixel;
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (lpPtr->errorBarGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->errorBarGC);
+    }
+    lpPtr->errorBarGC = newGC;
+
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPen --
+ *
+ *	Release memory and resources allocated for the style.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the pen style is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyPen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    LinePen *lpPtr = (LinePen *)penPtr;
+
+    Blt_FreeTextStyle(graphPtr->display, &(lpPtr->valueStyle));
+    if (lpPtr->symbol.outlineGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+    }
+    if (lpPtr->symbol.fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+    }
+    if (lpPtr->errorBarGC != NULL) {
+	Tk_FreeGC(graphPtr->display, lpPtr->errorBarGC);
+    }
+    if (lpPtr->traceGC != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+    }
+    if (lpPtr->symbol.bitmap != None) {
+	Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.bitmap);
+	lpPtr->symbol.bitmap = None;
+    }
+    if (lpPtr->symbol.mask != None) {
+	Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.mask);
+	lpPtr->symbol.mask = None;
+    }
+}
+
+
+static void
+InitPen(penPtr)
+    LinePen *penPtr;
+{
+    Blt_InitTextStyle(&penPtr->valueStyle);
+    penPtr->configProc = ConfigurePen;
+    penPtr->configSpecs = linePenConfigSpecs;
+    penPtr->destroyProc = DestroyPen;
+    penPtr->errorBarLineWidth = 1;
+    penPtr->errorBarShow = SHOW_BOTH;
+    penPtr->flags = NORMAL_PEN;
+    penPtr->name = "";
+    penPtr->symbol.bitmap = penPtr->symbol.mask = None;
+    penPtr->symbol.outlineColor = penPtr->symbol.fillColor = COLOR_DEFAULT;
+    penPtr->symbol.outlineWidth = penPtr->traceWidth = 1;
+    penPtr->symbol.type = SYMBOL_CIRCLE;
+    penPtr->valueShow = SHOW_NONE;
+}
+
+Pen *
+Blt_LinePen(penName)
+    char *penName;
+{
+    LinePen *penPtr;
+
+    penPtr = Blt_Calloc(1, sizeof(LinePen));
+    assert(penPtr);
+    InitPen(penPtr);
+    penPtr->name = Blt_Strdup(penName);
+    if (strcmp(penName, "activeLine") == 0) {
+	penPtr->flags = ACTIVE_PEN;
+    }
+    return (Pen *)penPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ *	In this section, the routines deal with building and filling
+ *	the element's data structures with transformed screen
+ *	coordinates.  They are triggered from TranformLine which is
+ *	called whenever the data or coordinates axes have changed and
+ *	new screen coordinates need to be calculated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScaleSymbol --
+ *
+ *	Returns the scaled size for the line element. Scaling depends
+ *	upon when the base line ranges for the element were set and
+ *	the current range of the graph.
+ *
+ * Results:
+ *	The new size of the symbol, after considering how much the
+ *	graph has been scaled, is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ScaleSymbol(elemPtr, normalSize)
+    Element *elemPtr;
+    int normalSize;
+{
+    int maxSize;
+    double scale;
+    int newSize;
+
+    scale = 1.0;
+    if (elemPtr->scaleSymbols) {
+	double xRange, yRange;
+
+	xRange = (elemPtr->axes.x->max - elemPtr->axes.x->min);
+	yRange = (elemPtr->axes.y->max - elemPtr->axes.y->min);
+	if (elemPtr->flags & SCALE_SYMBOL) {
+	    /* Save the ranges as a baseline for future scaling. */
+	    elemPtr->xRange = xRange;
+	    elemPtr->yRange = yRange;
+	    elemPtr->flags &= ~SCALE_SYMBOL;
+	} else {
+	    double xScale, yScale;
+
+	    /* Scale the symbol by the smallest change in the X or Y axes */
+	    xScale = elemPtr->xRange / xRange;
+	    yScale = elemPtr->yRange / yRange;
+	    scale = MIN(xScale, yScale);
+	}
+    }
+    newSize = Round(normalSize * scale);
+
+    /*
+     * Don't let the size of symbols go unbounded. Both X and Win32
+     * drawing routines assume coordinates to be a signed short int.
+     */
+    maxSize = (int)MIN(elemPtr->graphPtr->hRange, elemPtr->graphPtr->vRange);
+    if (newSize > maxSize) {
+	newSize = maxSize;
+    }
+
+    /* Make the symbol size odd so that its center is a single pixel. */
+    newSize |= 0x01;
+    return newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetScreenPoints --
+ *
+ *	Generates a coordinate array of transformed screen coordinates
+ *	from the data points.
+ *
+ * Results:
+ *	The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetScreenPoints(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    double *x, *y;
+    register int i, n;
+    register int count;
+    register Point2D *screenPts;
+    register int *indices;
+
+    n = NumberOfPoints(linePtr);
+    x = linePtr->x.valueArr;
+    y = linePtr->y.valueArr;
+    screenPts = Blt_Malloc(sizeof(Point2D) * n);
+    assert(screenPts);
+    indices = Blt_Malloc(sizeof(int) * n);
+    assert(indices);
+
+    count = 0;			/* Count the valid screen coordinates */
+    if (graphPtr->inverted) {
+	for (i = 0; i < n; i++) {
+	    if ((FINITE(x[i])) && (FINITE(y[i]))) {
+ 		screenPts[count].x = Blt_HMap(graphPtr, linePtr->axes.y, y[i]);
+		screenPts[count].y = Blt_VMap(graphPtr, linePtr->axes.x, x[i]);
+		indices[count] = i;
+		count++;
+	    }
+	}
+    } else {
+	for (i = 0; i < n; i++) {
+	    if ((FINITE(x[i])) && (FINITE(y[i]))) {
+		screenPts[count].x = Blt_HMap(graphPtr, linePtr->axes.x, x[i]);
+		screenPts[count].y = Blt_VMap(graphPtr, linePtr->axes.y, y[i]);
+		indices[count] = i;
+		count++;
+	    }
+	}
+    }
+    mapPtr->screenPts = screenPts;
+    mapPtr->nScreenPts = count;
+    mapPtr->indices = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReducePoints --
+ *
+ *	Generates a coordinate array of transformed screen coordinates
+ *	from the data points.
+ *
+ * Results:
+ *	The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReducePoints(mapPtr, tolerance)
+    MapInfo *mapPtr;
+    double tolerance;
+{
+    register int i, k, n;
+    Point2D *screenPts;
+    int *indices, *simple;
+
+    simple	= Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+    indices	= Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+    screenPts	= Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+    n = Blt_SimplifyLine(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1, 
+		 tolerance, simple);
+    for (i = 0; i < n; i++) {
+	k = simple[i];
+	screenPts[i] = mapPtr->screenPts[k];
+	indices[i] = mapPtr->indices[k];
+    }
+#ifdef notdef
+    if (n < mapPtr->nScreenPts) {
+	fprintf(stderr, "reduced from %d to %d\n", mapPtr->nScreenPts, n);
+    }
+#endif
+    Blt_Free(mapPtr->screenPts);
+    Blt_Free(mapPtr->indices);
+    Blt_Free(simple);
+    mapPtr->screenPts = screenPts;
+    mapPtr->indices = indices;
+    mapPtr->nScreenPts = n;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSteps --
+ *
+ *	Resets the coordinate and pen index arrays adding new points
+ *	for step-and-hold type smoothing.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The temporary arrays for screen coordinates and pen indices
+ *	are updated.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+GenerateSteps(mapPtr)
+    MapInfo *mapPtr;
+{
+    int newSize;
+    register int i, count;
+    Point2D *screenPts;
+    int *indices;
+
+    newSize = ((mapPtr->nScreenPts - 1) * 2) + 1;
+    screenPts = Blt_Malloc(newSize * sizeof(Point2D));
+    assert(screenPts);
+    indices = Blt_Malloc(sizeof(int) * newSize);
+    assert(indices);
+
+    screenPts[0] = mapPtr->screenPts[0];
+    indices[0] = 0;
+
+    count = 1;
+    for (i = 1; i < mapPtr->nScreenPts; i++) {
+	screenPts[count + 1] = mapPtr->screenPts[i];
+
+	/* Hold last y-coordinate, use new x-coordinate */
+	screenPts[count].x = screenPts[count + 1].x;
+	screenPts[count].y = screenPts[count - 1].y;
+
+	/* Use the same style for both the hold and the step points */
+	indices[count] = indices[count + 1] = mapPtr->indices[i];
+	count += 2;
+    }
+    Blt_Free(mapPtr->screenPts);
+    Blt_Free(mapPtr->indices);
+    mapPtr->indices = indices;
+    mapPtr->screenPts = screenPts;
+    mapPtr->nScreenPts = newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSpline --
+ *
+ *	Computes a spline based upon the data points, returning a new
+ *	(larger) coordinate array or points.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The temporary arrays for screen coordinates and data indices
+ *	are updated based upon spline.
+ *
+ * FIXME:  Can't interpolate knots along the Y-axis.   Need to break
+ *	   up point array into interchangable X and Y vectors earlier.
+ *	   Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateSpline(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    int extra;
+    register int i, j, count;
+    Point2D *origPts, *intpPts;
+    int *indices;
+    int nIntpPts, nOrigPts;
+    int result;
+    int x;
+
+    nOrigPts = mapPtr->nScreenPts;
+    origPts = mapPtr->screenPts;
+    assert(mapPtr->nScreenPts > 0);
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+	if (origPts[j].x <= origPts[i].x) {
+	    return;		/* Points are not monotonically increasing */
+	}
+    }
+    if (((origPts[0].x > (double)graphPtr->right)) ||
+	((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr->left))) {
+	return;			/* All points are clipped */
+    }
+    /*
+     * The spline is computed in screen coordinates instead of data
+     * points so that we can select the abscissas of the interpolated
+     * points from each pixel horizontally across the plotting area.
+     */
+    extra = (graphPtr->right - graphPtr->left) + 1;
+    if (extra < 1) {
+	return;
+    }
+    nIntpPts = nOrigPts + extra + 1;
+    intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+    assert(intpPts);
+
+    indices = Blt_Malloc(sizeof(int) * nIntpPts);
+    assert(indices);
+
+    /* Populate the x2 array with both the original X-coordinates and
+     * extra X-coordinates for each horizontal pixel that the line
+     * segment contains. */
+    count = 0;
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+
+	/* Add the original x-coordinate */
+	intpPts[count].x = origPts[i].x;
+
+	/* Include the starting offset of the point in the offset array */
+	indices[count] = mapPtr->indices[i];
+	count++;
+
+	/* Is any part of the interval (line segment) in the plotting
+	 * area?  */
+	if ((origPts[j].x >= (double)graphPtr->left) || 
+	    (origPts[i].x <= (double)graphPtr->right)) {
+	    int last;
+
+	    x = (int)(origPts[i].x + 1.0);
+
+	    /*
+	     * Since the line segment may be partially clipped on the
+	     * left or right side, the points to interpolate are
+	     * always interior to the plotting area.
+	     *
+	     *           left			    right
+	     *      x1----|--------------------------|---x2
+	     *
+	     * Pick the max of the starting X-coordinate and the
+	     * left edge and the min of the last X-coordinate and
+	     * the right edge.
+	     */
+	    x = MAX(x, graphPtr->left);
+	    last = (int)MIN(origPts[j].x, graphPtr->right);
+
+	    /* Add the extra x-coordinates to the interval. */
+	    while (x < last) {
+		indices[count] = mapPtr->indices[i];
+		intpPts[count++].x = (double)x;
+		x++;
+	    }
+	}
+    }
+    nIntpPts = count;
+    result = FALSE;
+    if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+	result = Blt_NaturalSpline(origPts, nOrigPts, intpPts, nIntpPts);
+    } else if (linePtr->smooth == PEN_SMOOTH_QUADRATIC) {
+	result = Blt_QuadraticSpline(origPts, nOrigPts, intpPts, nIntpPts);
+    }
+    if (!result) {
+	/* The spline interpolation failed.  We'll fallback to the
+	 * current coordinates and do no smoothing (standard line
+	 * segments).  */
+	linePtr->smooth = PEN_SMOOTH_NONE;
+	Blt_Free(intpPts);
+	Blt_Free(indices);
+    } else {
+	Blt_Free(mapPtr->screenPts);
+	Blt_Free(mapPtr->indices);
+	mapPtr->indices = indices;
+	mapPtr->screenPts = intpPts;
+	mapPtr->nScreenPts = nIntpPts;
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateParametricSpline --
+ *
+ *	Computes a spline based upon the data points, returning a new
+ *	(larger) coordinate array or points.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The temporary arrays for screen coordinates and data indices
+ *	are updated based upon spline.
+ *
+ * FIXME:  Can't interpolate knots along the Y-axis.   Need to break
+ *	   up point array into interchangable X and Y vectors earlier.
+ *	   Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateParametricSpline(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    Extents2D exts;
+    Point2D *origPts, *intpPts;
+    Point2D p, q;
+    double dist;
+    int *indices;
+    int nIntpPts, nOrigPts;
+    int result;
+    register int i, j, count;
+
+    nOrigPts = mapPtr->nScreenPts;
+    origPts = mapPtr->screenPts;
+    assert(mapPtr->nScreenPts > 0);
+
+    Blt_GraphExtents(graphPtr, &exts);
+
+    /* 
+     * Populate the x2 array with both the original X-coordinates and
+     * extra X-coordinates for each horizontal pixel that the line
+     * segment contains. 
+     */
+    count = 1;
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+        p = origPts[i];
+        q = origPts[j];
+	count++;
+        if (Blt_LineRectClip(&exts, &p, &q)) {
+	    count += (int)(hypot(q.x - p.x, q.y - p.y) * 0.5);
+	}
+    }
+    nIntpPts = count;
+    intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+    assert(intpPts);
+
+    indices = Blt_Malloc(sizeof(int) * nIntpPts);
+    assert(indices);
+
+    /* 
+     * FIXME: This is just plain wrong.  The spline should be computed
+     *        and evaluated in separate steps.  This will mean breaking
+     *	      up this routine since the catrom coefficients can be
+     *	      independently computed for original data point.  This 
+     *	      also handles the problem of allocating enough points 
+     *	      since evaluation is independent of the number of points 
+     *		to be evalualted.  The interpolated 
+     *	      line segments should be clipped, not the original segments.
+     */
+    count = 0;
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+        p = origPts[i];
+        q = origPts[j];
+
+        dist = hypot(q.x - p.x, q.y - p.y);
+        /* Add the original x-coordinate */
+        intpPts[count].x = (double)i;
+        intpPts[count].y = 0.0;
+
+        /* Include the starting offset of the point in the offset array */
+        indices[count] = mapPtr->indices[i];
+        count++;
+
+        /* Is any part of the interval (line segment) in the plotting
+         * area?  */
+
+        if (Blt_LineRectClip(&exts, &p, &q)) {
+            double distP, distQ;
+
+            distP = hypot(p.x - origPts[i].x, p.y - origPts[i].y);
+            distQ = hypot(q.x - origPts[i].x, q.y - origPts[i].y);
+            distP += 2.0;
+            while(distP <= distQ) {
+                /* Point is indicated by its interval and parameter t. */
+                intpPts[count].x = (double)i;
+                intpPts[count].y =  distP / dist;
+                indices[count] = mapPtr->indices[i];
+                count++;
+                distP += 2.0;
+            }
+        }
+    }
+    intpPts[count].x = (double)i;
+    intpPts[count].y = 0.0;
+    indices[count] = mapPtr->indices[i];
+    count++;
+    nIntpPts = count;
+    result = FALSE;
+    if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+        result = Blt_NaturalParametricSpline(origPts, nOrigPts, &exts, FALSE,
+                            intpPts, nIntpPts);
+    } else if (linePtr->smooth == PEN_SMOOTH_CATROM) {
+        result = Blt_CatromParametricSpline(origPts, nOrigPts, intpPts,
+                                            nIntpPts);
+    }
+    if (!result) {
+        /* The spline interpolation failed.  We'll fallback to the
+         * current coordinates and do no smoothing (standard line
+         * segments).  */
+        linePtr->smooth = PEN_SMOOTH_NONE;
+        Blt_Free(intpPts);
+        Blt_Free(indices);
+    } else {
+        Blt_Free(mapPtr->screenPts);
+        Blt_Free(mapPtr->indices);
+        mapPtr->indices = indices;
+        mapPtr->screenPts = intpPts;
+        mapPtr->nScreenPts = nIntpPts;
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapSymbols --
+ *
+ *	Creates two arrays of points and pen indices, filled with
+ *	the screen coordinates of the visible
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapSymbols(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    Extents2D exts;
+    Point2D *symbolPts;
+    int *indices;
+    register int i, count;
+
+    symbolPts = Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+    assert(symbolPts);
+
+    indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+    assert(indices);
+
+    Blt_GraphExtents(graphPtr, &exts);
+    count = 0;			/* Count the number of visible points */
+
+    for (i = 0; i < mapPtr->nScreenPts; i++) {
+	if (PointInRegion(&exts, mapPtr->screenPts[i].x, 
+		  mapPtr->screenPts[i].y)) {
+	    symbolPts[count].x = mapPtr->screenPts[i].x;
+	    symbolPts[count].y = mapPtr->screenPts[i].y;
+	    indices[count] = mapPtr->indices[i];
+	    count++;
+	}
+    }
+    linePtr->symbolPts = symbolPts;
+    linePtr->nSymbolPts = count;
+    linePtr->symbolToData = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapActiveSymbols --
+ *
+ *	Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is freed and allocated for the active point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapActiveSymbols(graphPtr, linePtr)
+    Graph *graphPtr;
+    Line *linePtr;
+{
+    Extents2D exts;
+    double x, y;
+    int count;
+    Point2D *activePts;
+    register int i;
+    int pointIndex;
+    int nPoints;
+    int *activeToData;
+
+    if (linePtr->activePts != NULL) {
+	Blt_Free(linePtr->activePts);
+	linePtr->activePts = NULL;
+    }
+    if (linePtr->activeToData != NULL) {
+	Blt_Free(linePtr->activeToData);
+	linePtr->activeToData = NULL;
+    }
+    Blt_GraphExtents(graphPtr, &exts);
+    activePts = Blt_Malloc(sizeof(Point2D) * linePtr->nActiveIndices);
+    assert(activePts);
+    activeToData = Blt_Malloc(sizeof(int) * linePtr->nActiveIndices);
+    nPoints = NumberOfPoints(linePtr);
+    count = 0;			/* Count the visible active points */
+    for (i = 0; i < linePtr->nActiveIndices; i++) {
+	pointIndex = linePtr->activeIndices[i];
+	if (pointIndex >= nPoints) {
+	    continue;		/* Index not available */
+	}
+	x = linePtr->x.valueArr[pointIndex];
+	y = linePtr->y.valueArr[pointIndex];
+	activePts[count] = Blt_Map2D(graphPtr, x, y, &(linePtr->axes));
+	activeToData[count] = pointIndex;
+	if (PointInRegion(&exts, activePts[count].x, activePts[count].y)) {
+	    count++;
+	}
+    }
+    if (count > 0) {
+	linePtr->activePts = activePts;
+	linePtr->activeToData = activeToData;
+    } else {
+	/* No active points were visible. */
+	Blt_Free(activePts);
+	Blt_Free(activeToData);	
+    }
+    linePtr->nActivePts = count;
+    linePtr->flags &= ~ACTIVE_PENDING;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapStrip --
+ *
+ *	Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is  allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapStrip(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    Extents2D exts;
+    Segment2D *strips;
+    int *indices, *indexPtr;
+    register Point2D *endPtr, *pointPtr;
+    register Segment2D *segPtr;
+    register int count;
+
+    indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+    assert(indices);
+
+    /* 
+     * Create array to hold points for line segments (not polyline 
+     * coordinates).  So allocate twice the number of points.  
+     */
+    segPtr = strips = Blt_Malloc(mapPtr->nScreenPts * sizeof(Segment2D));
+    assert(strips);
+
+    Blt_GraphExtents(graphPtr, &exts);
+    count = 0;			/* Count the number of segments. */
+    indexPtr = mapPtr->indices;
+    for (pointPtr = mapPtr->screenPts, 
+	     endPtr = mapPtr->screenPts + (mapPtr->nScreenPts - 1);
+	 pointPtr < endPtr; pointPtr++, indexPtr++) {
+	segPtr->p = pointPtr[0];
+	segPtr->q = pointPtr[1];
+	if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+	    segPtr++;
+	    indices[count] = *indexPtr;
+	    count++;
+	}
+    }
+    linePtr->stripToData = indices;
+    linePtr->nStrips = count;
+    linePtr->strips = strips;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ *	Reorders the both arrays of points and segments to merge pens.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The old arrays are freed and new ones allocated containing
+ *	the reordered points and segments.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MergePens(linePtr, dataToStyle)
+    Line *linePtr;
+    PenStyle **dataToStyle;
+{
+    LinePenStyle *stylePtr;
+    register int i;
+    Blt_ChainLink *linkPtr;
+
+    if (Blt_ChainGetLength(linePtr->palette) < 2) {
+	linkPtr = Blt_ChainFirstLink(linePtr->palette);
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->nStrips = linePtr->nStrips;
+	stylePtr->strips = linePtr->strips;
+	stylePtr->nSymbolPts = linePtr->nSymbolPts;
+	stylePtr->symbolPts = linePtr->symbolPts;
+	stylePtr->xErrorBarCnt = linePtr->xErrorBarCnt;
+	stylePtr->yErrorBarCnt = linePtr->yErrorBarCnt;
+	stylePtr->xErrorBars = linePtr->xErrorBars;
+	stylePtr->yErrorBars = linePtr->yErrorBars;
+	stylePtr->errorBarCapWidth = linePtr->errorBarCapWidth;
+	return;
+    }
+
+    /* We have more than one style. Group line segments and points of
+     * like pen styles.  */
+
+    if (linePtr->nStrips > 0) {
+	Segment2D *strips;
+	int *stripToData;
+	register Segment2D *segPtr;
+	register int *indexPtr;
+	int dataIndex;
+
+	strips = Blt_Malloc(linePtr->nStrips * sizeof(Segment2D));
+	stripToData = Blt_Malloc(linePtr->nStrips * sizeof(int));
+	assert(strips && stripToData);
+	segPtr = strips, indexPtr = stripToData;
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->strips = segPtr;
+	    for (i = 0; i < linePtr->nStrips; i++) {
+		dataIndex = linePtr->stripToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *segPtr++ = linePtr->strips[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->nStrips = segPtr - stylePtr->strips;
+	}
+	Blt_Free(linePtr->strips);
+	linePtr->strips = strips;
+	Blt_Free(linePtr->stripToData);
+	linePtr->stripToData = stripToData;
+    }
+    if (linePtr->nSymbolPts > 0) {
+	int *indexPtr;
+	register Point2D *symbolPts, *pointPtr;
+	register int *symbolToData;
+	int dataIndex;
+
+	symbolPts = Blt_Malloc(linePtr->nSymbolPts * sizeof(Point2D));
+	symbolToData = Blt_Malloc(linePtr->nSymbolPts * sizeof(int));
+	assert(symbolPts && symbolToData);
+	pointPtr = symbolPts, indexPtr = symbolToData;
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->symbolPts = pointPtr;
+	    for (i = 0; i < linePtr->nSymbolPts; i++) {
+		dataIndex = linePtr->symbolToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *pointPtr++ = linePtr->symbolPts[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->nSymbolPts = pointPtr - stylePtr->symbolPts;
+	}
+	Blt_Free(linePtr->symbolPts);
+	linePtr->symbolPts = symbolPts;
+	Blt_Free(linePtr->symbolToData);
+	linePtr->symbolToData = symbolToData;
+    }
+    if (linePtr->xErrorBarCnt > 0) {
+	Segment2D *xErrorBars, *segPtr;
+	int *xErrorToData, *indexPtr;
+	int dataIndex;
+
+	xErrorBars = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(Segment2D));
+	xErrorToData = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(int));
+	assert(xErrorBars);
+	segPtr = xErrorBars, indexPtr = xErrorToData;
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->xErrorBars = segPtr;
+	    for (i = 0; i < linePtr->xErrorBarCnt; i++) {
+		dataIndex = linePtr->xErrorToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *segPtr++ = linePtr->xErrorBars[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
+	}
+	Blt_Free(linePtr->xErrorBars);
+	linePtr->xErrorBars = xErrorBars;
+	Blt_Free(linePtr->xErrorToData);
+	linePtr->xErrorToData = xErrorToData;
+    }
+    if (linePtr->yErrorBarCnt > 0) {
+	Segment2D *errorBars, *segPtr;
+	int *errorToData, *indexPtr;
+	int dataIndex;
+
+	errorBars = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(Segment2D));
+	errorToData = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(int));
+	assert(errorBars);
+	segPtr = errorBars, indexPtr = errorToData;
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    stylePtr->yErrorBars = segPtr;
+	    for (i = 0; i < linePtr->yErrorBarCnt; i++) {
+		dataIndex = linePtr->yErrorToData[i];
+		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+		    *segPtr++ = linePtr->yErrorBars[i];
+		    *indexPtr++ = dataIndex;
+		}
+	    }
+	    stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
+	}
+	Blt_Free(linePtr->yErrorBars);
+	linePtr->yErrorBars = errorBars;
+	Blt_Free(linePtr->yErrorToData);
+	linePtr->yErrorToData = errorToData;
+    }
+}
+
+#define CLIP_TOP	(1<<0)
+#define CLIP_BOTTOM	(1<<1)
+#define CLIP_RIGHT	(1<<2)
+#define CLIP_LEFT	(1<<3)
+
+INLINE static int
+OutCode(extsPtr, p)
+    Extents2D *extsPtr;
+    Point2D *p;
+{
+    int code;
+
+    code = 0;
+    if (p->x > extsPtr->right) {
+	code |= CLIP_RIGHT;
+    } else if (p->x < extsPtr->left) {
+	code |= CLIP_LEFT;
+    }
+    if (p->y > extsPtr->bottom) {
+	code |= CLIP_BOTTOM;
+    } else if (p->y < extsPtr->top) {
+	code |= CLIP_TOP;
+    }
+    return code;
+}
+
+static int
+ClipSegment(extsPtr, code1, code2, p, q)
+    Extents2D *extsPtr;
+    register int code1, code2;
+    register Point2D *p, *q;
+{
+    int inside, outside;
+
+    inside = ((code1 | code2) == 0);
+    outside = ((code1 & code2) != 0);
+
+    /*
+     * In the worst case, we'll clip the line segment against each of
+     * the four sides of the bounding rectangle.
+     */
+    while ((!outside) && (!inside)) {
+	if (code1 == 0) {
+	    Point2D *tmp;
+	    int code;
+
+	    /* Swap pointers and out codes */
+	    tmp = p, p = q, q = tmp;
+	    code = code1, code1 = code2, code2 = code;
+	}
+	if (code1 & CLIP_LEFT) {
+	    p->y += (q->y - p->y) *
+		(extsPtr->left - p->x) / (q->x - p->x);
+	    p->x = extsPtr->left;
+	} else if (code1 & CLIP_RIGHT) {
+	    p->y += (q->y - p->y) *
+		(extsPtr->right - p->x) / (q->x - p->x);
+	    p->x = extsPtr->right;
+	} else if (code1 & CLIP_BOTTOM) {
+	    p->x += (q->x - p->x) *
+		(extsPtr->bottom - p->y) / (q->y - p->y);
+	    p->y = extsPtr->bottom;
+	} else if (code1 & CLIP_TOP) {
+	    p->x += (q->x - p->x) *
+		(extsPtr->top - p->y) / (q->y - p->y);
+	    p->y = extsPtr->top;
+	}
+	code1 = OutCode(extsPtr, p);
+
+	inside = ((code1 | code2) == 0);
+	outside = ((code1 & code2) != 0);
+    }
+    return (!inside);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SaveTrace --
+ *
+ *	Creates a new trace and inserts it into the line's
+ *	list of traces.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SaveTrace(linePtr, start, length, mapPtr)
+    Line *linePtr;
+    int start;			/* Starting index of the trace in data point
+				 * array.  Used to figure out closest point */
+    int length;			/* Number of points forming the trace */
+    MapInfo *mapPtr;
+{
+    Trace *tracePtr;
+    Point2D *screenPts;
+    int *indices;
+    register int i, j;
+
+    tracePtr = Blt_Malloc(sizeof(Trace));
+    assert(tracePtr);
+    screenPts = Blt_Malloc(sizeof(Point2D) * length);
+    assert(screenPts);
+    indices = Blt_Malloc(sizeof(int) * length);
+    assert(indices);
+
+    /* Copy the screen coordinates of the trace into the point array */
+
+    if (mapPtr->indices != NULL) {
+	for (i = 0, j = start; i < length; i++, j++) {
+	    screenPts[i].x = mapPtr->screenPts[j].x;
+	    screenPts[i].y = mapPtr->screenPts[j].y;
+	    indices[i] = mapPtr->indices[j];
+	}
+    } else {
+	for (i = 0, j = start; i < length; i++, j++) {
+	    screenPts[i].x = mapPtr->screenPts[j].x;
+	    screenPts[i].y = mapPtr->screenPts[j].y;
+	    indices[i] = j;
+	}
+    }
+    tracePtr->nScreenPts = length;
+    tracePtr->screenPts = screenPts;
+    tracePtr->symbolToData = indices;
+    tracePtr->start = start;
+    if (linePtr->traces == NULL) {
+	linePtr->traces = Blt_ChainCreate();
+    }
+    Blt_ChainAppend(linePtr->traces, tracePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeTraces --
+ *
+ *	Deletes all the traces for the line.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeTraces(linePtr)
+    Line *linePtr;
+{
+    Blt_ChainLink *linkPtr;
+    Trace *tracePtr;
+
+    for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tracePtr = Blt_ChainGetValue(linkPtr);
+	Blt_Free(tracePtr->symbolToData);
+	Blt_Free(tracePtr->screenPts);
+	Blt_Free(tracePtr);
+    }
+    Blt_ChainDestroy(linePtr->traces);
+    linePtr->traces = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapTraces --
+ *
+ *	Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is  allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapTraces(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    int start, count;
+    int code1, code2;
+    Point2D *p, *q;
+    Point2D s;
+    Extents2D exts;
+    register int i;
+    int broken, offscreen;
+
+    Blt_GraphExtents(graphPtr, &exts);
+    count = 1;
+    code1 = OutCode(&exts, mapPtr->screenPts);
+    p = mapPtr->screenPts;
+    q = p + 1;
+    for (i = 1; i < mapPtr->nScreenPts; i++, p++, q++) {
+	code2 = OutCode(&exts, q);
+	if (code2 != 0) {
+	    /* Save the coordinates of the last point, before clipping */
+	    s = *q;
+	}
+	broken = BROKEN_TRACE(linePtr->penDir, p->x, q->x);
+	offscreen = ClipSegment(&exts, code1, code2, p, q);
+	if (broken || offscreen) {
+
+	    /*
+	     * The last line segment is either totally clipped by the plotting
+	     * area or the x-direction is wrong, breaking the trace.  Either
+	     * way, save information about the last trace (if one exists),
+	     * discarding the current line segment
+	     */
+
+	    if (count > 1) {
+		start = i - count;
+		SaveTrace(linePtr, start, count, mapPtr);
+		count = 1;
+	    }
+	} else {
+	    count++;		/* Add the point to the trace. */
+	    if (code2 != 0) {
+
+		/*
+		 * If the last point is clipped, this means that the trace is
+		 * broken after this point.  Restore the original coordinate
+		 * (before clipping) after saving the trace.
+		 */
+
+		start = i - (count - 1);
+		SaveTrace(linePtr, start, count, mapPtr);
+		mapPtr->screenPts[i] = s;
+		count = 1;
+	    }
+	}
+	code1 = code2;
+    }
+    if (count > 1) {
+	start = i - count;
+	SaveTrace(linePtr, start, count, mapPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapFillArea --
+ *
+ *	Creates an array of points that represent a polygon that fills
+ *	the area under the element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is  allocated for the polygon point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapFillArea(graphPtr, linePtr, mapPtr)
+    Graph *graphPtr;
+    Line *linePtr;
+    MapInfo *mapPtr;
+{
+    Point2D *origPts, *clipPts;
+    Extents2D exts;
+    double maxY;
+    register int i, n;
+
+    if (linePtr->fillPts != NULL) {
+	Blt_Free(linePtr->fillPts);
+	linePtr->fillPts = NULL;
+	linePtr->nFillPts = 0;
+    }
+    if (mapPtr->nScreenPts < 3) {
+	return;
+    }
+    n = mapPtr->nScreenPts + 3;
+    Blt_GraphExtents(graphPtr, &exts);
+
+    maxY = (double)graphPtr->bottom;
+    origPts = Blt_Malloc(sizeof(Point2D) * n);
+    for (i = 0; i < mapPtr->nScreenPts; i++) {
+	origPts[i].x = mapPtr->screenPts[i].x + 1;
+	origPts[i].y = mapPtr->screenPts[i].y;
+	if (origPts[i].y > maxY) {
+	    maxY = origPts[i].y;
+	}
+    }	
+    /* Add edges to make (if necessary) the polygon fill to the bottom
+     * of plotting window */
+    origPts[i].x = origPts[i - 1].x;
+    origPts[i].y = maxY;
+    i++;
+    origPts[i].x = origPts[0].x; 
+    origPts[i].y = maxY;
+    i++;
+    origPts[i] = origPts[0];
+
+    clipPts = Blt_Malloc(sizeof(Point2D) * n * 3);
+    assert(clipPts);
+    n = Blt_PolyRectClip(&exts, origPts, n - 1, clipPts);
+
+    Blt_Free(origPts);
+    if (n < 3) {
+	Blt_Free(clipPts);
+    } else {
+	linePtr->fillPts = clipPts;
+	linePtr->nFillPts = n;
+    }
+}
+
+static void
+ResetLine(linePtr)
+    Line *linePtr;
+{
+    FreeTraces(linePtr);
+    ClearPalette(linePtr->palette);
+    if (linePtr->symbolPts != NULL) {
+	Blt_Free(linePtr->symbolPts);
+    }
+    if (linePtr->symbolToData != NULL) {
+	Blt_Free(linePtr->symbolToData);
+    }
+    if (linePtr->strips != NULL) {
+	Blt_Free(linePtr->strips);
+    }
+    if (linePtr->stripToData != NULL) {
+	Blt_Free(linePtr->stripToData);
+    }
+    if (linePtr->activePts != NULL) {
+	Blt_Free(linePtr->activePts);
+    }
+    if (linePtr->activeToData != NULL) {
+	Blt_Free(linePtr->activeToData);
+    }
+    if (linePtr->xErrorBars != NULL) {
+	Blt_Free(linePtr->xErrorBars);
+    }
+    if (linePtr->xErrorToData != NULL) {
+	Blt_Free(linePtr->xErrorToData);
+    }
+    if (linePtr->yErrorBars != NULL) {
+	Blt_Free(linePtr->yErrorBars);
+    }
+    if (linePtr->yErrorToData != NULL) {
+	Blt_Free(linePtr->yErrorToData);
+    }
+    linePtr->xErrorBars = linePtr->yErrorBars = linePtr->strips = NULL;
+    linePtr->symbolPts = linePtr->activePts = NULL;
+    linePtr->stripToData = linePtr->symbolToData = linePtr->xErrorToData = 
+	linePtr->yErrorToData = linePtr->activeToData = NULL;
+    linePtr->nActivePts = linePtr->nSymbolPts = linePtr->nStrips = 
+	linePtr->xErrorBarCnt = linePtr->yErrorBarCnt = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapLine --
+ *
+ *	Calculates the actual window coordinates of the line element.
+ *	The window coordinates are saved in an allocated point array.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory is (re)allocated for the point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapLine(graphPtr, elemPtr)
+    Graph *graphPtr;		/* Graph widget record */
+    Element *elemPtr;		/* Element component record */
+{
+    Line *linePtr = (Line *)elemPtr;
+    MapInfo mapInfo;
+    int size, nPoints;
+    PenStyle **dataToStyle;
+    Blt_ChainLink *linkPtr;
+    LinePenStyle *stylePtr;
+    
+    ResetLine(linePtr);
+    nPoints = NumberOfPoints(linePtr);
+    if (nPoints < 1) {
+	return;			/* No data points */
+    }
+    GetScreenPoints(graphPtr, linePtr, &mapInfo);
+    MapSymbols(graphPtr, linePtr, &mapInfo);
+
+    if ((linePtr->flags & ACTIVE_PENDING) && (linePtr->nActiveIndices > 0)) {
+	MapActiveSymbols(graphPtr, linePtr);
+    }
+    /*
+     * Map connecting line segments if they are to be displayed.
+     */
+    if ((nPoints > 1) && ((graphPtr->classUid == bltStripElementUid) ||
+	    (linePtr->builtinPen.traceWidth > 0))) {
+	linePtr->smooth = linePtr->reqSmooth;
+
+	/*
+	 * Do smoothing if necessary.  This can extend the coordinate array,
+	 * so both mapInfo.points and mapInfo.nPoints may change.
+	 */
+
+	switch (linePtr->smooth) {
+	case PEN_SMOOTH_STEP:
+	    GenerateSteps(&mapInfo);
+	    break;
+
+	case PEN_SMOOTH_NATURAL:
+	case PEN_SMOOTH_QUADRATIC:
+	    if (mapInfo.nScreenPts < 3) {
+		/* Can't interpolate with less than three points. */
+		linePtr->smooth = PEN_SMOOTH_NONE;
+	    } else {
+		GenerateSpline(graphPtr, linePtr, &mapInfo);
+	    }
+	    break;
+
+	case PEN_SMOOTH_CATROM:
+	    if (mapInfo.nScreenPts < 3) {
+		/* Can't interpolate with less than three points. */
+		linePtr->smooth = PEN_SMOOTH_NONE;
+	    } else {
+		GenerateParametricSpline(graphPtr, linePtr, &mapInfo);
+	    }
+	    break;
+
+	default:
+	    break;
+	}
+	if (linePtr->rTolerance > 0.0) {
+	    ReducePoints(&mapInfo, linePtr->rTolerance);
+	}
+	if ((linePtr->fillTile != NULL) || (linePtr->fillStipple != None)) {
+	    MapFillArea(graphPtr, linePtr, &mapInfo);
+	}
+	if (graphPtr->classUid == bltStripElementUid) {
+	    MapStrip(graphPtr, linePtr, &mapInfo);
+	} else {
+	    MapTraces(graphPtr, linePtr, &mapInfo);
+	}
+    }
+    Blt_Free(mapInfo.screenPts);
+    Blt_Free(mapInfo.indices);
+
+    /* Set the symbol size of all the pen styles. */
+    for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	size = ScaleSymbol(elemPtr, stylePtr->penPtr->symbol.size);
+	stylePtr->symbolSize = size;
+	stylePtr->errorBarCapWidth = (stylePtr->penPtr->errorBarCapWidth > 0) 
+	    ? stylePtr->penPtr->errorBarCapWidth : (int)(size * 0.6666666);
+	stylePtr->errorBarCapWidth /= 2;
+    }
+    dataToStyle = Blt_StyleMap((Element *)linePtr);
+    if (((linePtr->yHigh.nValues > 0) && (linePtr->yLow.nValues > 0)) ||
+	((linePtr->xHigh.nValues > 0) && (linePtr->xLow.nValues > 0)) ||
+	(linePtr->xError.nValues > 0) || (linePtr->yError.nValues > 0)) {
+	Blt_MapErrorBars(graphPtr, (Element *)linePtr, dataToStyle);
+    }
+    MergePens(linePtr, dataToStyle);
+    Blt_Free(dataToStyle);
+}
+
+static double
+DistanceToLine(x, y, p, q, t)
+    int x, y;			/* Sample X-Y coordinate. */
+    Point2D *p, *q;		/* End points of the line segment. */
+    Point2D *t;			/* (out) Point on line segment. */
+{
+    double right, left, top, bottom;
+
+    *t = Blt_GetProjection(x, y, p, q);
+    if (p->x > q->x) {
+	right = p->x, left = q->x;
+    } else {
+	left = p->x, right = q->x;
+    }
+    if (p->y > q->y) {
+	bottom = p->y, top = q->y;
+    } else {
+	top = p->y, bottom = q->y;
+    }
+    if (t->x > right) {
+	t->x = right;
+    } else if (t->x < left) {
+	t->x = left;
+    }
+    if (t->y > bottom) {
+	t->y = bottom;
+    } else if (t->y < top) {
+	t->y = top;
+    }
+    return hypot((t->x - x), (t->y - y));
+}
+
+static double
+DistanceToX(x, y, p, q, t)
+    int x, y;			/* Search X-Y coordinate. */
+    Point2D *p, *q;		/* End points of the line segment. */
+    Point2D *t;			/* (out) Point on line segment. */
+{
+    double dx, dy;
+    double dist;
+
+    if (p->x > q->x) {
+	if ((x > p->x) || (x < q->x)) {
+	    return DBL_MAX; /* X-coordinate outside line segment. */
+	}
+    } else {
+	if ((x > q->x) || (x < p->x)) {
+	    return DBL_MAX; /* X-coordinate outside line segment. */
+	}
+    }
+    dx = p->x - q->x;
+    dy = p->y - q->y;
+    t->x = (double)x;
+    if (FABS(dx) < DBL_EPSILON) {
+	double d1, d2;
+	/* Same X-coordinate indicates a vertical line.  Pick the
+	 * closest end point. */
+	d1 = p->y - y;
+	d2 = q->y - y;
+	if (FABS(d1) < FABS(d2)) {
+	    t->y = p->y, dist = d1;
+	} else {
+	    t->y = q->y, dist = d2;
+	}
+    } else if (FABS(dy) < DBL_EPSILON) {
+	/* Horizontal line. */
+	t->y = p->y, dist = p->y - y;
+    } else {
+	double m, b;
+		
+	m = dy / dx;
+	b = p->y - (m * p->x);
+	t->y = (x * m) + b;
+	dist = y - t->y;
+    }
+   return FABS(dist);
+}
+
+static double
+DistanceToY(x, y, p, q, t)
+    int x, y;			/* Search X-Y coordinate. */
+    Point2D *p, *q;		/* End points of the line segment. */
+    Point2D *t;			/* (out) Point on line segment. */
+{
+    double dx, dy;
+    double dist;
+
+    if (p->y > q->y) {
+	if ((y > p->y) || (y < q->y)) {
+	    return DBL_MAX;
+	}
+    } else {
+	if ((y > q->y) || (y < p->y)) {
+	    return DBL_MAX;
+	}
+    }
+    dx = p->x - q->x;
+    dy = p->y - q->y;
+    t->y = y;
+    if (FABS(dy) < DBL_EPSILON) {
+	double d1, d2;
+
+	/* Save Y-coordinate indicates an horizontal line. Pick the
+	 * closest end point. */
+	d1 = p->x - x;
+	d2 = q->x - x;
+	if (FABS(d1) < FABS(d2)) {
+	    t->x = p->x, dist = d1;
+	} else {
+	    t->x = q->x, dist = d2;
+	}
+    } else if (FABS(dx) < DBL_EPSILON) {
+	/* Vertical line. */
+	t->x = p->x, dist = p->x - x;
+    } else {
+	double m, b;
+	
+	m = dy / dx;
+	b = p->y - (m * p->x);
+	t->x = (y - b) / m;
+	dist = x - t->x;
+    }
+    return FABS(dist);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestTrace --
+ *
+ *	Find the line segment closest to the given window coordinate
+ *	in the element.
+ *
+ * Results:
+ *	If a new minimum distance is found, the information regarding
+ *	it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestTrace(graphPtr, linePtr, searchPtr, distProc)
+    Graph *graphPtr;		/* Graph widget record */
+    Line *linePtr;		/* Line element record */
+    ClosestSearch *searchPtr;	/* Info about closest point in element */
+    DistanceProc *distProc;
+{
+    Blt_ChainLink *linkPtr;
+    Point2D closest, b;
+    Trace *tracePtr;
+    double dist, minDist;
+    register Point2D *pointPtr, *endPtr;
+    int i;
+
+    i = -1;			/* Suppress compiler warning. */
+    minDist = searchPtr->dist;
+    for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tracePtr = Blt_ChainGetValue(linkPtr);
+	for (pointPtr = tracePtr->screenPts, 
+		 endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+	     pointPtr < endPtr; pointPtr++) {
+	    dist = (*distProc)(searchPtr->x, searchPtr->y, pointPtr, 
+		pointPtr + 1, &b);
+	    if (dist < minDist) {
+		closest = b;
+		i = tracePtr->symbolToData[pointPtr - tracePtr->screenPts];
+		minDist = dist;
+	    }
+	}
+    }
+    if (minDist < searchPtr->dist) {
+	searchPtr->dist = minDist;
+	searchPtr->elemPtr = (Element *)linePtr;
+	searchPtr->index = i;
+	searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+	    &(linePtr->axes));
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestStrip --
+ *
+ *	Find the line segment closest to the given window coordinate
+ *	in the element.
+ *
+ * Results:
+ *	If a new minimum distance is found, the information regarding
+ *	it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestStrip(graphPtr, linePtr, searchPtr, distProc)
+    Graph *graphPtr;		/* Graph widget record */
+    Line *linePtr;		/* Line element record */
+    ClosestSearch *searchPtr;	/* Info about closest point in element */
+    DistanceProc *distProc;
+{
+    Point2D closest, b;
+    double dist, minDist;
+    int count;
+    int i;
+    register Segment2D *s;
+
+    i = 0;
+    minDist = searchPtr->dist;
+    s = linePtr->strips;
+    for (count = 0; count < linePtr->nStrips; count++, s++) {
+	dist = (*distProc)(searchPtr->x, searchPtr->y, &(s->p), &(s->q), &b);
+	if (dist < minDist) {
+	    closest = b;
+	    i = linePtr->stripToData[count];
+	    minDist = dist;
+	}
+    }
+    if (minDist < searchPtr->dist) {
+	searchPtr->dist = minDist;
+	searchPtr->elemPtr = (Element *)linePtr;
+	searchPtr->index = i;
+	searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+	    &(linePtr->axes));
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestPoint --
+ *
+ *	Find the element whose data point is closest to the given screen
+ *	coordinate.
+ *
+ * Results:
+ *	If a new minimum distance is found, the information regarding
+ *	it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestPoint(linePtr, searchPtr)
+    Line *linePtr;		/* Line element that we are looking at */
+    ClosestSearch *searchPtr;	/* Assorted information related to searching
+				 * for the closest point */
+{
+    double dist, minDist;
+    double dx, dy;
+    int count, i;
+    register Point2D *pointPtr;
+
+    minDist = searchPtr->dist;
+    i = 0;
+
+    /*
+     * Instead of testing each data point in graph coordinates, look at
+     * the array of mapped screen coordinates. The advantages are
+     *   1) only examine points that are visible (unclipped), and
+     *   2) the computed distance is already in screen coordinates.
+     */
+    pointPtr = linePtr->symbolPts;
+    for (count = 0; count < linePtr->nSymbolPts; count++, pointPtr++) {
+	dx = (double)(searchPtr->x - pointPtr->x);
+	dy = (double)(searchPtr->y - pointPtr->y);
+	if (searchPtr->along == SEARCH_BOTH) {
+	    dist = hypot(dx, dy);
+	} else if (searchPtr->along == SEARCH_X) {
+	    dist = dx;
+	} else if (searchPtr->along == SEARCH_Y) {
+	    dist = dy;
+	} else {
+	    /* This can't happen */
+	    continue;
+	}
+	if (dist < minDist) {
+	    i = linePtr->symbolToData[count];
+	    minDist = dist;
+	}
+    }
+    if (minDist < searchPtr->dist) {
+	searchPtr->elemPtr = (Element *)linePtr;
+	searchPtr->dist = minDist;
+	searchPtr->index = i;
+	searchPtr->point.x = linePtr->x.valueArr[i];
+	searchPtr->point.y = linePtr->y.valueArr[i];
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetLineExtents --
+ *
+ *	Retrieves the range of the line element
+ *
+ * Results:
+ *	Returns the number of data points in the element.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetLineExtents(elemPtr, extsPtr)
+    Element *elemPtr;
+    Extents2D *extsPtr;
+{
+    int nPoints;
+
+    extsPtr->top = extsPtr->left = DBL_MAX;
+    extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+    nPoints = NumberOfPoints(elemPtr);
+    if (nPoints < 1) {
+	return;
+    } 
+    extsPtr->right = elemPtr->x.max;
+    if ((elemPtr->x.min <= 0.0) && (elemPtr->axes.x->logScale)) {
+	extsPtr->left = Blt_FindElemVectorMinimum(&elemPtr->x, DBL_MIN);
+    } else {
+	extsPtr->left = elemPtr->x.min;
+    }
+    extsPtr->bottom = elemPtr->y.max;
+    if ((elemPtr->y.min <= 0.0) && (elemPtr->axes.y->logScale)) {
+	extsPtr->top = Blt_FindElemVectorMinimum(&elemPtr->y, DBL_MIN);
+    } else {
+	extsPtr->top = elemPtr->y.min;
+    }
+
+    /* Correct the data limits for error bars */
+
+    if (elemPtr->xError.nValues > 0) {
+	register int i;
+	double x;
+	
+	nPoints = MIN(elemPtr->xError.nValues, nPoints);
+	for (i = 0; i < nPoints; i++) {
+	    x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
+	    if (x > extsPtr->right) {
+		extsPtr->right = x;
+	    }
+	    x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
+	    if (elemPtr->axes.x->logScale) {
+		if (x < 0.0) {
+		    x = -x;	/* Mirror negative values, instead
+				 * of ignoring them. */
+		}
+		if ((x > DBL_MIN) && (x < extsPtr->left)) {
+		    extsPtr->left = x;
+		}
+	    } else if (x < extsPtr->left) {
+		extsPtr->left = x;
+	    }
+	}		     
+    } else {
+	if ((elemPtr->xHigh.nValues > 0) && 
+	    (elemPtr->xHigh.max > extsPtr->right)) {
+	    extsPtr->right = elemPtr->xHigh.max;
+	}
+	if (elemPtr->xLow.nValues > 0) {
+	    double left;
+	    
+	    if ((elemPtr->xLow.min <= 0.0) && 
+		(elemPtr->axes.x->logScale)) {
+		left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
+	    } else {
+		left = elemPtr->xLow.min;
+	    }
+	    if (left < extsPtr->left) {
+		extsPtr->left = left;
+	    }
+	}
+    }
+    
+    if (elemPtr->yError.nValues > 0) {
+	register int i;
+	double y;
+	
+	nPoints = MIN(elemPtr->yError.nValues, nPoints);
+	for (i = 0; i < nPoints; i++) {
+	    y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
+	    if (y > extsPtr->bottom) {
+		extsPtr->bottom = y;
+	    }
+	    y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
+	    if (elemPtr->axes.y->logScale) {
+		if (y < 0.0) {
+		    y = -y;	/* Mirror negative values, instead
+				 * of ignoring them. */
+		}
+		if ((y > DBL_MIN) && (y < extsPtr->left)) {
+		    extsPtr->top = y;
+		}
+	    } else if (y < extsPtr->top) {
+		extsPtr->top = y;
+	    }
+	}		     
+    } else {
+	if ((elemPtr->yHigh.nValues > 0) && 
+	    (elemPtr->yHigh.max > extsPtr->bottom)) {
+	    extsPtr->bottom = elemPtr->yHigh.max;
+	}
+	if (elemPtr->yLow.nValues > 0) {
+	    double top;
+	    
+	    if ((elemPtr->yLow.min <= 0.0) && 
+		(elemPtr->axes.y->logScale)) {
+		top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
+	    } else {
+		top = elemPtr->yLow.min;
+	    }
+	    if (top < extsPtr->top) {
+		extsPtr->top = top;
+	    }
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ *	Rebuilds the designated GC with the new tile pixmap.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+    ClientData clientData;
+    Blt_Tile tile;		/* Not used. */
+{
+    Line *linePtr = clientData;
+    Graph *graphPtr;
+
+    graphPtr = linePtr->graphPtr;
+    if (graphPtr->tkwin != NULL) {
+	graphPtr->flags |= REDRAW_WORLD;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLine --
+ *
+ *	Sets up the appropriate configuration parameters in the GC.
+ *      It is assumed the parameters have been previously set by
+ *	a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information such as line width, line style, color
+ *	etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLine(graphPtr, elemPtr)
+    Graph *graphPtr;
+    Element *elemPtr;
+{
+    Line *linePtr = (Line *)elemPtr;
+    unsigned long gcMask;
+    XGCValues gcValues;
+    GC newGC;
+    Blt_ChainLink *linkPtr;
+
+    if (ConfigurePen(graphPtr, (Pen *)&(linePtr->builtinPen)) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /*
+     * Point to the static normal/active pens if no external pens have
+     * been selected.
+     */
+    if (linePtr->normalPenPtr == NULL) {
+	linePtr->normalPenPtr = &(linePtr->builtinPen);
+    }
+    linkPtr = Blt_ChainFirstLink(linePtr->palette);
+    if (linkPtr != NULL) {
+	LinePenStyle *stylePtr;
+
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	stylePtr->penPtr = linePtr->normalPenPtr;
+    }
+    if (linePtr->fillTile != NULL) {
+	Blt_SetTileChangedProc(linePtr->fillTile, TileChangedProc, linePtr);
+    }
+    /*
+     * Set the outline GC for this pen: GCForeground is outline color.
+     * GCBackground is the fill color (only used for bitmap symbols).
+     */
+    gcMask = 0;
+    if (linePtr->fillFgColor != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = linePtr->fillFgColor->pixel;
+    }
+    if (linePtr->fillBgColor != NULL) {
+	gcMask |= GCBackground;
+	gcValues.background = linePtr->fillBgColor->pixel;
+    }
+    if ((linePtr->fillStipple != None) && 
+	(linePtr->fillStipple != PATTERN_SOLID)) {
+	gcMask |= (GCStipple | GCFillStyle);
+	gcValues.stipple = linePtr->fillStipple;
+	gcValues.fill_style = (linePtr->fillBgColor == NULL) 
+	    ? FillStippled : FillOpaqueStippled;
+    }
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (linePtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+    }
+    linePtr->fillGC = newGC;
+
+    if (Blt_ConfigModified(linePtr->configSpecs, "-scalesymbols", 
+	(char *)NULL)) {
+	linePtr->flags |= (MAP_ITEM | SCALE_SYMBOL);
+    }
+    if (Blt_ConfigModified(linePtr->configSpecs, "-pixels", "-trace", "-*data",
+	 "-smooth", "-map*", "-label", "-hide", "-x", "-y", "-areapattern",
+			   (char *)NULL)) {
+	linePtr->flags |= MAP_ITEM;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestLine --
+ *
+ *	Find the closest point or line segment (if interpolated) to
+ *	the given window coordinate in the line element.
+ *
+ * Results:
+ *	Returns the distance of the closest point among other
+ *	information.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestLine(graphPtr, elemPtr, searchPtr)
+    Graph *graphPtr;		/* Graph widget record */
+    Element *elemPtr;		/* Element to examine */
+    ClosestSearch *searchPtr;	/* Info about closest point in element */
+{
+    Line *linePtr = (Line *)elemPtr;
+    int mode;
+
+    mode = searchPtr->mode;
+    if (mode == SEARCH_AUTO) {
+	LinePen *penPtr = linePtr->normalPenPtr;
+
+	mode = SEARCH_POINTS;
+	if ((NumberOfPoints(linePtr) > 1) && (penPtr->traceWidth > 0)) {
+	    mode = SEARCH_TRACES;
+	}
+    }
+    if (mode == SEARCH_POINTS) {
+	ClosestPoint(linePtr, searchPtr);
+    } else {
+	DistanceProc *distProc;
+	int found;
+
+	if (searchPtr->along == SEARCH_X) {
+	    distProc = DistanceToX;
+	} else if (searchPtr->along == SEARCH_Y) {
+	    distProc = DistanceToY;
+	} else {
+	    distProc = DistanceToLine;
+	}
+	if (elemPtr->classUid == bltStripElementUid) {
+	    found = ClosestStrip(graphPtr, linePtr, searchPtr, distProc);
+	} else {
+	    found = ClosestTrace(graphPtr, linePtr, searchPtr, distProc);
+	}
+	if ((!found) && (searchPtr->along != SEARCH_BOTH)) {
+	    ClosestPoint(linePtr, searchPtr);
+	}
+    }
+}
+
+/*
+ * XDrawLines() points: XMaxRequestSize(dpy) - 3
+ * XFillPolygon() points:  XMaxRequestSize(dpy) - 4
+ * XDrawSegments() segments:  (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawRectangles() rectangles:  (XMaxRequestSize(dpy) - 3) / 2
+ * XFillRectangles() rectangles:  (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawArcs() or XFillArcs() arcs:  (XMaxRequestSize(dpy) - 3) / 3
+ */
+
+#define MAX_DRAWLINES(d)	Blt_MaxRequestSize(d, sizeof(XPoint))
+#define MAX_DRAWPOLYGON(d)	Blt_MaxRequestSize(d, sizeof(XPoint))
+#define MAX_DRAWSEGMENTS(d)	Blt_MaxRequestSize(d, sizeof(XSegment))
+#define MAX_DRAWRECTANGLES(d)	Blt_MaxRequestSize(d, sizeof(XRectangle))
+#define MAX_DRAWARCS(d)		Blt_MaxRequestSize(d, sizeof(XArc))
+
+#ifdef WIN32
+
+static void
+DrawCircles(
+    Display *display,
+    Drawable drawable,
+    Line *linePtr,
+    LinePen *penPtr,
+    int nSymbolPts,
+    Point2D *symbolPts,
+    int radius)
+{
+    HBRUSH brush, oldBrush;
+    HPEN pen, oldPen;
+    HDC dc;
+    TkWinDCState state;
+    register Point2D *pointPtr, *endPtr;
+
+    if (drawable == None) {
+	return;			/* Huh? */
+    }
+    if ((penPtr->symbol.fillGC == NULL) && 
+	(penPtr->symbol.outlineWidth == 0)) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    /* SetROP2(dc, tkpWinRopModes[penPtr->symbol.fillGC->function]); */
+    if (penPtr->symbol.fillGC != NULL) {
+	brush = CreateSolidBrush(penPtr->symbol.fillGC->foreground);
+    } else {
+	brush = GetStockBrush(NULL_BRUSH);
+    }
+    if (penPtr->symbol.outlineWidth > 0) {
+	pen = Blt_GCToPen(dc, penPtr->symbol.outlineGC);
+    } else {
+	pen = GetStockPen(NULL_PEN);
+    }
+    oldPen = SelectPen(dc, pen);
+    oldBrush = SelectBrush(dc, brush);
+    for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	 pointPtr < endPtr; pointPtr++) {
+	Ellipse(dc, (int)pointPtr->x - radius, (int)pointPtr->y - radius,
+	    (int)pointPtr->x + radius + 1, (int)pointPtr->y + radius + 1);
+    }
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    DeletePen(SelectPen(dc, oldPen));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawCircles(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, radius)
+    Display *display;
+    Drawable drawable;
+    Line *linePtr;
+    LinePen *penPtr;
+    int nSymbolPts;
+    Point2D *symbolPts;
+    int radius;
+{
+    register int i;
+    XArc *arcArr;		/* Array of arcs (circle) */
+    register XArc *arcPtr;
+    int reqSize, nArcs;
+    int s;
+    int count;
+    register Point2D *pointPtr, *endPtr;
+
+    s = radius + radius;
+    arcArr = Blt_Malloc(nSymbolPts * sizeof(XArc));
+    arcPtr = arcArr;
+
+    if (linePtr->symbolInterval > 0) {
+	count = 0;
+	for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	     pointPtr < endPtr; pointPtr++) {
+	    if (DRAW_SYMBOL(linePtr)) {
+		arcPtr->x = (short int)pointPtr->x - radius;
+		arcPtr->y = (short int)pointPtr->y - radius;
+		arcPtr->width = arcPtr->height = (unsigned short)s;
+		arcPtr->angle1 = 0;
+		arcPtr->angle2 = 23040;
+		arcPtr++, count++;
+	    }
+	    linePtr->symbolCounter++;
+	}
+    } else {
+	count = nSymbolPts;
+	for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	     pointPtr < endPtr; pointPtr++) {
+	    arcPtr->x = (short int)pointPtr->x - radius;
+	    arcPtr->y = (short int)pointPtr->y - radius;
+	    arcPtr->width = arcPtr->height = (unsigned short)s;
+	    arcPtr->angle1 = 0;
+	    arcPtr->angle2 = 23040;
+	    arcPtr++;
+	}
+    }
+    reqSize = MAX_DRAWARCS(display);
+    for (i = 0; i < count; i += reqSize) {
+	nArcs = ((i + reqSize) > count) ? (count - i) : reqSize;
+	if (penPtr->symbol.fillGC != NULL) {
+	    XFillArcs(display, drawable, penPtr->symbol.fillGC, arcArr + i, 
+		      nArcs);
+	}
+	if (penPtr->symbol.outlineWidth > 0) {
+	    XDrawArcs(display, drawable, penPtr->symbol.outlineGC, arcArr + i, 
+		      nArcs);
+	}
+    }
+    Blt_Free(arcArr);
+}
+
+#endif
+
+static void
+DrawSquares(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, r)
+    Display *display;
+    Drawable drawable;
+    Line *linePtr;
+    LinePen *penPtr;
+    int nSymbolPts;
+    register Point2D *symbolPts;
+    int r;
+{
+    XRectangle *rectArr;
+    register Point2D *pointPtr, *endPtr;
+    register XRectangle *rectPtr;
+    int reqSize, nRects;
+    int s;
+    register int i;
+    int count;
+
+    s = r + r;
+    rectArr = Blt_Malloc(nSymbolPts * sizeof(XRectangle));
+    rectPtr = rectArr;
+
+    if (linePtr->symbolInterval > 0) {
+	count = 0;
+	for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	     pointPtr < endPtr; pointPtr++) {
+	    if (DRAW_SYMBOL(linePtr)) {
+		rectPtr->x = (short int)(pointPtr->x - r);
+		rectPtr->y = (short int)(pointPtr->y - r);
+		rectPtr->width = rectPtr->height = (unsigned short)s;
+		rectPtr++, count++;
+	    }
+	    linePtr->symbolCounter++;
+	}
+    } else {
+	count = nSymbolPts;
+	for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	     pointPtr < endPtr; pointPtr++) {
+	    rectPtr->x = (short int)(pointPtr->x - r);
+	    rectPtr->y = (short int)(pointPtr->y - r);
+	    rectPtr->width = rectPtr->height = (unsigned short)s;
+	    rectPtr++;
+	}
+    }
+    reqSize = MAX_DRAWRECTANGLES(display);
+    for (i = 0; i < count; i += reqSize) {
+	nRects = ((i + reqSize) > count) ? (count - i) : reqSize;
+	if (penPtr->symbol.fillGC != NULL) {
+	    XFillRectangles(display, drawable, penPtr->symbol.fillGC, 
+			    rectArr + i, nRects);
+	}
+	if (penPtr->symbol.outlineWidth > 0) {
+	    XDrawRectangles(display, drawable, penPtr->symbol.outlineGC, 
+			    rectArr + i, nRects);
+	}
+    }
+    Blt_Free(rectArr);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbols --
+ *
+ * 	Draw the symbols centered at the each given x,y coordinate
+ *	in the array of points.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws a symbol at each coordinate given.  If active,
+ *	only those coordinates which are currently active are
+ *	drawn.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbols(graphPtr, drawable, linePtr, penPtr, size, nSymbolPts, symbolPts)
+    Graph *graphPtr;		/* Graph widget record */
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Line *linePtr;
+    LinePen *penPtr;
+    int size;			/* Size of element */
+    int nSymbolPts;		/* Number of coordinates in array */
+    Point2D *symbolPts;		/* Array of x,y coordinates for line */
+{
+    XPoint pattern[13];		/* Template for polygon symbols */
+    int r1, r2;
+    register int i, n;
+    int count;
+    register Point2D *pointPtr, *endPtr;
+#define SQRT_PI		1.77245385090552
+#define S_RATIO		0.886226925452758
+
+    if (size < 3) {
+	if (penPtr->symbol.fillGC != NULL) {
+	    XPoint *points;
+	    
+	    points = Blt_Malloc(nSymbolPts * sizeof(XPoint));
+	    count = 0;
+	    for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		 pointPtr < endPtr; pointPtr++) {
+		points[count].x = (short int)pointPtr->x;
+		points[count].y = (short int)pointPtr->y;
+		count++;
+	    }
+	    XDrawPoints(graphPtr->display, drawable, penPtr->symbol.fillGC, 
+			points, nSymbolPts, CoordModeOrigin);
+	    Blt_Free(points);
+	}
+	return;
+    }
+    r1 = (int)ceil(size * 0.5);
+    r2 = (int)ceil(size * S_RATIO * 0.5);
+
+    switch (penPtr->symbol.type) {
+    case SYMBOL_NONE:
+	break;
+
+    case SYMBOL_SQUARE:
+	DrawSquares(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+	    symbolPts, r2);
+	break;
+
+    case SYMBOL_CIRCLE:
+	DrawCircles(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+	    symbolPts, r1);
+	break;
+
+    case SYMBOL_SPLUS:
+    case SYMBOL_SCROSS:
+	{
+	    XSegment *segArr;	/* Array of line segments (splus, scross) */
+	    register XSegment *segPtr;
+	    int reqSize, nSegs, chunk;
+
+	    if (penPtr->symbol.type == SYMBOL_SCROSS) {
+		r2 = Round(r2 * M_SQRT1_2);
+		pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2;
+		pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2;
+	    } else {
+		pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0;
+		pattern[0].x = pattern[2].y = -r2;
+		pattern[1].x = pattern[3].y = r2;
+	    }
+	    segArr = Blt_Malloc(nSymbolPts * 2 * sizeof(XSegment));
+	    segPtr = segArr;
+	    if (linePtr->symbolInterval > 0) {
+		count = 0;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    if (DRAW_SYMBOL(linePtr)) {
+			segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+			segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+			segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+			segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+			segPtr++;
+			segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+			segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+			segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+			segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+			segPtr++;
+			count++;
+		    }
+		    linePtr->symbolCounter++;
+		}
+	    } else {
+		count = nSymbolPts;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+		    segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+		    segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+		    segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+		    segPtr++;
+		    segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+		    segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+		    segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+		    segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+		    segPtr++;
+		}
+	    }
+	    nSegs = count * 2;
+	    /* Always draw skinny symbols regardless of the outline width */
+	    reqSize = MAX_DRAWSEGMENTS(graphPtr->display);
+	    for (i = 0; i < nSegs; i += reqSize) {
+		chunk = ((i + reqSize) > nSegs) ? (nSegs - i) : reqSize;
+		XDrawSegments(graphPtr->display, drawable, 
+			penPtr->symbol.outlineGC, segArr + i, chunk);
+	    }
+	    Blt_Free(segArr);
+	}
+	break;
+
+    case SYMBOL_PLUS:
+    case SYMBOL_CROSS:
+	{
+	    XPoint *polygon;
+	    register XPoint *p;
+	    int d;		/* Small delta for cross/plus thickness */
+
+	    d = (r2 / 3);
+
+	    /*
+	     *
+	     *          2   3       The plus/cross symbol is a closed polygon
+	     *                      of 12 points. The diagram to the left
+	     *    0,12  1   4    5  represents the positions of the points
+	     *           x,y        which are computed below. The extra
+	     *     11  10   7    6  (thirteenth) point connects the first and
+	     *                      last points.
+	     *          9   8
+	     */
+
+	    pattern[0].x = pattern[11].x = pattern[12].x = -r2;
+	    pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d;
+	    pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d;
+	    pattern[5].x = pattern[6].x = r2;
+	    pattern[2].y = pattern[3].y = -r2;
+	    pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y =
+		pattern[12].y = -d;
+	    pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d;
+	    pattern[9].y = pattern[8].y = r2;
+
+	    if (penPtr->symbol.type == SYMBOL_CROSS) {
+		double dx, dy;
+
+		/* For the cross symbol, rotate the points by 45 degrees. */
+		for (n = 0; n < 12; n++) {
+		    dx = (double)pattern[n].x * M_SQRT1_2;
+		    dy = (double)pattern[n].y * M_SQRT1_2;
+		    pattern[n].x = Round(dx - dy);
+		    pattern[n].y = Round(dx + dy);
+		}
+		pattern[12] = pattern[0];
+	    }
+	    polygon = Blt_Malloc(nSymbolPts * 13 * sizeof(XPoint));
+	    p = polygon;
+	    if (linePtr->symbolInterval > 0) {
+		count = 0;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    if (DRAW_SYMBOL(linePtr)) {
+			for (n = 0; n < 13; n++) {
+			    p->x = pattern[n].x + (short int)pointPtr->x;
+			    p->y = pattern[n].y + (short int)pointPtr->y;
+			    p++;
+			}
+			count++;
+		    }
+		    linePtr->symbolCounter++;
+		}
+	    } else {
+		count = nSymbolPts;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    for (n = 0; n < 13; n++) {
+			p->x = pattern[n].x + (short int)pointPtr->x;
+			p->y = pattern[n].y + (short int)pointPtr->y;
+			p++;
+		    }
+		}
+	    }
+	    if (penPtr->symbol.fillGC != NULL) {
+		for (p = polygon, i = 0; i < count; i++, p += 13) {
+		    XFillPolygon(graphPtr->display, drawable, 
+			penPtr->symbol.fillGC, p, 13, Complex, CoordModeOrigin);
+		}
+	    }
+	    if (penPtr->symbol.outlineWidth > 0) {
+		for (p = polygon, i = 0; i < count; i++, p += 13) {
+		    XDrawLines(graphPtr->display, drawable, 
+			penPtr->symbol.outlineGC, p, 13, CoordModeOrigin);
+		}
+	    }
+	    Blt_Free(polygon);
+	}
+	break;
+
+    case SYMBOL_DIAMOND:
+	{
+	    XPoint *polygon;
+	    register XPoint *p;
+
+	    /*
+	     *
+	     *                      The plus symbol is a closed polygon
+	     *            1         of 4 points. The diagram to the left
+	     *                      represents the positions of the points
+	     *       0,4 x,y  2     which are computed below. The extra
+	     *                      (fifth) point connects the first and
+	     *            3         last points.
+	     *
+	     */
+	    pattern[1].y = pattern[0].x = -r1;
+	    pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0;
+	    pattern[3].y = pattern[2].x = r1;
+	    pattern[4] = pattern[0];
+
+	    polygon = Blt_Malloc(nSymbolPts * 5 * sizeof(XPoint));
+	    p = polygon;
+	    if (linePtr->symbolInterval > 0) {
+		count = 0;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    if (DRAW_SYMBOL(linePtr)) {
+			for (n = 0; n < 5; n++, p++) {
+			    p->x = pattern[n].x + (short int)pointPtr->x;
+			    p->y = pattern[n].y + (short int)pointPtr->y;
+			}
+			count++;
+		    }
+		    linePtr->symbolCounter++;
+		}
+	    } else {
+		count = nSymbolPts;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    for (n = 0; n < 5; n++, p++) {
+			p->x = pattern[n].x + (short int)pointPtr->x;
+			p->y = pattern[n].y + (short int)pointPtr->y;
+		    }
+		}
+	    }
+	    if (penPtr->symbol.fillGC != NULL) {
+		for (p = polygon, i = 0; i < count; i++, p += 5) {
+		    XFillPolygon(graphPtr->display, drawable, 
+			 penPtr->symbol.fillGC, p, 5, Convex, CoordModeOrigin);
+
+		}
+	    }
+	    if (penPtr->symbol.outlineWidth > 0) {
+		for (p = polygon, i = 0; i < count; i++, p += 5) {
+		    XDrawLines(graphPtr->display, drawable, 
+		       penPtr->symbol.outlineGC, p, 5, CoordModeOrigin);
+		}
+	    }
+	    Blt_Free(polygon);
+	}
+	break;
+
+    case SYMBOL_TRIANGLE:
+    case SYMBOL_ARROW:
+	{
+	    XPoint *polygon;
+	    register XPoint *p;
+	    double b;
+	    int b2, h1, h2;
+#define H_RATIO		1.1663402261671607
+#define B_RATIO		1.3467736870885982
+#define TAN30		0.57735026918962573
+#define COS30		0.86602540378443871
+
+	    b = Round(size * B_RATIO * 0.7);
+	    b2 = Round(b * 0.5);
+	    h2 = Round(TAN30 * b2);
+	    h1 = Round(b2 / COS30);
+	    /*
+	     *
+	     *                      The triangle symbol is a closed polygon
+	     *           0,3         of 3 points. The diagram to the left
+	     *                      represents the positions of the points
+	     *           x,y        which are computed below. The extra
+	     *                      (fourth) point connects the first and
+	     *      2           1   last points.
+	     *
+	     */
+
+	    if (penPtr->symbol.type == SYMBOL_ARROW) {
+		pattern[3].x = pattern[0].x = 0;
+		pattern[3].y = pattern[0].y = h1;
+		pattern[1].x = b2;
+		pattern[2].y = pattern[1].y = -h2;
+		pattern[2].x = -b2;
+	    } else {
+		pattern[3].x = pattern[0].x = 0;
+		pattern[3].y = pattern[0].y = -h1;
+		pattern[1].x = b2;
+		pattern[2].y = pattern[1].y = h2;
+		pattern[2].x = -b2;
+	    }
+	    polygon = Blt_Malloc(nSymbolPts * 4 * sizeof(XPoint));
+	    p = polygon;
+	    if (linePtr->symbolInterval > 0) {
+		count = 0;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    if (DRAW_SYMBOL(linePtr)) {
+			for (n = 0; n < 4; n++) {
+			    p->x = pattern[n].x + (short int)pointPtr->x;
+			    p->y = pattern[n].y + (short int)pointPtr->y;
+			    p++;
+			}
+			count++;
+		    }
+		    linePtr->symbolCounter++;
+		}
+	    } else {
+		count = nSymbolPts;
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    for (n = 0; n < 4; n++) {
+			p->x = pattern[n].x + (short int)pointPtr->x;
+			p->y = pattern[n].y + (short int)pointPtr->y;
+			p++;
+		    }
+		}
+	    }
+	    if (penPtr->symbol.fillGC != NULL) {
+		for (p = polygon, i = 0; i < count; i++, p += 4) {
+		    XFillPolygon(graphPtr->display, drawable, 
+			penPtr->symbol.fillGC, p, 4, Convex, CoordModeOrigin);
+		}
+	    }
+	    if (penPtr->symbol.outlineWidth > 0) {
+		for (p = polygon, i = 0; i < count; i++, p += 4) {
+		    XDrawLines(graphPtr->display, drawable, 
+			penPtr->symbol.outlineGC, p, 4, CoordModeOrigin);
+		}
+	    }
+	    Blt_Free(polygon);
+	}
+	break;
+    case SYMBOL_BITMAP:
+	{
+	    Pixmap bitmap, mask;
+	    int width, height, bmWidth, bmHeight;
+	    double scale, sx, sy;
+	    int dx, dy;
+	    register int x, y;
+
+	    Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+		&width, &height);
+	    mask = None;
+
+	    /*
+	     * Compute the size of the scaled bitmap.  Stretch the
+	     * bitmap to fit a nxn bounding box.
+	     */
+	    sx = (double)size / (double)width;
+	    sy = (double)size / (double)height;
+	    scale = MIN(sx, sy);
+	    bmWidth = (int)(width * scale);
+	    bmHeight = (int)(height * scale);
+
+	    XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, None);
+	    if (penPtr->symbol.mask != None) {
+		mask = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.mask,
+		    width, height, bmWidth, bmHeight);
+		XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, 
+			     mask);
+	    }
+	    bitmap = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.bitmap,
+		width, height, bmWidth, bmHeight);
+	    if (penPtr->symbol.fillGC == NULL) {
+		XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, 
+			     bitmap);
+	    }
+	    dx = bmWidth / 2;
+	    dy = bmHeight / 2;
+	    if (linePtr->symbolInterval > 0) {
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    if (DRAW_SYMBOL(linePtr)) {
+			x = (int)pointPtr->x - dx;
+			y = (int)pointPtr->y - dy;
+			if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+			    XSetClipOrigin(graphPtr->display,
+				penPtr->symbol.outlineGC, x, y);
+			}
+			XCopyPlane(graphPtr->display, bitmap, drawable,
+			    penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight, 
+				   x, y, 1);
+		    }
+		    linePtr->symbolCounter++;
+		}
+	    } else {
+		for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+		     pointPtr < endPtr; pointPtr++) {
+		    x = (int)pointPtr->x - dx;
+		    y = (int)pointPtr->y - dy;
+		    if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+			XSetClipOrigin(graphPtr->display, 
+				       penPtr->symbol.outlineGC, x, y);
+		    }
+		    XCopyPlane(graphPtr->display, bitmap, drawable,
+			penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight, 
+			       x, y, 1);
+		}
+	    }
+	    Tk_FreePixmap(graphPtr->display, bitmap);
+	    if (mask != None) {
+		Tk_FreePixmap(graphPtr->display, mask);
+	    }
+	}
+	break;
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbol --
+ *
+ * 	Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
+    Graph *graphPtr;		/* Graph widget record */
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Element *elemPtr;		/* Line element information */
+    int x, y;			/* Center position of symbol */
+    int size;			/* Size of symbol. */
+{
+    Line *linePtr = (Line *)elemPtr;
+    LinePen *penPtr = linePtr->normalPenPtr;
+
+    if (penPtr->traceWidth > 0) {
+	/*
+	 * Draw an extra line offset by one pixel from the previous to
+	 * give a thicker appearance.  This is only for the legend
+	 * entry.  This routine is never called for drawing the actual
+	 * line segments.
+	 */
+	XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size, 
+		y, x + size, y);
+	XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size,
+		y + 1, x + size, y + 1);
+    }
+    if (penPtr->symbol.type != SYMBOL_NONE) {
+	Point2D point;
+
+	point.x = x, point.y = y;
+	DrawSymbols(graphPtr, drawable, linePtr, linePtr->normalPenPtr, size,
+	    1, &point);
+    }
+}
+
+#ifdef WIN32
+
+static void
+DrawTraces(
+    Graph *graphPtr,
+    Drawable drawable,		/* Pixmap or window to draw into */
+    Line *linePtr,
+    LinePen *penPtr)
+{
+    Blt_ChainLink *linkPtr;
+    HBRUSH brush, oldBrush;
+    HDC dc;
+    HPEN pen, oldPen;
+    POINT *points;
+    TkWinDCState state;
+    Trace *tracePtr;
+    int j;
+    int nPoints, remaining;
+    register POINT *p;
+    register int count;
+
+    /*  
+     * Depending if the line is wide (> 1 pixel), arbitrarily break
+     * the line in sections of 100 points.  This bit of weirdness has
+     * to do with wide geometric pens.  The longer the polyline, the
+     * slower it draws.  The trade off is that we lose dash and cap
+     * uniformity for unbearably slow polyline draws.
+     */
+    if (penPtr->traceGC->line_width > 1) {
+	nPoints = 100;
+    } else {
+	nPoints = Blt_MaxRequestSize(graphPtr->display, sizeof(POINT)) - 1;
+    }
+    points = Blt_Malloc((nPoints + 1) * sizeof(POINT));
+
+    dc = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
+
+    pen = Blt_GCToPen(dc, penPtr->traceGC);
+    oldPen = SelectPen(dc, pen);
+    brush = CreateSolidBrush(penPtr->traceGC->foreground);
+    oldBrush = SelectBrush(dc, brush);
+    SetROP2(dc, tkpWinRopModes[penPtr->traceGC->function]);
+
+    for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tracePtr = Blt_ChainGetValue(linkPtr);
+
+	/*
+	 * If the trace has to be split into separate XDrawLines
+	 * calls, then the end point of the current trace is also the
+	 * starting point of the new split.
+	 */
+
+	/* Step 1. Convert and draw the first section of the trace.
+	 *	   It may contain the entire trace. */
+
+	for (p = points, count = 0; count < MIN(nPoints, tracePtr->nScreenPts); 
+	     count++, p++) {
+	    p->x = (int)tracePtr->screenPts[count].x;
+	    p->y = (int)tracePtr->screenPts[count].y;
+	}
+	Polyline(dc, points, count);
+
+	/* Step 2. Next handle any full-size chunks left. */
+
+	while ((count + nPoints) < tracePtr->nScreenPts) {
+	    /* Start with the last point of the previous trace. */
+	    points[0].x = points[nPoints - 1].x;
+	    points[0].y = points[nPoints - 1].y;
+
+	    for (p = points + 1, j = 0; j < nPoints; j++, count++, p++) {
+		p->x = (int)tracePtr->screenPts[count].x;
+		p->y = (int)tracePtr->screenPts[count].y;
+	    }
+	    Polyline(dc, points, nPoints + 1);
+	}
+	
+	/* Step 3. Convert and draw the remaining points. */
+
+	remaining = tracePtr->nScreenPts - count;
+	if (remaining > 0) {
+	    /* Start with the last point of the previous trace. */
+	    points[0].x = points[nPoints - 1].x;
+	    points[0].y = points[nPoints - 1].y;
+
+	    for (p = points + 1; count < tracePtr->nScreenPts; count++, p++) {
+		p->x = (int)tracePtr->screenPts[count].x;
+		p->y = (int)tracePtr->screenPts[count].y;
+	    }	    
+	    Polyline(dc, points, remaining + 1);
+	}
+    }
+    Blt_Free(points);
+    DeletePen(SelectPen(dc, oldPen));
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawTraces(graphPtr, drawable, linePtr, penPtr)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Line *linePtr;
+    LinePen *penPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Trace *tracePtr;
+    XPoint *points;
+    int j;
+    int nPoints, remaining;
+    register XPoint *p;
+    register int count;
+
+    nPoints = Blt_MaxRequestSize(graphPtr->display, sizeof(XPoint)) - 1;
+    points = Blt_Malloc((nPoints + 1) * sizeof(XPoint));
+	    
+    for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	int n;
+
+	tracePtr = Blt_ChainGetValue(linkPtr);
+
+	/*
+	 * If the trace has to be split into separate XDrawLines
+	 * calls, then the end point of the current trace is also the
+	 * starting point of the new split.
+	 */
+	/* Step 1. Convert and draw the first section of the trace.
+	 *	   It may contain the entire trace. */
+
+	n = MIN(nPoints, tracePtr->nScreenPts); 
+	for (p = points, count = 0; count < n; count++, p++) {
+	    p->x = (short int)tracePtr->screenPts[count].x;
+	    p->y = (short int)tracePtr->screenPts[count].y;
+	}
+	XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, 
+	   count, CoordModeOrigin);
+
+	/* Step 2. Next handle any full-size chunks left. */
+
+	while ((count + nPoints) < tracePtr->nScreenPts) {
+	    /* Start with the last point of the previous trace. */
+	    points[0].x = points[nPoints - 1].x;
+	    points[0].y = points[nPoints - 1].y;
+	    
+	    for (p = points + 1, j = 0; j < nPoints; j++, count++, p++) {
+		p->x = (short int)tracePtr->screenPts[count].x;
+		p->y = (short int)tracePtr->screenPts[count].y;
+	    }
+	    XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, 
+		       nPoints + 1, CoordModeOrigin);
+	}
+	
+	/* Step 3. Convert and draw the remaining points. */
+
+	remaining = tracePtr->nScreenPts - count;
+	if (remaining > 0) {
+	    /* Start with the last point of the previous trace. */
+	    points[0].x = points[nPoints - 1].x;
+	    points[0].y = points[nPoints - 1].y;
+	    for (p = points + 1; count < tracePtr->nScreenPts; count++, p++) {
+		p->x = (short int)tracePtr->screenPts[count].x;
+		p->y = (short int)tracePtr->screenPts[count].y;
+	    }	    
+	    XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, 
+		remaining + 1, CoordModeOrigin);
+	}
+    }
+    Blt_Free(points);
+}
+#endif /* WIN32 */
+
+static void
+DrawValues(graphPtr, drawable, linePtr, penPtr, nSymbolPts, symbolPts, 
+	   pointToData)
+    Graph *graphPtr;
+    Drawable drawable;
+    Line *linePtr;
+    LinePen *penPtr;
+    int nSymbolPts;
+    Point2D *symbolPts;
+    int *pointToData;
+{
+    Point2D *pointPtr, *endPtr;
+    int count;
+    char string[TCL_DOUBLE_SPACE * 2 + 2];
+    char *fmt;
+    double x, y;
+    
+    fmt = penPtr->valueFormat;
+    if (fmt == NULL) {
+	fmt = "%g";
+    }
+    count = 0;
+    for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	 pointPtr < endPtr; pointPtr++) {
+	x = linePtr->x.valueArr[pointToData[count]];
+	y = linePtr->y.valueArr[pointToData[count]];
+	count++;
+	if (penPtr->valueShow == SHOW_X) {
+	    sprintf(string, fmt, x); 
+	} else if (penPtr->valueShow == SHOW_Y) {
+	    sprintf(string, fmt, y); 
+	} else if (penPtr->valueShow == SHOW_BOTH) {
+	    sprintf(string, fmt, x);
+	    strcat(string, ",");
+	    sprintf(string + strlen(string), fmt, y);
+	}
+	Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle), 
+		(int)pointPtr->x, (int)pointPtr->y);
+    } 
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawActiveLine --
+ *
+ *	Draws the connected line(s) representing the element. If the
+ *	line is made up of non-line symbols and the line width
+ *	parameter has been set (linewidth > 0), the element will also
+ *	be drawn as a line (with the linewidth requested).  The line
+ *	may consist of separate line segments.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawActiveLine(graphPtr, drawable, elemPtr)
+    Graph *graphPtr;		/* Graph widget record */
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Element *elemPtr;		/* Element to be drawn */
+{
+    Line *linePtr = (Line *)elemPtr;
+    LinePen *penPtr = linePtr->activePenPtr;
+    int symbolSize;
+
+    if (penPtr == NULL) {
+	return;
+    }
+    symbolSize = ScaleSymbol(elemPtr, linePtr->activePenPtr->symbol.size);
+
+    /* 
+     * nActiveIndices 
+     *	  > 0		Some points are active.  Uses activeArr.
+     *	  < 0		All points are active.
+     *    == 0		No points are active.
+     */
+    if (linePtr->nActiveIndices > 0) {
+	if (linePtr->flags & ACTIVE_PENDING) {
+	    MapActiveSymbols(graphPtr, linePtr);
+	}
+	if (penPtr->symbol.type != SYMBOL_NONE) {
+	    DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+		linePtr->nActivePts, linePtr->activePts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    DrawValues(graphPtr, drawable, linePtr, penPtr, 
+		       linePtr->nActivePts, linePtr->activePts, 
+		       linePtr->activeToData);
+	}
+    } else if (linePtr->nActiveIndices < 0) { 
+	if (penPtr->traceWidth > 0) {
+	    if (linePtr->nStrips > 0) {
+		Blt_Draw2DSegments(graphPtr->display, drawable, 
+			penPtr->traceGC, linePtr->strips, linePtr->nStrips);
+	    } else if (Blt_ChainGetLength(linePtr->traces) > 0) {
+		DrawTraces(graphPtr, drawable, linePtr, penPtr);
+	    }
+	}
+	if (penPtr->symbol.type != SYMBOL_NONE) {
+	    DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+		linePtr->nSymbolPts, linePtr->symbolPts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    DrawValues(graphPtr, drawable, linePtr, penPtr,
+		       linePtr->nSymbolPts, linePtr->symbolPts, 
+		       linePtr->symbolToData);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawNormalLine --
+ *
+ *	Draws the connected line(s) representing the element. If the
+ *	line is made up of non-line symbols and the line width parameter
+ *	has been set (linewidth > 0), the element will also be drawn as
+ *	a line (with the linewidth requested).  The line may consist of
+ *	separate line segments.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawNormalLine(graphPtr, drawable, elemPtr)
+    Graph *graphPtr;		/* Graph widget record */
+    Drawable drawable;		/* Pixmap or window to draw into */
+    Element *elemPtr;		/* Element to be drawn */
+{
+    Line *linePtr = (Line *)elemPtr;
+    LinePen *penPtr;
+    Blt_ChainLink *linkPtr;
+    register LinePenStyle *stylePtr;
+    unsigned int count;
+
+    /* Fill area under the curve */
+    if (linePtr->fillPts != NULL) {
+	XPoint *points;
+	Point2D *endPtr, *pointPtr;
+
+	points = Blt_Malloc(sizeof(XPoint) * linePtr->nFillPts);
+	count = 0;
+	for(pointPtr = linePtr->fillPts, 
+		endPtr = linePtr->fillPts + linePtr->nFillPts;
+	    pointPtr < endPtr; pointPtr++) {
+	    points[count].x = (short int)pointPtr->x;
+	    points[count].y = (short int)pointPtr->y;
+	    count++;
+	}
+	if (linePtr->fillTile != NULL) {
+	    Blt_SetTileOrigin(graphPtr->tkwin, linePtr->fillTile, 0, 0);
+	    Blt_TilePolygon(graphPtr->tkwin, drawable, linePtr->fillTile, 
+			    points, linePtr->nFillPts);
+	} else if (linePtr->fillStipple != None) {
+	    XFillPolygon(graphPtr->display, drawable, linePtr->fillGC, 
+		 points, linePtr->nFillPts, Complex, CoordModeOrigin);
+	}
+	Blt_Free(points);
+    }
+
+    /* Lines: stripchart segments or graph traces. */
+
+    if (linePtr->nStrips > 0) {
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    penPtr = stylePtr->penPtr;
+	    if ((stylePtr->nStrips > 0) && (penPtr->errorBarLineWidth > 0)) {
+		Blt_Draw2DSegments(graphPtr->display, drawable, 
+			penPtr->traceGC, stylePtr->strips, stylePtr->nStrips);
+	    }
+	}
+    } else if ((Blt_ChainGetLength(linePtr->traces) > 0) &&
+	(linePtr->normalPenPtr->traceWidth > 0)) {
+	DrawTraces(graphPtr, drawable, linePtr, linePtr->normalPenPtr);
+    }
+
+    if (linePtr->reqMaxSymbols > 0) {
+	int total;
+
+	total = 0;
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    total += stylePtr->nSymbolPts;
+	}
+	linePtr->symbolInterval = total / linePtr->reqMaxSymbols;
+	linePtr->symbolCounter = 0;
+    }
+
+    /* Symbols, error bars, values. */
+
+    count = 0;
+    for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	penPtr = stylePtr->penPtr;
+	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
+	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
+			       stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
+	}
+	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
+			       stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
+	}
+	if ((stylePtr->nSymbolPts > 0) && 
+	    (penPtr->symbol.type != SYMBOL_NONE)) {
+	    DrawSymbols(graphPtr, drawable, linePtr, penPtr, 
+			stylePtr->symbolSize, stylePtr->nSymbolPts, 
+			stylePtr->symbolPts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    DrawValues(graphPtr, drawable, linePtr, penPtr, 
+		       stylePtr->nSymbolPts, stylePtr->symbolPts, 
+		       linePtr->symbolToData + count);
+	}
+	count += stylePtr->nSymbolPts;
+    }
+    linePtr->symbolInterval = 0;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * GetSymbolPostScriptInfo --
+ *
+ *	Set up the PostScript environment with the macros and
+ *	attributes needed to draw the symbols of the element.
+ *
+ * Results:
+ *	None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size)
+    Graph *graphPtr;
+    PsToken psToken;
+    LinePen *penPtr;
+    int size;
+{
+    XColor *outlineColor, *fillColor, *defaultColor;
+
+    /* Set line and foreground attributes */
+    outlineColor = penPtr->symbol.outlineColor;
+    fillColor = penPtr->symbol.fillColor;
+    defaultColor = penPtr->traceColor;
+
+    if (fillColor == COLOR_DEFAULT) {
+	fillColor = defaultColor;
+    }
+    if (outlineColor == COLOR_DEFAULT) {
+	outlineColor = defaultColor;
+    }
+    if (penPtr->symbol.type == SYMBOL_NONE) {
+	Blt_LineAttributesToPostScript(psToken, defaultColor,
+	    penPtr->traceWidth + 2, &(penPtr->traceDashes), 
+	    CapButt, JoinMiter);
+    } else {
+	Blt_LineWidthToPostScript(psToken, penPtr->symbol.outlineWidth);
+	Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+    }
+
+    /*
+     * Build a PostScript procedure to draw the symbols.  For bitmaps,
+     * paint both the bitmap and its mask. Otherwise fill and stroke
+     * the path formed already.
+     */
+    Blt_AppendToPostScript(psToken, "\n/DrawSymbolProc {\n", (char *)NULL);
+    switch (penPtr->symbol.type) {
+    case SYMBOL_NONE:
+	break;			/* Do nothing */
+    case SYMBOL_BITMAP:
+	{
+	    int width, height;
+	    double sx, sy, scale;
+
+	    /*
+	     * Compute how much to scale the bitmap.  Don't let the
+	     * scaled bitmap exceed the bounding square for the
+	     * symbol.
+	     */
+	    Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+		&width, &height);
+	    sx = (double)size / (double)width;
+	    sy = (double)size / (double)height;
+	    scale = MIN(sx, sy);
+
+	    if ((penPtr->symbol.mask != None) && (fillColor != NULL)) {
+		Blt_AppendToPostScript(psToken,
+		    "\n  % Bitmap mask is \"",
+		    Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.mask),
+		    "\"\n\n  ", (char *)NULL);
+		Blt_BackgroundToPostScript(psToken, fillColor);
+		Blt_BitmapToPostScript(psToken, graphPtr->display,
+		    penPtr->symbol.mask, scale, scale);
+	    }
+	    Blt_AppendToPostScript(psToken,
+		"\n  % Bitmap symbol is \"",
+		Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.bitmap),
+		"\"\n\n  ", (char *)NULL);
+	    Blt_ForegroundToPostScript(psToken, outlineColor);
+	    Blt_BitmapToPostScript(psToken, graphPtr->display, 
+			penPtr->symbol.bitmap, scale, scale);
+	}
+	break;
+    default:
+	if (fillColor != NULL) {
+	    Blt_AppendToPostScript(psToken, "  ", (char *)NULL);
+	    Blt_BackgroundToPostScript(psToken, fillColor);
+	    Blt_AppendToPostScript(psToken, "  Fill\n", (char *)NULL);
+	}
+	if ((outlineColor != NULL) && (penPtr->symbol.outlineWidth > 0)) {
+	    Blt_AppendToPostScript(psToken, "  ", (char *)NULL);
+	    Blt_ForegroundToPostScript(psToken, outlineColor);
+	    Blt_AppendToPostScript(psToken, "  stroke\n", (char *)NULL);
+	}
+	break;
+    }
+    Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolsToPostScript --
+ *
+ * 	Draw a symbol centered at the given x,y window coordinate
+ *	based upon the element symbol type and size.
+ *
+ * Results:
+ *	None.
+ *
+ * Problems:
+ *	Most notable is the round-off errors generated when
+ *	calculating the centered position of the symbol.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolsToPostScript(graphPtr, psToken, penPtr, size, nSymbolPts, symbolPts)
+    Graph *graphPtr;
+    PsToken psToken;
+    LinePen *penPtr;
+    int size;
+    int nSymbolPts;
+    Point2D *symbolPts;
+{
+    double symbolSize;
+    register Point2D *pointPtr, *endPtr;
+    static char *symbolMacros[] =
+    {
+	"Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm", 
+	(char *)NULL,
+    };
+    GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size);
+
+    symbolSize = (double)size;
+    switch (penPtr->symbol.type) {
+    case SYMBOL_SQUARE:
+    case SYMBOL_CROSS:
+    case SYMBOL_PLUS:
+    case SYMBOL_SCROSS:
+    case SYMBOL_SPLUS:
+	symbolSize = (double)Round(size * S_RATIO);
+	break;
+    case SYMBOL_TRIANGLE:
+    case SYMBOL_ARROW:
+	symbolSize = (double)Round(size * 0.7);
+	break;
+    case SYMBOL_DIAMOND:
+	symbolSize = (double)Round(size * M_SQRT1_2);
+	break;
+
+    default:
+	break;
+    }
+    for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	 pointPtr < endPtr; pointPtr++) {
+	Blt_FormatToPostScript(psToken, "%g %g %g %s\n", pointPtr->x,
+	    pointPtr->y, symbolSize, symbolMacros[penPtr->symbol.type]);
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * 	Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
+    Graph *graphPtr;		/* Graph widget record */
+    PsToken psToken;
+    Element *elemPtr;		/* Line element information */
+    double x, y;		/* Center position of symbol */
+    int size;			/* Size of element */
+{
+    Line *linePtr = (Line *)elemPtr;
+    LinePen *penPtr = linePtr->normalPenPtr;
+
+    if (penPtr->traceWidth > 0) {
+	/*
+	 * Draw an extra line offset by one pixel from the previous to
+	 * give a thicker appearance.  This is only for the legend
+	 * entry.  This routine is never called for drawing the actual
+	 * line segments.
+	 */
+	Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+	    penPtr->traceWidth + 2, &(penPtr->traceDashes), CapButt, JoinMiter);
+	Blt_FormatToPostScript(psToken, "%g %g %d Li\n", x, y, size + size);
+    }
+    if (penPtr->symbol.type != SYMBOL_NONE) {
+	Point2D point;
+
+	point.x = x, point.y = y;
+	SymbolsToPostScript(graphPtr, psToken, penPtr, size, 1, &point);
+    }
+}
+
+static void
+SetLineAttributes(psToken, penPtr)
+    PsToken psToken;
+    LinePen *penPtr;
+{
+    /* Set the attributes of the line (color, dashes, linewidth) */
+    Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+	penPtr->traceWidth, &(penPtr->traceDashes), CapButt, JoinMiter);
+    if ((LineIsDashed(penPtr->traceDashes)) && 
+	(penPtr->traceOffColor != NULL)) {
+	Blt_AppendToPostScript(psToken, "/DashesProc {\n  gsave\n    ",
+	    (char *)NULL);
+	Blt_BackgroundToPostScript(psToken, penPtr->traceOffColor);
+	Blt_AppendToPostScript(psToken, "    ", (char *)NULL);
+	Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+	Blt_AppendToPostScript(psToken, "stroke\n  grestore\n} def\n",
+	    (char *)NULL);
+    } else {
+	Blt_AppendToPostScript(psToken, "/DashesProc {} def\n", (char *)NULL);
+    }
+}
+
+static void
+TracesToPostScript(psToken, linePtr, penPtr)
+    PsToken psToken;
+    Line *linePtr;
+    LinePen *penPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Trace *tracePtr;
+    register Point2D *pointPtr, *endPtr;
+    int count;
+
+    SetLineAttributes(psToken, penPtr);
+    for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tracePtr = Blt_ChainGetValue(linkPtr);
+	if (tracePtr->nScreenPts <= 0) {
+	    continue;
+	}
+#define PS_MAXPATH	1500	/* Maximum number of components in a PostScript
+				 * (level 1) path. */
+	pointPtr = tracePtr->screenPts;
+	Blt_FormatToPostScript(psToken, " newpath %g %g moveto\n", 
+			       pointPtr->x, 
+			       pointPtr->y);
+	pointPtr++;
+	count = 0;
+	for (endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+	     pointPtr < endPtr; pointPtr++) {
+	    Blt_FormatToPostScript(psToken, " %g %g lineto\n", 
+				   pointPtr->x, 
+				   pointPtr->y);
+	    if ((count % PS_MAXPATH) == 0) {
+		Blt_FormatToPostScript(psToken,
+			       "DashesProc stroke\n newpath  %g %g moveto\n", 
+				       pointPtr->x, 
+				       pointPtr->y);
+	    }
+	    count++;
+	}
+	Blt_FormatToPostScript(psToken, " %g %g lineto\n", 
+			       pointPtr->x, 
+			       pointPtr->y);
+	Blt_AppendToPostScript(psToken, "DashesProc stroke\n", (char *)NULL);
+    }
+}
+
+
+static void
+ValuesToPostScript(psToken, linePtr, penPtr, nSymbolPts, symbolPts, 
+		   pointToData)
+    PsToken psToken;
+    Line *linePtr;
+    LinePen *penPtr;
+    int nSymbolPts;
+    Point2D *symbolPts;
+    int *pointToData;
+{
+    Point2D *pointPtr, *endPtr;
+    int count;
+    char string[TCL_DOUBLE_SPACE * 2 + 2];
+    char *fmt;
+    double x, y;
+    
+    fmt = penPtr->valueFormat;
+    if (fmt == NULL) {
+	fmt = "%g";
+    }
+    count = 0;
+    for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+	 pointPtr < endPtr; pointPtr++) {
+	x = linePtr->x.valueArr[pointToData[count]];
+	y = linePtr->y.valueArr[pointToData[count]];
+	count++;
+	if (penPtr->valueShow == SHOW_X) {
+	    sprintf(string, fmt, x); 
+	} else if (penPtr->valueShow == SHOW_Y) {
+	    sprintf(string, fmt, y); 
+	} else if (penPtr->valueShow == SHOW_BOTH) {
+	    sprintf(string, fmt, x);
+	    strcat(string, ",");
+	    sprintf(string + strlen(string), fmt, y);
+	}
+	Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle), 
+		     pointPtr->x, pointPtr->y);
+    } 
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActiveLineToPostScript --
+ *
+ *	Generates PostScript commands to draw as "active" the points
+ *	(symbols) and or line segments (trace) representing the
+ *	element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ActiveLineToPostScript(graphPtr, psToken, elemPtr)
+    Graph *graphPtr;
+    PsToken psToken;
+    Element *elemPtr;
+{
+    Line *linePtr = (Line *)elemPtr;
+    LinePen *penPtr = linePtr->activePenPtr;
+    int symbolSize;
+
+    if (penPtr == NULL) {
+	return;
+    }
+    symbolSize = ScaleSymbol(elemPtr, penPtr->symbol.size);
+    if (linePtr->nActiveIndices > 0) {
+	if (linePtr->flags & ACTIVE_PENDING) {
+	    MapActiveSymbols(graphPtr, linePtr);
+	}
+	if (penPtr->symbol.type != SYMBOL_NONE) {
+	    SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+		linePtr->nActivePts, linePtr->activePts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nActivePts,
+		       linePtr->activePts, linePtr->activeToData);
+	}
+    } else if (linePtr->nActiveIndices < 0) {
+	if (penPtr->traceWidth > 0) {
+	    if (linePtr->nStrips > 0) {
+		SetLineAttributes(psToken, penPtr);
+		Blt_2DSegmentsToPostScript(psToken, linePtr->strips, 
+			  linePtr->nStrips);
+	    }
+	    if (Blt_ChainGetLength(linePtr->traces) > 0) {
+		TracesToPostScript(psToken, linePtr, (LinePen *)penPtr);
+	    }
+	}
+	if (penPtr->symbol.type != SYMBOL_NONE) {
+	    SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+		linePtr->nSymbolPts, linePtr->symbolPts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nSymbolPts, 
+		       linePtr->symbolPts, linePtr->symbolToData);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NormalLineToPostScript --
+ *
+ *	Similar to the DrawLine procedure, prints PostScript related
+ *	commands to form the connected line(s) representing the element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NormalLineToPostScript(graphPtr, psToken, elemPtr)
+    Graph *graphPtr;
+    PsToken psToken;
+    Element *elemPtr;
+{
+    Line *linePtr = (Line *)elemPtr;
+    register LinePenStyle *stylePtr;
+    Blt_ChainLink *linkPtr;
+    LinePen *penPtr;
+    unsigned int count;
+    XColor *colorPtr;
+
+    /* Draw fill area */
+    if (linePtr->fillPts != NULL) {
+	/* Create a path to use for both the polygon and its outline. */
+	Blt_PathToPostScript(psToken, linePtr->fillPts, linePtr->nFillPts);
+	Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+	/* If the background fill color was specified, draw the
+	 * polygon in a solid fashion with that color.  */
+	if (linePtr->fillBgColor != NULL) {
+	    Blt_BackgroundToPostScript(psToken, linePtr->fillBgColor);
+	    Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+	}
+	Blt_ForegroundToPostScript(psToken, linePtr->fillFgColor);
+	if (linePtr->fillTile != NULL) {
+	    /* TBA: Transparent tiling is the hard part. */
+	} else if ((linePtr->fillStipple != None) &&
+		   (linePtr->fillStipple != PATTERN_SOLID)) {
+	    /* Draw the stipple in the foreground color. */
+	    Blt_StippleToPostScript(psToken, graphPtr->display,
+		    linePtr->fillStipple);
+	} else {
+	    Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+	}
+    }
+    /* Draw lines */
+    if (linePtr->nStrips > 0) {
+	for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    stylePtr = Blt_ChainGetValue(linkPtr);
+	    penPtr = stylePtr->penPtr;
+	    if ((stylePtr->nStrips > 0) && (penPtr->traceWidth > 0)) {
+		SetLineAttributes(psToken, penPtr);
+		Blt_2DSegmentsToPostScript(psToken, stylePtr->strips,
+			stylePtr->nStrips);
+	    }
+	}
+    } else if ((Blt_ChainGetLength(linePtr->traces) > 0) &&
+	(linePtr->normalPenPtr->traceWidth > 0)) {
+	TracesToPostScript(psToken, linePtr, linePtr->normalPenPtr);
+    }
+
+    /* Draw symbols, error bars, values. */
+
+    count = 0;
+    for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	stylePtr = Blt_ChainGetValue(linkPtr);
+	penPtr = stylePtr->penPtr;
+	colorPtr = penPtr->errorBarColor;
+	if (colorPtr == COLOR_DEFAULT) {
+	    colorPtr = penPtr->traceColor;
+	}
+	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
+	    Blt_LineAttributesToPostScript(psToken, colorPtr,
+		penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
+	    Blt_2DSegmentsToPostScript(psToken, stylePtr->xErrorBars,
+		stylePtr->xErrorBarCnt);
+	}
+	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+	    Blt_LineAttributesToPostScript(psToken, colorPtr,
+		   penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
+	    Blt_2DSegmentsToPostScript(psToken, stylePtr->yErrorBars,
+		stylePtr->yErrorBarCnt);
+	}
+	if ((stylePtr->nSymbolPts > 0) &&
+	    (stylePtr->penPtr->symbol.type != SYMBOL_NONE)) {
+	    SymbolsToPostScript(graphPtr, psToken, penPtr, 
+				stylePtr->symbolSize, stylePtr->nSymbolPts, 
+				stylePtr->symbolPts);
+	}
+	if (penPtr->valueShow != SHOW_NONE) {
+	    ValuesToPostScript(psToken, linePtr, penPtr, 
+			       stylePtr->nSymbolPts, stylePtr->symbolPts, 
+			       linePtr->symbolToData + count);
+	}
+	count += stylePtr->nSymbolPts;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyLine --
+ *
+ *	Release memory and resources allocated for the line element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the line element is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+#define FreeVector(v) \
+    if ((v).clientId != NULL) { \
+	Blt_FreeVectorId((v).clientId); \
+    } else if ((v).valueArr != NULL) { \
+	Blt_Free((v).valueArr); \
+    } 
+
+static void
+DestroyLine(graphPtr, elemPtr)
+    Graph *graphPtr;
+    Element *elemPtr;
+{
+    Line *linePtr = (Line *)elemPtr;
+
+    if (linePtr->normalPenPtr != &(linePtr->builtinPen)) {
+	Blt_FreePen(graphPtr, (Pen *)linePtr->normalPenPtr);
+    }
+    DestroyPen(graphPtr, (Pen *)&(linePtr->builtinPen));
+    if (linePtr->activePenPtr != NULL) {
+	Blt_FreePen(graphPtr, (Pen *)linePtr->activePenPtr);
+    }
+
+    FreeVector(linePtr->w);
+    FreeVector(linePtr->x);
+    FreeVector(linePtr->xHigh);
+    FreeVector(linePtr->xLow);
+    FreeVector(linePtr->xError);
+    FreeVector(linePtr->y);
+    FreeVector(linePtr->yHigh);
+    FreeVector(linePtr->yLow);
+    FreeVector(linePtr->yError);
+
+    ResetLine(linePtr);
+    if (linePtr->palette != NULL) {
+	Blt_FreePalette(graphPtr, linePtr->palette);
+	Blt_ChainDestroy(linePtr->palette);
+    }
+    if (linePtr->tags != NULL) {
+	Blt_Free(linePtr->tags);
+    }
+    if (linePtr->activeIndices != NULL) {
+	Blt_Free(linePtr->activeIndices);
+    }
+    if (linePtr->fillPts != NULL) {
+	Blt_Free(linePtr->fillPts);
+    }
+    if (linePtr->fillTile != NULL) {
+	Blt_FreeTile(linePtr->fillTile);
+    }
+    if ((linePtr->fillStipple != None) && 
+	(linePtr->fillStipple != PATTERN_SOLID)) {
+	Tk_FreeBitmap(graphPtr->display, linePtr->fillStipple);
+    }
+    if (linePtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineElement --
+ *
+ *	Allocate memory and initialize methods for the new line element.
+ *
+ * Results:
+ *	The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the line element structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ElementProcs lineProcs =
+{
+    ClosestLine,		/* Finds the closest element/data point */
+    ConfigureLine,		/* Configures the element. */
+    DestroyLine,		/* Destroys the element. */
+    DrawActiveLine,		/* Draws active element */
+    DrawNormalLine,		/* Draws normal element */
+    DrawSymbol,			/* Draws the element symbol. */
+    GetLineExtents,		/* Find the extents of the element's data. */
+    ActiveLineToPostScript,	/* Prints active element. */
+    NormalLineToPostScript,	/* Prints normal element. */
+    SymbolToPostScript,		/* Prints the line's symbol. */
+    MapLine			/* Compute element's screen coordinates. */
+};
+
+Element *
+Blt_LineElement(graphPtr, name, classUid)
+    Graph *graphPtr;
+    char *name;
+    Blt_Uid classUid;
+{
+    register Line *linePtr;
+
+    linePtr = Blt_Calloc(1, sizeof(Line));
+    assert(linePtr);
+    linePtr->procsPtr = &lineProcs;
+    if (classUid == bltLineElementUid) {
+	linePtr->configSpecs = lineElemConfigSpecs;
+    } else {
+	linePtr->configSpecs = stripElemConfigSpecs;
+    }
+
+    /* By default an element's name and label are the same. */
+    linePtr->label = Blt_Strdup(name);
+    linePtr->name = Blt_Strdup(name);
+
+    linePtr->classUid = classUid;
+    linePtr->flags = SCALE_SYMBOL;
+    linePtr->graphPtr = graphPtr;
+    linePtr->labelRelief = TK_RELIEF_FLAT;
+    linePtr->normalPenPtr = &linePtr->builtinPen;
+    linePtr->palette = Blt_ChainCreate();
+    linePtr->penDir = PEN_BOTH_DIRECTIONS;
+    linePtr->reqSmooth = PEN_SMOOTH_NONE;
+    InitPen(linePtr->normalPenPtr);
+    return (Element *)linePtr;
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrMarker.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrMarker.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrMarker.c	(revision 175)
@@ -0,0 +1,4987 @@
+
+/*
+ * bltGrMarker.c --
+ *
+ *	This module implements markers for the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include "bltChain.h"
+#include "bltGrElem.h"
+
+#define GETBITMAP(b) \
+	(((b)->destBitmap == None) ? (b)->srcBitmap : (b)->destBitmap)
+
+#define MAX_OUTLINE_POINTS	12
+
+/* Map graph coordinates to normalized coordinates [0..1] */
+#define NORMALIZE(A,x) 	(((x) - (A)->axisRange.min) / (A)->axisRange.range)
+
+#define DEF_MARKER_ANCHOR	"center"
+#define DEF_MARKER_BACKGROUND	RGB_WHITE
+#define DEF_MARKER_BG_MONO	RGB_WHITE
+#define DEF_MARKER_BITMAP	(char *)NULL
+#define DEF_MARKER_CAP_STYLE	"butt"
+#define DEF_MARKER_COORDS	(char *)NULL
+#define DEF_MARKER_DASHES	(char *)NULL
+#define DEF_MARKER_DASH_OFFSET	"0"
+#define DEF_MARKER_ELEMENT	(char *)NULL
+#define DEF_MARKER_FOREGROUND	RGB_BLACK
+#define DEF_MARKER_FG_MONO	RGB_BLACK
+#define DEF_MARKER_FILL_COLOR	RGB_RED
+#define DEF_MARKER_FILL_MONO	RGB_WHITE
+#define DEF_MARKER_FONT		STD_FONT
+#define DEF_MARKER_GAP_COLOR	RGB_PINK
+#define DEF_MARKER_GAP_MONO	RGB_BLACK
+#define DEF_MARKER_HEIGHT	"0"
+#define DEF_MARKER_HIDE		"no"
+#define DEF_MARKER_JOIN_STYLE	"miter"
+#define DEF_MARKER_JUSTIFY	"left"
+#define DEF_MARKER_LINE_WIDTH	"1"
+#define DEF_MARKER_MAP_X	"x"
+#define DEF_MARKER_MAP_Y	"y"
+#define DEF_MARKER_NAME		(char *)NULL
+#define DEF_MARKER_OUTLINE_COLOR RGB_BLACK
+#define DEF_MARKER_OUTLINE_MONO	RGB_BLACK
+#define DEF_MARKER_PAD		"4"
+#define DEF_MARKER_ROTATE	"0.0"
+#define DEF_MARKER_SCALE	"1.0"
+#define DEF_MARKER_SHADOW_COLOR	(char *)NULL
+#define DEF_MARKER_SHADOW_MONO	(char *)NULL
+#define DEF_MARKER_STATE	"normal"
+#define DEF_MARKER_STIPPLE	(char *)NULL
+#define DEF_MARKER_TEXT		(char *)NULL
+#define DEF_MARKER_UNDER	"no"
+#define DEF_MARKER_WIDTH	"0"
+#define DEF_MARKER_WINDOW	(char *)NULL
+#define DEF_MARKER_XOR		"no"
+#define DEF_MARKER_X_OFFSET	"0"
+#define DEF_MARKER_Y_OFFSET	"0"
+
+#define DEF_MARKER_TEXT_TAGS	"Text all"
+#define DEF_MARKER_IMAGE_TAGS	"Image all"
+#define DEF_MARKER_BITMAP_TAGS	"Bitmap all"
+#define DEF_MARKER_WINDOW_TAGS	"Window all"
+#define DEF_MARKER_POLYGON_TAGS	"Polygon all"
+#define DEF_MARKER_LINE_TAGS	"Line all"
+
+static Tk_OptionParseProc StringToCoordinates;
+static Tk_OptionPrintProc CoordinatesToString;
+static Tk_CustomOption coordsOption =
+{
+    StringToCoordinates, CoordinatesToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorPairOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltStateOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+
+typedef Marker *(MarkerCreateProc) _ANSI_ARGS_((void));
+typedef void (MarkerDrawProc) _ANSI_ARGS_((Marker *markerPtr, 
+	Drawable drawable));
+typedef void (MarkerFreeProc) _ANSI_ARGS_((Graph *graphPtr, Marker *markerPtr));
+typedef int (MarkerConfigProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerMapProc) _ANSI_ARGS_((Marker *markerPtr));
+typedef void (MarkerPostScriptProc) _ANSI_ARGS_((Marker *markerPtr,
+	PsToken psToken));
+typedef int (MarkerPointProc) _ANSI_ARGS_((Marker *markerPtr, 
+	Point2D *samplePtr));
+typedef int (MarkerRegionProc) _ANSI_ARGS_((Marker *markerPtr, 
+	Extents2D *extsPtr, int enclosed));
+
+typedef struct {
+    Tk_ConfigSpec *configSpecs;	/* Marker configuration specifications */
+
+    MarkerConfigProc *configProc;
+    MarkerDrawProc *drawProc;
+    MarkerFreeProc *freeProc;
+    MarkerMapProc *mapProc;
+    MarkerPointProc *pointProc;
+    MarkerRegionProc *regionProc;
+    MarkerPostScriptProc *postscriptProc;
+
+}  MarkerClass;
+
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Marker --
+ *
+ *	Structure defining the generic marker.  In C++ parlance this
+ *	would be the base type from which all markers are derived.
+ *
+ *	This structure corresponds with the specific types of markers.
+ *	Don't change this structure without changing the individual
+ *	marker structures of each type below.
+ *
+ * ------------------------------------------------------------------- 
+ */
+struct MarkerStruct {
+    char *name;			/* Identifier for marker in list */
+
+    Blt_Uid classUid;		/* Type of marker. */
+
+    Graph *graphPtr;		/* Graph widget of marker. */
+
+    unsigned int flags;
+
+    char **tags;
+
+    int hidden;			/* If non-zero, don't display the marker. */
+
+    Blt_HashEntry *hashPtr;
+
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Coordinate array to position marker */
+    int nWorldPts;		/* Number of points in above array */
+
+    char *elemName;		/* Element associated with marker */
+
+    Axis2D axes;
+
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. This can
+				 * be a performance penalty because
+				 * the graph must be redraw entirely
+				 * each time the marker is redrawn. */
+
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+
+    int xOffset, yOffset;	/* Pixel offset from graph position */
+
+    MarkerClass *classPtr;
+
+    int state;
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * TextMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type of marker */
+    Graph *graphPtr;		/* The graph this marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* If non-zero, don't display the
+				 * marker. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Position of marker (1 X-Y coordinate) in
+				 * world (graph) coordinates. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+    int xOffset, yOffset;	/* pixel offset from anchor */
+
+    MarkerClass *classPtr;
+
+    int state;
+    /*
+     * Text specific fields and attributes
+     */
+#ifdef notdef
+    char *textVarName;		/* Name of variable (malloc'ed) or
+				 * NULL. If non-NULL, graph displays
+				 * the contents of this variable. */
+#endif
+    char *string;		/* Text string to be display.  The string
+				 * make contain newlines. */
+
+    Tk_Anchor anchor;		/* Indicates how to translate the given
+				 * marker position. */
+
+    Point2D anchorPos;		/* Translated anchor point. */
+
+    int width, height;		/* Dimension of bounding box.  */
+
+    TextStyle style;		/* Text attributes (font, fg, anchor, etc) */
+
+    TextLayout *textPtr;	/* Contains information about the layout
+				 * of the text. */
+    Point2D outline[5];
+    XColor *fillColor;
+    GC fillGC;
+} TextMarker;
+
+
+static Tk_ConfigSpec textConfigSpecs[] =
+{
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_MARKER_ANCHOR, Tk_Offset(TextMarker, anchor), 0},
+    {TK_CONFIG_COLOR, "-background", "background", "MarkerBackground",
+	(char *)NULL, Tk_Offset(TextMarker, fillColor),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-bg", "background", "Background",
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_TEXT_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", "Foreground",
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_FONT, "-font", "font", "Font",
+	DEF_MARKER_FONT, Tk_Offset(TextMarker, style.font), 0},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_MARKER_FOREGROUND, Tk_Offset(TextMarker, style.color),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_MARKER_FG_MONO, Tk_Offset(TextMarker, style.color),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+	DEF_MARKER_JUSTIFY, Tk_Offset(TextMarker, style.justify),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+	DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+	DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+	DEF_MARKER_ROTATE, Tk_Offset(TextMarker, style.theta),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_MARKER_SHADOW_COLOR, Tk_Offset(TextMarker, style.shadow),
+	TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_MARKER_SHADOW_MONO, Tk_Offset(TextMarker, style.shadow),
+	TK_CONFIG_MONO_ONLY, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_STRING, "-text", "text", "Text",
+	DEF_MARKER_TEXT, Tk_Offset(TextMarker, string), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * WindowMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type of marker */
+    Graph *graphPtr;		/* Graph marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* Indicates if the marker is
+				 * currently hidden or not. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Position of marker (1 X-Y coordinate) in
+				 * world (graph) coordinates. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+    int xOffset, yOffset;	/* Pixel offset from anchor. */
+
+    MarkerClass *classPtr;
+
+    int state;
+
+    /*
+     * Window specific attributes
+     */
+    char *pathName;		/* Name of child widget to be displayed. */
+    Tk_Window tkwin;		/* Window to display. */
+    int reqWidth, reqHeight;	/* If non-zero, this overrides the size 
+				 * requested by the child widget. */
+
+    Tk_Anchor anchor;		/* Indicates how to translate the given
+				 * marker position. */
+
+    Point2D anchorPos;		/* Translated anchor point. */
+    int width, height;		/* Current size of the child window. */
+
+} WindowMarker;
+
+static Tk_ConfigSpec windowConfigSpecs[] =
+{
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_MARKER_ANCHOR, Tk_Offset(WindowMarker, anchor), 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_WINDOW_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(WindowMarker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+	DEF_MARKER_HEIGHT, Tk_Offset(WindowMarker, reqHeight),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+	DEF_MARKER_WIDTH, Tk_Offset(WindowMarker, reqWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+    {TK_CONFIG_STRING, "-window", "window", "Window",
+	DEF_MARKER_WINDOW, Tk_Offset(WindowMarker, pathName),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * BitmapMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type of marker */
+    Graph *graphPtr;		/* Graph marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* Indicates if the marker is currently
+				 * hidden or not. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Position of marker in world (graph)
+				 * coordinates. If 2 pairs of X-Y
+				 * coordinates are specified, then the
+				 * bitmap is resized to fit this area.
+				 * Otherwise if 1 pair, then the bitmap
+				 * is positioned at the coordinate at its
+				 * normal size. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+
+    int xOffset, yOffset;	/* Pixel offset from origin of bitmap */
+
+    MarkerClass *classPtr;
+
+    int state;
+
+    /* Bitmap specific attributes */
+    Pixmap srcBitmap;		/* Original bitmap. May be further
+				 * scaled or rotated. */
+    double rotate;		/* Requested rotation of the bitmap */
+    double theta;		/* Normalized rotation (0..360
+				 * degrees) */
+    Tk_Anchor anchor;		/* If only one X-Y coordinate is
+				 * given, indicates how to translate
+				 * the given marker position.  Otherwise,
+				 * if there are two X-Y coordinates, then
+				 * this value is ignored. */
+    Point2D anchorPos;		/* Translated anchor point. */
+
+    XColor *outlineColor;	/* Foreground color */
+    XColor *fillColor;		/* Background color */
+
+    GC gc;			/* Private graphic context */
+    GC fillGC;			/* Shared graphic context */
+    Pixmap destBitmap;		/* Bitmap to be drawn. */
+    int destWidth, destHeight;	/* Dimensions of the final bitmap */
+
+    Point2D outline[MAX_OUTLINE_POINTS]; 
+				/* Polygon representing the background
+				 * of the bitmap. */
+    int nOutlinePts;
+} BitmapMarker;
+
+static Tk_ConfigSpec bitmapConfigSpecs[] =
+{
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_MARKER_ANCHOR, Tk_Offset(BitmapMarker, anchor), 0},
+    {TK_CONFIG_COLOR, "-background", "background", "Background",
+	DEF_MARKER_BACKGROUND, Tk_Offset(BitmapMarker, fillColor),
+	TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-background", "background", "Background",
+	DEF_MARKER_BG_MONO, Tk_Offset(BitmapMarker, fillColor),
+	TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_BITMAP_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
+	DEF_MARKER_BITMAP, Tk_Offset(BitmapMarker, srcBitmap), 
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_MARKER_FOREGROUND, Tk_Offset(BitmapMarker, outlineColor),
+	TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_MARKER_FG_MONO, Tk_Offset(BitmapMarker, outlineColor),
+	TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+	DEF_MARKER_ROTATE, Tk_Offset(BitmapMarker, rotate),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * ImageMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type of marker */
+    Graph *graphPtr;		/* Graph marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* Indicates if the marker is
+				 * currently hidden or not. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+    Point2D *worldPts;		/* Position of marker in world (graph)
+				 * coordinates. If 2 pairs of X-Y
+				 * coordinates are specified, then the
+				 * image is resized to fit this area.
+				 * Otherwise if 1 pair, then the image
+				 * is positioned at the coordinate at
+				 * its normal size. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+    int xOffset, yOffset;	/* Pixel offset from anchor */
+
+    MarkerClass *classPtr;
+
+    int state;
+
+    /* Image specific attributes */
+    char *imageName;		/* Name of image to be displayed. */
+    Tk_Image tkImage;		/* Tk image to be displayed. */
+    Tk_Anchor anchor;		/* Indicates how to translate the given
+				 * marker position. */
+    Point2D anchorPos;		/* Translated anchor point. */
+    int width, height;		/* Dimensions of the image */
+    Tk_Image tmpImage;
+    Pixmap pixmap;		/* Pixmap containing the scaled image */
+    ColorTable colorTable;	/* Pointer to color table */
+    Blt_ColorImage srcImage;
+    GC gc;
+
+} ImageMarker;
+
+static Tk_ConfigSpec imageConfigSpecs[] =
+{
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_MARKER_ANCHOR, Tk_Offset(ImageMarker, anchor), 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_IMAGE_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-image", "image", "Image",
+	(char *)NULL, Tk_Offset(ImageMarker, imageName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * LineMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type is "linemarker" */
+    Graph *graphPtr;		/* Graph marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* Indicates if the marker is currently
+				 * hidden or not. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Position of marker (X-Y coordinates) in
+				 * world (graph) coordinates. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+    int xOffset, yOffset;	/* Pixel offset */
+
+    MarkerClass *classPtr;
+
+    int state;
+
+    /* Line specific attributes */
+    XColor *fillColor;
+    XColor *outlineColor;	/* Foreground and background colors */
+
+    int lineWidth;		/* Line width. */
+    int capStyle;		/* Cap style. */
+    int joinStyle;		/* Join style.*/
+    Blt_Dashes dashes;		/* Dash list values (max 11) */
+
+    GC gc;			/* Private graphic context */
+
+    Segment2D *segments;	/* Malloc'ed array of points.
+				 * Represents individual line segments
+				 * (2 points per segment) comprising
+				 * the mapped line.  The segments may
+				 * not necessarily be connected after
+				 * clipping. */
+    int nSegments;		/* # segments in the above array. */
+
+    int xor;
+    int xorState;		/* State of the XOR drawing. Indicates
+				 * if the marker is currently drawn. */
+} LineMarker;
+
+static Tk_ConfigSpec lineConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_LINE_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+	DEF_MARKER_CAP_STYLE, Tk_Offset(LineMarker, capStyle),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_MARKER_DASHES, Tk_Offset(LineMarker, dashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_CUSTOM, "-dashoffset", "dashOffset", "DashOffset",
+	DEF_MARKER_DASH_OFFSET, Tk_Offset(LineMarker, dashes.offset),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-fill", "fill", "Fill",
+	(char *)NULL, Tk_Offset(LineMarker, fillColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+	DEF_MARKER_JOIN_STYLE, Tk_Offset(LineMarker, joinStyle),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+	DEF_MARKER_LINE_WIDTH, Tk_Offset(LineMarker, lineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+	DEF_MARKER_OUTLINE_COLOR, Tk_Offset(LineMarker, outlineColor),
+	TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-outline", "outline", "Outline",
+	DEF_MARKER_OUTLINE_MONO, Tk_Offset(LineMarker, outlineColor),
+	TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+	DEF_MARKER_XOR, Tk_Offset(LineMarker, xor), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * PolygonMarker --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Identifier for marker */
+    Blt_Uid classUid;		/* Type of marker */
+    Graph *graphPtr;		/* Graph marker belongs to */
+    unsigned int flags;
+    char **tags;
+    int hidden;			/* Indicates if the marker is currently
+				 * hidden or not. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_ChainLink *linkPtr;
+
+    Point2D *worldPts;		/* Position of marker (X-Y coordinates) in
+				 * world (graph) coordinates. */
+    int nWorldPts;		/* Number of points */
+
+    char *elemName;		/* Element associated with marker */
+    Axis2D axes;
+    int drawUnder;		/* If non-zero, draw the marker
+				 * underneath any elements. There can
+				 * be a performance because the graph
+				 * must be redraw entirely each time
+				 * this marker is redrawn. */
+    int clipped;		/* Indicates if the marker is totally
+				 * clipped by the plotting area. */
+    int xOffset, yOffset;	/* Pixel offset */
+
+    MarkerClass *classPtr;
+
+    int state;
+
+    /* Polygon specific attributes and fields */
+
+    Point2D *screenPts;		/* Array of points representing the
+				 * polygon in screen coordinates. It's
+				 * not used for drawing, but to
+				 * generate the outlinePts and fillPts
+				 * arrays that are the coordinates of
+				 * the possibly clipped outline and
+				 * filled polygon. */
+
+    ColorPair outline;
+    ColorPair fill;
+
+    Pixmap stipple;		/* Stipple pattern to fill the polygon. */
+    int lineWidth;		/* Width of polygon outline. */
+    int capStyle;
+    int joinStyle;
+    Blt_Dashes dashes;		/* List of dash values.  Indicates how
+				 * draw the dashed line.  If no dash
+				 * values are provided, or the first value
+				 * is zero, then the line is drawn solid. */
+
+    GC outlineGC;		/* Graphics context to draw the outline of 
+				 * the polygon. */
+    GC fillGC;			/* Graphics context to draw the filled
+				 * polygon. */
+
+    Point2D *fillPts;		/* Malloc'ed array of points used to draw
+				 * the filled polygon. These points may
+				 * form a degenerate polygon after clipping.
+				 */
+
+    int nFillPts;		/* # points in the above array. */
+
+    Segment2D *outlinePts;	/* Malloc'ed array of points.
+				 * Represents individual line segments
+				 * (2 points per segment) comprising
+				 * the outline of the polygon.  The
+				 * segments may not necessarily be
+				 * closed or connected after clipping. */
+
+    int nOutlinePts;		/* # points in the above array. */
+
+    int xor;
+    int xorState;		/* State of the XOR drawing. Indicates
+				 * if the marker is visible. We have
+				 * to drawn it again to erase it. */
+} PolygonMarker;
+
+static Tk_ConfigSpec polygonConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_MARKER_POLYGON_TAGS, Tk_Offset(Marker, tags),
+	TK_CONFIG_NULL_OK, &bltListOption},
+    {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap",
+	DEF_MARKER_CAP_STYLE, Tk_Offset(PolygonMarker, capStyle),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords",
+	DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts),
+	TK_CONFIG_NULL_OK, &coordsOption},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_MARKER_DASHES, Tk_Offset(PolygonMarker, dashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_STRING, "-element", "element", "Element",
+	DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_MARKER_FILL_COLOR, Tk_Offset(PolygonMarker, fill),
+	TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_MARKER_FILL_MONO, Tk_Offset(PolygonMarker, fill),
+	TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+    {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join",
+	DEF_MARKER_JOIN_STYLE, Tk_Offset(PolygonMarker, joinStyle),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+	DEF_MARKER_LINE_WIDTH, Tk_Offset(PolygonMarker, lineWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_MARKER_HIDE, Tk_Offset(Marker, hidden),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+	DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption},
+    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+	DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption},
+    {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL,
+	DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_MARKER_OUTLINE_COLOR, Tk_Offset(PolygonMarker, outline),
+	TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+    {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+	DEF_MARKER_OUTLINE_MONO, Tk_Offset(PolygonMarker, outline),
+	TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_MARKER_STATE, Tk_Offset(Marker, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+	DEF_MARKER_STIPPLE, Tk_Offset(PolygonMarker, stipple),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-under", "under", "Under",
+	DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
+	DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor",
+	DEF_MARKER_XOR, Tk_Offset(PolygonMarker, xor), 
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
+	DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerCreateProc CreateBitmapMarker, CreateLineMarker, CreateImageMarker,
+	CreatePolygonMarker, CreateTextMarker, CreateWindowMarker;
+
+static MarkerDrawProc DrawBitmapMarker, DrawLineMarker, DrawImageMarker,
+	DrawPolygonMarker, DrawTextMarker, DrawWindowMarker;
+
+static MarkerFreeProc FreeBitmapMarker, FreeLineMarker, FreeImageMarker, 
+	FreePolygonMarker, FreeTextMarker, FreeWindowMarker;
+
+static MarkerConfigProc ConfigureBitmapMarker, ConfigureLineMarker, 
+	ConfigureImageMarker, ConfigurePolygonMarker, ConfigureTextMarker, 
+	ConfigureWindowMarker;
+
+static MarkerMapProc MapBitmapMarker, MapLineMarker, MapImageMarker, 
+	MapPolygonMarker, MapTextMarker, MapWindowMarker;
+
+static MarkerPostScriptProc BitmapMarkerToPostScript, LineMarkerToPostScript, 
+	ImageMarkerToPostScript, PolygonMarkerToPostScript, 
+	TextMarkerToPostScript, WindowMarkerToPostScript;
+
+static MarkerPointProc PointInBitmapMarker, PointInLineMarker, 
+	PointInImageMarker, PointInPolygonMarker, PointInTextMarker, 
+	PointInWindowMarker;
+
+static MarkerRegionProc RegionInBitmapMarker, RegionInLineMarker, 
+	RegionInImageMarker, RegionInPolygonMarker, RegionInTextMarker, 
+	RegionInWindowMarker;
+
+static Tk_ImageChangedProc ImageChangedProc;
+
+static MarkerClass bitmapMarkerClass = {
+    bitmapConfigSpecs,
+    ConfigureBitmapMarker,
+    DrawBitmapMarker,
+    FreeBitmapMarker,
+    MapBitmapMarker,
+    PointInBitmapMarker,
+    RegionInBitmapMarker,
+    BitmapMarkerToPostScript,
+};
+
+static MarkerClass imageMarkerClass = {
+    imageConfigSpecs,
+    ConfigureImageMarker,
+    DrawImageMarker,
+    FreeImageMarker,
+    MapImageMarker,
+    PointInImageMarker,
+    RegionInImageMarker,
+    ImageMarkerToPostScript,
+};
+
+static MarkerClass lineMarkerClass = {
+    lineConfigSpecs,
+    ConfigureLineMarker,
+    DrawLineMarker,
+    FreeLineMarker,
+    MapLineMarker,
+    PointInLineMarker,
+    RegionInLineMarker,
+    LineMarkerToPostScript,
+};
+
+static MarkerClass polygonMarkerClass = {
+    polygonConfigSpecs,
+    ConfigurePolygonMarker,
+    DrawPolygonMarker,
+    FreePolygonMarker,
+    MapPolygonMarker,
+    PointInPolygonMarker,
+    RegionInPolygonMarker,
+    PolygonMarkerToPostScript,
+};
+
+static MarkerClass textMarkerClass = {
+    textConfigSpecs,
+    ConfigureTextMarker,
+    DrawTextMarker,
+    FreeTextMarker,
+    MapTextMarker,
+    PointInTextMarker,
+    RegionInTextMarker,
+    TextMarkerToPostScript,
+};
+
+static MarkerClass windowMarkerClass = {
+    windowConfigSpecs,
+    ConfigureWindowMarker,
+    DrawWindowMarker,
+    FreeWindowMarker,
+    MapWindowMarker,
+    PointInWindowMarker,
+    RegionInWindowMarker,
+    WindowMarkerToPostScript,
+};
+
+#ifdef notdef
+static MarkerClass rectangleMarkerClass = {
+    rectangleConfigSpecs,
+    ConfigureRectangleMarker,
+    DrawRectangleMarker,
+    FreeRectangleMarker,
+    MapRectangleMarker,
+    PointInRectangleMarker,
+    RegionInRectangleMarker,
+    RectangleMarkerToPostScript,
+};
+
+static MarkerClass ovalMarkerClass = {
+    ovalConfigSpecs,
+    ConfigureOvalMarker,
+    DrawOvalMarker,
+    FreeOvalMarker,
+    MapOvalMarker,
+    PointInOvalMarker,
+    RegionInOvalMarker,
+    OvalMarkerToPostScript,
+};
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BoxesDontOverlap --
+ *
+ *	Tests if the bounding box of a marker overlaps the plotting
+ *	area in any way.  If so, the marker will be drawn.  Just do a
+ *	min/max test on the extents of both boxes.
+ *
+ *	Note: It's assumed that the extents of the bounding box lie 
+ *	      within the area.  So for a 10x10 rectangle, bottom and
+ *	      left would be 9.
+ *
+ * Results:
+ *	Returns 0 is the marker is visible in the plotting area, and
+ *	1 otherwise (marker is clipped).
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+BoxesDontOverlap(graphPtr, extsPtr)
+    Graph *graphPtr;
+    Extents2D *extsPtr;
+{
+/*
+    assert(extsPtr->right >= extsPtr->left);
+    assert(extsPtr->bottom >= extsPtr->top);
+    assert(graphPtr->right >= graphPtr->left);
+    assert(graphPtr->bottom >= graphPtr->top);
+*/
+
+    return (((double)graphPtr->right < extsPtr->left) ||
+	    ((double)graphPtr->bottom < extsPtr->top) ||
+	    (extsPtr->right < (double)graphPtr->left) ||
+	    (extsPtr->bottom < (double)graphPtr->top));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetCoordinate --
+ *
+ * 	Convert the expression string into a floating point value. The
+ *	only reason we use this routine instead of Blt_ExprDouble is to
+ *	handle "elastic" bounds.  That is, convert the strings "-Inf",
+ *	"Inf" into -(DBL_MAX) and DBL_MAX respectively.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The value of the
+ * 	expression is passed back via valuePtr.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+GetCoordinate(interp, expr, valuePtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    char *expr;			/* Numeric expression string to parse */
+    double *valuePtr;		/* Real-valued result of expression */
+{
+    char c;
+
+    c = expr[0];
+    if ((c == 'I') && (strcmp(expr, "Inf") == 0)) {
+	*valuePtr = DBL_MAX;	/* Elastic upper bound */
+    } else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) {
+	*valuePtr = -DBL_MAX;	/* Elastic lower bound */
+    } else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) {
+	*valuePtr = DBL_MAX;	/* Elastic upper bound */
+    } else if (Tcl_ExprDouble(interp, expr, valuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PrintCoordinate --
+ *
+ * 	Convert the floating point value into its string
+ * 	representation.  The only reason this routine is used in
+ * 	instead of sprintf, is to handle the "elastic" bounds.  That
+ * 	is, convert the values DBL_MAX and -(DBL_MAX) into "+Inf" and
+ * 	"-Inf" respectively.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The string of the
+ * 	expression is passed back via string.
+ *
+ * ---------------------------------------------------------------------- */
+static char *
+PrintCoordinate(interp, x)
+    Tcl_Interp *interp;
+    double x;			/* Numeric value */
+{
+    if (x == DBL_MAX) {
+	return "+Inf";
+    } else if (x == -DBL_MAX) {
+	return "-Inf";
+    } else {
+	static char string[TCL_DOUBLE_SPACE + 1];
+
+	Tcl_PrintDouble(interp, (double)x, string);
+	return string;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ParseCoordinates --
+ *
+ *	The Tcl coordinate list is converted to their floating point
+ *	values. It will then replace the current marker coordinates.
+ *
+ *	Since different marker types require different number of
+ *	coordinates this must be checked here.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side effects:
+ *	If the marker coordinates are reset, the graph is eventually
+ *	redrawn with at the new marker coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ParseCoordinates(interp, markerPtr, nExprs, exprArr)
+    Tcl_Interp *interp;
+    Marker *markerPtr;
+    int nExprs;
+    char **exprArr;
+{
+    int nWorldPts;
+    int minArgs, maxArgs;
+    Point2D *worldPts;
+    register int i;
+    register Point2D *pointPtr;
+    double x, y;
+
+    if (nExprs == 0) {
+	return TCL_OK;
+    }
+    if (nExprs & 1) {
+	Tcl_AppendResult(interp, "odd number of marker coordinates specified",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (markerPtr->classUid == bltLineMarkerUid) {
+	minArgs = 4, maxArgs = 0;
+    } else if (markerPtr->classUid == bltPolygonMarkerUid) {
+	minArgs = 6, maxArgs = 0;
+    } else if ((markerPtr->classUid == bltWindowMarkerUid) ||
+	       (markerPtr->classUid == bltTextMarkerUid)) {
+	minArgs = 2, maxArgs = 2;
+    } else if ((markerPtr->classUid == bltImageMarkerUid) ||
+	       (markerPtr->classUid == bltBitmapMarkerUid)) {
+	minArgs = 2, maxArgs = 4;
+    } else {
+	Tcl_AppendResult(interp, "unknown marker type", (char *)NULL);
+	return TCL_ERROR;
+    }
+
+    if (nExprs < minArgs) {
+	Tcl_AppendResult(interp, "too few marker coordinates specified",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((maxArgs > 0) && (nExprs > maxArgs)) {
+	Tcl_AppendResult(interp, "too many marker coordinates specified",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    nWorldPts = nExprs / 2;
+    worldPts = Blt_Malloc(nWorldPts * sizeof(Point2D));
+    if (worldPts == NULL) {
+	Tcl_AppendResult(interp, "can't allocate new coordinate array",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+
+    /* Don't free the old coordinate array until we've parsed the new
+     * coordinates without errors.  */
+    pointPtr = worldPts;
+    for (i = 0; i < nExprs; i += 2) {
+	if ((GetCoordinate(interp, exprArr[i], &x) != TCL_OK) ||
+	    (GetCoordinate(interp, exprArr[i + 1], &y) != TCL_OK)) {
+	    Blt_Free(worldPts);
+	    return TCL_ERROR;
+	}
+	pointPtr->x = x, pointPtr->y = y;
+	pointPtr++;
+    }
+    if (markerPtr->worldPts != NULL) {
+	Blt_Free(markerPtr->worldPts);
+    }
+    markerPtr->worldPts = worldPts;
+    markerPtr->nWorldPts = nWorldPts;
+    markerPtr->flags |= MAP_ITEM;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * StringToCoordinates --
+ *
+ *	Given a Tcl list of numeric expression representing the
+ *	element values, convert into an array of floating point
+ *	values. In addition, the minimum and maximum values are saved.
+ *	Since elastic values are allow (values which translate to the
+ *	min/max of the graph), we must try to get the non-elastic
+ *	minimum and maximum.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The vector is
+ *	passed back via the vecPtr.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+StringToCoordinates(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Tcl list of numeric expressions */
+    char *widgRec;		/* Marker record */
+    int offset;			/* Not used. */
+{
+    Marker *markerPtr = (Marker *)widgRec;
+    int nExprs;
+    char **exprArr;
+    int result;
+
+    nExprs = 0;
+    if ((string != NULL) &&
+	(Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (nExprs == 0) {
+	if (markerPtr->worldPts != NULL) {
+	    Blt_Free(markerPtr->worldPts);
+	    markerPtr->worldPts = NULL;
+	}
+	markerPtr->nWorldPts = 0;
+	return TCL_OK;
+    }
+    result = ParseCoordinates(interp, markerPtr, nExprs, exprArr);
+    Blt_Free(exprArr);
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CoordinatesToString --
+ *
+ *	Convert the vector of floating point values into a Tcl list.
+ *
+ * Results:
+ *	The string representation of the vector is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+CoordinatesToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Marker record */
+    int offset;			/* Not used. */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Marker *markerPtr = (Marker *)widgRec;
+    Tcl_Interp *interp;
+    Tcl_DString dString;
+    char *result;
+    register int i;
+    register Point2D *p;
+
+    if (markerPtr->nWorldPts < 1) {
+	return "";
+    }
+    interp = markerPtr->graphPtr->interp;
+
+    Tcl_DStringInit(&dString);
+    p = markerPtr->worldPts;
+    for (i = 0; i < markerPtr->nWorldPts; i++) {
+	Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->x));
+	Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->y));
+	p++;
+    }
+    result = Tcl_DStringValue(&dString);
+
+    /*
+     * If memory wasn't allocated for the dynamic string, do it here (it's
+     * currently on the stack), so that Tcl can free it normally.
+     */
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * HMap --
+ *
+ *	Map the given graph coordinate value to its axis, returning a
+ *	window position.
+ *
+ * Results:
+ *	Returns a floating point number representing the window
+ *	coordinate position on the given axis.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+static double
+HMap(graphPtr, axisPtr, x)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double x;
+{
+    register double norm;
+
+    if (x == DBL_MAX) {
+	norm = 1.0;
+    } else if (x == -DBL_MAX) {
+	norm = 0.0;
+    } else {
+	if (axisPtr->logScale) {
+	    if (x > 0.0) {
+		x = log10(x);
+	    } else if (x < 0.0) {
+		x = 0.0;
+	    }
+	}
+	norm = NORMALIZE(axisPtr, x);
+    }
+    if (axisPtr->descending) {
+	norm = 1.0 - norm;
+    }
+    /* Horizontal transformation */
+    return ((norm * graphPtr->hRange) + graphPtr->hOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VMap --
+ *
+ *	Map the given graph coordinate value to its axis, returning a
+ *	window position.
+ *
+ * Results:
+ *	Returns a double precision number representing the window
+ *	coordinate position on the given axis.
+ *
+ * ----------------------------------------------------------------------
+ */
+static double
+VMap(graphPtr, axisPtr, y)
+    Graph *graphPtr;
+    Axis *axisPtr;
+    double y;
+{
+    register double norm;
+
+    if (y == DBL_MAX) {
+	norm = 1.0;
+    } else if (y == -DBL_MAX) {
+	norm = 0.0;
+    } else {
+	if (axisPtr->logScale) {
+	    if (y > 0.0) {
+		y = log10(y);
+	    } else if (y < 0.0) {
+		y = 0.0;
+	    }
+	}
+	norm = NORMALIZE(axisPtr, y);
+    }
+    if (axisPtr->descending) {
+	norm = 1.0 - norm;
+    }
+    /* Vertical transformation */
+    return (((1.0 - norm) * graphPtr->vRange) + graphPtr->vOffset);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPoint --
+ *
+ *	Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ *	Returns a XPoint structure containing the window coordinates
+ *	of the given graph x,y coordinate.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Point2D
+MapPoint(graphPtr, pointPtr, axesPtr)
+    Graph *graphPtr;
+    Point2D *pointPtr;		/* Graph X-Y coordinate. */
+    Axis2D *axesPtr;		/* Specifies which axes to use */
+{
+    Point2D result;
+
+    if (graphPtr->inverted) {
+	result.x = HMap(graphPtr, axesPtr->y, pointPtr->y);
+	result.y = VMap(graphPtr, axesPtr->x, pointPtr->x);
+    } else {
+	result.x = HMap(graphPtr, axesPtr->x, pointPtr->x);
+	result.y = VMap(graphPtr, axesPtr->y, pointPtr->y);
+    }
+    return result;		/* Result is screen coordinate. */
+}
+
+static Marker *
+CreateMarker(graphPtr, name, classUid)
+    Graph *graphPtr;
+    char *name;
+    Blt_Uid classUid;    
+{    
+    Marker *markerPtr;
+
+    /* Create the new marker based upon the given type */
+    if (classUid == bltBitmapMarkerUid) {
+	markerPtr = CreateBitmapMarker(); /* bitmap */
+    } else if (classUid == bltLineMarkerUid) {
+	markerPtr = CreateLineMarker(); /* line */
+    } else if (classUid == bltImageMarkerUid) {
+	markerPtr = CreateImageMarker(); /* image */
+    } else if (classUid == bltTextMarkerUid) {
+	markerPtr = CreateTextMarker(); /* text */
+    } else if (classUid == bltPolygonMarkerUid) {
+	markerPtr = CreatePolygonMarker(); /* polygon */
+    } else if (classUid == bltWindowMarkerUid) {
+	markerPtr = CreateWindowMarker(); /* window */
+    } else {
+	return NULL;
+    }
+    assert(markerPtr);
+    markerPtr->graphPtr = graphPtr;
+    markerPtr->hidden = markerPtr->drawUnder = FALSE;
+    markerPtr->flags |= MAP_ITEM;
+    markerPtr->name = Blt_Strdup(name);
+    markerPtr->classUid = classUid;
+    return markerPtr;
+}
+
+static void
+DestroyMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+
+    if (markerPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    /* Free the resources allocated for the particular type of marker */
+    (*markerPtr->classPtr->freeProc) (graphPtr, markerPtr);
+    if (markerPtr->worldPts != NULL) {
+	Blt_Free(markerPtr->worldPts);
+    }
+    Blt_DeleteBindings(graphPtr->bindTable, markerPtr);
+    Tk_FreeOptions(markerPtr->classPtr->configSpecs, (char *)markerPtr,
+	graphPtr->display, 0);
+    if (markerPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&graphPtr->markers.table, markerPtr->hashPtr);
+    }
+    if (markerPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(graphPtr->markers.displayList, markerPtr->linkPtr);
+    }
+    if (markerPtr->name != NULL) {
+	Blt_Free(markerPtr->name);
+    }
+    if (markerPtr->elemName != NULL) {
+	Blt_Free(markerPtr->elemName);
+    }
+    if (markerPtr->tags != NULL) {
+	Blt_Free(markerPtr->tags);
+    }
+    Blt_Free(markerPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureBitmapMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	a bitmap marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as bitmap pixmap, colors,
+ *	rotation, etc. get set for markerPtr; old resources get freed,
+ *	if there were any.  The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ConfigureBitmapMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    if (bmPtr->srcBitmap == None) {
+	return TCL_OK;
+    }
+    bmPtr->theta = FMOD(bmPtr->rotate, 360.0);
+    if (bmPtr->theta < 0.0) {
+	bmPtr->theta += 360.0;
+    }
+    gcMask = 0;
+    if (bmPtr->outlineColor != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = bmPtr->outlineColor->pixel;
+    }
+    if (bmPtr->fillColor != NULL) {
+	gcValues.background = bmPtr->fillColor->pixel;
+	gcMask |= GCBackground;
+    } else {
+	gcValues.clip_mask = bmPtr->srcBitmap;
+	gcMask |= GCClipMask;
+    }
+
+    /* Note that while this is a "shared" GC, we're going to change
+     * the clip origin right before the bitmap is drawn anyways.  This
+     * assumes that any drawing code using this GC (with GCClipMask
+     * set) is going to want to set the clip origin anyways.  */
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (bmPtr->gc != NULL) {
+	Tk_FreeGC(graphPtr->display, bmPtr->gc);
+    }
+    bmPtr->gc = newGC;
+
+    /* Create background GC color */
+
+    if (bmPtr->fillColor != NULL) {
+	gcValues.foreground = bmPtr->fillColor->pixel;
+	newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+	if (bmPtr->fillGC != NULL) {
+	    Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+	}
+	bmPtr->fillGC = newGC;
+    }
+    bmPtr->flags |= MAP_ITEM;
+    if (bmPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapBitmapMarker --
+ *
+ * 	This procedure gets called each time the layout of the graph
+ *	changes.  The x, y window coordinates of the bitmap marker are
+ *	saved in the marker structure.
+ *
+ *	Additionly, if no background color was specified, the
+ *	GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ *	the private GC.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Window coordinates are saved and if no background color was
+ * 	set, the GC stipple origins are changed to calculated window
+ *	coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapBitmapMarker(markerPtr)
+    Marker *markerPtr;
+{
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+    Extents2D exts;
+    Graph *graphPtr = markerPtr->graphPtr;
+    Point2D anchorPos;
+    Point2D corner1, corner2;
+    int destWidth, destHeight;
+    int srcWidth, srcHeight;
+    register int i;
+
+    if (bmPtr->srcBitmap == None) {
+	return;
+    }
+    if (bmPtr->destBitmap != None) {
+	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+	bmPtr->destBitmap = None;
+    }
+    /* 
+     * Collect the coordinates.  The number of coordinates will determine
+     * the calculations to be made.
+     * 
+     *	   x1 y1	A single pair of X-Y coordinates.  They represent
+     *			the anchor position of the bitmap.  
+     *
+     *	x1 y1 x2 y2	Two pairs of X-Y coordinates.  They represent
+     *			two opposite corners of a bounding rectangle. The
+     *			bitmap is possibly rotated and scaled to fit into
+     *			this box.
+     *
+     */   
+    Tk_SizeOfBitmap(graphPtr->display, bmPtr->srcBitmap, &srcWidth, 
+		    &srcHeight);
+    corner1 = MapPoint(graphPtr, bmPtr->worldPts, &bmPtr->axes);
+    if (bmPtr->nWorldPts > 1) {
+	double hold;
+
+	corner2 = MapPoint(graphPtr, bmPtr->worldPts + 1, &bmPtr->axes);
+	/* Flip the corners if necessary */
+	if (corner1.x > corner2.x) {
+	    hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+	}
+	if (corner1.y > corner2.y) {
+	    hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+	}
+    } else {
+	corner2.x = corner1.x + srcWidth - 1;
+	corner2.y = corner1.y + srcHeight - 1;
+    }
+    destWidth = (int)(corner2.x - corner1.x) + 1;
+    destHeight = (int)(corner2.y - corner1.y) + 1;
+
+    if (bmPtr->nWorldPts == 1) {
+	anchorPos = Blt_TranslatePoint(&corner1, destWidth, destHeight, 
+		bmPtr->anchor);
+    } else {
+	anchorPos = corner1;
+    }
+    anchorPos.x += bmPtr->xOffset;
+    anchorPos.y += bmPtr->yOffset;
+
+    /* Check if the bitmap sits at least partially in the plot area. */
+    exts.left = anchorPos.x;
+    exts.top = anchorPos.y;
+    exts.right = anchorPos.x + destWidth - 1;
+    exts.bottom = anchorPos.y + destHeight - 1;
+
+    bmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+    if (bmPtr->clipped) {
+	return;			/* Bitmap is offscreen. Don't generate
+				 * rotated or scaled bitmaps. */
+    }
+
+    /*  
+     * Scale the bitmap if necessary. It's a little tricky because we
+     * only want to scale what's visible on the screen, not the entire
+     * bitmap.  
+     */
+    if ((bmPtr->theta != 0.0) || (destWidth != srcWidth) || 
+	(destHeight != srcHeight)) {
+	int regionWidth, regionHeight;
+	Region2D region;	/* Indicates the portion of the scaled
+				 * bitmap that we want to display. */
+	double left, right, top, bottom;
+
+	/*
+	 * Determine the region of the bitmap visible in the plot area.
+	 */
+	left = MAX(graphPtr->left, exts.left);
+	right = MIN(graphPtr->right, exts.right);
+	top = MAX(graphPtr->top, exts.top);
+	bottom = MIN(graphPtr->bottom, exts.bottom);
+
+	region.left = region.top = 0;
+	if (graphPtr->left > exts.left) {
+	    region.left = (int)(graphPtr->left - exts.left);
+	}
+	if (graphPtr->top > exts.top) {
+	    region.top = (int)(graphPtr->top - exts.top);
+	}	    
+	regionWidth = (int)(right - left) + 1;
+	regionHeight = (int)(bottom - top) + 1;
+	region.right = region.left + (int)(right - left);
+	region.bottom = region.top + (int)(bottom - top);
+	
+	anchorPos.x = left;
+	anchorPos.y = top;
+	bmPtr->destBitmap = Blt_ScaleRotateBitmapRegion(graphPtr->tkwin, 
+		bmPtr->srcBitmap, srcWidth, srcHeight, 
+		region.left, region.top, regionWidth, regionHeight, 
+		destWidth, destHeight, bmPtr->theta);
+	bmPtr->destWidth = regionWidth;
+	bmPtr->destHeight = regionHeight;
+    } else {
+	bmPtr->destWidth = srcWidth;
+	bmPtr->destHeight = srcHeight;
+	bmPtr->destBitmap = None;
+    }
+    bmPtr->anchorPos = anchorPos;
+    {
+	double xScale, yScale;
+	double tx, ty;
+	double rotWidth, rotHeight;
+	Point2D polygon[5];
+	int n;
+
+	/* 
+	 * Compute a polygon to represent the background area of the bitmap.  
+	 * This is needed for backgrounds of arbitrarily rotated bitmaps.  
+	 * We also use it to print a background in PostScript. 
+	 */
+	Blt_GetBoundingBox(srcWidth, srcHeight, bmPtr->theta, &rotWidth, 
+			   &rotHeight, polygon);
+	xScale = (double)destWidth / rotWidth;
+	yScale = (double)destHeight / rotHeight;
+	
+	/* 
+	 * Adjust each point of the polygon. Both scale it to the new size
+	 * and translate it to the actual screen position of the bitmap.
+	 */
+	tx = exts.left + destWidth * 0.5;
+	ty = exts.top + destHeight * 0.5;
+	for (i = 0; i < 4; i++) {
+	    polygon[i].x = (polygon[i].x * xScale) + tx;
+	    polygon[i].y = (polygon[i].y * yScale) + ty;
+	}
+	Blt_GraphExtents(graphPtr, &exts);
+	n = Blt_PolyRectClip(&exts, polygon, 4, bmPtr->outline); 
+	assert(n <= MAX_OUTLINE_POINTS);
+	if (n < 3) { 
+	    memcpy(&bmPtr->outline, polygon, sizeof(Point2D) * 4);
+	    bmPtr->nOutlinePts = 4;
+	} else {
+	    bmPtr->nOutlinePts = n;
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInBitmapMarker --
+ *
+ *	Indicates if the given point is over the bitmap marker.  The
+ *	area of the bitmap is the rectangle.
+ *
+ * Results:
+ *	Returns 1 is the point is over the bitmap marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInBitmapMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+    if (bmPtr->srcBitmap == None) {
+	return 0;
+    }
+    if (bmPtr->theta != 0.0) {
+	Point2D points[MAX_OUTLINE_POINTS];
+	register int i;
+
+	/*  
+	 * Generate the bounding polygon (isolateral) for the bitmap
+	 * and see if the point is inside of it.  
+	 */
+	for (i = 0; i < bmPtr->nOutlinePts; i++) {
+	    points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+	    points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+	}
+	return Blt_PointInPolygon(samplePtr, points, bmPtr->nOutlinePts);
+    }
+    return ((samplePtr->x >= bmPtr->anchorPos.x) && 
+	    (samplePtr->x < (bmPtr->anchorPos.x + bmPtr->destWidth)) &&
+	    (samplePtr->y >= bmPtr->anchorPos.y) && 
+	    (samplePtr->y < (bmPtr->anchorPos.y + bmPtr->destHeight)));
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInBitmapMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInBitmapMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+    if (bmPtr->nWorldPts < 1) {
+	return FALSE;
+    }
+    if (bmPtr->theta != 0.0) {
+	Point2D points[MAX_OUTLINE_POINTS];
+	register int i;
+	
+	/*  
+	 * Generate the bounding polygon (isolateral) for the bitmap
+	 * and see if the point is inside of it.  
+	 */
+	for (i = 0; i < bmPtr->nOutlinePts; i++) {
+	    points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x;
+	    points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y;
+	}
+	return Blt_RegionInPolygon(extsPtr, points, bmPtr->nOutlinePts, 
+		   enclosed);
+    }
+    if (enclosed) {
+	return ((bmPtr->anchorPos.x >= extsPtr->left) &&
+		(bmPtr->anchorPos.y >= extsPtr->top) && 
+		((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->right) &&
+		((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->bottom));
+    }
+    return !((bmPtr->anchorPos.x >= extsPtr->right) ||
+	     (bmPtr->anchorPos.y >= extsPtr->bottom) ||
+	     ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->left) ||
+	     ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawBitmapMarker --
+ *
+ *	Draws the bitmap marker that have a transparent of filled
+ *	background.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	GC stipple origins are changed to current window coordinates.
+ *	Commands are output to X to draw the marker in its current
+ *	mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawBitmapMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+    double theta;
+    Pixmap bitmap;
+
+    bitmap = GETBITMAP(bmPtr);
+    if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) {
+	return;
+    }
+    theta = FMOD(bmPtr->theta, (double)90.0);
+    if ((bmPtr->fillColor == NULL) || (theta != 0.0)) {
+
+	/* 
+	 * If the bitmap is rotated and a filled background is
+	 * required, then a filled polygon is drawn before the
+	 * bitmap. 
+	 */
+
+	if (bmPtr->fillColor != NULL) {
+	    int i;
+	    XPoint polygon[MAX_OUTLINE_POINTS];
+
+	    for (i = 0; i < bmPtr->nOutlinePts; i++) {
+		polygon[i].x = (short int)bmPtr->outline[i].x;
+		polygon[i].y = (short int)bmPtr->outline[i].y;
+	    }
+	    XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+		 polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin);
+	}
+	XSetClipMask(graphPtr->display, bmPtr->gc, bitmap);
+	XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x, 
+	       (int)bmPtr->anchorPos.y);
+    } else {
+	XSetClipMask(graphPtr->display, bmPtr->gc, None);
+	XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+    }
+    XCopyPlane(graphPtr->display, bitmap, drawable, bmPtr->gc, 0, 0,
+	bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x, 
+	(int)bmPtr->anchorPos.y, 1);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * BitmapMarkerToPostScript --
+ *
+ *	Generates PostScript to print a bitmap marker.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+BitmapMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;		/* Marker to be printed */
+    PsToken psToken;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+    Pixmap bitmap;
+
+    bitmap = GETBITMAP(bmPtr);
+    if (bitmap == None) {
+	return;
+    }
+    if (bmPtr->fillColor != NULL) {
+	Blt_BackgroundToPostScript(psToken, bmPtr->fillColor);
+	Blt_PolygonToPostScript(psToken, bmPtr->outline, 4);
+    }
+    Blt_ForegroundToPostScript(psToken, bmPtr->outlineColor);
+
+    Blt_FormatToPostScript(psToken,
+	"  gsave\n    %g %g translate\n    %d %d scale\n", 
+	   bmPtr->anchorPos.x, bmPtr->anchorPos.y + bmPtr->destHeight, 
+	   bmPtr->destWidth, -bmPtr->destHeight);
+    Blt_FormatToPostScript(psToken, "    %d %d true [%d 0 0 %d 0 %d] {",
+	bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth, 
+	-bmPtr->destHeight, bmPtr->destHeight);
+    Blt_BitmapDataToPostScript(psToken, graphPtr->display, bitmap,
+	bmPtr->destWidth, bmPtr->destHeight);
+    Blt_AppendToPostScript(psToken, "    } imagemask\n",
+	"grestore\n", (char *)NULL);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeBitmapMarker --
+ *
+ *	Releases the memory and attributes of the bitmap marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Bitmap attributes (GCs, colors, bitmap, etc) get destroyed.
+ *	Memory is released, X resources are freed, and the graph is
+ *	redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeBitmapMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+    if (bmPtr->gc != NULL) {
+	Tk_FreeGC(graphPtr->display, bmPtr->gc);
+    }
+    if (bmPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+    }
+    if (bmPtr->destBitmap != None) {
+	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateBitmapMarker --
+ *
+ *	Allocate memory and initialize methods for the new bitmap marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the bitmap marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateBitmapMarker()
+{
+    BitmapMarker *bmPtr;
+
+    bmPtr = Blt_Calloc(1, sizeof(BitmapMarker));
+    if (bmPtr != NULL) {
+	bmPtr->classPtr = &bitmapMarkerClass;
+    }
+    return (Marker *)bmPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+    ClientData clientData;
+    int x, y, width, height;	/* Not used. */
+    int imageWidth, imageHeight; /* Not used. */
+{
+    ImageMarker *imPtr = clientData;
+    Tk_PhotoHandle photo;
+    
+    photo = Blt_FindPhoto(imPtr->graphPtr->interp, imPtr->imageName);
+    if (photo != NULL) {
+	if (imPtr->srcImage != NULL) {
+	    Blt_FreeColorImage(imPtr->srcImage);
+	}
+	/* Convert the latest incarnation of the photo image back to a
+	 * color image that we can scale. */
+	imPtr->srcImage = Blt_PhotoToColorImage(photo);
+    }
+    imPtr->graphPtr->flags |= REDRAW_BACKING_STORE;
+    imPtr->flags |= MAP_ITEM;
+    Blt_EventuallyRedrawGraph(imPtr->graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureImageMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	a image marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as image pixmap, colors,
+ *	rotation, etc. get set for markerPtr; old resources get freed,
+ *	if there were any.  The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureImageMarker(markerPtr)
+    Marker *markerPtr;
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+    Graph *graphPtr = markerPtr->graphPtr;
+
+    if (Blt_ConfigModified(markerPtr->classPtr->configSpecs, "-image", 
+			   (char *)NULL)) {
+	Tcl_Interp *interp = graphPtr->interp;
+
+	if (imPtr->tkImage != NULL) {
+	    Tk_FreeImage(imPtr->tkImage);
+	    imPtr->tkImage = NULL;
+	}
+	if (imPtr->imageName[0] != '\0') {
+	    GC newGC;
+	    Tk_PhotoHandle photo;
+
+	    imPtr->tkImage = Tk_GetImage(interp, graphPtr->tkwin,
+		imPtr->imageName, ImageChangedProc, imPtr);
+	    if (imPtr->tkImage == NULL) {
+		Blt_Free(imPtr->imageName);
+		imPtr->imageName = NULL;
+		return TCL_ERROR;
+	    }
+	    photo = Blt_FindPhoto(interp, imPtr->imageName);
+	    if (photo != NULL) {
+		if (imPtr->srcImage != NULL) {
+		    Blt_FreeColorImage(imPtr->srcImage);
+		}
+		/* Convert the photo into a color image */
+		imPtr->srcImage = Blt_PhotoToColorImage(photo);
+	    }
+	    newGC = Tk_GetGC(graphPtr->tkwin, 0L, (XGCValues *)NULL);
+	    if (imPtr->gc != NULL) {
+		Tk_FreeGC(graphPtr->display, imPtr->gc);
+	    }
+	    imPtr->gc = newGC;
+	}
+    }
+    imPtr->flags |= MAP_ITEM;
+    if (imPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapImageMarker --
+ *
+ * 	This procedure gets called each time the layout of the graph
+ *	changes.  The x, y window coordinates of the image marker are
+ *	saved in the marker structure.
+ *
+ *	Additionly, if no background color was specified, the
+ *	GCTileStipXOrigin and GCTileStipYOrigin attributes are set in
+ *	the private GC.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Window coordinates are saved and if no background color was *
+ *	set, the GC stipple origins are changed to calculated window
+ *	coordinates.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapImageMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Extents2D exts;
+    Graph *graphPtr;
+    ImageMarker *imPtr;
+    Point2D anchorPos;
+    Point2D corner1, corner2;
+    int scaledWidth, scaledHeight;
+    int srcWidth, srcHeight;
+
+    imPtr = (ImageMarker *)markerPtr;
+    if (imPtr->tkImage == NULL) {
+	return;
+    }
+    graphPtr = imPtr->graphPtr;
+    corner1 = MapPoint(graphPtr, imPtr->worldPts, &imPtr->axes);
+    if (imPtr->srcImage == NULL) {
+	/* 
+	 * Don't scale or rotate non-photo images.
+	 */
+	Tk_SizeOfImage(imPtr->tkImage, &srcWidth, &srcHeight);
+	imPtr->width = srcWidth;
+	imPtr->height = srcHeight;
+	imPtr->anchorPos.x = corner1.x + imPtr->xOffset;
+	imPtr->anchorPos.y = corner1.y + imPtr->yOffset;
+	exts.left = imPtr->anchorPos.x;
+	exts.top = imPtr->anchorPos.y;
+	exts.right = exts.left + srcWidth - 1;
+	exts.bottom = exts.top + srcHeight - 1;
+	imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+	return;
+    }
+	
+    imPtr->width = srcWidth = Blt_ColorImageWidth(imPtr->srcImage);
+    imPtr->height = srcHeight = Blt_ColorImageHeight(imPtr->srcImage);
+    if ((srcWidth == 0) && (srcHeight == 0)) {
+	imPtr->clipped = TRUE;
+	return;			/* Empty image. */
+    }
+    if (imPtr->nWorldPts > 1) {
+	double hold;
+
+	corner2 = MapPoint(graphPtr, imPtr->worldPts + 1, &imPtr->axes);
+	/* Flip the corners if necessary */
+	if (corner1.x > corner2.x) {
+	    hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+	}
+	if (corner1.y > corner2.y) {
+	    hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+	}
+    } else {
+	corner2.x = corner1.x + srcWidth - 1;
+	corner2.y = corner1.y + srcHeight - 1;
+    }
+    scaledWidth = (int)(corner2.x - corner1.x) + 1;
+    scaledHeight = (int)(corner2.y - corner1.y) + 1;
+
+    if (imPtr->nWorldPts == 1) {
+	anchorPos = Blt_TranslatePoint(&corner1, scaledWidth, scaledHeight, 
+		imPtr->anchor);
+    } else {
+	anchorPos = corner1;
+    }
+    anchorPos.x += imPtr->xOffset;
+    anchorPos.y += imPtr->yOffset;
+
+    /* Check if the image sits at least partially in the plot area. */
+    exts.left = anchorPos.x;
+    exts.top = anchorPos.y;
+    exts.right = anchorPos.x + scaledWidth - 1;
+    exts.bottom = anchorPos.y + scaledHeight - 1;
+
+    imPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+    if (imPtr->clipped) {
+	return;			/* Image is offscreen. Don't generate
+				 * rotated or scaled images. */
+    }
+    if ((scaledWidth != srcWidth) || (scaledHeight != srcHeight)) {
+	Tk_PhotoHandle photo;
+	Blt_ColorImage destImage;
+	int x, y, width, height;
+	int left, right, top, bottom;
+
+	/* Determine the region of the subimage inside of the
+	 * destination image. */
+ 	left = MAX((int)exts.left, graphPtr->left);
+	top = MAX((int)exts.top, graphPtr->top);
+	right = MIN((int)exts.right, graphPtr->right);
+	bottom = MIN((int)exts.bottom, graphPtr->bottom);
+
+	/* Reset image location and coordinates to that of the region */
+	anchorPos.x = left;
+	anchorPos.y = top;
+
+	x = y = 0;
+	if (graphPtr->left > (int)exts.left) {
+	    x = graphPtr->left - (int)exts.left;
+	} 
+	if (graphPtr->top > (int)exts.top) {
+	    y = graphPtr->top - (int)exts.top;
+	} 
+	width = (int)(right - left + 1);
+	height = (int)(bottom - top + 1);
+
+	destImage = Blt_ResizeColorSubimage(imPtr->srcImage, x, y, width, 
+		height, scaledWidth, scaledHeight);
+#ifdef notyet
+	/* Now convert the color image into a pixmap */
+	if (imPtr->pixmap != None) {
+	    Blt_FreeColorTable(imPtr->colorTable);
+	    Tk_FreePixmap(Tk_Display(graphPtr->tkwin), imPtr->pixmap);
+	    imPtr->colorTable = NULL;
+	}
+	imPtr->pixmap = Blt_ColorImageToPixmap(graphPtr->interp,
+	    graphPtr->tkwin, destImage, &imPtr->colorTable);
+#else
+	imPtr->pixmap = None;
+	if (imPtr->tmpImage == NULL) {
+	    imPtr->tmpImage = Blt_CreateTemporaryImage(graphPtr->interp, 
+		     graphPtr->tkwin, imPtr);
+	    if (imPtr->tmpImage == NULL) {
+		return;
+	    }
+	}	    
+	/* Put the scaled colorimage into the photo. */
+	photo = Blt_FindPhoto(graphPtr->interp, 
+			      Blt_NameOfImage(imPtr->tmpImage));
+	Blt_ColorImageToPhoto(destImage, photo);
+#endif
+	Blt_FreeColorImage(destImage);
+	imPtr->width = width;
+	imPtr->height = height;
+    }
+    imPtr->anchorPos = anchorPos;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ *	Indicates if the given point is over the window marker.  The
+ *	area of the window is the rectangle.
+ *
+ * Results:
+ *	Returns 1 is the point is over the window marker, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInImageMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+    return ((samplePtr->x >= imPtr->anchorPos.x) && 
+	    (samplePtr->x < (imPtr->anchorPos.x + imPtr->width)) &&
+	    (samplePtr->y >= imPtr->anchorPos.y) && 
+	    (samplePtr->y < (imPtr->anchorPos.y + imPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInImageMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInImageMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+    if (imPtr->nWorldPts < 1) {
+	return FALSE;
+    }
+    if (enclosed) {
+	return ((imPtr->anchorPos.x >= extsPtr->left) &&
+		(imPtr->anchorPos.y >= extsPtr->top) && 
+		((imPtr->anchorPos.x + imPtr->width) <= extsPtr->right) &&
+		((imPtr->anchorPos.y + imPtr->height) <= extsPtr->bottom));
+    } 
+    return !((imPtr->anchorPos.x >= extsPtr->right) ||
+	     (imPtr->anchorPos.y >= extsPtr->bottom) ||
+	     ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->left) ||
+	     ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawImageMarker --
+ *
+ *	This procedure is invoked to draw a image marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	GC stipple origins are changed to current window coordinates.
+ *	Commands are output to X to draw the marker in its current mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawImageMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+    int width, height;
+
+    if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+	return;
+    }
+    if (imPtr->pixmap == None) {
+	Pixmap pixmap;
+	Tk_Image tkImage;
+
+	tkImage = (imPtr->tmpImage != NULL) ? imPtr->tmpImage : imPtr->tkImage;
+	Tk_SizeOfImage(tkImage, &width, &height);
+	/* pixmap = Tk_ImageGetPhotoPixmap(tkImage); */
+	pixmap = None;
+	if (pixmap == None) {	/* May not be a "photo" image. */
+	    Tk_RedrawImage(tkImage, 0, 0, width, height, drawable,
+			   (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+	} else {
+	    XCopyArea(imPtr->graphPtr->display, pixmap, drawable,
+		      imPtr->gc, 0, 0, width, height, (int)imPtr->anchorPos.x, 
+			(int)imPtr->anchorPos.y);
+	}
+    } else {
+	XCopyArea(imPtr->graphPtr->display, imPtr->pixmap, drawable,
+	    imPtr->gc, 0, 0, imPtr->width, imPtr->height,
+	    (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ImageMarkerToPostScript --
+ *
+ *	This procedure is invoked to print a image marker.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ImageMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;		/* Marker to be printed */
+    PsToken psToken;
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+    char *imageName;
+    Tk_PhotoHandle photo;
+
+    if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) {
+	return;			/* Image doesn't exist anymore */
+    }
+    imageName = (imPtr->tmpImage == NULL) 
+	? Blt_NameOfImage(imPtr->tkImage) : Blt_NameOfImage(imPtr->tmpImage);
+    photo = Blt_FindPhoto(markerPtr->graphPtr->interp, imageName);
+    if (photo == NULL) {
+	return;			/* Image isn't a photo image */
+    }
+    Blt_PhotoToPostScript(psToken, photo, imPtr->anchorPos.x, 
+			  imPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeImageMarker --
+ *
+ *	Destroys the structure containing the attributes of the image
+ * 	marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Image attributes (GCs, colors, image, etc) get destroyed.
+ *	Memory is released, X resources are freed, and the graph is
+ *	redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeImageMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+    if (imPtr->pixmap != None) {
+	Tk_FreePixmap(graphPtr->display, imPtr->pixmap);
+    }
+    if (imPtr->tkImage != NULL) {
+	Tk_FreeImage(imPtr->tkImage);
+    }
+    if (imPtr->tmpImage != NULL) {
+	Blt_DestroyTemporaryImage(graphPtr->interp, imPtr->tmpImage);
+    }	
+    if (imPtr->srcImage != NULL) {
+	Blt_FreeColorImage(imPtr->srcImage);
+    }
+    if (imPtr->gc != NULL) {
+	Tk_FreeGC(graphPtr->display, imPtr->gc);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateImageMarker --
+ *
+ *	Allocate memory and initialize methods for the new image marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the image marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateImageMarker()
+{
+    ImageMarker *imPtr;
+
+    imPtr = Blt_Calloc(1, sizeof(ImageMarker));
+    if (imPtr != NULL) {
+	imPtr->classPtr = &imageMarkerClass;
+    }
+    return (Marker *)imPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureTextMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or
+ *	reconfigure) a text marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for markerPtr;  old resources get freed, if there
+ *	were any.  The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureTextMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    tmPtr->style.theta = FMOD(tmPtr->style.theta, 360.0);
+    if (tmPtr->style.theta < 0.0) {
+	tmPtr->style.theta += 360.0;
+    }
+    newGC = NULL;
+    if (tmPtr->fillColor != NULL) {
+	gcMask = GCForeground;
+	gcValues.foreground = tmPtr->fillColor->pixel;
+	newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    }
+    if (tmPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, tmPtr->fillGC);
+    }
+    tmPtr->fillGC = newGC;
+    Blt_ResetTextStyle(graphPtr->tkwin, &tmPtr->style);
+
+    if (Blt_ConfigModified(tmPtr->classPtr->configSpecs, "-text", 
+	(char *)NULL)) {
+	if (tmPtr->textPtr != NULL) {
+	    Blt_Free(tmPtr->textPtr);
+	    tmPtr->textPtr = NULL;
+	}
+	tmPtr->width = tmPtr->height = 0;
+	if (tmPtr->string != NULL) {
+	    register int i;
+	    double rotWidth, rotHeight;
+
+	    tmPtr->textPtr = Blt_GetTextLayout(tmPtr->string, &tmPtr->style);
+	    Blt_GetBoundingBox(tmPtr->textPtr->width, tmPtr->textPtr->height, 
+	       tmPtr->style.theta, &rotWidth, &rotHeight, tmPtr->outline);
+	    tmPtr->width = ROUND(rotWidth);
+	    tmPtr->height = ROUND(rotHeight);
+	    for (i = 0; i < 4; i++) {
+		tmPtr->outline[i].x += ROUND(rotWidth * 0.5);
+		tmPtr->outline[i].y += ROUND(rotHeight * 0.5);
+	    }
+	    tmPtr->outline[4].x = tmPtr->outline[0].x;
+	    tmPtr->outline[4].y = tmPtr->outline[0].y;
+	}
+    }
+    tmPtr->flags |= MAP_ITEM;
+    if (tmPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapTextMarker --
+ *
+ *	Calculate the layout position for a text marker.  Positional
+ *	information is saved in the marker.  If the text is rotated,
+ *	a bitmap containing the text is created.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	If no background color has been specified, the GC stipple
+ *	origins are changed to current window coordinates. For both
+ *	rotated and non-rotated text, if any old bitmap is leftover,
+ *	it is freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapTextMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+    Extents2D exts;
+    Point2D anchorPos;
+
+    if (tmPtr->string == NULL) {
+	return;
+    }
+    anchorPos = MapPoint(graphPtr, tmPtr->worldPts, &tmPtr->axes);
+    anchorPos = Blt_TranslatePoint(&anchorPos, tmPtr->width, tmPtr->height, 
+	tmPtr->anchor);
+    anchorPos.x += tmPtr->xOffset;
+    anchorPos.y += tmPtr->yOffset;
+    /*
+     * Determine the bounding box of the text and test to see if it
+     * is at least partially contained within the plotting area.
+     */
+    exts.left = anchorPos.x;
+    exts.top = anchorPos.y;
+    exts.right = anchorPos.x + tmPtr->width - 1;
+    exts.bottom = anchorPos.y + tmPtr->height - 1;
+    tmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+    tmPtr->anchorPos = anchorPos;
+
+}
+
+static int
+PointInTextMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+    if (tmPtr->string == NULL) {
+	return 0;
+    }
+    if (tmPtr->style.theta != 0.0) {
+	Point2D points[5];
+	register int i;
+
+	/* 
+	 * Figure out the bounding polygon (isolateral) for the text
+	 * and see if the point is inside of it.
+	 */
+
+	for (i = 0; i < 5; i++) {
+	    points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+	    points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+	}
+	return Blt_PointInPolygon(samplePtr, points, 5);
+    } 
+    return ((samplePtr->x >= tmPtr->anchorPos.x) && 
+	    (samplePtr->x < (tmPtr->anchorPos.x + tmPtr->width)) &&
+	    (samplePtr->y >= tmPtr->anchorPos.y) && 
+	    (samplePtr->y < (tmPtr->anchorPos.y + tmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInTextMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInTextMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+    if (tmPtr->nWorldPts < 1) {
+	return FALSE;
+    }
+    if (tmPtr->style.theta != 0.0) {
+	Point2D points[5];
+	register int i;
+	
+	/*  
+	 * Generate the bounding polygon (isolateral) for the bitmap
+	 * and see if the point is inside of it.  
+	 */
+	for (i = 0; i < 4; i++) {
+	    points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+	    points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+	}
+	return Blt_RegionInPolygon(extsPtr, points, 4, enclosed);
+    } 
+    if (enclosed) {
+	return ((tmPtr->anchorPos.x >= extsPtr->left) &&
+		(tmPtr->anchorPos.y >= extsPtr->top) && 
+		((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->right) &&
+		((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->bottom));
+    }
+    return !((tmPtr->anchorPos.x >= extsPtr->right) ||
+	     (tmPtr->anchorPos.y >= extsPtr->bottom) ||
+	     ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->left) ||
+	     ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawTextMarker --
+ *
+ *	Draws the text marker on the graph.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Commands are output to X to draw the marker in its current
+ *	mode.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawTextMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+    Graph *graphPtr = markerPtr->graphPtr;
+
+    if (tmPtr->string == NULL) {
+	return;
+    }
+    if (tmPtr->fillGC != NULL) {
+	XPoint pointArr[4];
+	register int i;
+
+	/*
+	 * Simulate the rotated background of the bitmap by
+	 * filling a bounding polygon with the background color.
+	 */
+	for (i = 0; i < 4; i++) {
+	    pointArr[i].x = (short int)
+		(tmPtr->outline[i].x + tmPtr->anchorPos.x);
+	    pointArr[i].y = (short int)
+		(tmPtr->outline[i].y + tmPtr->anchorPos.y);
+	}
+	XFillPolygon(graphPtr->display, drawable, tmPtr->fillGC, pointArr, 4,
+	    Convex, CoordModeOrigin);
+    }
+    if (tmPtr->style.color != NULL) {
+	Blt_DrawTextLayout(graphPtr->tkwin, drawable, tmPtr->textPtr,
+	    &tmPtr->style, (int)tmPtr->anchorPos.x, (int)tmPtr->anchorPos.y);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextMarkerToPostScript --
+ *
+ *	Outputs PostScript commands to draw a text marker at a given
+ *	x,y coordinate, rotation, anchor, and font.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript font and color settings are changed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TextMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;
+    PsToken psToken;
+{
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+    if (tmPtr->string == NULL) {
+	return;
+    }
+    if (tmPtr->fillGC != NULL) {
+	Point2D polygon[4];
+	register int i;
+
+	/*
+	 * Simulate the rotated background of the bitmap by
+	 * filling a bounding polygon with the background color.
+	 */
+	for (i = 0; i < 4; i++) {
+	    polygon[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x;
+	    polygon[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y;
+	}
+	Blt_BackgroundToPostScript(psToken, tmPtr->fillColor);
+	Blt_PolygonToPostScript(psToken, polygon, 4);
+    }
+    Blt_TextToPostScript(psToken, tmPtr->string, &tmPtr->style, 
+		 tmPtr->anchorPos.x, tmPtr->anchorPos.y);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeTextMarker --
+ *
+ *	Destroys the structure containing the attributes of the text
+ * 	marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Text attributes (GCs, colors, stipple, font, etc) get destroyed.
+ *	Memory is released, X resources are freed, and the graph is
+ *	redrawn.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeTextMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+    Blt_FreeTextStyle(graphPtr->display, &tmPtr->style);
+    if (tmPtr->textPtr != NULL) {
+	Blt_Free(tmPtr->textPtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTextMarker --
+ *
+ *	Allocate memory and initialize methods for the new text marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the text marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateTextMarker()
+{
+    TextMarker *tmPtr;
+
+    tmPtr = Blt_Calloc(1, sizeof(TextMarker));
+    assert(tmPtr);
+
+    tmPtr->classPtr = &textMarkerClass;
+    Blt_InitTextStyle(&tmPtr->style);
+    tmPtr->style.anchor = TK_ANCHOR_NW;
+    tmPtr->style.padLeft = tmPtr->style.padRight = 4;
+    tmPtr->style.padTop = tmPtr->style.padBottom = 4;
+
+    return (Marker *)tmPtr;
+}
+
+
+static void ChildEventProc _ANSI_ARGS_((ClientData clientData,
+	XEvent *eventPtr));
+static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin));
+
+static void ChildCustodyProc _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin));
+
+static Tk_GeomMgr winMarkerMgrInfo =
+{
+    "graph",			/* Name of geometry manager used by winfo */
+    ChildGeometryProc,		/* Procedure to for new geometry requests */
+    ChildCustodyProc,		/* Procedure when window is taken away */
+};
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureWindowMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	a window marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as window pathname, placement,
+ *	etc. get set for markerPtr; old resources get freed, if there
+ *	were any.  The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureWindowMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+    Tk_Window tkwin;
+
+    if (wmPtr->pathName == NULL) {
+	return TCL_OK;
+    }
+    tkwin = Tk_NameToWindow(graphPtr->interp, wmPtr->pathName, 
+	    graphPtr->tkwin);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    if (Tk_Parent(tkwin) != graphPtr->tkwin) {
+	Tcl_AppendResult(graphPtr->interp, "\"", wmPtr->pathName,
+	    "\" is not a child of \"", Tk_PathName(graphPtr->tkwin), "\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (tkwin != wmPtr->tkwin) {
+	if (wmPtr->tkwin != NULL) {
+	    Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+		ChildEventProc, wmPtr);
+	    Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+	    Tk_UnmapWindow(wmPtr->tkwin);
+	}
+	Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildEventProc, 
+		wmPtr);
+	Tk_ManageGeometry(tkwin, &winMarkerMgrInfo, wmPtr);
+    }
+    wmPtr->tkwin = tkwin;
+
+    wmPtr->flags |= MAP_ITEM;
+    if (wmPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapWindowMarker --
+ *
+ *	Calculate the layout position for a window marker.  Positional
+ *	information is saved in the marker.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapWindowMarker(markerPtr)
+    Marker *markerPtr;
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+    Graph *graphPtr = markerPtr->graphPtr;
+    Extents2D exts;
+    int width, height;
+
+    if (wmPtr->tkwin == (Tk_Window)NULL) {
+	return;
+    }
+    wmPtr->anchorPos = MapPoint(graphPtr, wmPtr->worldPts, &wmPtr->axes);
+
+    width = Tk_ReqWidth(wmPtr->tkwin);
+    height = Tk_ReqHeight(wmPtr->tkwin);
+    if (wmPtr->reqWidth > 0) {
+	width = wmPtr->reqWidth;
+    }
+    if (wmPtr->reqHeight > 0) {
+	height = wmPtr->reqHeight;
+    }
+    wmPtr->anchorPos = Blt_TranslatePoint(&wmPtr->anchorPos, width, height, 
+	wmPtr->anchor);
+    wmPtr->anchorPos.x += wmPtr->xOffset;
+    wmPtr->anchorPos.y += wmPtr->yOffset;
+    wmPtr->width = width;
+    wmPtr->height = height;
+
+    /*
+     * Determine the bounding box of the window and test to see if it
+     * is at least partially contained within the plotting area.
+     */
+    exts.left = wmPtr->anchorPos.x;
+    exts.top = wmPtr->anchorPos.y;
+    exts.right = wmPtr->anchorPos.x + wmPtr->width - 1;
+    exts.bottom = wmPtr->anchorPos.y + wmPtr->height - 1;
+    wmPtr->clipped = BoxesDontOverlap(graphPtr, &exts);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * PointInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+PointInWindowMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+    return ((samplePtr->x >= wmPtr->anchorPos.x) && 
+	    (samplePtr->x < (wmPtr->anchorPos.x + wmPtr->width)) &&
+	    (samplePtr->y >= wmPtr->anchorPos.y) && 
+	    (samplePtr->y < (wmPtr->anchorPos.y + wmPtr->height)));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInWindowMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+    if (wmPtr->nWorldPts < 1) {
+	return FALSE;
+    }
+    if (enclosed) {
+	return ((wmPtr->anchorPos.x >= extsPtr->left) &&
+		(wmPtr->anchorPos.y >= extsPtr->top) && 
+		((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->right) &&
+		((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->bottom));
+    }
+    return !((wmPtr->anchorPos.x >= extsPtr->right) ||
+	     (wmPtr->anchorPos.y >= extsPtr->bottom) ||
+	     ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->left) ||
+	     ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->top));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawWindowMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawWindowMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+    if (wmPtr->tkwin == NULL) {
+	return;
+    }
+    if ((wmPtr->height != Tk_Height(wmPtr->tkwin)) ||
+	(wmPtr->width != Tk_Width(wmPtr->tkwin)) ||
+	((int)wmPtr->anchorPos.x != Tk_X(wmPtr->tkwin)) ||
+	((int)wmPtr->anchorPos.y != Tk_Y(wmPtr->tkwin))) {
+	Tk_MoveResizeWindow(wmPtr->tkwin, (int)wmPtr->anchorPos.x, 
+	    (int)wmPtr->anchorPos.y, wmPtr->width, wmPtr->height);
+    }
+    if (!Tk_IsMapped(wmPtr->tkwin)) {
+	Tk_MapWindow(wmPtr->tkwin);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * WindowMarkerToPostScript --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+WindowMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;
+    PsToken psToken;
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+    if (wmPtr->tkwin == NULL) {
+	return;
+    }
+    if (Tk_IsMapped(wmPtr->tkwin)) {
+	Blt_WindowToPostScript(psToken, wmPtr->tkwin, wmPtr->anchorPos.x, 
+	       wmPtr->anchorPos.y);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeWindowMarker --
+ *
+ *	Destroys the structure containing the attributes of the window
+ *      marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Window is destroyed and removed from the screen.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeWindowMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+    if (wmPtr->tkwin != NULL) {
+	Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask,
+	    ChildEventProc, wmPtr);
+	Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0);
+	Tk_DestroyWindow(wmPtr->tkwin);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateWindowMarker --
+ *
+ *	Allocate memory and initialize methods for the new window marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the window marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateWindowMarker()
+{
+    WindowMarker *wmPtr;
+
+    wmPtr = Blt_Calloc(1, sizeof(WindowMarker));
+    if (wmPtr != NULL) {
+	wmPtr->classPtr = &windowMarkerClass;
+    }
+    return (Marker *)wmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildEventProc --
+ *
+ *	This procedure is invoked whenever StructureNotify events
+ *	occur for a window that's managed as part of a graph window
+ *	marker. This procedure's only purpose is to clean up when
+ *	windows are deleted.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The window is disassociated from the window item when it is
+ *	deleted.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ChildEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Pointer to record describing window item. */
+    XEvent *eventPtr;		/* Describes what just happened. */
+{
+    WindowMarker *wmPtr = clientData;
+
+    if (eventPtr->type == DestroyNotify) {
+	wmPtr->tkwin = NULL;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildGeometryProc --
+ *
+ *	This procedure is invoked whenever a window that's associated
+ *	with a window item changes its requested dimensions.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The size and location on the window of the window may change,
+ *	depending on the options specified for the window item.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ChildGeometryProc(clientData, tkwin)
+    ClientData clientData;	/* Pointer to record for window item. */
+    Tk_Window tkwin;		/* Window that changed its desired size. */
+{
+    WindowMarker *wmPtr = clientData;
+
+    if (wmPtr->reqWidth == 0) {
+	wmPtr->width = Tk_ReqWidth(tkwin);
+    }
+    if (wmPtr->reqHeight == 0) {
+	wmPtr->height = Tk_ReqHeight(tkwin);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ChildCustodyProc --
+ *
+ *	This procedure is invoked when an embedded window has been
+ *	stolen by another geometry manager.  The information and
+ *	memory associated with the widget is released.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Arranges for the graph to be redrawn without the embedded
+ *	widget at the next idle point.
+ *
+ * ----------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+ChildCustodyProc(clientData, tkwin)
+    ClientData clientData;	/* Window marker to be destroyed. */
+    Tk_Window tkwin;		/* Not used. */
+{
+    Marker *markerPtr = clientData;
+    Graph *graphPtr;
+
+    graphPtr = markerPtr->graphPtr;
+    DestroyMarker(markerPtr);
+    /*
+     * Not really needed. We should get an Expose event when the
+     * child window is unmapped.
+     */
+    Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapLineMarker --
+ *
+ *	Calculate the layout position for a line marker.  Positional
+ *	information is saved in the marker.  The line positions are
+ *	stored in an array of points (malloc'ed).
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapLineMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+    Point2D *srcPtr, *endPtr;
+    Segment2D *segments, *segPtr;
+    Point2D p, q, next;
+    Extents2D exts;
+
+    lmPtr->nSegments = 0;
+    if (lmPtr->segments != NULL) {
+	Blt_Free(lmPtr->segments);
+    }
+    if (lmPtr->nWorldPts < 2) {
+	return;			/* Too few points */
+    }
+    Blt_GraphExtents(graphPtr, &exts);
+
+    /* 
+     * Allow twice the number of world coordinates. The line will
+     * represented as series of line segments, not one continous
+     * polyline.  This is because clipping against the plot area may
+     * chop the line into several disconnected segments.
+     */
+    segments = Blt_Malloc(lmPtr->nWorldPts * sizeof(Segment2D));
+    srcPtr = lmPtr->worldPts;
+    p = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+    p.x += lmPtr->xOffset;
+    p.y += lmPtr->yOffset;
+
+    segPtr = segments;
+    for (srcPtr++, endPtr = lmPtr->worldPts + lmPtr->nWorldPts; 
+	 srcPtr < endPtr; srcPtr++) {
+	next = MapPoint(graphPtr, srcPtr, &lmPtr->axes);
+	next.x += lmPtr->xOffset;
+	next.y += lmPtr->yOffset;
+	q = next;
+	if (Blt_LineRectClip(&exts, &p, &q)) {
+	    segPtr->p = p;
+	    segPtr->q = q;
+	    segPtr++;
+	}
+	p = next;
+    }
+    lmPtr->nSegments = segPtr - segments;
+    lmPtr->segments = segments;
+    lmPtr->clipped = (lmPtr->nSegments == 0);
+}
+
+static int
+PointInLineMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+    return Blt_PointInSegments(samplePtr, lmPtr->segments, lmPtr->nSegments, 
+	   (double)lmPtr->graphPtr->halo);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInLineMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+    if (lmPtr->nWorldPts < 2) {
+	return FALSE;
+    }
+    if (enclosed) {
+	Point2D p;
+	Point2D *pointPtr, *endPtr;
+
+	for (pointPtr = lmPtr->worldPts, 
+		 endPtr = lmPtr->worldPts + lmPtr->nWorldPts;
+	     pointPtr < endPtr; pointPtr++) {
+	    p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+	    if ((p.x < extsPtr->left) && (p.x > extsPtr->right) &&
+		(p.y < extsPtr->top) && (p.y > extsPtr->bottom)) {
+		return FALSE;
+	    }
+	}
+	return TRUE;		/* All points inside bounding box. */
+    } else {
+	Point2D p, q;
+	int count;
+	Point2D *pointPtr, *endPtr;
+
+	count = 0;
+	for (pointPtr = lmPtr->worldPts, 
+		 endPtr = lmPtr->worldPts + (lmPtr->nWorldPts - 1);
+	     pointPtr < endPtr; pointPtr++) {
+	    p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes);
+	    q = MapPoint(lmPtr->graphPtr, pointPtr + 1, &lmPtr->axes);
+	    if (Blt_LineRectClip(extsPtr, &p, &q)) {
+		count++;
+	    }
+	}
+	return (count > 0);	/* At least 1 segment passes through region. */
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DrawLineMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DrawLineMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+    if (lmPtr->nSegments > 0) {
+	Graph *graphPtr = markerPtr->graphPtr;
+
+	Blt_Draw2DSegments(graphPtr->display, drawable, lmPtr->gc, 
+		lmPtr->segments, lmPtr->nSegments);
+	if (lmPtr->xor) {	/* Toggle the drawing state */
+	    lmPtr->xorState = (lmPtr->xorState == 0);
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureLineMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	a line marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as line width, colors, dashes,
+ *	etc. get set for markerPtr; old resources get freed, if there
+ *	were any.  The marker is eventually redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLineMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+    Drawable drawable;
+
+    drawable = Tk_WindowId(graphPtr->tkwin);
+    gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
+    if (lmPtr->outlineColor != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = lmPtr->outlineColor->pixel;
+    }
+    if (lmPtr->fillColor != NULL) {
+	gcMask |= GCBackground;
+	gcValues.background = lmPtr->fillColor->pixel;
+    }
+    gcValues.cap_style = lmPtr->capStyle;
+    gcValues.join_style = lmPtr->joinStyle;
+    gcValues.line_width = LineWidth(lmPtr->lineWidth);
+    gcValues.line_style = LineSolid;
+    if (LineIsDashed(lmPtr->dashes)) {
+	gcValues.line_style = 
+	    (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash;
+    }
+    if (lmPtr->xor) {
+	unsigned long pixel;
+	gcValues.function = GXxor;
+
+	gcMask |= GCFunction;
+	if (graphPtr->plotBg == NULL) {
+	    pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+	} else {
+	    pixel = graphPtr->plotBg->pixel;
+	}
+	if (gcMask & GCBackground) {
+	    gcValues.background ^= pixel;
+	}
+	gcValues.foreground ^= pixel;
+	if (drawable != None) {
+	    DrawLineMarker(markerPtr, drawable);
+	}
+    }
+    newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (lmPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+    }
+    if (LineIsDashed(lmPtr->dashes)) {
+	Blt_SetDashes(graphPtr->display, newGC, &lmPtr->dashes);
+    }
+    lmPtr->gc = newGC;
+    if (lmPtr->xor) {
+	if (drawable != None) {
+	    MapLineMarker(markerPtr);
+	    DrawLineMarker(markerPtr, drawable);
+	}
+	return TCL_OK;
+    }
+    lmPtr->flags |= MAP_ITEM;
+    if (lmPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * LineMarkerToPostScript --
+ *
+ *	Prints postscript commands to display the connect line.
+ *	Dashed lines need to be handled specially, especially if a
+ *	background color is designated.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	PostScript output commands are saved in the interpreter
+ *	(infoPtr->interp) result field.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+LineMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;
+    PsToken psToken;
+{
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+    if (lmPtr->nSegments > 0) {
+	Blt_LineAttributesToPostScript(psToken, lmPtr->outlineColor,
+	    lmPtr->lineWidth, &lmPtr->dashes, lmPtr->capStyle,
+	    lmPtr->joinStyle);
+	if ((LineIsDashed(lmPtr->dashes)) && (lmPtr->fillColor != NULL)) {
+	    Blt_AppendToPostScript(psToken, "/DashesProc {\n  gsave\n    ",
+		(char *)NULL);
+	    Blt_BackgroundToPostScript(psToken, lmPtr->fillColor);
+	    Blt_AppendToPostScript(psToken, "    ", (char *)NULL);
+	    Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+	    Blt_AppendToPostScript(psToken,
+		"stroke\n",
+		"  grestore\n",
+		"} def\n", (char *)NULL);
+	} else {
+	    Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+		(char *)NULL);
+	}
+	Blt_2DSegmentsToPostScript(psToken, lmPtr->segments, lmPtr->nSegments);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreeLineMarker --
+ *
+ *	Destroys the structure and attributes of a line marker.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Line attributes (GCs, colors, stipple, etc) get released.
+ *	Memory is deallocated, X resources are freed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreeLineMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+    if (lmPtr->gc != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+    }
+    if (lmPtr->segments != NULL) {
+	Blt_Free(lmPtr->segments);
+    }
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateLineMarker --
+ *
+ *	Allocate memory and initialize methods for a new line marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the line marker structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Marker *
+CreateLineMarker()
+{
+    LineMarker *lmPtr;
+
+    lmPtr = Blt_Calloc(1, sizeof(LineMarker));
+    if (lmPtr != NULL) {
+	lmPtr->classPtr = &lineMarkerClass;
+	lmPtr->xor = FALSE;
+	lmPtr->capStyle = CapButt;
+	lmPtr->joinStyle = JoinMiter;
+    }
+    return (Marker *)lmPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * MapPolygonMarker --
+ *
+ *	Calculate the layout position for a polygon marker.  Positional
+ *	information is saved in the polygon in an array of points
+ *	(malloc'ed).
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+MapPolygonMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+    Point2D *srcPtr, *destPtr, *endPtr;
+    Point2D *screenPts;
+    Extents2D exts;
+    int nScreenPts;
+
+    if (pmPtr->outlinePts != NULL) {
+	Blt_Free(pmPtr->outlinePts);
+	pmPtr->outlinePts = NULL;
+	pmPtr->nOutlinePts = 0;
+    }
+    if (pmPtr->fillPts != NULL) {
+	Blt_Free(pmPtr->fillPts);
+	pmPtr->fillPts = NULL;
+	pmPtr->nFillPts = 0;
+    }
+    if (pmPtr->screenPts != NULL) {
+	Blt_Free(pmPtr->screenPts);
+	pmPtr->screenPts = NULL;
+    }
+    if (pmPtr->nWorldPts < 3) {
+	return;			/* Too few points */
+    }
+
+    /* 
+     * Allocate and fill a temporary array to hold the screen
+     * coordinates of the polygon. 
+     */
+    nScreenPts = pmPtr->nWorldPts + 1;
+    screenPts = Blt_Malloc((nScreenPts + 1) * sizeof(Point2D));
+    endPtr = pmPtr->worldPts + pmPtr->nWorldPts;
+    destPtr = screenPts;
+    for (srcPtr = pmPtr->worldPts; srcPtr < endPtr; srcPtr++) {
+	*destPtr = MapPoint(graphPtr, srcPtr, &pmPtr->axes);
+	destPtr->x += pmPtr->xOffset;
+	destPtr->y += pmPtr->yOffset;
+	destPtr++;
+    }
+    *destPtr = screenPts[0];
+
+    Blt_GraphExtents(graphPtr, &exts);
+    pmPtr->clipped = TRUE;
+    if (pmPtr->fill.fgColor != NULL) { /* Polygon fill required. */
+	Point2D *fillPts;
+	int n;
+
+	fillPts = Blt_Malloc(sizeof(Point2D) * nScreenPts * 3);
+	assert(fillPts);
+	n = Blt_PolyRectClip(&exts, screenPts, pmPtr->nWorldPts, fillPts);
+	if (n < 3) { 
+	    Blt_Free(fillPts);
+	} else {
+	    pmPtr->nFillPts = n;
+	    pmPtr->fillPts = fillPts;
+	    pmPtr->clipped = FALSE;
+	}
+    }
+    if ((pmPtr->outline.fgColor != NULL) && (pmPtr->lineWidth > 0)) { 
+	Segment2D *outlinePts;
+	register Segment2D *segPtr;
+	/* 
+	 * Generate line segments representing the polygon outline.
+	 * The resulting outline may or may not be closed from
+	 * viewport clipping.  
+	 */
+	outlinePts = Blt_Malloc(nScreenPts * sizeof(Segment2D));
+	if (outlinePts == NULL) {
+	    return;		/* Can't allocate point array */
+	}
+	/* 
+	 * Note that this assumes that the point array contains an
+	 * extra point that closes the polygon. 
+	 */
+	segPtr = outlinePts;
+	for (srcPtr = screenPts, endPtr = screenPts + (nScreenPts - 1);
+	     srcPtr < endPtr; srcPtr++) {
+	    segPtr->p = srcPtr[0];
+	    segPtr->q = srcPtr[1];
+	    if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+		segPtr++;
+	    }
+	}
+	pmPtr->nOutlinePts = segPtr - outlinePts;
+	pmPtr->outlinePts = outlinePts;
+	if (pmPtr->nOutlinePts > 0) {
+	    pmPtr->clipped = FALSE;
+	}
+    }
+    pmPtr->screenPts = screenPts;
+}
+
+static int
+PointInPolygonMarker(markerPtr, samplePtr)
+    Marker *markerPtr;
+    Point2D *samplePtr;
+{
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+    if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+	return Blt_PointInPolygon(samplePtr, pmPtr->screenPts, 
+		  pmPtr->nWorldPts + 1);
+    }
+    return FALSE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RegionInPolygonMarker --
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+RegionInPolygonMarker(markerPtr, extsPtr, enclosed)
+    Marker *markerPtr;
+    Extents2D *extsPtr;
+    int enclosed;
+{
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+    
+    if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+	return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts,
+	       enclosed);
+    }
+    return FALSE;
+}
+
+static void
+DrawPolygonMarker(markerPtr, drawable)
+    Marker *markerPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+    /* Draw polygon fill region */
+    if ((pmPtr->nFillPts > 0) && (pmPtr->fill.fgColor != NULL)) {
+	XPoint *destPtr, *pointArr;
+	Point2D *srcPtr, *endPtr;
+	
+	pointArr = Blt_Malloc(pmPtr->nFillPts * sizeof(XPoint));
+	if (pointArr == NULL) {
+	    return;
+	}
+	destPtr = pointArr;
+	for (srcPtr = pmPtr->fillPts, 
+		 endPtr = pmPtr->fillPts + pmPtr->nFillPts; srcPtr < endPtr; 
+	     srcPtr++) {
+	    destPtr->x = (short int)srcPtr->x;
+	    destPtr->y = (short int)srcPtr->y;
+	    destPtr++;
+	}
+	XFillPolygon(graphPtr->display, drawable, pmPtr->fillGC,
+	    pointArr, pmPtr->nFillPts, Complex, CoordModeOrigin);
+	Blt_Free(pointArr);
+    }
+    /* and then the outline */
+    if ((pmPtr->nOutlinePts > 0) && (pmPtr->lineWidth > 0) && 
+	(pmPtr->outline.fgColor != NULL)) {
+	Blt_Draw2DSegments(graphPtr->display, drawable, pmPtr->outlineGC,
+	    pmPtr->outlinePts, pmPtr->nOutlinePts);
+    }
+}
+
+
+static void
+PolygonMarkerToPostScript(markerPtr, psToken)
+    Marker *markerPtr;
+    PsToken psToken;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+    if (pmPtr->fill.fgColor != NULL) {
+
+	/*
+	 * Options:  fg bg
+	 *			Draw outline only.
+	 *	     x          Draw solid or stipple.
+	 *	     x  x       Draw solid or stipple.
+	 */
+
+	/* Create a path to use for both the polygon and its outline. */
+	Blt_PathToPostScript(psToken, pmPtr->fillPts, pmPtr->nFillPts);
+	Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+	/* If the background fill color was specified, draw the
+	 * polygon in a solid fashion with that color.  */
+	if (pmPtr->fill.bgColor != NULL) {
+	    Blt_BackgroundToPostScript(psToken, pmPtr->fill.bgColor);
+	    Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+	}
+	Blt_ForegroundToPostScript(psToken, pmPtr->fill.fgColor);
+	if (pmPtr->stipple != None) {
+	    /* Draw the stipple in the foreground color. */
+	    Blt_StippleToPostScript(psToken, graphPtr->display, 
+				    pmPtr->stipple);
+	} else {
+	    Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+	}
+    }
+
+    /* Draw the outline in the foreground color.  */
+    if ((pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) {
+
+	/*  Set up the line attributes.  */
+	Blt_LineAttributesToPostScript(psToken, pmPtr->outline.fgColor,
+	    pmPtr->lineWidth, &pmPtr->dashes, pmPtr->capStyle,
+	    pmPtr->joinStyle);
+
+	/*  
+	 * Define on-the-fly a PostScript macro "DashesProc" that
+	 * will be executed for each call to the Polygon drawing
+	 * routine.  If the line isn't dashed, simply make this an
+	 * empty definition.  
+	 */
+	if ((pmPtr->outline.bgColor != NULL) && (LineIsDashed(pmPtr->dashes))) {
+	    Blt_AppendToPostScript(psToken,
+		"/DashesProc {\n",
+		"gsave\n    ", (char *)NULL);
+	    Blt_BackgroundToPostScript(psToken, pmPtr->outline.bgColor);
+	    Blt_AppendToPostScript(psToken, "    ", (char *)NULL);
+	    Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+	    Blt_AppendToPostScript(psToken,
+		"stroke\n",
+		"  grestore\n",
+		"} def\n", (char *)NULL);
+	} else {
+	    Blt_AppendToPostScript(psToken, "/DashesProc {} def\n",
+		(char *)NULL);
+	}
+	Blt_2DSegmentsToPostScript(psToken, pmPtr->outlinePts, 
+		   pmPtr->nOutlinePts);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigurePolygonMarker --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	a polygon marker.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as polygon color, dashes,
+ *	fillstyle, etc. get set for markerPtr; old resources get
+ *	freed, if there were any.  The marker is eventually
+ *	redisplayed.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+ConfigurePolygonMarker(markerPtr)
+    Marker *markerPtr;
+{
+    Graph *graphPtr = markerPtr->graphPtr;
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+    Drawable drawable;
+
+    drawable = Tk_WindowId(graphPtr->tkwin);
+    gcMask = (GCLineWidth | GCLineStyle);
+    if (pmPtr->outline.fgColor != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = pmPtr->outline.fgColor->pixel;
+    }
+    if (pmPtr->outline.bgColor != NULL) {
+	gcMask |= GCBackground;
+	gcValues.background = pmPtr->outline.bgColor->pixel;
+    }
+    gcMask |= (GCCapStyle | GCJoinStyle);
+    gcValues.cap_style = pmPtr->capStyle;
+    gcValues.join_style = pmPtr->joinStyle;
+    gcValues.line_style = LineSolid;
+    gcValues.dash_offset = 0;
+    gcValues.line_width = LineWidth(pmPtr->lineWidth);
+    if (LineIsDashed(pmPtr->dashes)) {
+	gcValues.line_style = (pmPtr->outline.bgColor == NULL)
+	    ? LineOnOffDash : LineDoubleDash;
+    }
+    if (pmPtr->xor) {
+	unsigned long pixel;
+	gcValues.function = GXxor;
+
+	gcMask |= GCFunction;
+	if (graphPtr->plotBg == NULL) {
+	    /* The graph's color option may not have been set yet */
+	    pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+	} else {
+	    pixel = graphPtr->plotBg->pixel;
+	}
+	if (gcMask & GCBackground) {
+	    gcValues.background ^= pixel;
+	}
+	gcValues.foreground ^= pixel;
+	if (drawable != None) {
+	    DrawPolygonMarker(markerPtr, drawable);
+	}
+    }
+    newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (LineIsDashed(pmPtr->dashes)) {
+	Blt_SetDashes(graphPtr->display, newGC, &pmPtr->dashes);
+    }
+    if (pmPtr->outlineGC != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+    }
+    pmPtr->outlineGC = newGC;
+
+    gcMask = 0;
+    if (pmPtr->fill.fgColor != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = pmPtr->fill.fgColor->pixel;
+    }
+    if (pmPtr->fill.bgColor != NULL) {
+	gcMask |= GCBackground;
+	gcValues.background = pmPtr->fill.bgColor->pixel;
+    }
+    if (pmPtr->stipple != None) {
+	gcValues.stipple = pmPtr->stipple;
+	gcValues.fill_style = (pmPtr->fill.bgColor != NULL)
+	    ? FillOpaqueStippled : FillStippled;
+	gcMask |= (GCStipple | GCFillStyle);
+    }
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (pmPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+    }
+    pmPtr->fillGC = newGC;
+
+    if ((gcMask == 0) && !(graphPtr->flags & RESET_AXES) && (pmPtr->xor)) {
+	if (drawable != None) {
+	    MapPolygonMarker(markerPtr);
+	    DrawPolygonMarker(markerPtr, drawable);
+	}
+	return TCL_OK;
+    }
+    pmPtr->flags |= MAP_ITEM;
+    if (pmPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FreePolygonMarker --
+ *
+ *	Release memory and resources allocated for the polygon element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the polygon element is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+FreePolygonMarker(graphPtr, markerPtr)
+    Graph *graphPtr;
+    Marker *markerPtr;
+{
+    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+    if (pmPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+    }
+    if (pmPtr->outlineGC != NULL) {
+	Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+    }
+    if (pmPtr->fillPts != NULL) {
+	Blt_Free(pmPtr->fillPts);
+    }
+    if (pmPtr->outlinePts != NULL) {
+	Blt_Free(pmPtr->outlinePts);
+    }
+    if (pmPtr->screenPts != NULL) {
+	Blt_Free(pmPtr->screenPts);
+    }
+    Blt_FreeColorPair(&pmPtr->outline);
+    Blt_FreeColorPair(&pmPtr->fill);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreatePolygonMarker --
+ *
+ *	Allocate memory and initialize methods for the new polygon
+ *	marker.
+ *
+ * Results:
+ *	The pointer to the newly allocated marker structure is
+ *	returned.
+ *
+ * Side effects:
+ *	Memory is allocated for the polygon marker structure.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+static Marker *
+CreatePolygonMarker()
+{
+    PolygonMarker *pmPtr;
+
+    pmPtr = Blt_Calloc(1, sizeof(PolygonMarker));
+    if (pmPtr != NULL) {
+	pmPtr->classPtr = &polygonMarkerClass;
+	pmPtr->capStyle = CapButt;
+	pmPtr->joinStyle = JoinMiter;
+
+    }
+    return (Marker *)pmPtr;
+}
+
+static int
+NameToMarker(graphPtr, name, markerPtrPtr)
+    Graph *graphPtr;
+    char *name;
+    Marker **markerPtrPtr;
+{
+    Blt_HashEntry *hPtr;
+    
+    hPtr = Blt_FindHashEntry(&graphPtr->markers.table, name);
+    if (hPtr != NULL) {
+	*markerPtrPtr = (Marker *)Blt_GetHashValue(hPtr);
+	return TCL_OK;
+    }
+    Tcl_AppendResult(graphPtr->interp, "can't find marker \"", name, 
+	     "\" in \"", Tk_PathName(graphPtr->tkwin), (char *)NULL);
+    return TCL_ERROR;
+}
+
+
+static int
+RenameMarker(graphPtr, markerPtr, oldName, newName)
+    Graph *graphPtr;
+    Marker *markerPtr;
+    char *oldName, *newName;
+{
+    int isNew;
+    Blt_HashEntry *hPtr;
+
+    /* Rename the marker only if no marker already exists by that name */
+    hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, newName, &isNew);
+    if (!isNew) {
+	Tcl_AppendResult(graphPtr->interp, "can't rename marker: \"", newName,
+	    "\" already exists", (char *)NULL);
+	return TCL_ERROR;
+    }
+    markerPtr->name = Blt_Strdup(newName);
+    markerPtr->hashPtr = hPtr;
+    Blt_SetHashValue(hPtr, (char *)markerPtr);
+
+    /* Delete the old hash entry */
+    hPtr = Blt_FindHashEntry(&graphPtr->markers.table, oldName);
+    Blt_DeleteHashEntry(&graphPtr->markers.table, hPtr);
+    if (oldName != NULL) {
+	Blt_Free(oldName);
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ *	Returns a list of marker identifiers in interp->result;
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+NamesOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+    Blt_ChainLink *linkPtr;
+    register int i;
+
+    Tcl_ResetResult(interp);
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+	if (argc == 3) {
+	    Tcl_AppendElement(interp, markerPtr->name);
+	    continue;
+	}
+	for (i = 3; i < argc; i++) {
+	    if (Tcl_StringMatch(markerPtr->name, argv[i])) {
+		Tcl_AppendElement(interp, markerPtr->name);
+		break;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+ClientData
+Blt_MakeMarkerTag(graphPtr, tagName)
+    Graph *graphPtr;
+    char *tagName;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    hPtr = Blt_CreateHashEntry(&graphPtr->markers.tagTable, tagName, &isNew);
+    assert(hPtr);
+    return Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *	.g element bind elemName sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    if (argc == 3) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	char *tag;
+
+	for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.tagTable, &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tag = Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr);
+	    Tcl_AppendElement(interp, tag);
+	}
+	return TCL_OK;
+    }
+    return Blt_ConfigureBindings(interp, graphPtr->bindTable,
+	Blt_MakeMarkerTag(graphPtr, argv[3]), argc - 4, argv + 4);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+
+    if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Tk_ConfigureValue(interp, graphPtr->tkwin, 
+	markerPtr->classPtr->configSpecs, (char *)markerPtr, argv[4], 0) 
+	!= TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+    int flags = TK_CONFIG_ARGV_ONLY;
+    char *oldName;
+    int nNames, nOpts;
+    char **options;
+    register int i;
+    int under;
+
+    /* Figure out where the option value pairs begin */
+    argc -= 3;
+    argv += 3;
+    for (i = 0; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    break;
+	}
+	if (NameToMarker(graphPtr, argv[i], &markerPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    nNames = i;			/* Number of element names specified */
+    nOpts = argc - i;		/* Number of options specified */
+    options = argv + nNames;	/* Start of options in argv  */
+
+    for (i = 0; i < nNames; i++) {
+	NameToMarker(graphPtr, argv[i], &markerPtr);
+	if (nOpts == 0) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
+		markerPtr->classPtr->configSpecs, (char *)markerPtr, 
+		(char *)NULL, flags);
+	} else if (nOpts == 1) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin,
+		markerPtr->classPtr->configSpecs, (char *)markerPtr, 
+		options[0], flags);
+	}
+	/* Save the old marker. */
+	oldName = markerPtr->name;
+	under = markerPtr->drawUnder;
+	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, 
+		markerPtr->classPtr->configSpecs, nOpts, options, 
+		(char *)markerPtr, flags) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (oldName != markerPtr->name) {
+	    if (RenameMarker(graphPtr, markerPtr, oldName, markerPtr->name) 
+		!= TCL_OK) {
+		markerPtr->name = oldName;
+		return TCL_ERROR;
+	    }
+	}
+	if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (markerPtr->drawUnder != under) {
+	    graphPtr->flags |= REDRAW_BACKING_STORE;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ *	This procedure creates and initializes a new marker.
+ *
+ * Results:
+ *	The return value is a pointer to a structure describing
+ *	the new element.  If an error occurred, then the return
+ *	value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ *	Memory is allocated, etc.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+CreateOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+    Blt_HashEntry *hPtr;
+    int isNew;
+    Blt_Uid classUid;
+    register int i;
+    char *name;
+    char string[200];
+    unsigned int length;
+    char c;
+    register Tk_ConfigSpec *specPtr;
+
+    c = argv[3][0];
+    /* Create the new marker based upon the given type */
+    if ((c == 't') && (strcmp(argv[3], "text") == 0)) {
+	classUid = bltTextMarkerUid;
+    } else if ((c == 'l') && (strcmp(argv[3], "line") == 0)) {
+	classUid = bltLineMarkerUid;
+    } else if ((c == 'p') && (strcmp(argv[3], "polygon") == 0)) {
+	classUid = bltPolygonMarkerUid;
+    } else if ((c == 'i') && (strcmp(argv[3], "image") == 0)) {
+	classUid = bltImageMarkerUid;
+    } else if ((c == 'b') && (strcmp(argv[3], "bitmap") == 0)) {
+	classUid = bltBitmapMarkerUid;
+    } else if ((c == 'w') && (strcmp(argv[3], "window") == 0)) {
+	classUid = bltWindowMarkerUid;
+    } else {
+	Tcl_AppendResult(interp, "unknown marker type \"", argv[3],
+    "\": should be \"text\", \"line\", \"polygon\", \"bitmap\", \"image\", or \
+\"window\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Scan for "-name" option. We need it for the component name */
+    name = NULL;
+    for (i = 4; i < argc; i += 2) {
+	length = strlen(argv[i]);
+	if ((length > 1) && (strncmp(argv[i], "-name", length) == 0)) {
+	    name = argv[i + 1];
+	    break;
+	}
+    }
+    /* If no name was given for the marker, make up one. */
+    if (name == NULL) {
+	sprintf(string, "marker%d", graphPtr->nextMarkerId++);
+	name = string;
+    } else if (name[0] == '-') {
+	Tcl_AppendResult(interp, "name of marker \"", name, 
+		"\" can't start with a '-'", (char *)NULL);
+	return TCL_ERROR;
+    }
+    markerPtr = CreateMarker(graphPtr, name, classUid);
+    if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, name, 
+	     markerPtr->classUid, markerPtr->classPtr->configSpecs,
+	    argc - 4, argv + 4, (char *)markerPtr, 0) != TCL_OK) {
+	DestroyMarker(markerPtr);
+	return TCL_ERROR;
+    }
+
+    for (argc -= 4, argv += 4 ; argc > 0; argc -= 2, argv += 2)
+      for (specPtr = markerPtr->classPtr->configSpecs; specPtr->type != TK_CONFIG_END; specPtr++)
+      	if ((Tcl_StringMatch(specPtr->argvName, *argv)))
+          specPtr->specFlags |= TK_CONFIG_OPTION_SPECIFIED;
+
+    if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+	DestroyMarker(markerPtr);
+	return TCL_ERROR;
+    }
+    hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, name, &isNew);
+    if (!isNew) {
+	Marker *oldMarkerPtr;
+	/*
+	 * Marker by the same name already exists.  Delete the old
+	 * marker and it's list entry.  But save the hash entry.
+	 */
+	oldMarkerPtr = (Marker *)Blt_GetHashValue(hPtr);
+	oldMarkerPtr->hashPtr = NULL;
+	DestroyMarker(oldMarkerPtr);
+    }
+    Blt_SetHashValue(hPtr, markerPtr);
+    markerPtr->hashPtr = hPtr;
+    markerPtr->linkPtr = 
+	Blt_ChainAppend(graphPtr->markers.displayList, markerPtr);
+    if (markerPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    Tcl_SetResult(interp, name, TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes the marker given by markerId.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+    register int i;
+
+    for (i = 3; i < argc; i++) {
+	if (NameToMarker(graphPtr, argv[i], &markerPtr) == TCL_OK) {
+	    DestroyMarker(markerPtr);
+	}
+    }
+    Tcl_ResetResult(interp);
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * 	Find the legend entry from the given argument.  The argument
+ *	can be either a screen position "@x,y" or the name of an
+ *	element.
+ *
+ *	I don't know how useful it is to test with the name of an
+ *	element.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new legend attributes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char *argv[];
+{
+    register Marker *markerPtr;
+
+    if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) {
+	markerPtr = (Marker *)Blt_GetCurrentItem(graphPtr->bindTable);
+	/* Report only on markers. */
+	if (markerPtr == NULL) {
+	    return TCL_OK;
+	}
+	if ((markerPtr->classUid == bltBitmapMarkerUid) ||
+	    (markerPtr->classUid == bltLineMarkerUid) ||
+	    (markerPtr->classUid == bltWindowMarkerUid) ||
+	    (markerPtr->classUid == bltPolygonMarkerUid) ||
+	    (markerPtr->classUid == bltTextMarkerUid) ||
+	    (markerPtr->classUid == bltImageMarkerUid)) {
+	    Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RelinkOp --
+ *
+ *	Reorders the marker (given by the first name) before/after
+ *	the another marker (given by the second name) in the
+ *	marker display list.  If no second name is given, the
+ *	marker is placed at the beginning/end of the list.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	Graph will be redrawn to reflect the new display list.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RelinkOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Blt_ChainLink *linkPtr, *placePtr;
+    Marker *markerPtr;
+
+    /* Find the marker to be raised or lowered. */
+    if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Right now it's assumed that all markers are always in the
+       display list. */
+    linkPtr = markerPtr->linkPtr;
+    Blt_ChainUnlinkLink(graphPtr->markers.displayList, markerPtr->linkPtr);
+
+    placePtr = NULL;
+    if (argc == 5) {
+	if (NameToMarker(graphPtr, argv[4], &markerPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	placePtr = markerPtr->linkPtr;
+    }
+
+    /* Link the marker at its new position. */
+    if (argv[2][0] == 'a') {
+	Blt_ChainLinkAfter(graphPtr->markers.displayList, linkPtr, placePtr);
+    } else {
+	Blt_ChainLinkBefore(graphPtr->markers.displayList, linkPtr, placePtr);
+    }
+    if (markerPtr->drawUnder) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ *	Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_ChainLink *linkPtr;
+    Extents2D exts;
+    Marker *markerPtr;
+    int mode;
+    int left, right, top, bottom;
+    int enclosed;
+
+#define FIND_ENCLOSED	 (1<<0)
+#define FIND_OVERLAPPING (1<<1)
+    if (strcmp(argv[3], "enclosed") == 0) {
+	mode = FIND_ENCLOSED;
+    } else if (strcmp(argv[3], "overlapping") == 0) {
+	mode = FIND_OVERLAPPING;
+    } else {
+	Tcl_AppendResult(interp, "bad search type \"", argv[3], 
+		": should be \"enclosed\", or \"overlapping\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+
+    if ((Tcl_GetInt(interp, argv[4], &left) != TCL_OK) ||
+	(Tcl_GetInt(interp, argv[5], &top) != TCL_OK) ||
+	(Tcl_GetInt(interp, argv[6], &right) != TCL_OK) ||
+	(Tcl_GetInt(interp, argv[7], &bottom) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (left < right) {
+	exts.left = (double)left;
+	exts.right = (double)right;
+    } else {
+	exts.left = (double)right;
+	exts.right = (double)left;
+    }
+    if (top < bottom) {
+	exts.top = (double)top;
+	exts.bottom = (double)bottom;
+    } else {
+	exts.top = (double)bottom;
+	exts.bottom = (double)top;
+    }
+    enclosed = (mode == FIND_ENCLOSED);
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+	 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+	if (markerPtr->hidden) {
+	    continue;
+	}
+	if (markerPtr->elemName != NULL) {
+	    Blt_HashEntry *hPtr;
+	    
+	    hPtr = Blt_FindHashEntry(&graphPtr->elements.table, 
+				     markerPtr->elemName);
+	    if (hPtr != NULL) {
+		Element *elemPtr;
+		
+		elemPtr = (Element *)Blt_GetHashValue(hPtr);
+		if (elemPtr->hidden) {
+		    continue;
+		}
+	    }
+	}
+	if ((*markerPtr->classPtr->regionProc)(markerPtr, &exts, enclosed)) {
+	    Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE);
+	    return TCL_OK;
+	}
+    }
+    Tcl_SetResult(interp, "", TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ *	Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&graphPtr->markers.table, argv[3]);
+    Blt_SetBooleanResult(interp, (hPtr != NULL));
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ *	Returns a symbolic name for the type of the marker whose ID is
+ *	given.
+ *
+ * Results:
+ *	A standard Tcl result. interp->result will contain the symbolic
+ *	type of the marker.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Marker *markerPtr;
+
+    if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_SetResult(interp, markerPtr->classUid, TCL_STATIC);
+    return TCL_OK;
+}
+
+/* Public routines */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_MarkerOp --
+ *
+ *	This procedure is invoked to process the Tcl command
+ *	that corresponds to a widget managed by this module.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec markerOps[] =
+{
+    {"after", 1, (Blt_Op)RelinkOp, 4, 5, "marker ?afterMarker?",},
+    {"before", 2, (Blt_Op)RelinkOp, 4, 5, "marker ?beforeMarker?",},
+    {"bind", 2, (Blt_Op)BindOp, 3, 6, "marker sequence command",},
+    {"cget", 2, (Blt_Op)CgetOp, 5, 5, "marker option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+	"marker ?marker?... ?option value?...",},
+    {"create", 2, (Blt_Op)CreateOp, 4, 0,
+	"type ?option value?...",},
+    {"delete", 1, (Blt_Op)DeleteOp, 3, 0, "?marker?...",},
+    {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "marker",},
+    {"find", 1, (Blt_Op)FindOp, 8, 8, "enclosed|overlapping x1 y1 x2 y2",},
+    {"get", 1, (Blt_Op)GetOp, 4, 4, "name",},
+    {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+    {"type", 1, (Blt_Op)TypeOp, 4, 4, "marker",},
+};
+static int nMarkerOps = sizeof(markerOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_MarkerOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nMarkerOps, markerOps, BLT_OP_ARG2, argc, argv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (graphPtr, interp, argc, argv);
+    return result;
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_MarkersToPostScript --
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_MarkersToPostScript(graphPtr, psToken, under)
+    Graph *graphPtr;
+    PsToken psToken;
+    int under;
+{
+    Blt_ChainLink *linkPtr;
+    register Marker *markerPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+	if ((markerPtr->classPtr->postscriptProc == NULL) || 
+	    (markerPtr->nWorldPts == 0)) {
+	    continue;
+	}
+	if (markerPtr->drawUnder != under) {
+	    continue;
+	}
+	if (markerPtr->hidden) {
+	    continue;
+	}
+	if (markerPtr->elemName != NULL) {
+	    Blt_HashEntry *hPtr;
+
+	    hPtr = Blt_FindHashEntry(&graphPtr->elements.table, 
+			     markerPtr->elemName);
+	    if (hPtr != NULL) {
+		Element *elemPtr;
+
+		elemPtr = (Element *)Blt_GetHashValue(hPtr);
+		if (elemPtr->hidden) {
+		    continue;
+		}
+	    }
+	}
+	Blt_AppendToPostScript(psToken, "\n% Marker \"", markerPtr->name,
+	    "\" is a ", markerPtr->classUid, " marker\n", (char *)NULL);
+	(*markerPtr->classPtr->postscriptProc) (markerPtr, psToken);
+    }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_DrawMarkers --
+ *
+ *	Calls the individual drawing routines (based on marker type)
+ *	for each marker in the display list.
+ *
+ *	A marker will not be drawn if
+ *
+ *	1) An element linked to the marker (indicated by elemName) 
+ *	   is currently hidden.
+ *
+ *	2) No coordinates have been specified for the marker.
+ *
+ *	3) The marker is requesting to be drawn at a different level
+ *	   (above/below the elements) from the current mode.
+ *
+ *	4) The marker is configured as hidden (-hide option).
+ *
+ *	5) The marker isn't visible in the current viewport
+ *	   (i.e. clipped).
+ *
+ * Results:
+ *	None
+ *
+ * Side Effects:
+ *	Markers are drawn into the drawable (pixmap) which will eventually
+ *	be displayed in the graph window.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_DrawMarkers(graphPtr, drawable, under)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+    int under;
+{
+    Blt_ChainLink *linkPtr;
+    Marker *markerPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+
+	if ((markerPtr->nWorldPts == 0) || 
+	    (markerPtr->drawUnder != under) ||
+	    (markerPtr->hidden) || 
+	    (markerPtr->clipped)) {
+	    continue;
+	}
+	if (markerPtr->elemName != NULL) {
+	    Blt_HashEntry *hPtr;
+
+	    /* Look up the named element and see if it's hidden */
+	    hPtr = Blt_FindHashEntry(&graphPtr->elements.table, 
+				     markerPtr->elemName);
+	    if (hPtr != NULL) {
+		Element *elemPtr;
+
+		elemPtr = (Element *)Blt_GetHashValue(hPtr);
+		if (elemPtr->hidden) {
+		    continue;
+		}
+	    }
+	}
+
+	(*markerPtr->classPtr->drawProc) (markerPtr, drawable);
+    }
+}
+
+void
+Blt_MapMarkers(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_ChainLink *linkPtr;
+    Marker *markerPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+	if ((markerPtr->nWorldPts == 0) || (markerPtr->hidden)) {
+	    continue;
+	}
+	if ((graphPtr->flags & MAP_ALL) || (markerPtr->flags & MAP_ITEM)) {
+	    (*markerPtr->classPtr->mapProc) (markerPtr);
+	    markerPtr->flags &= ~MAP_ITEM;
+	}
+    }
+}
+
+
+void
+Blt_DestroyMarkers(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Marker *markerPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.table, &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	markerPtr = (Marker *)Blt_GetHashValue(hPtr);
+	/*
+	 * Dereferencing the pointer to the hash table prevents the
+	 * hash table entry from being automatically deleted.
+	 */
+	markerPtr->hashPtr = NULL;
+	DestroyMarker(markerPtr);
+    }
+    Blt_DeleteHashTable(&graphPtr->markers.table);
+    Blt_DeleteHashTable(&graphPtr->markers.tagTable);
+    Blt_ChainDestroy(graphPtr->markers.displayList);
+}
+
+Marker *
+Blt_NearestMarker(graphPtr, x, y, under)
+    Graph *graphPtr;
+    int x, y;			/* Screen coordinates */
+    int under;
+{
+    Blt_ChainLink *linkPtr;
+    Marker *markerPtr;
+    Point2D point;
+
+    point.x = (double)x;
+    point.y = (double)y;
+    for (linkPtr = Blt_ChainLastLink(graphPtr->markers.displayList);
+	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	markerPtr = Blt_ChainGetValue(linkPtr);
+	/* 
+	 * Don't consider markers that are pending to be mapped. Even
+	 * if the marker has already been mapped, the coordinates
+	 * could be invalid now.  Better to pick no marker than the
+	 * wrong marker.
+	 */
+	if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) && 
+	    ((markerPtr->flags & MAP_ITEM) == 0) && 
+	    (!markerPtr->hidden) && (markerPtr->state == STATE_NORMAL)) {
+	    if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
+		return markerPtr;
+	    }
+	}
+    }
+    return NULL;
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrMisc.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrMisc.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrMisc.c	(revision 175)
@@ -0,0 +1,1884 @@
+
+/*
+ * bltGrMisc.c --
+ *
+ *	This module implements miscellaneous routines for the BLT
+ *	graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.  
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+
+static Tk_OptionParseProc StringToPoint;
+static Tk_OptionPrintProc PointToString;
+static Tk_OptionParseProc StringToColorPair;
+static Tk_OptionPrintProc ColorPairToString;
+Tk_CustomOption bltPointOption =
+{
+    StringToPoint, PointToString, (ClientData)0
+};
+Tk_CustomOption bltColorPairOption =
+{
+    StringToColorPair, ColorPairToString, (ClientData)0
+};
+
+/* ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetXY --
+ *
+ *	Converts a string in the form "@x,y" into an XPoint structure
+ *	of the x and y coordinates.
+ *
+ * Results:
+ *	A standard Tcl result. If the string represents a valid position
+ *	*pointPtr* will contain the converted x and y coordinates and
+ *	TCL_OK is returned.  Otherwise,	TCL_ERROR is returned and
+ *	interp->result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetXY(interp, tkwin, string, xPtr, yPtr)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    char *string;
+    int *xPtr, *yPtr;
+{
+    char *comma;
+    int result;
+    int x, y;
+
+    if ((string == NULL) || (*string == '\0')) {
+	*xPtr = *yPtr = -SHRT_MAX;
+	return TCL_OK;
+    }
+    if (*string != '@') {
+	goto badFormat;
+    }
+    comma = strchr(string + 1, ',');
+    if (comma == NULL) {
+	goto badFormat;
+    }
+    *comma = '\0';
+    result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
+	(Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
+    *comma = ',';
+    if (!result) {
+	Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    *xPtr = x, *yPtr = y;
+    return TCL_OK;
+
+  badFormat:
+    Tcl_AppendResult(interp, "bad position \"", string, 
+	     "\": should be \"@x,y\"", (char *)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPoint --
+ *
+ *	Convert the string representation of a legend XY position into
+ *	window coordinates.  The form of the string must be "@x,y" or
+ *	none.
+ *
+ * Results:
+ *	A standard Tcl result.  The symbol type is written into the
+ *	widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPoint(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* New legend position string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset to XPoint structure */
+{
+    XPoint *pointPtr = (XPoint *)(widgRec + offset);
+    int x, y;
+
+    if (Blt_GetXY(interp, tkwin, string, &x, &y) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    pointPtr->x = x, pointPtr->y = y;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PointToString --
+ *
+ *	Convert the window coordinates into a string.
+ *
+ * Results:
+ *	The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PointToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of XPoint in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    char *result;
+    XPoint *pointPtr = (XPoint *)(widgRec + offset);
+
+    result = "";
+    if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
+	char string[200];
+
+	sprintf(string, "@%d,%d", pointPtr->x, pointPtr->y);
+	result = Blt_Strdup(string);
+	assert(result);
+	*freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    }
+    return result;
+}
+
+/*LINTLIBRARY*/
+static int
+GetColorPair(interp, tkwin, fgStr, bgStr, pairPtr, allowDefault)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    char *fgStr, *bgStr;
+    ColorPair *pairPtr;
+    int allowDefault;
+{
+    unsigned int length;
+    XColor *fgColor, *bgColor;
+
+    length = strlen(fgStr);
+    if (fgStr[0] == '\0') {
+	fgColor = NULL;
+    } else if ((allowDefault) && (fgStr[0] == 'd') &&
+	(strncmp(fgStr, "defcolor", length) == 0)) {
+	fgColor = COLOR_DEFAULT;
+    } else {
+	fgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(fgStr));
+	if (fgColor == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    length = strlen(bgStr);
+    if (bgStr[0] == '\0') {
+	bgColor = NULL;
+    } else if ((allowDefault) && (bgStr[0] == 'd') &&
+	(strncmp(bgStr, "defcolor", length) == 0)) {
+	bgColor = COLOR_DEFAULT;
+    } else {
+	bgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(bgStr));
+	if (bgColor == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    pairPtr->fgColor = fgColor;
+    pairPtr->bgColor = bgColor;
+    return TCL_OK;
+}
+
+void
+Blt_FreeColorPair(pairPtr)
+    ColorPair *pairPtr;
+{
+    if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
+	Tk_FreeColor(pairPtr->bgColor);
+    }
+    if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
+	Tk_FreeColor(pairPtr->fgColor);
+    }
+    pairPtr->bgColor = pairPtr->fgColor = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorPair --
+ *
+ *	Convert the color names into pair of XColor pointers.
+ *
+ * Results:
+ *	A standard Tcl result.  The color pointer is written into the
+ *	widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorPair(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing color */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of color field in record */
+{
+    ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+    ColorPair sample;
+    int allowDefault = (int)clientData;
+
+    sample.fgColor = sample.bgColor = NULL;
+    if ((string != NULL) && (*string != '\0')) {
+	int nColors;
+	char **colors;
+	int result;
+
+	if (Tcl_SplitList(interp, string, &nColors, &colors) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	switch (nColors) {
+	case 0:
+	    result = TCL_OK;
+	    break;
+	case 1:
+	    result = GetColorPair(interp, tkwin, colors[0], "", &sample,
+		allowDefault);
+	    break;
+	case 2:
+	    result = GetColorPair(interp, tkwin, colors[0], colors[1],
+		&sample, allowDefault);
+	    break;
+	default:
+	    result = TCL_ERROR;
+	    Tcl_AppendResult(interp, "too many names in colors list",
+		(char *)NULL);
+	}
+	Blt_Free(colors);
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    Blt_FreeColorPair(pairPtr);
+    *pairPtr = sample;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ *	Convert the color option value into a string.
+ *
+ * Results:
+ *	The static string representing the color option is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColor(colorPtr)
+    XColor *colorPtr;
+{
+    if (colorPtr == NULL) {
+	return "";
+    } else if (colorPtr == COLOR_DEFAULT) {
+	return "defcolor";
+    } else {
+	return Tk_NameOfColor(colorPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorPairToString --
+ *
+ *	Convert the color pairs into color names.
+ *
+ * Results:
+ *	The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorPairToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Element information record */
+    int offset;			/* Offset of symbol type field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+    Tcl_DString dString;
+    char *result;
+
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->fgColor));
+    Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->bgColor));
+    result = Tcl_DStringValue(&dString);
+    if (result == dString.staticSpace) {
+	result = Blt_Strdup(result);
+    }
+    *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+    return result;
+}
+
+int
+Blt_PointInSegments(samplePtr, segments, nSegments, halo)
+    Point2D *samplePtr;
+    Segment2D *segments;
+    int nSegments;
+    double halo;
+{
+    register Segment2D *segPtr, *endPtr;
+    double left, right, top, bottom;
+    Point2D p, t;
+    double dist, minDist;
+
+    minDist = DBL_MAX;
+    for (segPtr = segments, endPtr = segments + nSegments; segPtr < endPtr; 
+	 segPtr++) {
+	t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y, 
+		      &segPtr->p, &segPtr->q);
+	if (segPtr->p.x > segPtr->q.x) {
+	    right = segPtr->p.x, left = segPtr->q.x;
+	} else {
+	    right = segPtr->q.x, left = segPtr->p.x;
+	}
+	if (segPtr->p.y > segPtr->q.y) {
+	    bottom = segPtr->p.y, top = segPtr->q.y;
+	} else {
+	    bottom = segPtr->q.y, top = segPtr->p.y;
+	}
+	p.x = BOUND(t.x, left, right);
+	p.y = BOUND(t.y, top, bottom);
+	dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
+	if (dist < minDist) {
+	    minDist = dist;
+	}
+    }
+    return (minDist < halo);
+}
+
+int
+Blt_PointInPolygon(samplePtr, points, nPoints)
+    Point2D *samplePtr;
+    Point2D *points;
+    int nPoints;
+{
+    double b;
+    register Point2D *p, *q, *endPtr;
+    register int count;
+
+    count = 0;
+    for (p = points, q = p + 1, endPtr = p + nPoints; q < endPtr; p++, q++) {
+	if (((p->y <= samplePtr->y) && (samplePtr->y < q->y)) || 
+	    ((q->y <= samplePtr->y) && (samplePtr->y < p->y))) {
+	    b = (q->x - p->x) * (samplePtr->y - p->y) / (q->y - p->y) + p->x;
+	    if (samplePtr->x < b) {
+		count++;	/* Count the number of intersections. */
+	    }
+	}
+    }
+    return (count & 0x01);
+}
+
+int
+Blt_RegionInPolygon(extsPtr, points, nPoints, enclosed)
+    Extents2D *extsPtr;
+    Point2D *points;
+    int nPoints;
+    int enclosed;
+{
+    register Point2D *pointPtr, *endPtr;
+
+    if (enclosed) {
+	/*  
+	 * All points of the polygon must be inside the rectangle.
+	 */
+	for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr; 
+	     pointPtr++) {
+	    if ((pointPtr->x < extsPtr->left) ||
+		(pointPtr->x > extsPtr->right) ||
+		(pointPtr->y < extsPtr->top) ||
+		(pointPtr->y > extsPtr->bottom)) {
+		return FALSE;	/* One point is exterior. */
+	    }
+	}
+	return TRUE;
+    } else {
+	Point2D p, q;
+
+	/*
+	 * If any segment of the polygon clips the bounding region, the
+	 * polygon overlaps the rectangle.
+	 */
+	points[nPoints] = points[0];
+	for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr; 
+	     pointPtr++) {
+	    p = *pointPtr;
+	    q = *(pointPtr + 1);
+	    if (Blt_LineRectClip(extsPtr, &p, &q)) {
+		return TRUE;
+	    }
+	}
+	/* 
+	 * Otherwise the polygon and rectangle are either disjoint
+	 * or enclosed.  Check if one corner of the rectangle is
+	 * inside the polygon.  
+	 */
+	p.x = extsPtr->left;
+	p.y = extsPtr->top;
+	return Blt_PointInPolygon(&p, points, nPoints);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GraphExtents --
+ *
+ *	Generates a bounding box representing the plotting area of
+ *	the graph. This data structure is used to clip the points and
+ *	line segments of the line element.
+ *
+ *	The clip region is the plotting area plus such arbitrary extra
+ *	space.  The reason we clip with a bounding box larger than the
+ *	plot area is so that symbols will be drawn even if their center
+ *	point isn't in the plotting area.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The bounding box is filled with the dimensions of the plotting
+ *	area.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GraphExtents(graphPtr, extsPtr)
+    Graph *graphPtr;
+    Extents2D *extsPtr;
+{
+    extsPtr->left = (double)(graphPtr->hOffset - graphPtr->padX.side1);
+    extsPtr->top = (double)(graphPtr->vOffset - graphPtr->padY.side1);
+    extsPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange + 
+	graphPtr->padX.side2);
+    extsPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange + 
+	graphPtr->padY.side2);
+}
+
+static int 
+ClipTest (double ds, double dr, double *t1, double *t2)
+{
+  double t;
+
+  if (ds < 0.0) {
+      t = dr / ds;
+      if (t > *t2) {
+	  return FALSE;
+      } 
+      if (t > *t1) {
+	  *t1 = t;
+      }
+  } else if (ds > 0.0) {
+      t = dr / ds;
+      if (t < *t1) {
+	  return FALSE;
+      } 
+      if (t < *t2) {
+	  *t2 = t;
+      }
+  } else {
+      /* d = 0, so line is parallel to this clipping edge */
+      if (dr < 0.0) { /* Line is outside clipping edge */
+	  return FALSE;
+      }
+  }
+  return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineRectClip --
+ *
+ *	Clips the given line segment to a rectangular region.  The
+ *	coordinates of the clipped line segment are returned.  The
+ *	original coordinates are overwritten.
+ *
+ *	Reference:  Liang-Barsky Line Clipping Algorithm.
+ *
+ * Results:
+ *	Returns if line segment is visible within the region. The
+ *	coordinates of the original line segment are overwritten
+ *	by the clipped coordinates.
+ *
+ *---------------------------------------------------------------------- 
+ */
+int 
+Blt_LineRectClip(extsPtr, p, q)
+    Extents2D *extsPtr;		/* Rectangular region to clip. */
+    Point2D *p, *q;		/* (in/out) Coordinates of original
+				 * and clipped line segment. */
+{
+    double t1, t2;
+    double dx, dy;
+
+    t1 = 0.0;
+    t2 = 1.0;
+    dx = q->x - p->x;
+    if ((ClipTest (-dx, p->x - extsPtr->left, &t1, &t2)) &&
+	(ClipTest (dx, extsPtr->right - p->x, &t1, &t2))) {
+	dy = q->y - p->y;
+	if ((ClipTest (-dy, p->y - extsPtr->top, &t1, &t2)) && 
+	    (ClipTest (dy, extsPtr->bottom - p->y, &t1, &t2))) {
+	    if (t2 < 1.0) {
+		q->x = p->x + t2 * dx;
+		q->y = p->y + t2 * dy;
+	    }
+	    if (t1 > 0.0) {
+		p->x += t1 * dx;
+		p->y += t1 * dy;
+	    }
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PolyRectClip --
+ *
+ *	Clips the given polygon to a rectangular region.  The resulting
+ *	polygon is returned. Note that the resulting polyon may be 
+ *	complex, connected by zero width/height segments.  The drawing 
+ *	routine (such as XFillPolygon) will not draw a connecting
+ *	segment.
+ *
+ *	Reference:  Liang-Barsky Polygon Clipping Algorithm 
+ *
+ * Results:
+ *	Returns the number of points in the clipped polygon. The
+ *	points of the clipped polygon are stored in *outputPts*.
+ *
+ *---------------------------------------------------------------------- 
+ */
+#define EPSILON  FLT_EPSILON
+#define AddVertex(vx, vy)	    r->x=(vx), r->y=(vy), r++, count++ 
+#define LastVertex(vx, vy)	    r->x=(vx), r->y=(vy), count++ 
+
+int 
+Blt_PolyRectClip(extsPtr, points, nPoints, clipPts)
+    Extents2D *extsPtr;
+    Point2D *points;
+    int nPoints;
+    Point2D *clipPts;
+{
+    Point2D *endPtr;
+    double dx, dy;
+    double tin1, tin2;
+    double tinx, tiny;
+    double xin, yin, xout, yout;
+    int count;
+    register Point2D *p;	/* First vertex of input polygon edge. */
+    register Point2D *q;	/* Last vertex of input polygon edge. */
+    register Point2D *r;
+
+    r = clipPts;
+    count = 0;			/* Counts # of vertices in output polygon. */
+
+    points[nPoints] = points[0];
+
+    for (p = points, q = p + 1, endPtr = p + nPoints; p < endPtr; p++, q++) {
+	dx = q->x - p->x;	/* X-direction */
+	dy = q->y - p->y;	/* Y-direction */
+
+	if (FABS(dx) < EPSILON) { 
+	    dx = (p->x > extsPtr->left) ? -EPSILON : EPSILON ;
+	}
+	if (FABS(dy) < EPSILON) { 
+	    dy = (p->y > extsPtr->top) ? -EPSILON : EPSILON ;
+	}
+
+	if (dx > 0.0) {		/* Left */
+	    xin = extsPtr->left;
+	    xout = extsPtr->right + 1.0;
+	} else {		/* Right */
+	    xin = extsPtr->right + 1.0;
+	    xout = extsPtr->left;
+	}
+	if (dy > 0.0) {		/* Top */
+	    yin = extsPtr->top;
+	    yout = extsPtr->bottom + 1.0;
+	} else {		/* Bottom */
+	    yin = extsPtr->bottom + 1.0;
+	    yout = extsPtr->top;
+	}
+	
+	tinx = (xin - p->x) / dx;
+	tiny = (yin - p->y) / dy;
+	
+	if (tinx < tiny) {	/* Hits x first */
+	    tin1 = tinx;
+	    tin2 = tiny;
+	} else {		/* Hits y first */
+	    tin1 = tiny;
+	    tin2 = tinx;
+	}
+	
+	if (tin1 <= 1.0) {
+	    if (tin1 > 0.0) {
+		AddVertex(xin, yin);
+            }
+	    if (tin2 <= 1.0) {
+		double toutx, touty, tout1;
+
+		toutx = (xout - p->x) / dx;
+		touty = (yout - p->y) / dy;
+		tout1 = MIN(toutx, touty);
+		
+		if ((tin2 > 0.0) || (tout1 > 0.0)) {
+		    if (tin2 <= tout1) {
+			if (tin2 > 0.0) {
+			    if (tinx > tiny) {
+				AddVertex(xin, p->y + tinx * dy);
+			    } else {
+				AddVertex(p->x + tiny * dx, yin);
+			    }
+			}
+			if (tout1 < 1.0) {
+			    if (toutx < touty) {
+				AddVertex(xout, p->y + toutx * dy);
+			    } else {
+				AddVertex(p->x + touty * dx, yout);
+			    }
+			} else {
+			    AddVertex(q->x, q->y);
+			}
+		    } else {
+			if (tinx > tiny) {
+			    AddVertex(xin, yout);
+			} else {
+			    AddVertex(xout, yin);
+			}
+		    }
+		}
+            }
+	}
+    }
+    if (count > 0) {
+	LastVertex(clipPts[0].x, clipPts[0].y);
+    }
+    return count;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetProjection --
+ *
+ *	Computes the projection of a point on a line.  The line (given
+ *	by two points), is assumed the be infinite.
+ *
+ *	Compute the slope (angle) of the line and rotate it 90 degrees.
+ *	Using the slope-intercept method (we know the second line from
+ *	the sample test point and the computed slope), then find the
+ *	intersection of both lines. This will be the projection of the
+ *	sample point on the first line.
+ *
+ * Results:
+ *	Returns the coordinates of the projection on the line.
+ *
+ *----------------------------------------------------------------------
+ */
+Point2D
+Blt_GetProjection(x, y, p, q)
+    int x, y;			/* Screen coordinates of the sample point. */
+    Point2D *p, *q;		/* Line segment to project point onto */
+{
+    double dx, dy;
+    Point2D t;
+
+    dx = p->x - q->x;
+    dy = p->y - q->y;
+
+    /* Test for horizontal and vertical lines */
+    if (FABS(dx) < DBL_EPSILON) {
+	t.x = p->x, t.y = (double)y;
+    } else if (FABS(dy) < DBL_EPSILON) {
+	t.x = (double)x, t.y = p->y;
+    } else {
+	double m1, m2;		/* Slope of both lines */
+	double b1, b2;		/* y-intercepts */
+	double midX, midY;	/* Midpoint of line segment. */
+	double ax, ay, bx, by;
+
+	/* Compute the slop and intercept of the line segment. */
+	m1 = (dy / dx);
+	b1 = p->y - (p->x * m1);
+
+	/* 
+	 * Compute the slope and intercept of a second line segment:
+	 * one that intersects through sample X-Y coordinate with a
+	 * slope perpendicular to original line. 
+	 */
+
+	/* Find midpoint of original segment. */
+	midX = (p->x + q->x) * 0.5;
+	midY = (p->y + q->y) * 0.5;
+
+	/* Rotate the line 90 degrees */
+	ax = midX - (0.5 * dy);
+	ay = midY - (0.5 * -dx);
+	bx = midX + (0.5 * dy);
+	by = midY + (0.5 * -dx);
+
+	m2 = (ay - by) / (ax - bx);
+	b2 = y - (x * m2);
+
+	/*
+	 * Given the equations of two lines which contain the same point,
+	 *
+	 *    y = m1 * x + b1
+	 *    y = m2 * x + b2
+	 *
+	 * solve for the intersection.
+	 *
+	 *    x = (b2 - b1) / (m1 - m2)
+	 *    y = m1 * x + b1
+	 *
+	 */
+
+	t.x = (b2 - b1) / (m1 - m2);
+	t.y = m1 * t.x + b1;
+    }
+    return t;
+}
+
+typedef struct {
+    double hue, sat, val;
+} HSV;
+
+#define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
+			   (c)->green = (int)((g) * 65535.0), \
+			   (c)->blue = (int)((b) * 65535.0))
+
+void
+Blt_XColorToHSV(colorPtr, hsvPtr)
+    XColor *colorPtr;
+    HSV *hsvPtr;
+{
+    unsigned short max, min;
+    double range;
+    unsigned short *colorValues;
+
+    /* Find the minimum and maximum RGB intensities */
+    colorValues = (unsigned short *)&colorPtr->red;
+    max = MAX3(colorValues[0], colorValues[1], colorValues[2]);
+    min = MIN3(colorValues[0], colorValues[1], colorValues[2]);
+
+    hsvPtr->val = (double)max / 65535.0;
+    hsvPtr->hue = hsvPtr->sat = 0.0;
+
+    range = (double)(max - min);
+    if (max != min) {
+	hsvPtr->sat = range / (double)max;
+    }
+    if (hsvPtr->sat > 0.0) {
+	double red, green, blue;
+
+	/* Normalize the RGB values */
+	red = (double)(max - colorPtr->red) / range;
+	green = (double)(max - colorPtr->green) / range;
+	blue = (double)(max - colorPtr->blue) / range;
+
+	if (colorPtr->red == max) {
+	    hsvPtr->hue = (blue - green);
+	} else if (colorPtr->green == max) {
+	    hsvPtr->hue = 2 + (red - blue);
+	} else if (colorPtr->blue == max) {
+	    hsvPtr->hue = 4 + (green - red);
+	}
+	hsvPtr->hue *= 60.0;
+    } else {
+	hsvPtr->sat = 0.5;
+    }
+    if (hsvPtr->hue < 0.0) {
+	hsvPtr->hue += 360.0;
+    }
+}
+
+void
+Blt_HSVToXColor(hsvPtr, colorPtr)
+    HSV *hsvPtr;
+    XColor *colorPtr;
+{
+    double hue, p, q, t;
+    double frac;
+    int quadrant;
+
+    if (hsvPtr->val < 0.0) {
+	hsvPtr->val = 0.0;
+    } else if (hsvPtr->val > 1.0) {
+	hsvPtr->val = 1.0;
+    }
+    if (hsvPtr->sat == 0.0) {
+	SetColor(colorPtr, hsvPtr->val, hsvPtr->val, hsvPtr->val);
+	return;
+    }
+    hue = FMOD(hsvPtr->hue, 360.0) / 60.0;
+    quadrant = (int)floor(hue);
+    frac = hsvPtr->hue - quadrant;
+    p = hsvPtr->val * (1 - (hsvPtr->sat));
+    q = hsvPtr->val * (1 - (hsvPtr->sat * frac));
+    t = hsvPtr->val * (1 - (hsvPtr->sat * (1 - frac)));
+
+    switch (quadrant) {
+    case 0:
+	SetColor(colorPtr, hsvPtr->val, t, p);
+	break;
+    case 1:
+	SetColor(colorPtr, q, hsvPtr->val, p);
+	break;
+    case 2:
+	SetColor(colorPtr, p, hsvPtr->val, t);
+	break;
+    case 3:
+	SetColor(colorPtr, p, q, hsvPtr->val);
+	break;
+    case 4:
+	SetColor(colorPtr, t, p, hsvPtr->val);
+	break;
+    case 5:
+	SetColor(colorPtr, hsvPtr->val, p, q);
+	break;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_AdjustViewport --
+ *
+ *	Adjusts the offsets of the viewport according to the scroll mode.
+ *	This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ *	"canvas"	The viewport scrolls within the range of world
+ *			coordinates.  This way the viewport always displays
+ *			a full page of the world.  If the world is smaller
+ *			than the viewport, then (bizarrely) the world and
+ *			viewport are inverted so that the world moves up
+ *			and down within the viewport.
+ *
+ *	"listbox"	The viewport can scroll beyond the range of world
+ *			coordinates.  Every entry can be displayed at the
+ *			top of the viewport.  This also means that the
+ *			scrollbar thumb weirdly shrinks as the last entry
+ *			is scrolled upward.
+ *
+ * Results:
+ *	The corrected offset is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode)
+    int offset, worldSize, windowSize;
+    int scrollUnits;
+    int scrollMode;
+{
+    switch (scrollMode) {
+    case BLT_SCROLL_MODE_CANVAS:
+
+	/*
+	 * Canvas-style scrolling allows the world to be scrolled
+	 * within the window.
+	 */
+
+	if (worldSize < windowSize) {
+	    if ((worldSize - offset) > windowSize) {
+		offset = worldSize - windowSize;
+	    }
+	    if (offset > 0) {
+		offset = 0;
+	    }
+	} else {
+	    if ((offset + windowSize) > worldSize) {
+		offset = worldSize - windowSize;
+	    }
+	    if (offset < 0) {
+		offset = 0;
+	    }
+	}
+	break;
+
+    case BLT_SCROLL_MODE_LISTBOX:
+	if (offset < 0) {
+	    offset = 0;
+	}
+	if (offset >= worldSize) {
+	    offset = worldSize - scrollUnits;
+	}
+	break;
+
+    case BLT_SCROLL_MODE_HIERBOX:
+
+	/*
+	 * Hierbox-style scrolling allows the world to be scrolled
+	 * within the window.
+	 */
+	if ((offset + windowSize) > worldSize) {
+	    offset = worldSize - windowSize;
+	}
+	if (offset < 0) {
+	    offset = 0;
+	}
+	break;
+    }
+    return offset;
+}
+
+int
+Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize,
+    scrollUnits, scrollMode)
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    int *offsetPtr;
+    int worldSize, windowSize;
+    int scrollUnits;
+    int scrollMode;
+{
+    char c;
+    unsigned int length;
+    int offset;
+    int count;
+    double fract;
+
+    offset = *offsetPtr;
+    c = argv[0][0];
+    length = strlen(argv[0]);
+    if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
+	if (argc != 3) {
+	    return TCL_ERROR;
+	}
+	/* scroll number unit/page */
+	if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	c = argv[2][0];
+	length = strlen(argv[2]);
+	if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
+	    fract = (double)count *scrollUnits;
+	} else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
+	    /* A page is 90% of the view-able window. */
+	    fract = (double)count *windowSize * 0.9;
+	} else {
+	    Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
+		"\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	offset += (int)fract;
+    } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
+	if (argc != 2) {
+	    return TCL_ERROR;
+	}
+	/* moveto fraction */
+	if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	offset = (int)(worldSize * fract);
+    } else {
+	/* Treat like "scroll units" */
+	if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	fract = (double)count *scrollUnits;
+	offset += (int)fract;
+    }
+    *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+	scrollMode);
+    return TCL_OK;
+}
+
+#if (TCL_MAJOR_VERSION >= 8)
+int
+Blt_GetScrollInfoFromObj(interp, objc, objv, offsetPtr, worldSize, windowSize,
+    scrollUnits, scrollMode)
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+    int *offsetPtr;
+    int worldSize, windowSize;
+    int scrollUnits;
+    int scrollMode;
+{
+    char c;
+    unsigned int length;
+    int offset;
+    int count;
+    double fract;
+    char *string;
+
+    offset = *offsetPtr;
+
+    string = Tcl_GetString(objv[0]);
+    c = string[0];
+    length = strlen(string);
+    if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
+	if (objc != 3) {
+	    return TCL_ERROR;
+	}
+	/* scroll number unit/page */
+	if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	string = Tcl_GetString(objv[2]);
+	c = string[0];
+	length = strlen(string);
+	if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
+	    fract = (double)count *scrollUnits;
+	} else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
+	    /* A page is 90% of the view-able window. */
+	    fract = (double)count *windowSize * 0.9;
+	} else {
+	    Tcl_AppendResult(interp, "unknown \"scroll\" units \"", 
+		     Tcl_GetString(objv[2]), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	offset += (int)fract;
+    } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
+	if (objc != 2) {
+	    return TCL_ERROR;
+	}
+	/* moveto fraction */
+	if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	offset = (int)(worldSize * fract);
+    } else {
+	/* Treat like "scroll units" */
+	if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	fract = (double)count *scrollUnits;
+	offset += (int)fract;
+    }
+    *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+	scrollMode);
+    return TCL_OK;
+}
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_UpdateScrollbar --
+ *
+ * 	Invoke a Tcl command to the scrollbar, defining the new
+ *	position and length of the scroll. See the Tk documentation
+ *	for further information on the scrollbar.  It is assumed the
+ *	scrollbar command prefix is valid.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Scrollbar is commanded to change position and/or size.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract)
+    Tcl_Interp *interp;
+    char *scrollCmd;		/* scrollbar command */
+    double firstFract, lastFract;
+{
+    char string[200];
+    Tcl_DString dString;
+
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppend(&dString, scrollCmd, -1);
+    sprintf(string, " %f %f", firstFract, lastFract);
+    Tcl_DStringAppend(&dString, string, -1);
+    if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+	Tcl_BackgroundError(interp);
+    }
+    Tcl_DStringFree(&dString);
+}
+
+/* -------------- */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGCFromDrawable --
+ *
+ *      Like Tk_GetGC, but doesn't share the GC with any other widget.
+ *	This is needed because the certain GC parameters (like dashes)
+ *	can not be set via XCreateGC, therefore there is no way for
+ *	Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ *      A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr)
+    Display *display;
+    Drawable drawable;
+    unsigned long gcMask;
+    XGCValues *valuePtr;
+{
+    GC newGC;
+
+#ifdef WIN32
+    newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
+#else
+    newGC = XCreateGC(display, drawable, gcMask, valuePtr);
+#endif
+    return newGC;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGC --
+ *
+ *      Like Tk_GetGC, but doesn't share the GC with any other widget.
+ *	This is needed because the certain GC parameters (like dashes)
+ *	can not be set via XCreateGC, therefore there is no way for
+ *	Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ *      A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGC(tkwin, gcMask, valuePtr)
+    Tk_Window tkwin;
+    unsigned long gcMask;
+    XGCValues *valuePtr;
+{
+    GC gc;
+    Pixmap pixmap;
+    Drawable drawable;
+    Display *display;
+
+    pixmap = None;
+    drawable = Tk_WindowId(tkwin);
+    display = Tk_Display(tkwin);
+
+    if (drawable == None) {
+	Drawable root;
+	int depth;
+
+	root = RootWindow(display, Tk_ScreenNumber(tkwin));
+	depth = Tk_Depth(tkwin);
+
+	if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
+	    drawable = root;
+	} else {
+	    pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
+	    drawable = pixmap;
+	}
+    }
+    gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
+    if (pixmap != None) {
+	Tk_FreePixmap(display, pixmap);
+    }
+    return gc;
+}
+
+void
+Blt_FreePrivateGC(display, gc)
+    Display *display;
+    GC gc;
+{
+    Tk_FreeXId(display, (XID) XGContextFromGC(gc));
+    XFreeGC(display, gc);
+}
+
+#ifndef WIN32
+void
+Blt_SetDashes(display, gc, dashesPtr)
+    Display *display;
+    GC gc;
+    Blt_Dashes *dashesPtr;
+{
+    XSetDashes(display, gc, dashesPtr->offset, 
+       (CONST char *)dashesPtr->values, strlen((char *)dashesPtr->values));
+}
+#endif
+
+
+static double
+FindSplit(points, i, j, split) 
+    Point2D points[];
+    int i, j;			/* Indices specifying the range of points. */
+    int *split;			/* (out) Index of next split. */
+{    double maxDist;
+    
+    maxDist = -1.0;
+    if ((i + 1) < j) {
+	register int k;
+	double a, b, c;	
+	double sqDist;
+
+	/* 
+	 * 
+	 * sqDist P(k) =  |  1  P(i).x  P(i).y  |
+	 *		  |  1  P(j).x  P(j).y  |
+	 *                |  1  P(k).x  P(k).y  |
+	 *              ---------------------------
+ 	 *       (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
+	 */
+
+	a = points[i].y - points[j].y;
+	b = points[j].x - points[i].x;
+	c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
+	for (k = (i + 1); k < j; k++) {
+	    sqDist = (points[k].x * a) + (points[k].y * b) + c;
+	    if (sqDist < 0.0) {
+		sqDist = -sqDist;	
+	    }
+	    if (sqDist > maxDist) {
+		maxDist = sqDist;	/* Track the maximum. */
+		*split = k;
+	    }
+	}
+	/* Correction for segment length---should be redone if can == 0 */
+	maxDist *= maxDist / (a * a + b * b);
+    } 
+    return maxDist;
+}
+
+
+/* Douglas-Peucker line simplification algorithm */
+int
+Blt_SimplifyLine(inputPts, low, high, tolerance, indices) 
+   Point2D inputPts[];
+   int low, high;
+   double tolerance;
+   int indices[];
+{
+#define StackPush(a)	s++, stack[s] = (a)
+#define StackPop(a)	(a) = stack[s], s--
+#define StackEmpty()	(s < 0)
+#define StackTop()	stack[s]
+    int *stack;
+    int split = -1; 
+    double sqDist, sqTolerance;
+    int s = -1;			/* Points to top stack item. */
+    int count;
+
+    stack = Blt_Malloc(sizeof(int) * (high - low + 1));
+    StackPush(high);
+    count = 0;
+    indices[count++] = 0;
+    sqTolerance = tolerance * tolerance;
+    while (!StackEmpty()) {
+	sqDist = FindSplit(inputPts, low, StackTop(), &split);
+	if (sqDist > sqTolerance) {
+	    StackPush(split);
+	} else {
+	    indices[count++] = StackTop();
+	    StackPop(low);
+	}
+    } 
+    Blt_Free(stack);
+    return count;
+}
+
+void
+Blt_Draw2DSegments(display, drawable, gc, segPtr, nSegments)
+    Display *display;
+    Drawable drawable;
+    GC gc;
+    register Segment2D *segPtr;
+    int nSegments;
+{
+    XSegment *xSegPtr, *xSegArr;
+    Segment2D *endPtr;
+
+    xSegArr = Blt_Malloc(nSegments * sizeof(XSegment));
+    if (xSegArr == NULL) {
+	return;
+    }
+    xSegPtr = xSegArr;
+    for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
+	xSegPtr->x1 = (short int)segPtr->p.x;
+	xSegPtr->y1 = (short int)segPtr->p.y;
+	xSegPtr->x2 = (short int)segPtr->q.x;
+	xSegPtr->y2 = (short int)segPtr->q.y;
+	xSegPtr++;
+    }
+    XDrawSegments(display, drawable, gc, xSegArr, nSegments);
+    Blt_Free(xSegArr);
+}
+
+void
+Blt_DrawArrow(display, drawable, gc, x, y, arrowHeight, orientation)
+    Display *display;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    int arrowHeight;
+    int orientation;
+{
+    XPoint arrow[5];
+    int a, b;
+    
+    a = arrowHeight / 2 + 1;
+    b = arrowHeight;
+    switch (orientation) {
+    case ARROW_UP:
+	/*
+	 *            0
+	 *            +
+	 *           / \
+	 *          /   \
+	 *         /     \  a
+	 *        /       \
+	 *   x,y /         \
+	 *      +-----------+
+	 *     1      b      2
+	 */
+	arrow[0].x = x;
+	arrow[0].y = y - a;
+	arrow[1].x = arrow[0].x - b;
+	arrow[1].y = arrow[0].y + b;
+	arrow[2].x = arrow[0].x + b;
+	arrow[2].y = arrow[0].y + b;
+	arrow[3].x = arrow[0].x;
+	arrow[3].y = arrow[0].y;
+	break;
+
+    case ARROW_DOWN:
+	/*
+	 *     1      b      2
+	 *      +-----------+
+	 *       \         /
+	 *        \  x,y  /
+	 *         \     /  a
+	 *          \   /
+	 *           \ /
+	 *            +
+	 *            0
+	 */
+	arrow[0].x = x;
+	arrow[0].y = y + a;
+	arrow[1].x = arrow[0].x - b;
+	arrow[1].y = arrow[0].y - b;
+	arrow[2].x = arrow[0].x + b;
+	arrow[2].y = arrow[0].y - b;
+	arrow[3].x = arrow[0].x;
+	arrow[3].y = arrow[0].y;
+	break;
+
+    case ARROW_RIGHT:
+	/*
+	 *       2
+	 *	 +
+	 *       |\
+	 *       | \
+	 *       |  \ 
+	 *       |   \
+	 *       |    \
+	 *       | x,y + 0
+	 *       |    /
+	 *	 |   /
+	 *       |  /
+	 *       | /
+	 *       |/
+	 *       +
+	 *       1
+	 */
+	arrow[0].x = x + a;
+	arrow[0].y = y;
+	arrow[1].x = arrow[0].x - b;
+	arrow[1].y = arrow[0].y + b;
+	arrow[2].x = arrow[0].x - b;
+	arrow[2].y = arrow[0].y - b;
+	arrow[3].x = arrow[0].x;
+	arrow[3].y = arrow[0].y;
+	break;
+
+    case ARROW_LEFT:
+	/*
+	 *              2
+	 *	       	+
+	 *             /|
+	 *            /	|
+	 *           /	|
+	 *          /	|
+	 *         /  	|
+	 *       0+ x,y |
+	 *         \  	|
+	 *	    \	|
+	 *           \	|
+	 *            \ |
+	 *             \|
+	 *       	+
+	 *             	1
+	 */
+	arrow[0].x = x - a;
+	arrow[0].y = y;
+	arrow[1].x = arrow[0].x + b;
+	arrow[1].y = arrow[0].y + b;
+	arrow[2].x = arrow[0].x + b;
+	arrow[2].y = arrow[0].y - b;
+	arrow[3].x = arrow[0].x;
+	arrow[3].y = arrow[0].y;
+	break;
+
+    }
+    XFillPolygon(display, drawable, gc, arrow, 4, Convex, CoordModeOrigin);
+    XDrawLines(display, drawable, gc, arrow, 4, CoordModeOrigin);
+}
+
+int 
+Blt_MaxRequestSize(Display *display, unsigned int elemSize) 
+{
+    long size;
+
+#ifdef HAVE_XEXTENDEDMAXREQUESTSIZE
+    size = XExtendedMaxRequestSize(display);
+    if (size == 0) {
+	size = XMaxRequestSize(display);
+    }
+#else
+    size = XMaxRequestSize(display);
+#endif
+    size -= 4;
+    return ((size * 4) / elemSize);
+}
+
+#undef Blt_Fill3DRectangle
+void
+Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width,
+	height, borderWidth, relief)
+    Tk_Window tkwin;		/* Window for which border was allocated. */
+    Drawable drawable;		/* X window or pixmap in which to draw. */
+    Tk_3DBorder border;		/* Token for border to draw. */
+    int x, y, width, height;	/* Outside area of rectangular region. */
+    int borderWidth;		/* Desired width for border, in
+				 * pixels. Border will be *inside* region. */
+    int relief;			/* Indicates 3D effect: TK_RELIEF_FLAT,
+				 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+#ifndef notdef
+    if ((borderWidth > 1) && (width > 2) && (height > 2) &&
+	((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
+	GC lightGC, darkGC;
+	int x2, y2;
+
+	x2 = x + width - 1;
+	y2 = y + height - 1;
+#define TK_3D_LIGHT2_GC TK_3D_DARK_GC+1
+#define TK_3D_DARK2_GC TK_3D_DARK_GC+2
+	if (relief == TK_RELIEF_RAISED) {
+	    lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+#ifdef WIN32
+	    darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+#else
+	    darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+	} else {
+#ifdef WIN32
+	    lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+#else
+	    lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+	    darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+	}
+	XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
+	XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
+	XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
+	XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
+	x++, y++, width -= 2, height -= 2, borderWidth--;
+    }
+#endif
+    Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width, height, 
+	borderWidth, relief);
+}
+
+
+#undef Blt_Draw3DRectangle
+void
+Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
+	height, borderWidth, relief)
+    Tk_Window tkwin;		/* Window for which border was allocated. */
+    Drawable drawable;		/* X window or pixmap in which to draw. */
+    Tk_3DBorder border;		/* Token for border to draw. */
+    int x, y, width, height;	/* Outside area of rectangular region. */
+    int borderWidth;		/* Desired width for border, in
+				 * pixels. Border will be *inside* region. */
+    int relief;			/* Indicates 3D effect: TK_RELIEF_FLAT,
+				 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+#ifndef notdef
+    if ((borderWidth > 1) && (width > 2) && (height > 2) &&
+	((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
+	GC lightGC, darkGC;
+	int x2, y2;
+
+	x2 = x + width - 1;
+	y2 = y + height - 1;
+	if (relief == TK_RELIEF_RAISED) {
+	    lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+#ifdef WIN32
+	    darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+#else
+	    darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+	} else {
+#ifdef WIN32
+	    lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+#else
+	    lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+	    darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+	}
+	XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
+	XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
+	XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
+	XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
+	x++, y++, width -= 2, height -= 2, borderWidth--;
+    }
+#endif
+    Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height, 
+	borderWidth, relief);
+}
+
+#ifdef notdef
+typedef struct {
+    Screen *screen;
+    Visual *visual;
+    Colormap colormap;
+    Tk_Uid nameUid;
+} BorderKey;
+
+typedef struct {
+    Screen *screen;		/* Screen on which the border will be used. */
+    Visual *visual;		/* Visual for all windows and pixmaps using
+				 * the border. */
+    int depth;			/* Number of bits per pixel of drawables where
+				 * the border will be used. */
+    Colormap colormap;		/* Colormap out of which pixels are
+				 * allocated. */
+    int refCount;		/* Number of active uses of this color
+				 * (each active use corresponds to a
+				 * call to Blt_Get3DBorder).  If this
+				 * count is 0, then this structure is
+				 * no longer valid and it isn't
+				 * present in borderTable: it is being
+				 * kept around only because there are
+				 * objects referring to it.  The
+				 * structure is freed when refCount is
+				 * 0. */
+
+    XColor *bgColorPtr;		/* Color of face. */
+    XColor *shadows[4];
+
+    Pixmap darkStipple;		/* Stipple pattern to use for drawing
+				 * shadows areas.  Used for displays with
+				 * <= 64 colors or where colormap has filled
+				 * up. */
+    Pixmap lightStipple;	/* Stipple pattern to use for drawing
+				 * shadows areas.  Used for displays with
+				 * <= 64 colors or where colormap has filled
+				 * up. */
+    GC bgGC;			/* Used (if necessary) to draw areas in
+				 * the background color. */
+    GC lightGC, darkGC;
+    Tcl_HashEntry *hashPtr;	/* Entry in borderTable (needed in
+				 * order to delete structure). */
+
+    struct Blt_3DBorderStruct *nextPtr;
+} Border, *Blt_3DBorder;
+    
+
+void
+Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
+	height, borderWidth, relief)
+    Tk_Window tkwin;		/* Window for which border was allocated. */
+    Drawable drawable;		/* X window or pixmap in which to draw. */
+    Blt_3DBorder *borderPtr;	/* Border to draw. */
+    int x, y, width, height;	/* Outside area of rectangular region. */
+    int borderWidth;		/* Desired width for border, in
+				 * pixels. Border will be *inside* region. */
+    int relief;			/* Indicates 3D effect: TK_RELIEF_FLAT,
+				 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+    if ((width > (2 * borderWidth)) && (height > (2 * borderWidth))) {
+	int x2, y2;
+	int i;
+
+	x2 = x + width - 1;
+	y2 = y + height - 1;
+
+	XSetForeground(borderPtr->lightGC, borderPtr->shadows[0]);
+	XSetForeground(borderPtr->darkGC, borderPtr->shadows[3]);
+	XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC, 
+		  x, y, x2, y);
+	XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC, 
+		  x, y, x, y2);
+	XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC, 
+		  x2, y, x2, y2);
+	XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC, 
+		  x2, y2, x, y2);
+	XSetForeground(borderPtr->lightGC, borderPtr->shadows[1]);
+	XSetForeground(borderPtr->darkGC, borderPtr->shadows[2]);
+	for (i = 1; i < (borderWidth - 1); i++) {
+
+	    /*
+	     *  +---------
+	     *  |+-------
+	     *  ||+-----
+	     *  |||
+	     *  |||
+	     *  ||
+	     *  |
+	     */
+	    x++, y++, x2--, y2--;
+	    XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC, 
+		x, y, x2, y);
+	    XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC, 
+		x, y, x, y2);
+	    XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC, 
+		x2, y, x2, y2);
+	    XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC, 
+		x2, y2, x, y2);
+	}
+    }
+}
+
+void
+Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width, height, borderWidth, 
+	relief)
+    Tk_Window tkwin;		/* Window for which border was allocated. */
+    Drawable drawable;		/* X window or pixmap in which to draw. */
+    Tk_3DBorder border;		/* Token for border to draw. */
+    int x, y, width, height;	/* Outside area of rectangular region. */
+    int borderWidth;		/* Desired width for border, in
+				 * pixels. Border will be *inside* region. */
+    int relief;			/* Indicates 3D effect: TK_RELIEF_FLAT,
+				 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+    Blt_3DBorder *borderPtr;
+
+    XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC, x, y, width,
+	   height);
+    if ((borderWidth > 0) && (relief != BLT_RELIEF_FLAT)) {
+	Blt_Draw3DRectangle(tkwin, drawable, borderPtr, x, y, width, height, 
+	    borderWidth, relief);
+    }
+}
+
+
+void 
+FreeBorder(display, borderPtr)
+    Display *display;
+    Border *borderPtr;
+{
+    int i;
+
+    if (borderPtr->bgColorPtr != NULL) {
+	Tk_FreeColor(display, borderPtr->bgColorPtr);
+    }
+    for (i = 0; i < 4; i++) {
+	Tk_FreeColor(display, borderPtr->shadows[i]);
+    }
+    if (borderPtr->tile != NULL) {
+	Blt_FreeTile(tile);
+    }
+    if (borderPtr->darkGC != NULL) {
+	Blt_FreePrivateGC(display, borderPtr->darkGC);
+    }
+    if (borderPtr->lightGC != NULL) {
+	Blt_FreePrivateGC(tkwin, borderPtr->lightGC);
+    }
+    if (borderPtr->bgGC != NULL) {
+	Blt_FreePrivateGC(tkwin, borderPtr->bgGC);
+    }
+    Blt_Free(borderPtr);
+}
+
+void
+Blt_Free3DBorder(display, border)
+    Display *display;
+    Blt_3DBorder border;
+{
+    Border *borderPtr = (Border *)border;
+
+    borderPtr->refCount--;
+    if (borderPtr->refCount >= 0) {
+	/* Search for the border in the bucket. Start at the head. */
+	headPtr = Blt_GetHashValue(borderPtr->hashPtr);
+	lastPtr = NULL;
+	while ((headPtr != borderPtr) && (headPtr != NULL)) {
+	    lastPtr = headPtr;
+	    headPtr = headPtr->next;
+	}
+	if (headPtr == NULL) {
+	    return;		/* This can't happen. It means that 
+				 * we could not find the border. */
+	}
+	if (lastPtr != NULL) {
+	    lastPtr->next = borderPtr->next;
+	} else {
+	    Tcl_DeleteHashEntry(borderPtr->hashPtr);
+	}
+	FreeBorder(display, borderPtr);
+    }
+}
+
+Blt_3DBorder *
+Blt_Get3DBorder(interp, tkwin, borderName)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    char *borderName;
+{
+    Blt_3DBorder *borderPtr, *lastBorderPtr;
+    Blt_HashEntry *hPtr;
+    Blt_Tile tile;
+    XColor *bgColorPtr;
+    char **argv;
+    char *colorName;
+    int argc;
+    int isNew;
+
+    lastBorderPtr = NULL;
+    hPtr = Tcl_CreateHashEntry(&dataPtr->borderTable, borderName, &isNew);
+    if (!isNew) {
+	borderPtr = lastBorderPtr = Blt_GetHashValue(hPtr);
+	while (borderPtr != NULL) {
+	    if ((Tk_Screen(tkwin) == borderPtr->screen) && 
+		(Tk_Colormap(tkwin) == borderPtr->colormap)) {
+		borderPtr->refCount++;
+		return borderPtr;
+	    }
+	    borderPtr = borderPtr->nextPtr;
+	}
+    }
+    /* Create a new border. */
+    argv = NULL;
+    bgColorPtr = NULL;
+    tile = NULL;
+
+    if (Tcl_SplitList(interp, borderName, &argc, &argv) != TCL_OK) {
+	goto error;
+    }
+    colorName = borderName;
+    if ((argc == 2) && (Blt_GetTile(interp, tkwin, argv[0], &tile) == TCL_OK)) {
+	colorName = argv[1];
+    }
+    bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
+    if (bgColorPtr == NULL) {
+	goto error;
+    }
+
+    /* Create a new border */
+    borderPtr = Blt_Calloc(1, sizeof(Blt_3DBorder));
+    assert(borderPtr);
+    borderPtr->screen = Tk_Screen(tkwin);
+    borderPtr->visual = Tk_Visual(tkwin);
+    borderPtr->depth = Tk_Depth(tkwin);
+    borderPtr->colormap = Tk_Colormap(tkwin);
+    borderPtr->refCount = 1;
+    borderPtr->bgColorPtr = bgColorPtr;
+    borderPtr->tile = tile;
+    borderPtr->darkGC = Blt_GetPrivateGC(tkwin, 0, NULL);
+    borderPtr->lightGC = Blt_GetPrivateGC(tkwin, 0, NULL);
+    borderPtr->hashPtr = lastBorderPtr->hashPtr;
+    lastBorderPtr->nextPtr = lastBorderPtr;
+    {
+	HSV hsv;
+	XColor color;
+	double sat, sat0, diff, step, hstep;
+	int count;
+	
+	/* Convert the face (background) color to HSV */
+	Blt_XColorToHSV(borderPtr->bgColorPtr, &hsv);
+	
+	/* Using the color as the baseline intensity, pick a set of
+	 * colors around the intensity. */
+#define UFLOOR(x,u)		(floor((x)*(u))/(u))
+	diff = hsv.sat - UFLOOR(hsv.sat, 0.2);
+	sat = 0.1 + (diff - 0.1);
+	sat0 = hsv.sat;
+	count = 0;
+	for (sat = 0.1 + (diff - 0.1); sat <= 1.0; sat += 0.2) {
+	    if (FABS(sat0 - sat) >= 0.1) {
+		hsv.sat = sat;
+		Blt_HSVToXColor(&hsv, &color);
+		borderPtr->shadows[count] = Tk_GetColorByValue(tkwin, &color);
+		count++;
+	    }
+	}
+    }
+    Blt_SetHashValue(hPtr, borderPtr);
+    if (argv != NULL) {
+	Blt_Free(argv);
+    }
+    return TCL_OK;
+
+ error:
+    if (argv != NULL) {
+	Blt_Free(argv);
+    }
+    if (tile != NULL) {
+	Blt_FreeTile(tile);
+    }
+    if (bgColorPtr != NULL) {
+	Tk_FreeColor(bgColorPtr);
+    }
+    if (isNew) {
+	Blt_DeleteHashEntry(&borderTable, hPtr);
+    }
+    return NULL;
+}
+
+#endif
Index: trunk/kitgen/8.x/blt/generic/bltGrPen.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrPen.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrPen.c	(revision 175)
@@ -0,0 +1,707 @@
+
+/*
+ * bltGrPen.c --
+ *
+ *	This module implements pens for the BLT graph widget.
+ *
+ * Copyright 1996-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+static Tk_OptionParseProc StringToColor;
+static Tk_OptionPrintProc ColorToString;
+static Tk_OptionParseProc StringToPen;
+static Tk_OptionPrintProc PenToString;
+Tk_CustomOption bltColorOption =
+{
+    StringToColor, ColorToString, (ClientData)0
+};
+Tk_CustomOption bltPenOption =
+{
+    StringToPen, PenToString, (ClientData)0
+};
+Tk_CustomOption bltBarPenOption =
+{
+    StringToPen, PenToString, (ClientData)&bltBarElementUid
+};
+Tk_CustomOption bltLinePenOption =
+{
+    StringToPen, PenToString, (ClientData)&bltLineElementUid
+};
+
+/*
+ *----------------------------------------------------------------------
+
+ * StringToColor --
+ *
+ *	Convert the string representation of a color into a XColor pointer.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The color pointer is
+ *	written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColor(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing color */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of color field in record */
+{
+    XColor **colorPtrPtr = (XColor **)(widgRec + offset);
+    XColor *colorPtr;
+    unsigned int length;
+    char c;
+
+    if ((string == NULL) || (*string == '\0')) {
+	*colorPtrPtr = NULL;
+	return TCL_OK;
+    }
+    c = string[0];
+    length = strlen(string);
+
+    if ((c == 'd') && (strncmp(string, "defcolor", length) == 0)) {
+	colorPtr = COLOR_DEFAULT;
+    } else {
+	colorPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(string));
+	if (colorPtr == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    *colorPtrPtr = colorPtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ *	Convert the color option value into a string.
+ *
+ * Results:
+ *	The static string representing the color option is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColor(colorPtr)
+    XColor *colorPtr;
+{
+    if (colorPtr == NULL) {
+	return "";
+    } else if (colorPtr == COLOR_DEFAULT) {
+	return "defcolor";
+    } else {
+	return Tk_NameOfColor(colorPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorToString --
+ *
+ *	Convert the color value into a string.
+ *
+ * Results:
+ *	The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget information record */
+    int offset;			/* Offset of symbol type in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    XColor *colorPtr = *(XColor **)(widgRec + offset);
+
+    return NameOfColor(colorPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPen --
+ *
+ *	Convert the color value into a string.
+ *
+ * Results:
+ *	The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPen(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representing pen */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of pen field in record */
+{
+    Blt_Uid classUid = *(Blt_Uid *)clientData; /* Element type. */
+    Pen **penPtrPtr = (Pen **)(widgRec + offset);
+    Pen *penPtr;
+    Graph *graphPtr;
+
+    penPtr = NULL;
+    graphPtr = Blt_GetGraphFromWindowData(tkwin);
+
+    if (classUid == NULL) {	
+	classUid = graphPtr->classUid;
+    }
+    if ((string != NULL) && (string[0] != '\0')) {
+	if (Blt_GetPen(graphPtr, string, classUid, &penPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    /* Release the old pen */
+    if (*penPtrPtr != NULL) {
+	Blt_FreePen(graphPtr, *penPtrPtr);
+    }
+    *penPtrPtr = penPtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PenToString --
+ *
+ *	Parse the name of the name.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PenToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget information record */
+    int offset;			/* Offset of pen in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    Pen *penPtr = *(Pen **)(widgRec + offset);
+
+    return penPtr->name;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameToPen --
+ *
+ *	Find and return the pen style from a given name.
+ *
+ * Results:
+ *     	A standard TCL result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pen *
+NameToPen(graphPtr, name)
+    Graph *graphPtr;
+    char *name;
+{
+    Blt_HashEntry *hPtr;
+    Pen *penPtr;
+
+    hPtr = Blt_FindHashEntry(&(graphPtr->penTable), name);
+    if (hPtr == NULL) {
+      notFound:
+	Tcl_AppendResult(graphPtr->interp, "can't find pen \"", name,
+	    "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+	return NULL;
+    }
+    penPtr = (Pen *)Blt_GetHashValue(hPtr);
+    if (penPtr->flags & PEN_DELETE_PENDING) {
+	goto notFound;
+    }
+    return penPtr;
+}
+
+static void
+DestroyPen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    Tk_FreeOptions(penPtr->configSpecs, (char *)penPtr, graphPtr->display, 0);
+    (*penPtr->destroyProc) (graphPtr, penPtr);
+    if ((penPtr->name != NULL) && (penPtr->name[0] != '\0')) {
+	Blt_Free(penPtr->name);
+    }
+    if (penPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&(graphPtr->penTable), penPtr->hashPtr);
+    }
+    Blt_Free(penPtr);
+}
+
+void
+Blt_FreePen(graphPtr, penPtr)
+    Graph *graphPtr;
+    Pen *penPtr;
+{
+    penPtr->refCount--;
+    if ((penPtr->refCount == 0) && (penPtr->flags & PEN_DELETE_PENDING)) {
+	DestroyPen(graphPtr, penPtr);
+    }
+}
+
+Pen *
+Blt_CreatePen(graphPtr, penName, classUid, nOpts, options)
+    Graph *graphPtr;
+    char *penName;
+    Blt_Uid classUid;
+    int nOpts;
+    char **options;
+{
+    
+    Pen *penPtr;
+    Blt_HashEntry *hPtr;
+    unsigned int length, configFlags;
+    int isNew;
+    register int i;
+
+    /*
+     * Scan the option list for a "-type" entry.  This will indicate
+     * what type of pen we are creating. Otherwise we'll default to the
+     * suggested type.  Last -type option wins.
+     */
+    for (i = 0; i < nOpts; i += 2) {
+	length = strlen(options[i]);
+	if ((length > 2) && (strncmp(options[i], "-type", length) == 0)) {
+	    char *arg;
+
+	    arg = options[i + 1];
+	    if (strcmp(arg, "bar") == 0) {
+		classUid = bltBarElementUid;
+	    } else if (strcmp(arg, "line") != 0) {
+		classUid = bltLineElementUid;
+	    } else if (strcmp(arg, "strip") != 0) {
+		classUid = bltLineElementUid;
+	    } else {
+		Tcl_AppendResult(graphPtr->interp, "unknown pen type \"",
+		    arg, "\" specified", (char *)NULL);
+		return NULL;
+	    }
+	}
+    }
+    if (classUid == bltStripElementUid) {
+	classUid = bltLineElementUid;
+    }
+    hPtr = Blt_CreateHashEntry(&(graphPtr->penTable), penName, &isNew);
+    if (!isNew) {
+	penPtr = (Pen *)Blt_GetHashValue(hPtr);
+	if (!(penPtr->flags & PEN_DELETE_PENDING)) {
+	    Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+		"\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+		(char *)NULL);
+	    return NULL;
+	}
+	if (penPtr->classUid != classUid) {
+	    Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+		"\" in-use: can't change pen type from \"", penPtr->classUid, 
+		"\" to \"", classUid, "\"", (char *)NULL);
+	    return NULL;
+	}
+	penPtr->flags &= ~PEN_DELETE_PENDING;
+    } else {
+	if (classUid == bltBarElementUid) {
+	    penPtr = Blt_BarPen(penName);
+	} else {
+	    penPtr = Blt_LinePen(penName);
+	}
+	penPtr->classUid = classUid;
+	penPtr->hashPtr = hPtr;
+	Blt_SetHashValue(hPtr, penPtr);
+    }
+
+    configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+	    penPtr->name, "Pen", penPtr->configSpecs, nOpts, options,
+	    (char *)penPtr, configFlags) != TCL_OK) {
+	if (isNew) {
+	    DestroyPen(graphPtr, penPtr);
+	}
+	return NULL;
+    }
+    (*penPtr->configProc) (graphPtr, penPtr);
+    return penPtr;
+}
+
+int
+Blt_GetPen(graphPtr, name, classUid, penPtrPtr)
+    Graph *graphPtr;
+    char *name;
+    Blt_Uid classUid;
+    Pen **penPtrPtr;
+{
+    Pen *penPtr;
+
+    penPtr = NameToPen(graphPtr, name);
+    if (penPtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (classUid == bltStripElementUid) {
+	classUid = bltLineElementUid;
+    }
+    if (penPtr->classUid != classUid) {
+	Tcl_AppendResult(graphPtr->interp, "pen \"", name, 
+		"\" is the wrong type (is \"", penPtr->classUid, "\"", 
+		", wanted \"", classUid, "\")", (char *)NULL);
+	return TCL_ERROR;
+    }
+    penPtr->refCount++;
+    *penPtrPtr = penPtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DestroyPens --
+ *
+ *	Release memory and resources allocated for the style.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the pen style is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DestroyPens(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Pen *penPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&(graphPtr->penTable), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	penPtr = (Pen *)Blt_GetHashValue(hPtr);
+	penPtr->hashPtr = NULL;
+	DestroyPen(graphPtr, penPtr);
+    }
+    Blt_DeleteHashTable(&(graphPtr->penTable));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *	Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ *	A standard Tcl result.  If querying configuration values,
+ *	interp->result will contain the results.
+ *
+ * ----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;			/* Not used. */
+    char *argv[];
+{
+    Pen *penPtr;
+    unsigned int configFlags;
+
+    penPtr = NameToPen(graphPtr, argv[3]);
+    if (penPtr == NULL) {
+	return TCL_ERROR;
+    }
+    configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+    return Tk_ConfigureValue(interp, graphPtr->tkwin, penPtr->configSpecs,
+	(char *)penPtr, argv[4], configFlags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Queries or resets pen attributes (font, line width, color, etc).
+ *
+ * Results:
+ *	A standard Tcl result.  If querying configuration values,
+ *	interp->result will contain the results.
+ *
+ * Side Effects:
+ *	Pen resources are possibly allocated (GC, font).
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;
+    char *argv[];
+{
+    int flags;
+    Pen *penPtr;
+    int nNames, nOpts;
+    int redraw;
+    char **options;
+    register int i;
+
+    /* Figure out where the option value pairs begin */
+    argc -= 3;
+    argv += 3;
+    for (i = 0; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    break;
+	}
+	if (NameToPen(graphPtr, argv[i]) == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    nNames = i;			/* Number of pen names specified */
+    nOpts = argc - i;		/* Number of options specified */
+    options = argv + i;		/* Start of options in argv  */
+
+    redraw = 0;
+    for (i = 0; i < nNames; i++) {
+	penPtr = NameToPen(graphPtr, argv[i]);
+	flags = TK_CONFIG_ARGV_ONLY | (penPtr->flags & (ACTIVE_PEN|NORMAL_PEN));
+	if (nOpts == 0) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
+		    penPtr->configSpecs, (char *)penPtr, (char *)NULL, flags);
+	} else if (nOpts == 1) {
+	    return Tk_ConfigureInfo(interp, graphPtr->tkwin, 
+		    penPtr->configSpecs, (char *)penPtr, options[0], flags);
+	}
+	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, penPtr->configSpecs,
+		nOpts, options, (char *)penPtr, flags) != TCL_OK) {
+	    break;
+	}
+	(*penPtr->configProc) (graphPtr, penPtr);
+	if (penPtr->refCount > 0) {
+	    redraw++;
+	}
+    }
+    if (redraw) {
+	graphPtr->flags |= REDRAW_BACKING_STORE | DRAW_MARGINS;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+    if (i < nNames) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ *	Adds a new penstyle to the graph.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CreateOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Pen *penPtr;
+
+    penPtr = Blt_CreatePen(graphPtr, argv[3], graphPtr->classUid, argc - 4, 
+	argv + 4);
+    if (penPtr == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_SetResult(interp, penPtr->name, TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Delete the given pen.
+ *
+ * Results:
+ *	Always returns TCL_OK.  The interp->result field is
+ *	a list of the graph axis limits.
+ *
+ *--------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+DeleteOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Pen *penPtr;
+    int i;
+
+    for (i = 3; i < argc; i++) {
+	penPtr = NameToPen(graphPtr, argv[i]);
+	if (penPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (penPtr->flags & PEN_DELETE_PENDING) {
+	    Tcl_AppendResult(graphPtr->interp, "can't find pen \"", argv[i],
+		"\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	penPtr->flags |= PEN_DELETE_PENDING;
+	if (penPtr->refCount == 0) {
+	    DestroyPen(graphPtr, penPtr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ *	Return a list of the names of all the axes.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NamesOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Blt_HashSearch cursor;
+    Pen *penPtr;
+    register int i;
+    register Blt_HashEntry *hPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&(graphPtr->penTable), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	penPtr = (Pen *)Blt_GetHashValue(hPtr);
+	if (penPtr->flags & PEN_DELETE_PENDING) {
+	    continue;
+	}
+	if (argc == 3) {
+	    Tcl_AppendElement(interp, penPtr->name);
+	    continue;
+	}
+	for (i = 3; i < argc; i++) {
+	    if (Tcl_StringMatch(penPtr->name, argv[i])) {
+		Tcl_AppendElement(interp, penPtr->name);
+		break;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ *	Return the type of pen.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(interp, graphPtr, argc, argv)
+    Tcl_Interp *interp;
+    Graph *graphPtr;
+    int argc;
+    char **argv;
+{
+    Pen *penPtr;
+
+    penPtr = NameToPen(graphPtr, argv[3]);
+    if (penPtr == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_SetResult(interp, penPtr->classUid, TCL_STATIC);
+    return TCL_OK;
+}
+
+static Blt_OpSpec penOps[] =
+{
+    {"cget", 2, (Blt_Op)CgetOp, 5, 5, "penName option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 4, 0,
+	"penName ?penName?... ?option value?...",},
+    {"create", 2, (Blt_Op)CreateOp, 4, 0, "penName ?option value?...",},
+    {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "?penName?...",},
+    {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",},
+    {"type", 1, (Blt_Op)TypeOp, 4, 4, "penName",},
+};
+static int nPenOps = sizeof(penOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PenOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+
+    proc = Blt_GetOp(interp, nPenOps, penOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (interp, graphPtr, argc, argv);
+}
Index: trunk/kitgen/8.x/blt/generic/bltGrPs.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGrPs.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGrPs.c	(revision 175)
@@ -0,0 +1,1273 @@
+
+/*
+ * bltGrPs.c --
+ *
+ *      This module implements the "postscript" operation for BLT
+ *      graph widget.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.  
+ */
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * PostScript routines to print a graph
+ *
+ * -----------------------------------------------------------------
+ */
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define PS_PREVIEW_EPSI	0
+#define PS_PREVIEW_WMF	1
+#define PS_PREVIEW_TIFF	2
+
+static Tk_OptionParseProc StringToColorMode;
+static Tk_OptionPrintProc ColorModeToString;
+static Tk_CustomOption colorModeOption =
+{
+    StringToColorMode, ColorModeToString, (ClientData)0,
+};
+static Tk_OptionParseProc StringToFormat;
+static Tk_OptionPrintProc FormatToString;
+static Tk_CustomOption formatOption =
+{
+    StringToFormat, FormatToString, (ClientData)0,
+};
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPadOption;
+
+#define DEF_PS_CENTER		"yes"
+#define DEF_PS_COLOR_MAP	(char *)NULL
+#define DEF_PS_COLOR_MODE	"color"
+#define DEF_PS_DECORATIONS	"yes"
+#define DEF_PS_FONT_MAP		(char *)NULL
+#define DEF_PS_FOOTER		"no"
+#define DEF_PS_HEIGHT		"0"
+#define DEF_PS_LANDSCAPE	"no"
+#define DEF_PS_MAXPECT		"no"
+#define DEF_PS_PADX		"1.0i"
+#define DEF_PS_PADY		"1.0i"
+#define DEF_PS_PAPERHEIGHT	"11.0i"
+#define DEF_PS_PAPERWIDTH	"8.5i"
+#define DEF_PS_PREVIEW		"no"
+#define DEF_PS_PREVIEW_FORMAT   "epsi"
+#define DEF_PS_WIDTH		"0"
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_BOOLEAN, "-center", "center", "Center",
+	DEF_PS_CENTER, Tk_Offset(PostScript, center),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-colormap", "colorMap", "ColorMap",
+	DEF_PS_COLOR_MAP, Tk_Offset(PostScript, colorVarName),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-colormode", "colorMode", "ColorMode",
+	DEF_PS_COLOR_MODE, Tk_Offset(PostScript, colorMode),
+	TK_CONFIG_DONT_SET_DEFAULT, &colorModeOption},
+    {TK_CONFIG_BOOLEAN, "-decorations", "decorations", "Decorations",
+	DEF_PS_DECORATIONS, Tk_Offset(PostScript, decorations),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-fontmap", "fontMap", "FontMap",
+	DEF_PS_FONT_MAP, Tk_Offset(PostScript, fontVarName),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-footer", "footer", "Footer",
+	DEF_PS_FOOTER, Tk_Offset(PostScript, footer),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+	DEF_PS_HEIGHT, Tk_Offset(PostScript, reqHeight),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-landscape", "landscape", "Landscape",
+	DEF_PS_LANDSCAPE, Tk_Offset(PostScript, landscape),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-maxpect", "maxpect", "Maxpect",
+	DEF_PS_MAXPECT, Tk_Offset(PostScript, maxpect),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+	DEF_PS_PADX, Tk_Offset(PostScript, padX), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+	DEF_PS_PADY, Tk_Offset(PostScript, padY), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-paperheight", "paperHeight", "PaperHeight",
+	DEF_PS_PAPERHEIGHT, Tk_Offset(PostScript, reqPaperHeight),
+	0, &bltPositiveDistanceOption},
+    {TK_CONFIG_CUSTOM, "-paperwidth", "paperWidth", "PaperWidth",
+	DEF_PS_PAPERWIDTH, Tk_Offset(PostScript, reqPaperWidth),
+	0, &bltPositiveDistanceOption},
+    {TK_CONFIG_BOOLEAN, "-preview", "preview", "Preview",
+	DEF_PS_PREVIEW, Tk_Offset(PostScript, addPreview),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-previewformat", "previewFormat", "PreviewFormat",
+	DEF_PS_PREVIEW_FORMAT, Tk_Offset(PostScript, previewFormat), 
+        TK_CONFIG_DONT_SET_DEFAULT, &formatOption},
+    {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+	DEF_PS_WIDTH, Tk_Offset(PostScript, reqWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+extern void Blt_MarkersToPostScript _ANSI_ARGS_((Graph *graphPtr, 
+	PsToken psToken, int under));
+extern void Blt_ElementsToPostScript _ANSI_ARGS_((Graph *graphPtr, 
+	PsToken psToken));
+extern void Blt_ActiveElementsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+	PsToken psToken));
+extern void Blt_LegendToPostScript _ANSI_ARGS_((Legend *legendPtr, 
+	PsToken psToken));
+extern void Blt_GridToPostScript _ANSI_ARGS_((Graph *graphPtr, 
+	PsToken psToken));
+extern void Blt_AxesToPostScript _ANSI_ARGS_((Graph *graphPtr, 
+	PsToken psToken));
+extern void Blt_AxisLimitsToPostScript _ANSI_ARGS_((Graph *graphPtr,
+	PsToken psToken));
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorMode --
+ *
+ *	Convert the string representation of a PostScript color mode
+ *	into the enumerated type representing the color level:
+ *
+ *	    PS_MODE_COLOR 	- Full color
+ *	    PS_MODE_GREYSCALE  	- Color converted to greyscale
+ *	    PS_MODE_MONOCHROME 	- Only black and white
+ *
+ * Results:
+ *	A standard Tcl result.  The color level is written into the
+ *	page layout information structure.
+ *
+ * Side Effects:
+ *	Future invocations of the "postscript" option will use this
+ *	variable to determine how color information will be displayed
+ *	in the PostScript output it produces.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorMode(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* New value. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in record */
+{
+    PsColorMode *modePtr = (PsColorMode *) (widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'c') && (strncmp(string, "color", length) == 0)) {
+	*modePtr = PS_MODE_COLOR;
+    } else if ((c == 'g') && (strncmp(string, "grayscale", length) == 0)) {
+	*modePtr = PS_MODE_GREYSCALE;
+    } else if ((c == 'g') && (strncmp(string, "greyscale", length) == 0)) {
+	*modePtr = PS_MODE_GREYSCALE;
+    } else if ((c == 'm') && (strncmp(string, "monochrome", length) == 0)) {
+	*modePtr = PS_MODE_MONOCHROME;
+    } else {
+	Tcl_AppendResult(interp, "bad color mode \"", string, "\": should be \
+\"color\", \"greyscale\", or \"monochrome\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColorMode --
+ *
+ *	Convert the PostScript mode value into the string representing
+ *	a valid color mode.
+ *
+ * Results:
+ *	The static string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColorMode(colorMode)
+    PsColorMode colorMode;
+{
+    switch (colorMode) {
+    case PS_MODE_COLOR:
+	return "color";
+    case PS_MODE_GREYSCALE:
+	return "greyscale";
+    case PS_MODE_MONOCHROME:
+	return "monochrome";
+    default:
+	return "unknown color mode";
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorModeToString --
+ *
+ *	Convert the current color mode into the string representing a
+ *	valid color mode.
+ *
+ * Results:
+ *	The string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record. */
+    int offset;			/* field of colorMode in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    PsColorMode mode = *(PsColorMode *) (widgRec + offset);
+
+    return NameOfColorMode(mode);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ *	Convert the string of the PostScript preview format into 
+ *	an enumerated type representing the desired format.  The
+ *	available formats are:
+ *
+ *	    PS_PREVIEW_WMF 	- Windows Metafile.
+ *	    PS_PREVIEW_TIFF  	- TIFF bitmap image.
+ *	    PS_PREVIEW_EPSI 	- Device independent ASCII preview
+ *
+ * Results:
+ *	A standard Tcl result.  The format is written into the
+ *	page layout information structure.
+ *
+ * Side Effects:
+ *	Future invocations of the "postscript" option will use this
+ *	variable to determine how to format a preview image (if one
+ *	is selected) when the PostScript output is produced.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* New value. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in record */
+{
+    int *formatPtr = (int *) (widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'c') && (strncmp(string, "epsi", length) == 0)) {
+	*formatPtr = PS_PREVIEW_EPSI;
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+    } else if ((c == 't') && (strncmp(string, "tiff", length) == 0)) {
+	*formatPtr = PS_PREVIEW_TIFF;
+#endif /* HAVE_TIFF_H */
+    } else if ((c == 'w') && (strncmp(string, "wmf", length) == 0)) {
+	*formatPtr = PS_PREVIEW_WMF;
+#endif /* WIN32 */
+    } else {
+	Tcl_AppendResult(interp, "bad format \"", string, "\": should be ",
+#ifdef WIN32
+#ifdef HAVE_TIFF_H
+			 "\"tiff\" or ",
+#endif /* HAVE_TIFF_H */
+			 "\"wmf\" or ",
+#endif /* WIN32 */
+			 "\"epsi\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatToString --
+ *
+ *	Convert the preview format into the string representing its
+ *	type.
+ *
+ * Results:
+ *	The string representing the preview format is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+FormatToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* PostScript structure record */
+    int offset;			/* field of colorMode in record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int format = *(int *)(widgRec + offset);
+
+    switch (format) {
+    case PS_PREVIEW_EPSI:
+	return "epsi";
+    case PS_PREVIEW_WMF:
+	return "wmf";
+    case PS_PREVIEW_TIFF:
+	return "tiff";
+    }
+    return "?unknown preview format?";
+}
+
+
+void
+Blt_DestroyPostScript(graphPtr)
+    Graph *graphPtr;
+{
+    Tk_FreeOptions(configSpecs, (char *)graphPtr->postscript, 
+	   graphPtr->display, 0);
+    Blt_Free(graphPtr->postscript);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char *argv[];
+{
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+    if (Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs, (char *)psPtr,
+	    argv[3], 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *      This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ *      A standard TCL result.
+ *
+ * Side effects:
+ *      A new PostScript file is created.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Number of options in argv vector */
+    char **argv;		/* Option vector */
+{
+    int flags = TK_CONFIG_ARGV_ONLY;
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+
+    if (argc == 3) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)psPtr, (char *)NULL, flags);
+    } else if (argc == 4) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+		(char *)psPtr, argv[3], flags);
+    }
+    if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+	    argv + 3, (char *)psPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * ComputeBoundingBox --
+ *
+ * 	Computes the bounding box for the PostScript plot.  First get
+ * 	the size of the plot (by default, it's the size of graph's X
+ * 	window).  If the plot plus the page border is bigger than the
+ * 	designated paper size, or if the "-maxpect" option is turned
+ * 	on, scale the plot to the page.
+ *
+ *	Note: All coordinates/sizes are in screen coordinates, not
+ *	      PostScript coordinates.  This includes the computed
+ *	      bounding box and paper size.  They will be scaled to
+ *	      printer points later.
+ *
+ * Results:
+ *	Returns the height of the paper in screen coordinates.
+ *
+ * Side Effects:
+ *	The graph dimensions (width and height) are changed to the
+ *	requested PostScript plot size.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+ComputeBoundingBox(graphPtr, psPtr)
+    Graph *graphPtr;
+    PostScript *psPtr;
+{
+    int paperWidth, paperHeight;
+    int x, y, hSize, vSize, hBorder, vBorder;
+    double hScale, vScale, scale;
+
+    x = psPtr->padLeft;
+    y = psPtr->padTop;
+    hBorder = PADDING(psPtr->padX);
+    vBorder = PADDING(psPtr->padY);
+
+    if (psPtr->reqWidth > 0) {
+	graphPtr->width = psPtr->reqWidth;
+    }
+    if (psPtr->reqHeight > 0) {
+	graphPtr->height = psPtr->reqHeight;
+    }
+    if (psPtr->landscape) {
+	hSize = graphPtr->height;
+	vSize = graphPtr->width;
+    } else {
+	hSize = graphPtr->width;
+	vSize = graphPtr->height;
+    }
+    /*
+     * If the paper size wasn't specified, set it to the graph size plus
+     * the paper border.
+     */
+    paperWidth = psPtr->reqPaperWidth;
+    paperHeight = psPtr->reqPaperHeight;
+    if (paperWidth < 1) {
+	paperWidth = hSize + hBorder;
+    }
+    if (paperHeight < 1) {
+	paperHeight = vSize + vBorder;
+    }
+    hScale = vScale = 1.0;
+    /*
+     * Scale the plot size (the graph itself doesn't change size) if
+     * it's bigger than the paper or if -maxpect was set.
+     */
+    if ((psPtr->maxpect) || ((hSize + hBorder) > paperWidth)) {
+	hScale = (double)(paperWidth - hBorder) / (double)hSize;
+    }
+    if ((psPtr->maxpect) || ((vSize + vBorder) > paperHeight)) {
+	vScale = (double)(paperHeight - vBorder) / (double)vSize;
+    }
+    scale = MIN(hScale, vScale);
+    if (scale != 1.0) {
+	hSize = (int)((hSize * scale) + 0.5);
+	vSize = (int)((vSize * scale) + 0.5);
+    }
+    psPtr->pageScale = scale;
+    if (psPtr->center) {
+	if (paperWidth > hSize) {
+	    x = (paperWidth - hSize) / 2;
+	}
+	if (paperHeight > vSize) {
+	    y = (paperHeight - vSize) / 2;
+	}
+    }
+    psPtr->left = x;
+    psPtr->bottom = y;
+    psPtr->right = x + hSize - 1;
+    psPtr->top = y + vSize - 1;
+
+    graphPtr->flags |= LAYOUT_NEEDED | MAP_WORLD;
+    Blt_LayoutGraph(graphPtr);
+    return paperHeight;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PreviewImage --
+ *
+ * 	Generates a EPSI thumbnail of the graph.  The thumbnail is
+ *	restricted to a certain size.  This is to keep the size of the
+ *	PostScript file small and the processing time low.
+ *
+ *	The graph is drawn into a pixmap.  We then take a snapshot
+ *	of that pixmap, and rescale it to a smaller image.  Finally,
+ * 	the image is dumped to PostScript.
+ *
+ * Results:
+ *	None.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void
+PreviewImage(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+    int noBackingStore = 0;
+    Pixmap drawable;
+    Blt_ColorImage image;
+    int nLines;
+    Tcl_DString dString;
+
+    /* Create a pixmap and draw the graph into it. */
+
+    drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+	graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
+    Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+    
+    /* Get a color image from the pixmap */
+    image = Blt_DrawableToColorImage(graphPtr->tkwin, drawable, 0, 0, 
+	graphPtr->width, graphPtr->height, 1.0);
+    Tk_FreePixmap(graphPtr->display, drawable);
+    if (image == NULL) {
+	return;			/* Can't grab pixmap? */
+    }
+#ifdef THUMBNAIL_PREVIEW
+    {
+	double scale, xScale, yScale;
+	int width, height;
+	Blt_ColorImage destImage;
+
+	/* Scale the source image into a size appropriate for a thumbnail. */
+#define PS_MAX_PREVIEW_WIDTH	300.0
+#define PS_MAX_PREVIEW_HEIGHT	300.0
+	xScale = PS_MAX_PREVIEW_WIDTH / (double)graphPtr->width;
+	yScale = PS_MAX_PREVIEW_HEIGHT / (double)graphPtr->height;
+	scale = MIN(xScale, yScale);
+
+	width = (int)(scale * graphPtr->width + 0.5);
+	height = (int)(scale * graphPtr->height + 0.5);
+	destImage = Blt_ResampleColorImage(image, width, height, 
+		bltBoxFilterPtr, bltBoxFilterPtr);
+	Blt_FreeColorImage(image);
+	image = destImage;
+    }
+#endif /* THUMBNAIL_PREVIEW */
+    Blt_ColorImageToGreyscale(image);
+    if (psPtr->landscape) {
+	Blt_ColorImage oldImage;
+
+	oldImage = image;
+	image = Blt_RotateColorImage(image, 90.0);
+	Blt_FreeColorImage(oldImage);
+    }
+    Tcl_DStringInit(&dString);
+    /* Finally, we can generate PostScript for the image */
+    nLines = Blt_ColorImageToPsData(image, 1, &dString, "%");
+
+    Blt_AppendToPostScript(psToken, "%%BeginPreview: ", (char *)NULL);
+    Blt_FormatToPostScript(psToken, "%d %d 8 %d\n", Blt_ColorImageWidth(image),
+	Blt_ColorImageHeight(image), nLines);
+    Blt_AppendToPostScript(psToken, Tcl_DStringValue(&dString), (char *)NULL);
+    Blt_AppendToPostScript(psToken, "%%EndPreview\n\n", (char *)NULL);
+    Tcl_DStringFree(&dString);
+    Blt_FreeColorImage(image);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * PostScriptPreamble
+ *
+ *    	The PostScript preamble calculates the needed translation and scaling
+ *    	to make X11 coordinates compatible with PostScript.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+static int
+PostScriptPreamble(graphPtr, fileName, psToken)
+    Graph *graphPtr;
+    char *fileName;
+    PsToken psToken;
+{
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+    time_t ticks;
+    char date[200];		/* Hold the date string from ctime() */
+    CONST char *version;
+    double dpiX, dpiY;
+    double xPixelsToPica, yPixelsToPica; /* Scales to convert pixels to pica */
+    Screen *screenPtr;
+    char *nl;
+    int paperHeightPixels;
+
+    paperHeightPixels = ComputeBoundingBox(graphPtr, psPtr);
+    if (fileName == NULL) {
+	fileName = Tk_PathName(graphPtr->tkwin);
+    }
+    Blt_AppendToPostScript(psToken, "%!PS-Adobe-3.0 EPSF-3.0\n", 
+			   (char *)NULL);
+
+    /*
+     * Compute the scale factors to convert PostScript to X11 coordinates.
+     * Round the pixels per inch (dpi) to an integral value before computing
+     * the scale.
+     */
+#define MM_INCH		25.4
+#define PICA_INCH	72.0
+    screenPtr = Tk_Screen(graphPtr->tkwin);
+    dpiX = (WidthOfScreen(screenPtr) * MM_INCH) / WidthMMOfScreen(screenPtr);
+    xPixelsToPica = PICA_INCH / dpiX;
+    dpiY = (HeightOfScreen(screenPtr) * MM_INCH) / HeightMMOfScreen(screenPtr);
+    yPixelsToPica = PICA_INCH / dpiY;
+
+    /*
+     * The "BoundingBox" comment is required for EPS files. The box
+     * coordinates are integers, so we need round away from the
+     * center of the box.
+     */
+    Blt_FormatToPostScript(psToken, "%%%%BoundingBox: %d %d %d %d\n",
+	(int)floor(psPtr->left * xPixelsToPica),
+	(int)floor((paperHeightPixels - psPtr->top) * yPixelsToPica),
+	(int)ceil(psPtr->right * xPixelsToPica),
+	(int)ceil((paperHeightPixels - psPtr->bottom) * yPixelsToPica));
+
+    Blt_AppendToPostScript(psToken, "%%Pages: 0\n", (char *)NULL);
+
+    version = Tcl_GetVar(graphPtr->interp, "blt_version", TCL_GLOBAL_ONLY);
+    if (version == NULL) {
+	version = "???";
+    }
+    Blt_FormatToPostScript(psToken, "%%%%Creator: (BLT %s %s)\n", version,
+	Tk_Class(graphPtr->tkwin));
+
+    ticks = time((time_t *) NULL);
+    strcpy(date, ctime(&ticks));
+    nl = date + strlen(date) - 1;
+    if (*nl == '\n') {
+	*nl = '\0';
+    }
+    Blt_FormatToPostScript(psToken, "%%%%CreationDate: (%s)\n", date);
+    Blt_FormatToPostScript(psToken, "%%%%Title: (%s)\n", fileName);
+    Blt_AppendToPostScript(psToken, "%%DocumentData: Clean7Bit\n", 
+			   (char *)NULL);
+    if (psPtr->landscape) {
+	Blt_AppendToPostScript(psToken, "%%Orientation: Landscape\n", 
+			       (char *)NULL);
+    } else {
+	Blt_AppendToPostScript(psToken, "%%Orientation: Portrait\n", 
+			       (char *)NULL);
+    }
+    Blt_AppendToPostScript(psToken,
+	"%%DocumentNeededResources: font Helvetica Courier\n", (char *)NULL);
+    Blt_AppendToPostScript(psToken, "%%EndComments\n\n", (char *)NULL);
+    if ((psPtr->addPreview) && (psPtr->previewFormat == PS_PREVIEW_EPSI)) {
+	PreviewImage(graphPtr, psToken);
+    }
+    if (Blt_FileToPostScript(psToken, "bltGraph.pro") != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (psPtr->footer) {
+	char *who;
+
+	who = getenv("LOGNAME");
+	if (who == NULL) {
+	    who = "???";
+	}
+	Blt_AppendToPostScript(psToken,
+	    "8 /Helvetica SetFont\n",
+	    "10 30 moveto\n",
+	    "(Date: ", date, ") show\n",
+	    "10 20 moveto\n",
+	    "(File: ", fileName, ") show\n",
+	    "10 10 moveto\n",
+	    "(Created by: ", who, "@", Tcl_GetHostName(), ") show\n",
+	    "0 0 moveto\n",
+	    (char *)NULL);
+    }
+    /*
+     * Set the conversion from PostScript to X11 coordinates.  Scale
+     * pica to pixels and flip the y-axis (the origin is the upperleft
+     * corner).
+     */
+    Blt_AppendToPostScript(psToken,
+	"% Transform coordinate system to use X11 coordinates\n\n",
+	"% 1. Flip y-axis over by reversing the scale,\n",
+	"% 2. Translate the origin to the other side of the page,\n",
+	"%    making the origin the upper left corner\n", (char *)NULL);
+    Blt_FormatToPostScript(psToken, "%g -%g scale\n", xPixelsToPica, 
+	yPixelsToPica);
+    /* Papersize is in pixels.  Translate the new origin *after*
+     * changing the scale. */
+    Blt_FormatToPostScript(psToken, "0 %d translate\n\n", 
+	-paperHeightPixels);
+    Blt_AppendToPostScript(psToken, "% User defined page layout\n\n",
+	"% Set color level\n", (char *)NULL);
+    Blt_FormatToPostScript(psToken, "/CL %d def\n\n", psPtr->colorMode);
+    Blt_FormatToPostScript(psToken, "%% Set origin\n%d %d translate\n\n",
+	psPtr->left, psPtr->bottom);
+    if (psPtr->landscape) {
+	Blt_FormatToPostScript(psToken,
+	    "%% Landscape orientation\n0 %g translate\n-90 rotate\n",
+	    ((double)graphPtr->width * psPtr->pageScale));
+    }
+    if (psPtr->pageScale != 1.0) {
+	Blt_AppendToPostScript(psToken, "\n% Setting graph scale factor\n",
+	    (char *)NULL);
+	Blt_FormatToPostScript(psToken, " %g %g scale\n", psPtr->pageScale,
+	    psPtr->pageScale);
+    }
+    Blt_AppendToPostScript(psToken, "\n%%EndSetup\n\n", (char *)NULL);
+    return TCL_OK;
+}
+
+
+static void
+MarginsToPostScript(graphPtr, psToken)
+    Graph *graphPtr;
+    PsToken psToken;
+{
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+    XRectangle margin[4];
+
+    margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0;
+    margin[0].width = margin[3].width = graphPtr->width;
+    margin[0].height = graphPtr->top;
+    margin[3].y = graphPtr->bottom;
+    margin[3].height = graphPtr->height - graphPtr->bottom;
+    margin[2].y = margin[1].y = graphPtr->top;
+    margin[1].width = graphPtr->left;
+    margin[2].height = margin[1].height = graphPtr->bottom - graphPtr->top;
+    margin[2].x = graphPtr->right;
+    margin[2].width = graphPtr->width - graphPtr->right;
+
+    /* Clear the surrounding margins and clip the plotting surface */
+    if (psPtr->decorations) {
+	Blt_BackgroundToPostScript(psToken,
+	    Tk_3DBorderColor(graphPtr->border));
+    } else {
+	Blt_ClearBackgroundToPostScript(psToken);
+    }
+    Blt_RectanglesToPostScript(psToken, margin, 4);
+    
+    /* Interior 3D border */
+    if ((psPtr->decorations) && (graphPtr->plotBorderWidth > 0)) {
+	int x, y, width, height;
+
+	x = graphPtr->left - graphPtr->plotBorderWidth;
+	y = graphPtr->top - graphPtr->plotBorderWidth;
+	width = (graphPtr->right - graphPtr->left) + 
+	    (2 * graphPtr->plotBorderWidth);
+	height = (graphPtr->bottom - graphPtr->top) + 
+	    (2 * graphPtr->plotBorderWidth);
+	Blt_Draw3DRectangleToPostScript(psToken, graphPtr->border, 
+	   (double)x, (double)y, width, height, graphPtr->plotBorderWidth, 
+	   graphPtr->plotRelief);
+    }
+    if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
+	/*
+	 * Print the legend if we're using a site which lies in one
+	 * of the margins (left, right, top, or bottom) of the graph.
+	 */
+	Blt_LegendToPostScript(graphPtr->legend, psToken);
+    }
+    if (graphPtr->title != NULL) {
+	Blt_TextToPostScript(psToken, graphPtr->title, 
+		&graphPtr->titleTextStyle, (double)graphPtr->titleX, 
+		(double)graphPtr->titleY);
+    }
+    Blt_AxesToPostScript(graphPtr, psToken);
+}
+
+
+static int
+GraphToPostScript(graphPtr, ident, psToken)
+    Graph *graphPtr;
+    char *ident;		/* Identifier string (usually the filename) */
+    PsToken psToken;
+{
+    int x, y, width, height;
+    int result;
+
+    /*   
+     * We need to know how big a graph to print.  If the graph hasn't
+     * been drawn yet, the width and height will be 1.  Instead use
+     * the requested size of the widget.  The user can still override
+     * this with the -width and -height postscript options.  
+     */
+    if (graphPtr->height <= 1) {
+	graphPtr->height = Tk_ReqHeight(graphPtr->tkwin);
+    }
+    if (graphPtr->width <= 1) {
+	graphPtr->width = Tk_ReqWidth(graphPtr->tkwin);
+    }
+    result = PostScriptPreamble(graphPtr, ident, psToken);
+    if (result != TCL_OK) {
+	goto error;
+    }
+    /*
+     * Determine rectangle of the plotting area for the graph window
+     */
+    x = graphPtr->left - graphPtr->plotBorderWidth;
+    y = graphPtr->top - graphPtr->plotBorderWidth;
+
+    width = (graphPtr->right - graphPtr->left + 1) + 
+	(2 * graphPtr->plotBorderWidth);
+    height = (graphPtr->bottom - graphPtr->top + 1) + 
+	(2 * graphPtr->plotBorderWidth);
+
+    Blt_FontToPostScript(psToken, graphPtr->titleTextStyle.font);
+    Blt_RegionToPostScript(psToken, (double)x, (double)y, width, height);
+    if (graphPtr->postscript->decorations) {
+	Blt_BackgroundToPostScript(psToken, graphPtr->plotBg);
+    } else {
+	Blt_ClearBackgroundToPostScript(psToken);
+    }
+    Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+    Blt_AppendToPostScript(psToken, "gsave clip\n\n", (char *)NULL);
+    /* Draw the grid, elements, and markers in the plotting area. */
+    if (!graphPtr->gridPtr->hidden) {
+	Blt_GridToPostScript(graphPtr, psToken);
+    }
+    Blt_MarkersToPostScript(graphPtr, psToken, TRUE);
+    if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) && 
+	(!Blt_LegendIsRaised(graphPtr->legend))) {
+	/* Print legend underneath elements and markers */
+	Blt_LegendToPostScript(graphPtr->legend, psToken);
+    }
+    Blt_AxisLimitsToPostScript(graphPtr, psToken);
+    Blt_ElementsToPostScript(graphPtr, psToken);
+    if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) && 
+	(Blt_LegendIsRaised(graphPtr->legend))) {
+	/* Print legend above elements (but not markers) */
+	Blt_LegendToPostScript(graphPtr->legend, psToken);
+    }
+    Blt_MarkersToPostScript(graphPtr, psToken, FALSE);
+    Blt_ActiveElementsToPostScript(graphPtr, psToken);
+    Blt_AppendToPostScript(psToken, "\n",
+	"% Unset clipping\n",
+	"grestore\n\n", (char *)NULL);
+    MarginsToPostScript(graphPtr, psToken);
+    Blt_AppendToPostScript(psToken,
+	"showpage\n",
+	"%Trailer\n",
+	"grestore\n",
+	"end\n",
+	"%EOF\n", (char *)NULL);
+  error:
+    /* Reset height and width of graph window */
+    graphPtr->width = Tk_Width(graphPtr->tkwin);
+    graphPtr->height = Tk_Height(graphPtr->tkwin);
+    graphPtr->flags = MAP_WORLD;
+
+    /*
+     * Redraw the graph in order to re-calculate the layout as soon as
+     * possible. This is in the case the crosshairs are active.
+     */
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return result;
+}
+
+#ifdef WIN32
+
+static void
+InitAPMHeader(
+    Tk_Window tkwin,
+    int width, int height,
+    APMHEADER *headerPtr)
+{
+    unsigned int *p;
+    unsigned int sum;
+    Screen *screen;
+#define MM_INCH		25.4
+    double dpiX, dpiY;
+
+    headerPtr->key = 0x9ac6cdd7L;
+    headerPtr->hmf = 0;
+    headerPtr->inch = 1440;
+
+    screen = Tk_Screen(tkwin);
+    dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
+    dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
+
+    headerPtr->bbox.Left = headerPtr->bbox.Top = 0;
+    headerPtr->bbox.Bottom = (SHORT)((width * 1440) / dpiX);
+    headerPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
+    headerPtr->reserved = 0;
+    sum = 0;
+    for (p = (unsigned int *)headerPtr; 
+	 p < (unsigned int *)&(headerPtr->checksum); p++) {
+	sum ^= *p;
+    }
+    headerPtr->checksum = sum;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * CreateWindowEPS --
+ *
+ * 	Generates an EPS file with a Window metafile preview.  
+ *
+ *	Windows metafiles aren't very robust.  Including exactly the
+ *	same metafile (one embedded in a DOS EPS, the other as .wmf
+ *	file) will play back differently.  
+ *	
+ * Results:
+ *	None.
+ *
+ * -------------------------------------------------------------------------- 
+ */
+static int
+CreateWindowsEPS(
+    Graph *graphPtr,
+    PsToken psToken,
+    FILE *f)
+{
+    DWORD size;
+    DOSEPSHEADER epsHeader;
+    HANDLE hMem;
+    HDC hRefDC, hDC;
+    HENHMETAFILE hMetaFile;
+    Tcl_DString dString;
+    TkWinDC drawableDC;
+    TkWinDCState state;
+    int result;
+    unsigned char *buffer, *psBuffer;
+    
+    Blt_AppendToPostScript(psToken, "\n", (char *)NULL);
+    psBuffer = Blt_PostScriptFromToken(psToken);
+    /*
+     * Fill out as much information as we can into the DOS EPS header.
+     * We won't know the start of the length of the WMF segment until 
+     * we create the metafile.
+     */
+    epsHeader.magic[0] = 0xC5;
+    epsHeader.magic[1] = 0xD0;
+    epsHeader.magic[2] = 0xD3;
+    epsHeader.magic[3] = 0xC6;
+    epsHeader.psStart = sizeof(epsHeader); 
+    epsHeader.psLength = strlen(psBuffer) + 1;
+    epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+    epsHeader.wmfLength = 0L;	/* Fill in later. */
+    epsHeader.tiffStart = 0L;	
+    epsHeader.tiffLength = 0L;
+    epsHeader.checksum = 0xFFFF;
+
+    result = TCL_ERROR;
+    hRefDC = TkWinGetDrawableDC(graphPtr->display, 
+	Tk_WindowId(graphPtr->tkwin), &state);
+
+    /* Build a description string. */
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+    Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+    Tcl_DStringAppend(&dString, "\0", -1);
+    Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+    Tcl_DStringAppend(&dString, "\0", -1);
+
+    hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, Tcl_DStringValue(&dString));
+    Tcl_DStringFree(&dString);
+    
+    if (hDC == NULL) {
+	Tcl_AppendResult(graphPtr->interp, "can't create metafile: ",	
+		Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Assemble a Tk drawable that points to the metafile and let the
+     * graph's drawing routine draw into it. */
+    drawableDC.hdc = hDC;
+    drawableDC.type = TWD_WINDC;
+    
+    graphPtr->width = Tk_Width(graphPtr->tkwin);
+    graphPtr->height = Tk_Height(graphPtr->tkwin);
+    graphPtr->flags |= RESET_WORLD;
+    Blt_LayoutGraph(graphPtr);
+    Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
+    GdiFlush();
+    hMetaFile = CloseEnhMetaFile(hDC);
+
+    size = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hRefDC);
+    hMem = GlobalAlloc(GHND, size);
+    if (hMem == NULL) {
+	Tcl_AppendResult(graphPtr->interp, "can't allocate global memory:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    buffer = (LPVOID)GlobalLock(hMem);
+    if (!GetWinMetaFileBits(hMetaFile, size, buffer, MM_ANISOTROPIC, hRefDC)) {
+	Tcl_AppendResult(graphPtr->interp, "can't get metafile data:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+
+    /*  
+     * Fix up the EPS header with the correct metafile length and PS
+     * offset (now that we what they are).
+     */
+    epsHeader.wmfLength = size;
+    epsHeader.wmfStart = epsHeader.psStart + epsHeader.psLength;
+
+    /* Write out the eps header, */
+    if (fwrite(&epsHeader, 1, sizeof(epsHeader), f) != sizeof(epsHeader)) {
+	Tcl_AppendResult(graphPtr->interp, "error writing eps header:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    /* the PostScript, */
+    if (fwrite(psBuffer, 1, epsHeader.psLength, f) != epsHeader.psLength) {
+	Tcl_AppendResult(graphPtr->interp, "error writing PostScript data:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    /* and finally the metadata itself. */
+    if (fwrite(buffer, 1, size, f) != size) {
+	Tcl_AppendResult(graphPtr->interp, "error writing metafile data:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    result = TCL_OK;
+
+ error:
+    DeleteEnhMetaFile(hMetaFile); 
+    TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hRefDC, &state);
+    fclose(f);
+    if (hMem != NULL) {
+	GlobalUnlock(hMem);
+	GlobalFree(hMem);
+    }
+    graphPtr->flags = MAP_WORLD;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return result;
+}
+
+#endif /*WIN32*/
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OutputOp --
+ *
+ *      This procedure is invoked to print the graph in a file.
+ *
+ * Results:
+ *      Standard TCL result.  TCL_OK if plot was successfully printed,
+ *	TCL_ERROR otherwise.
+ *
+ * Side effects:
+ *      A new PostScript file is created.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+OutputOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Number of options in argv vector */
+    char **argv;		/* Option vector */
+{
+    PostScript *psPtr = (PostScript *)graphPtr->postscript;
+    FILE *f = NULL;
+    PsToken psToken;
+    char *fileName;		/* Name of file to write PostScript output
+                                 * If NULL, output is returned via
+                                 * interp->result. */
+    fileName = NULL;
+    if (argc > 3) {
+	if (argv[3][0] != '-') {
+	    fileName = argv[3];	/* First argument is the file name. */
+	    argv++, argc--;
+	}
+	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 3,
+		argv + 3, (char *)psPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (fileName != NULL) {
+#ifdef WIN32
+	    f = fopen(fileName, "wb");
+#else 
+	    f = fopen(fileName, "w");
+#endif
+	    if (f == NULL) {
+		Tcl_AppendResult(interp, "can't create \"", fileName, "\": ",
+		    Tcl_PosixError(interp), (char *)NULL);
+		return TCL_ERROR;
+	    }
+	}
+    }
+    psToken = Blt_GetPsToken(graphPtr->interp, graphPtr->tkwin);
+    psToken->fontVarName = psPtr->fontVarName;
+    psToken->colorVarName = psPtr->colorVarName;
+    psToken->colorMode = psPtr->colorMode;
+
+    if (GraphToPostScript(graphPtr, fileName, psToken) != TCL_OK) {
+	goto error;
+    }
+    /*
+     * If a file name was given, write the results to that file
+     */
+    if (f != NULL) {
+#ifdef WIN32
+	if ((psPtr->addPreview) && (psPtr->previewFormat != PS_PREVIEW_EPSI)) {
+	    if (CreateWindowsEPS(graphPtr, psToken, f)) {
+		return TCL_ERROR;
+	    }
+	} else {	    
+	    fputs(Blt_PostScriptFromToken(psToken), f);
+	    if (ferror(f)) {
+		Tcl_AppendResult(interp, "error writing file \"", fileName, 
+			"\": ", Tcl_PosixError(interp), (char *)NULL);
+		goto error;
+	    }
+	}
+#else 
+	fputs(Blt_PostScriptFromToken(psToken), f);
+	if (ferror(f)) {
+	    Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ",
+		Tcl_PosixError(interp), (char *)NULL);
+	    goto error;
+	}
+#endif /* WIN32 */
+        fclose(f);
+    } else {
+	Tcl_SetResult(interp, Blt_PostScriptFromToken(psToken), TCL_VOLATILE);
+    }
+    Blt_ReleasePsToken(psToken);
+    return TCL_OK;
+
+  error:
+    if (f != NULL) {
+	fclose(f);
+    }
+    Blt_ReleasePsToken(psToken);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreatePostScript --
+ *
+ *      Creates a postscript structure.
+ *
+ * Results:
+ *      Always TCL_OK.
+ *
+ * Side effects:
+ *      A new PostScript structure is created.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CreatePostScript(graphPtr)
+    Graph *graphPtr;
+{
+    PostScript *psPtr;
+
+    psPtr = Blt_Calloc(1, sizeof(PostScript));
+    assert(psPtr);
+    psPtr->colorMode = PS_MODE_COLOR;
+    psPtr->center = TRUE;
+    psPtr->decorations = TRUE;
+    graphPtr->postscript = psPtr;
+
+    if (Blt_ConfigureWidgetComponent(graphPtr->interp, graphPtr->tkwin,
+	    "postscript", "Postscript", configSpecs, 0, (char **)NULL,
+	    (char *)psPtr, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_PostScriptOp --
+ *
+ *	This procedure is invoked to process the Tcl command
+ *	that corresponds to a widget managed by this module.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_OpSpec psOps[] =
+{
+    {"cget", 2, (Blt_Op)CgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "?option value?...",},
+    {"output", 1, (Blt_Op)OutputOp, 3, 0,
+	"?fileName? ?option value?...",},
+};
+
+static int nPsOps = sizeof(psOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PostScriptOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* # arguments */
+    char **argv;		/* Argument list */
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nPsOps, psOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (graphPtr, interp, argc, argv);
+    return result;
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltGraph.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGraph.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGraph.c	(revision 175)
@@ -0,0 +1,2412 @@
+
+/*
+ * bltGraph.c --
+ *
+ *	This module implements a graph widget for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The graph widget was created by Sani Nassif and George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 2) Update manual pages.
+ *
+ * 3) Update comments.
+ *
+ * 5) Surface, contour, and flow graphs
+ *
+ * 7) Arrows for line markers
+ *
+ */
+
+#include "bltGraph.h"
+#include "bltBind.h"
+#include "bltGrElem.h"
+#include "bltSwitch.h"
+#include <X11/Xutil.h>
+
+Blt_Uid bltXAxisUid;
+Blt_Uid bltYAxisUid;
+Blt_Uid bltBarElementUid;
+Blt_Uid bltLineElementUid;
+Blt_Uid bltStripElementUid;
+Blt_Uid bltContourElementUid;
+Blt_Uid bltLineMarkerUid;
+Blt_Uid bltBitmapMarkerUid;
+Blt_Uid bltImageMarkerUid;
+Blt_Uid bltTextMarkerUid;
+Blt_Uid bltPolygonMarkerUid;
+Blt_Uid bltWindowMarkerUid;
+
+extern Tk_CustomOption bltLinePenOption;
+extern Tk_CustomOption bltBarPenOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltBarModeOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltShadowOption;
+
+#define DEF_GRAPH_ASPECT_RATIO		"0.0"
+#define DEF_GRAPH_BAR_BASELINE		"0.0"
+#define DEF_GRAPH_BAR_MODE		"normal"
+#define DEF_GRAPH_BAR_WIDTH		"0.8"
+#define DEF_GRAPH_BACKGROUND		STD_NORMAL_BACKGROUND
+#define DEF_GRAPH_BG_MONO		STD_NORMAL_BG_MONO
+#define DEF_GRAPH_BORDERWIDTH		STD_BORDERWIDTH
+#define DEF_GRAPH_BUFFER_ELEMENTS	"1"
+#define DEF_GRAPH_BUFFER_GRAPH		"1"
+#define DEF_GRAPH_CURSOR		"crosshair"
+#define DEF_GRAPH_FONT			STD_FONT_LARGE
+#define DEF_GRAPH_HALO			"2m"
+#define DEF_GRAPH_HALO_BAR		"0.1i"
+#define DEF_GRAPH_HEIGHT		"4i"
+#define DEF_GRAPH_HIGHLIGHT_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_GRAPH_HIGHLIGHT_BG_MONO	STD_NORMAL_BG_MONO
+#define DEF_GRAPH_HIGHLIGHT_COLOR	RGB_BLACK
+#define DEF_GRAPH_HIGHLIGHT_WIDTH	"2"
+#define DEF_GRAPH_INVERT_XY		"0"
+#define DEF_GRAPH_JUSTIFY		"center"
+#define DEF_GRAPH_MARGIN		"0"
+#define DEF_GRAPH_MARGIN_VAR		(char *)NULL
+#define DEF_GRAPH_PLOT_BACKGROUND		RGB_WHITE
+#define DEF_GRAPH_PLOT_BG_MONO		RGB_WHITE
+#define DEF_GRAPH_PLOT_BW_COLOR		STD_BORDERWIDTH
+#define DEF_GRAPH_PLOT_BW_MONO		"0"
+#define DEF_GRAPH_PLOT_PADX		"8"
+#define DEF_GRAPH_PLOT_PADY		"8"
+#define DEF_GRAPH_PLOT_RELIEF		"sunken"
+#define DEF_GRAPH_RELIEF		"flat"
+#define DEF_GRAPH_SHADOW_COLOR		(char *)NULL
+#define DEF_GRAPH_SHADOW_MONO		(char *)NULL
+#define DEF_GRAPH_SHOW_VALUES		"no"
+#define DEF_GRAPH_TAKE_FOCUS		""
+#define DEF_GRAPH_TITLE			(char *)NULL
+#define DEF_GRAPH_TITLE_COLOR		STD_NORMAL_FOREGROUND
+#define DEF_GRAPH_TITLE_MONO		STD_NORMAL_FG_MONO
+#define DEF_GRAPH_WIDTH			"5i"
+#define DEF_GRAPH_DATA			(char *)NULL
+#define DEF_GRAPH_DATA_COMMAND		(char *)NULL
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_DOUBLE, "-aspect", "aspect", "Aspect",
+	DEF_GRAPH_ASPECT_RATIO, Tk_Offset(Graph, aspect),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_GRAPH_BACKGROUND, Tk_Offset(Graph, border),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-barmode", "barMode", "BarMode",
+	DEF_GRAPH_BAR_MODE, Tk_Offset(Graph, mode),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltBarModeOption},
+    {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
+	DEF_GRAPH_BAR_WIDTH, Tk_Offset(Graph, barWidth), 0},
+    {TK_CONFIG_DOUBLE, "-baseline", "baseline", "Baseline",
+	DEF_GRAPH_BAR_BASELINE, Tk_Offset(Graph, baseline), 0},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-bm", "bottomMargin",
+	(char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_GRAPH_BORDERWIDTH, Tk_Offset(Graph, borderWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-bottommargin", "bottomMargin", "Margin",
+	DEF_GRAPH_MARGIN, Tk_Offset(Graph, bottomMargin.reqSize), 0,
+	&bltDistanceOption},
+    {TK_CONFIG_STRING, "-bottomvariable", "bottomVariable", "BottomVariable",
+	DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, bottomMargin.varName), 
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
+	DEF_GRAPH_BUFFER_ELEMENTS, Tk_Offset(Graph, backingStore),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-buffergraph", "bufferGraph", "BufferGraph",
+	DEF_GRAPH_BUFFER_GRAPH, Tk_Offset(Graph, doubleBuffer),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_STRING, "-data", "data", "Data", 
+        (char *)NULL, Tk_Offset(Graph, data), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-datacommand", "dataCommand", "DataCommand", 
+        (char *)NULL, Tk_Offset(Graph, dataCmd), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_FONT, "-font", "font", "Font",
+	DEF_GRAPH_FONT, Tk_Offset(Graph, titleTextStyle.font), 0},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_GRAPH_TITLE_COLOR, Tk_Offset(Graph, titleTextStyle.color), 
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleTextStyle.color), 
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_CUSTOM, "-halo", "halo", "Halo",
+	DEF_GRAPH_HALO, Tk_Offset(Graph, halo), 0, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+	DEF_GRAPH_HEIGHT, Tk_Offset(Graph, reqHeight), 0, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+	"HighlightBackground",
+	DEF_GRAPH_HIGHLIGHT_BACKGROUND, Tk_Offset(Graph, highlightBgColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+	"HighlightBackground",
+	DEF_GRAPH_HIGHLIGHT_BG_MONO, Tk_Offset(Graph, highlightBgColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+	DEF_GRAPH_HIGHLIGHT_COLOR, Tk_Offset(Graph, highlightColor), 0},
+    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+	"HighlightThickness",
+	DEF_GRAPH_HIGHLIGHT_WIDTH, Tk_Offset(Graph, highlightWidth),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
+	DEF_GRAPH_INVERT_XY, Tk_Offset(Graph, inverted),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+	DEF_GRAPH_JUSTIFY, Tk_Offset(Graph, titleTextStyle.justify),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-leftmargin", "leftMargin", "Margin",
+	DEF_GRAPH_MARGIN, Tk_Offset(Graph, leftMargin.reqSize),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_STRING, "-leftvariable", "leftVariable", "LeftVariable",
+	DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, leftMargin.varName), 
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-lm", "leftMargin", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
+	DEF_GRAPH_PLOT_BG_MONO, Tk_Offset(Graph, plotBg),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
+	DEF_GRAPH_PLOT_BACKGROUND, Tk_Offset(Graph, plotBg),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
+	DEF_GRAPH_PLOT_BW_COLOR, Tk_Offset(Graph, plotBorderWidth),
+	TK_CONFIG_COLOR_ONLY, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
+	DEF_GRAPH_PLOT_BW_MONO, Tk_Offset(Graph, plotBorderWidth),
+	TK_CONFIG_MONO_ONLY, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-plotpadx", "plotPadX", "PlotPad",
+	DEF_GRAPH_PLOT_PADX, Tk_Offset(Graph, padX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-plotpady", "plotPadY", "PlotPad",
+	DEF_GRAPH_PLOT_PADY, Tk_Offset(Graph, padY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
+	DEF_GRAPH_PLOT_RELIEF, Tk_Offset(Graph, plotRelief),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-rightmargin", "rightMargin", "Margin",
+	DEF_GRAPH_MARGIN, Tk_Offset(Graph, rightMargin.reqSize),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_STRING, "-rightvariable", "rightVariable", "RightVariable",
+	DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, rightMargin.varName), 
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-rm", "rightMargin", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_GRAPH_SHADOW_COLOR, Tk_Offset(Graph, titleTextStyle.shadow),
+	TK_CONFIG_COLOR_ONLY, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_GRAPH_SHADOW_MONO, Tk_Offset(Graph, titleTextStyle.shadow),
+	TK_CONFIG_MONO_ONLY, &bltShadowOption},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_GRAPH_TITLE_MONO, Tk_Offset(Graph, titleTextStyle.color), 
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+	DEF_GRAPH_TAKE_FOCUS, Tk_Offset(Graph, takeFocus), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+	(char *)NULL, Tk_Offset(Graph, tile), 
+	TK_CONFIG_NULL_OK, &bltTileOption},
+    {TK_CONFIG_STRING, "-title", "title", "Title",
+	DEF_GRAPH_TITLE, Tk_Offset(Graph, title), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-tm", "topMargin", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-topmargin", "topMargin", "Margin",
+	DEF_GRAPH_MARGIN, Tk_Offset(Graph, topMargin.reqSize),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_STRING, "-topvariable", "topVariable", "TopVariable",
+	DEF_GRAPH_MARGIN_VAR, Tk_Offset(Graph, topMargin.varName), 
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+	DEF_GRAPH_WIDTH, Tk_Offset(Graph, reqWidth),
+	0, &bltDistanceOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToFormat;
+static Blt_SwitchCustom formatSwitch =
+{
+    StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+    char *name;
+    int width, height;
+    int format;
+} SnapData;
+
+enum SnapFormats { FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
+
+static Blt_SwitchSpec snapSwitches[] = 
+{
+    {BLT_SWITCH_INT_POSITIVE, "-width", Blt_Offset(SnapData, width), 0},
+    {BLT_SWITCH_INT_POSITIVE, "-height", Blt_Offset(SnapData, height), 0},
+    {BLT_SWITCH_CUSTOM, "-format", Blt_Offset(SnapData, format), 0, 
+	&formatSwitch},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Tcl_IdleProc DisplayGraph;
+static Tcl_FreeProc DestroyGraph;
+static Tk_EventProc GraphEventProc;
+Tcl_CmdProc Blt_GraphInstCmdProc;
+
+static Blt_BindPickProc PickEntry;
+static Tcl_CmdProc StripchartCmd;
+static Tcl_CmdProc BarchartCmd;
+static Tcl_CmdProc GraphCmd;
+static Tcl_CmdDeleteProc GraphInstCmdDeleteProc;
+static Blt_TileChangedProc TileChangedProc;
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_EventuallyRedrawGraph --
+ *
+ *	Tells the Tk dispatcher to call the graph display routine at
+ *	the next idle point.  This request is made only if the window
+ *	is displayed and no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ *	The window is eventually redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_EventuallyRedrawGraph(graphPtr)
+    Graph *graphPtr;		/* Graph widget record */
+{
+    if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
+	Tcl_DoWhenIdle(DisplayGraph, graphPtr);
+	graphPtr->flags |= REDRAW_PENDING;
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GraphEventProc --
+ *
+ *	This procedure is invoked by the Tk dispatcher for various
+ *	events on graphs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the window gets deleted, internal structures get
+ *	cleaned up.  When it gets exposed, the graph is eventually
+ *	redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+GraphEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Graph widget record */
+    register XEvent *eventPtr;	/* Event which triggered call to routine */
+{
+    Graph *graphPtr = clientData;
+
+    if (eventPtr->type == Expose) {
+	if (eventPtr->xexpose.count == 0) {
+	    graphPtr->flags |= REDRAW_WORLD;
+	    Blt_EventuallyRedrawGraph(graphPtr);
+	}
+    } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+	if (eventPtr->xfocus.detail != NotifyInferior) {
+	    if (eventPtr->type == FocusIn) {
+		graphPtr->flags |= GRAPH_FOCUS;
+	    } else {
+		graphPtr->flags &= ~GRAPH_FOCUS;
+	    }
+	    graphPtr->flags |= REDRAW_WORLD;
+	    Blt_EventuallyRedrawGraph(graphPtr);
+	}
+    } else if (eventPtr->type == DestroyNotify) {
+	if (graphPtr->tkwin != NULL) {
+	    Blt_DeleteWindowInstanceData(graphPtr->tkwin);
+	    graphPtr->tkwin = NULL;
+	    Tcl_DeleteCommandFromToken(graphPtr->interp, graphPtr->cmdToken);
+	}
+	if (graphPtr->flags & REDRAW_PENDING) {
+	    Tcl_CancelIdleCall(DisplayGraph, graphPtr);
+	}
+	Tcl_EventuallyFree(graphPtr, DestroyGraph);
+    } else if (eventPtr->type == ConfigureNotify) {
+	graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GraphInstCmdDeleteProc --
+ *
+ *	This procedure is invoked when a widget command is deleted.  If
+ *	the widget isn't already in the process of being destroyed,
+ *	this command destroys it.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The widget is destroyed.
+ *
+ *---------------------------------------------------------------------- */
+static void
+GraphInstCmdDeleteProc(clientData)
+    ClientData clientData;	/* Pointer to widget record. */
+{
+    Graph *graphPtr = clientData;
+
+    if (graphPtr->tkwin != NULL) {	/* NULL indicates window has
+					 * already been destroyed. */
+	Tk_Window tkwin;
+
+	tkwin = graphPtr->tkwin;
+	graphPtr->tkwin = NULL;
+#ifdef ITCL_NAMESPACES
+	Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+	Blt_DeleteWindowInstanceData(tkwin);
+	Tk_DestroyWindow(tkwin);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ *	Rebuilds the designated GC with the new tile pixmap.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+    ClientData clientData;
+    Blt_Tile tile;		/* Not used. */
+{
+    Graph *graphPtr = clientData;
+
+    if (graphPtr->tkwin != NULL) {
+	graphPtr->flags |= REDRAW_WORLD;
+	Blt_EventuallyRedrawGraph(graphPtr);
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * AdjustAxisPointers --
+ *
+ *	Sets the axis pointers according to whether the axis is
+ *	inverted on not.  The axis sites are also reset.
+ *
+ * Results:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+AdjustAxisPointers(graphPtr)
+    Graph *graphPtr;		/* Graph widget record */
+{
+    if (graphPtr->inverted) {
+	graphPtr->leftMargin.axes = graphPtr->axisChain[0];
+	graphPtr->bottomMargin.axes = graphPtr->axisChain[1];
+	graphPtr->rightMargin.axes = graphPtr->axisChain[2];
+	graphPtr->topMargin.axes = graphPtr->axisChain[3];
+    } else {
+	graphPtr->leftMargin.axes = graphPtr->axisChain[1];
+	graphPtr->bottomMargin.axes = graphPtr->axisChain[0];
+	graphPtr->rightMargin.axes = graphPtr->axisChain[3];
+	graphPtr->topMargin.axes = graphPtr->axisChain[2];
+    }
+}
+
+static int
+InitPens(graphPtr)
+    Graph *graphPtr;
+{
+    Blt_InitHashTable(&graphPtr->penTable, BLT_STRING_KEYS);
+    if (Blt_CreatePen(graphPtr, "activeLine", bltLineElementUid, 0, 
+	      (char **)NULL) == NULL) {
+  	return TCL_ERROR;
+    }
+    if (Blt_CreatePen(graphPtr, "activeBar", bltBarElementUid, 0, 
+	      (char **)NULL) == NULL) {
+  	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GraphTags --
+ *
+ *	Sets the binding tags for a graph object. This routine is
+ *	called by Tk when an event occurs in the graph.  It fills
+ *	an array of pointers with bind tag addresses.
+ *
+ *	The object addresses are strings hashed in one of two tag
+ *	tables: one for elements and the another for markers.  Note
+ *	that there's only one binding table for elements and markers.
+ *	[We don't want to trigger both a marker and element bind
+ *	command for the same event.]  But we don't want a marker and
+ *	element with the same tag name to activate the others
+ *	bindings. A tag "all" for markers should mean all markers, not
+ *	all markers and elements.  As a result, element and marker
+ *	tags are stored in separate hash tables, which means we can't
+ *	generate the same tag address for both an elements and marker,
+ *	even if they have the same name.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	This information will be used by the binding code in bltUtil.c
+ *	to determine what graph objects match the current event.  The
+ *	tags are placed in tagArr and *nTagsPtr is set with the
+ *	number of tags found.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+void
+Blt_GraphTags(table, object, context, list)
+    Blt_BindTable table;
+    ClientData object;
+    ClientData context;		/* Not used. */
+    Blt_List list;
+{
+    Element *elemPtr;
+    MakeTagProc *tagProc;
+    Graph *graphPtr;
+
+    graphPtr = (Graph *)Blt_GetBindingData(table);
+    /* 
+     * Trick:   Markers, elements, and axes have the same first few
+     *		fields in their structures, such as "type", "name", or
+     *		"tags".  This is so we can look at graph objects
+     *		interchangably.  It doesn't matter what we cast the
+     *		object to.  
+     */
+    elemPtr = (Element *)object;
+
+    if ((elemPtr->classUid == bltLineElementUid) ||
+	(elemPtr->classUid == bltStripElementUid) ||
+	(elemPtr->classUid == bltBarElementUid)) {
+	tagProc = Blt_MakeElementTag;
+    } else if ((elemPtr->classUid == bltXAxisUid) ||
+	       (elemPtr->classUid == bltYAxisUid)) {
+	tagProc = Blt_MakeAxisTag;
+    } else {
+	tagProc = Blt_MakeMarkerTag;
+    }
+    /*
+     * Always add the name of the object to the tag array.
+     */
+    Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->name), 0);
+    Blt_ListAppend(list, (*tagProc) (graphPtr, elemPtr->classUid), 0);
+    if (elemPtr->tags != NULL) {
+	register char **p;
+
+	for (p = elemPtr->tags; *p != NULL; p++) {
+	    Blt_ListAppend(list, (*tagProc) (graphPtr, *p), 0);
+	}
+    }
+}
+
+/*
+ * Find the closest point from the set of displayed elements,
+ * searching the display list from back to front.  That way, if
+ * the points from two different elements overlay each other exactly,
+ * the one that's on top (visible) is picked.
+ */
+/*ARGSUSED*/
+static ClientData
+PickEntry(clientData, x, y, contextPtr)
+    ClientData clientData;
+    int x, y;
+    ClientData *contextPtr;	/* Not used. */
+{
+    Graph *graphPtr = clientData;
+    Blt_ChainLink *linkPtr;
+    Element *elemPtr;
+    Marker *markerPtr;
+    Extents2D exts;
+
+    if (graphPtr->flags & MAP_ALL) {
+	return NULL;		/* Can't pick anything until the next
+				 * redraw occurs. */
+    }
+    Blt_GraphExtents(graphPtr, &exts);
+
+    if ((x > exts.right) || (x < exts.left) || (y > exts.bottom) ||
+	(y < exts.top)) {
+	/* 
+	 * Sample coordinate is in one of the graph margins.  Can only
+	 * pick an axis.
+	 */
+	return Blt_NearestAxis(graphPtr, x, y);
+    }
+
+    /* 
+     * From top-to-bottom check:
+     *	1. markers drawn on top (-under false).
+     *	2. elements using its display list back to front.
+     *  3. markers drawn under element (-under true).
+     */
+    markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, FALSE);
+    if (markerPtr != NULL) {
+	return markerPtr;	/* Found a marker (-under false). */
+    }
+    {
+	ClosestSearch search;
+
+	search.along = SEARCH_BOTH;
+	search.halo = graphPtr->halo + 1;
+	search.index = -1;
+	search.x = x;
+	search.y = y;
+	search.dist = (double)(search.halo + 1);
+	search.mode = SEARCH_AUTO;
+	
+	for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+	     linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+	    elemPtr = Blt_ChainGetValue(linkPtr);
+	    if ((elemPtr->flags & MAP_ITEM) ||
+		(Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+		(Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+		continue;
+	    }
+	    if ((!elemPtr->hidden) && (elemPtr->state == STATE_NORMAL)) {
+		(*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+	    }
+	}
+	if (search.dist <= (double)search.halo) {
+	    return search.elemPtr;	/* Found an element within the
+					 * minimum halo distance. */
+	}
+    }
+    markerPtr = (Marker *)Blt_NearestMarker(graphPtr, x, y, TRUE);
+    if (markerPtr != NULL) {
+	return markerPtr;	/* Found a marker (-under true) */
+    }
+    return NULL;		/* Nothing found. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureGraph --
+ *
+ *	Allocates resources for the graph.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for graphPtr;  old resources get freed, if there
+ *	were any.  The graph is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureGraph(graphPtr)
+    Graph *graphPtr;		/* Graph widget record */
+{
+    XColor *colorPtr;
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    /* Don't allow negative bar widths. Reset to an arbitrary value (0.1) */
+    if (graphPtr->barWidth <= 0.0) {
+	graphPtr->barWidth = 0.1;
+    }
+    graphPtr->inset = graphPtr->borderWidth + graphPtr->highlightWidth + 1;
+    if ((graphPtr->reqHeight != Tk_ReqHeight(graphPtr->tkwin)) ||
+	(graphPtr->reqWidth != Tk_ReqWidth(graphPtr->tkwin))) {
+	Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
+	    graphPtr->reqHeight);
+    }
+    Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
+    colorPtr = Tk_3DBorderColor(graphPtr->border);
+
+    if (graphPtr->title != NULL) {
+	int w, h;
+
+	Blt_GetTextExtents(&graphPtr->titleTextStyle, graphPtr->title, &w, &h);
+	graphPtr->titleTextStyle.height = h + 10;
+    } else {
+	graphPtr->titleTextStyle.width = graphPtr->titleTextStyle.height = 0;
+    }
+
+    /*
+     * Create GCs for interior and exterior regions, and a background
+     * GC for clearing the margins with XFillRectangle
+     */
+
+    /* Margin GC */
+
+    gcValues.foreground = graphPtr->titleTextStyle.color->pixel;
+    gcValues.background = colorPtr->pixel;
+    gcMask = (GCForeground | GCBackground);
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (graphPtr->drawGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+    }
+    graphPtr->drawGC = newGC;
+
+    /* Plot fill GC (Background = Foreground) */
+
+    gcValues.foreground = graphPtr->plotBg->pixel;
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (graphPtr->plotFillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
+    }
+    graphPtr->plotFillGC = newGC;
+
+    /* Margin fill GC (Background = Foreground) */
+
+    gcValues.foreground = colorPtr->pixel;
+    gcValues.background = graphPtr->titleTextStyle.color->pixel;
+    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+    if (graphPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
+    }
+    graphPtr->fillGC = newGC;
+    if (graphPtr->tile != NULL) {
+	Blt_SetTileChangedProc(graphPtr->tile, TileChangedProc, graphPtr);
+    }
+
+    Blt_ResetTextStyle(graphPtr->tkwin, &graphPtr->titleTextStyle);
+
+    if (Blt_ConfigModified(configSpecs, "-invertxy", (char *)NULL)) {
+
+	/*
+	 * If the -inverted option changed, we need to readjust the pointers
+	 * to the axes and recompute the their scales.
+	 */
+
+	AdjustAxisPointers(graphPtr);
+	graphPtr->flags |= RESET_AXES;
+    }
+    if ((!graphPtr->backingStore) && (graphPtr->backPixmap != None)) {
+
+	/*
+	 * Free the pixmap if we're not buffering the display of elements
+	 * anymore.
+	 */
+
+	Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+	graphPtr->backPixmap = None;
+    }
+    /*
+     * Reconfigure the crosshairs, just in case the background color of
+     * the plotarea has been changed.
+     */
+    Blt_ConfigureCrosshairs(graphPtr);
+
+    /*
+     *  Update the layout of the graph (and redraw the elements) if
+     *  any of the following graph options which affect the size of
+     *	the plotting area has changed.
+     *
+     *	    -aspect
+     *      -borderwidth, -plotborderwidth
+     *	    -font, -title
+     *	    -width, -height
+     *	    -invertxy
+     *	    -bottommargin, -leftmargin, -rightmargin, -topmargin,
+     *	    -barmode, -barwidth
+     */
+    if (Blt_ConfigModified(configSpecs, "-invertxy", "-title", "-font",
+	    "-*margin", "-*width", "-height", "-barmode", "-*pad*", "-aspect",
+	    (char *)NULL)) {
+	graphPtr->flags |= RESET_WORLD;
+    }
+    if (Blt_ConfigModified(configSpecs, "-plotbackground", (char *)NULL)) {
+	graphPtr->flags |= REDRAW_BACKING_STORE;
+    }
+    graphPtr->flags |= REDRAW_WORLD;
+    Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyGraph --
+ *
+ *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ *	to clean up the internal structure of a graph at a safe time
+ *	(when no-one is using it anymore).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyGraph(dataPtr)
+    DestroyData dataPtr;
+{
+    Graph *graphPtr = (Graph *)dataPtr;
+
+    Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
+    /*
+     * Destroy the individual components of the graph: elements, markers,
+     * X and Y axes, legend, display lists etc.
+     */
+    Blt_DestroyMarkers(graphPtr);
+    Blt_DestroyElements(graphPtr);
+
+    Blt_DestroyAxes(graphPtr);
+    Blt_DestroyPens(graphPtr);
+
+    if (graphPtr->legend != NULL) {
+	Blt_DestroyLegend(graphPtr);
+    }
+    if (graphPtr->postscript != NULL) {
+	Blt_DestroyPostScript(graphPtr);
+    }
+    if (graphPtr->crosshairs != NULL) {
+	Blt_DestroyCrosshairs(graphPtr);
+    }
+    if (graphPtr->gridPtr != NULL) {
+	Blt_DestroyGrid(graphPtr);
+    }
+    if (graphPtr->bindTable != NULL) {
+	Blt_DestroyBindingTable(graphPtr->bindTable);
+    }
+
+    /* Release allocated X resources and memory. */
+    if (graphPtr->drawGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+    }
+    if (graphPtr->fillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->fillGC);
+    }
+    if (graphPtr->plotFillGC != NULL) {
+	Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
+    }
+    Blt_FreeTextStyle(graphPtr->display, &graphPtr->titleTextStyle);
+    if (graphPtr->backPixmap != None) {
+	Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+    }
+    if (graphPtr->freqArr != NULL) {
+	Blt_Free(graphPtr->freqArr);
+    }
+    if (graphPtr->nStacks > 0) {
+	Blt_DeleteHashTable(&graphPtr->freqTable);
+    }
+    if (graphPtr->tile != NULL) {
+	Blt_FreeTile(graphPtr->tile);
+    }
+    Blt_Free(graphPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateGraph --
+ *
+ *	This procedure creates and initializes a new widget.
+ *
+ * Results:
+ *	The return value is a pointer to a structure describing
+ *	the new widget.  If an error occurred, then the return
+ *	value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ *	Memory is allocated, a Tk_Window is created, etc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Graph *
+CreateGraph(interp, argc, argv, classUid)
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    Blt_Uid classUid;
+{
+    Graph *graphPtr;
+    Tk_Window tkwin;
+
+    tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1], 
+	    (char *)NULL);
+    if (tkwin == NULL) {
+	return NULL;
+    }
+    graphPtr = Blt_Calloc(1, sizeof(Graph));
+    assert(graphPtr);
+
+    /* Initialize the graph data structure. */
+
+    graphPtr->tkwin = tkwin;
+    graphPtr->display = Tk_Display(tkwin);
+    graphPtr->interp = interp;
+    graphPtr->classUid = classUid;
+    graphPtr->backingStore = TRUE;
+    graphPtr->doubleBuffer = TRUE;
+    graphPtr->highlightWidth = 2;
+    graphPtr->plotRelief = TK_RELIEF_SUNKEN;
+    graphPtr->relief = TK_RELIEF_FLAT;
+    graphPtr->flags = (RESET_WORLD);
+    graphPtr->nextMarkerId = 1;
+    graphPtr->padLeft = graphPtr->padRight = 8;
+    graphPtr->padTop = graphPtr->padBottom = 8;
+    graphPtr->bottomMargin.site = MARGIN_BOTTOM;
+    graphPtr->leftMargin.site = MARGIN_LEFT;
+    graphPtr->topMargin.site = MARGIN_TOP;
+    graphPtr->rightMargin.site = MARGIN_RIGHT;
+    Blt_InitTextStyle(&graphPtr->titleTextStyle);
+
+    Blt_InitHashTable(&graphPtr->axes.table, BLT_STRING_KEYS);
+    Blt_InitHashTable(&graphPtr->axes.tagTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&graphPtr->elements.table, BLT_STRING_KEYS);
+    Blt_InitHashTable(&graphPtr->elements.tagTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&graphPtr->markers.table, BLT_STRING_KEYS);
+    Blt_InitHashTable(&graphPtr->markers.tagTable, BLT_STRING_KEYS);
+    graphPtr->elements.displayList = Blt_ChainCreate();
+    graphPtr->markers.displayList = Blt_ChainCreate();
+    graphPtr->axes.displayList = Blt_ChainCreate();
+
+    if (classUid == bltLineElementUid) {
+	Tk_SetClass(tkwin, "Graph");
+    } else if (classUid == bltBarElementUid) {
+	Tk_SetClass(tkwin, "Barchart");
+    } else if (classUid == bltStripElementUid) {
+	Tk_SetClass(tkwin, "Stripchart");
+    }
+    Blt_SetWindowInstanceData(tkwin, graphPtr);
+
+    if (InitPens(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 2, argv + 2,
+	    (char *)graphPtr, 0) != TCL_OK) {
+	goto error;
+    }
+    if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    AdjustAxisPointers(graphPtr);
+
+    if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    if (Blt_CreateLegend(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    if (Blt_CreateGrid(graphPtr) != TCL_OK) {
+	goto error;
+    }
+    Tk_CreateEventHandler(graphPtr->tkwin, 
+	ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc, 
+	graphPtr);
+
+    graphPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], 
+	Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
+#ifdef ITCL_NAMESPACES
+    Itk_SetWidgetCommand(graphPtr->tkwin, graphPtr->cmdToken);
+#endif
+    ConfigureGraph(graphPtr);
+    graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr, 
+	PickEntry, Blt_GraphTags);
+    return graphPtr;
+
+ error:
+    DestroyGraph((DestroyData)graphPtr);
+    return NULL;
+}
+
+
+/* Widget sub-commands */
+
+/*ARGSUSED*/
+static int
+XAxisOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    int margin;
+
+    margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
+    return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+X2AxisOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    int margin;
+
+    margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
+    return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+YAxisOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    int margin;
+
+    margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
+    return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+Y2AxisOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    int margin;
+
+    margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
+    return Blt_AxisOp(graphPtr, margin, argc, argv);
+}
+
+/*ARGSUSED*/
+static int
+BarOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    return Blt_ElementOp(graphPtr, interp, argc, argv, bltBarElementUid);
+}
+
+/*ARGSUSED*/
+static int
+LineOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    return Blt_ElementOp(graphPtr, interp, argc, argv, bltLineElementUid);
+}
+
+/*ARGSUSED*/
+static int
+ElementOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    return Blt_ElementOp(graphPtr, interp, argc, argv, graphPtr->classUid);
+}
+
+static int
+ConfigureOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int flags;
+
+    flags = TK_CONFIG_ARGV_ONLY;
+    if (argc == 2) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+	    (char *)graphPtr, (char *)NULL, flags);
+    } else if (argc == 3) {
+	return Tk_ConfigureInfo(interp, graphPtr->tkwin, configSpecs,
+	    (char *)graphPtr, argv[2], flags);
+    } else {
+	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, configSpecs, argc - 2,
+		argv + 2, (char *)graphPtr, flags) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	ConfigureGraph(graphPtr);
+	return TCL_OK;
+    }
+}
+
+/* ARGSUSED*/
+static int
+CgetOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    return Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
+	(char *)graphPtr, argv[2], 0);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExtentsOp --
+ *
+ *	Reports the size of one of several items within the graph.
+ *	The following are valid items:
+ *
+ *	  "bottommargin"	Height of the bottom margin
+ *	  "leftmargin"		Width of the left margin
+ *	  "legend"		x y w h of the legend
+ *	  "plotarea"		x y w h of the plotarea
+ *	  "plotheight"		Height of the plot area
+ *	  "rightmargin"		Width of the right margin
+ *	  "topmargin"		Height of the top margin
+ *        "plotwidth"		Width of the plot area
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+ExtentsOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    char c;
+    unsigned int length;
+    char string[200];
+
+    c = argv[2][0];
+    length = strlen(argv[2]);
+    if ((c == 'p') && (length > 4) &&
+	(strncmp("plotheight", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottom - graphPtr->top + 1),
+	    TCL_VOLATILE);
+    } else if ((c == 'p') && (length > 4) &&
+	(strncmp("plotwidth", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->right - graphPtr->left + 1),
+	    TCL_VOLATILE);
+    } else if ((c == 'p') && (length > 4) &&
+	(strncmp("plotarea", argv[2], length) == 0)) {
+	sprintf(string, "%d %d %d %d", graphPtr->left, graphPtr->top,
+	    graphPtr->right - graphPtr->left + 1,
+	    graphPtr->bottom - graphPtr->top + 1);
+	Tcl_SetResult(interp, string, TCL_VOLATILE);
+    } else if ((c == 'l') && (length > 2) &&
+	(strncmp("legend", argv[2], length) == 0)) {
+	sprintf(string, "%d %d %d %d", Blt_LegendX(graphPtr->legend), 
+		Blt_LegendY(graphPtr->legend), 
+		Blt_LegendWidth(graphPtr->legend), 
+		Blt_LegendHeight(graphPtr->legend));
+	Tcl_SetResult(interp, string, TCL_VOLATILE);
+    } else if ((c == 'l') && (length > 2) &&
+	(strncmp("leftmargin", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->leftMargin.width), 
+		      TCL_VOLATILE);
+    } else if ((c == 'r') && (length > 1) &&
+	(strncmp("rightmargin", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->rightMargin.width), 
+		      TCL_VOLATILE);
+    } else if ((c == 't') && (length > 1) &&
+	(strncmp("topmargin", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->topMargin.height), TCL_VOLATILE);
+    } else if ((c == 'b') && (length > 1) &&
+	(strncmp("bottommargin", argv[2], length) == 0)) {
+	Tcl_SetResult(interp, Blt_Itoa(graphPtr->bottomMargin.height), 
+		      TCL_VOLATILE);
+    } else {
+	Tcl_AppendResult(interp, "bad extent item \"", argv[2],
+	    "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
+topmargin, bottommargin, plotarea, or legend", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InsideOp --
+ *
+ *	Returns true of false whether the given point is inside
+ *	the plotting area (defined by left,bottom right, top).
+ *
+ * Results:
+ *	Always returns TCL_OK.  interp->result will contain
+ *	the boolean string representation.
+ *
+ *--------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+InsideOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int x, y;
+    Extents2D exts;
+    int result;
+
+    if (Tk_GetPixels(interp, graphPtr->tkwin, argv[2], &x) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &y) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_GraphExtents(graphPtr, &exts);
+    result = PointInRegion(&exts, x, y);
+    Blt_SetBooleanResult(interp, result);
+    return TCL_OK;
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * InvtransformOp --
+ *
+ *	This procedure returns a list of the graph coordinate
+ *	values corresponding with the given window X and Y
+ *	coordinate positions.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred while
+ *	parsing the window positions, TCL_ERROR is returned, and
+ *	interp->result will contain the error message.  Otherwise
+ *	interp->result will contain a Tcl list of the x and y
+ *	coordinates.
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvtransformOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    double x, y;
+    Point2D point;
+    Axis2D axes;
+
+    if (Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK ||
+	Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    /* Perform the reverse transformation, converting from window
+     * coordinates to graph data coordinates.  Note that the point is
+     * always mapped to the bottom and left axes (which may not be
+     * what the user wants).  */
+
+    /*  Pick the first pair of axes */
+    axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+    axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+    point = Blt_InvMap2D(graphPtr, x, y, &axes);
+
+    Tcl_AppendElement(interp, Blt_Dtoa(interp, point.x));
+    Tcl_AppendElement(interp, Blt_Dtoa(interp, point.y));
+    return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ *	This procedure returns a list of the window coordinates
+ *	corresponding with the given graph x and y coordinates.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the list of the graph coordinates. If an error occurred
+ *	while parsing the window positions, TCL_ERROR is returned,
+ *	then interp->result will contain an error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    double x, y;
+    Point2D point;
+    Axis2D axes;
+
+    if ((Tcl_ExprDouble(interp, argv[2], &x) != TCL_OK) ||
+	(Tcl_ExprDouble(interp, argv[3], &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    /*
+     * Perform the transformation from window to graph coordinates.
+     * Note that the points are always mapped onto the bottom and left
+     * axes (which may not be the what the user wants).
+     */
+    axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+    axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+
+    point = Blt_Map2D(graphPtr, x, y, &axes);
+    Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.x)));
+    Tcl_AppendElement(interp, Blt_Itoa(ROUND(point.y)));
+    return TCL_OK;
+}
+
+#ifndef NO_PRINTER
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * Print1Op --
+ *
+ *	Prints the equivalent of a screen snapshot of the graph
+ *	to the designated printer.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred
+ *	TCL_ERROR is returned and interp->result will contain an
+ *	error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+Print1Op(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int noBackingStore = 0;
+    BITMAPINFO info;
+    void *data;
+    TkWinDCState state;
+    TkWinBitmap bd;
+    DIBSECTION ds;
+    Drawable drawable;
+    HBITMAP hBitmap;
+    HDC hDC;
+    DOCINFO di;
+    double pageWidth, pageHeight;
+    int result;
+    double scale, sx, sy;
+    int jobId;
+
+    graphPtr->width = Tk_Width(graphPtr->tkwin);
+    graphPtr->height = Tk_Height(graphPtr->tkwin);
+    if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+	graphPtr->width = graphPtr->reqWidth;
+    }
+    if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+	graphPtr->height = graphPtr->reqHeight;
+    }
+    if (argc == 2) {
+	result = Blt_PrintDialog(interp, &drawable);
+	if (result == TCL_ERROR) {
+	    return TCL_ERROR;
+	}
+	if (result == TCL_RETURN) {
+	    return TCL_OK;
+	}
+    } else {
+	if (Blt_GetOpenPrinter(interp, argv[2], &drawable) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    /*  
+     * This is a taken from Blt_SnapPhoto.  The difference is that
+     * here we're using the DIBSection directly, without converting 
+     * the section into a ColorImage.  
+     */
+    ZeroMemory(&info, sizeof(info));
+    info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    info.bmiHeader.biWidth = graphPtr->width;
+    info.bmiHeader.biHeight = graphPtr->height;
+    info.bmiHeader.biPlanes = 1;
+    info.bmiHeader.biBitCount = 32;
+    info.bmiHeader.biCompression = BI_RGB;
+    hDC = TkWinGetDrawableDC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+		&state);
+    hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+    TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
+    
+    /*
+     * Create our own drawable by hand using the DIB we just created.
+     * We'll then draw into it using the standard drawing functions.
+     */
+    bd.type = TWD_BITMAP;
+    bd.handle = hBitmap;
+    bd.colormap = DefaultColormap(graphPtr->display, 
+	DefaultScreen(graphPtr->display));
+    bd.depth = Tk_Depth(graphPtr->tkwin);
+    
+    graphPtr->flags |= RESET_WORLD;
+    Blt_DrawGraph(graphPtr, (Drawable)&bd, noBackingStore);
+
+    /*
+     * Now that the DIB contains the image of the graph, get the the
+     * data bits and write them to the printer device, stretching the
+     * image to the fit the printer's resolution.
+     */
+    result = TCL_ERROR;
+    if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+	Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
+	    (char *)NULL);
+	goto done;
+    }
+    hDC = ((TkWinDC *) drawable)->hdc;
+    /* Get the resolution of the printer device. */
+    sx = (double)GetDeviceCaps(hDC, HORZRES) / (double)graphPtr->width;
+    sy = (double)GetDeviceCaps(hDC, VERTRES) / (double)graphPtr->height;
+    scale = MIN(sx, sy);
+    pageWidth = scale * graphPtr->width;
+    pageHeight = scale * graphPtr->height;
+
+    ZeroMemory(&di, sizeof(di));
+    di.cbSize = sizeof(di);
+    di.lpszDocName = "Graph Contents";
+    jobId = StartDoc(hDC, &di);
+    if (jobId <= 0) {
+	Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
+	    (char *)NULL);
+	goto done;
+    }
+    if (StartPage(hDC) <= 0) {
+	Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
+	    (char *)NULL);
+	goto done;
+    }
+    StretchDIBits(hDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0, 
+	graphPtr->width, graphPtr->height, ds.dsBm.bmBits, 
+	(LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
+    EndPage(hDC);
+    EndDoc(hDC);
+    result = TCL_OK;
+  done:
+    DeleteBitmap(hBitmap);
+    return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * Print2Op --
+ *
+ *	Prints directly to the designated printer device.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred,
+ *	TCL_ERROR is returned and interp->result will contain an
+ *	error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+Print2Op(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Drawable drawable;
+    int noBackingStore = 0;
+    int result;
+
+    graphPtr->width = Tk_Width(graphPtr->tkwin);
+    graphPtr->height = Tk_Height(graphPtr->tkwin);
+    if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+	graphPtr->width = graphPtr->reqWidth;
+    }
+    if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+	graphPtr->height = graphPtr->reqHeight;
+    }
+    if (argc == 2) {
+	result = Blt_PrintDialog(interp, &drawable);
+	if (result == TCL_ERROR) {
+	    return TCL_ERROR;
+	}
+	if (result == TCL_RETURN) {
+	    return TCL_OK;
+	}
+    } else {
+	result = Blt_GetOpenPrinter(interp, argv[2], &drawable);
+    }
+    if (result == TCL_OK) {
+	int oldMode;
+	HDC hDC;
+	double xRatio, yRatio;
+	TkWinDC *drawPtr;
+	double vportWidth, vportHeight; 
+
+	drawPtr = (TkWinDC *) drawable;
+	hDC = drawPtr->hdc;
+	Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
+	oldMode = SetMapMode(hDC, MM_ISOTROPIC);
+	if (oldMode == 0) {
+	    Tcl_AppendResult(interp, "can't set mode for printer DC: ",
+		Blt_LastError(), (char *)NULL);
+	    return TCL_ERROR;
+	}
+	vportWidth = graphPtr->width * xRatio;
+	vportHeight = graphPtr->height * yRatio;
+	SetViewportExtEx(hDC, ROUND(vportWidth), ROUND(vportHeight), NULL);
+	SetWindowExtEx(hDC, graphPtr->width, graphPtr->height, NULL);
+
+	Blt_StartPrintJob(interp, drawable);
+	graphPtr->flags |= RESET_WORLD;
+	Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+	Blt_EndPrintJob(interp, drawable);
+    }
+    return result;
+}
+
+#endif /* NO_PRINTER */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(clientData, interp, switchName, string, record, offset)
+    ClientData clientData;	/* Contains a pointer to the tabset containing
+				 * this image. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    char *switchName;		/* Not used. */
+    char *string;		/* String representation */
+    char *record;		/* Structure record */
+    int offset;			/* Offset to field in structure */
+{
+    int *formatPtr = (int *)(record + offset);
+    char c;
+
+    c = string[0];
+    if ((c == 'p') && (strcmp(string, "photo") == 0)) {
+	*formatPtr = FORMAT_PHOTO;
+#ifdef WIN32
+    } else if ((c == 'e') && (strcmp(string, "emf") == 0)) {
+	*formatPtr = FORMAT_EMF;
+    } else if ((c == 'w') && (strcmp(string, "wmf") == 0)) {
+	*formatPtr = FORMAT_WMF;
+#endif /* WIN32 */
+    } else {
+#ifdef WIN32
+	Tcl_AppendResult(interp, "bad format \"", string, 
+		 "\": should be photo, emf, or wmf.", (char *)NULL);
+#else
+	Tcl_AppendResult(interp, "bad format \"", string, 
+		 "\": should be photo.", (char *)NULL);
+#endif /* WIN32 */
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+#ifdef WIN32
+static int InitMetaFileHeader(
+    Tk_Window tkwin,
+    int width, int height,
+    APMHEADER *mfhPtr)
+{
+    unsigned int *p;
+    unsigned int sum;
+    Screen *screen;
+#define MM_INCH		25.4
+    double dpiX, dpiY;
+
+    mfhPtr->key = 0x9ac6cdd7L;
+    mfhPtr->hmf = 0;
+    mfhPtr->inch = 1440;
+
+    screen = Tk_Screen(tkwin);
+    dpiX = (WidthOfScreen(screen) * MM_INCH) / WidthMMOfScreen(screen);
+    dpiY = (HeightOfScreen(screen) * MM_INCH) / HeightMMOfScreen(screen);
+
+    mfhPtr->bbox.Left = mfhPtr->bbox.Top = 0;
+    mfhPtr->bbox.Bottom = (SHORT)((width * 1440)/ dpiX);
+    mfhPtr->bbox.Right = (SHORT)((height * 1440) / dpiY);
+    mfhPtr->reserved = 0;
+    sum = 0;
+    for (p = (unsigned int *)mfhPtr; 
+	 p < (unsigned int *)&(mfhPtr->checksum); p++) {
+	sum ^= *p;
+    }
+    mfhPtr->checksum = sum;
+    return TCL_OK;
+}
+
+static int
+CreateAPMetaFile(
+    Tcl_Interp *interp,
+    HANDLE hMetaFile,
+    HDC hDC,
+    APMHEADER *mfhPtr,
+    char *fileName)
+{
+    HANDLE hFile;
+    HANDLE hMem;
+    LPVOID buffer;
+    int result;
+    DWORD count, nBytes;
+
+    result = TCL_ERROR;
+    hMem = NULL;
+    hFile = CreateFile(
+       fileName,	/* File path */
+       GENERIC_WRITE,	/* Access mode */
+       0,		/* No sharing. */
+       NULL,		/* Security attributes */
+       CREATE_ALWAYS,	/* Overwrite any existing file */
+       FILE_ATTRIBUTE_NORMAL,
+       NULL);			/* No template file */
+    if (hFile == INVALID_HANDLE_VALUE) {
+	Tcl_AppendResult(interp, "can't create metafile \"", fileName, 
+		"\":", Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((!WriteFile(hFile, (LPVOID)mfhPtr, sizeof(APMHEADER), &count, 
+		NULL)) || (count != sizeof(APMHEADER))) {
+	Tcl_AppendResult(interp, "can't create metafile header to \"", 
+			 fileName, "\":", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
+    hMem = GlobalAlloc(GHND, nBytes);
+    if (hMem == NULL) {
+	Tcl_AppendResult(interp, "can't create allocate global memory:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    buffer = (LPVOID)GlobalLock(hMem);
+    if (!GetWinMetaFileBits(hMetaFile, nBytes, buffer, MM_ANISOTROPIC, hDC)) {
+	Tcl_AppendResult(interp, "can't get metafile bits:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    if ((!WriteFile(hFile, buffer, nBytes, &count, NULL)) ||
+	(count != nBytes)) {
+	Tcl_AppendResult(interp, "can't write metafile bits:", 
+		Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    result = TCL_OK;
+ error:
+    CloseHandle(hFile);
+    if (hMem != NULL) {
+	GlobalUnlock(hMem);
+	GlobalFree(hMem);
+    }
+    return result;
+}
+#endif /*WIN32*/
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ *	Snaps a picture of the graph and stores it in the specified image
+ *
+ * Results:
+ *	Returns a standard Tcl result.  interp->result contains
+ *	the list of the graph coordinates. If an error occurred
+ *	while parsing the window positions, TCL_ERROR is returned,
+ *	then interp->result will contain an error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SnapOp(graphPtr, interp, argc, argv)
+    Graph *graphPtr;		/* Graph widget record */
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int result;
+    Pixmap drawable;
+    int noBackingStore = 0;
+    register int i;
+    SnapData data;
+
+    /* .g snap ?switches? name */
+    data.height = Tk_Height(graphPtr->tkwin);
+    data.width = Tk_Width(graphPtr->tkwin);
+    data.format = FORMAT_PHOTO;
+    /* Process switches  */
+    i = Blt_ProcessSwitches(interp, snapSwitches, argc - 2, argv + 2, 
+	    (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
+    if (i < 0) {
+	return TCL_ERROR;
+    }
+    i += 2;
+    if (i >= argc) {
+	Tcl_AppendResult(interp, "missing name argument: should be \"",
+		 argv[0], "snap ?switches? name\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    data.name = argv[i];
+    if (data.width < 2) {
+	data.width = 400;
+    }
+    if (data.height < 2) {
+	data.height = 400;
+    }
+    /* Always re-compute the layout of the graph before snapping the photo. */
+    graphPtr->width = data.width;
+    graphPtr->height = data.height;
+    Blt_LayoutGraph(graphPtr);
+
+    drawable = Tk_WindowId(graphPtr->tkwin);
+    if (data.format == FORMAT_PHOTO) {
+	drawable = Tk_GetPixmap(graphPtr->display, drawable, graphPtr->width, 
+		graphPtr->height, Tk_Depth(graphPtr->tkwin));
+#ifdef WIN32
+	assert(drawable != None);
+#endif
+	graphPtr->flags |= RESET_WORLD;
+	Blt_DrawGraph(graphPtr, drawable, noBackingStore);
+	result = Blt_SnapPhoto(interp, graphPtr->tkwin, drawable, 0, 0, 
+	    data.width, data.height, data.width, data.height, data.name, 1.0);
+	Tk_FreePixmap(graphPtr->display, drawable);
+#ifdef WIN32
+    } else if ((data.format == FORMAT_WMF) || (data.format == FORMAT_EMF)) {
+	TkWinDC drawableDC;
+	TkWinDCState state;
+	HDC hRefDC, hDC;
+	HENHMETAFILE hMetaFile;
+	Tcl_DString dString;
+	char *title;
+
+	hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
+
+	Tcl_DStringInit(&dString);
+	Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+	Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+	Tcl_DStringAppend(&dString, "\0", -1);
+	Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+	Tcl_DStringAppend(&dString, "\0", -1);
+	title = Tcl_DStringValue(&dString);
+	hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, title);
+	Tcl_DStringFree(&dString);
+	
+	if (hDC == NULL) {
+	    Tcl_AppendResult(interp, "can't create metafile: ",
+		     Blt_LastError(), (char *)NULL);
+	    return TCL_ERROR;
+	}
+
+	drawableDC.hdc = hDC;
+	drawableDC.type = TWD_WINDC;
+
+	Blt_LayoutGraph(graphPtr);
+	graphPtr->flags |= RESET_WORLD;
+	Blt_DrawGraph(graphPtr, (Drawable)&drawableDC, FALSE);
+
+	hMetaFile = CloseEnhMetaFile(hDC);
+	if (strcmp(data.name, "CLIPBOARD") == 0) {
+	    HWND hWnd;
+	    
+	    hWnd = Tk_GetHWND(drawable);
+	    OpenClipboard(hWnd);
+	    EmptyClipboard();
+	    SetClipboardData(CF_ENHMETAFILE, hMetaFile);
+	    CloseClipboard();
+	    result = TCL_OK;
+	} else {
+	    result = TCL_ERROR;
+	    if (data.format == FORMAT_WMF) {
+		APMHEADER mfh;
+
+		assert(sizeof(mfh) == 22);
+		InitMetaFileHeader(graphPtr->tkwin, data.width, data.height, 
+			&mfh);
+		result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh, 
+			data.name);
+	    } else {
+		HENHMETAFILE hMetaFile2;
+
+		hMetaFile2 = CopyEnhMetaFile(hMetaFile, data.name);
+		if (hMetaFile2 != NULL) {
+		    result = TCL_OK;
+		    DeleteEnhMetaFile(hMetaFile2); 
+		}
+	    }
+	    DeleteEnhMetaFile(hMetaFile); 
+	}
+	TkWinReleaseDrawableDC(drawable, hRefDC, &state);
+#endif /*WIN32*/
+    } else {
+	Tcl_AppendResult(interp, "bad snapshot format", (char *)NULL);
+	return TCL_ERROR;
+    }
+    graphPtr->flags = MAP_WORLD;
+    Blt_EventuallyRedrawGraph(graphPtr);
+    return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * GraphWidgetCmd --
+ *
+ *	This procedure is invoked to process the Tcl command that
+ *	corresponds to a widget managed by this module.  See the user
+ *	documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+static Blt_OpSpec graphOps[] =
+{
+    {"axis", 1, (Blt_Op)Blt_VirtualAxisOp, 2, 0, "oper ?args?",},
+    {"bar", 2, (Blt_Op)BarOp, 2, 0, "oper ?args?",},
+    {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+    {"crosshairs", 2, (Blt_Op)Blt_CrosshairsOp, 2, 0, "oper ?args?",},
+    {"element", 2, (Blt_Op)ElementOp, 2, 0, "oper ?args?",},
+    {"extents", 2, (Blt_Op)ExtentsOp, 3, 3, "item",},
+    {"grid", 1, (Blt_Op)Blt_GridOp, 2, 0, "oper ?args?",},
+    {"inside", 3, (Blt_Op)InsideOp, 4, 4, "winX winY",},
+    {"invtransform", 3, (Blt_Op)InvtransformOp, 4, 4, "winX winY",},
+    {"legend", 2, (Blt_Op)Blt_LegendOp, 2, 0, "oper ?args?",},
+    {"line", 2, (Blt_Op)LineOp, 2, 0, "oper ?args?",},
+    {"marker", 2, (Blt_Op)Blt_MarkerOp, 2, 0, "oper ?args?",},
+    {"pen", 2, (Blt_Op)Blt_PenOp, 2, 0, "oper ?args?",},
+    {"postscript", 2, (Blt_Op)Blt_PostScriptOp, 2, 0, "oper ?args?",},
+#ifndef NO_PRINTER
+    {"print1", 2, (Blt_Op)Print1Op, 2, 3, "?printerName?",},
+    {"print2", 2, (Blt_Op)Print2Op, 2, 3, "?printerName?",},
+#endif /*NO_PRINTER*/
+    {"snap", 1, (Blt_Op)SnapOp, 3, 0, "?switches? name",},
+    {"transform", 1, (Blt_Op)TransformOp, 4, 4, "x y",},
+    {"x2axis", 2, (Blt_Op)X2AxisOp, 2, 0, "oper ?args?",},
+    {"xaxis", 2, (Blt_Op)XAxisOp, 2, 0, "oper ?args?",},
+    {"y2axis", 2, (Blt_Op)Y2AxisOp, 2, 0, "oper ?args?",},
+    {"yaxis", 2, (Blt_Op)YAxisOp, 2, 0, "oper ?args?",},
+};
+static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_GraphInstCmdProc(clientData, interp, argc, argv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+    Graph *graphPtr = clientData;
+
+    proc = Blt_GetOp(interp, nGraphOps, graphOps, BLT_OP_ARG1, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(graphPtr);
+    result = (*proc) (graphPtr, interp, argc, argv);
+    Tcl_Release(graphPtr);
+    return result;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * NewGraph --
+ *
+ *	Creates a new window and Tcl command representing an
+ *	instance of a graph widget.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+static int
+NewGraph(interp, argc, argv, classUid)
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+    Blt_Uid classUid;
+{
+    Graph *graphPtr;
+
+    if (argc < 2) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+	    " pathName ?option value?...\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    graphPtr = CreateGraph(interp, argc, argv, classUid);
+    if (graphPtr == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_SetResult(interp, Tk_PathName(graphPtr->tkwin), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * GraphCmd --
+ *
+ *	Creates a new window and Tcl command representing an
+ *	instance of a graph widget.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GraphCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return NewGraph(interp, argc, argv, bltLineElementUid);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BarchartCmd --
+ *
+ *	Creates a new window and Tcl command representing an
+ *	instance of a barchart widget.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BarchartCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return NewGraph(interp, argc, argv, bltBarElementUid);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * StripchartCmd --
+ *
+ *	Creates a new window and Tcl command representing an
+ *	instance of a barchart widget.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StripchartCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return NewGraph(interp, argc, argv, bltStripElementUid);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DrawMargins --
+ *
+ * 	Draws the exterior region of the graph (axes, ticks, titles, etc)
+ *	onto a pixmap. The interior region is defined by the given
+ *	rectangle structure.
+ *
+ *	---------------------------------
+ *      |                               |
+ *      |           rectArr[0]          |
+ *      |                               |
+ *	---------------------------------
+ *      |     |top           right|     |
+ *      |     |                   |     |
+ *      |     |                   |     |
+ *      | [1] |                   | [2] |
+ *      |     |                   |     |
+ *      |     |                   |     |
+ *      |     |                   |     |
+ *      |     |                   |     |
+ *      |     |                   |     |
+ *      |     |left         bottom|     |
+ *	---------------------------------
+ *      |                               |
+ *      |          rectArr[3]           |
+ *      |                               |
+ *	---------------------------------
+ *
+ *		X coordinate axis
+ *		Y coordinate axis
+ *		legend
+ *		interior border
+ *		exterior border
+ *		titles (X and Y axis, graph)
+ *
+ * Returns:
+ *	None.
+ *
+ * Side Effects:
+ *	Exterior of graph is displayed in its window.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+DrawMargins(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    XRectangle rects[4];
+    /*
+     * Draw the four outer rectangles which encompass the plotting
+     * surface. This clears the surrounding area and clips the plot.
+     */
+    rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0;
+    rects[0].width = rects[3].width = (short int)graphPtr->width;
+    rects[0].height = (short int)graphPtr->top;
+    rects[3].y = graphPtr->bottom;
+    rects[3].height = graphPtr->height - graphPtr->bottom;
+    rects[2].y = rects[1].y = graphPtr->top;
+    rects[1].width = graphPtr->left;
+    rects[2].height = rects[1].height = graphPtr->bottom - graphPtr->top;
+    rects[2].x = graphPtr->right;
+    rects[2].width = graphPtr->width - graphPtr->right;
+
+    if (graphPtr->tile != NULL) {
+	Blt_SetTileOrigin(graphPtr->tkwin, graphPtr->tile, 0, 0);
+	Blt_TileRectangles(graphPtr->tkwin, drawable, graphPtr->tile, rects, 4);
+    } else {
+	XFillRectangles(graphPtr->display, drawable, graphPtr->fillGC, rects, 
+			4);
+    }
+
+    /* Draw 3D border around the plotting area */
+
+    if (graphPtr->plotBorderWidth > 0) {
+	int x, y, width, height;
+
+	x = graphPtr->left - graphPtr->plotBorderWidth;
+	y = graphPtr->top - graphPtr->plotBorderWidth;
+	width = (graphPtr->right - graphPtr->left) + 
+	    (2 * graphPtr->plotBorderWidth);
+	height = (graphPtr->bottom - graphPtr->top) + 
+	    (2 * graphPtr->plotBorderWidth);
+	Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border, x, y, 
+	    width, height, graphPtr->plotBorderWidth, graphPtr->plotRelief);
+    }
+    if (Blt_LegendSite(graphPtr->legend) & LEGEND_IN_MARGIN) {
+	/* Legend is drawn on one of the graph margins */
+	Blt_DrawLegend(graphPtr->legend, drawable);
+    }
+    if (graphPtr->title != NULL) {
+	Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->title,
+	    &graphPtr->titleTextStyle, graphPtr->titleX, graphPtr->titleY);
+    }
+    Blt_DrawAxes(graphPtr, drawable);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawPlotRegion --
+ *
+ *	Draws the contents of the plotting area.  This consists of
+ *	the elements, markers (draw under elements), axis limits,
+ *	grid lines, and possibly the legend.  Typically, the output
+ *	will be cached into a backing store pixmap, so that redraws
+ *	can occur quickly.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawPlotRegion(graphPtr, drawable)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+{
+    /* Clear the background of the plotting area. */
+    XFillRectangle(graphPtr->display, drawable, graphPtr->plotFillGC,
+	graphPtr->left, graphPtr->top, graphPtr->right - graphPtr->left + 1,
+	graphPtr->bottom - graphPtr->top + 1);
+
+    /* Draw the elements, markers, legend, and axis limits. */
+
+    if (!graphPtr->gridPtr->hidden) {
+	Blt_DrawGrid(graphPtr, drawable);
+    }
+    Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
+    if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) && 
+	(!Blt_LegendIsRaised(graphPtr->legend))) {
+	Blt_DrawLegend(graphPtr->legend, drawable);
+    }
+    Blt_DrawAxisLimits(graphPtr, drawable);
+    Blt_DrawElements(graphPtr, drawable);
+}
+
+void
+Blt_LayoutGraph(graphPtr)
+    Graph *graphPtr;
+{
+    if (graphPtr->flags & RESET_AXES) {
+	Blt_ResetAxes(graphPtr);
+    }
+    if (graphPtr->flags & LAYOUT_NEEDED) {
+	Blt_LayoutMargins(graphPtr);
+	graphPtr->flags &= ~LAYOUT_NEEDED;
+    }
+    /* Compute coordinate transformations for graph components */
+    if ((graphPtr->vRange > 1) && (graphPtr->hRange > 1)) {
+	if (graphPtr->flags & MAP_WORLD) {
+	    Blt_MapAxes(graphPtr);
+	}
+	Blt_MapElements(graphPtr);
+	Blt_MapMarkers(graphPtr);
+	Blt_MapGrid(graphPtr);
+	graphPtr->flags &= ~(MAP_ALL);
+    }
+}
+
+void
+Blt_DrawGraph(graphPtr, drawable, backingStore)
+    Graph *graphPtr;
+    Drawable drawable;		/* Pixmap or window to draw into */
+    int backingStore;		/* If non-zero, use backing store for
+				 * plotting area. */
+{
+    if (backingStore) {
+	/*
+	 * Create another pixmap to save elements if one doesn't
+	 * already exist or the size of the window has changed.
+	 */
+	if ((graphPtr->backPixmap == None) ||
+	    (graphPtr->backWidth != graphPtr->width) ||
+	    (graphPtr->backHeight != graphPtr->height)) {
+
+	    if (graphPtr->backPixmap != None) {
+		Tk_FreePixmap(graphPtr->display, graphPtr->backPixmap);
+	    }
+	    graphPtr->backPixmap = Tk_GetPixmap(graphPtr->display,
+		Tk_WindowId(graphPtr->tkwin), graphPtr->width, 
+		graphPtr->height, Tk_Depth(graphPtr->tkwin));
+	    graphPtr->backWidth = graphPtr->width;
+	    graphPtr->backHeight = graphPtr->height;
+	    graphPtr->flags |= REDRAW_BACKING_STORE;
+	}
+	if (graphPtr->flags & REDRAW_BACKING_STORE) {
+
+	    /* The backing store is new or out-of-date. */
+
+	    DrawPlotRegion(graphPtr, graphPtr->backPixmap);
+	    graphPtr->flags &= ~REDRAW_BACKING_STORE;
+	}
+
+	/* Copy the pixmap to the one used for drawing the entire graph. */
+
+	XCopyArea(graphPtr->display, graphPtr->backPixmap, drawable,
+	    graphPtr->drawGC, graphPtr->left, graphPtr->top,
+	    (graphPtr->right - graphPtr->left + 1),
+	    (graphPtr->bottom - graphPtr->top + 1),
+	    graphPtr->left, graphPtr->top);
+    } else {
+	DrawPlotRegion(graphPtr, drawable);
+    }
+
+    /* Draw markers above elements */
+    Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
+    Blt_DrawActiveElements(graphPtr, drawable);
+
+    if (graphPtr->flags & DRAW_MARGINS) {
+	DrawMargins(graphPtr, drawable);
+    }
+    if ((Blt_LegendSite(graphPtr->legend) & LEGEND_IN_PLOT) && 
+	(Blt_LegendIsRaised(graphPtr->legend))) {
+	Blt_DrawLegend(graphPtr->legend, drawable);
+    }
+    /* Draw 3D border just inside of the focus highlight ring. */
+    if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
+	Blt_Draw3DRectangle(graphPtr->tkwin, drawable, graphPtr->border, 
+	    graphPtr->highlightWidth, graphPtr->highlightWidth,
+	    graphPtr->width - 2 * graphPtr->highlightWidth, 
+	    graphPtr->height - 2 * graphPtr->highlightWidth, 
+	    graphPtr->borderWidth, graphPtr->relief);
+    }
+    /* Draw focus highlight ring. */
+    if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & GRAPH_FOCUS)) {
+	GC gc;
+
+	gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
+	Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
+	    drawable);
+    }
+}
+
+static void
+UpdateMarginTraces(graphPtr)
+    Graph *graphPtr;
+{
+    Margin *marginPtr;
+    int size;
+    register int i;
+
+    for (i = 0; i < 4; i++) {
+	marginPtr = graphPtr->margins + i;
+	if (marginPtr->varName != NULL) {	/* Trigger variable traces */
+	    if ((marginPtr->site == MARGIN_LEFT) || 
+		(marginPtr->site == MARGIN_RIGHT)) {
+		size = marginPtr->width;
+	    } else {
+		size = marginPtr->height;
+	    }
+	    Tcl_SetVar(graphPtr->interp, marginPtr->varName, Blt_Itoa(size), 
+		TCL_GLOBAL_ONLY);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayGraph --
+ *
+ *	This procedure is invoked to display a graph widget.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Commands are output to X to display the graph in its
+ *	current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DisplayGraph(clientData)
+    ClientData clientData;
+{
+    Graph *graphPtr = clientData;
+    Pixmap drawable;
+
+    graphPtr->flags &= ~REDRAW_PENDING;
+    if (graphPtr->tkwin == NULL) {
+	return;			/* Window destroyed (should not get here) */
+    }
+#ifdef notdef
+    fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(graphPtr->tkwin));
+#endif
+    if (Blt_GraphUpdateNeeded(graphPtr)) {
+	/*
+	 * One of the elements of the graph has a vector notification
+	 * pending.  This means that the vector will eventually notify
+	 * the graph that its data has changed.  Since the graph uses
+	 * the actual vector (not a copy) we need to keep in-sync.
+	 * Therefore don't draw right now but wait until we've been
+	 * notified before redrawing.
+	 */
+	return;
+    }
+    graphPtr->width = Tk_Width(graphPtr->tkwin);
+    graphPtr->height = Tk_Height(graphPtr->tkwin);
+    Blt_LayoutGraph(graphPtr);
+    Blt_UpdateCrosshairs(graphPtr);
+    if (!Tk_IsMapped(graphPtr->tkwin)) {
+	/* The graph's window isn't displayed, so don't bother
+	 * drawing anything.  By getting this far, we've at least
+	 * computed the coordinates of the graph's new layout.  */
+	return;
+    }
+
+    /* Disable crosshairs before redisplaying to the screen */
+    Blt_DisableCrosshairs(graphPtr);
+    /*
+     * Create a pixmap the size of the window for double buffering.
+     */
+    if (graphPtr->doubleBuffer) {
+	drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+		graphPtr->width, graphPtr->height, Tk_Depth(graphPtr->tkwin));
+    } else {
+	drawable = Tk_WindowId(graphPtr->tkwin);
+    }
+#ifdef WIN32
+    assert(drawable != None);
+#endif
+    Blt_DrawGraph(graphPtr, drawable, 
+		  graphPtr->backingStore && graphPtr->doubleBuffer);
+    if (graphPtr->flags & DRAW_MARGINS) {
+	XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
+	    graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0);
+    } else {
+	XCopyArea(graphPtr->display, drawable, Tk_WindowId(graphPtr->tkwin),
+		  graphPtr->drawGC, graphPtr->left, graphPtr->top,
+		  (graphPtr->right - graphPtr->left + 1), 
+		  (graphPtr->bottom - graphPtr->top + 1),
+		  graphPtr->left, graphPtr->top);
+    }
+    if (graphPtr->doubleBuffer) {
+	Tk_FreePixmap(graphPtr->display, drawable);
+    }
+    Blt_EnableCrosshairs(graphPtr);
+    graphPtr->flags &= ~RESET_WORLD;
+    UpdateMarginTraces(graphPtr);
+}
+
+/*LINTLIBRARY*/
+int
+Blt_GraphInit(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpecs[] =
+    {
+	{"graph", GraphCmd,},
+	{"barchart", BarchartCmd,},
+	{"stripchart", StripchartCmd,},
+    };
+    bltBarElementUid = (Blt_Uid)Tk_GetUid("BarElement");
+    bltLineElementUid = (Blt_Uid)Tk_GetUid("LineElement");
+    bltStripElementUid = (Blt_Uid)Tk_GetUid("StripElement");
+    bltContourElementUid = (Blt_Uid)Tk_GetUid("ContourElement");
+
+    bltLineMarkerUid = (Blt_Uid)Tk_GetUid("LineMarker");
+    bltBitmapMarkerUid = (Blt_Uid)Tk_GetUid("BitmapMarker");
+    bltImageMarkerUid = (Blt_Uid)Tk_GetUid("ImageMarker");
+    bltTextMarkerUid = (Blt_Uid)Tk_GetUid("TextMarker");
+    bltPolygonMarkerUid = (Blt_Uid)Tk_GetUid("PolygonMarker");
+    bltWindowMarkerUid = (Blt_Uid)Tk_GetUid("WindowMarker");
+
+    bltXAxisUid = (Blt_Uid)Tk_GetUid("X");
+    bltYAxisUid = (Blt_Uid)Tk_GetUid("Y");
+
+    return Blt_InitCmds(interp, "blt", cmdSpecs, 3);
+}
+
+Graph *
+Blt_GetGraphFromWindowData(tkwin)
+    Tk_Window tkwin;
+{
+    Graph *graphPtr;
+
+    while (tkwin != NULL) {
+	graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
+	if (graphPtr != NULL) {
+	    return graphPtr;
+	}
+	tkwin = Tk_Parent(tkwin);
+    }
+    return NULL;
+}
+
+int
+Blt_GraphType(graphPtr)	
+    Graph *graphPtr;
+{
+    if (graphPtr->classUid == bltLineElementUid) {
+	return GRAPH;
+    } else if (graphPtr->classUid == bltBarElementUid) {
+	return BARCHART;
+    } else if (graphPtr->classUid == bltStripElementUid) {
+	return STRIPCHART;
+    }
+    return 0;
+}
Index: trunk/kitgen/8.x/blt/generic/bltGraph.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltGraph.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltGraph.h	(revision 175)
@@ -0,0 +1,684 @@
+/*
+ * bltGraph.h --
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_GRAPH_H
+#define _BLT_GRAPH_H
+
+#include "bltInt.h"
+#include "bltHash.h"
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltPs.h"
+#include "bltTile.h"
+
+typedef struct GraphStruct Graph;
+typedef struct ElementStruct Element;
+typedef struct LegendStruct Legend;
+
+#include "bltGrAxis.h"
+#include "bltGrLegd.h"
+
+#define MARKER_UNDER	1	/* Draw markers designated to lie underneath
+				 * elements, grids, legend, etc. */
+#define MARKER_ABOVE	0	/* Draw markers designated to rest above
+				 * elements, grids, legend, etc. */
+
+#define PADX		2	/* Padding between labels/titles */
+#define PADY    	2	/* Padding between labels */
+
+#define MINIMUM_MARGIN	20	/* Minimum margin size */
+
+
+#define BOUND(x, lo, hi)	 \
+	(((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x))
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * 	Graph component structure definitions
+ *
+ * -------------------------------------------------------------------
+ */
+#define PointInGraph(g,x,y) \
+	(((x) <= (g)->right) && ((x) >= (g)->left) && \
+	 ((y) <= (g)->bottom) && ((y) >= (g)->top))
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * ClassType --
+ *
+ *	Enumerates the different types of graph elements this program
+ *	produces.  An element can be either a line or a bar.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef enum {
+    CLASS_UNKNOWN,
+    CLASS_LINE_ELEMENT,
+    CLASS_STRIP_ELEMENT,
+    CLASS_BAR_ELEMENT,
+    CLASS_BITMAP_MARKER,
+    CLASS_IMAGE_MARKER,
+    CLASS_LINE_MARKER,
+    CLASS_POLYGON_MARKER,
+    CLASS_TEXT_MARKER,
+    CLASS_WINDOW_MARKER
+
+} ClassType;
+
+/*
+ * Mask values used to selectively enable GRAPH or BARCHART entries in
+ * the various configuration specs.
+ */
+#define GRAPH		(TK_CONFIG_USER_BIT << 1)
+#define STRIPCHART	(TK_CONFIG_USER_BIT << 2)
+#define BARCHART	(TK_CONFIG_USER_BIT << 3)
+#define LINE_GRAPHS	(GRAPH | STRIPCHART)
+#define ALL_GRAPHS	(GRAPH | BARCHART | STRIPCHART)
+
+#define PEN_DELETE_PENDING	(1<<0)
+#define ACTIVE_PEN		(TK_CONFIG_USER_BIT << 6)
+#define NORMAL_PEN		(TK_CONFIG_USER_BIT << 7)
+#define ALL_PENS		(NORMAL_PEN | ACTIVE_PEN)
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * FreqInfo --
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    int freq;			/* Number of occurrences of x-coordinate */
+    Axis2D axes;		/* Indicates which x and y axis are mapped to
+				 * the x-value */
+    double sum;			/* Sum of the ordinates of each duplicate
+				 * abscissa */
+    int count;
+    double lastY;
+
+} FreqInfo;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * FreqKey --
+ *
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    double value;		/* Duplicated abscissa */
+    Axis2D axes;		/* Axis mapping of element */
+} FreqKey;
+
+/*
+ * BarModes --
+ *
+ *	Bar elements are displayed according to their x-y coordinates.
+ *	If two bars have the same abscissa (x-coordinate), the bar
+ *	segments will be drawn according to one of the following
+ *	modes:
+ */
+
+typedef enum BarModes {
+    MODE_INFRONT,		/* Each successive segment is drawn in
+				 * front of the previous. */
+    MODE_STACKED,		/* Each successive segment is drawn
+				 * stacked above the previous. */
+    MODE_ALIGNED,		/* Each successive segment is drawn
+				 * aligned to the previous from
+				 * right-to-left. */
+    MODE_OVERLAP		/* Like "aligned", each successive segment
+				 * is drawn from right-to-left. In addition
+				 * the segments will overlap each other
+				 * by a small amount */
+} BarMode;
+
+typedef struct PenStruct Pen;
+typedef struct MarkerStruct Marker;
+
+typedef Pen *(PenCreateProc) _ANSI_ARGS_((void));
+typedef int (PenConfigureProc) _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+typedef void (PenDestroyProc) _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+
+struct PenStruct {
+    char *name;			/* Pen style identifier.  If NULL pen
+				 * was statically allocated. */
+    Blt_Uid classUid;		/* Type of pen */
+    char *typeId;		/* String token identifying the type of pen */
+    unsigned int flags;		/* Indicates if the pen element is active or
+				 * normal */
+    int refCount;		/* Reference count for elements using
+				 * this pen. */
+    Blt_HashEntry *hashPtr;
+
+    Tk_ConfigSpec *configSpecs;	/* Configuration specifications */
+
+    PenConfigureProc *configProc;
+    PenDestroyProc *destroyProc;
+
+};
+
+typedef enum {
+    PS_MONO_BACKGROUND,
+    PS_MONO_FOREGROUND
+} MonoAttribute;
+
+/*
+ * PostScript --
+ *
+ * 	Structure contains information specific to the outputting of
+ *	PostScript commands to print the graph.
+ *
+ */
+typedef struct  {
+    /* User configurable fields */
+
+    int decorations;		/* If non-zero, print graph with
+				 * color background and 3D borders */
+
+    int reqWidth, reqHeight;	/* If greater than zero, represents the
+				 * requested dimensions of the printed graph */
+    int reqPaperWidth;
+    int reqPaperHeight;		/* Requested dimensions for the PostScript
+				 * page. Can constrain the size of the graph
+				 * if the graph (plus padding) is larger than
+				 * the size of the page. */
+    Blt_Pad padX, padY;		/* Requested padding on the exterior of the
+				 * graph. This forms the bounding box for
+				 * the page. */
+    PsColorMode colorMode;	/* Selects the color mode for PostScript page
+				 * (0=monochrome, 1=greyscale, 2=color) */
+    char *colorVarName;		/* If non-NULL, is the name of a Tcl array
+				 * variable containing X to PostScript color
+				 * translations */
+    char *fontVarName;		/* If non-NULL, is the name of a Tcl array
+				 * variable containing X to PostScript font
+				 * translations */
+    int landscape;		/* If non-zero, orient the page 90 degrees */
+    int center;			/* If non-zero, center the graph on the page */
+    int maxpect;		/* If non-zero, indicates to scale the graph
+				 * so that it fills the page (maintaining the
+				 * aspect ratio of the graph) */
+    int addPreview;		/* If non-zero, generate a preview image and
+				 * add it to the PostScript output */
+    int footer;			/* If non-zero, a footer with the title, date
+				 * and user will be added to the PostScript
+				 * output outside of the bounding box. */
+    int previewFormat;		/* Format of EPS preview:
+				 * PS_PREVIEW_WMF, PS_PREVIEW_EPSI, or
+				 * PS_PREVIEW_TIFF. */
+
+    /* Computed fields */
+
+    int left, bottom;		/* Bounding box of PostScript plot. */
+    int right, top;
+
+    double pageScale;		/* Scale of page. Set if "-maxpect" option
+				 * is set, otherwise 1.0. */
+} PostScript;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Grid
+ *
+ *	Contains attributes of describing how to draw grids (at major
+ *	ticks) in the graph.  Grids may be mapped to either/both x and
+ *	y axis.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    GC gc;			/* Graphics context for the grid. */
+    Axis2D axes;
+    int hidden;			/* If non-zero, grid isn't displayed. */
+    int minorGrid;		/* If non-zero, draw grid line for minor
+				 * axis ticks too */
+    Blt_Dashes dashes;		/* Dashstyle of the grid. This represents
+				 * an array of alternatingly drawn pixel
+				 * values. */
+    int lineWidth;		/* Width of the grid lines */
+    XColor *colorPtr;		/* Color of the grid lines */
+
+    struct GridSegments {
+	Segment2D *segments;	/* Array of line segments representing the
+				 * x or y grid lines */
+	int nSegments;		/* # of axis segments. */
+    } x, y;	
+
+} Grid;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ *	Contains the line segments positions and graphics context used
+ *	to simulate crosshairs (by XOR-ing) on the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct CrosshairsStruct Crosshairs;
+
+typedef struct {
+    short int width, height;	/* Extents of the margin */
+
+    short int axesOffset;
+    short int axesTitleLength;	/* Width of the widest title to be shown. 
+				 * Multiple titles are displayed in 
+				 * another margin. This is the minimum
+				 * space requirement. */
+    unsigned int nAxes;		/* Number of axes to be displayed */
+    Blt_Chain *axes;		/* Extra axes associated with this margin */
+
+    char *varName;		/* If non-NULL, name of variable to be
+				 * updated when the margin size changes */
+
+    int reqSize;		/* Requested size of margin */
+    int site;			/* Indicates where margin is located: 
+				 * left/right/top/bottom. */
+} Margin;
+
+#define MARGIN_NONE	-1
+#define MARGIN_BOTTOM	0	
+#define MARGIN_LEFT	1 
+#define MARGIN_TOP	2			
+#define MARGIN_RIGHT	3
+
+#define rightMargin	margins[MARGIN_RIGHT]
+#define leftMargin	margins[MARGIN_LEFT]
+#define topMargin	margins[MARGIN_TOP]
+#define bottomMargin	margins[MARGIN_BOTTOM]
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Graph --
+ *
+ *	Top level structure containing everything pertaining to
+ *	the graph.
+ *
+ * -------------------------------------------------------------------
+ */
+struct GraphStruct {
+    unsigned int flags;		/* Flags;  see below for definitions. */
+    Tcl_Interp *interp;		/* Interpreter associated with graph */
+    Tk_Window tkwin;		/* Window that embodies the graph.  NULL
+				 * means that the window has been
+				 * destroyed but the data structures
+				 * haven't yet been cleaned up. */
+    Display *display;		/* Display containing widget; needed,
+				 * among other things, to release
+				 * resources after tkwin has already gone
+				 * away. */
+    Tcl_Command cmdToken;	/* Token for graph's widget command. */
+
+    char *data;			/* This value isn't used in C code.
+				 * It may be used in Tcl bindings to
+				 * associate extra data. */
+
+    Tk_Cursor cursor;
+
+    int inset;			/* Sum of focus highlight and 3-D
+				 * border.  Indicates how far to 
+				 * offset the graph from outside 
+				 * edge of the window. */
+
+    int borderWidth;		/* Width of the exterior border */
+    int relief;			/* Relief of the exterior border */
+    Tk_3DBorder border;		/* 3-D border used to delineate the plot
+				 * surface and outer edge of window */
+    
+    int highlightWidth;		/* Width in pixels of highlight to draw
+				 * around widget when it has the focus.
+				 * <= 0 means don't draw a highlight. */
+    XColor *highlightBgColor;	/* Color for drawing traversal highlight
+				 * area when highlight is off. */
+    XColor *highlightColor;	/* Color for drawing traversal highlight. */
+
+    char *title;
+    short int titleX, titleY;
+    TextStyle titleTextStyle;	/* Graph title */
+    
+    char *takeFocus;
+    
+    int reqWidth, reqHeight;	/* Requested size of graph window */
+    int width, height;		/* Size of graph window or PostScript
+				 * page */
+    
+    Blt_HashTable penTable;	/* Table of pens */
+    
+    struct Component {
+	Blt_HashTable table;	/* Hash table of ids. */
+	Blt_Chain *displayList;	/* Display list. */
+	Blt_HashTable tagTable;	/* Table of bind tags. */
+    } elements, markers, axes;
+    
+    Blt_Uid classUid;		/* Default element type */
+
+    Blt_BindTable bindTable;
+    int nextMarkerId;		/* Tracks next marker identifier available */
+    
+    Blt_Chain *axisChain[4];	/* Chain of axes for each of the
+				 * margins.  They're separate from the
+				 * margin structures to make it easier
+				 * to invert the X-Y axes by simply
+				 * switching chain pointers.  
+				 */
+    Margin margins[4];
+    
+    PostScript *postscript;	/* PostScript options: see bltGrPS.c */
+    Legend *legend;		/* Legend information: see bltGrLegd.c */
+    Crosshairs *crosshairs;	/* Crosshairs information: see bltGrHairs.c */
+    Grid *gridPtr;		/* Grid attribute information */
+
+    int halo;			/* Maximum distance allowed between points
+				 * when searching for a point */
+    int inverted;		/* If non-zero, indicates the x and y axis
+				 * positions should be inverted. */
+    Blt_Tile tile;
+    GC drawGC;			/* Used for drawing on the margins. This
+				 * includes the axis lines */
+    GC fillGC;			/* Used to fill the background of the
+				 * margins. The fill is governed by
+				 * the background color or the tiled
+				 * pixmap. */
+    int plotBorderWidth;	/* Width of interior 3-D border. */
+    int plotRelief;		/* 3-d effect: TK_RELIEF_RAISED etc. */
+    XColor *plotBg;		/* Color of plotting surface */
+
+    GC plotFillGC;		/* Used to fill the plotting area with a
+				 * solid background color. The fill color
+				 * is stored in "plotBg". */
+
+    /* If non-zero, force plot to conform to aspect ratio W/H */
+    double aspect;
+
+    short int left, right;	/* Coordinates of plot bbox */
+    short int top, bottom;	
+
+    Blt_Pad padX;		/* Vertical padding for plotarea */
+    int vRange, vOffset;	/* Vertical axis range and offset from the
+				 * left side of the graph window. Used to
+				 * transform coordinates to vertical
+				 * axes. */
+    Blt_Pad padY;		/* Horizontal padding for plotarea */
+    int hRange, hOffset;	/* Horizontal axis range and offset from
+				 * the top of the graph window. Used to
+				 * transform horizontal axes */
+    double vScale, hScale;
+
+    int doubleBuffer;		/* If non-zero, draw the graph into a pixmap
+				 * first to reduce flashing. */
+    int backingStore;		/* If non-zero, cache elements by drawing
+				 * them into a pixmap */
+    Pixmap backPixmap;		/* Pixmap used to cache elements
+				 * displayed.  If *backingStore* is
+				 * non-zero, each element is drawn
+				 * into this pixmap before it is
+				 * copied onto the screen.  The pixmap
+				 * then acts as a cache (only the
+				 * pixmap is redisplayed if the none
+				 * of elements have changed). This is
+				 * done so that markers can be redrawn
+				 * quickly over elements without
+				 * redrawing each element. */
+    int backWidth, backHeight;	/* Size of element backing store pixmap. */
+
+    /*
+     * barchart specific information
+     */
+    double baseline;		/* Baseline from bar chart.  */
+    double barWidth;		/* Default width of each bar in graph units.
+				 * The default width is 1.0 units. */
+    BarMode mode;		/* Mode describing how to display bars
+				 * with the same x-coordinates. Mode can
+				 * be "stack", "align", or "normal" */
+    FreqInfo *freqArr;		/* Contains information about duplicate
+				 * x-values in bar elements (malloc-ed).
+				 * This information can also be accessed
+				 * by the frequency hash table */
+    Blt_HashTable freqTable;	/* */
+    int nStacks;		/* Number of entries in frequency array.
+				 * If zero, indicates nothing special needs
+				 * to be done for "stack" or "align" modes */
+    char *dataCmd;		/* New data callback? */
+
+};
+
+/*
+ * Bit flags definitions:
+ *
+ * 	All kinds of state information kept here.  All these
+ *	things happen when the window is available to draw into
+ *	(DisplayGraph). Need the window width and height before
+ *	we can calculate graph layout (i.e. the screen coordinates
+ *	of the axes, elements, titles, etc). But we want to do this
+ *	only when we have to, not every time the graph is redrawn.
+ *
+ *	Same goes for maintaining a pixmap to double buffer graph
+ *	elements.  Need to mark when the pixmap needs to updated.
+ *
+ *
+ *	MAP_ITEM		Indicates that the element/marker/axis
+ *				configuration has changed such that
+ *				its layout of the item (i.e. its
+ *				position in the graph window) needs
+ *				to be recalculated.
+ *
+ *	MAP_ALL			Indicates that the layout of the axes and 
+ *				all elements and markers and the graph need 
+ *				to be recalculated. Otherwise, the layout
+ *				of only those markers and elements that
+ *				have changed will be reset. 
+ *
+ *	GET_AXIS_GEOMETRY	Indicates that the size of the axes needs 
+ *				to be recalculated. 
+ *
+ *	RESET_AXES		Flag to call to Blt_ResetAxes routine.  
+ *				This routine recalculates the scale offset
+ *				(used for mapping coordinates) of each axis.
+ *				If an axis limit has changed, then it sets 
+ *				flags to re-layout and redraw the entire 
+ *				graph.  This needs to happend before the axis
+ *				can compute transformations between graph and 
+ *				screen coordinates. 
+ *
+ *	LAYOUT_NEEDED		
+ *
+ *	REDRAW_BACKING_STORE	If set, redraw all elements into the pixmap 
+ *				used for buffering elements. 
+ *
+ *	REDRAW_PENDING		Non-zero means a DoWhenIdle handler has 
+ *				already been queued to redraw this window. 
+ *
+ *	DRAW_LEGEND		Non-zero means redraw the legend. If this is 
+ *				the only DRAW_* flag, the legend display 
+ *				routine is called instead of the graph 
+ *				display routine. 
+ *
+ *	DRAW_MARGINS		Indicates that the margins bordering 
+ *				the plotting area need to be redrawn. 
+ *				The possible reasons are:
+ *
+ *				1) an axis configuration changed
+ *				2) an axis limit changed
+ *				3) titles have changed
+ *				4) window was resized. 
+ *
+ *	GRAPH_FOCUS	
+ */
+
+#define	MAP_ITEM		(1<<0) /* 0x0001 */
+#define	MAP_ALL			(1<<1) /* 0x0002 */
+#define	GET_AXIS_GEOMETRY	(1<<2) /* 0x0004 */
+#define RESET_AXES		(1<<3) /* 0x0008 */
+#define LAYOUT_NEEDED		(1<<4) /* 0x0010 */
+
+#define REDRAW_PENDING		(1<<8) /* 0x0100 */
+#define DRAW_LEGEND		(1<<9) /* 0x0200 */
+#define DRAW_MARGINS		(1<<10)/* 0x0400 */
+#define	REDRAW_BACKING_STORE	(1<<11)/* 0x0800 */
+
+#define GRAPH_FOCUS		(1<<12)/* 0x1000 */
+#define DATA_CHANGED		(1<<13)/* 0x2000 */
+
+#define	MAP_WORLD		(MAP_ALL|RESET_AXES|GET_AXIS_GEOMETRY)
+#define REDRAW_WORLD		(DRAW_MARGINS | DRAW_LEGEND)
+#define RESET_WORLD		(REDRAW_WORLD | MAP_WORLD)
+
+/*
+ * ---------------------- Forward declarations ------------------------
+ */
+
+extern int Blt_CreatePostScript _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_CreateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_CreateGrid _ANSI_ARGS_((Graph *graphPtr));
+extern double Blt_InvHMap _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr, 
+	double x));
+extern double Blt_InvVMap _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr, 
+	double x));
+extern double Blt_HMap _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr, double x));
+extern double Blt_VMap _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr, double y));
+extern Point2D Blt_InvMap2D _ANSI_ARGS_((Graph *graphPtr, double x,
+	double y, Axis2D *pairPtr));
+extern Point2D Blt_Map2D _ANSI_ARGS_((Graph *graphPtr, double x,
+	double y, Axis2D *pairPtr));
+extern Graph *Blt_GetGraphFromWindowData _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_AdjustAxisPointers _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_LineRectClip _ANSI_ARGS_((Extents2D *extsPtr, Point2D *p,
+	Point2D *q));
+extern int Blt_PolyRectClip _ANSI_ARGS_((Extents2D *extsPtr, Point2D *inputPts,
+	int nInputPts, Point2D *outputPts));
+
+extern void Blt_ComputeStacks _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ConfigureCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyGrid _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyElements _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyMarkers _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyPostScript _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DrawAxes _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawAxisLimits _ANSI_ARGS_((Graph *graphPtr,
+	Drawable drawable));
+extern void Blt_DrawElements _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawActiveElements _ANSI_ARGS_((Graph *graphPtr,
+	Drawable drawable));
+extern void Blt_DrawGraph _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+	int backingStore));
+extern void Blt_DrawGrid _ANSI_ARGS_((Graph *graphPtr, Drawable drawable));
+extern void Blt_DrawMarkers _ANSI_ARGS_((Graph *graphPtr, Drawable drawable,
+	int under));
+extern void Blt_Draw2DSegments _ANSI_ARGS_((Display *display, 
+	Drawable drawable, GC gc, Segment2D *segments, int nSegments));
+extern int Blt_GetCoordinate _ANSI_ARGS_((Tcl_Interp *interp,
+	char *expr, double *valuePtr));
+extern void Blt_InitFreqTable _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_LayoutGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_LayoutMargins _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_EventuallyRedrawGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ResetAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_ResetStacks _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_GraphExtents _ANSI_ARGS_((Graph *graphPtr, Extents2D *extsPtr));
+extern void Blt_DisableCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_EnableCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapAxes _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapElements _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapGraph _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapMarkers _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_MapGrid _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_UpdateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_DestroyPens _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_GetPen _ANSI_ARGS_((Graph *graphPtr, char *name, 
+	Blt_Uid classUid, Pen **penPtrPtr));
+extern Pen *Blt_BarPen _ANSI_ARGS_((char *penName));
+extern Pen *Blt_LinePen _ANSI_ARGS_((char *penName));
+extern Pen *Blt_CreatePen _ANSI_ARGS_((Graph *graphPtr, char *penName,
+	Blt_Uid classUid, int nOpts, char **options));
+extern int Blt_InitLinePens _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_InitBarPens _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_FreePen _ANSI_ARGS_((Graph *graphPtr, Pen *penPtr));
+
+extern int Blt_VirtualAxisOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_AxisOp _ANSI_ARGS_((Graph *graphPtr, int margin, int argc, 
+	char **argv));
+extern int Blt_ElementOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv, Blt_Uid classUid));
+extern int Blt_GridOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_CrosshairsOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_MarkerOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_PenOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_PointInPolygon _ANSI_ARGS_((Point2D *samplePtr, 
+	Point2D *screenPts, int nScreenPts));
+extern int Blt_RegionInPolygon _ANSI_ARGS_((Extents2D *extsPtr, Point2D *points,
+	int nPoints, int enclosed));
+extern int Blt_PointInSegments _ANSI_ARGS_((Point2D *samplePtr, 
+	Segment2D *segments, int nSegments, double halo));
+extern int Blt_PostScriptOp _ANSI_ARGS_((Graph *graphPtr, Tcl_Interp *interp,
+	int argc, char **argv));
+extern int Blt_GraphUpdateNeeded _ANSI_ARGS_((Graph *graphPtr));
+extern int Blt_DefaultAxes _ANSI_ARGS_((Graph *graphPtr));
+extern Axis *Blt_GetFirstAxis _ANSI_ARGS_((Blt_Chain *chainPtr));
+extern void Blt_UpdateAxisBackgrounds _ANSI_ARGS_((Graph *graphPtr));
+extern void Blt_GetAxisSegments _ANSI_ARGS_((Graph *graphPtr, Axis *axisPtr,
+	Segment2D **segPtrPtr, int *nSegmentsPtr));
+extern Marker *Blt_NearestMarker _ANSI_ARGS_((Graph *graphPtr, int x, int y,
+	int under));
+extern Axis *Blt_NearestAxis _ANSI_ARGS_((Graph *graphPtr, int x, int y));
+
+
+typedef ClientData (MakeTagProc) _ANSI_ARGS_((Graph *graphPtr, char *tagName));
+extern MakeTagProc Blt_MakeElementTag;
+extern MakeTagProc Blt_MakeMarkerTag;
+extern MakeTagProc Blt_MakeAxisTag;
+
+extern Blt_BindTagProc Blt_GraphTags;
+extern Blt_BindTagProc Blt_AxisTags;
+
+extern int Blt_GraphType _ANSI_ARGS_((Graph *graphPtr));
+
+/* ---------------------- Global declarations ------------------------ */
+
+extern Blt_Uid bltBarElementUid;
+extern Blt_Uid bltLineElementUid;
+extern Blt_Uid bltStripElementUid;
+extern Blt_Uid bltLineMarkerUid;
+extern Blt_Uid bltBitmapMarkerUid;
+extern Blt_Uid bltImageMarkerUid;
+extern Blt_Uid bltTextMarkerUid;
+extern Blt_Uid bltPolygonMarkerUid;
+extern Blt_Uid bltWindowMarkerUid;
+extern Blt_Uid bltXAxisUid;
+extern Blt_Uid bltYAxisUid;
+
+#endif /* _BLT_GRAPH_H */
Index: trunk/kitgen/8.x/blt/generic/bltHash.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltHash.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltHash.c	(revision 175)
@@ -0,0 +1,1336 @@
+
+/* 
+ * bltHash.c --
+ *
+ *
+ *	This module implements an in-memory hash table for the BLT
+ *	toolkit.  Built upon the Tcl hash table, it adds pool
+ *	allocation 64-bit address handling, improved array hash
+ *	function.
+ *
+ * Copyright 2001 Silicon Metrics Corporation.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Bob Jenkins, 1996.  hash.c.  Public Domain.
+ * Bob Jenkins, 1997.  lookup8.c.  Public Domain.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: bltHash.c,v 1.10 2002/08/09 07:15:18 ghowlett Exp $
+ */
+
+#include "bltInt.h"
+
+#include <stdio.h>
+#include <string.h>
+/* The following header is required for LP64 compilation */
+#include <stdlib.h>
+
+#include "bltHash.h"
+
+/*
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+
+#define REBUILD_MULTIPLIER	3
+
+#if (SIZEOF_VOID_P == 8)
+#define RANDOM_INDEX		HashOneWord
+#define DOWNSHIFT_START		62
+#else 
+
+/*
+ * The following macro takes a preliminary integer hash value and
+ * produces an index into a hash tables bucket list.  The idea is
+ * to make it so that preliminary values that are arbitrarily similar
+ * will end up in different buckets.  The hash function was taken
+ * from a random-number generator.
+ */
+#define RANDOM_INDEX(tablePtr, i) \
+    (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
+#define DOWNSHIFT_START	28
+#endif
+
+/*
+ * Procedure prototypes for static procedures in this file:
+ */
+static Blt_Hash HashArray _ANSI_ARGS_((CONST void *key, size_t length));
+static Blt_HashEntry *ArrayFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key));
+static Blt_HashEntry *ArrayCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key, int *newPtr));
+static Blt_HashEntry *BogusFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key));
+static Blt_HashEntry *BogusCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key, int *newPtr));
+static Blt_Hash HashString _ANSI_ARGS_((CONST char *string));
+static void RebuildTable _ANSI_ARGS_((Blt_HashTable *tablePtr));
+static Blt_HashEntry *StringFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key));
+static Blt_HashEntry *StringCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key, int *newPtr));
+static Blt_HashEntry *OneWordFind _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key));
+static Blt_HashEntry *OneWordCreate _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key, int *newPtr));
+
+#if (SIZEOF_VOID_P == 8)
+static Blt_Hash HashOneWord _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	CONST void *key));
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashString --
+ *
+ *	Compute a one-word summary of a text string, which can be
+ *	used to generate a hash index.
+ *
+ * Results:
+ *	The return value is a one-word summary of the information in
+ *	string.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_Hash
+HashString(register CONST char *string) /* String from which to
+					 * compute hash value. */
+{
+    register Blt_Hash result;
+    register Blt_Hash c;
+
+    /*
+     * I tried a zillion different hash functions and asked many other
+     * people for advice.  Many people had their own favorite functions,
+     * all different, but no-one had much idea why they were good ones.
+     * I chose the one below (multiply by 9 and add new character)
+     * because of the following reasons:
+     *
+     * 1. Multiplying by 10 is perfect for keys that are decimal strings,
+     *    and multiplying by 9 is just about as good.
+     * 2. Times-9 is (shift-left-3) plus (old).  This means that each
+     *    character's bits hang around in the low-order bits of the
+     *    hash value for ever, plus they spread fairly rapidly up to
+     *    the high-order bits to fill out the hash value.  This seems
+     *    to work well both for decimal and non-decimal strings.
+     */
+
+    result = 0;
+    while ((c = *string++) != 0) {
+	result += (result << 3) + c;
+    }
+    return (Blt_Hash)result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringFind --
+ *
+ *	Given a hash table with string keys, and a string key, find
+ *	the entry with a matching key.
+ *
+ * Results:
+ *	The return value is a token for the matching entry in the
+ *	hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+StringFind(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key)		/* Key to use to find matching entry. */
+{
+    Blt_Hash hval;
+    register Blt_HashEntry *hPtr;
+    size_t hindex;
+
+    hval = HashString((char *)key);
+    hindex = hval & tablePtr->mask;
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->hval == hval) {
+	    register CONST char *p1, *p2;
+
+	    for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+		if (*p1 != *p2) {
+		    break;
+		}
+		if (*p1 == '\0') {
+		    return hPtr;
+		}
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringCreate --
+ *
+ *	Given a hash table with string keys, and a string key, find
+ *	the entry with a matching key.  If there is no matching entry,
+ *	then create a new entry that does match.
+ *
+ * Results:
+ *	The return value is a pointer to the matching entry.  If this
+ *	is a newly-created entry, then *newPtr will be set to a non-zero
+ *	value;  otherwise *newPtr will be set to 0.  If this is a new
+ *	entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *	A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+StringCreate(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key,		/* Key to use to find or create matching
+				 * entry. */
+    int *newPtr)		/* Store info here telling whether a new
+				 * entry was created. */
+{
+    Blt_Hash hval;
+    Blt_HashEntry **bucketPtr;
+    register Blt_HashEntry *hPtr;
+    size_t size, hindex;
+
+    hval = HashString(key);
+    hindex = hval & tablePtr->mask;
+
+    /*
+     * Search all of the entries in this bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->hval == hval) {
+	    register CONST char *p1, *p2;
+
+	    for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+		if (*p1 != *p2) {
+		    break;
+		}
+		if (*p1 == '\0') {
+		    *newPtr = FALSE;
+		    return hPtr;
+		}
+	    }
+	}
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+
+    *newPtr = TRUE;
+    size = sizeof(Blt_HashEntry) + strlen(key) - sizeof(Blt_HashKey) + 1;
+    if (tablePtr->hPool != NULL) {
+	hPtr = Blt_PoolAllocItem(tablePtr->hPool, size);
+    } else {
+	hPtr = Blt_Malloc(size);
+    }
+    bucketPtr = tablePtr->buckets + hindex;
+    hPtr->nextPtr = *bucketPtr;
+    hPtr->hval = hval;
+    hPtr->clientData = 0;
+    strcpy(hPtr->key.string, key);
+    *bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+	RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+
+#if (SIZEOF_VOID_P == 8)
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashOneWord --
+ *
+ *	Compute a one-word hash value of a 64-bit word, which then can
+ *	be used to generate a hash index.
+ *
+ *	From Knuth, it's a multiplicative hash.  Multiplies an unsigned
+ *	64-bit value with the golden ratio (sqrt(5) - 1) / 2.  The
+ *	downshift value is 64 - n, when n is the log2 of the size of
+ *	the hash table.
+ *		
+ * Results:
+ *	The return value is a one-word summary of the information in
+ *	64 bit word.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_Hash
+HashOneWord(
+    Blt_HashTable *tablePtr,
+    CONST void *key)
+{
+    uint64_t a0, a1;
+    uint64_t y0, y1;
+    uint64_t y2, y3; 
+    uint64_t p1, p2;
+    uint64_t result;
+    /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
+    a0 = (uint64_t)key & 0x00000000FFFFFFFF; 
+    a1 = (uint64_t)key >> 32;
+    
+    y0 = a0 * 0x000000007f4a7c13;
+    y1 = a0 * 0x000000009e3779b9; 
+    y2 = a1 * 0x000000007f4a7c13;
+    y3 = a1 * 0x000000009e3779b9; 
+    y1 += y0 >> 32;		/* Can't carry */ 
+    y1 += y2;			/* Might carry */
+    if (y1 < y2) {
+	y3 += (1LL << 32);	/* Propagate */ 
+    }
+
+    /* 128-bit product: p1 = loword, p2 = hiword */
+    p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
+    p2 = y3 + (y1 >> 32);
+    
+    /* Left shift the value downward by the size of the table */
+    if (tablePtr->downShift > 0) { 
+	if (tablePtr->downShift < 64) { 
+	    result = ((p2 << (64 - tablePtr->downShift)) | 
+		      (p1 >> (tablePtr->downShift & 63))); 
+	} else { 
+	    result = p2 >> (tablePtr->downShift & 63); 
+	} 
+    } else { 
+	result = p1;
+    } 
+    /* Finally mask off the high bits */
+    return (Blt_Hash)(result & tablePtr->mask);
+}
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordFind --
+ *
+ *	Given a hash table with one-word keys, and a one-word key,
+ *	find the entry with a matching key.
+ *
+ * Results:
+ *	The return value is a token for the matching entry in the
+ *	hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_HashEntry *
+OneWordFind(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    register CONST void *key)	/* Key to use to find matching entry. */
+{
+    register Blt_HashEntry *hPtr;
+    size_t hindex;
+
+    hindex = RANDOM_INDEX(tablePtr, key);
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->key.oneWordValue == key) {
+	    return hPtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordCreate --
+ *
+ *	Given a hash table with one-word keys, and a one-word key, find
+ *	the entry with a matching key.  If there is no matching entry,
+ *	then create a new entry that does match.
+ *
+ * Results:
+ *	The return value is a pointer to the matching entry.  If this
+ *	is a newly-created entry, then *newPtr will be set to a non-zero
+ *	value;  otherwise *newPtr will be set to 0.  If this is a new
+ *	entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *	A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+OneWordCreate(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key,		/* Key to use to find or create matching
+				 * entry. */
+    int *newPtr)		/* Store info here telling whether a new
+				 * entry was created. */
+{
+    Blt_HashEntry **bucketPtr;
+    register Blt_HashEntry *hPtr;
+    size_t hindex;
+
+    hindex = RANDOM_INDEX(tablePtr, key);
+
+    /*
+     * Search all of the entries in this bucket.
+     */
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->key.oneWordValue == key) {
+	    *newPtr = FALSE;
+	    return hPtr;
+	}
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+
+    *newPtr = TRUE;
+    if (tablePtr->hPool != NULL) {
+	hPtr = Blt_PoolAllocItem(tablePtr->hPool, sizeof(Blt_HashEntry));
+    } else {
+	hPtr = Blt_Malloc(sizeof(Blt_HashEntry));
+    }	
+    bucketPtr = tablePtr->buckets + hindex;
+    hPtr->nextPtr = *bucketPtr;
+    hPtr->hval = (Blt_Hash)key;
+    hPtr->clientData = 0;
+    hPtr->key.oneWordValue = (void *)key;	/* CONST XXXX */
+    *bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+	RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+
+
+#if (SIZEOF_VOID_P == 4)
+/*
+ * --------------------------------------------------------------------
+ *
+ * MIX32 -- 
+ *
+ *      Bob Jenkins, 1996.  Public Domain.
+ *
+ *	Mix 3 32/64-bit values reversibly.  For every delta with one or
+ *	two bit set, and the deltas of all three high bits or all
+ *	three low bits, whether the original value of a,b,c is almost
+ *	all zero or is uniformly distributed, If mix() is run
+ *	forward or backward, at least 32 bits in a,b,c have at least
+ *	1/4 probability of changing.  * If mix() is run forward, every
+ *	bit of c will change between 1/3 and 2/3 of the time.  (Well,
+ *	22/100 and 78/100 for some 2-bit deltas.)  mix() was built out
+ *	of 36 single-cycle latency instructions in a structure that
+ *	could supported 2x parallelism, like so:
+ *
+ *		a -= b; 
+ *		a -= c; x = (c>>13);
+ *		b -= c; a ^= x;
+ *		b -= a; x = (a<<8);
+ *		c -= a; b ^= x;
+ *		c -= b; x = (b>>13);
+ *		...
+ *
+ *	 Unfortunately, superscalar Pentiums and Sparcs can't take
+ *	 advantage of that parallelism.  They've also turned some of
+ *	 those single-cycle latency instructions into multi-cycle
+ *	 latency instructions.  Still, this is the fastest good hash I
+ *	 could find.  There were about 2^^68 to choose from.  I only
+ *	 looked at a billion or so.
+ *
+ * -------------------------------------------------------------------- 
+ */
+#define MIX32(a,b,c) \
+	a -= b, a -= c, a ^= (c >> 13), \
+	b -= c, b -= a, b ^= (a <<  8), \
+	c -= a, c -= b, c ^= (b >> 13), \
+	a -= b, a -= c, a ^= (c >> 12), \
+	b -= c, b -= a, b ^= (a << 16), \
+	c -= a, c -= b, c ^= (b >>  5), \
+	a -= b, a -= c, a ^= (c >>  3), \
+	b -= c, b -= a, b ^= (a << 10), \
+	c -= a, c -= b, c ^= (b >> 15)
+
+#define GOLDEN_RATIO32	0x9e3779b9	/* An arbitrary value */
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * HashArray --
+ *
+ *      Bob Jenkins, 1996.  Public Domain.
+ *
+ *	This works on all machines.  Length has to be measured in
+ *	unsigned longs instead of bytes.  It requires that
+ *
+ *	  o The key be an array of unsigned ints.
+ *	  o All your machines have the same endianness
+ *	  o The length be the number of unsigned ints in the key.
+ *
+ * --------------------------------------------------------------------
+ */
+static Blt_Hash
+HashArray(
+    CONST void *key, 
+    size_t length)		/* Length of the key in 32-bit words */
+{
+    register uint32_t a, b, c, len;
+    register uint32_t *arrayPtr = (uint32_t *)key;
+    /* Set up the internal state */
+    len = length;
+    a = b = GOLDEN_RATIO32;	/* An arbitrary value */
+    c = 0;			/* Previous hash value */
+
+    while (len >= 3) {		/* Handle most of the key */
+	a += arrayPtr[0];
+	b += arrayPtr[1];
+	c += arrayPtr[2];
+	MIX32(a, b, c);
+	arrayPtr += 3; len -= 3;
+    }
+    c += length;		
+    /* And now the last 2 words */
+    /* Note that all the case statements fall through */
+    switch(len) {
+	/* c is reserved for the length */
+    case 2 : b += arrayPtr[1];
+    case 1 : a += arrayPtr[0];
+	/* case 0: nothing left to add */
+    }
+    MIX32(a, b, c);
+    return (Blt_Hash)c;
+}
+#endif /* SIZEOF_VOID_P == 4 */
+
+#if (SIZEOF_VOID_P == 8)
+
+/* 
+ * --------------------------------------------------------------------
+ *
+ * MIX64 --
+ *
+ *	Bob Jenkins, January 4 1997, Public Domain.  You can use
+ *	this free for any purpose.  It has no warranty.
+ *
+ *	Returns a 64-bit value.  Every bit of the key affects every
+ *	bit of the return value.  No funnels.  Every 1-bit and 2-bit
+ *	delta achieves avalanche.  About 41+5len instructions.
+ *
+ *	The best hash table sizes are powers of 2.  There is no need
+ *	to do mod a prime (mod is sooo slow!).  If you need less than
+ *	64 bits, use a bitmask.  For example, if you need only 10
+ *	bits, do h = (h & hashmask(10)); In which case, the hash table
+ *	should have hashsize(10) elements.
+ *
+ *	By Bob Jenkins, Jan 4 1997.  bob_jenkins@burtleburtle.net.
+ *	You may use this code any way you wish, private, educational,
+ *	or commercial, as long as this whole comment accompanies it.
+ * 
+ *	See http://burtleburtle.net/bob/hash/evahash.html
+ *	Use for hash table lookup, or anything where one collision in
+ *	2^^64 * is acceptable.  Do NOT use for cryptographic purposes.
+ *
+ * --------------------------------------------------------------------
+ */
+
+#define MIX64(a,b,c) \
+	a -= b, a -= c, a ^= (c >> 43), \
+	b -= c, b -= a, b ^= (a <<  9), \
+	c -= a, c -= b, c ^= (b >>  8), \
+	a -= b, a -= c, a ^= (c >> 38), \
+	b -= c, b -= a, b ^= (a << 23), \
+	c -= a, c -= b, c ^= (b >>  5), \
+	a -= b, a -= c, a ^= (c >> 35), \
+	b -= c, b -= a, b ^= (a << 49), \
+	c -= a, c -= b, c ^= (b >> 11), \
+	a -= b, a -= c, a ^= (c >> 12), \
+	b -= c, b -= a, b ^= (a << 18), \
+	c -= a, c -= b, c ^= (b >> 22)
+
+#define GOLDEN_RATIO64	0x9e3779b97f4a7c13LL
+
+/*
+ * --------------------------------------------------------------------
+ *
+ * HashArray --
+ *
+ *	Bob Jenkins, January 4 1997, Public Domain.  You can use
+ *	this free for any purpose.  It has no warranty.
+ *
+ *	This works on all machines.  The length has to be measured in
+ *	64 bit words, instead of bytes.  It requires that
+ *
+ *	  o The key be an array of 64 bit words (unsigned longs).
+ *	  o All your machines have the same endianness.
+ *	  o The length be the number of 64 bit words in the key.
+ *
+ * --------------------------------------------------------------------
+ */
+static Blt_Hash
+HashArray(
+    CONST void *key, 
+    size_t length)		/* Length of key in 32-bit words. */
+{
+    register uint64_t a, b, c, len;
+    register uint32_t *iPtr = (uint32_t *)key;
+
+#ifdef WORDS_BIGENDIAN
+#define PACK(a,b)	((uint64_t)(b) | ((uint64_t)(a) << 32))
+#else
+#define PACK(a,b)	((uint64_t)(a) | ((uint64_t)(b) << 32))
+#endif
+    /* Set up the internal state */
+    len = length;		/* Length is the number of 64-bit words. */
+    a = b = GOLDEN_RATIO64;	/* An arbitrary value */
+    c = 0;			/* Previous hash value */
+
+    while (len >= 6) {		/* Handle most of the key */
+	a += PACK(iPtr[0], iPtr[1]);
+	b += PACK(iPtr[2], iPtr[3]);
+	c += PACK(iPtr[4], iPtr[5]);
+	MIX64(a,b,c);
+	iPtr += 6; len -= 6;
+    }
+    c += length;		
+    /* And now the last 2 words */
+    /* Note that all the case statements fall through */
+    switch(len) {
+	/* c is reserved for the length */
+    case 5 : 
+    case 4 : 
+	a += PACK(iPtr[0], iPtr[1]);
+	b += PACK(iPtr[2], iPtr[3]);
+	iPtr += 4; len -= 4;
+	break;
+    case 3 : 
+    case 2 : 
+	a += PACK(iPtr[0], iPtr[1]);
+	iPtr += 2; len -= 2;
+ /* case 0: nothing left to add */
+    }
+    if (len > 0) {		
+	b += iPtr[0];
+    }
+    MIX64(a,b,c);
+    return (Blt_Hash)c;
+}
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayFind --
+ *
+ *	Given a hash table with array-of-int keys, and a key, find
+ *	the entry with a matching key.
+ *
+ * Results:
+ *	The return value is a token for the matching entry in the
+ *	hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+ArrayFind(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key)		/* Key to use to find matching entry. */
+{
+    Blt_Hash hval;
+    register Blt_HashEntry *hPtr;
+    size_t hindex;
+
+    hval = HashArray(key, tablePtr->keyType);
+    hindex = hval & tablePtr->mask;
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->hval == hval) {
+	    register unsigned int *iPtr1, *iPtr2;
+	    unsigned int count;
+
+	    for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words,
+		     count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+		if (count == 0) {
+		    return hPtr;
+		}
+		if (*iPtr1 != *iPtr2) {
+		    break;
+		}
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayCreate --
+ *
+ *	Given a hash table with one-word keys, and a one-word key, find
+ *	the entry with a matching key.  If there is no matching entry,
+ *	then create a new entry that does match.
+ *
+ * Results:
+ *	The return value is a pointer to the matching entry.  If this
+ *	is a newly-created entry, then *newPtr will be set to a non-zero
+ *	value;  otherwise *newPtr will be set to 0.  If this is a new
+ *	entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *	A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_HashEntry *
+ArrayCreate(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    register CONST void *key,	/* Key to use to find or create matching
+				 * entry. */
+    int *newPtr)		/* Store info here telling whether a new
+				 * entry was created. */
+{
+    Blt_Hash hval;
+    Blt_HashEntry **bucketPtr;
+    int count;
+    register Blt_HashEntry *hPtr;
+    register uint32_t *iPtr1, *iPtr2;
+    size_t size, hindex;
+
+    hval = HashArray(key, tablePtr->keyType);
+    hindex = hval & tablePtr->mask;
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
+	    hPtr = hPtr->nextPtr) {
+	if (hPtr->hval == hval) {
+	    for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words,
+		     count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+		if (count == 0) {
+		    *newPtr = FALSE;
+		    return hPtr;
+		}
+		if (*iPtr1 != *iPtr2) {
+		    break;
+		}
+	    }
+	}
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+    *newPtr = TRUE;
+    /* We assume here that the size of the key is at least 2 words */
+    size = sizeof(Blt_HashEntry) + tablePtr->keyType * sizeof(uint32_t) - 
+	sizeof(Blt_HashKey);
+    if (tablePtr->hPool != NULL) {
+	hPtr = Blt_PoolAllocItem(tablePtr->hPool, size);
+    } else {
+	hPtr = Blt_Malloc(size);
+    }
+    bucketPtr = tablePtr->buckets + hindex;
+    hPtr->nextPtr = *bucketPtr;
+    hPtr->hval = hval;
+    hPtr->clientData = 0;
+    count = tablePtr->keyType;
+    for (iPtr1 = (uint32_t *)key, iPtr2 = (uint32_t *)hPtr->key.words; 
+	 count > 0; count--, iPtr1++, iPtr2++) {
+	*iPtr2 = *iPtr1;
+    }
+    *bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+	RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusFind --
+ *
+ *	This procedure is invoked when an Blt_FindHashEntry is called
+ *	on a table that has been deleted.
+ *
+ * Results:
+ *	If panic returns (which it shouldn't) this procedure returns
+ *	NULL.
+ *
+ * Side effects:
+ *	Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static Blt_HashEntry *
+BogusFind(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key)		/* Key to use to find matching entry. */
+{
+    Blt_Panic("called Blt_FindHashEntry on deleted table");
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusCreate --
+ *
+ *	This procedure is invoked when an Blt_CreateHashEntry is called
+ *	on a table that has been deleted.
+ *
+ * Results:
+ *	If panic returns (which it shouldn't) this procedure returns
+ *	NULL.
+ *
+ * Side effects:
+ *	Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static Blt_HashEntry *
+BogusCreate(
+    Blt_HashTable *tablePtr,	/* Table in which to lookup entry. */
+    CONST void *key,		/* Key to use to find or create matching
+				 * entry. */
+    int *newPtr)		/* Store info here telling whether a new
+				 * entry was created. */
+{
+    Blt_Panic("called Blt_CreateHashEntry on deleted table");
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildTable --
+ *
+ *	This procedure is invoked when the ratio of entries to hash
+ *	buckets becomes too large.  It creates a new table with a
+ *	larger bucket array and moves all of the entries into the
+ *	new table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory gets reallocated and entries get re-hashed to new
+ *	buckets.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+RebuildTable(Blt_HashTable *tablePtr) /* Table to enlarge. */
+{
+    Blt_HashEntry **bucketPtr, **oldBuckets;
+    register Blt_HashEntry **oldChainPtr, **endPtr;
+    register Blt_HashEntry *hPtr, *nextPtr;
+    size_t hindex;
+
+    oldBuckets = tablePtr->buckets;
+    endPtr = tablePtr->buckets + tablePtr->numBuckets;
+    /*
+     * Allocate and initialize the new bucket array, and set up
+     * hashing constants for new array size.
+     */
+    tablePtr->numBuckets <<= 2;
+    tablePtr->buckets = Blt_Calloc(tablePtr->numBuckets, 
+				   sizeof(Blt_HashEntry *));
+    tablePtr->rebuildSize <<= 2;
+    tablePtr->downShift -= 2;
+    tablePtr->mask = tablePtr->numBuckets - 1;
+
+    /*
+     * Move all of the existing entries into the new bucket array,
+     * based on their hash values.  
+     */
+    if (tablePtr->keyType == BLT_ONE_WORD_KEYS) {
+	/* 
+	 * BLT_ONE_WORD_KEYS are handled slightly differently because
+	 * they use the current table size (number of buckets) to be
+	 * distributed.
+	 */
+	for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
+	    for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
+		nextPtr = hPtr->nextPtr;
+		hindex = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
+		bucketPtr = tablePtr->buckets + hindex;
+		hPtr->nextPtr = *bucketPtr;
+		*bucketPtr = hPtr;
+	    }
+	}
+    } else {
+	for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
+	    for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
+		nextPtr = hPtr->nextPtr;
+		hindex = hPtr->hval & tablePtr->mask;
+		bucketPtr = tablePtr->buckets + hindex;
+		hPtr->nextPtr = *bucketPtr;
+		*bucketPtr = hPtr;
+	    }
+	}
+    }
+
+    /*
+     * Free up the old bucket array, if it was dynamically allocated.
+     */
+    if (oldBuckets != tablePtr->staticBuckets) {
+	Blt_Free(oldBuckets);
+    }
+}
+
+
+
+/* Public hash table routines */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitHashTable --
+ *
+ *	Given storage for a hash table, set up the fields to prepare
+ *	the hash table for use.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	TablePtr is now ready to be passed to Blt_FindHashEntry and
+ *	Blt_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_InitHashTable(
+    register Blt_HashTable *tablePtr,	/* Pointer to table record, which
+					 * is supplied by the caller. */
+    size_t keyType)	                /* Type of keys to use in table. */
+{
+#if (BLT_SMALL_HASH_TABLE != 4) 
+    Blt_Panic("Blt_InitHashTable: BLT_SMALL_HASH_TABLE is %d, not 4\n",
+	    BLT_SMALL_HASH_TABLE);
+#endif
+    tablePtr->buckets = tablePtr->staticBuckets;
+    tablePtr->numBuckets = BLT_SMALL_HASH_TABLE;
+    tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
+    tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
+    tablePtr->numEntries = 0;
+    tablePtr->rebuildSize = BLT_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
+    tablePtr->downShift = DOWNSHIFT_START;
+
+    /* The number of buckets is always a power of 2, so we can
+     * generate the mask by simply subtracting 1 from the number of
+     * buckets. */
+    tablePtr->mask = (Blt_Hash)(tablePtr->numBuckets - 1);
+    tablePtr->keyType = keyType;
+
+    switch (keyType) {
+    case BLT_STRING_KEYS:	/* NUL terminated string keys. */
+	tablePtr->findProc = StringFind;
+	tablePtr->createProc = StringCreate;
+	break;
+
+    case BLT_ONE_WORD_KEYS:	/* 32 or 64 bit atomic keys. */
+	tablePtr->findProc = OneWordFind;
+	tablePtr->createProc = OneWordCreate;
+	break;
+
+    default:			/* Structures/arrays. */
+	if (keyType == 0) {
+	    Blt_Panic("Blt_InitHashTable: Key size can't be %d, must be > 0\n",
+		  keyType);
+	}
+	tablePtr->findProc = ArrayFind;
+	tablePtr->createProc = ArrayCreate;
+	break;
+    }
+    tablePtr->hPool = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitHashTableWithPool --
+ *
+ *	Given storage for a hash table, set up the fields to prepare
+ *	the hash table for use.  The only difference between this 
+ *	routine and Blt_InitHashTable is that is uses a pool allocator
+ *	to allocate memory for hash table entries.  The type of pool
+ *	is either fixed or variable size (string) keys.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	TablePtr is now ready to be passed to Blt_FindHashEntry and
+ *	Blt_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_InitHashTableWithPool(
+    register Blt_HashTable *tablePtr,	/* Pointer to table record, which
+					 * is supplied by the caller. */
+    size_t keyType)			/* Type of keys to use in table. */
+{
+    Blt_InitHashTable(tablePtr, keyType);
+    if (keyType == BLT_STRING_KEYS) {
+	tablePtr->hPool = Blt_PoolCreate(BLT_VARIABLE_SIZE_ITEMS);
+    } else {
+	tablePtr->hPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DeleteHashEntry --
+ *
+ *	Remove a single entry from a hash table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The entry given by entryPtr is deleted from its table and
+ *	should never again be used by the caller.  It is up to the
+ *	caller to free the clientData field of the entry, if that
+ *	is relevant.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DeleteHashEntry(
+    Blt_HashTable *tablePtr,
+    Blt_HashEntry *entryPtr)
+{
+    register Blt_HashEntry *prevPtr;
+    Blt_HashEntry **bucketPtr;
+    size_t hindex;
+
+    if (tablePtr->keyType == BLT_ONE_WORD_KEYS) {
+	hindex = RANDOM_INDEX(tablePtr, (CONST void *)entryPtr->hval);
+    } else {
+	hindex = (entryPtr->hval & tablePtr->mask);
+    }	
+    bucketPtr = tablePtr->buckets + hindex;
+    if (*bucketPtr == entryPtr) {
+	*bucketPtr = entryPtr->nextPtr;
+    } else {
+	for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->nextPtr) {
+	    if (prevPtr == NULL) {
+		Blt_Panic("malformed bucket chain in Blt_DeleteHashEntry");
+	    }
+	    if (prevPtr->nextPtr == entryPtr) {
+		prevPtr->nextPtr = entryPtr->nextPtr;
+		break;
+	    }
+	}
+    }
+    tablePtr->numEntries--;
+    if (tablePtr->hPool != NULL) {
+	Blt_PoolFreeItem(tablePtr->hPool, (char *)entryPtr);
+    } else {
+	Blt_Free(entryPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DeleteHashTable --
+ *
+ *	Free up everything associated with a hash table except for
+ *	the record for the table itself.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The hash table is no longer useable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_DeleteHashTable(Blt_HashTable *tablePtr)	/* Table to delete. */
+{
+    /*
+     * Free up all the entries in the table.
+     */
+    if (tablePtr->hPool != NULL) {
+	Blt_PoolDestroy(tablePtr->hPool);
+	tablePtr->hPool = NULL;
+    } else {
+	register Blt_HashEntry *hPtr, *nextPtr;
+	size_t i;
+
+	for (i = 0; i < tablePtr->numBuckets; i++) {
+	    hPtr = tablePtr->buckets[i];
+	    while (hPtr != NULL) {
+		nextPtr = hPtr->nextPtr;
+		Blt_Free(hPtr);
+		hPtr = nextPtr;
+	    }
+	}
+    }
+    
+    /*
+     * Free up the bucket array, if it was dynamically allocated.
+     */
+    if (tablePtr->buckets != tablePtr->staticBuckets) {
+	Blt_Free(tablePtr->buckets);
+    }
+
+    /*
+     * Arrange for panics if the table is used again without
+     * re-initialization.
+     */
+
+    tablePtr->findProc = BogusFind;
+    tablePtr->createProc = BogusCreate;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FirstHashEntry --
+ *
+ *	Locate the first entry in a hash table and set up a record
+ *	that can be used to step through all the remaining entries
+ *	of the table.
+ *
+ * Results:
+ *	The return value is a pointer to the first entry in tablePtr,
+ *	or NULL if tablePtr has no entries in it.  The memory at
+ *	*searchPtr is initialized so that subsequent calls to
+ *	Blt_NextHashEntry will return all of the entries in the table,
+ *	one at a time.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashEntry *
+Blt_FirstHashEntry(
+    Blt_HashTable *tablePtr,		/* Table to search. */
+    Blt_HashSearch *searchPtr)		/* Place to store information about
+					 * progress through the table. */
+{
+    searchPtr->tablePtr = tablePtr;
+    searchPtr->nextIndex = 0;
+    searchPtr->nextEntryPtr = NULL;
+    return Blt_NextHashEntry(searchPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NextHashEntry --
+ *
+ *	Once a hash table enumeration has been initiated by calling
+ *	Blt_FirstHashEntry, this procedure may be called to return
+ *	successive elements of the table.
+ *
+ * Results:
+ *	The return value is the next entry in the hash table being
+ *	enumerated, or NULL if the end of the table is reached.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashEntry *
+Blt_NextHashEntry(Blt_HashSearch *searchPtr)
+{
+    Blt_HashEntry *hPtr;
+
+    while (searchPtr->nextEntryPtr == NULL) {
+	if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {
+	    return NULL;
+	}
+	searchPtr->nextEntryPtr =
+		searchPtr->tablePtr->buckets[searchPtr->nextIndex];
+	searchPtr->nextIndex++;
+    }
+    hPtr = searchPtr->nextEntryPtr;
+    searchPtr->nextEntryPtr = hPtr->nextPtr;
+    return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_HashStats --
+ *
+ *	Return statistics describing the layout of the hash table
+ *	in its hash buckets.
+ *
+ * Results:
+ *	The return value is a malloc-ed string containing information
+ *	about tablePtr.  It is the caller's responsibility to free
+ *	this string.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_HashStats(Blt_HashTable *tablePtr) /* Table for which to produce stats. */
+{
+#define NUM_COUNTERS 10
+    size_t count[NUM_COUNTERS], overflow, i, j, max;
+    double average, tmp;
+    register Blt_HashEntry *hPtr;
+    Blt_HashEntry **bucketPtr, **endPtr;
+    char *result, *p;
+
+    /*
+     * Compute a histogram of bucket usage.
+     */
+    for (i = 0; i < NUM_COUNTERS; i++) {
+	count[i] = 0;
+    }
+    overflow = 0;
+    average = 0.0;
+    max = 0;
+    endPtr = tablePtr->buckets + tablePtr->numBuckets;
+    for (bucketPtr = tablePtr->buckets; bucketPtr < endPtr; bucketPtr++) {
+	j = 0;
+	for (hPtr = *bucketPtr; hPtr != NULL; hPtr = hPtr->nextPtr) {
+	    j++;
+	}
+	if (j > max) {
+	    max = j;
+	}
+	if (j < NUM_COUNTERS) {
+	    count[j]++;
+	} else {
+	    overflow++;
+	}
+	tmp = j;
+	average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
+    }
+
+    /*
+     * Print out the histogram and a few other pieces of information.
+     */
+    result = Blt_Malloc((unsigned) ((NUM_COUNTERS*60) + 300));
+#if SIZEOF_VOID_P == 8
+    sprintf(result, "%ld entries in table, %ld buckets\n",
+	    tablePtr->numEntries, tablePtr->numBuckets);
+#else 
+    sprintf(result, "%d entries in table, %d buckets\n",
+	    tablePtr->numEntries, tablePtr->numBuckets);
+#endif
+    p = result + strlen(result);
+    for (i = 0; i < NUM_COUNTERS; i++) {
+#if SIZEOF_VOID_P == 8
+	sprintf(p, "number of buckets with %ld entries: %ld\n",
+		i, count[i]);
+#else 
+	sprintf(p, "number of buckets with %d entries: %d\n",
+		i, count[i]);
+#endif
+	p += strlen(p);
+    }
+#if SIZEOF_VOID_P == 8
+    sprintf(p, "number of buckets with %d or more entries: %ld\n",
+	    NUM_COUNTERS, overflow);
+#else 
+    sprintf(p, "number of buckets with %d or more entries: %d\n",
+	    NUM_COUNTERS, overflow);
+#endif
+    p += strlen(p);
+    sprintf(p, "average search distance for entry: %.2f\n", average);
+    p += strlen(p);
+#if SIZEOF_VOID_P == 8
+    sprintf(p, "maximum search distance for entry: %ld", max);
+#else
+    sprintf(p, "maximum search distance for entry: %d", max);
+#endif
+    return result;
+}
Index: trunk/kitgen/8.x/blt/generic/bltHash.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltHash.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltHash.h	(revision 175)
@@ -0,0 +1,223 @@
+
+/* 
+ * bltHash.h --
+ *
+ * Copyright 2001 Silicon Metrics Corporation.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Bob Jenkins, 1996.  hash.c.  Public Domain.
+ * Bob Jenkins, 1997.  lookup8.c.  Public Domain.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: bltHash.h.in,v 1.5 2002/07/14 00:08:13 ghowlett Exp $
+ */
+
+#ifndef BLT_HASH_H
+#define BLT_HASH_H 1
+
+#ifndef BLT_INT_H
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG 4
+#endif
+#ifndef SIZEOF_LONG_LONG
+#define SIZEOF_LONG_LONG 8
+#endif
+#ifndef SIZEOF_INT
+#define SIZEOF_INT 4
+#endif
+#ifndef SIZEOF_VOID_P
+#define SIZEOF_VOID_P 4
+#endif
+#ifndef HAVE_INTTYPES_H
+#if 1
+#define HAVE_INTTYPES_H 1
+#endif
+#endif
+#endif /* !BLT_INT_H */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#if (SIZEOF_VOID_P == 8)
+#if (SIZEOF_LONG == 8)
+typedef signed long int64_t;
+typedef unsigned long uint64_t;
+#else
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#endif /* SIZEOF_LONG == 8 */
+#else
+#ifndef __CYGWIN__
+typedef signed int int32_t;
+#endif /* __CYGWIN__ */
+typedef unsigned int uint32_t;
+#endif /* SIZEOF_VOID_P == 8 */
+#endif /* HAVE_INTTYPES_H */
+
+#if (SIZEOF_VOID_P == 8) 
+typedef uint64_t Blt_Hash;
+#else
+typedef uint32_t Blt_Hash;
+#endif /* SIZEOF_VOID_P == 8 */
+
+#include "bltPool.h"
+
+/*
+ * Acceptable key types for hash tables:
+ */
+#define BLT_STRING_KEYS		0
+#define BLT_ONE_WORD_KEYS	((size_t)-1)
+
+/*
+ * Forward declaration of Blt_HashTable.  Needed by some C++ compilers
+ * to prevent errors when the forward reference to Blt_HashTable is
+ * encountered in the Blt_HashEntry structure.
+ */
+
+#ifdef __cplusplus
+struct Blt_HashTable;
+#endif
+
+typedef union {			/* Key has one of these forms: */
+    void *oneWordValue;		/* One-word value for key. */
+    unsigned long words[1];	/* Multiple integer words for key.
+				 * The actual size will be as large
+				 * as necessary for this table's
+				 * keys. */
+    char string[4];		/* String for key.  The actual size
+				 * will be as large as needed to hold
+				 * the key. */
+} Blt_HashKey;
+
+/*
+ * Structure definition for an entry in a hash table.  No-one outside
+ * Blt should access any of these fields directly;  use the macros
+ * defined below.
+ */
+typedef struct Blt_HashEntry {
+    struct Blt_HashEntry *nextPtr;	/* Pointer to next entry in this
+					 * hash bucket, or NULL for end of
+					 * chain. */
+    Blt_Hash hval;
+
+    ClientData clientData;		/* Application stores something here
+					 * with Blt_SetHashValue. */
+    Blt_HashKey key;			/* MUST BE LAST FIELD IN RECORD!! */
+} Blt_HashEntry;
+
+/*
+ * Structure definition for a hash table.  Must be in blt.h so clients
+ * can allocate space for these structures, but clients should never
+ * access any fields in this structure.
+ */
+#define BLT_SMALL_HASH_TABLE 4
+typedef struct Blt_HashTable {
+    Blt_HashEntry **buckets;		/* Pointer to bucket array.  Each
+					 * element points to first entry in
+					 * bucket's hash chain, or NULL. */
+    Blt_HashEntry *staticBuckets[BLT_SMALL_HASH_TABLE];
+					/* Bucket array used for small tables
+					 * (to avoid mallocs and frees). */
+    size_t numBuckets;		        /* Total number of buckets allocated
+					 * at **buckets. */
+    size_t numEntries;	                /* Total number of entries present
+					 * in table. */
+    size_t rebuildSize;	                /* Enlarge table when numEntries gets
+					 * to be this large. */
+    Blt_Hash mask;		        /* Mask value used in hashing
+				         * function. */	
+    unsigned int downShift;	        /* Shift count used in hashing
+					 * function.  Designed to use high-
+					 * order bits of randomized keys. */
+    size_t keyType;			/* Type of keys used in this table. 
+					 * It's either BLT_STRING_KEYS,
+					 * BLT_ONE_WORD_KEYS, or an integer
+					 * giving the number of ints that
+                                         * is the size of the key.
+					 */
+    Blt_HashEntry *(*findProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+	    CONST void *key));
+    Blt_HashEntry *(*createProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+	    CONST void *key, int *newPtr));
+
+    Blt_Pool hPool;		/* Pointer to the pool allocator used
+				 * for entries in this hash table. If
+				 * NULL, the standard Tcl_Alloc,
+				 * Tcl_Free routines will be used
+				 * instead.
+				 */
+} Blt_HashTable;
+
+/*
+ * Structure definition for information used to keep track of searches
+ * through hash tables:
+ */
+
+typedef struct {
+    Blt_HashTable *tablePtr;		/* Table being searched. */
+    unsigned long nextIndex;	        /* Index of next bucket to be
+					 * enumerated after present one. */
+    Blt_HashEntry *nextEntryPtr;	/* Next entry to be enumerated in the
+					 * the current bucket. */
+} Blt_HashSearch;
+
+/*
+ * Macros for clients to use to access fields of hash entries:
+ */
+
+#define Blt_GetHashValue(h) ((h)->clientData)
+#define Blt_SetHashValue(h, value) ((h)->clientData = (ClientData)(value))
+#define Blt_GetHashKey(tablePtr, h) \
+    ((void *) (((tablePtr)->keyType == BLT_ONE_WORD_KEYS) ? \
+     (void *)(h)->key.oneWordValue : (h)->key.string))
+
+/*
+ * Macros to use for clients to use to invoke find and create procedures
+ * for hash tables:
+ */
+#define Blt_FindHashEntry(tablePtr, key) \
+	(*((tablePtr)->findProc))(tablePtr, key)
+#define Blt_CreateHashEntry(tablePtr, key, newPtr) \
+	(*((tablePtr)->createProc))(tablePtr, key, newPtr)
+
+EXTERN void Blt_InitHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr, 
+	size_t keyType));
+
+EXTERN void Blt_InitHashTableWithPool _ANSI_ARGS_((Blt_HashTable *tablePtr, 
+	size_t keyType));
+
+EXTERN void Blt_DeleteHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+EXTERN void Blt_DeleteHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	Blt_HashEntry *entryPtr));
+
+EXTERN Blt_HashEntry *Blt_FirstHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	Blt_HashSearch *searchPtr));
+
+EXTERN Blt_HashEntry *Blt_NextHashEntry _ANSI_ARGS_((Blt_HashSearch *srchPtr));
+
+EXTERN char *Blt_HashStats _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+#endif /* BLT_HASH_H */
Index: trunk/kitgen/8.x/blt/generic/bltHash.h.in
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltHash.h.in	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltHash.h.in	(revision 175)
@@ -0,0 +1,223 @@
+
+/* 
+ * bltHash.h --
+ *
+ * Copyright 2001 Silicon Metrics Corporation.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * Bob Jenkins, 1996.  hash.c.  Public Domain.
+ * Bob Jenkins, 1997.  lookup8.c.  Public Domain.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: bltHash.h.in,v 1.5 2002/07/14 00:08:13 ghowlett Exp $
+ */
+
+#ifndef BLT_HASH_H
+#define BLT_HASH_H 1
+
+#ifndef BLT_INT_H
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG @SIZEOF_LONG@
+#endif
+#ifndef SIZEOF_LONG_LONG
+#define SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@
+#endif
+#ifndef SIZEOF_INT
+#define SIZEOF_INT @SIZEOF_INT@
+#endif
+#ifndef SIZEOF_VOID_P
+#define SIZEOF_VOID_P @SIZEOF_VOID_P@
+#endif
+#ifndef HAVE_INTTYPES_H
+#if @HAVE_INTTYPES_H@
+#define HAVE_INTTYPES_H 1
+#endif
+#endif
+#endif /* !BLT_INT_H */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#if (SIZEOF_VOID_P == 8)
+#if (SIZEOF_LONG == 8)
+typedef signed long int64_t;
+typedef unsigned long uint64_t;
+#else
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#endif /* SIZEOF_LONG == 8 */
+#else
+#ifndef __CYGWIN__
+typedef signed int int32_t;
+#endif /* __CYGWIN__ */
+typedef unsigned int uint32_t;
+#endif /* SIZEOF_VOID_P == 8 */
+#endif /* HAVE_INTTYPES_H */
+
+#if (SIZEOF_VOID_P == 8) 
+typedef uint64_t Blt_Hash;
+#else
+typedef uint32_t Blt_Hash;
+#endif /* SIZEOF_VOID_P == 8 */
+
+#include "bltPool.h"
+
+/*
+ * Acceptable key types for hash tables:
+ */
+#define BLT_STRING_KEYS		0
+#define BLT_ONE_WORD_KEYS	((size_t)-1)
+
+/*
+ * Forward declaration of Blt_HashTable.  Needed by some C++ compilers
+ * to prevent errors when the forward reference to Blt_HashTable is
+ * encountered in the Blt_HashEntry structure.
+ */
+
+#ifdef __cplusplus
+struct Blt_HashTable;
+#endif
+
+typedef union {			/* Key has one of these forms: */
+    void *oneWordValue;		/* One-word value for key. */
+    unsigned long words[1];	/* Multiple integer words for key.
+				 * The actual size will be as large
+				 * as necessary for this table's
+				 * keys. */
+    char string[4];		/* String for key.  The actual size
+				 * will be as large as needed to hold
+				 * the key. */
+} Blt_HashKey;
+
+/*
+ * Structure definition for an entry in a hash table.  No-one outside
+ * Blt should access any of these fields directly;  use the macros
+ * defined below.
+ */
+typedef struct Blt_HashEntry {
+    struct Blt_HashEntry *nextPtr;	/* Pointer to next entry in this
+					 * hash bucket, or NULL for end of
+					 * chain. */
+    Blt_Hash hval;
+
+    ClientData clientData;		/* Application stores something here
+					 * with Blt_SetHashValue. */
+    Blt_HashKey key;			/* MUST BE LAST FIELD IN RECORD!! */
+} Blt_HashEntry;
+
+/*
+ * Structure definition for a hash table.  Must be in blt.h so clients
+ * can allocate space for these structures, but clients should never
+ * access any fields in this structure.
+ */
+#define BLT_SMALL_HASH_TABLE 4
+typedef struct Blt_HashTable {
+    Blt_HashEntry **buckets;		/* Pointer to bucket array.  Each
+					 * element points to first entry in
+					 * bucket's hash chain, or NULL. */
+    Blt_HashEntry *staticBuckets[BLT_SMALL_HASH_TABLE];
+					/* Bucket array used for small tables
+					 * (to avoid mallocs and frees). */
+    size_t numBuckets;		        /* Total number of buckets allocated
+					 * at **buckets. */
+    size_t numEntries;	                /* Total number of entries present
+					 * in table. */
+    size_t rebuildSize;	                /* Enlarge table when numEntries gets
+					 * to be this large. */
+    Blt_Hash mask;		        /* Mask value used in hashing
+				         * function. */	
+    unsigned int downShift;	        /* Shift count used in hashing
+					 * function.  Designed to use high-
+					 * order bits of randomized keys. */
+    size_t keyType;			/* Type of keys used in this table. 
+					 * It's either BLT_STRING_KEYS,
+					 * BLT_ONE_WORD_KEYS, or an integer
+					 * giving the number of ints that
+                                         * is the size of the key.
+					 */
+    Blt_HashEntry *(*findProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+	    CONST void *key));
+    Blt_HashEntry *(*createProc) _ANSI_ARGS_((struct Blt_HashTable *tablePtr,
+	    CONST void *key, int *newPtr));
+
+    Blt_Pool hPool;		/* Pointer to the pool allocator used
+				 * for entries in this hash table. If
+				 * NULL, the standard Tcl_Alloc,
+				 * Tcl_Free routines will be used
+				 * instead.
+				 */
+} Blt_HashTable;
+
+/*
+ * Structure definition for information used to keep track of searches
+ * through hash tables:
+ */
+
+typedef struct {
+    Blt_HashTable *tablePtr;		/* Table being searched. */
+    unsigned long nextIndex;	        /* Index of next bucket to be
+					 * enumerated after present one. */
+    Blt_HashEntry *nextEntryPtr;	/* Next entry to be enumerated in the
+					 * the current bucket. */
+} Blt_HashSearch;
+
+/*
+ * Macros for clients to use to access fields of hash entries:
+ */
+
+#define Blt_GetHashValue(h) ((h)->clientData)
+#define Blt_SetHashValue(h, value) ((h)->clientData = (ClientData)(value))
+#define Blt_GetHashKey(tablePtr, h) \
+    ((void *) (((tablePtr)->keyType == BLT_ONE_WORD_KEYS) ? \
+     (void *)(h)->key.oneWordValue : (h)->key.string))
+
+/*
+ * Macros to use for clients to use to invoke find and create procedures
+ * for hash tables:
+ */
+#define Blt_FindHashEntry(tablePtr, key) \
+	(*((tablePtr)->findProc))(tablePtr, key)
+#define Blt_CreateHashEntry(tablePtr, key, newPtr) \
+	(*((tablePtr)->createProc))(tablePtr, key, newPtr)
+
+EXTERN void Blt_InitHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr, 
+	size_t keyType));
+
+EXTERN void Blt_InitHashTableWithPool _ANSI_ARGS_((Blt_HashTable *tablePtr, 
+	size_t keyType));
+
+EXTERN void Blt_DeleteHashTable _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+EXTERN void Blt_DeleteHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	Blt_HashEntry *entryPtr));
+
+EXTERN Blt_HashEntry *Blt_FirstHashEntry _ANSI_ARGS_((Blt_HashTable *tablePtr,
+	Blt_HashSearch *searchPtr));
+
+EXTERN Blt_HashEntry *Blt_NextHashEntry _ANSI_ARGS_((Blt_HashSearch *srchPtr));
+
+EXTERN char *Blt_HashStats _ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+#endif /* BLT_HASH_H */
Index: trunk/kitgen/8.x/blt/generic/bltImage.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltImage.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltImage.c	(revision 175)
@@ -0,0 +1,2776 @@
+
+/*
+ * bltImage.c --
+ *
+ *	This module implements image processing procedures for the BLT
+ *	toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include <X11/Xutil.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+#define CLAMP(c)	((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateColorImage --
+ *
+ *      Allocates a color image of a designated height and width.
+ *
+ *	This routine will be augmented with other types of information
+ *	such as a color table, etc.
+ *
+ * Results:
+ *      Returns the new color image.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_CreateColorImage(width, height)
+    int width, height;		/* Dimensions of new image */
+{
+    struct ColorImage *imagePtr;
+    size_t size;
+
+    size = width * height;
+    imagePtr = Blt_Malloc(sizeof(struct ColorImage));
+    assert(imagePtr);
+    imagePtr->bits = Blt_Malloc(sizeof(Pix32) * size);
+    assert(imagePtr->bits);
+
+    imagePtr->width = width;
+    imagePtr->height = height;
+    return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeColorImage --
+ *
+ *      Deallocates the given color image.
+ *
+ * Results:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeColorImage(imagePtr)
+    struct ColorImage *imagePtr;
+{
+    Blt_Free(imagePtr->bits);
+    Blt_Free(imagePtr);
+}
+
+void
+Blt_GammaCorrectColorImage(src, newGamma)
+    Blt_ColorImage src;
+    double newGamma;
+{
+    unsigned int nPixels;
+    register Pix32 *srcPtr, *endPtr;
+    register unsigned int i;
+    double value;
+    unsigned char lut[256];
+    double invGamma;
+
+    invGamma = 1.0 / newGamma;
+    for (i = 0; i < 256; i++) {
+	value = 255.0 * pow((double)i / 255.0, invGamma);
+	lut[i] = (unsigned char)CLAMP(value);
+    }
+    nPixels = Blt_ColorImageWidth(src) * Blt_ColorImageHeight(src);
+    srcPtr = Blt_ColorImageBits(src);
+    for (endPtr = srcPtr + nPixels; srcPtr < endPtr; srcPtr++) {
+	srcPtr->Red = lut[srcPtr->Red];
+	srcPtr->Green = lut[srcPtr->Green];
+	srcPtr->Blue = lut[srcPtr->Blue];
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToGreyscale --
+ *
+ *	Converts a color image to PostScript grey scale (1 component)
+ *	output.  Luminosity isn't computed using the old NTSC formula,
+ *
+ *	    Y = 0.299 * Red + 0.587 * Green + 0.114 * Blue
+ *
+ *      but the following
+ *
+ *	    Y = 0.212671 * Red + 0.715160 * Green + 0.072169 * Blue
+ *
+ *	which better represents contemporary monitors.
+ *
+ * Results:
+ *	The color image is converted to greyscale.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorImageToGreyscale(image)
+    Blt_ColorImage image;
+{
+    register Pix32 *srcPtr, *endPtr;
+    double Y;
+    int nPixels;
+    int width, height;
+
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+    nPixels = width * height;
+    srcPtr = Blt_ColorImageBits(image);
+    for (endPtr = srcPtr + nPixels; srcPtr < endPtr; srcPtr++) {
+	Y = ((0.212671 * (double)srcPtr->Red) +
+	     (0.715160 * (double)srcPtr->Green) +
+	     (0.072169 * (double)srcPtr->Blue));
+	srcPtr->Red = srcPtr->Green = srcPtr->Blue = (unsigned char)CLAMP(Y);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPhoto --
+ *
+ *      Translates a color image into a Tk photo.
+ *
+ * Results:
+ *      The photo is re-written with the new color image.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorImageToPhoto(src, photo)
+    Blt_ColorImage src;		/* Image to use as source */
+    Tk_PhotoHandle photo;	/* Photo to write color image into */
+{
+    Tk_PhotoImageBlock dest;
+    int width, height;
+
+    width = Blt_ColorImageWidth(src);
+    height = Blt_ColorImageHeight(src);
+
+    Tk_PhotoGetImage(photo, &dest);
+    dest.pixelSize = sizeof(Pix32);
+    dest.pitch = sizeof(Pix32) * width;
+    dest.width = width;
+    dest.height = height;
+    dest.offset[0] = Tk_Offset(Pix32, Red);
+    dest.offset[1] = Tk_Offset(Pix32, Green);
+    dest.offset[2] = Tk_Offset(Pix32, Blue);
+    dest.offset[3] = Tk_Offset(Pix32, Alpha); 
+    dest.pixelPtr = (unsigned char *)Blt_ColorImageBits(src);
+    Tk_PhotoSetSize(photo, width, height);
+    Tk_PhotoPutBlock(photo, &dest, 0, 0, width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PhotoRegionToColorImage --
+ *
+ *      Create a photo to a color image.
+ *
+ * Results:
+ *      The new color image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_PhotoRegionToColorImage(photo, x, y, width, height)
+    Tk_PhotoHandle photo;	/* Source photo image to scale */
+    int x, y;
+    int width, height;
+{
+    Tk_PhotoImageBlock src;
+    Blt_ColorImage image;
+    register Pix32 *destPtr;
+    register unsigned char *srcData;
+    register int offset;
+    unsigned int offR, offG, offB, offA;
+
+    Tk_PhotoGetImage(photo, &src);
+    if (x < 0) {
+	x = 0;
+    } 
+    if (y < 0) {
+	y = 0;
+    }
+    if (width < 0) {
+	width = src.width;
+    }
+    if (height < 0) {
+	height = src.height;
+    }
+    if ((x + width) > src.width) {
+	width = src.width - x;
+    }
+    if ((height + y) > src.height) {
+	height = src.width - y;
+    }
+    image = Blt_CreateColorImage(width, height);
+    destPtr = Blt_ColorImageBits(image);
+
+    offset = (x * src.pixelSize) + (y * src.pitch);
+
+    offR = src.offset[0];
+    offG = src.offset[1];
+    offB = src.offset[2];
+    offA = src.offset[3];
+
+    if (src.pixelSize == 4) {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = srcData[offR];
+	        destPtr->Green = srcData[offG];
+	        destPtr->Blue = srcData[offB];
+	        destPtr->Alpha = srcData[offA];
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } else if (src.pixelSize == 3) {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = srcData[offR];
+	        destPtr->Green = srcData[offG];
+	        destPtr->Blue = srcData[offB];
+		/* No transparency information */
+	        destPtr->Alpha = (unsigned char)-1;
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } else {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = destPtr->Green = destPtr->Blue = srcData[offA];
+		/* No transparency information */
+	        destPtr->Alpha = (unsigned char)-1;
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } 
+    return image;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PhotoToColorImage --
+ *
+ *      Create a photo to a color image.
+ *
+ * Results:
+ *      The new color image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_PhotoToColorImage(photo)
+    Tk_PhotoHandle photo;	/* Source photo image to scale */
+
+{
+    Blt_ColorImage image;
+    Tk_PhotoImageBlock src;
+    int width, height;
+    register Pix32 *destPtr;
+    register int offset;
+    register int x, y;
+    register unsigned char *srcData;
+
+    Tk_PhotoGetImage(photo, &src);
+    width = src.width;
+    height = src.height;
+    image = Blt_CreateColorImage(width, height);
+    destPtr = Blt_ColorImageBits(image);
+    offset = 0;
+    if (src.pixelSize == 4) {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = srcData[src.offset[0]];
+	        destPtr->Green = srcData[src.offset[1]];
+	        destPtr->Blue = srcData[src.offset[2]];
+	        destPtr->Alpha = srcData[src.offset[3]];
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } else if (src.pixelSize == 3) {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = srcData[src.offset[0]];
+	        destPtr->Green = srcData[src.offset[1]];
+	        destPtr->Blue = srcData[src.offset[2]];
+		/* No transparency information */
+	        destPtr->Alpha = (unsigned char)-1;
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } else {
+        for (y = 0; y < height; y++) {
+	    srcData = src.pixelPtr + offset;
+	    for (x = 0; x < width; x++) {
+	        destPtr->Red = destPtr->Green = destPtr->Blue = 
+		    srcData[src.offset[0]];
+		/* No transparency information */
+	        destPtr->Alpha = (unsigned char)-1;
+	        srcData += src.pixelSize;
+	        destPtr++;
+	    }
+	    offset += src.pitch;
+        }
+    } 
+    return image;
+}
+
+/*
+ *	filter function definitions
+ */
+
+static ResampleFilterProc DefaultFilter;
+static ResampleFilterProc BellFilter;
+static ResampleFilterProc BesselFilter;
+static ResampleFilterProc BoxFilter;
+static ResampleFilterProc BSplineFilter;
+static ResampleFilterProc CatRomFilter;
+static ResampleFilterProc DummyFilter;
+static ResampleFilterProc GaussianFilter;
+static ResampleFilterProc GiFilter;
+static ResampleFilterProc Lanczos3Filter;
+static ResampleFilterProc MitchellFilter;
+static ResampleFilterProc SincFilter;
+static ResampleFilterProc TriangleFilter;
+static Tk_ImageChangedProc TempImageChangedProc;
+
+static double
+DefaultFilter(x)
+    double x;
+{
+    if (x < 0.0) {
+	x = -x;
+    }
+    if (x < 1.0) {
+	/* f(x) = 2x^3 - 3x^2 + 1, -1 <= x <= 1 */
+	return (2.0 * x - 3.0) * x * x + 1.0;
+    }
+    return 0.0;
+}
+
+/* Just for testing */
+static double
+DummyFilter(x)
+    double x;
+{
+    return FABS(x);
+}
+
+/*
+ *
+ * Finite filters in increasing order:
+ *	Box (constant)
+ *	Triangle (linear)
+ *	Bell
+ *	BSpline (cubic)
+ *
+ */
+static double
+BoxFilter(x)
+    double x;
+{
+    if ((x < -0.5) || (x > 0.5)) {
+	return 0.0;
+    }
+    return 1.0;
+}
+
+static double
+TriangleFilter(x)
+    double x;
+{
+    if (x < 0.0) {
+	x = -x;
+    }
+    if (x < 1.0) {
+	return (1.0 - x);
+    }
+    return 0.0;
+}
+
+static double
+BellFilter(x)
+    double x;
+{
+    if (x < 0.0) {
+	x = -x;
+    }
+    if (x < 0.5) {
+	return (0.75 - (x * x));
+    }
+    if (x < 1.5) {
+	x = (x - 1.5);
+	return (0.5 * (x * x));
+    }
+    return 0.0;
+}
+
+static double
+BSplineFilter(x)
+    double x;
+{
+    double x2;
+
+    if (x < 0.0) {
+	x = -x;
+    }
+    if (x < 1) {
+	x2 = x * x;
+	return ((.5 * x2 * x) - x2 + (2.0 / 3.0));
+    } else if (x < 2) {
+	x = 2 - x;
+	return ((x * x * x) / 6.0);
+    }
+    return 0.0;
+}
+
+/*
+ *
+ * Infinite Filters:
+ *	Sinc		perfect lowpass filter
+ *	Bessel		circularly symmetric 2-D filter
+ *	Gaussian
+ *	Lanczos3
+ *	Mitchell
+ */
+
+static double
+SincFilter(x)
+    double x;
+{
+    x *= M_PI;
+    if (x == 0.0) {
+	return 1.0;
+    }
+    return (sin(x) / x);
+}
+
+static double
+BesselFilter(x)
+    double x;
+{
+#ifdef NEED_DECL_J1
+    extern double j1 _ANSI_ARGS_((double value));
+#endif
+    /*
+     * See Pratt "Digital Image Processing" p. 97 for Bessel functions
+     * zeros are at approx x=1.2197, 2.2331, 3.2383, 4.2411, 5.2428, 6.2439,
+     * 7.2448, 8.2454
+     */
+#ifdef __BORLANDC__
+    return 0.0;
+#else
+    return (x == 0.0) ? M_PI / 4.0 : j1(M_PI * x) / (x + x);
+#endif
+}
+
+#define SQRT_2PI	0.79788456080286541	/* sqrt(2.0 / M_PI) */
+
+static double
+GaussianFilter(x)
+    double x;
+{
+    return exp(-2.0 * x * x) * SQRT_2PI;
+}
+
+static double
+Lanczos3Filter(x)
+    double x;
+{
+    if (x < 0) {
+	x = -x;
+    }
+    if (x < 3.0) {
+	return (SincFilter(x) * SincFilter(x / 3.0));
+    }
+    return 0.0;
+}
+
+#define	B		0.3333333333333333	/* (1.0 / 3.0) */
+#define	C		0.3333333333333333	/* (1.0 / 3.0) */
+
+static double
+MitchellFilter(x)
+    double x;
+{
+    double x2;
+
+    x2 = x * x;
+    if (x < 0) {
+	x = -x;
+    }
+    if (x < 1.0) {
+	x = (((12.0 - 9.0 * B - 6.0 * C) * (x * x2)) +
+	    ((-18.0 + 12.0 * B + 6.0 * C) * x2) + (6.0 - 2 * B));
+	return (x / 6.0);
+    } else if (x < 2.0) {
+	x = (((-1.0 * B - 6.0 * C) * (x * x2)) + ((6.0 * B + 30.0 * C) * x2) +
+	    ((-12.0 * B - 48.0 * C) * x) + (8.0 * B + 24 * C));
+	return (x / 6.0);
+    }
+    return 0.0;
+}
+
+/*
+ * Catmull-Rom spline
+ */
+static double
+CatRomFilter(x)
+    double x;
+{
+    if (x < -2.) {
+	return 0.0;
+    }
+    if (x < -1.0) {
+	return 0.5 * (4.0 + x * (8.0 + x * (5.0 + x)));
+    }
+    if (x < 0.0) {
+	return 0.5 * (2.0 + x * x * (-5.0 + x * -3.0));
+    }
+    if (x < 1.0) {
+	return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
+    }
+    if (x < 2.0) {
+	return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
+    }
+    return 0.0;
+}
+
+/* approximation to the gaussian integral [x, inf) */
+static double
+GiFilter(x)
+    double x;
+{
+    if (x > 1.5) {
+	return 0.0;
+    } else if (x < -1.5) {
+	return 1.0;
+    } else {
+#define I6 0.166666666666667
+#define I4 0.25
+#define I3 0.333333333333333
+	double x2 = x * x;
+	double x3 = x2 * x;
+
+	if (x > 0.5) {
+	    return .5625  - ( x3 * I6 - 3 * x2 * I4 + 1.125 * x);
+	} else if (x > -0.5) {
+	    return 0.5    - (0.75 * x - x3 * I3);
+	} else {
+	    return 0.4375 + (-x3 * I6 - 3 * x2 * I4 - 1.125 * x);
+	}
+    }
+}
+
+
+
+static ResampleFilter filterTable[] =
+{
+    /* name,     function,		support */
+    {"bell",     BellFilter,		1.5	 },
+    {"bessel",   BesselFilter,		3.2383   },
+    {"box",      BoxFilter,		0.5      },
+    {"bspline",  BSplineFilter,		2.0	 },
+    {"catrom",   CatRomFilter,		2.0	 },
+    {"default",  DefaultFilter,		1.0	 },
+    {"dummy",    DummyFilter,		0.5	 },
+    {"gauss8",   GaussianFilter,	8.0	 },
+    {"gaussian", GaussianFilter,	1.25	 },
+    {"gi",	 GiFilter,		1.25	 },
+    {"lanczos3", Lanczos3Filter,	3.0	 },
+    {"mitchell", MitchellFilter,	2.0	 },
+    {"none",     (ResampleFilterProc *)NULL,	0.0	 },
+    {"sinc",     SincFilter,		4.0	 },
+    {"triangle", TriangleFilter,	1.0	 },
+};
+
+static int nFilters = sizeof(filterTable) / sizeof(ResampleFilter);
+
+ResampleFilter *bltBoxFilterPtr = &(filterTable[1]);
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetResampleFilter --
+ *
+ *      Finds a 1-D filter associated by the given filter name.
+ *
+ * Results:
+ *      A standard Tcl result.  Returns TCL_OK is the filter was
+ *	found.  The filter information (proc and support) is returned
+ *	via filterPtrPtr. Otherwise TCL_ERROR is returned and an error
+ *	message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetResampleFilter(interp, name, filterPtrPtr)
+    Tcl_Interp *interp;
+    char *name;
+    ResampleFilter **filterPtrPtr;
+{
+    ResampleFilter *filterPtr, *endPtr;
+
+    endPtr = filterTable + nFilters;
+    for (filterPtr = filterTable; filterPtr < endPtr; filterPtr++) {
+	if (strcmp(name, filterPtr->name) == 0) {
+	    *filterPtrPtr = (filterPtr->proc == NULL) ? NULL : filterPtr;
+	    return TCL_OK;
+	}
+    }
+    Tcl_AppendResult(interp, "can't find filter \"", name, "\"", (char *)NULL);
+    return TCL_ERROR;
+}
+
+
+/*
+ * Scaled integers are fixed point values.  The upper 18 bits is the integer
+ * portion, the lower 14 bits the fractional remainder.  Must be careful
+ * not to overflow the values (especially during multiplication). 
+ *
+ * The following operations are defined:
+ * 
+ *	S * n		Scaled integer times an integer.
+ *	S1 + S2		Scaled integer plus another scaled integer.
+ *
+ */
+
+#define float2si(f)	(int)((f) * 16384.0 + 0.5)
+#define uchar2si(b)	(((int)(b)) << 14)
+#define si2int(s)	(((s) + 8192) >> 14)
+
+#ifdef notdef
+typedef struct {
+    int pixel;
+    union Weight {
+	int i;			/* Fixed point, scaled integer. */
+	float f;
+    } weight;
+} Sample;
+
+typedef struct {
+    int count;			/* Number of contributors */
+    Sample *samples;		/* Array of contributors */
+} Contribution;
+
+typedef struct {
+    int pixel;
+    union Weight {
+	int i;			/* Fixed point, scaled integer. */
+	float f;
+    } weight;
+} Sample;
+#endif
+
+
+typedef union {
+    int i;			/* Fixed point, scaled integer. */
+    float f;
+} Weight;
+
+typedef struct {
+    int count;			/* Number of samples. */
+    int start;
+    Weight weights[1];		/* Array of weights. */
+} Sample;
+
+static size_t
+ComputeWeights(srcWidth, destWidth, filterPtr, samplePtrPtr)
+    int srcWidth, destWidth;
+    ResampleFilter *filterPtr;
+    Sample **samplePtrPtr;
+{
+    Sample *samples;
+    double scale;
+    int filterSize;
+    double center;
+    register Sample *s;
+    register Weight *weight;
+    register int x, i;
+    register int left, right;	/* filter bounds */
+    double factor, sum;
+    size_t size;
+
+    /* Pre-calculate filter contributions for a row */
+    scale = (double)destWidth / (double)srcWidth;
+
+    if (scale < 1.0) {
+	double radius, fscale;
+
+	/* Downsample */
+
+	radius = filterPtr->support / scale;
+	fscale = 1.0 / scale;
+	filterSize = (int)(radius * 2 + 2);
+
+	size = sizeof(Sample) + (filterSize - 1) * sizeof(Weight);
+	samples = Blt_Calloc(destWidth, size);
+	assert(samples);
+
+	s = samples;
+	for (x = 0; x < destWidth; x++) {
+	    center = (double)x * fscale;
+
+	    /* Determine bounds of filter and its density */
+	    left = (int)(center - radius + 0.5);
+	    if (left < 0) {
+		left = 0;
+	    }
+	    right = (int)(center + radius + 0.5);
+	    if (right >= srcWidth) {
+		right = srcWidth - 1;
+	    }
+	    sum = 0.0;
+	    s->start = left;
+	    for (weight = s->weights, i = left; i <= right; i++, weight++) {
+		weight->f = (float)
+		    (*filterPtr->proc) (((double)i + 0.5 - center) * scale);
+		sum += weight->f;
+	    }
+	    s->count = right - left + 1;
+
+	    factor = (sum == 0.0) ? 1.0 : (1.0 / sum);
+	    for (weight = s->weights, i = left; i <= right; i++, weight++) {
+		weight->f = (float)(weight->f * factor);
+		weight->i = float2si(weight->f);
+	    }
+	    s = (Sample *)((char *)s + size);
+	}
+    } else {
+	double fscale;
+	/* Upsample */
+
+	filterSize = (int)(filterPtr->support * 2 + 2);
+	size = sizeof(Sample) + (filterSize - 1) * sizeof(Weight);
+	samples = Blt_Calloc(destWidth, size);
+	assert(samples);
+
+	fscale = 1.0 / scale;
+
+	s = samples;
+	for (x = 0; x < destWidth; x++) {
+	    center = (double)x * fscale;
+	    left = (int)(center - filterPtr->support + 0.5);
+	    if (left < 0) {
+		left = 0;
+	    }
+	    right = (int)(center + filterPtr->support + 0.5);
+	    if (right >= srcWidth) {
+		right = srcWidth - 1;
+	    }
+	    sum = 0.0;
+	    s->start = left;
+	    for (weight = s->weights, i = left; i <= right; i++, weight++) {
+		weight->f = (float)
+		    (*filterPtr->proc) ((double)i - center + 0.5);
+		sum += weight->f;
+	    }
+	    s->count = right - left + 1;
+	    factor = (sum == 0.0) ? 1.0 : (1.0 / sum);
+	    for (weight = s->weights, i = left; i <= right; i++, weight++) {
+		weight->f = (float)(weight->f * factor);
+		weight->i = float2si(weight->f);
+	    }
+	    s = (Sample *)((char *)s + size);
+	}
+    }
+    *samplePtrPtr = samples;
+    return size;
+}
+
+/* 
+ * The following macro converts a fixed-point scaled integer to a
+ * byte, clamping the value between 0 and 255.  
+ */
+#define SICLAMP(s) \
+    (unsigned char)(((s) < 0) ? 0 : ((s) > 4177920) ? 255 : (si2int(s)))
+
+static void
+ZoomImageVertically(src, dest, filterPtr)
+    Blt_ColorImage src, dest;
+    ResampleFilter *filterPtr;
+{
+    Sample *samples, *s, *endPtr;
+    int destWidth, destHeight;
+    int red, green, blue, alpha;
+    int srcWidth, srcHeight;
+    register Pix32 *srcColumnPtr;
+    register Pix32 *srcPtr, *destPtr;
+    register Weight *weight;
+    int x, i;
+    size_t size;		/* Size of sample. */
+
+    srcWidth = Blt_ColorImageWidth(src);
+    srcHeight = Blt_ColorImageHeight(src);
+    destWidth = Blt_ColorImageWidth(dest);
+    destHeight = Blt_ColorImageHeight(dest);
+
+    /* Pre-calculate filter contributions for a row */
+    size = ComputeWeights(srcHeight, destHeight, filterPtr, &samples);
+    endPtr = (Sample *)((char *)samples + (destHeight * size));
+
+    /* Apply filter to zoom vertically from tmp to destination */
+    for (x = 0; x < srcWidth; x++) {
+	srcColumnPtr = Blt_ColorImageBits(src) + x;
+	destPtr = Blt_ColorImageBits(dest) + x;
+	for (s = samples; s < endPtr; s = (Sample *)((char *)s + size)) {
+	    red = green = blue = alpha = 0;
+	    srcPtr = srcColumnPtr + (s->start * srcWidth);
+	    for (weight = s->weights, i = 0; i < s->count; i++, weight++) {
+		red += srcPtr->Red * weight->i;
+		green += srcPtr->Green * weight->i;
+		blue += srcPtr->Blue * weight->i;
+		alpha += srcPtr->Alpha * weight->i;
+		srcPtr += srcWidth;
+	    }
+	    destPtr->Red = SICLAMP(red);
+	    destPtr->Green = SICLAMP(green);
+	    destPtr->Blue = SICLAMP(blue);
+	    destPtr->Alpha = SICLAMP(alpha);
+	    destPtr += destWidth;
+
+	}
+    }
+    /* Free the memory allocated for filter weights */
+    Blt_Free(samples);
+}
+
+static void
+ZoomImageHorizontally(src, dest, filterPtr)
+    Blt_ColorImage src, dest;
+    ResampleFilter *filterPtr;
+{
+    Sample *samples, *s, *endPtr;
+    Weight *weight;
+    int destWidth;
+    int red, green, blue, alpha;
+    int srcWidth, srcHeight;
+    int y, i;
+    register Pix32 *srcPtr, *destPtr;
+    register Pix32 *srcRowPtr;
+    size_t size;		/* Size of sample. */
+
+    srcWidth = Blt_ColorImageWidth(src);
+    srcHeight = Blt_ColorImageHeight(src);
+    destWidth = Blt_ColorImageWidth(dest);
+
+    /* Pre-calculate filter contributions for a row */
+    size = ComputeWeights(srcWidth, destWidth, filterPtr, &samples);
+    endPtr = (Sample *)((char *)samples + (destWidth * size));
+
+    /* Apply filter to zoom horizontally from srcPtr to tmpPixels */
+    srcRowPtr = Blt_ColorImageBits(src);
+    destPtr = Blt_ColorImageBits(dest);
+    for (y = 0; y < srcHeight; y++) {
+	for (s = samples; s < endPtr; s = (Sample *)((char *)s + size)) {
+	    red = green = blue = alpha = 0;
+	    srcPtr = srcRowPtr + s->start;
+	    for (weight = s->weights, i = 0; i < s->count; i++, weight++) {
+		red += srcPtr->Red * weight->i;
+		green += srcPtr->Green * weight->i;
+		blue += srcPtr->Blue * weight->i;
+		alpha += srcPtr->Alpha * weight->i;
+		srcPtr++;
+	    }
+	    destPtr->Red = SICLAMP(red);
+	    destPtr->Green = SICLAMP(green);
+	    destPtr->Blue = SICLAMP(blue);
+	    destPtr->Alpha = SICLAMP(alpha);
+	    destPtr++;
+	}
+	srcRowPtr += srcWidth;
+    }
+    /* free the memory allocated for horizontal filter weights */
+    Blt_Free(samples);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResampleColorImage --
+ *
+ *      Resamples a given color image using 1-D filters and returns
+ *	a new color image of the designated size.
+ *
+ * Results:
+ *      Returns the resampled color image. The original color image
+ *	is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_ResampleColorImage(src, width, height, horzFilterPtr, vertFilterPtr)
+    Blt_ColorImage src;
+    int width, height;
+    ResampleFilter *horzFilterPtr, *vertFilterPtr;
+{
+    Blt_ColorImage tmp, dest;
+
+    /* 
+     * It's usually faster to zoom vertically last.  This has to do
+     * with the fact that images are stored in contiguous rows.
+     */
+
+    tmp = Blt_CreateColorImage(width, Blt_ColorImageHeight(src));
+    ZoomImageHorizontally(src, tmp, horzFilterPtr);
+    dest = Blt_CreateColorImage(width, height);
+    ZoomImageVertically(tmp, dest, vertFilterPtr);
+    Blt_FreeColorImage(tmp);
+    return dest;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResamplePhoto --
+ *
+ *      Resamples a Tk photo image using 1-D filters and writes the
+ *      image into another Tk photo.  It is possible for the
+ *      source and destination to be the same photo.
+ *
+ * Results:
+ *      The designated destination photo will contain the resampled
+ *	color image. The original photo is left intact.
+ *
+ *---------------------------------------------------------------------- 
+ */
+void
+Blt_ResamplePhoto(srcPhoto, x, y, width, height, destPhoto, horzFilterPtr, 
+	vertFilterPtr)
+    Tk_PhotoHandle srcPhoto;	/* Source photo image to scale */
+    int x, y;
+    int width, height;
+    Tk_PhotoHandle destPhoto;	/* Resulting scaled photo image */
+    ResampleFilter *horzFilterPtr, *vertFilterPtr;
+{
+    Blt_ColorImage srcImage, destImage;
+    Tk_PhotoImageBlock dest;
+
+    Tk_PhotoGetImage(destPhoto, &dest);
+    srcImage = Blt_PhotoRegionToColorImage(srcPhoto, x, y, width, height);
+    destImage = Blt_ResampleColorImage(srcImage, dest.width, dest.height,
+	horzFilterPtr, vertFilterPtr);
+    Blt_FreeColorImage(srcImage);
+    Blt_ColorImageToPhoto(destImage, destPhoto);
+    Blt_FreeColorImage(destImage);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizePhoto --
+ *
+ *	Scales the region of the source image to the size of the
+ *	destination image.  This routine performs raw scaling of
+ *	the image and unlike Blt_ResamplePhoto does not handle
+ *	aliasing effects from subpixel sampling. It is possible
+ *	for the source and destination to be the same photo.
+ *
+ * Results:
+ *      The designated destination photo will contain the resampled
+ *	color image. The original photo is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ResizePhoto(srcPhoto, x, y, width, height, destPhoto)
+    Tk_PhotoHandle srcPhoto;	/* Source photo image to scaled. */
+    register int x, y;		/* Region of source photo to be
+				 * scaled. */
+    int width, height;
+    Tk_PhotoHandle destPhoto;	/* (out) Resulting scaled photo image.
+				 * Scaling factors are derived from
+				 * the destination photo's
+				 * dimensions. */
+{
+    double xScale, yScale;
+    Blt_ColorImage destImage;
+    Pix32 *destPtr;
+    Tk_PhotoImageBlock src, dest;
+    unsigned char *srcPtr, *srcRowPtr;
+    int *mapX, *mapY;
+    register int sx, sy;
+    int left, right, top, bottom;
+
+    Tk_PhotoGetImage(srcPhoto, &src);
+    Tk_PhotoGetImage(destPhoto, &dest);
+
+    left = x, top = y, right = x + width - 1, bottom = y + height - 1;
+    destImage = Blt_CreateColorImage(dest.width, dest.height);
+    xScale = (double)width / (double)dest.width;
+    yScale = (double)height / (double)dest.height;
+    mapX = (int *)Blt_Malloc(sizeof(int) * dest.width);
+    mapY = (int *)Blt_Malloc(sizeof(int) * dest.height);
+    for(x = 0; x < dest.width; x++) {
+	sx = (int)(xScale * (double)(x + left));
+	if (sx > right) {
+	    sx = right;
+	}
+	mapX[x] = sx;
+    }
+    for(y = 0; y < dest.height; y++) {
+	sy = (int)(yScale * (double)(y + top));
+	if (sy > bottom) {
+	    sy = bottom;
+	}
+	mapY[y] = sy;
+    }
+    destPtr = Blt_ColorImageBits(destImage);
+    if (src.pixelSize == 4) {
+	for (y = 0; y < dest.height; y++) {
+	    srcRowPtr = src.pixelPtr + (mapY[y] * src.pitch);
+	    for (x = 0; x < dest.width; x++) {
+		srcPtr = srcRowPtr + (mapX[x] * src.pixelSize);
+		destPtr->Red = srcPtr[src.offset[0]];
+		destPtr->Green = srcPtr[src.offset[1]];
+		destPtr->Blue = srcPtr[src.offset[2]];
+		destPtr->Alpha = srcPtr[src.offset[3]];
+		destPtr++;
+	    }
+	}
+    } else if (src.pixelSize == 3) {
+	for (y = 0; y < dest.height; y++) {
+	    srcRowPtr = src.pixelPtr + (mapY[y] * src.pitch);
+	    for (x = 0; x < dest.width; x++) {
+		srcPtr = srcRowPtr + (mapX[x] * src.pixelSize);
+		destPtr->Red = srcPtr[src.offset[0]];
+		destPtr->Green = srcPtr[src.offset[1]];
+		destPtr->Blue = srcPtr[src.offset[2]];
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    } else  {
+	for (y = 0; y < dest.height; y++) {
+	    srcRowPtr = src.pixelPtr + (mapY[y] * src.pitch);
+	    for (x = 0; x < dest.width; x++) {
+		srcPtr = srcRowPtr + (mapX[x] * src.pixelSize);
+		destPtr->Red = destPtr->Green = destPtr->Blue = 
+		    srcPtr[src.offset[0]];
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    }	
+    Blt_Free(mapX);
+    Blt_Free(mapY);
+    Blt_ColorImageToPhoto(destImage, destPhoto);
+    Blt_FreeColorImage(destImage);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizeColorImage --
+ *
+ *	Scales the region of the source image to the size of the
+ *	destination image.  This routine performs raw scaling of
+ *	the image and unlike Blt_ResamplePhoto does not perform
+ *	any antialiasing.
+ *
+ * Results:
+ *      Returns the new resized color image.  The original image
+ *	is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_ResizeColorImage(src, x, y, width, height, destWidth, destHeight)
+    Blt_ColorImage src;		/* Source color image to be scaled. */
+    register int x, y;		/* Region of source image to scaled. */
+    int width, height;
+    int destWidth, destHeight;	/* Requested dimensions of the scaled
+				 * image. */
+{
+    register int sx, sy;
+    double xScale, yScale;
+    Blt_ColorImage dest;
+    Pix32 *srcPtr, *srcRowPtr, *destPtr;
+    int *mapX, *mapY;
+    int left, right, top, bottom;
+
+    left = x, top = y; right = x + width - 1, bottom = y + height - 1;
+
+    dest = Blt_CreateColorImage(destWidth, destHeight);
+    xScale = (double)width / (double)destWidth;
+    yScale = (double)height / (double)destHeight;
+    mapX = (int *)Blt_Malloc(sizeof(int) * destWidth);
+    mapY = (int *)Blt_Malloc(sizeof(int) * destHeight);
+    for(x = 0; x < destWidth; x++) {
+	sx = (int)(xScale * (double)(x + left));
+	if (sx > right) {
+	    sx = right;
+	} 
+	mapX[x] = sx;
+    }
+    for(y = 0; y < destHeight; y++) {
+	sy = (int)(yScale * (double)(y + top));
+	if (sy > bottom) {
+	    sy = bottom;
+	} 
+	mapY[y] = sy;
+    }
+    destPtr = Blt_ColorImageBits(dest);
+    for (y = 0; y < destHeight; y++) {
+	srcRowPtr = Blt_ColorImageBits(src) + 
+	    (Blt_ColorImageWidth(src) * mapY[y]);
+	for (x = 0; x < destWidth; x++) {
+	    srcPtr = srcRowPtr + mapX[x];
+	    destPtr->value = srcPtr->value; /* Copy the pixel. */
+	    destPtr++;
+	}
+    }
+    Blt_Free(mapX);
+    Blt_Free(mapY);
+    return dest;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizeColorSubimage --
+ *
+ *	Scales the region of the source image to the size of the
+ *	destination image.  This routine performs raw scaling of
+ *	the image and unlike Blt_ResamplePhoto does not perform
+ *	any antialiasing.
+ *
+ * Results:
+ *      Returns the new resized color image.  The original image
+ *	is left intact.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_ResizeColorSubimage(
+    Blt_ColorImage src,		/* Source color image to be scaled. */
+    int regionX, 
+    int regionY,		/* Offset of subimage in destination. */
+    int regionWidth,		/* Dimension of subimage. */
+    int regionHeight,
+    int destWidth, 
+    int destHeight)		/* Dimensions of the entire scaled
+				   image. */
+{
+    Blt_ColorImage dest;
+    Pix32 *srcPtr, *srcRowPtr, *destPtr;
+    double xScale, yScale;
+    int *mapX, *mapY;
+    int srcWidth, srcHeight;
+    register int sx, sy;
+    register int x, y;
+
+    srcWidth = Blt_ColorImageWidth(src);
+    srcHeight = Blt_ColorImageHeight(src);
+
+    xScale = (double)srcWidth / (double)destWidth;
+    yScale = (double)srcHeight / (double)destHeight;
+    mapX = Blt_Malloc(sizeof(int) * regionWidth);
+    mapY = Blt_Malloc(sizeof(int) * regionHeight);
+
+    /* Precompute scaling factors for each row and column. */
+    for(x = 0; x < regionWidth; x++) {
+	sx = (int)(xScale * (double)(x + regionX));
+	if (sx >= srcWidth) {
+	    sx = srcWidth - 1;
+	} 
+	mapX[x] = sx;
+    }
+    for(y = 0; y < regionHeight; y++) {
+	sy = (int)(yScale * (double)(y + regionY));
+	if (sy > srcHeight) {
+	    sy = srcHeight - 1;
+	} 
+	mapY[y] = sy;
+    }
+
+    dest = Blt_CreateColorImage(regionWidth, regionHeight);
+    destPtr = Blt_ColorImageBits(dest);
+    for (y = 0; y < regionHeight; y++) {
+	srcRowPtr = Blt_ColorImageBits(src) + 
+	    (Blt_ColorImageWidth(src) * mapY[y]);
+	for (x = 0; x < regionWidth; x++) {
+	    srcPtr = srcRowPtr + mapX[x];
+	    destPtr->value = srcPtr->value; /* Copy the pixel. */
+	    destPtr++;
+	}
+    }
+    Blt_Free(mapX);
+    Blt_Free(mapY);
+    return dest;
+}
+
+/*
+ * FIXME: Boundary handling could be better (pixels are replicated).
+ *	  It's slow. Take boundary tests out of inner loop.
+ */
+Blt_ColorImage
+Blt_ConvolveColorImage(src, filterPtr)
+    Blt_ColorImage src;
+    Filter2D *filterPtr;
+{
+    Blt_ColorImage dest;
+    register Pix32 *srcPtr, *destPtr;
+#define MAXROWS	24
+    register int sx, sy, dx, dy;
+    register int x, y;
+    double red, green, blue;
+    int width, height;
+    int radius;
+    register double *valuePtr;
+
+    width = Blt_ColorImageWidth(src);
+    height = Blt_ColorImageHeight(src);
+
+    dest = Blt_CreateColorImage(width, height);
+    radius = (int)filterPtr->support;
+    if (radius < 1) {
+	radius = 1;
+    }
+    destPtr = Blt_ColorImageBits(dest);
+    for (dy = 0; dy < height; dy++) {
+	for (dx = 0; dx < width; dx++) {
+	    red = green = blue = 0.0;
+	    valuePtr = filterPtr->kernel;
+	    for (sy = (dy - radius); sy <= (dy + radius); sy++) {
+		y = sy;
+		if (y < 0) {
+		    y = 0;
+		} else if (y >= height) {
+		    y = height - 1;
+		}
+		for (sx = (dx - radius); sx <= (dx + radius); sx++) {
+		    x = sx;
+		    if (x < 0) {
+			x = 0;
+		    } else if (sx >= width) {
+			x = width - 1;
+		    }
+		    srcPtr = Blt_ColorImagePixel(src, x, y);
+		    red += *valuePtr * (double)srcPtr->Red;
+		    green += *valuePtr * (double)srcPtr->Green;
+		    blue += *valuePtr * (double)srcPtr->Blue;
+#ifdef notdef
+		    fprintf(stderr, "%d,%d = r=%f,g=%f,b=%f\n", x, y,
+			red, green, blue);
+#endif
+		    valuePtr++;
+		}
+	    }
+	    red /= filterPtr->sum;
+	    green /= filterPtr->sum;
+	    blue /= filterPtr->sum;
+	    destPtr->Red = (unsigned char)CLAMP(red);
+	    destPtr->Green = (unsigned char)CLAMP(green);
+	    destPtr->Blue = (unsigned char)CLAMP(blue);
+	    destPtr->Alpha = (unsigned char)-1;
+	    destPtr++;
+	}
+    }
+    return dest;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SnapPhoto --
+ *
+ *      Takes a snapshot of an X drawable (pixmap or window) and
+ *	writes it to an existing Tk photo image.
+ *
+ * Results:
+ *      A standard Tcl result.
+ *
+ * Side Effects:
+ *	The named Tk photo is updated with the snapshot.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_SnapPhoto(interp, tkwin, drawable, x, y, width, height, destWidth, 
+	destHeight, photoName, inputGamma)
+    Tcl_Interp *interp;		/* Interpreter to report errors back to */
+    Tk_Window tkwin;
+    Drawable drawable;		/* Window or pixmap to be snapped */
+    int x, y;			/* Offset of image from drawable origin. */
+    int width, height;		/* Dimension of the drawable */
+    int destWidth, destHeight;	/* Desired size of the Tk photo */
+    char *photoName;		/* Name of an existing Tk photo image. */
+    double inputGamma;
+{
+    Tk_PhotoHandle photo;	/* The photo image to write into. */
+    Blt_ColorImage image;
+
+    photo = Blt_FindPhoto(interp, photoName);
+    if (photo == NULL) {
+	Tcl_AppendResult(interp, "can't find photo \"", photoName, "\"", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    image = Blt_DrawableToColorImage(tkwin, drawable, x, y, width, height, 
+	inputGamma);
+    if (image == NULL) {
+	Tcl_AppendResult(interp,
+	    "can't grab window or pixmap (possibly obscured?)", (char *)NULL);
+	return TCL_ERROR;	/* Can't grab window image */
+    }
+    if ((destWidth != width) || (destHeight != height)) {
+	Blt_ColorImage destImage;
+
+	/*
+	 * The requested size for the destination image is different than
+	 * that of the source snapshot.  Resample the image as necessary.
+	 * We'll use a cheap box filter. I'm assuming that the destination
+	 * image will typically be smaller than the original.
+	 */
+	destImage = Blt_ResampleColorImage(image, destWidth, destHeight, 
+		bltBoxFilterPtr, bltBoxFilterPtr);
+	Blt_FreeColorImage(image);
+	image = destImage;
+    }
+    Blt_ColorImageToPhoto(image, photo);
+    Blt_FreeColorImage(image);
+    return TCL_OK;
+}
+
+#if HAVE_JPEG
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToPhoto --
+ *
+ *      Reads a JPEG file and converts it into a Tk photo.
+ *
+ * Results:
+ *      A standard Tcl result.  If successful, TCL_OK is returned
+ *	and the designated photo is re-written with the image.
+ *	Otherwise, TCL_ERROR is returned and interp->result will
+ *	contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_JPEGToPhoto(interp, fileName, photo)
+    Tcl_Interp *interp;
+    char *fileName;
+    Tk_PhotoHandle photo;	/* The photo image to write into. */
+{
+    Blt_ColorImage image;
+
+    image = Blt_JPEGToColorImage(interp, fileName);
+    if (image == NULL) {
+	return TCL_ERROR;
+    }
+    Blt_ColorImageToPhoto(image, photo);
+    Blt_FreeColorImage(image);
+    return TCL_OK;
+}
+#endif /* HAVE_JPEG */
+
+/* 
+ * --------------------------------------------------------------------------
+ *
+ * ShearY --
+ *
+ *	Shears a row horizontally. Antialiasing limited to filtering
+ *	two adjacent pixels.  So the shear angle must be between +-45
+ *	degrees.
+ *	
+ * Results:   
+ *	None.
+ *
+ * Side Effects:
+ *	The sheared image is drawn into the destination color image.
+ *
+ * --------------------------------------------------------------------------
+ */
+static void 
+ShearY(src, dest, y, offset, frac, bgColor)
+    Blt_ColorImage src, dest;
+    int y;			/* Designates the row to be sheared */
+    int offset;			/* Difference between  of  */
+    double frac;
+    Pix32 bgColor;
+{
+    Pix32 *srcPtr, *destPtr;
+    Pix32 *srcRowPtr, *destRowPtr;
+    register int x, dx;
+    int destWidth;
+    int srcWidth;
+    int red, blue, green, alpha;
+    int leftRed, leftGreen, leftBlue, leftAlpha;
+    int oldLeftRed, oldLeftGreen, oldLeftBlue, oldLeftAlpha;
+    int ifrac;
+
+    srcWidth = Blt_ColorImageWidth(src);
+    destWidth = Blt_ColorImageWidth(dest);
+
+    destRowPtr = Blt_ColorImageBits(dest) + (y * destWidth);
+    srcRowPtr = Blt_ColorImageBits(src) + (y * srcWidth);
+
+    destPtr = destRowPtr;
+    for (x = 0; x < offset; x++) { 
+        *destPtr++ = bgColor;
+    }
+    destPtr = destRowPtr + offset;
+    srcPtr = srcRowPtr;
+    dx = offset;
+
+    oldLeftRed = uchar2si(bgColor.Red);
+    oldLeftGreen = uchar2si(bgColor.Green);
+    oldLeftBlue = uchar2si(bgColor.Blue);
+    oldLeftAlpha = uchar2si(bgColor.Alpha);
+
+    ifrac = float2si(frac);
+    for (x = 0; x < srcWidth; x++, dx++) { /* Loop through row pixels */
+	leftRed = srcPtr->Red * ifrac;
+	leftGreen = srcPtr->Green * ifrac;
+	leftBlue = srcPtr->Blue * ifrac;
+	leftAlpha = srcPtr->Alpha * ifrac;
+        if ((dx >= 0) && (dx < destWidth)) {
+	    red = uchar2si(srcPtr->Red) - (leftRed - oldLeftRed);
+	    green = uchar2si(srcPtr->Green) - (leftGreen - oldLeftGreen);
+	    blue = uchar2si(srcPtr->Blue) - (leftBlue - oldLeftBlue);
+	    alpha = uchar2si(srcPtr->Alpha) - (leftAlpha - oldLeftAlpha);
+	    destPtr->Red = SICLAMP(red);
+	    destPtr->Green = SICLAMP(green);
+	    destPtr->Blue = SICLAMP(blue);
+	    destPtr->Alpha = SICLAMP(alpha);
+        }
+	oldLeftRed = leftRed;
+	oldLeftGreen = leftGreen;
+	oldLeftBlue = leftBlue;
+	oldLeftAlpha = leftAlpha;
+	srcPtr++, destPtr++;
+    }
+    x = srcWidth + offset;  
+    destPtr = Blt_ColorImageBits(dest) + (y * destWidth) + x;
+    if (x < destWidth) {
+	leftRed = uchar2si(bgColor.Red);
+	leftGreen = uchar2si(bgColor.Green);
+	leftBlue = uchar2si(bgColor.Blue);
+	leftAlpha = uchar2si(bgColor.Alpha);
+
+	red = leftRed + oldLeftRed - (bgColor.Red * ifrac);
+	green = leftGreen + oldLeftGreen - (bgColor.Green * ifrac);
+	blue = leftBlue + oldLeftBlue - (bgColor.Blue * ifrac);
+	alpha = leftAlpha + oldLeftAlpha - (bgColor.Alpha * ifrac);
+	destPtr->Red = SICLAMP(red);
+	destPtr->Green = SICLAMP(green);
+	destPtr->Blue = SICLAMP(blue);
+	destPtr->Alpha = SICLAMP(alpha);
+	destPtr++;
+    }
+    for (x++; x < destWidth; x++) {
+        *destPtr++ = bgColor;
+    }
+}  
+
+/* 
+ * --------------------------------------------------------------------------
+ *
+ * ShearX --
+ *
+ *	Shears a column. Antialiasing is limited to filtering two
+ *	adjacent pixels.  So the shear angle must be between +-45
+ *	degrees.
+ *	
+ * Results:   
+ *	None.
+ *
+ * Side Effects:
+ *	The sheared image is drawn into the destination color image.
+ *
+ * -------------------------------------------------------------------------- 
+ */
+static void
+ShearX(src, dest, x, offset, frac, bgColor)
+    Blt_ColorImage src, dest;
+    int x;			/* Column in source image to be sheared. */
+    int offset;			/* Offset of */
+    double frac;			/* Fraction of subpixel. */
+    Pix32 bgColor;
+{
+    Pix32 *srcPtr, *destPtr;
+    register int y, dy;
+#ifdef notef
+    int srcWidth;
+    int destWidth;
+#endif
+    int destHeight;
+    int srcHeight;
+    int red, blue, green, alpha;
+    int leftRed, leftGreen, leftBlue, leftAlpha;
+    int oldLeftRed, oldLeftGreen, oldLeftBlue, oldLeftAlpha;
+    int ifrac;
+
+#ifdef notdef
+    srcWidth = Blt_ColorImageWidth(src);
+    destWidth = Blt_ColorImageWidth(dest);
+#endif
+    srcHeight = Blt_ColorImageHeight(src);
+    destHeight = Blt_ColorImageHeight(dest);
+#ifdef notdef
+    destPtr = Blt_ColorImageBits(dest) + x;
+#endif
+    for (y = 0; y < offset; y++) {
+	destPtr = Blt_ColorImagePixel(dest, x, y);
+        *destPtr = bgColor;
+#ifdef notdef
+	destPtr += destWidth;
+#endif
+    }
+
+    oldLeftRed = uchar2si(bgColor.Red);
+    oldLeftGreen = uchar2si(bgColor.Green);
+    oldLeftBlue = uchar2si(bgColor.Blue);
+    oldLeftAlpha = uchar2si(bgColor.Alpha);
+#ifdef notdef
+    destPtr = Blt_ColorImageBits(dest) + x + offset;
+    srcPtr = Blt_ColorImageBits(src) + x;
+#endif
+    dy = offset;
+    ifrac = float2si(frac);
+    for (y = 0; y < srcHeight; y++, dy++) {
+	srcPtr = Blt_ColorImagePixel(src, x, y);
+	leftRed = srcPtr->Red * ifrac;
+	leftGreen = srcPtr->Green * ifrac;
+	leftBlue = srcPtr->Blue * ifrac;
+	leftAlpha = srcPtr->Alpha * ifrac;
+        if ((dy >= 0) && (dy < destHeight)) {
+	    destPtr = Blt_ColorImagePixel(dest, x, dy);
+	    red = uchar2si(srcPtr->Red) - (leftRed - oldLeftRed);
+	    green = uchar2si(srcPtr->Green) - (leftGreen - oldLeftGreen);
+	    blue = uchar2si(srcPtr->Blue) - (leftBlue - oldLeftBlue);
+	    alpha = uchar2si(srcPtr->Alpha) - (leftAlpha - oldLeftAlpha);
+	    destPtr->Red = SICLAMP(red);
+	    destPtr->Green = SICLAMP(green);
+	    destPtr->Blue = SICLAMP(blue);
+	    destPtr->Alpha = SICLAMP(alpha);
+        }
+	oldLeftRed = leftRed;
+	oldLeftGreen = leftGreen;
+	oldLeftBlue = leftBlue;
+	oldLeftAlpha = leftAlpha;
+#ifdef notdef
+	srcPtr += srcWidth; 
+	destPtr += destWidth;
+#endif
+    }
+    y = srcHeight + offset;  
+#ifdef notdef
+    destPtr = Blt_ColorImageBits(dest) + (y * destWidth) + x + offset;
+#endif
+    if (y < destHeight) {
+	leftRed = uchar2si(bgColor.Red);
+	leftGreen = uchar2si(bgColor.Green);
+	leftBlue = uchar2si(bgColor.Blue);
+	leftAlpha = uchar2si(bgColor.Alpha);
+
+	destPtr = Blt_ColorImagePixel(dest, x, y);
+	red = leftRed + oldLeftRed - (bgColor.Red * ifrac);
+	green = leftGreen + oldLeftGreen - (bgColor.Green * ifrac);
+	blue = leftBlue + oldLeftBlue - (bgColor.Blue  * ifrac);
+	alpha = leftAlpha + oldLeftAlpha - (bgColor.Alpha  * ifrac);
+	destPtr->Red = SICLAMP(red);
+	destPtr->Green = SICLAMP(green);
+	destPtr->Blue = SICLAMP(blue);
+	destPtr->Alpha = SICLAMP(alpha);
+#ifdef notdef
+	destPtr += destWidth;
+#endif
+    }
+    
+    for (y++; y < destHeight; y++) {
+	destPtr = Blt_ColorImagePixel(dest, x, y);
+	*destPtr = bgColor;
+#ifdef notdef
+	destPtr += destWidth; 
+#endif
+    }
+}  
+
+/* 
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate45 --
+ *
+ *	Rotates an image by a given angle.  The angle must be in the
+ *	range -45.0 to 45.0 inclusive.  Anti-aliasing filtering is
+ *	performed on two adjacent pixels, so the angle can't be so
+ *	great as to force a sheared pixel to occupy 3 destination
+ *	pixels.  Performs a three shear rotation described below.
+ *
+ *	Reference: Alan W. Paeth, "A Fast Algorithm for General Raster
+ *		   Rotation", Graphics Gems, pp 179-195.  
+ *
+ *
+ * Results:  
+ *	Returns a newly allocated rotated image.
+ *
+ * --------------------------------------------------------------------------- 
+ */
+static Blt_ColorImage
+Rotate45(src, theta, bgColor)
+    Blt_ColorImage src;
+    double theta;
+    Pix32 bgColor;
+{
+    int tmpWidth, tmpHeight;
+    int srcWidth, srcHeight;
+    double sinTheta, cosTheta, tanTheta;
+    double  skewf;
+    int skewi;
+    Blt_ColorImage tmp1, tmp2, dest;
+    register int x, y;
+
+    sinTheta = sin(theta);
+    cosTheta = cos(theta);
+    tanTheta = tan(theta * 0.5);
+    
+    srcWidth = Blt_ColorImageWidth(src);
+    srcHeight = Blt_ColorImageHeight(src);
+
+    tmpWidth = srcWidth + (int)(srcHeight * FABS(tanTheta));
+    tmpHeight = srcHeight;
+
+    /* 1st shear */
+
+    tmp1 = Blt_CreateColorImage(tmpWidth, tmpHeight);
+    assert(tmp1);
+
+    if (tanTheta >= 0.0) {	/* Positive angle */
+	for (y = 0; y < tmpHeight; y++) {  
+	    skewf = (y + 0.5) * tanTheta;
+	    skewi = (int)floor(skewf);
+	    ShearY(src, tmp1, y, skewi, skewf - skewi, bgColor);
+	}
+    } else {			/* Negative angle */
+	for (y = 0; y < tmpHeight; y++) {  
+	    skewf = ((y - srcHeight) + 0.5) * tanTheta;
+	    skewi = (int)floor(skewf);
+	    ShearY(src, tmp1, y, skewi, skewf - skewi, bgColor);
+	}
+    }
+    tmpHeight = (int)(srcWidth * FABS(sinTheta) + srcHeight * cosTheta) + 1;
+
+    tmp2 = Blt_CreateColorImage(tmpWidth, tmpHeight);
+    assert(tmp2);
+
+    /* 2nd shear */
+
+    if (sinTheta > 0.0) {	/* Positive angle */
+        skewf = (srcWidth - 1) * sinTheta;
+    } else {			/* Negative angle */
+        skewf = (srcWidth - tmpWidth) * -sinTheta;
+    }
+    for (x = 0; x < tmpWidth; x++) {
+	skewi = (int)floor(skewf);
+        ShearX(tmp1, tmp2, x, skewi, skewf - skewi, bgColor);
+	skewf -= sinTheta;
+    }
+
+    Blt_FreeColorImage(tmp1);
+
+    /* 3rd shear */
+
+    tmpWidth = (int)(srcHeight * FABS(sinTheta) + srcWidth * cosTheta) + 1;
+
+    dest = Blt_CreateColorImage(tmpWidth, tmpHeight);
+    assert(dest);
+
+    if (sinTheta >= 0.0) {	/* Positive angle */
+        skewf = (srcWidth - 1) * sinTheta * -tanTheta;
+    } else {			/* Negative angle */
+        skewf = tanTheta * ((srcWidth - 1) * -sinTheta - (tmpHeight - 1));
+    }
+    for (y = 0; y < tmpHeight; y++) {
+	skewi = (int)floor(skewf);
+        ShearY(tmp2, dest, y, skewi, skewf - skewi, bgColor);
+	skewf += tanTheta;
+    }
+    Blt_FreeColorImage(tmp2);
+    return dest;      
+}
+
+/* 
+ * ---------------------------------------------------------------------------
+ *
+ * CopyColorImage --
+ *
+ *	Creates a copy of the given color image.  
+ *
+ * Results:  
+ *	Returns the new copy.
+ *
+ * --------------------------------------------------------------------------- 
+ */
+static Blt_ColorImage
+CopyColorImage(src)
+    Blt_ColorImage src;
+{
+    unsigned int width, height;
+    Pix32 *srcPtr, *destPtr;
+    Blt_ColorImage dest;
+
+    width = Blt_ColorImageWidth(src);
+    height = Blt_ColorImageHeight(src);
+    dest = Blt_CreateColorImage(width, height);
+    srcPtr = Blt_ColorImageBits(src);
+    destPtr = Blt_ColorImageBits(dest);
+    memcpy(destPtr, srcPtr, width * height * sizeof(Pix32));
+    return dest;
+}
+
+/* 
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate90 --
+ *
+ *	Rotates the given color image by 90 degrees.  This is part
+ *	of the special case right-angle rotations that do not create
+ *	subpixel aliasing.
+ *
+ * Results:  
+ *	Returns a newly allocated, rotated color image.
+ *
+ * --------------------------------------------------------------------------- 
+ */
+static Blt_ColorImage
+Rotate90(src)
+    Blt_ColorImage src;
+{
+    int width, height, offset;
+    Pix32 *srcPtr, *destPtr;
+    Blt_ColorImage dest;
+    register int x, y;
+
+    height = Blt_ColorImageWidth(src);
+    width = Blt_ColorImageHeight(src);
+
+    srcPtr = Blt_ColorImageBits(src);
+    dest = Blt_CreateColorImage(width, height);
+    offset = (height - 1) * width;
+
+    for (x = 0; x < width; x++) {
+	destPtr = Blt_ColorImageBits(dest) + offset + x;
+	for (y = 0; y < height; y++) {
+	    *destPtr = *srcPtr++;
+	    destPtr -= width;
+	}
+    }
+    return dest;
+}
+
+/* 
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate180 --
+ *
+ *	Rotates the given color image by 180 degrees.  This is one of
+ *	the special case orthogonal rotations that do not create
+ *	subpixel aliasing.
+ *
+ * Results:  
+ *	Returns a newly allocated, rotated color image.
+ *
+ * --------------------------------------------------------------------------- 
+ */
+static Blt_ColorImage
+Rotate180(src)
+    Blt_ColorImage src;
+{
+    int width, height, offset;
+    Pix32 *srcPtr, *destPtr;
+    Blt_ColorImage dest;
+    register int x, y;
+
+    width = Blt_ColorImageWidth(src);
+    height = Blt_ColorImageHeight(src);
+    dest = Blt_CreateColorImage(width, height);
+
+    srcPtr = Blt_ColorImageBits(src);
+    offset = (height - 1) * width;
+    for (y = 0; y < height; y++) {
+	destPtr = Blt_ColorImageBits(dest) + offset + width - 1;
+	for (x = 0; x < width; x++) {
+	    *destPtr-- = *srcPtr++;
+	}
+	offset -= width;
+    }
+    return dest;
+}
+
+/* 
+ * ---------------------------------------------------------------------------
+ *
+ * Rotate270 --
+ *
+ *	Rotates the given color image by 270 degrees.  This is part
+ *	of the special case right-angle rotations that do not create
+ *	subpixel aliasing.
+ *
+ * Results:  
+ *	Returns a newly allocated, rotated color image.
+ *
+ * --------------------------------------------------------------------------- 
+ */
+static Blt_ColorImage
+Rotate270(src)
+    Blt_ColorImage src;
+{
+    int width, height;
+    Pix32 *srcPtr, *destPtr;
+    Blt_ColorImage dest;
+    register int x, y;
+
+    height = Blt_ColorImageWidth(src);
+    width = Blt_ColorImageHeight(src);
+    dest = Blt_CreateColorImage(width, height);
+
+    srcPtr = Blt_ColorImageBits(src);
+    for (x = width - 1; x >= 0; x--) {
+	destPtr = Blt_ColorImageBits(dest) + x;
+	for (y = 0; y < height; y++) {
+	    *destPtr = *srcPtr++;
+	    destPtr += width;
+	}
+    }
+    return dest;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RotateColorImage --
+ *
+ *	Rotates a color image by a given # of degrees.
+ *
+ * Results:
+ *	Returns a newly allocated, rotated color image.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_RotateColorImage(src, angle)
+    Blt_ColorImage src;
+    double angle;
+{
+    Blt_ColorImage dest, tmp;
+    int quadrant;
+    
+    tmp = src;			/* Suppress compiler warning. */
+
+    /* Make the angle positive between 0 and 360 degrees. */ 
+    angle = FMOD(angle, 360.0);
+    if (angle < 0.0) {
+	angle += 360.0;
+    }
+    quadrant = ROTATE_0;
+    if ((angle > 45.0) && (angle <= 135.0)) {
+        quadrant = ROTATE_90;
+        angle -= 90.0;
+    } else if ((angle > 135.0) && (angle <= 225.0)) { 
+        quadrant = ROTATE_180;
+        angle -= 180.0;
+    } else if ((angle > 225.0) && (angle <= 315.0)) { 
+        quadrant = ROTATE_270;
+        angle -= 270.0;
+    } else if (angle > 315.0) {
+	angle -= 360.0;
+    }
+    /* 
+     * If necessary, create a temporary image that's been rotated
+     * by a right-angle.  We'll then rotate this color image between
+     * -45 to 45 degrees to arrive at its final angle.  
+     */
+    switch (quadrant) {
+    case ROTATE_270:		/* 270 degrees */
+	tmp = Rotate270(src);
+	break;
+
+    case ROTATE_90:		/* 90 degrees */
+	tmp = Rotate90(src);
+	break;
+
+    case ROTATE_180:		/* 180 degrees */
+	tmp = Rotate180(src);
+	break;
+
+    case ROTATE_0:		/* 0 degrees */
+	if (angle == 0.0) {
+	    tmp = CopyColorImage(src); /* Make a copy of the source. */
+	} 
+	break;
+    }
+
+    assert((angle >= -45.0) && (angle <= 45.0));
+
+    dest = tmp;
+    if (angle != 0.0) {
+	double theta;
+	Pix32 *srcPtr;
+	Pix32 bgColor;
+
+	/* FIXME: pick up background blending color from somewhere */
+	srcPtr = Blt_ColorImageBits(src);
+	bgColor = *srcPtr;
+	bgColor.Red = bgColor.Green = bgColor.Blue = 0xFF;
+	bgColor.Alpha = 0x00;	/* Transparent background */
+	theta = (angle / 180.0) * M_PI;
+	dest = Rotate45(tmp, theta, bgColor);
+	if (tmp != src) {
+	    Blt_FreeColorImage(tmp);
+	}
+    } 
+    return dest;
+}
+
+#define NC		256
+enum ColorIndices { RED, GREEN, BLUE };
+
+#define R0	(cubePtr->r0)
+#define R1	(cubePtr->r1)
+#define G0	(cubePtr->g0)
+#define G1	(cubePtr->g1)
+#define B0	(cubePtr->b0)
+#define B1	(cubePtr->b1)
+
+typedef struct {
+    int r0, r1;			/* min, max values: 
+				 * min exclusive max inclusive */
+    int g0, g1;
+    int b0, b1;
+    int vol;
+} Cube;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Histogram is in elements 1..HISTSIZE along each axis,
+ * element 0 is for base or marginal value
+ * NB: these must start out 0!
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+    long int wt[33][33][33];	/* # pixels in voxel */
+    long int mR[33][33][33];	/* Sum over voxel of red pixel values */
+    long int mG[33][33][33];	/* Sum over voxel of green pixel values */
+    long int mB[33][33][33];	/* Sum over voxel of blue pixel values */
+    long int gm2[33][33][33];	/* Variance */
+} ColorImageStatistics;
+
+/*
+ * Build 3-D color histogram of counts, R/G/B, c^2
+ */
+static ColorImageStatistics *
+GetColorImageStatistics(image)
+    Blt_ColorImage image;
+{
+    register int r, g, b;
+#define MAX_INTENSITIES	256
+    unsigned int sqr[MAX_INTENSITIES];
+    int numPixels;
+    Pix32 *srcPtr, *endPtr;
+    register int i;
+    ColorImageStatistics *s;
+
+    s = Blt_Calloc(1, sizeof(ColorImageStatistics));
+    assert(s);
+
+    /* Precompute table of squares. */
+    for (i = 0; i < MAX_INTENSITIES; i++) {
+	sqr[i] = i * i;
+    }
+    numPixels = Blt_ColorImageWidth(image) * Blt_ColorImageHeight(image);
+
+    for (srcPtr = Blt_ColorImageBits(image), endPtr = srcPtr + numPixels;
+	 srcPtr < endPtr; srcPtr++) {
+	/*
+	 * Reduce the number of bits (5) per color component. This
+	 * will keep the table size (2^15) reasonable without perceptually
+	 * affecting the final image.
+	 */
+	r = (srcPtr->Red >> 3) + 1;
+	g = (srcPtr->Green >> 3) + 1;
+	b = (srcPtr->Blue >> 3) + 1;
+	s->wt[r][g][b] += 1;
+	s->mR[r][g][b] += srcPtr->Red;
+	s->mG[r][g][b] += srcPtr->Green;
+	s->mB[r][g][b] += srcPtr->Blue;
+	s->gm2[r][g][b] += sqr[srcPtr->Red] + sqr[srcPtr->Green] + 
+	    sqr[srcPtr->Blue];
+    }
+    return s;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * At conclusion of the histogram step, we can interpret
+ *   wt[r][g][b] = sum over voxel of P(c)
+ *   mR[r][g][b] = sum over voxel of r*P(c)  ,  similarly for mG, mB
+ *   m2[r][g][b] = sum over voxel of c^2*P(c)
+ * Actually each of these should be divided by 'size' to give the usual
+ * interpretation of P() as ranging from 0 to 1, but we needn't do that here.
+ *----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ We now convert histogram into moments so that we can rapidly calculate
+ * the sums of the above quantities over any desired box.
+ *----------------------------------------------------------------------
+ */
+
+static void
+M3d(s)	/* compute cumulative moments. */
+    ColorImageStatistics *s;
+{
+    register unsigned char i, r, g, b, r0;
+    long int line, rLine, gLine, bLine;
+    long int area[33], rArea[33], gArea[33], bArea[33];
+    unsigned int line2, area2[33];
+
+    for (r = 1, r0 = 0; r <= 32; r++, r0++) {
+	for (i = 0; i <= 32; ++i) {
+	    area2[i] = area[i] = rArea[i] = gArea[i] = bArea[i] = 0;
+	}
+	for (g = 1; g <= 32; g++) {
+	    line2 = line = rLine = gLine = bLine = 0;
+	    for (b = 1; b <= 32; b++) {
+		/* ind1 = RGBIndex(r, g, b); */
+
+		line += s->wt[r][g][b];
+		rLine += s->mR[r][g][b];
+		gLine += s->mG[r][g][b];
+		bLine += s->mB[r][g][b];
+		line2 += s->gm2[r][g][b];
+
+		area[b] += line;
+		rArea[b] += rLine;
+		gArea[b] += gLine;
+		bArea[b] += bLine;
+		area2[b] += line2;
+
+		/* ind2 = ind1 - 1089; [r0][g][b] */
+		s->wt[r][g][b] = s->wt[r0][g][b] + area[b];
+		s->mR[r][g][b] = s->mR[r0][g][b] + rArea[b];
+		s->mG[r][g][b] = s->mG[r0][g][b] + gArea[b];
+		s->mB[r][g][b] = s->mB[r0][g][b] + bArea[b];
+		s->gm2[r][g][b] = s->gm2[r0][g][b] + area2[b];
+	    }
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ *	Compute sum over a box of any given statistic
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE 
+long int
+Volume(cubePtr, m)
+    Cube *cubePtr;
+    long int m[33][33][33];
+{
+    return (m[R1][G1][B1] - m[R1][G1][B0] - m[R1][G0][B1] + m[R1][G0][B0] -
+	    m[R0][G1][B1] + m[R0][G1][B0] + m[R0][G0][B1] - m[R0][G0][B0]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ *	The next two routines allow a slightly more efficient
+ *	calculation of Volume() for a proposed subbox of a given box.
+ *	The sum of Top() and Bottom() is the Volume() of a subbox split
+ *	in the given direction and with the specified new upper
+ *	bound.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* Compute part of Volume(cubePtr, mmt) that doesn't depend on r1, g1, or b1 */
+/* (depending on dir) */
+static long int
+Bottom(cubePtr, dir, m)
+    Cube *cubePtr;
+    unsigned char dir;
+    long int m[33][33][33];	/* Moment */
+{
+    switch (dir) {
+    case RED:
+	return -m[R0][G1][B1] + m[R0][G1][B0] + m[R0][G0][B1] - m[R0][G0][B0];
+    case GREEN:
+	return -m[R1][G0][B1] + m[R1][G0][B0] + m[R0][G0][B1] - m[R0][G0][B0];
+    case BLUE:
+	return -m[R1][G1][B0] + m[R1][G0][B0] + m[R0][G1][B0] - m[R0][G0][B0];
+    }
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Compute remainder of Volume(cubePtr, mmt), substituting pos for
+ * r1, g1, or b1 (depending on dir)
+ *
+ *----------------------------------------------------------------------
+ */
+static long int
+Top(cubePtr, dir, pos, m)
+    Cube *cubePtr;
+    unsigned char dir;
+    int pos;
+    long int m[33][33][33];
+{
+    switch (dir) {
+    case RED:
+	return (m[pos][G1][B1] - m[pos][G1][B0] - 
+		m[pos][G0][B1] + m[pos][G0][B0]);
+
+    case GREEN:
+	return (m[R1][pos][B1] - m[R1][pos][B0] - 
+		m[R0][pos][B1] + m[R0][pos][B0]);
+
+    case BLUE:
+	return (m[R1][G1][pos] - m[R1][G0][pos] -
+		m[R0][G1][pos] + m[R0][G0][pos]);
+    }
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ *	Compute the weighted variance of a box NB: as with the raw
+ *	statistics, this is really the (variance * size)
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+Variance(cubePtr, s)
+    Cube *cubePtr;
+    ColorImageStatistics *s;
+{
+    double dR, dG, dB, xx;
+
+    dR = Volume(cubePtr, s->mR);
+    dG = Volume(cubePtr, s->mG);
+    dB = Volume(cubePtr, s->mB);
+    xx = (s->gm2[R1][G1][B1] - s->gm2[R1][G1][B0] -
+	  s->gm2[R1][G0][B1] + s->gm2[R1][G0][B0] -
+	  s->gm2[R0][G1][B1] + s->gm2[R0][G1][B0] +
+	  s->gm2[R0][G0][B1] - s->gm2[R0][G0][B0]);
+    return (xx - (dR * dR + dG * dG + dB * dB) / Volume(cubePtr, s->wt));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ *	We want to minimize the sum of the variances of two subboxes.
+ *	The sum(c^2) terms can be ignored since their sum over both
+ *	subboxes is the same (the sum for the whole box) no matter
+ *	where we split.  The remaining terms have a minus sign in
+ *	the variance formula, so we drop the minus sign and MAXIMIZE
+ *	the sum of the two terms.
+ *
+ *----------------------------------------------------------------------
+ */
+static double
+Maximize(cubePtr, dir, first, last, cut, rWhole, gWhole, bWhole, wWhole, s)
+    Cube *cubePtr;
+    unsigned char dir;
+    int first, last, *cut;
+    long int rWhole, gWhole, bWhole, wWhole;
+    ColorImageStatistics *s;
+{
+    register long int rHalf, gHalf, bHalf, wHalf;
+    long int rBase, gBase, bBase, wBase;
+    register int i;
+    register double temp, max;
+
+    rBase = Bottom(cubePtr, dir, s->mR);
+    gBase = Bottom(cubePtr, dir, s->mG);
+    bBase = Bottom(cubePtr, dir, s->mB);
+    wBase = Bottom(cubePtr, dir, s->wt);
+    max = 0.0;
+    *cut = -1;
+    for (i = first; i < last; i++) {
+	rHalf = rBase + Top(cubePtr, dir, i, s->mR);
+	gHalf = gBase + Top(cubePtr, dir, i, s->mG);
+	bHalf = bBase + Top(cubePtr, dir, i, s->mB);
+	wHalf = wBase + Top(cubePtr, dir, i, s->wt);
+
+	/* Now half_x is sum over lower half of box, if split at i */
+	if (wHalf == 0) {	/* subbox could be empty of pixels! */
+	    continue;		/* never split into an empty box */
+	} else {
+	    temp = ((double)rHalf * rHalf + (float)gHalf * gHalf +
+		    (double)bHalf * bHalf) / wHalf;
+	}
+	rHalf = rWhole - rHalf;
+	gHalf = gWhole - gHalf;
+	bHalf = bWhole - bHalf;
+	wHalf = wWhole - wHalf;
+	if (wHalf == 0) {	/* Subbox could be empty of pixels! */
+	    continue;		/* never split into an empty box */
+	} else {
+	    temp += ((double)rHalf * rHalf + (float)gHalf * gHalf +
+		(double)bHalf * bHalf) / wHalf;
+	}
+	if (temp > max) {
+	    max = temp;
+	    *cut = i;
+	}
+    }
+    return max;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *----------------------------------------------------------------------
+ */
+static int
+Cut(set1, set2, s)
+    Cube *set1, *set2;
+    ColorImageStatistics *s;
+{
+    unsigned char dir;
+    int rCut, gCut, bCut;
+    double rMax, gMax, bMax;
+    long int rWhole, gWhole, bWhole, wWhole;
+
+    rWhole = Volume(set1, s->mR);
+    gWhole = Volume(set1, s->mG);
+    bWhole = Volume(set1, s->mB);
+    wWhole = Volume(set1, s->wt);
+
+    rMax = Maximize(set1, RED, set1->r0 + 1, set1->r1, &rCut,
+	rWhole, gWhole, bWhole, wWhole, s);
+    gMax = Maximize(set1, GREEN, set1->g0 + 1, set1->g1, &gCut,
+	rWhole, gWhole, bWhole, wWhole, s);
+    bMax = Maximize(set1, BLUE, set1->b0 + 1, set1->b1, &bCut,
+	rWhole, gWhole, bWhole, wWhole, s);
+
+    if ((rMax >= gMax) && (rMax >= bMax)) {
+	dir = RED;
+	if (rCut < 0) {
+	    return 0;		/* can't split the box */
+	}
+    } else {
+	dir = ((gMax >= rMax) && (gMax >= bMax)) ? GREEN : BLUE;
+    }
+    set2->r1 = set1->r1;
+    set2->g1 = set1->g1;
+    set2->b1 = set1->b1;
+
+    switch (dir) {
+    case RED:
+	set2->r0 = set1->r1 = rCut;
+	set2->g0 = set1->g0;
+	set2->b0 = set1->b0;
+	break;
+
+    case GREEN:
+	set2->g0 = set1->g1 = gCut;
+	set2->r0 = set1->r0;
+	set2->b0 = set1->b0;
+	break;
+
+    case BLUE:
+	set2->b0 = set1->b1 = bCut;
+	set2->r0 = set1->r0;
+	set2->g0 = set1->g0;
+	break;
+    }
+    set1->vol = (set1->r1 - set1->r0) * (set1->g1 - set1->g0) *
+	(set1->b1 - set1->b0);
+    set2->vol = (set2->r1 - set2->r0) * (set2->g1 - set2->g0) *
+	(set2->b1 - set2->b0);
+    return 1;
+}
+
+
+static int
+SplitColorSpace(s, cubes, nColors)
+    ColorImageStatistics *s;
+    Cube *cubes;
+    int nColors;
+{
+    double *vv, temp;
+    register int i;
+    register int n, k;
+
+    vv = Blt_Malloc(sizeof(double) * nColors);
+    assert(vv);
+    
+    cubes[0].r0 = cubes[0].g0 = cubes[0].b0 = 0;
+    cubes[0].r1 = cubes[0].g1 = cubes[0].b1 = 32;
+    for (i = 1, n = 0; i < nColors; i++) {
+	if (Cut(cubes + n, cubes + i, s)) {
+	    /*
+	     * Volume test ensures we won't try to cut one-cell box
+	     */
+	    vv[n] = vv[i] = 0.0;
+	    if (cubes[n].vol > 1) {
+		vv[n] = Variance(cubes + n, s);
+	    }
+	    if (cubes[i].vol > 1) {
+		vv[i] = Variance(cubes + i, s);
+	    }
+	} else {
+	    vv[n] = 0.0;	/* don't try to split this box again */
+	    i--;		/* didn't create box i */
+	}
+	
+	n = 0;
+	temp = vv[0];
+	for (k = 1; k <= i; k++) {
+	    if (vv[k] > temp) {
+		temp = vv[k];
+		n = k;
+	    }
+	}
+	if (temp <= 0.0) {
+	    i++;
+	    fprintf(stderr, "Only got %d boxes\n", i);
+	    break;
+	}
+    }
+    Blt_Free(vv);
+    return i;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *--------------------------------------------------------------------
+ */
+static void
+Mark(cubePtr, label, tag)
+    Cube *cubePtr;
+    int label;
+    unsigned int tag[33][33][33];
+{
+    register int r, g, b;
+
+    for (r = R0 + 1; r <= R1; r++) {
+	for (g = G0 + 1; g <= G1; g++) {
+	    for (b = B0 + 1; b <= B1; b++) {
+		tag[r][g][b] = label;
+	    }
+	}
+    }
+}
+
+static unsigned int *
+CreateColorLookupTable(s, cubes, nColors)
+    ColorImageStatistics *s;
+    Cube *cubes;
+    int nColors;
+{
+    unsigned int *lut;
+    Pix32 color;
+    unsigned int red, green, blue;
+    unsigned int weight;
+    register Cube *cubePtr;
+    register int i;
+
+    lut = Blt_Calloc(sizeof(unsigned int), 33 * 33 * 33);
+    assert(lut);
+
+    color.Alpha = (unsigned char)-1;
+    for (cubePtr = cubes, i = 0; i < nColors; i++, cubePtr++) {
+	weight = Volume(cubePtr, s->wt);
+	if (weight) {
+	    red = (Volume(cubePtr, s->mR) / weight) * (NC + 1);
+	    green = (Volume(cubePtr, s->mG) / weight) * (NC + 1);
+	    blue = (Volume(cubePtr, s->mB) / weight) * (NC + 1);
+	} else {
+	    fprintf(stderr, "bogus box %d\n", i);
+	    red = green = blue = 0;
+	}
+	color.Red = red >> 8;
+	color.Green = green >> 8;
+	color.Blue = blue >> 8;
+	Mark(cubePtr, color.value, lut);
+    }
+    return lut;
+}
+
+static void
+MapColors(src, dest, lut) 
+     Blt_ColorImage src, dest;
+     unsigned int lut[33][33][33];
+{
+    /* Apply the color lookup table against the original image */
+    int width, height;
+    int count;
+    Pix32 *srcPtr, *destPtr, *endPtr;
+    unsigned char alpha;
+    
+    width = Blt_ColorImageWidth(src);
+    height = Blt_ColorImageHeight(src);
+    count = width * height;
+    
+    srcPtr = Blt_ColorImageBits(src);
+    destPtr = Blt_ColorImageBits(dest);
+    for (endPtr = destPtr + count; destPtr < endPtr; srcPtr++, destPtr++) {
+	alpha = srcPtr->Alpha;
+	destPtr->value = lut[srcPtr->Red>>3][srcPtr->Green>>3][srcPtr->Blue>>3];
+	destPtr->Alpha = alpha;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_QuantizeColorImage --
+ *
+ *	C Implementation of Wu's Color Quantizer (v. 2) (see Graphics Gems
+ *	vol. II, pp. 126-133)
+ *
+ *	Author: Xiaolin Wu
+ *		Dept. of Computer Science Univ. of Western
+ *		Ontario London, Ontario
+ *		N6A 5B7
+ *		wu@csd.uwo.ca
+ *
+ *	Algorithm:
+ *		Greedy orthogonal bipartition of RGB space for variance
+ *		minimization aided by inclusion-exclusion tricks.  For
+ *		speed no nearest neighbor search is done. Slightly
+ *		better performance can be expected by more
+ *		sophisticated but more expensive versions.
+ *
+ *	The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
+ *	additional documentation and a cure to a previous bug.
+ *
+ *	Free to distribute, comments and suggestions are appreciated.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_QuantizeColorImage(src, dest, reduceColors)
+    Blt_ColorImage src, dest;	/* Source and destination images. */
+    int reduceColors;		/* Reduced number of colors. */
+{
+    Cube *cubes;
+    ColorImageStatistics *statistics;
+    int nColors;
+    unsigned int *lut;
+
+    /*
+     * Allocated a structure to hold color statistics.
+     */
+    statistics = GetColorImageStatistics(src);
+    M3d(statistics);
+
+    cubes = Blt_Malloc(sizeof(Cube) * reduceColors);
+    assert(cubes);
+
+    nColors = SplitColorSpace(statistics, cubes, reduceColors);
+    assert(nColors <= reduceColors);
+
+    lut = CreateColorLookupTable(statistics, cubes, nColors);
+    Blt_Free(statistics);
+    Blt_Free(cubes);
+    MapColors(src, dest, lut);
+    Blt_Free(lut);
+    return TCL_OK;
+}
+
+Region2D *
+Blt_SetRegion(x, y, width, height, regionPtr)
+    int x, y, width, height;
+    Region2D *regionPtr;	
+{
+    regionPtr->left = x;
+    regionPtr->top = y;
+    regionPtr->right = x + width - 1;
+    regionPtr->bottom = y + height - 1;
+    return regionPtr;
+}
+
+
+/*
+ * Each call to Tk_GetImage returns a pointer to one of the following
+ * structures, which is used as a token by clients (widgets) that
+ * display images.
+ */
+typedef struct TkImageStruct {
+    Tk_Window tkwin;		/* Window passed to Tk_GetImage (needed to
+				 * "re-get" the image later if the manager
+				 * changes). */
+    Display *display;		/* Display for tkwin.  Needed because when
+				 * the image is eventually freed tkwin may
+				 * not exist anymore. */
+    struct TkImageMasterStruct *masterPtr;
+				/* Master for this image (identifiers image
+				 * manager, for example). */
+    ClientData instanceData;
+				/* One word argument to pass to image manager
+				 * when dealing with this image instance. */
+    Tk_ImageChangedProc *changeProc;
+				/* Code in widget to call when image changes
+				 * in a way that affects redisplay. */
+    ClientData widgetClientData;
+				/* Argument to pass to changeProc. */
+    struct Image *nextPtr;	/* Next in list of all image instances
+				 * associated with the same name. */
+
+} TkImage;
+
+/*
+ * For each image master there is one of the following structures,
+ * which represents a name in the image table and all of the images
+ * instantiated from it.  Entries in mainPtr->imageTable point to
+ * these structures.
+ */
+typedef struct TkImageMasterStruct {
+    Tk_ImageType *typePtr;	/* Information about image type.  NULL means
+				 * that no image manager owns this image:  the
+				 * image was deleted. */
+    ClientData masterData;	/* One-word argument to pass to image mgr
+				 * when dealing with the master, as opposed
+				 * to instances. */
+    int width, height;		/* Last known dimensions for image. */
+    Blt_HashTable *tablePtr;	/* Pointer to hash table containing image
+				 * (the imageTable field in some TkMainInfo
+				 * structure). */
+    Blt_HashEntry *hPtr;	/* Hash entry in mainPtr->imageTable for
+				 * this structure (used to delete the hash
+				 * entry). */
+    TkImage *instancePtr;	/* Pointer to first in list of instances
+				 * derived from this name. */
+} TkImageMaster;
+
+
+typedef struct TkPhotoMasterStruct TkPhotoMaster;
+typedef struct TkColorTableStruct TkColorTable;
+
+typedef struct TkPhotoInstanceStruct {
+    TkPhotoMaster *masterPtr;	/* Pointer to master for image. */
+    Display *display;		/* Display for windows using this instance. */
+    Colormap colormap;		/* The image may only be used in windows with
+				 * this particular colormap. */
+    struct TkPhotoInstanceStruct *nextPtr;
+				/* Pointer to the next instance in the list
+				 * of instances associated with this master. */
+    int refCount;		/* Number of instances using this structure. */
+    Tk_Uid palette;		/* Palette for these particular instances. */
+    double outputGamma;		/* Gamma value for these instances. */
+    Tk_Uid defaultPalette;	/* Default palette to use if a palette
+				 * is not specified for the master. */
+    TkColorTable *colorTablePtr;	/* Pointer to information about colors
+				 * allocated for image display in windows
+				 * like this one. */
+    Pixmap pixels;		/* X pixmap containing dithered image. */
+    int width, height;		/* Dimensions of the pixmap. */
+    char *error;		/* Error image, used in dithering. */
+    XImage *imagePtr;		/* Image structure for converted pixels. */
+    XVisualInfo visualInfo;	/* Information about the visual that these
+				 * windows are using. */
+    GC gc;			/* Graphics context for writing images
+				 * to the pixmap. */
+} TkPhotoInstance;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Tk_ImageDeleted --
+ *
+ *	Is there any other way to determine if an image has been
+ *	deleted?
+ *
+ * Results:
+ *	Returns 1 if the image has been deleted, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Tk_ImageIsDeleted(tkImage)
+    Tk_Image tkImage;		/* Token for image. */
+{
+    TkImage *imagePtr = (TkImage *) tkImage;
+
+    if (imagePtr->masterPtr == NULL) {
+	return TRUE;
+    }
+    return (imagePtr->masterPtr->typePtr == NULL);
+}
+
+/*LINTLIBRARY*/
+Tk_ImageMaster
+Tk_ImageGetMaster(tkImage)
+    Tk_Image tkImage;		/* Token for image. */
+{
+    TkImage *imagePtr = (TkImage *)tkImage;
+
+    return (Tk_ImageMaster) imagePtr->masterPtr;
+}
+
+/*LINTLIBRARY*/
+Tk_ImageType *
+Tk_ImageGetType(tkImage)
+    Tk_Image tkImage;		/* Token for image. */
+{
+    TkImage *imagePtr = (TkImage *)tkImage;
+
+    return imagePtr->masterPtr->typePtr;
+}
+
+/*LINTLIBRARY*/
+Pixmap
+Tk_ImageGetPhotoPixmap(tkImage)
+    Tk_Image tkImage;		/* Token for image. */
+{
+    TkImage *imagePtr = (TkImage *)tkImage;
+
+    if (strcmp(imagePtr->masterPtr->typePtr->name, "photo") == 0) {
+	TkPhotoInstance *instPtr = (TkPhotoInstance *)imagePtr->instanceData;
+	return instPtr->pixels;
+    }
+    return None;
+}
+
+/*LINTLIBRARY*/
+GC
+Tk_ImageGetPhotoGC(photoImage)
+    Tk_Image photoImage;		/* Token for image. */
+{
+    TkImage *imagePtr = (TkImage *) photoImage;
+    if (strcmp(imagePtr->masterPtr->typePtr->name, "photo") == 0) {
+	TkPhotoInstance *instPtr = (TkPhotoInstance *)imagePtr->instanceData;
+	return instPtr->gc;
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TempImageChangedProc
+ *
+ *	The image is over-written each time it's resized.  We always
+ *	resample from the color image we saved when the photo image
+ *	was specified (-image option). So we only worry if the image
+ *	is deleted.
+ *
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+/* ARGSUSED */
+static void
+TempImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+    ClientData clientData;
+    int x, y, width, height;	/* Not used. */
+    int imageWidth, imageHeight;/* Not used. */
+{
+#ifdef notdef
+    fprintf(stderr, "should be redrawing temp image\n");
+#endif
+}
+
+Tk_Image 
+Blt_CreateTemporaryImage(interp, tkwin, clientData)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    ClientData clientData;
+{
+    Tk_Image token;
+    char *name;			/* Contains image name. */
+
+    if (Tcl_Eval(interp, "image create photo") != TCL_OK) {
+	return NULL;
+    }
+    name = (char *)Tcl_GetStringResult(interp);
+    token = Tk_GetImage(interp, tkwin, name, TempImageChangedProc, clientData);
+    if (token == NULL) {
+	return NULL;
+    }
+    return token;
+}
+
+int
+Blt_DestroyTemporaryImage(interp, tkImage)
+    Tcl_Interp *interp;
+    Tk_Image tkImage;
+{
+    if (tkImage != NULL) {
+	if (Tcl_VarEval(interp, "image delete ", Blt_NameOfImage(tkImage), 
+			(char *)NULL) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Tk_FreeImage(tkImage);
+    }
+    return TCL_OK;
+}
+
+char *
+Blt_NameOfImage(tkImage)
+    Tk_Image tkImage;
+{
+    Tk_ImageMaster master;
+
+    master = Tk_ImageGetMaster(tkImage);
+    return Tk_NameOfImage(master);
+}
Index: trunk/kitgen/8.x/blt/generic/bltImage.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltImage.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltImage.h	(revision 175)
@@ -0,0 +1,307 @@
+/*
+ * bltImage.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include <X11/Xutil.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+#ifndef _BLT_IMAGE_H
+#define _BLT_IMAGE_H
+
+#define DIV255(i) ((((i) + 1) + (((i) + 1) >> 8) ) >> 8)
+
+#define GAMMA	(1.0)
+
+#define ROTATE_0	0
+#define ROTATE_90	1
+#define ROTATE_180	2
+#define ROTATE_270	3
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Pix32 --
+ *
+ *      A union representing either a pixel as a RGB triplet or a
+ *	single word value.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef union {
+    unsigned int value;		/* Lookup table address */
+    struct RGBA {
+	unsigned char red;	/* Red intensity 0..255 */
+	unsigned char green;	/* Green intensity 0.255 */
+	unsigned char blue;	/* Blue intensity 0..255 */
+	unsigned char alpha;	/* Alpha-channel for compositing. 0..255 */
+    } rgba;
+    unsigned char channel[4];
+} Pix32;
+
+#define Red	rgba.red
+#define Blue	rgba.blue
+#define Green	rgba.green
+#define Alpha	rgba.alpha
+
+
+typedef struct {
+    XColor exact, best;
+    double error;
+    unsigned int freq;
+    int allocated;
+    int index;
+} ColorInfo;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorTable --
+ *
+ *	For colormap-ed visuals, this structure contains color lookup
+ *	information needed to translate RGB triplets to pixel indices.
+ *
+ *	This structure isn't needed for TrueColor or Monochrome visuals.
+ *
+ *	DirectColor:
+ *		Pixel values for each color channel
+ *	StaticColor, PsuedoColor, StaticGray, and GrayScale:
+ *		Red represents the 8-bit color. Green and Blue pixel
+ *		values are unused.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct ColorTableStruct {
+    double outputGamma;		/* Gamma correction value */
+    Display *display;		/* Display of colortable. Used to free
+				 * colors allocated. */
+    XVisualInfo visualInfo;	/* Visual information for window displaying
+				 * the image. */
+    Colormap colorMap;		/* Colormap used.  This may be the default
+				 * colormap, or an allocated private map. */
+    int flags;
+    unsigned int red[256], green[256], blue[256];
+
+    /* Array of allocated pixels in colormap */
+    ColorInfo colorInfo[256];
+    ColorInfo *sortedColors[256];
+
+    int nUsedColors, nFreeColors;
+    int nPixels;		/* Number of colors in the quantized image */
+    unsigned long int pixelValues[256];
+
+    unsigned int *lut;		/* Color lookup table. Used to collect
+				 * frequencies of colors and later
+				 * colormap indices */
+} *ColorTable;
+
+#define PRIVATE_COLORMAP	1
+#define RGBIndex(r,g,b) (((r)<<10) + ((r)<<6) + (r) + ((g) << 5) + (g) + (b))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImage --
+ *
+ *      The structure below represents a color image.  Each pixel
+ *	occupies a 32-bit word of memory: one byte for each of the
+ *	red, green, and blue color intensities, and another for
+ *	alpha-channel image compositing (e.g. transparency).
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct ColorImage {
+    int width, height;		/* Dimensions of the image */
+    Pix32 *bits;		/* Array of pixels representing the image. */
+} *Blt_ColorImage;
+
+/*
+ * Blt_ColorImage is supposed to be an opaque type.
+ * Use the macros below to access its members.
+ */
+#define Blt_ColorImageHeight(c)	((c)->height)
+#define Blt_ColorImageWidth(c)	((c)->width)
+#define Blt_ColorImageBits(c)	((c)->bits)
+#define Blt_ColorImagePixel(c, x, y) ((c)->bits + ((c)->width * (y)) + (x))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResampleFilterProc --
+ *
+ *      A function implementing a 1-D filter.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef double (ResampleFilterProc) _ANSI_ARGS_((double value));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResampleFilter --
+ *
+ *      Contains information about a 1-D filter (its support and
+ *	the procedure implementing the filter).
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Name of the filter */
+    ResampleFilterProc *proc;	/* 1-D filter procedure. */
+    double support;		/* Width of 1-D filter */
+} ResampleFilter;
+
+extern ResampleFilter *bltBoxFilterPtr; /* The ubiquitous box filter */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Filter2D --
+ *
+ *      Defines a convolution mask for a 2-D filter.  Used to smooth or
+ *	enhance images.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+    double support;		/* Radius of filter */
+    double sum, scale;		/* Sum of kernel */
+    double *kernel;		/* Array of values (malloc-ed) representing
+				 * the discrete 2-D filter. */
+} Filter2D;
+
+/* Prototypes of image routines */
+
+extern void Blt_ColorImageToGreyscale _ANSI_ARGS_((Blt_ColorImage image));
+
+extern void Blt_ColorImageToPhoto _ANSI_ARGS_((Blt_ColorImage image,
+	Tk_PhotoHandle photo));
+
+extern Pixmap Blt_ColorImageToPixmap _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Blt_ColorImage image, ColorTable *colorTablePtr));
+
+extern Blt_ColorImage Blt_ConvolveColorImage _ANSI_ARGS_((
+	Blt_ColorImage srcImage, Filter2D *filter));
+
+extern Blt_ColorImage Blt_CreateColorImage _ANSI_ARGS_((int width,int height));
+
+extern Blt_ColorImage Blt_DrawableToColorImage _ANSI_ARGS_((Tk_Window tkwin, 
+	Drawable drawable, int x, int y, int width, int height, 
+	double inputGamma));
+
+extern int Blt_GetResampleFilter _ANSI_ARGS_((Tcl_Interp *interp,
+	char *filterName, ResampleFilter **filterPtrPtr));
+
+extern void Blt_FreeColorImage _ANSI_ARGS_((Blt_ColorImage image));
+
+#if HAVE_JPEG
+extern Blt_ColorImage Blt_JPEGToColorImage _ANSI_ARGS_((Tcl_Interp *interp,
+	char *fileName));
+#endif
+
+extern Blt_ColorImage Blt_PhotoToColorImage _ANSI_ARGS_((
+	Tk_PhotoHandle photo));
+
+extern Blt_ColorImage Blt_PhotoRegionToColorImage _ANSI_ARGS_((
+	Tk_PhotoHandle photo, int x, int y, int width, int height));
+
+extern int Blt_QuantizeColorImage _ANSI_ARGS_((Blt_ColorImage src, 
+        Blt_ColorImage dest, int nColors));
+
+extern Blt_ColorImage Blt_ResampleColorImage _ANSI_ARGS_((Blt_ColorImage image,
+	int destWidth, int destHeight, ResampleFilter *horzFilterPtr, 
+	ResampleFilter *vertFilterPtr));
+
+extern void Blt_ResamplePhoto _ANSI_ARGS_((Tk_PhotoHandle srcPhoto,
+	int x, int y, int width, int height, Tk_PhotoHandle destPhoto,
+	ResampleFilter *horzFilterPtr, ResampleFilter *vertFilterPtr));
+
+extern Blt_ColorImage Blt_ResizeColorImage _ANSI_ARGS_((Blt_ColorImage src,
+	int x, int y, int width, int height, int destWidth, int destHeight));
+
+extern Blt_ColorImage Blt_ResizeColorSubimage _ANSI_ARGS_((Blt_ColorImage src,
+	int x, int y, int width, int height, int destWidth, int destHeight));
+
+extern Blt_ColorImage Blt_RotateColorImage _ANSI_ARGS_((Blt_ColorImage image,
+	double theta));
+
+extern void Blt_ResizePhoto _ANSI_ARGS_((Tk_PhotoHandle srcPhoto, int x, int y,
+	int width, int height, Tk_PhotoHandle destPhoto));
+
+extern int Blt_SnapPhoto _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	Drawable drawable, int x, int y, int width, int height, int destWidth,
+	int destHeight, char *photoName, double inputGamma));
+
+extern void Blt_ImageRegion _ANSI_ARGS_((Blt_ColorImage image, 
+	Region2D *regionPtr));
+
+extern Region2D *Blt_ColorImageRegion _ANSI_ARGS_((Blt_ColorImage image, 
+	Region2D *regionPtr));
+
+extern Region2D *Blt_SetRegion _ANSI_ARGS_((int x, int y, int width, 
+	int height, Region2D *regionPtr));
+
+extern ColorTable Blt_CreateColorTable _ANSI_ARGS_((Tk_Window tkwin));
+
+extern ColorTable Blt_DirectColorTable _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Blt_ColorImage image));
+
+extern ColorTable Blt_PseudoColorTable _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Blt_ColorImage image));
+
+extern void Blt_FreeColorTable _ANSI_ARGS_((ColorTable colorTable));
+
+/* Missing routines from the Tk photo C API */
+
+extern int Tk_ImageIsDeleted _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_ImageMaster Tk_ImageGetMaster _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_ImageType *Tk_ImageGetType _ANSI_ARGS_((Tk_Image tkImage));
+extern Pixmap Tk_ImageGetPhotoPixmap _ANSI_ARGS_((Tk_Image photoImage));
+extern GC Tk_ImageGetPhotoGC _ANSI_ARGS_((Tk_Image photoImage));
+
+extern char *Blt_NameOfImage _ANSI_ARGS_((Tk_Image tkImage));
+extern Tk_Image Blt_CreateTemporaryImage _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, ClientData clientData));
+extern int Blt_DestroyTemporaryImage _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Image tkImage));
+
+extern GC Blt_GetBitmapGC _ANSI_ARGS_((Tk_Window tkwin));
+extern Pixmap Blt_PhotoImageMask _ANSI_ARGS_((Tk_Window tkwin, 
+	Tk_PhotoImageBlock src));
+
+extern Pixmap Blt_RotateBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap bitmap,
+	int width, int height, double theta, int *widthPtr, int *heightPtr));
+
+extern Pixmap Blt_ScaleBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap srcBitmap,
+	int srcWidth, int srcHeight, int scaledWidth, int scaledHeight));
+
+extern Pixmap Blt_ScaleRotateBitmapRegion _ANSI_ARGS_((Tk_Window tkwin,
+	Pixmap srcBitmap, unsigned int srcWidth, unsigned int srcHeight, 
+	int regionX, int regionY, unsigned int regionWidth, 
+	unsigned int regionHeight, unsigned int virtWidth, 
+	unsigned int virtHeight, double theta));
+
+#endif /*_BLT_IMAGE_H*/
Index: trunk/kitgen/8.x/blt/generic/bltInit.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltInit.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltInit.c	(revision 175)
@@ -0,0 +1,364 @@
+
+/*
+ * bltInit.c --
+ *
+ *	This module initials the BLT toolkit, registering its commands
+ *	with the Tcl/Tk interpreter.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#define EXACT 0
+
+double bltNaN;
+Tcl_Obj *bltEmptyStringObjPtr;
+
+static Tcl_MathProc MinMathProc, MaxMathProc;
+
+static Tcl_AppInitProc *bltCmds[] =
+{
+    Blt_BusyInit,
+    Blt_VectorInit,
+    Blt_SplineInit,
+    Blt_Crc32Init,
+    Blt_GraphInit,
+    Blt_TableInit,
+    Blt_TabnotebookInit,
+    Blt_BitmapInit,
+    Blt_TreeInit,
+    Blt_TreeViewInit,
+#ifndef NO_PRINTER
+    Blt_PrinterInit,
+#endif
+    (Tcl_AppInitProc *) NULL
+};
+
+#ifdef __BORLANDC__
+static double
+MakeNaN(void)
+{
+    union Real {
+        struct DoubleWord {
+            int lo, hi;
+        } doubleWord;
+        double number;
+    } real;
+
+    real.doubleWord.lo = real.doubleWord.hi = 0x7FFFFFFF;
+    return real.number;
+}
+#endif /* __BORLANDC__ */
+
+#ifdef _MSC_VER
+static double
+MakeNaN(void)
+{
+    return sqrt(-1.0);  /* Generate IEEE 754 Quiet Not-A-Number. */
+}
+#endif /* _MSC_VER */
+
+#if !defined(__BORLANDC__) && !defined(_MSC_VER)
+static double
+MakeNaN(void)
+{
+    return 0.0 / 0.0;           /* Generate IEEE 754 Not-A-Number. */
+}
+#endif /* !__BORLANDC__  && !_MSC_VER */
+
+/* ARGSUSED */
+static int
+MinMathProc(clientData, interp, argsPtr, resultPtr)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    Tcl_Value *argsPtr;
+    Tcl_Value *resultPtr;
+{
+    Tcl_Value *op1Ptr, *op2Ptr;
+
+    op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+    if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+	resultPtr->intValue = MIN(op1Ptr->intValue, op2Ptr->intValue);
+	resultPtr->type = TCL_INT;
+    } else {
+	double a, b;
+
+	a = (op1Ptr->type == TCL_INT) 
+	    ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+	b = (op2Ptr->type == TCL_INT)
+	    ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+	resultPtr->doubleValue = MIN(a, b);
+	resultPtr->type = TCL_DOUBLE;
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+MaxMathProc(clientData, interp, argsPtr, resultPtr)
+    ClientData clientData;	/* Not Used. */
+    Tcl_Interp *interp;
+    Tcl_Value *argsPtr;
+    Tcl_Value *resultPtr;
+{
+    Tcl_Value *op1Ptr, *op2Ptr;
+
+    op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+    if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+	resultPtr->intValue = MAX(op1Ptr->intValue, op2Ptr->intValue);
+	resultPtr->type = TCL_INT;
+    } else {
+	double a, b;
+
+	a = (op1Ptr->type == TCL_INT)
+	    ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+	b = (op2Ptr->type == TCL_INT)
+	    ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+	resultPtr->doubleValue = MAX(a, b);
+	resultPtr->type = TCL_DOUBLE;
+    }
+    return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+EXPORT int
+Blt_Init(interp)
+    Tcl_Interp *interp;		/* Interpreter to add extra commands */
+{
+    Tcl_AppInitProc **p;
+    Tcl_Namespace *nsPtr;
+    Tcl_ValueType args[2];
+    
+    /*
+     * Check that the versions of Tcl that have been loaded are
+     * the same ones that BLT was compiled against.
+     */
+
+    if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, EXACT) == NULL) {
+        return TCL_ERROR;
+    }
+
+    /* Set the "blt_version", "blt_patchLevel", and "blt_libPath" Tcl
+     * variables. We'll use them in the following script. */
+    if ((Tcl_SetVar(interp, "blt_version", BLT_VERSION, 
+    		TCL_GLOBAL_ONLY) == NULL) ||
+        (Tcl_SetVar(interp, "blt_patchLevel", BLT_PATCH_LEVEL, 
+    		TCL_GLOBAL_ONLY) == NULL)) {
+        return TCL_ERROR;
+    }
+
+    nsPtr = Tcl_CreateNamespace(interp, "blt", NULL,
+    			    (Tcl_NamespaceDeleteProc *) NULL);
+    if (nsPtr == NULL) {
+        return TCL_ERROR;
+    }
+    /* Initialize the BLT commands that only require Tcl. */
+    for (p = bltCmds; *p != NULL; p++) {
+        if ((**p) (interp) != TCL_OK) {
+    	Tcl_DeleteNamespace(nsPtr);
+    	return TCL_ERROR;
+        }
+    }
+    args[0] = args[1] = TCL_EITHER;
+    Tcl_CreateMathFunc(interp, "min", 2, args, MinMathProc, (ClientData)0);
+    Tcl_CreateMathFunc(interp, "max", 2, args, MaxMathProc, (ClientData)0);
+    Blt_RegisterArrayObj(interp);
+    bltEmptyStringObjPtr = Tcl_NewStringObj("", -1);
+    bltNaN = MakeNaN();
+
+    return Tcl_PkgProvide(interp, "BLT", BLT_VERSION);
+}
+
+
+/*LINTLIBRARY*/
+EXPORT int
+Blt_SafeInit(interp)
+    Tcl_Interp *interp;		/* Interpreter to add extra commands */
+{
+    return Blt_Init(interp);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitCmd --
+ *
+ *      Given the name of a command, return a pointer to the
+ *      clientData field of the command.
+ *
+ * Results:
+ *      A standard TCL result. If the command is found, TCL_OK
+ *	is returned and clientDataPtr points to the clientData
+ *	field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ *      If the command is found, clientDataPtr is set to the address
+ *	of the clientData of the command.  If not found, an error
+ *	message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+Tcl_Command
+Blt_InitCmd(interp, nsName, specPtr)
+    Tcl_Interp *interp;
+    char *nsName;
+    Blt_CmdSpec *specPtr;
+{
+    char *cmdPath;
+    Tcl_DString dString;
+    Tcl_Command cmdToken;
+
+    Tcl_DStringInit(&dString);
+
+    if (nsName != NULL) {
+	Tcl_DStringAppend(&dString, nsName, -1);
+    }
+    Tcl_DStringAppend(&dString, "::", -1);
+
+    Tcl_DStringAppend(&dString, specPtr->name, -1);
+
+    cmdPath = Tcl_DStringValue(&dString);
+    cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0);
+    if (cmdToken != NULL) {
+	Tcl_DStringFree(&dString);
+	return cmdToken;	/* Assume command was already initialized */
+    }
+    cmdToken = Tcl_CreateCommand(interp, cmdPath, specPtr->cmdProc,
+	specPtr->clientData, specPtr->cmdDeleteProc);
+    Tcl_DStringFree(&dString);
+
+    {
+	Tcl_Namespace *nsPtr;
+	int dontResetList = 0;
+
+	nsPtr = Tcl_FindNamespace(interp, nsName, (Tcl_Namespace *)NULL,
+	    TCL_LEAVE_ERR_MSG);
+	if (nsPtr == NULL) {
+	    return NULL;
+	}
+	if (Tcl_Export(interp, nsPtr, specPtr->name, dontResetList) != TCL_OK) {
+	    return NULL;
+	}
+    }
+    return cmdToken;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitObjCmd --
+ *
+ *      Given the name of a command, return a pointer to the
+ *      clientData field of the command.
+ *
+ * Results:
+ *      A standard TCL result. If the command is found, TCL_OK
+ *	is returned and clientDataPtr points to the clientData
+ *	field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ *      If the command is found, clientDataPtr is set to the address
+ *	of the clientData of the command.  If not found, an error
+ *	message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+Tcl_Command
+Blt_InitObjCmd(interp, nsName, specPtr)
+    Tcl_Interp *interp;
+    char *nsName;
+    Blt_ObjCmdSpec *specPtr;
+{
+    char *cmdPath;
+    Tcl_DString dString;
+    Tcl_Command cmdToken;
+    Tcl_Namespace *nsPtr;
+
+    Tcl_DStringInit(&dString);
+    if (nsName != NULL) {
+	Tcl_DStringAppend(&dString, nsName, -1);
+    }
+    Tcl_DStringAppend(&dString, "::", -1);
+    Tcl_DStringAppend(&dString, specPtr->name, -1);
+
+    cmdPath = Tcl_DStringValue(&dString);
+    cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0);
+    if (cmdToken != NULL) {
+	Tcl_DStringFree(&dString);
+	return cmdToken;	/* Assume command was already initialized */
+    }
+    cmdToken = Tcl_CreateObjCommand(interp, cmdPath, 
+		(Tcl_ObjCmdProc *)specPtr->cmdProc, 
+		specPtr->clientData, 
+		specPtr->cmdDeleteProc);
+    Tcl_DStringFree(&dString);
+
+    nsPtr = Tcl_FindNamespace(interp, nsName, (Tcl_Namespace *)NULL,
+	      TCL_LEAVE_ERR_MSG);
+    if (nsPtr == NULL) {
+	return NULL;
+    }
+    if (Tcl_Export(interp, nsPtr, specPtr->name, FALSE) != TCL_OK) {
+	return NULL;
+    }
+    return cmdToken;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_InitCmds --
+ *
+ *      Given the name of a command, return a pointer to the
+ *      clientData field of the command.
+ *
+ * Results:
+ *      A standard TCL result. If the command is found, TCL_OK
+ *	is returned and clientDataPtr points to the clientData
+ *	field of the command (if the clientDataPtr in not NULL).
+ *
+ * Side effects:
+ *      If the command is found, clientDataPtr is set to the address
+ *	of the clientData of the command.  If not found, an error
+ *	message is left in interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_InitCmds(interp, nsName, specPtr, nCmds)
+    Tcl_Interp *interp;
+    char *nsName;
+    Blt_CmdSpec *specPtr;
+    int nCmds;
+{
+    Blt_CmdSpec *endPtr;
+
+    for (endPtr = specPtr + nCmds; specPtr < endPtr; specPtr++) {
+	if (Blt_InitCmd(interp, nsName, specPtr) == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
Index: trunk/kitgen/8.x/blt/generic/bltInt.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltInt.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltInt.h	(revision 175)
@@ -0,0 +1,960 @@
+
+/*
+ * bltInt.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_INT_H
+#define _BLT_INT_H
+
+#ifdef WIN32
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef STRICT
+#undef WIN32_LEAN_AND_MEAN
+#include <windowsx.h>
+#endif /* WIN32 */
+
+#define USE_NON_CONST
+#include <tcl.h>
+#define USE_COMPOSITELESS_PHOTO_PUT_BLOCK 
+#include <tk.h>
+
+#define _VERSION(a,b,c)	    (((a) << 16) + ((b) << 8) + (c))
+#define TCL_VERSION_NUMBER _VERSION(TCL_MAJOR_VERSION, TCL_MINOR_VERSION, TCL_RELEASE_SERIAL)
+#define TK_VERSION_NUMBER _VERSION(TK_MAJOR_VERSION, TK_MINOR_VERSION, TK_RELEASE_SERIAL)
+
+#include "bltTkInt.h"
+
+#include <stdio.h>
+#include <assert.h>
+
+#if defined(WIN32) && !defined(__GNUC__)
+#include "bltWinConfig.h"
+#else 
+#include "bltConfig.h"
+#endif
+
+#ifdef WIN32 
+#ifndef EXPORT
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif /* _MSC_VER || __BORLANDC__ */
+#endif /* EXPORT */
+
+/* Misc. definitions */
+#define	NO_CUTBUFFER	1
+#define NO_TILESCROLLBAR	1
+#define NO_DND		1
+
+#ifndef __GNUC__
+#ifdef O_NONBLOCK
+#define O_NONBLOCK	1
+#endif
+#endif /* __GNUC__ */
+#endif /* WIN32 */
+
+#include "blt.h"
+#include "bltNsUtil.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif /* HAVE_ERRNO_H */
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif /* HAVE_CTYPE_H */
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif /* HAVE_MEMORY_H */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "bltMath.h"
+
+#undef INLINE
+#ifdef __GNUC__
+#define INLINE inline
+#else
+#define INLINE
+#endif
+#undef EXPORT
+#define EXPORT
+
+#undef VARARGS
+#ifdef __cplusplus
+#define ANYARGS (...)
+#define VARARGS(first)  (first, ...)
+#define VARARGS2(first, second)  (first, second, ...)
+#else
+#define ANYARGS ()
+#define VARARGS(first) ()
+#define VARARGS2(first, second) ()
+#endif /* __cplusplus */
+
+#undef MIN
+#define MIN(a,b)	(((a)<(b))?(a):(b))
+
+#undef MAX
+#define MAX(a,b)	(((a)>(b))?(a):(b))
+
+#undef MIN3
+#define MIN3(a,b,c)	(((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c)))
+
+#undef MAX3
+#define MAX3(a,b,c)	(((a)>(b))?(((a)>(c))?(a):(c)):(((b)>(c))?(b):(c)))
+
+#define TRUE 	1
+#define FALSE 	0
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define UCHAR(c) ((unsigned char) (c))
+
+#undef panic
+#define panic(mesg)	Blt_Panic("%s:%d %s", __FILE__, __LINE__, (mesg))
+
+/*
+ * Since the Tcl/Tk distribution doesn't perform any asserts, dynamic
+ * loading can fail to find the __assert function.  As a workaround,
+ * we'll include our own.
+ */
+#undef	assert
+#ifdef	NDEBUG
+#define	assert(EX) ((void)0)
+#else
+extern void Blt_Assert _ANSI_ARGS_((char *expr, char *file, int line));
+#ifdef __STDC__
+#define	assert(EX) (void)((EX) || (Blt_Assert(#EX, __FILE__, __LINE__), 0))
+#else
+#define	assert(EX) (void)((EX) || (Blt_Assert("EX", __FILE__, __LINE__), 0))
+#endif /* __STDC__ */
+
+#endif /* NDEBUG */
+
+#if (TCL_MAJOR_VERSION >= 8)
+extern Tcl_Obj *bltEmptyStringObjPtr;
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_CmdSpec --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Name of command */
+    Tcl_CmdProc *cmdProc;
+    Tcl_CmdDeleteProc *cmdDeleteProc;
+    ClientData clientData;
+} Blt_CmdSpec;
+
+#if (TCL_MAJOR_VERSION >= 8)
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_CmdSpec --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Name of command */
+    Tcl_ObjCmdProc *cmdProc;
+    Tcl_CmdDeleteProc *cmdDeleteProc;
+    ClientData clientData;
+} Blt_ObjCmdSpec;
+
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Op --
+ *
+ * 	Generic function prototype of CmdOptions.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef int (*Blt_Op) _ANSI_ARGS_(ANYARGS);
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_OpSpec --
+ *
+ * 	Structure to specify a set of operations for a Tcl command.
+ *      This is passed to the Blt_GetOp procedure to look
+ *      for a function pointer associated with the operation name.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    char *name;			/* Name of operation */
+    int minChars;		/* Minimum # characters to disambiguate */
+    Blt_Op proc;
+    int minArgs;		/* Minimum # args required */
+    int maxArgs;		/* Maximum # args required */
+    char *usage;		/* Usage message */
+
+} Blt_OpSpec;
+
+typedef enum {
+    BLT_OP_ARG0,		/* Op is the first argument. */
+    BLT_OP_ARG1,		/* Op is the second argument. */
+    BLT_OP_ARG2,		/* Op is the third argument. */
+    BLT_OP_ARG3,		/* Op is the fourth argument. */
+    BLT_OP_ARG4			/* Op is the fifth argument. */
+
+} Blt_OpIndex;
+
+#define BLT_OP_LINEAR_SEARCH	1
+#define BLT_OP_BINARY_SEARCH	0
+
+extern Blt_Op Blt_GetOp _ANSI_ARGS_((Tcl_Interp *interp, int nSpecs, 
+	Blt_OpSpec *specArr, int operPos, int argc, char **argv, int flags));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+extern Blt_Op Blt_GetOpFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	int nSpecs, Blt_OpSpec *specArr, int operPos, int objc, 
+	Tcl_Obj *CONST *objv, int flags));
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ *	Assume we need to declare free if there's no stdlib.h or malloc.h
+ *
+ * ----------------------------------------------------------------------
+ */
+#if !defined(HAVE_STDLIB_H) && !defined(HAVE_MALLOC_H)
+extern void free _ANSI_ARGS_((void *));
+#endif
+
+#define free(x)		abc123(x)
+
+extern int Blt_DictionaryCompare _ANSI_ARGS_((char *s1, char *s2));
+
+EXTERN void Blt_Panic _ANSI_ARGS_(TCL_VARARGS(char *, args));
+
+
+extern void Blt_Draw3DRectangle _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	Tk_3DBorder border, int x, int y, int width, int height, 
+	int borderWidth, int relief));
+extern void Blt_Fill3DRectangle _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	Tk_3DBorder border, int x, int y, int width, int height, 
+	int borderWidth, int relief));
+
+#ifdef notdef
+#define Blt_Fill3DRectangle	Tk_Fill3DRectangle
+#define Blt_Draw3DRectangle	Tk_Draw3DRectangle
+#endif
+
+/* ---------------------------------------------------------------- */
+
+
+#define PIXELS_NONNEGATIVE	0
+#define PIXELS_POSITIVE		1
+#define PIXELS_ANY		2
+
+#define COUNT_NONNEGATIVE	0
+#define COUNT_POSITIVE		1
+#define COUNT_ANY		2
+
+#define BLT_SCROLL_MODE_CANVAS	(1<<0)
+#define BLT_SCROLL_MODE_LISTBOX	(1<<1)
+#define BLT_SCROLL_MODE_HIERBOX	(1<<2)
+
+#define RGB_ANTIQUEWHITE1	"#ffefdb"
+#define RGB_BISQUE1		"#ffe4c4"
+#define RGB_BISQUE2		"#eed5b7"
+#define RGB_BISQUE3		"#cdb79e"
+#define RGB_BLACK		"#000000"
+#define RGB_BLUE		"#0000ff"
+#define RGB_GREEN		"#00ff00"
+#define RGB_GREY		"#b0b0b0"
+#define RGB_GREY15		"#262626"
+#define RGB_GREY50		"#7f7f7f"
+#define RGB_GREY64		"#a3a3a3"
+#define RGB_GREY70		"#b3b3b3"
+#define RGB_GREY75		"#bfbfbf"
+#define RGB_GREY77		"#c3c3c3"
+#define RGB_GREY82		"#d1d1d1"
+#define RGB_GREY85		"#d9d9d9"
+#define RGB_GREY90		"#e5e5e5"
+#define RGB_GREY95		"#f2f2f2"
+#define RGB_LIGHTBLUE0		"#e4f7ff"
+#define RGB_LIGHTBLUE1		"#bfefff"
+#define RGB_LIGHTBLUE2		"#b2dfee"
+#define RGB_LIGHTSKYBLUE1	"#b0e2ff"
+#define RGB_MAROON		"#b03060"
+#define RGB_NAVYBLUE		"#000080"
+#define RGB_PINK		"#ffc0cb"
+#define RGB_BISQUE1		"#ffe4c4"
+#define RGB_RED			"#ff0000"
+#define RGB_WHITE		"#ffffff"
+#define RGB_YELLOW		"#ffff00"
+
+#ifdef OLD_TK_COLORS
+#define STD_NORMAL_BACKGROUND	RGB_BISQUE1
+#define STD_ACTIVE_BACKGROUND	RGB_BISQUE2
+#define STD_SELECT_BACKGROUND	RGB_LIGHTBLUE1
+#define STD_DISABLE_FOREGROUND	RGB_GREY64
+#else
+#define STD_NORMAL_BACKGROUND	RGB_GREY85
+#define STD_ACTIVE_BACKGROUND	RGB_GREY64
+#define STD_SELECT_BACKGROUND	RGB_LIGHTBLUE1
+#define STD_DISABLE_FOREGROUND	RGB_GREY64
+#endif /* OLD_TK_COLORS */
+
+#define STD_ACTIVE_BG_MONO	RGB_BLACK
+#define STD_ACTIVE_FOREGROUND	RGB_BLACK
+#define STD_ACTIVE_FG_MONO	RGB_WHITE
+#define STD_BORDERWIDTH 	"2"
+#define STD_FONT		"*-Helvetica-Medium-R-Normal-*-12-120-*"
+#define STD_FONT_HUGE		"*-Helvetica-Medium-R-Normal-*-18-180-*"
+#define STD_FONT_LARGE		"*-Helvetica-Medium-R-Normal-*-14-140-*"
+#define STD_FONT_SMALL		"*-Helvetica-Medium-R-Normal-*-10-100-*"
+#define STD_INDICATOR_COLOR	RGB_MAROON
+#define STD_NORMAL_BG_MONO	RGB_WHITE
+#define STD_NORMAL_FOREGROUND	RGB_BLACK
+#define STD_NORMAL_FG_MONO	RGB_BLACK
+#define STD_SELECT_BG_MONO	RGB_BLACK
+#define STD_SELECT_BORDERWIDTH	"2"
+#define STD_SELECT_FOREGROUND	RGB_BLACK
+#define STD_SELECT_FG_MONO	RGB_WHITE
+#define STD_SHADOW_COLOR	RGB_GREY64
+#define STD_SHADOW_MONO		RGB_BLACK
+
+#define LineWidth(w)	(((w) > 1) ? (w) : 0)
+
+#ifdef TCL_UTF_MAX
+#define HAVE_UTF	1
+extern FILE *Blt_OpenUtfFile _ANSI_ARGS_((char *fileName, char *mode));
+#define fopen(f,m)	Blt_OpenUtfFile((f),(m));
+#else
+#define HAVE_UTF	0
+#endif /* TCL_UTF_MAX */
+
+typedef char *DestroyData;
+
+
+
+#ifndef TK_RELIEF_SOLID
+#define TK_RELIEF_SOLID		TK_RELIEF_FLAT
+#endif
+
+/*
+ * Tcl/Tk Backward compatibility section.
+ */
+#if (TCL_MAJOR_VERSION > 7)
+
+#define NO_FLAGS			0
+#define Blt_FindPhoto(interp, name)	Tk_FindPhoto(interp, name)
+
+#else
+
+#define Tcl_GetStringResult(interp)	((interp)->result)
+#define Blt_FindPhoto(interp, name)	Tk_FindPhoto(name)
+
+#define Tcl_DeleteCommandFromToken(interp, token) \
+	Tcl_DeleteCommand(interp, Tcl_GetCommandName(interp, token))
+
+/*
+ *--------------------------------------------------------------
+ *
+ * The definitions below provide foreward compatibility for
+ * functions and types related to event handling that used to
+ * be in Tk but have moved to Tcl.
+ *
+ *--------------------------------------------------------------
+ */
+
+#define Tcl_IdleProc		Tk_IdleProc
+#define Tcl_FileProc		Tk_FileProc
+#define Tcl_TimerProc		Tk_TimerProc
+#define Tcl_TimerToken		Tk_TimerToken
+
+#define Tcl_BackgroundError	Tk_BackgroundError
+#define Tcl_CancelIdleCall	Tk_CancelIdleCall
+
+#define Tcl_CreateTimerHandler	Tk_CreateTimerHandler
+#define Tcl_DeleteTimerHandler	Tk_DeleteTimerHandler
+#define Tcl_DoOneEvent		Tk_DoOneEvent
+#define Tcl_DoWhenIdle		Tk_DoWhenIdle
+#define Tcl_Sleep		Tk_Sleep
+
+/* Additional stuff that has moved to Tcl: */
+
+#define Tcl_AfterCmd		Tk_AfterCmd
+#define Tcl_EventuallyFree	Tk_EventuallyFree
+#define Tcl_FreeProc		Tk_FreeProc
+#define Tcl_Preserve		Tk_Preserve
+#define Tcl_Release		Tk_Release
+
+#endif /* TCL_MAJOR_VERSION > 7 */
+
+typedef int (QSortCompareProc) _ANSI_ARGS_((const void *, const void *));
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Pad --
+ *
+ * 	Specifies vertical and horizontal padding.
+ *
+ *	Padding can be specified on a per side basis.  The fields
+ *	side1 and side2 refer to the opposite sides, either
+ *	horizontally or vertically.
+ *
+ *		side1	side2
+ *              -----   -----
+ *          x | left    right
+ *	    y | top     bottom
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    short int side1, side2;
+} Blt_Pad;
+
+#define padLeft  	padX.side1
+#define padRight  	padX.side2
+#define padTop		padY.side1
+#define padBottom	padY.side2
+#define PADDING(x)	((x).side1 + (x).side2)
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * The following enumerated values are used as bit flags.
+ *	FILL_NONE		Neither coordinate plane is specified 
+ *	FILL_X			Horizontal plane.
+ *	FILL_Y			Vertical plane.
+ *	FILL_BOTH		Both vertical and horizontal planes.
+ *
+ * ----------------------------------------------------------------------
+ */
+#define FILL_NONE	0
+#define FILL_X		1
+#define FILL_Y		2
+#define FILL_BOTH	3
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_Dashes --
+ *
+ * 	List of dash values (maximum 11 based upon PostScript limit).
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    unsigned char values[12];
+    int offset;
+} Blt_Dashes;
+
+#define LineIsDashed(d) ((d).values[0] != 0)
+
+extern void Blt_SetDashes _ANSI_ARGS_((Display *display, GC gc,
+	Blt_Dashes *dashesPtr));
+extern Blt_Dashes *Blt_GetDashes _ANSI_ARGS_((GC gc));
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Point2D --
+ *
+ *	2-D coordinate.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    double x, y;
+} Point2D;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Point3D --
+ *
+ *	3-D coordinate.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    double x, y, z;
+} Point3D;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Segment2D --
+ *
+ *	2-D line segment.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    Point2D p, q;		/* The two end points of the segment. */
+} Segment2D;
+
+/*
+ * -------------------------------------------------------------------
+ *
+ * Dim2D --
+ *
+ *	2-D dimension.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    short int width, height;
+} Dim2D;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Region2D --
+ *
+ *      2-D region.  Used to copy parts of images.
+ *
+ *----------------------------------------------------------------------
+ */
+typedef struct {
+    int left, right, top, bottom;
+} Region2D;
+
+#define RegionWidth(r)		((r)->right - (r)->left + 1)
+#define RegionHeight(r)		((r)->bottom - (r)->top + 1)
+
+typedef struct {
+    double left, right, top, bottom;
+} Extents2D;
+
+typedef struct {
+    double left, right, top, bottom, front, back;
+} Extents3D;
+
+#define PointInRegion(e,x,y) \
+	(((x) <= (e)->right) && ((x) >= (e)->left) && \
+	 ((y) <= (e)->bottom) && ((y) >= (e)->top))
+
+#define PointInRectangle(r,x0,y0) \
+	(((x0) <= (int)((r)->x + (r)->width - 1)) && ((x0) >= (int)(r)->x) && \
+	 ((y0) <= (int)((r)->y + (r)->height - 1)) && ((y0) >= (int)(r)->y))
+
+
+/* -------------------------------------------------------------------
+ *
+ * ColorPair --
+ *
+ *	Holds a pair of foreground, background colors.
+ *
+ * -------------------------------------------------------------------
+ */
+typedef struct {
+    XColor *fgColor, *bgColor;
+} ColorPair;
+
+#define COLOR_NONE		(XColor *)0
+#define COLOR_DEFAULT		(XColor *)1
+#define COLOR_ALLOW_DEFAULTS	1
+
+extern int Blt_GetColorPair _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	char *fgColor, char *bgColor, ColorPair *pairPtr, int colorFlag));
+extern void Blt_FreeColorPair _ANSI_ARGS_((ColorPair *pairPtr));
+
+#define STATE_NORMAL	0
+#define STATE_ACTIVE	(1<<0)
+#define STATE_DISABLED	(1<<1)
+#define STATE_EMPHASIS	(1<<2)
+
+
+#define ARROW_LEFT		(0)
+#define ARROW_UP		(1)
+#define ARROW_RIGHT		(2)
+#define ARROW_DOWN		(3)
+#define ARROW_OFFSET		4
+#define STD_ARROW_HEIGHT	3
+#define STD_ARROW_WIDTH		((2 * (ARROW_OFFSET - 1)) + 1)
+
+#include "bltText.h"
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * 	X11/Xosdefs.h requires XNOSTDHDRS be set for some systems.
+ *	This is a guess.  If I can't find STDC headers or unistd.h,
+ *	assume that this is non-POSIX and non-STDC environment.
+ *	(needed for Encore Umax 3.4 ?)
+ *
+ * ----------------------------------------------------------------------
+ */
+#if !defined(STDC_HEADERS) && !defined(HAVE_UNISTD_H)
+#define XNOSTDHDRS 	1
+#endif
+
+extern char *Blt_Itoa _ANSI_ARGS_((int value));
+extern char *Blt_Utoa _ANSI_ARGS_((unsigned int value));
+extern char *Blt_Dtoa _ANSI_ARGS_((Tcl_Interp *interp, double value));
+extern Tcl_Command Blt_InitCmd _ANSI_ARGS_((Tcl_Interp *interp,
+	char *namespace, Blt_CmdSpec *specPtr));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+extern Tcl_Command Blt_InitObjCmd _ANSI_ARGS_((Tcl_Interp *interp,
+	char *namespace, Blt_ObjCmdSpec *specPtr));
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
+extern char *Tcl_GetString _ANSI_ARGS_((Tcl_Obj *objPtr));
+extern int Tcl_EvalObjv _ANSI_ARGS_((Tcl_Interp *interp, int objc, 
+	Tcl_Obj **objv, int flags));
+extern int Tcl_WriteObj _ANSI_ARGS_((Tcl_Channel channel, Tcl_Obj *objPtr));
+extern char *Tcl_SetVar2Ex _ANSI_ARGS_((Tcl_Interp *interp, char *part1, 
+	char *part2, Tcl_Obj *objPtr, int flags));
+extern Tcl_Obj *Tcl_GetVar2Ex _ANSI_ARGS_((Tcl_Interp *interp, char *part1, 
+	char *part2, int flags));
+#endif /* TCL_VERSION_NUMBER < 8.2.0 */
+#endif /* TCL_VERSION_NUMBER >= 8.0.0 */
+
+
+extern int Blt_InitCmds _ANSI_ARGS_((Tcl_Interp *interp, char *namespace,
+	Blt_CmdSpec *specPtr, int nCmds));
+
+extern int Blt_NaturalSpline _ANSI_ARGS_((Point2D *origPts, int nOrigPts, 
+	Point2D *intpPts, int nIntpPts));
+
+extern int Blt_QuadraticSpline _ANSI_ARGS_((Point2D *origPts, int nOrigPts,
+	Point2D *intpPts, int nIntpPts));
+
+extern int Blt_SimplifyLine _ANSI_ARGS_((Point2D *origPts, int low, int high,
+	 double tolerance, int indices[]));
+
+extern int Blt_NaturalParametricSpline _ANSI_ARGS_((Point2D *origPts, 
+	int nOrigPts, Extents2D *extsPtr, int isClosed, Point2D *intpPts, 
+	int nIntpPts));
+
+extern int Blt_CatromParametricSpline _ANSI_ARGS_((Point2D *origPts, 
+	int nOrigPts, Point2D *intpPts, int nIntpPts));
+
+extern int Blt_StringToFlag _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int flags));
+extern char *Blt_FlagToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *string, int offset, Tcl_FreeProc **freeProc));
+
+extern void Blt_InitHexTable _ANSI_ARGS_((char *table));
+
+extern GC Blt_GetPrivateGC _ANSI_ARGS_((Tk_Window tkwin, unsigned long gcMask,
+	XGCValues *valuePtr));
+
+extern GC Blt_GetPrivateGCFromDrawable _ANSI_ARGS_((Display *display,
+	Drawable drawable, unsigned long gcMask, XGCValues *valuePtr));
+
+extern void Blt_FreePrivateGC _ANSI_ARGS_((Display *display, GC gc));
+
+extern Tk_Window Blt_FindChild _ANSI_ARGS_((Tk_Window parent, char *name));
+
+extern Tk_Window Blt_FirstChild _ANSI_ARGS_((Tk_Window parent));
+
+extern Tk_Window Blt_NextChild _ANSI_ARGS_((Tk_Window tkwin));
+
+extern void Blt_RelinkWindow _ANSI_ARGS_((Tk_Window tkwin, Tk_Window newParent,
+	int x, int y));
+
+extern Tk_Window Blt_Toplevel _ANSI_ARGS_((Tk_Window tkwin));
+
+extern int Blt_GetPixels _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	char *string, int check, int *valuePtr));
+extern int Blt_GetPosition _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+	int *indexPtr));
+extern int Blt_GetCount _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+	int check, int *valuePtr));
+
+extern char *Blt_NameOfFill _ANSI_ARGS_((int fill));
+
+extern int Blt_GetXY _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	char *string, int *x, int *y));
+
+extern Point2D Blt_GetProjection _ANSI_ARGS_((int x, int y, Point2D *p, 
+	Point2D *q));
+
+extern void Blt_DrawArrow _ANSI_ARGS_((Display *display, Drawable drawable,
+	GC gc, int x, int y, int arrowHeight, int orientation));
+
+extern Tk_OptionParseProc Blt_StringToEnum;
+extern Tk_OptionPrintProc Blt_EnumToString;
+
+extern int Blt_ConfigModified _ANSI_ARGS_(TCL_VARARGS(Tk_ConfigSpec *, specs));
+
+extern void Blt_DStringAppendElements _ANSI_ARGS_(TCL_VARARGS(Tcl_DString *, args));
+
+extern void Blt_MakeTransparentWindowExist _ANSI_ARGS_((Tk_Window tkwin,
+	Window parent, int isBusy));
+
+extern Window Blt_GetParent _ANSI_ARGS_((Display *display, Window tkwin));
+
+extern void Blt_GetBoundingBox _ANSI_ARGS_((int width, int height,
+	double theta, double *widthPtr, double *heightPtr, Point2D *points));
+
+extern void Blt_InitEpsCanvasItem _ANSI_ARGS_((Tcl_Interp *interp));
+
+extern void Blt_TranslateAnchor _ANSI_ARGS_((int x, int y, int width,
+	int height, Tk_Anchor anchor, int *transXPtr, int *transYPtr));
+
+extern Point2D Blt_TranslatePoint _ANSI_ARGS_((Point2D *pointPtr, int width,
+	int height, Tk_Anchor anchor));
+
+extern int Blt_ConfigureWidgetComponent _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, char *name, char *class, Tk_ConfigSpec *specs,
+	int argc, char **argv, char *widgRec, int flags));
+
+extern void Blt_HSV _ANSI_ARGS_((XColor *colorPtr, double *huePtr,
+	double *valPtr, double *satPtr));
+
+extern void Blt_RGB _ANSI_ARGS_((double hue, double sat, double val,
+	XColor *colorPtr));
+
+extern int Blt_ParseFlag _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window,
+	char *, char *, int));
+extern char *Blt_FlagPrint _ANSI_ARGS_((ClientData, Tk_Window, char *, int,
+	Tcl_FreeProc **));
+
+extern int Blt_MaxRequestSize _ANSI_ARGS_((Display *display, 
+	unsigned int elemSize));
+
+extern Window Blt_GetRealWindowId _ANSI_ARGS_((Tk_Window tkwin));
+extern int Blt_RootX _ANSI_ARGS_((Tk_Window tkwin));
+extern int Blt_RootY _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_RootCoordinates _ANSI_ARGS_((Tk_Window tkwin, int x, int y,
+    int *rootXPtr, int *rootYPtr));
+extern void Blt_MapToplevel _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_UnmapToplevel _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_RaiseToplevel _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_LowerToplevel _ANSI_ARGS_((Tk_Window tkwin));
+extern void Blt_ResizeToplevel _ANSI_ARGS_((Tk_Window tkwin, 
+	int width, int height));
+extern void Blt_MoveToplevel _ANSI_ARGS_((Tk_Window tkwin, int x, int y));
+extern void Blt_MoveResizeToplevel _ANSI_ARGS_((Tk_Window tkwin, 
+	int x, int y, int width, int height));
+extern ClientData Blt_GetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin));
+
+extern void Blt_SetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin,
+	ClientData instanceData));
+
+extern void Blt_DeleteWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin));
+
+extern int Blt_AdjustViewport _ANSI_ARGS_((int offset, int worldSize,
+	int windowSize, int scrollUnits, int scrollMode));
+
+extern int Blt_GetScrollInfo _ANSI_ARGS_((Tcl_Interp *interp, int argc,
+	char **argv, int *offsetPtr, int worldSize, int windowSize,
+	int scrollUnits, int scrollMode));
+
+#if (TK_MAJOR_VERSION >= 8) 
+extern int Blt_GetScrollInfoFromObj _ANSI_ARGS_((Tcl_Interp *interp, int objc,
+	Tcl_Obj *CONST *objv, int *offsetPtr, int worldSize, int windowSize,
+	int scrollUnits, int scrollMode));
+#endif
+
+extern void Blt_UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
+	char *scrollCmd, double firstFract, double lastFract));
+
+extern int Blt_ReparentWindow _ANSI_ARGS_((Display *display, Window window,
+	Window newParent, int x, int y));
+
+#if defined(HAVE_JPEGLIB_H) || defined(HAVE_IJL_H)
+#define HAVE_JPEG 1
+extern int Blt_JPEGToPhoto _ANSI_ARGS_((Tcl_Interp *interp, char *fileName,
+	Tk_PhotoHandle photo));
+#endif /* HAVE_JPEGLIB_H || HAVE_IJL_H */
+
+#define Blt_SetBooleanResult(i, b) \
+	Tcl_SetResult((i), (b) ? "1" : "0", TCL_STATIC)
+
+/*
+ * Define this if you want to be able to tile to the main window "."
+ * This will cause a conflict with Tk if you try to compile and link
+ * statically.
+ */
+#undef TILE_MAINWINDOW
+
+#ifdef WIN32
+#if (TCL_MAJOR_VERSION == 8)  && (TCL_MINOR_VERSION == 0)
+#else
+#define NO_DDE		1
+#endif
+#else 
+#define NO_DDE		1
+#define NO_PRINTER	1
+#endif /* WIN32 */
+
+#if (TCL_MAJOR_VERSION == 7) 
+#define NO_TREE		1
+#define NO_ARRAY	1
+#define NO_TREEVIEW	1
+#endif
+
+/* #define NO_TED */
+
+#ifndef NO_BEEP
+extern Tcl_AppInitProc Blt_BeepInit;
+#endif
+#ifndef NO_BGEXEC
+extern Tcl_AppInitProc Blt_BgexecInit;
+#endif
+#ifndef NO_BITMAP
+extern Tcl_AppInitProc Blt_BitmapInit;
+#endif
+#ifndef NO_BUSY
+extern Tcl_AppInitProc Blt_BusyInit;
+#endif
+#ifndef NO_CONTAINER
+extern Tcl_AppInitProc Blt_ContainerInit;
+#endif
+#ifndef NO_CRC32
+extern Tcl_AppInitProc Blt_Crc32Init;
+#endif
+#ifndef NO_CUTBUFFER
+extern Tcl_AppInitProc Blt_CutbufferInit;
+#endif
+#ifndef NO_DEBUG
+extern Tcl_AppInitProc Blt_DebugInit;
+#endif
+#ifndef NO_DRAGDROP
+extern Tcl_AppInitProc Blt_DragDropInit;
+#endif
+#ifndef NO_DND
+extern Tcl_AppInitProc Blt_DndInit;
+#endif
+#ifndef NO_GRAPH
+extern Tcl_AppInitProc Blt_GraphInit;
+#endif
+#ifndef NO_HIERBOX
+extern Tcl_AppInitProc Blt_HierboxInit;
+#endif
+#ifndef NO_HIERTABLE
+extern Tcl_AppInitProc Blt_HiertableInit;
+#endif
+#ifndef NO_HTEXT
+extern Tcl_AppInitProc Blt_HtextInit;
+#endif
+#ifdef WIN32
+#ifndef NO_PRINTER
+extern Tcl_AppInitProc Blt_PrinterInit;
+#endif
+#endif
+#ifndef NO_TABLE
+extern Tcl_AppInitProc Blt_TableInit;
+#endif
+#ifndef NO_VECTOR
+extern Tcl_AppInitProc Blt_VectorInit;
+#endif
+#ifndef NO_WINOP
+extern Tcl_AppInitProc Blt_WinopInit;
+#endif
+#ifndef NO_WATCH
+extern Tcl_AppInitProc Blt_WatchInit;
+#endif
+#ifndef NO_SPLINE
+extern Tcl_AppInitProc Blt_SplineInit;
+#endif
+#ifndef NO_TABSET
+extern Tcl_AppInitProc Blt_TabsetInit;
+#endif
+#ifndef NO_TABNOTEBOOK
+extern Tcl_AppInitProc Blt_TabnotebookInit;
+#endif
+#ifndef NO_TREE
+extern Tcl_AppInitProc Blt_TreeInit;
+#endif
+#ifndef NO_TREEVIEW
+extern Tcl_AppInitProc Blt_TreeViewInit;
+#endif
+#ifndef NO_TILEFRAME
+extern Tcl_AppInitProc Blt_FrameInit;
+#endif
+#ifndef NO_TILEBUTTON
+extern Tcl_AppInitProc Blt_ButtonInit;
+#endif
+#ifndef NO_TILESCROLLBAR
+extern Tcl_AppInitProc Blt_ScrollbarInit;
+#endif
+
+#if (BLT_MAJOR_VERSION == 3)
+#ifndef NO_MOUNTAIN
+extern Tcl_AppInitProc Blt_MountainInit;
+#endif
+#endif
+#ifndef NO_TED
+extern Tcl_AppInitProc Blt_TedInit;
+#endif
+
+#ifndef NO_DDE
+extern Tcl_AppInitProc Blt_DdeInit;
+#endif
+
+typedef void *(Blt_MallocProc) _ANSI_ARGS_((size_t size));
+typedef void *(Blt_CallocProc) _ANSI_ARGS_((int nElem, size_t size));
+typedef void *(Blt_ReallocProc) _ANSI_ARGS_((void *ptr, size_t size));
+typedef void *(Blt_FreeProc) _ANSI_ARGS_((void *ptr));
+
+EXTERN Blt_MallocProc *Blt_MallocProcPtr;
+EXTERN Blt_FreeProc *Blt_FreeProcPtr;
+EXTERN Blt_ReallocProc *Blt_ReallocProcPtr;
+
+#define Blt_Malloc(size)	(*Blt_MallocProcPtr)(size)
+#define Blt_Free		(*Blt_FreeProcPtr)
+#define Blt_Realloc(ptr, size)  (*Blt_ReallocProcPtr)(ptr, size)
+
+EXTERN char *Blt_Strdup _ANSI_ARGS_((CONST char *ptr));
+EXTERN void *Blt_Calloc _ANSI_ARGS_((unsigned int nElem, size_t size));
+
+#ifdef WIN32
+#include "bltWin.h"
+#endif
+
+#ifndef WIN32
+#define PurifyPrintf  printf
+#endif /* WIN32 */
+#endif /*_BLT_INT_H*/
Index: trunk/kitgen/8.x/blt/generic/bltInterp.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltInterp.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltInterp.h	(revision 175)
@@ -0,0 +1,376 @@
+/*
+ * bltInterp.h --
+ *
+ *	Excerpts from tclInt.h.  Used to examine interpreter internals.
+ *	Needed by the former (now obsoleted) TclParse* functions.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 1993-1997 Lucent Technologies.
+ * Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ *
+ */
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to command parsing. These are used in
+ * tclParse.c and its clients.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The following data structure is used by various parsing procedures
+ * to hold information about where to store the results of parsing
+ * (e.g. the substituted contents of a quoted argument, or the result
+ * of a nested command).  At any given time, the space available
+ * for output is fixed, but a procedure may be called to expand the
+ * space available if the current space runs out.
+ */
+typedef struct ParseValueStruct ParseValue;
+
+struct ParseValueStruct {
+    char *buffer;		/* Address of first character in
+				 * output buffer. */
+    char *next;			/* Place to store next character in
+				 * output buffer. */
+    char *end;			/* Address of the last usable character
+				 * in the buffer. */
+    void (*expandProc) _ANSI_ARGS_((ParseValue *pvPtr, int needed));
+				/* Procedure to call when space runs out;
+				 * it will make more space. */
+    ClientData clientData;	/* Arbitrary information for use of
+				 * expandProc. */
+};
+
+
+/*
+ * The definitions for the LiteralTable and LiteralEntry structures. Each
+ * interpreter contains a LiteralTable. It is used to reduce the storage
+ * needed for all the Tcl objects that hold the literals of scripts compiled
+ * by the interpreter. A literal's object is shared by all the ByteCodes
+ * that refer to the literal. Each distinct literal has one LiteralEntry
+ * entry in the LiteralTable. A literal table is a specialized hash table
+ * that is indexed by the literal's string representation, which may contain
+ * null characters.
+ *
+ * Note that we reduce the space needed for literals by sharing literal
+ * objects both within a ByteCode (each ByteCode contains a local
+ * LiteralTable) and across all an interpreter's ByteCodes (with the
+ * interpreter's global LiteralTable).
+ */
+
+typedef struct LiteralEntryStruct LiteralEntry;
+
+struct LiteralEntryStruct {
+    LiteralEntry *nextPtr;	/* Points to next entry in this
+				 * hash bucket or NULL if end of
+				 * chain. */
+    Tcl_Obj *objPtr;		/* Points to Tcl object that
+				 * holds the literal's bytes and
+				 * length. */
+    int refCount;		/* If in an interpreter's global
+				 * literal table, the number of
+				 * ByteCode structures that share
+				 * the literal object; the literal
+				 * entry can be freed when refCount
+				 * drops to 0. If in a local literal
+				 * table, -1. */
+};
+
+typedef struct {
+    LiteralEntry **buckets;	/* Pointer to bucket array. Each
+					 * element points to first entry in
+					 * bucket's hash chain, or NULL. */
+    LiteralEntry *staticBuckets[TCL_SMALL_HASH_TABLE];
+    /* Bucket array used for small
+					 * tables to avoid mallocs and
+					 * frees. */
+    int numBuckets;		/* Total number of buckets allocated
+					 * at **buckets. */
+    int numEntries;		/* Total number of entries present
+					 * in table. */
+    int rebuildSize;		/* Enlarge table when numEntries
+					 * gets to be this large. */
+    int mask;			/* Mask value used in hashing
+					 * function. */
+} LiteralTable;
+
+/*
+ * The following structure defines for each Tcl interpreter various
+ * statistics-related information about the bytecode compiler and
+ * interpreter's operation in that interpreter.
+ */
+
+#ifdef TCL_COMPILE_STATS
+typedef struct {
+    long numExecutions;		/* Number of ByteCodes executed. */
+    long numCompilations;	/* Number of ByteCodes created. */
+    long numByteCodesFreed;	/* Number of ByteCodes destroyed. */
+    long instructionCount[256];	/* Number of times each instruction was
+				   * executed. */
+
+    double totalSrcBytes;	/* Total source bytes ever compiled. */
+    double totalByteCodeBytes;	/* Total bytes for all ByteCodes. */
+    double currentSrcBytes;	/* Src bytes for all current ByteCodes. */
+    double currentByteCodeBytes;/* Code bytes in all current ByteCodes. */
+
+    long srcCount[32];		/* Source size distribution: # of srcs of
+				   * size [2**(n-1)..2**n), n in [0..32). */
+    long byteCodeCount[32];	/* ByteCode size distribution. */
+    long lifetimeCount[32];	/* ByteCode lifetime distribution (ms). */
+
+    double currentInstBytes;	/* Instruction bytes-current ByteCodes. */
+    double currentLitBytes;	/* Current literal bytes. */
+    double currentExceptBytes;	/* Current exception table bytes. */
+    double currentAuxBytes;	/* Current auxiliary information bytes. */
+    double currentCmdMapBytes;	/* Current src<->code map bytes. */
+
+    long numLiteralsCreated;	/* Total literal objects ever compiled. */
+    double totalLitStringBytes;	/* Total string bytes in all literals. */
+    double currentLitStringBytes;	/* String bytes in current literals. */
+    long literalCount[32];	/* Distribution of literal string sizes. */
+} ByteCodeStats;
+
+#endif /* TCL_COMPILE_STATS */
+
+
+/*
+ *----------------------------------------------------------------
+ * Data structures and procedures related to TclHandles, which
+ * are a very lightweight method of preserving enough information
+ * to determine if an arbitrary malloc'd block has been deleted.
+ *----------------------------------------------------------------
+ */
+
+typedef VOID **TclHandle;
+
+
+/*
+ *   The following fills in dummy types for structure refered to 
+ *   internally by the Tcl interpreter.  Since we don't need the actual
+ *   size of the structures (they are only pointer references), we'll 
+ *   simply provide empty opaque types.
+ *
+ */
+typedef struct CallFrameStruct CallFrame;
+typedef struct NamespaceStruct Namespace;
+typedef struct ActiveVarTraceStruct ActiveVarTrace;
+typedef struct ProcStruct Proc;
+typedef struct TraceStruct Trace;
+
+typedef struct TclRegexpStruct TclRegexp;
+typedef struct ExecEnvStruct ExecEnv;
+
+
+/*
+ *----------------------------------------------------------------
+ * This structure defines an interpreter, which is a collection of
+ * commands plus other state information related to interpreting
+ * commands, such as variable storage. Primary responsibility for
+ * this data structure is in tclBasic.c, but almost every Tcl
+ * source file uses something in here.
+ *----------------------------------------------------------------
+ */
+
+typedef struct {
+
+    /*
+     * Note:  the first three fields must match exactly the fields in
+     * a Tcl_Interp struct (see tcl.h).  If you change one, be sure to
+     * change the other.
+     *
+     * The interpreter's result is held in both the string and the
+     * objResultPtr fields. These fields hold, respectively, the result's
+     * string or object value. The interpreter's result is always in the
+     * result field if that is non-empty, otherwise it is in objResultPtr.
+     * The two fields are kept consistent unless some C code sets
+     * interp->result directly. Programs should not access result and
+     * objResultPtr directly; instead, they should always get and set the
+     * result using procedures such as Tcl_SetObjResult, Tcl_GetObjResult,
+     * and Tcl_GetStringResult. See the SetResult man page for details.
+     */
+
+    char *result;		/* If the last command returned a string
+				 * result, this points to it. Should not be
+				 * accessed directly; see comment above. */
+    Tcl_FreeProc *freeProc;	/* Zero means a string result is statically
+                                 * allocated. TCL_DYNAMIC means string
+                                 * result was allocated with ckalloc and
+                                 * should be freed with ckfree. Other values
+                                 * give address of procedure to invoke to
+                                 * free the string result. Tcl_Eval must
+                                 * free it before executing next command. */
+    int errorLine;		/* When TCL_ERROR is returned, this gives
+				 * the line number in the command where the
+				 * error occurred (1 means first line). */
+    Tcl_Obj *objResultPtr;	/* If the last command returned an object
+				 * result, this points to it. Should not be
+				 * accessed directly; see comment above. */
+
+    TclHandle handle;		/* Handle used to keep track of when this
+				 * interp is deleted. */
+
+    Namespace *globalNsPtr;	/* The interpreter's global namespace. */
+    Tcl_HashTable *hiddenCmdTablePtr;
+    /* Hash table used by tclBasic.c to keep
+				 * track of hidden commands on a per-interp
+				 * basis. */
+    ClientData interpInfo;	/* Information used by tclInterp.c to keep
+				 * track of master/slave interps on
+				 * a per-interp basis. */
+    Tcl_HashTable mathFuncTable;/* Contains all the math functions currently
+				 * defined for the interpreter.  Indexed by
+				 * strings (function names); values have
+				 * type (MathFunc *). */
+
+
+
+    /*
+     * Information related to procedures and variables. See tclProc.c
+     * and tclvar.c for usage.
+     */
+
+    int numLevels;		/* Keeps track of how many nested calls to
+				 * Tcl_Eval are in progress for this
+				 * interpreter.  It's used to delay deletion
+				 * of the table until all Tcl_Eval
+				 * invocations are completed. */
+    int maxNestingDepth;	/* If numLevels exceeds this value then Tcl
+				 * assumes that infinite recursion has
+				 * occurred and it generates an error. */
+    CallFrame *framePtr;	/* Points to top-most in stack of all nested
+				 * procedure invocations.  NULL means there
+				 * are no active procedures. */
+    CallFrame *varFramePtr;	/* Points to the call frame whose variables
+				 * are currently in use (same as framePtr
+				 * unless an "uplevel" command is
+				 * executing). NULL means no procedure is
+				 * active or "uplevel 0" is executing. */
+    ActiveVarTrace *activeTracePtr;
+    /* First in list of active traces for
+				 * interp, or NULL if no active traces. */
+    int returnCode;		/* Completion code to return if current
+				 * procedure exits with TCL_RETURN code. */
+    char *errorInfo;		/* Value to store in errorInfo if returnCode
+				 * is TCL_ERROR.  Malloc'ed, may be NULL */
+    char *errorCode;		/* Value to store in errorCode if returnCode
+				 * is TCL_ERROR.  Malloc'ed, may be NULL */
+
+    /*
+     * Information used by Tcl_AppendResult to keep track of partial
+     * results.  See Tcl_AppendResult code for details.
+     */
+
+    char *appendResult;		/* Storage space for results generated
+				 * by Tcl_AppendResult.  Malloc-ed.  NULL
+				 * means not yet allocated. */
+    int appendAvl;		/* Total amount of space available at
+				 * partialResult. */
+    int appendUsed;		/* Number of non-null bytes currently
+				 * stored at partialResult. */
+
+    /*
+     * A cache of compiled regular expressions.  See Tcl_RegExpCompile
+     * in tclUtil.c for details.  THIS CACHE IS OBSOLETE and is only
+     * retained for backward compatibility with Tcl_RegExpCompile.
+     * New code should use the object interface so the Tcl_Obj caches
+     * the compiled expression.
+     */
+
+#define NUM_REGEXPS 5
+    char *patterns[NUM_REGEXPS];/* Strings corresponding to compiled
+				 * regular expression patterns.  NULL
+				 * means that this slot isn't used.
+				 * Malloc-ed. */
+    int patLengths[NUM_REGEXPS];/* Number of non-null characters in
+				 * corresponding entry in patterns.
+				 * -1 means entry isn't used. */
+    TclRegexp *regexps[NUM_REGEXPS];
+    /* Compiled forms of above strings.  Also
+				 * malloc-ed, or NULL if not in use yet. */
+
+    /*
+     * Information about packages.  Used only in tclPkg.c.
+     */
+
+    Tcl_HashTable packageTable;	/* Describes all of the packages loaded
+				 * in or available to this interpreter.
+				 * Keys are package names, values are
+				 * (Package *) pointers. */
+    char *packageUnknown;	/* Command to invoke during "package
+				 * require" commands for packages that
+				 * aren't described in packageTable.
+				 * Malloc'ed, may be NULL. */
+
+    /*
+     * Miscellaneous information:
+     */
+
+    int cmdCount;		/* Total number of times a command procedure
+				 * has been called for this interpreter. */
+    int evalFlags;		/* Flags to control next call to Tcl_Eval.
+				 * Normally zero, but may be set before
+				 * calling Tcl_Eval.  See below for valid
+				 * values. */
+    int termOffset;		/* Offset of character just after last one
+				 * compiled or executed by Tcl_EvalObj. */
+    LiteralTable literalTable;	/* Contains LiteralEntry's describing all
+				 * Tcl objects holding literals of scripts
+				 * compiled by the interpreter. Indexed by
+				 * the string representations of literals.
+				 * Used to avoid creating duplicate
+				 * objects. */
+    int compileEpoch;		/* Holds the current "compilation epoch"
+				 * for this interpreter. This is
+				 * incremented to invalidate existing
+				 * ByteCodes when, e.g., a command with a
+				 * compile procedure is redefined. */
+    Proc *compiledProcPtr;	/* If a procedure is being compiled, a
+				 * pointer to its Proc structure; otherwise,
+				 * this is NULL. Set by ObjInterpProc in
+				 * tclProc.c and used by tclCompile.c to
+				 * process local variables appropriately. */
+    char *scriptFile;		/* NULL means there is no nested source
+				 * command active;  otherwise this points to
+				 * the name of the file being sourced (it's
+				 * not malloc-ed:  it points to an argument
+				 * to Tcl_EvalFile. */
+    int flags;			/* Various flag bits.  See below. */
+    long randSeed;		/* Seed used for rand() function. */
+    Trace *tracePtr;		/* List of traces for this interpreter. */
+    Tcl_HashTable *assocData;	/* Hash table for associating data with
+                                 * this interpreter. Cleaned up when
+                                 * this interpreter is deleted. */
+    ExecEnv *execEnvPtr;	/* Execution environment for Tcl bytecode
+                                 * execution. Contains a pointer to the
+				 * Tcl evaluation stack. */
+    Tcl_Obj *emptyObjPtr;	/* Points to an object holding an empty
+				 * string. Returned by Tcl_ObjSetVar2 when
+				 * variable traces change a variable in a
+				 * gross way. */
+    char resultSpace[TCL_RESULT_SIZE + 1];
+    /* Static space holding small results. */
+    Tcl_ThreadId threadId;	/* ID of thread that owns the interpreter */
+
+    /*
+     * Statistical information about the bytecode compiler and interpreter's
+     * operation.
+     */
+
+#ifdef TCL_COMPILE_STATS
+    ByteCodeStats stats;	/* Holds compilation and execution
+				 * statistics for this interpreter. */
+#endif /* TCL_COMPILE_STATS */
+} Interp;
+
+/*
+ * EvalFlag bits for Interp structures:
+ *
+ * TCL_BRACKET_TERM	1 means that the current script is terminated by
+ *			a close bracket rather than the end of the string.
+ * TCL_ALLOW_EXCEPTIONS	1 means it's OK for the script to terminate with
+ *			a code other than TCL_OK or TCL_ERROR;  0 means
+ *			codes other than these should be turned into errors.
+ */
+
+#define TCL_BRACKET_TERM	  1
+#define TCL_ALLOW_EXCEPTIONS	  4
Index: trunk/kitgen/8.x/blt/generic/bltList.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltList.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltList.c	(revision 175)
@@ -0,0 +1,590 @@
+/*
+ * bltList.c --
+ *
+ *	The module implements generic linked lists.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltList.h"
+
+static struct Blt_ListNodeStruct *
+FindString(listPtr, key)
+    struct Blt_ListStruct *listPtr; /* List to search */
+    CONST char *key;		/* Key to match */
+{
+    register struct Blt_ListNodeStruct *nodePtr;
+    char c;
+
+    c = key[0];
+    for (nodePtr = listPtr->headPtr; nodePtr != NULL; 
+	 nodePtr = nodePtr->nextPtr) {
+	if ((c == nodePtr->key.string[0]) &&
+	    (strcmp(key, nodePtr->key.string) == 0)) {
+	    return nodePtr;
+	}
+    }
+    return NULL;
+}
+
+static Blt_ListNode
+FindOneWord(listPtr, key)
+    struct Blt_ListStruct *listPtr; /* List to search */
+    CONST char *key;		/* Key to match */
+{
+    register struct Blt_ListNodeStruct *nodePtr;
+
+    for (nodePtr = listPtr->headPtr; nodePtr != NULL; 
+	 nodePtr = nodePtr->nextPtr) {
+	if (key == nodePtr->key.oneWordValue) {
+	    return nodePtr;
+	}
+    }
+    return NULL;
+}
+
+static Blt_ListNode
+FindArray(listPtr, key)
+    struct Blt_ListStruct *listPtr; /* List to search */
+    CONST char *key;		/* Key to match */
+{
+    register struct Blt_ListNodeStruct *nodePtr;
+    int nBytes;
+
+    nBytes = sizeof(int) * listPtr->type;
+    for (nodePtr = listPtr->headPtr; nodePtr != NULL; 
+	 nodePtr = nodePtr->nextPtr) {
+	if (memcmp(key, nodePtr->key.words, nBytes) == 0) {
+	    return nodePtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeNode --
+ *
+ *	Free the memory allocated for the node.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeNode(nodePtr)
+    struct Blt_ListNodeStruct *nodePtr;
+{
+    Blt_Free(nodePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListCreate --
+ *
+ *	Creates a new linked list structure and initializes its pointers
+ *
+ * Results:
+ *	Returns a pointer to the newly created list structure.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_List 
+Blt_ListCreate(type)
+    int type;
+{
+    struct Blt_ListStruct *listPtr;
+
+    listPtr = Blt_Malloc(sizeof(struct Blt_ListStruct));
+    if (listPtr != NULL) {
+	Blt_ListInit(listPtr, type);
+    }
+    return listPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListCreateNode --
+ *
+ *	Creates a list node holder.  This routine does not insert
+ *	the node into the list, nor does it no attempt to maintain
+ *	consistency of the keys.  For example, more than one node
+ *	may use the same key.
+ *
+ * Results:
+ *	The return value is the pointer to the newly created node.
+ *
+ * Side Effects:
+ *	The key is not copied, only the Uid is kept.  It is assumed
+ *	this key will not change in the life of the node.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListCreateNode(listPtr, key)
+    struct Blt_ListStruct *listPtr;
+    CONST char *key;		/* Unique key to reference object */
+{
+    register struct Blt_ListNodeStruct *nodePtr;
+    int keySize;
+
+    if (listPtr->type == BLT_STRING_KEYS) {
+	keySize = strlen(key) + 1;
+    } else if (listPtr->type == BLT_ONE_WORD_KEYS) {
+	keySize = sizeof(int);
+    } else {
+	keySize = sizeof(int) * listPtr->type;
+    }
+    nodePtr = Blt_Calloc(1, sizeof(struct Blt_ListNodeStruct) + keySize - 4);
+    assert(nodePtr);
+    nodePtr->clientData = NULL;
+    nodePtr->nextPtr = nodePtr->prevPtr = NULL;
+    nodePtr->listPtr = listPtr;
+    switch (listPtr->type) {
+    case BLT_STRING_KEYS:
+	strcpy(nodePtr->key.string, key);
+	break;
+    case BLT_ONE_WORD_KEYS:
+	nodePtr->key.oneWordValue = key;
+	break;
+    default:
+	memcpy(nodePtr->key.words, key, keySize);
+	break;
+    }
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListReset --
+ *
+ *	Removes all the entries from a list, removing pointers to the
+ *	objects and keys (not the objects or keys themselves).  The
+ *	node counter is reset to zero.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListReset(listPtr)
+    struct Blt_ListStruct *listPtr; /* List to clear */
+{
+    if (listPtr != NULL) {
+	register struct Blt_ListNodeStruct *oldPtr;
+	register struct Blt_ListNodeStruct *nodePtr = listPtr->headPtr;
+
+	while (nodePtr != NULL) {
+	    oldPtr = nodePtr;
+	    nodePtr = nodePtr->nextPtr;
+	    FreeNode(oldPtr);
+	}
+	Blt_ListInit(listPtr, listPtr->type);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDestroy
+ *
+ *     Frees all list structures
+ *
+ * Results:
+ *	Returns a pointer to the newly created list structure.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDestroy(listPtr)
+    struct Blt_ListStruct *listPtr;
+{
+    if (listPtr != NULL) {
+	Blt_ListReset(listPtr);
+	Blt_Free(listPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListInit --
+ *
+ *	Initializes a linked list.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListInit(listPtr, type)
+    struct Blt_ListStruct *listPtr;
+    int type;
+{
+    listPtr->nNodes = 0;
+    listPtr->headPtr = listPtr->tailPtr = NULL;
+    listPtr->type = type;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListLinkAfter --
+ *
+ *	Inserts an node following a given node.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListLinkAfter(listPtr, nodePtr, afterPtr)
+    struct Blt_ListStruct *listPtr;
+    struct Blt_ListNodeStruct *nodePtr;
+    struct Blt_ListNodeStruct *afterPtr;
+{
+    if (listPtr->headPtr == NULL) {
+	listPtr->tailPtr = listPtr->headPtr = nodePtr;
+    } else {
+	if (afterPtr == NULL) {
+	    /* Prepend to the front of the list */
+	    nodePtr->nextPtr = listPtr->headPtr;
+	    nodePtr->prevPtr = NULL;
+	    listPtr->headPtr->prevPtr = nodePtr;
+	    listPtr->headPtr = nodePtr;
+	} else {
+	    nodePtr->nextPtr = afterPtr->nextPtr;
+	    nodePtr->prevPtr = afterPtr;
+	    if (afterPtr == listPtr->tailPtr) {
+		listPtr->tailPtr = nodePtr;
+	    } else {
+		afterPtr->nextPtr->prevPtr = nodePtr;
+	    }
+	    afterPtr->nextPtr = nodePtr;
+	}
+    }
+    nodePtr->listPtr = listPtr;
+    listPtr->nNodes++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListLinkBefore --
+ *
+ *	Inserts an node preceding a given node.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListLinkBefore(listPtr, nodePtr, beforePtr)
+    struct Blt_ListStruct *listPtr; /* List to contain new node */
+    struct Blt_ListNodeStruct *nodePtr;	/* New node to be inserted */
+    struct Blt_ListNodeStruct *beforePtr;	/* Node to link before */
+{
+    if (listPtr->headPtr == NULL) {
+	listPtr->tailPtr = listPtr->headPtr = nodePtr;
+    } else {
+	if (beforePtr == NULL) {
+	    /* Append onto the end of the list */
+	    nodePtr->nextPtr = NULL;
+	    nodePtr->prevPtr = listPtr->tailPtr;
+	    listPtr->tailPtr->nextPtr = nodePtr;
+	    listPtr->tailPtr = nodePtr;
+	} else {
+	    nodePtr->prevPtr = beforePtr->prevPtr;
+	    nodePtr->nextPtr = beforePtr;
+	    if (beforePtr == listPtr->headPtr) {
+		listPtr->headPtr = nodePtr;
+	    } else {
+		beforePtr->prevPtr->nextPtr = nodePtr;
+	    }
+	    beforePtr->prevPtr = nodePtr;
+	}
+    }
+    nodePtr->listPtr = listPtr;
+    listPtr->nNodes++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListUnlinkNode --
+ *
+ *	Unlinks an node from the given list. The node itself is
+ *	not deallocated, but only removed from the list.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListUnlinkNode(nodePtr)
+    struct Blt_ListNodeStruct *nodePtr;
+{
+    struct Blt_ListStruct *listPtr;
+
+    listPtr = nodePtr->listPtr;
+    if (listPtr != NULL) {
+	if (listPtr->headPtr == nodePtr) {
+	    listPtr->headPtr = nodePtr->nextPtr;
+	}
+	if (listPtr->tailPtr == nodePtr) {
+	    listPtr->tailPtr = nodePtr->prevPtr;
+	}
+	if (nodePtr->nextPtr != NULL) {
+	    nodePtr->nextPtr->prevPtr = nodePtr->prevPtr;
+	}
+	if (nodePtr->prevPtr != NULL) {
+	    nodePtr->prevPtr->nextPtr = nodePtr->nextPtr;
+	}
+	nodePtr->listPtr = NULL;
+	listPtr->nNodes--;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListGetNode --
+ *
+ *	Find the first node matching the key given.
+ *
+ * Results:
+ *	Returns the pointer to the node.  If no node matching
+ *	the key given is found, then NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListGetNode(listPtr, key)
+    struct Blt_ListStruct *listPtr; /* List to search */
+    CONST char *key;		/* Key to match */
+{
+    if (listPtr != NULL) {
+	switch (listPtr->type) {
+	case BLT_STRING_KEYS:
+	    return FindString(listPtr, key);
+	case BLT_ONE_WORD_KEYS:
+	    return FindOneWord(listPtr, key);
+	default:
+	    return FindArray(listPtr, key);
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDeleteNode --
+ *
+ *	Unlinks and deletes the given node.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDeleteNode(nodePtr)
+    struct Blt_ListNodeStruct *nodePtr;
+{
+    Blt_ListUnlinkNode(nodePtr);
+    FreeNode(nodePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListDeleteNodeByKey --
+ *
+ *	Find the node and free the memory allocated for the node.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListDeleteNodeByKey(listPtr, key)
+    struct Blt_ListStruct *listPtr;
+    CONST char *key;
+{
+    struct Blt_ListNodeStruct *nodePtr;
+
+    nodePtr = Blt_ListGetNode(listPtr, key);
+    if (nodePtr != NULL) {
+	Blt_ListDeleteNode(nodePtr);
+    }
+}
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListAppend(listPtr, key, clientData)
+    struct Blt_ListStruct *listPtr;
+    CONST char *key;
+    ClientData clientData;
+{
+    struct Blt_ListNodeStruct *nodePtr;
+
+    nodePtr = Blt_ListCreateNode(listPtr, key);
+    Blt_ListSetValue(nodePtr, clientData);
+    Blt_ListAppendNode(listPtr, nodePtr);
+    return nodePtr;
+}
+
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListPrepend(listPtr, key, clientData)
+    struct Blt_ListStruct *listPtr;
+    CONST char *key;
+    ClientData clientData;
+{
+    struct Blt_ListNodeStruct *nodePtr;
+
+    nodePtr = Blt_ListCreateNode(listPtr, key);
+    Blt_ListSetValue(nodePtr, clientData);
+    Blt_ListPrependNode(listPtr, nodePtr);
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListGetNthNode --
+ *
+ *	Find the node based upon a given position in list.
+ *
+ * Results:
+ *	Returns the pointer to the node, if that numbered element
+ *	exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Blt_ListNode
+Blt_ListGetNthNode(listPtr, position, direction)
+    struct Blt_ListStruct *listPtr; /* List to traverse */
+    int position;		/* Index of node to select from front
+				 * or back of the list. */
+    int direction;
+{
+    register struct Blt_ListNodeStruct *nodePtr;
+
+    if (listPtr != NULL) {
+	if (direction > 0) {
+	    for (nodePtr = listPtr->headPtr; nodePtr != NULL; 
+		 nodePtr = nodePtr->nextPtr) {
+		if (position == 0) {
+		    return nodePtr;
+		}
+		position--;
+	    }
+	} else {
+	    for (nodePtr = listPtr->tailPtr; nodePtr != NULL; 
+		 nodePtr = nodePtr->prevPtr) {
+		if (position == 0) {
+		    return nodePtr;
+		}
+		position--;
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ListSort --
+ *
+ *	Find the node based upon a given position in list.
+ *
+ * Results:
+ *	Returns the pointer to the node, if that numbered element
+ *	exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_ListSort(listPtr, proc)
+    struct Blt_ListStruct *listPtr; /* List to traverse */
+    Blt_ListCompareProc *proc;
+{
+    struct Blt_ListNodeStruct **nodeArr;
+    register struct Blt_ListNodeStruct *nodePtr;
+    register int i;
+
+    if (listPtr->nNodes < 2) {
+	return;
+    }
+    nodeArr = Blt_Malloc(sizeof(Blt_List) * (listPtr->nNodes + 1));
+    if (nodeArr == NULL) {
+	return;			/* Out of memory. */
+    }
+    i = 0;
+    for (nodePtr = listPtr->headPtr; nodePtr != NULL; 
+	 nodePtr = nodePtr->nextPtr) {
+	nodeArr[i++] = nodePtr;
+    }
+    qsort((char *)nodeArr, listPtr->nNodes, 
+		sizeof(struct Blt_ListNodeStruct *), (QSortCompareProc *)proc);
+
+    /* Rethread the list. */
+    nodePtr = nodeArr[0];
+    listPtr->headPtr = nodePtr;
+    nodePtr->prevPtr = NULL;
+    for (i = 1; i < listPtr->nNodes; i++) {
+	nodePtr->nextPtr = nodeArr[i];
+	nodePtr->nextPtr->prevPtr = nodePtr;
+	nodePtr = nodePtr->nextPtr;
+    }
+    listPtr->tailPtr = nodePtr;
+    nodePtr->nextPtr = NULL;
+    Blt_Free(nodeArr);
+}
Index: trunk/kitgen/8.x/blt/generic/bltList.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltList.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltList.h	(revision 175)
@@ -0,0 +1,108 @@
+/*
+ * bltList.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#ifndef _BLT_LIST_H
+#define _BLT_LIST_H
+
+typedef struct Blt_ListStruct *Blt_List;
+typedef struct Blt_ListNodeStruct *Blt_ListNode;
+
+/*
+ * A Blt_ListNode is the container structure for the Blt_List.
+ */
+struct Blt_ListNodeStruct {
+    struct Blt_ListNodeStruct *prevPtr; /* Link to the previous node */
+    struct Blt_ListNodeStruct *nextPtr; /* Link to the next node */
+    ClientData clientData;	/* Pointer to the data object */
+    struct Blt_ListStruct *listPtr; /* List to eventually insert node */
+    union {			/* Key has one of these forms: */
+	CONST char *oneWordValue; /* One-word value for key. */
+	int *words[1];		/* Multiple integer words for key.
+				 * The actual size will be as large
+				 * as necessary for this table's
+				 * keys. */
+	char string[4];		/* String for key.  The actual size
+				 * will be as large as needed to hold
+				 * the key. */
+    } key;			/* MUST BE LAST FIELD IN RECORD!! */
+};
+
+typedef int (Blt_ListCompareProc) _ANSI_ARGS_((Blt_ListNode *node1Ptr, 
+	Blt_ListNode *node2Ptr));
+
+/*
+ * A Blt_List is a doubly chained list structure.
+ */
+struct Blt_ListStruct {
+    struct Blt_ListNodeStruct *headPtr;	/* Pointer to first element in list */
+    struct Blt_ListNodeStruct *tailPtr;	/* Pointer to last element in list */
+    int nNodes;			/* Number of node currently in the list. */
+    int type;			/* Type of keys in list. */
+};
+
+EXTERN void Blt_ListInit _ANSI_ARGS_((Blt_List list, int type));
+EXTERN void Blt_ListReset _ANSI_ARGS_((Blt_List list));
+EXTERN Blt_List Blt_ListCreate _ANSI_ARGS_((int type));
+EXTERN void Blt_ListDestroy _ANSI_ARGS_((Blt_List list));
+EXTERN Blt_ListNode Blt_ListCreateNode _ANSI_ARGS_((Blt_List list, 
+	CONST char *key));
+EXTERN void Blt_ListDeleteNode _ANSI_ARGS_((Blt_ListNode node));
+
+EXTERN Blt_ListNode Blt_ListAppend _ANSI_ARGS_((Blt_List list, CONST char *key,
+	ClientData clientData));
+EXTERN Blt_ListNode Blt_ListPrepend _ANSI_ARGS_((Blt_List list, CONST char *key,
+	ClientData clientData));
+EXTERN void Blt_ListLinkAfter _ANSI_ARGS_((Blt_List list, Blt_ListNode node, 
+	Blt_ListNode afterNode));
+EXTERN void Blt_ListLinkBefore _ANSI_ARGS_((Blt_List list, Blt_ListNode node, 
+	Blt_ListNode beforeNode));
+EXTERN void Blt_ListUnlinkNode _ANSI_ARGS_((Blt_ListNode node));
+EXTERN Blt_ListNode Blt_ListGetNode _ANSI_ARGS_((Blt_List list, 
+	CONST char *key));
+EXTERN void Blt_ListDeleteNodeByKey _ANSI_ARGS_((Blt_List list, 
+	CONST char *key));
+EXTERN Blt_ListNode Blt_ListGetNthNode _ANSI_ARGS_((Blt_List list,
+	int position, int direction));
+EXTERN void Blt_ListSort _ANSI_ARGS_((Blt_List list,
+	Blt_ListCompareProc * proc));
+
+#define Blt_ListGetLength(list) \
+	(((list) == NULL) ? 0 : ((struct Blt_ListStruct *)list)->nNodes)
+#define Blt_ListFirstNode(list) \
+	(((list) == NULL) ? NULL : ((struct Blt_ListStruct *)list)->headPtr)
+#define Blt_ListLastNode(list)	\
+	(((list) == NULL) ? NULL : ((struct Blt_ListStruct *)list)->tailPtr)
+#define Blt_ListPrevNode(node)	((node)->prevPtr)
+#define Blt_ListNextNode(node) 	((node)->nextPtr)
+#define Blt_ListGetKey(node)	\
+	(((node)->listPtr->type == BLT_STRING_KEYS) \
+		 ? (node)->key.string : (node)->key.oneWordValue)
+#define Blt_ListGetValue(node)  	((node)->clientData)
+#define Blt_ListSetValue(node, value) \
+	((node)->clientData = (ClientData)(value))
+#define Blt_ListAppendNode(list, node) \
+	(Blt_ListLinkBefore((list), (node), (Blt_ListNode)NULL))
+#define Blt_ListPrependNode(list, node) \
+	(Blt_ListLinkAfter((list), (node), (Blt_ListNode)NULL))
+
+#endif /* _BLT_LIST_H */
Index: trunk/kitgen/8.x/blt/generic/bltMath.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltMath.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltMath.h	(revision 175)
@@ -0,0 +1,210 @@
+
+/*
+ * bltMath.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_MATH_H
+#define _BLT_MATH_H
+
+#include <math.h>
+
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif /* HAVE_IEEEFP_H */
+
+#ifndef M_PI
+#define M_PI    	3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef M_PI_2
+#define M_PI_2		1.57079632679489661923
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2		1.41421356237309504880
+#endif /* M_SQRT2 */
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2	0.70710678118654752440
+#endif /* M_SQRT1_2 */
+
+#ifndef SHRT_MAX
+#define SHRT_MAX	0x7FFF
+#endif /* SHRT_MAX */
+
+#ifndef SHRT_MIN
+#define SHRT_MIN	-(SHRT_MAX)
+#endif /* SHRT_MAX */
+
+#ifndef USHRT_MAX
+#define	USHRT_MAX	0xFFFF
+#endif /* USHRT_MAX */
+
+#ifndef INT_MAX
+#define INT_MAX		2147483647
+#endif /* INT_MAX */
+
+#ifndef HAVE_FLOAT_H
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DBL_MIN, DBL_MAX --
+ *
+ * 	DBL_MAX and DBL_MIN are the largest and smaller double
+ * 	precision numbers that can be represented by the floating
+ * 	point hardware. If the compiler is ANSI, they can be found in
+ * 	float.h.  Otherwise, we use HUGE_VAL or HUGE to determine
+ * 	them.
+ *
+ * ----------------------------------------------------------------------
+ */
+/*
+ * Don't want to include __infinity (definition of HUGE_VAL (SC1.x))
+ */
+#ifdef sun
+#define DBL_MAX		1.7976931348623157E+308
+#define DBL_MIN		2.2250738585072014E-308
+#define DBL_EPSILON	2.2204460492503131e-16
+#else
+#ifdef HUGE_VAL
+#define DBL_MAX		HUGE_VAL
+#define DBL_MIN		(1/HUGE_VAL)
+#else
+#ifdef HUGE
+#define DBL_MAX		HUGE
+#define DBL_MIN		(1/HUGE)
+#else
+/*
+ * Punt: Assume relatively small values
+ */
+#define DBL_MAX		3.40282347E+38
+#define DBL_MIN		1.17549435E-38
+#endif /*HUGE*/
+#endif /*HUGE_VAL*/
+#endif /*sun*/
+
+#ifndef BLT_DBL_EPSILON
+#  define BLT_DBL_EPSILON 2.2204460492503131e-16
+#endif
+#ifndef BLT_FLT_EPSILON
+#  define BLT_FLT_EPSILON 1.19209290e-07F
+#endif
+
+#ifndef DBL_EPSILON
+#  define DBL_EPSILON	BLT_DBL_EPSILON
+#endif
+#ifndef FLT_EPSILON
+#  define FLT_EPSILON	BLT_FLT_EPSILON
+#endif
+
+#endif /*!HAVE_FLOAT_H*/
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ *  	The following are macros replacing math library functions:
+ *  	"fabs", "fmod", "abs", "rint", and "exp10".
+ *
+ *  	Although many of these routines may exist in your math
+ *  	library, they aren't used in libtcl.a or libtk.a.  This makes
+ *  	it difficult to dynamically load the BLT library as a shared
+ *  	object unless the math library is also shared (which isn't
+ *  	true on several systems).  We can avoid the problem by
+ *  	replacing the "exotic" math routines with macros.
+ *
+ * ----------------------------------------------------------------------
+ */
+#undef ABS
+#define ABS(x)		(((x)<0)?(-(x)):(x))
+
+#undef EXP10
+#define EXP10(x)	(pow(10.0,(x)))
+
+#undef FABS
+#define FABS(x) 	(((x)<0.0)?(-(x)):(x))
+
+#undef SIGN
+#define SIGN(x)		(((x) < 0.0) ? -1 : 1)
+
+/*
+ * Be careful when using the next two macros.  They both assume the floating
+ * point number is less than the size of an int.  That means, for example, you
+ * can't use these macros with numbers bigger than than 2^31-1.
+ */
+#undef FMOD
+#define FMOD(x,y) 	((x)-(((int)((x)/(y)))*y))
+
+#undef ROUND
+#define ROUND(x) 	((int)((x) + (((x)<0.0) ? -0.5 : 0.5)))
+
+#ifdef HAVE_FINITE
+#define FINITE(x)	finite(x)
+#else
+#ifdef HAVE_ISFINITE
+#define FINITE(x)	isfinite(x)
+#else
+#ifdef HAVE_ISNAN
+#define FINITE(x)	(!isnan(x))
+#else
+#define FINITE(x)	(TRUE)
+#endif /* HAVE_ISNAN */
+#endif /* HAVE_ISFINITE */
+#endif /* HAVE_FINITE */
+
+extern double bltNaN;
+
+#define DEFINED(x)	(!isnan(x))
+#define UNDEFINED(x)	(isnan(x))
+#define VALUE_UNDEFINED bltNaN
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ *	On some systems "strdup" and "strcasecmp" are in the C library,
+ *      but have no declarations in the C header files. Make sure we
+ *      supply them here.
+ *
+ * ----------------------------------------------------------------------
+ */
+#ifdef NEED_DECL_STRDUP
+extern char *strdup _ANSI_ARGS_((CONST char *s));
+#endif /* NEED_DECL_STRDUP */
+
+#ifndef HAVE_STRTOLOWER
+extern void strtolower _ANSI_ARGS_((char *s));
+#endif /* HAVE_STRTOLOWER */
+
+#ifdef NEED_DECL_DRAND48
+extern double drand48 _ANSI_ARGS_((void));
+extern void srand48 _ANSI_ARGS_((long seed));
+#endif /* NEED_DECL_DRAND48 */
+
+#ifdef NEED_DECL_STRCASECMP
+extern int strcasecmp _ANSI_ARGS_((CONST char *s1, CONST char *s2));
+#endif /* NEED_DECL_STRCASECMP */
+
+#endif /* BLT_MATH_H */
Index: trunk/kitgen/8.x/blt/generic/bltNsUtil.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltNsUtil.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltNsUtil.c	(revision 175)
@@ -0,0 +1,730 @@
+/*
+ * bltNsUtil.c --
+ *
+ *	This module implements utility procedures for namespaces
+ *	in the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltList.h"
+
+/* Namespace related routines */
+
+typedef struct {
+    char *result;
+    Tcl_FreeProc *freeProc;
+    int errorLine;
+    Tcl_HashTable commandTable;
+    Tcl_HashTable mathFuncTable;
+
+    Tcl_HashTable globalTable;	/* This is the only field we care about */
+
+    int nLevels;
+    int maxNestingDepth;
+} TclInterp;
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_GetVariableNamespace --
+ *
+ *	Returns the namespace context of the vector variable.  If NULL,
+ *	this indicates that the variable is local to the call frame.
+ *
+ *	Note the ever-dangerous manner in which we get this information.
+ *	All of these structures are "private".   Now who's calling Tcl
+ *	an "extension" language?
+ *
+ * Results:
+ *	Returns the context of the namespace in an opaque type.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7)
+
+#ifdef ITCL_NAMESPACES
+
+struct VarTrace;
+struct ArraySearch;
+struct NamespCacheRef;
+
+typedef struct VarStruct Var;
+
+struct VarStruct {
+    int valueLength;
+    int valueSpace;
+    union {
+	char *string;
+	Tcl_HashTable *tablePtr;
+	Var *upvarPtr;
+    } value;
+    Tcl_HashEntry *hPtr;
+    int refCount;
+    struct VarTrace *tracePtr;
+    struct ArraySearch *searchPtr;
+    int flags;
+
+    /* >>>>>>>>>> stuff for [incr Tcl] namespaces <<<<<<<<<< */
+
+    char *name;
+    int protection;
+
+    Itcl_Namespace *namesp;
+    struct NamespCacheRef *cacheInfo;
+
+} Var;
+
+
+Tcl_Namespace *
+Tcl_FindNamespace(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    Itcl_Namespace nsToken;
+
+    if (Itcl_FindNamesp(interp, name, 0, &nsToken) != TCL_OK) {
+	Tcl_ResetResult(interp);
+	return NULL;
+    }
+    return (Tcl_Namespace *) nsToken;
+}
+
+Tcl_Namespace *
+Tcl_GetGlobalNamespace(interp)
+    Tcl_Interp *interp;
+{
+    return (Tcl_Namespace *) Itcl_GetGlobalNamesp(interp);
+}
+
+Tcl_Namespace *
+Tcl_GetCurrentNamespace(interp)
+    Tcl_Interp *interp;
+{
+    return (Tcl_Namespace *) Itcl_GetActiveNamesp(interp);
+}
+
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+    Tcl_Interp *interp;
+    Tcl_Command cmdToken;
+{
+    return (Tcl_Namespace *)interp;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+    Tcl_Interp *interp;
+    CONST char *name;
+{
+    Tcl_Var varToken;
+    Var *varPtr;
+
+    if (Itcl_FindVariable(interp, name, 0, &varToken) != TCL_OK) {
+	return NULL;
+    }
+    varPtr = (Var *) varToken;
+    if (varPtr == NULL) {
+	return NULL;
+    }
+    return (Tcl_Namespace *) varPtr->namesp;
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+    Tcl_Interp *interp;
+    Tcl_Namespace *nsPtr;
+{
+    Itcl_Namespace nsToken = (Itcl_Namespace) nsPtr;
+
+    return (Tcl_CallFrame *) Itcl_ActivateNamesp(interp, nsToken);
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+    Tcl_Interp *interp;
+    Tcl_CallFrame *framePtr;
+{
+    Itcl_DeactivateNamesp(interp, (Itcl_ActiveNamespace) framePtr);
+}
+
+#else
+
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+    Tcl_Interp *interp;
+    Tcl_Command cmdToken;
+{
+    return (Tcl_Namespace *)interp;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+    Tcl_Interp *interp;
+    CONST char *name;
+{
+    TclInterp *iPtr = (TclInterp *) interp;
+
+    return (Tcl_Namespace *)
+	Tcl_FindHashEntry(&(iPtr->globalTable), (char *)name);
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+    Tcl_Interp *interp;
+    Tcl_Namespace *nsPtr;	/* Not used. */
+{
+    return NULL;
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+    Tcl_Interp *interp;
+    Tcl_CallFrame *framePtr;
+{
+    /* empty */
+}
+
+Tcl_Namespace *
+Tcl_GetGlobalNamespace(interp)
+    Tcl_Interp *interp;
+{
+    return (Tcl_Namespace *) interp;
+}
+
+Tcl_Namespace *
+Tcl_GetCurrentNamespace(interp)
+    Tcl_Interp *interp;
+{
+    return (Tcl_Namespace *) interp;
+}
+
+Tcl_Namespace *
+Tcl_FindNamespace(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    return (Tcl_Namespace *) interp;
+}
+
+#endif /* ITCL_NAMESPACES */
+
+#else
+
+/*
+ * A Command structure exists for each command in a namespace. The
+ * Tcl_Command opaque type actually refers to these structures.
+ */
+
+typedef struct CompileProcStruct CompileProc;
+typedef struct ImportRefStruct ImportRef;
+
+typedef struct {
+    Tcl_HashEntry *hPtr;	/* Pointer to the hash table entry that
+				 * refers to this command. The hash table is
+				 * either a namespace's command table or an
+				 * interpreter's hidden command table. This
+				 * pointer is used to get a command's name
+				 * from its Tcl_Command handle. NULL means
+				 * that the hash table entry has been
+				 * removed already (this can happen if
+				 * deleteProc causes the command to be
+				 * deleted or recreated). */
+    Tcl_Namespace *nsPtr;	/* Points to the namespace containing this
+				 * command. */
+    int refCount;		/* 1 if in command hashtable plus 1 for each
+				 * reference from a CmdName Tcl object
+				 * representing a command's name in a
+				 * ByteCode instruction sequence. This
+				 * structure can be freed when refCount
+				 * becomes zero. */
+    int cmdEpoch;		/* Incremented to invalidate any references
+				 * that point to this command when it is
+				 * renamed, deleted, hidden, or exposed. */
+    CompileProc *compileProc;	/* Procedure called to compile command. NULL
+				 * if no compile proc exists for command. */
+    Tcl_ObjCmdProc *objProc;	/* Object-based command procedure. */
+    ClientData objClientData;	/* Arbitrary value passed to object proc. */
+    Tcl_CmdProc *proc;		/* String-based command procedure. */
+    ClientData clientData;	/* Arbitrary value passed to string proc. */
+    Tcl_CmdDeleteProc *deleteProc;
+				/* Procedure invoked when deleting command
+				 * to, e.g., free all client data. */
+    ClientData deleteData;	/* Arbitrary value passed to deleteProc. */
+    int deleted;		/* Means that the command is in the process
+				 * of being deleted (its deleteProc is
+				 * currently executing). Other attempts to
+				 * delete the command should be ignored. */
+    ImportRef *importRefPtr;	/* List of each imported Command created in
+				 * another namespace when this command is
+				 * imported. These imported commands
+				 * redirect invocations back to this
+				 * command. The list is used to remove all
+				 * those imported commands when deleting
+				 * this "real" command. */
+} Command;
+
+
+struct VarTrace;
+struct ArraySearch;
+
+typedef struct VarStruct Var;
+
+struct VarStruct {
+    union {
+	Tcl_Obj *objPtr;
+	Tcl_HashTable *tablePtr;
+	Var *linkPtr;
+    } value;
+    char *name;
+    Tcl_Namespace *nsPtr;
+    Tcl_HashEntry *hPtr;
+    int refCount;
+    struct VarTrace *tracePtr;
+    struct ArraySearch *searchPtr;
+    int flags;
+};
+
+extern Var *TclLookupVar _ANSI_ARGS_((Tcl_Interp *interp, CONST char *part1, 
+	CONST char *part2, int flags, char *mesg, int p1Flags, int p2Flags, 
+	Var ** varPtrPtr));
+
+#define VAR_SCALAR		0x1
+#define VAR_ARRAY		0x2
+#define VAR_LINK		0x4
+#define VAR_UNDEFINED	        0x8
+#define VAR_IN_HASHTABLE	0x10
+#define VAR_TRACE_ACTIVE	0x20
+#define VAR_ARRAY_ELEMENT	0x40
+#define VAR_NAMESPACE_VAR	0x80
+
+#define VAR_ARGUMENT		0x100
+#define VAR_TEMPORARY		0x200
+#define VAR_RESOLVED		0x400
+
+
+Tcl_HashTable *
+Blt_GetArrayVariableTable(interp, varName, flags)
+    Tcl_Interp *interp;
+    CONST char *varName;
+    int flags;
+{
+    Var *varPtr, *arrayPtr;
+
+    varPtr = TclLookupVar(interp, varName, (char *)NULL, flags, "read",
+	FALSE, FALSE, &arrayPtr);
+    if ((varPtr == NULL) || ((varPtr->flags & VAR_ARRAY) == 0)) {
+	return NULL;
+    }
+    return varPtr->value.tablePtr;
+}
+
+Tcl_Namespace *
+Blt_GetVariableNamespace(interp, name)
+    Tcl_Interp *interp;
+    CONST char *name;
+{
+    Var *varPtr;
+
+    varPtr = (Var *)Tcl_FindNamespaceVar(interp, (char *)name, 
+	(Tcl_Namespace *)NULL, 0);
+    if (varPtr == NULL) {
+	return NULL;
+    }
+    return varPtr->nsPtr;
+}
+
+/*ARGSUSED*/
+Tcl_Namespace *
+Blt_GetCommandNamespace(interp, cmdToken)
+    Tcl_Interp *interp;		/* Not used. */
+    Tcl_Command cmdToken;
+{
+    Command *cmdPtr = (Command *)cmdToken;
+
+    return (Tcl_Namespace *)cmdPtr->nsPtr;
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(interp, nsPtr)
+    Tcl_Interp *interp;
+    Tcl_Namespace *nsPtr;
+{
+    Tcl_CallFrame *framePtr;
+
+    framePtr = Blt_Malloc(sizeof(Tcl_CallFrame));
+    assert(framePtr);
+    if (Tcl_PushCallFrame(interp, framePtr, (Tcl_Namespace *)nsPtr, 0)
+	!= TCL_OK) {
+	Blt_Free(framePtr);
+	return NULL;
+    }
+    return framePtr;
+}
+
+void
+Blt_LeaveNamespace(interp, framePtr)
+    Tcl_Interp *interp;
+    Tcl_CallFrame *framePtr;
+{
+    Tcl_PopCallFrame(interp);
+    Blt_Free(framePtr);
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 */
+
+int
+Blt_ParseQualifiedName(interp, qualName, nsPtrPtr, namePtrPtr)
+    Tcl_Interp *interp;
+    CONST char *qualName;
+    Tcl_Namespace **nsPtrPtr;
+    CONST char **namePtrPtr;
+{
+    register char *p, *colon;
+    Tcl_Namespace *nsPtr;
+
+    colon = NULL;
+    p = (char *)(qualName + strlen(qualName));
+    while (--p > qualName) {
+	if ((*p == ':') && (*(p - 1) == ':')) {
+	    p++;		/* just after the last "::" */
+	    colon = p - 2;
+	    break;
+	}
+    }
+    if (colon == NULL) {
+	*nsPtrPtr = NULL;
+	*namePtrPtr = (char *)qualName;
+	return TCL_OK;
+    }
+    *colon = '\0';
+    if (qualName[0] == '\0') {
+	nsPtr = Tcl_GetGlobalNamespace(interp);
+    } else {
+	nsPtr = Tcl_FindNamespace(interp, (char *)qualName, 
+		(Tcl_Namespace *)NULL, 0);
+    }
+    *colon = ':';
+    if (nsPtr == NULL) {
+	return TCL_ERROR;
+    }
+    *nsPtrPtr = nsPtr;
+    *namePtrPtr = p;
+    return TCL_OK;
+}
+
+char *
+Blt_GetQualifiedName(nsPtr, name, resultPtr)
+    Tcl_Namespace *nsPtr;
+    CONST char *name;
+    Tcl_DString *resultPtr;
+{
+    Tcl_DStringInit(resultPtr);
+#if (TCL_MAJOR_VERSION > 7)
+    if ((nsPtr->fullName[0] != ':') || (nsPtr->fullName[1] != ':') ||
+	(nsPtr->fullName[2] != '\0')) {
+	Tcl_DStringAppend(resultPtr, nsPtr->fullName, -1);
+    }
+#endif
+    Tcl_DStringAppend(resultPtr, "::", -1);
+    Tcl_DStringAppend(resultPtr, (char *)name, -1);
+    return Tcl_DStringValue(resultPtr);
+}
+
+
+#if (TCL_MAJOR_VERSION > 7)
+
+typedef struct {
+    Tcl_HashTable clientTable;
+
+    /* Original clientdata and delete procedure. */
+    ClientData origClientData;
+    Tcl_NamespaceDeleteProc *origDeleteProc;
+
+} Callback;
+
+static Tcl_CmdProc NamespaceDeleteCmd;
+static Tcl_NamespaceDeleteProc NamespaceDeleteNotify;
+
+#define NS_DELETE_CMD	"#NamespaceDeleteNotifier"
+
+/*ARGSUSED*/
+static int
+NamespaceDeleteCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/*  */
+    int argc;
+    char **argv;
+{
+    Tcl_AppendResult(interp, "command \"", argv[0], "\" shouldn't be invoked",
+	(char *)NULL);
+    return TCL_ERROR;
+}
+
+static void
+NamespaceDeleteNotify(clientData)
+    ClientData clientData;
+{
+    Blt_List list;
+    Blt_ListNode node;
+    Tcl_CmdDeleteProc *deleteProc;
+
+    list = (Blt_List)clientData;
+    for (node = Blt_ListFirstNode(list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	deleteProc = (Tcl_CmdDeleteProc *)Blt_ListGetValue(node);
+	clientData = (ClientData)Blt_ListGetKey(node);
+	(*deleteProc) (clientData);
+    }
+    Blt_ListDestroy(list);
+}
+
+void
+Blt_DestroyNsDeleteNotify(interp, nsPtr, clientData)
+    Tcl_Interp *interp;
+    Tcl_Namespace *nsPtr;
+    ClientData clientData;
+{
+    Blt_List list;
+    Blt_ListNode node;
+    char *string;
+    Tcl_CmdInfo cmdInfo;
+
+    string = Blt_Malloc(sizeof(nsPtr->fullName) + strlen(NS_DELETE_CMD) + 4);
+    strcpy(string, nsPtr->fullName);
+    strcat(string, "::");
+    strcat(string, NS_DELETE_CMD);
+    if (!Tcl_GetCommandInfo(interp, string, &cmdInfo)) {
+	goto done;
+    }
+    list = (Blt_List)cmdInfo.clientData;
+    node = Blt_ListGetNode(list, clientData);
+    if (node != NULL) {
+	Blt_ListDeleteNode(node);
+    }
+  done:
+    Blt_Free(string);
+}
+
+int
+Blt_CreateNsDeleteNotify(interp, nsPtr, clientData, deleteProc)
+    Tcl_Interp *interp;
+    Tcl_Namespace *nsPtr;
+    ClientData clientData;
+    Tcl_CmdDeleteProc *deleteProc;
+{
+    Blt_List list;
+    char *string;
+    Tcl_CmdInfo cmdInfo;
+
+    string = Blt_Malloc(sizeof(nsPtr->fullName) + strlen(NS_DELETE_CMD) + 4);
+    strcpy(string, nsPtr->fullName);
+    strcat(string, "::");
+    strcat(string, NS_DELETE_CMD);
+    if (!Tcl_GetCommandInfo(interp, string, &cmdInfo)) {
+	list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+	Blt_CreateCommand(interp, string, NamespaceDeleteCmd, list, 
+		NamespaceDeleteNotify);
+    } else {
+	list = (Blt_List)cmdInfo.clientData;
+    }
+    Blt_Free(string);
+    Blt_ListAppend(list, clientData, (ClientData)deleteProc);
+    return TCL_OK;
+}
+
+#endif /* TCL_MAJOR_VERSION > 7 */
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,0,0)) 
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommand --
+ *
+ *	Like Tcl_CreateCommand, but creates command in current namespace
+ *	instead of global, if one isn't defined.  Not a problem with
+ *	[incr Tcl] namespaces.
+ *
+ * Results:
+ *	The return value is a token for the command, which can
+ *	be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
+    Tcl_Interp *interp;		/* Token for command interpreter returned by
+				 * a previous call to Tcl_CreateInterp. */
+    CONST char *cmdName;	/* Name of command. If it contains namespace
+				 * qualifiers, the new command is put in the
+				 * specified namespace; otherwise it is put
+				 * in the global namespace. */
+    Tcl_CmdProc *proc;		/* Procedure to associate with cmdName. */
+    ClientData clientData;	/* Arbitrary value passed to string proc. */
+    Tcl_CmdDeleteProc *deleteProc;
+				/* If not NULL, gives a procedure to call
+				 * when this command is deleted. */
+{
+    return Tcl_CreateCommand(interp, (char *)cmdName, proc, clientData, 
+	deleteProc);
+}
+
+/*ARGSUSED*/
+Tcl_Command
+Tcl_FindCommand(interp, cmdName, nsPtr, flags)
+    Tcl_Interp *interp;
+    char *cmdName;
+    Tcl_Namespace *nsPtr;	/* Not used. */
+    int flags;			/* Not used. */
+{
+    Tcl_HashEntry *hPtr;
+    TclInterp *iPtr = (TclInterp *) interp;
+
+    hPtr = Tcl_FindHashEntry(&iPtr->commandTable, cmdName);
+    if (hPtr == NULL) {
+	return NULL;
+    }
+    return (Tcl_Command) Tcl_GetHashValue(hPtr);
+}
+
+#endif /* TCL_MAJOR_VERSION <= 7 */
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommand --
+ *
+ *	Like Tcl_CreateCommand, but creates command in current namespace
+ *	instead of global, if one isn't defined.  Not a problem with
+ *	[incr Tcl] namespaces.
+ *
+ * Results:
+ *	The return value is a token for the command, which can
+ *	be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
+    Tcl_Interp *interp;		/* Token for command interpreter returned by
+				 * a previous call to Tcl_CreateInterp. */
+    CONST char *cmdName;	/* Name of command. If it contains namespace
+				 * qualifiers, the new command is put in the
+				 * specified namespace; otherwise it is put
+				 * in the global namespace. */
+    Tcl_CmdProc *proc;		/* Procedure to associate with cmdName. */
+    ClientData clientData;	/* Arbitrary value passed to string proc. */
+    Tcl_CmdDeleteProc *deleteProc;
+    /* If not NULL, gives a procedure to call
+
+				 * when this command is deleted. */
+{
+    register CONST char *p;
+
+    p = cmdName + strlen(cmdName);
+    while (--p > cmdName) {
+	if ((*p == ':') && (*(p - 1) == ':')) {
+	    p++;		/* just after the last "::" */
+	    break;
+	}
+    }
+    if (cmdName == p) {
+	Tcl_DString dString;
+	Tcl_Namespace *nsPtr;
+	Tcl_Command cmdToken;
+
+	Tcl_DStringInit(&dString);
+	nsPtr = Tcl_GetCurrentNamespace(interp);
+	Tcl_DStringAppend(&dString, nsPtr->fullName, -1);
+	Tcl_DStringAppend(&dString, "::", -1);
+	Tcl_DStringAppend(&dString, cmdName, -1);
+	cmdToken = Tcl_CreateCommand(interp, Tcl_DStringValue(&dString), proc,
+	    clientData, deleteProc);
+	Tcl_DStringFree(&dString);
+	return cmdToken;
+    }
+    return Tcl_CreateCommand(interp, (char *)cmdName, proc, clientData, 
+	deleteProc);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_CreateCommandObj --
+ *
+ *	Like Tcl_CreateCommand, but creates command in current namespace
+ *	instead of global, if one isn't defined.  Not a problem with
+ *	[incr Tcl] namespaces.
+ *
+ * Results:
+ *	The return value is a token for the command, which can
+ *	be used in future calls to Tcl_GetCommandName.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Command
+Blt_CreateCommandObj(interp, cmdName, proc, clientData, deleteProc)
+    Tcl_Interp *interp;		/* Token for command interpreter returned by
+				 * a previous call to Tcl_CreateInterp. */
+    CONST char *cmdName;	/* Name of command. If it contains namespace
+				 * qualifiers, the new command is put in the
+				 * specified namespace; otherwise it is put
+				 * in the global namespace. */
+    Tcl_ObjCmdProc *proc;	/* Procedure to associate with cmdName. */
+    ClientData clientData;	/* Arbitrary value passed to string proc. */
+    Tcl_CmdDeleteProc *deleteProc;
+				/* If not NULL, gives a procedure to call
+				 * when this command is deleted. */
+{
+    register CONST char *p;
+
+    p = cmdName + strlen(cmdName);
+    while (--p > cmdName) {
+	if ((*p == ':') && (*(p - 1) == ':')) {
+	    p++;		/* just after the last "::" */
+	    break;
+	}
+    }
+    if (cmdName == p) {
+	Tcl_DString dString;
+	Tcl_Namespace *nsPtr;
+	Tcl_Command cmdToken;
+
+	Tcl_DStringInit(&dString);
+	nsPtr = Tcl_GetCurrentNamespace(interp);
+	Tcl_DStringAppend(&dString, nsPtr->fullName, -1);
+	Tcl_DStringAppend(&dString, "::", -1);
+	Tcl_DStringAppend(&dString, cmdName, -1);
+	cmdToken = Tcl_CreateObjCommand(interp, Tcl_DStringValue(&dString), 
+		proc, clientData, deleteProc);
+	Tcl_DStringFree(&dString);
+	return cmdToken;
+    }
+    return Tcl_CreateObjCommand(interp, (char *)cmdName, proc, clientData, 
+	deleteProc);
+}
+#endif /* TCL_VERSION_NUMBER < 8.0.0 */
Index: trunk/kitgen/8.x/blt/generic/bltNsUtil.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltNsUtil.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltNsUtil.h	(revision 175)
@@ -0,0 +1,63 @@
+/*
+ * bltNsUtil.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef BLT_NS_UTIL_H
+#define BLT_NS_UTIL_H 1
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define NS_SEARCH_NONE		(0)
+#define NS_SEARCH_CURRENT	(1<<0)
+#define NS_SEARCH_GLOBAL	(1<<1)
+#define NS_SEARCH_BOTH		(NS_SEARCH_GLOBAL | NS_SEARCH_CURRENT)
+
+/*
+ * Auxillary procedures
+ */
+EXTERN Tcl_Namespace *Blt_GetVariableNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+	CONST char *varName));
+
+EXTERN Tcl_Namespace *Blt_GetCommandNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+	Tcl_Command cmdToken));
+
+EXTERN Tcl_CallFrame *Blt_EnterNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+	Tcl_Namespace *nsPtr));
+
+EXTERN void Blt_LeaveNamespace _ANSI_ARGS_((Tcl_Interp *interp,
+	Tcl_CallFrame * framePtr));
+
+EXTERN int Blt_ParseQualifiedName _ANSI_ARGS_((Tcl_Interp *interp, 
+	CONST char *name, Tcl_Namespace **nsPtrPtr, CONST char **namePtr));
+
+EXTERN char *Blt_GetQualifiedName _ANSI_ARGS_((Tcl_Namespace *nsPtr, 
+	CONST char *name, Tcl_DString *resultPtr));
+
+EXTERN Tcl_Command Blt_CreateCommand _ANSI_ARGS_((Tcl_Interp *interp,
+	CONST char *cmdName, Tcl_CmdProc *proc, ClientData clientData,
+	Tcl_CmdDeleteProc *deleteProc));
+
+
+#endif /* BLT_NS_UTIL_H */
Index: trunk/kitgen/8.x/blt/generic/bltObjConfig.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltObjConfig.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltObjConfig.c	(revision 175)
@@ -0,0 +1,2338 @@
+/* 
+ * bltObjConfig.c --
+ *
+ *	This file contains the Tk_ConfigureWidget procedure. THIS FILE
+ *	IS HERE FOR BACKWARD COMPATIBILITY; THE NEW CONFIGURATION
+ *	PACKAGE SHOULD BE USED FOR NEW PROJECTS.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: bltObjConfig.c,v 1.22 2002/09/18 22:30:51 ghowlett Exp $
+ */
+
+#include "bltInt.h"
+#if (TK_VERSION_NUMBER >= _VERSION(8,0,0))
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "bltObjConfig.h"
+#include "bltTile.h"
+
+#if (TK_VERSION_NUMBER < _VERSION(8,1,0))
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetAnchorFromObj --
+ *
+ *	Return a Tk_Anchor value based on the value of the objPtr.
+ *
+ * Results:
+ *	The return value is a standard Tcl result. If an error occurs during
+ *	conversion, an error message is left in the interpreter's result
+ *	unless "interp" is NULL.
+ *
+ * Side effects:
+ *	The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetAnchorFromObj(interp, objPtr, anchorPtr)
+    Tcl_Interp *interp;		/* Used for error reporting. */
+    Tcl_Obj *objPtr;		/* The object we are trying to get the 
+				 * value from. */
+    Tk_Anchor *anchorPtr;	/* Where to place the Tk_Anchor that
+				 * corresponds to the string value of
+				 * objPtr. */
+{
+    return Tk_GetAnchor(interp, Tcl_GetString(objPtr), anchorPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetJustifyFromObj --
+ *
+ *	Return a Tk_Justify value based on the value of the objPtr.
+ *
+ * Results:
+ *	The return value is a standard Tcl result. If an error occurs during
+ *	conversion, an error message is left in the interpreter's result
+ *	unless "interp" is NULL.
+ *
+ * Side effects:
+ *	The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetJustifyFromObj(interp, objPtr, justifyPtr)
+    Tcl_Interp *interp;		/* Used for error reporting. */
+    Tcl_Obj *objPtr;		/* The object we are trying to get the 
+				 * value from. */
+    Tk_Justify *justifyPtr;	/* Where to place the Tk_Justify that
+				 * corresponds to the string value of
+				 * objPtr. */
+{
+    return Tk_GetJustify(interp, Tcl_GetString(objPtr), justifyPtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetReliefFromObj --
+ *
+ *	Return an integer value based on the value of the objPtr.
+ *
+ * Results:
+ *	The return value is a standard Tcl result. If an error occurs during
+ *	conversion, an error message is left in the interpreter's result
+ *	unless "interp" is NULL.
+ *
+ * Side effects:
+ *	The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetReliefFromObj(interp, objPtr, reliefPtr)
+    Tcl_Interp *interp;		/* Used for error reporting. */
+    Tcl_Obj *objPtr;		/* The object we are trying to get the 
+				 * value from. */
+    int *reliefPtr;		/* Where to place the answer. */
+{
+    return Tk_GetRelief(interp, Tcl_GetString(objPtr), reliefPtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetMMFromObj --
+ *
+ *	Attempt to return an mm value from the Tcl object "objPtr". If the
+ *	object is not already an mm value, an attempt will be made to convert
+ *	it to one.
+ *
+ * Results:
+ *	The return value is a standard Tcl object result. If an error occurs
+ *	during conversion, an error message is left in the interpreter's
+ *	result unless "interp" is NULL.
+ *
+ * Side effects:
+ *	If the object is not already a pixel, the conversion will free
+ *	any old internal representation. 
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetMMFromObj(interp, tkwin, objPtr, doublePtr)
+    Tcl_Interp *interp; 	/* Used for error reporting if not NULL. */
+    Tk_Window tkwin;
+    Tcl_Obj *objPtr;		/* The object from which to get mms. */
+    double *doublePtr;		/* Place to store resulting millimeters. */
+{
+    return Tk_GetScreenMM(interp, tkwin, Tcl_GetString(objPtr), doublePtr);
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetPixelsFromObj --
+ *
+ *	Attempt to return a pixel value from the Tcl object "objPtr". If the
+ *	object is not already a pixel value, an attempt will be made to convert
+ *	it to one.
+ *
+ * Results:
+ *	The return value is a standard Tcl object result. If an error occurs
+ *	during conversion, an error message is left in the interpreter's
+ *	result unless "interp" is NULL.
+ *
+ * Side effects:
+ *	If the object is not already a pixel, the conversion will free
+ *	any old internal representation. 
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Tk_GetPixelsFromObj(interp, tkwin, objPtr, intPtr)
+    Tcl_Interp *interp; 	/* Used for error reporting if not NULL. */
+    Tk_Window tkwin;
+    Tcl_Obj *objPtr;		/* The object from which to get pixels. */
+    int *intPtr;		/* Place to store resulting pixels. */
+{
+    return Tk_GetPixels(interp, tkwin, Tcl_GetString(objPtr), intPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_Alloc3DBorderFromObj --
+ *
+ *	Given a Tcl_Obj *, map the value to a corresponding
+ *	Tk_3DBorder structure based on the tkwin given.
+ *
+ * Results:
+ *	The return value is a token for a data structure describing a
+ *	3-D border.  This token may be passed to procedures such as
+ *	Blt_Draw3DRectangle and Tk_Free3DBorder.  If an error prevented
+ *	the border from being created then NULL is returned and an error
+ *	message will be left in the interp's result.
+ *
+ * Side effects:
+ *	The border is added to an internal database with a reference
+ *	count. For each call to this procedure, there should eventually
+ *	be a call to FreeBorderObjProc so that the database is
+ *	cleaned up when borders aren't in use anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_3DBorder
+Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr)
+    Tcl_Interp *interp;		/* Interp for error results. */
+    Tk_Window tkwin;		/* Need the screen the border is used on.*/
+    Tcl_Obj *objPtr;		/* Object giving name of color for window
+				 * background. */
+{
+    return Tk_Get3DBorder(interp, tkwin, Tcl_GetString(objPtr));
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocBitmapFromObj --
+ *
+ *	Given a Tcl_Obj *, map the value to a corresponding
+ *	Pixmap structure based on the tkwin given.
+ *
+ * Results:
+ *	The return value is the X identifer for the desired bitmap
+ *	(i.e. a Pixmap with a single plane), unless string couldn't be
+ *	parsed correctly.  In this case, None is returned and an error
+ *	message is left in the interp's result.  The caller should never
+ *	modify the bitmap that is returned, and should eventually call
+ *	Tk_FreeBitmapFromObj when the bitmap is no longer needed.
+ *
+ * Side effects:
+ *	The bitmap is added to an internal database with a reference count.
+ *	For each call to this procedure, there should eventually be a call
+ *	to Tk_FreeBitmapFromObj, so that the database can be cleaned up 
+ *	when bitmaps aren't needed anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Tk_AllocBitmapFromObj(interp, tkwin, objPtr)
+    Tcl_Interp *interp;		/* Interp for error results. This may 
+				 * be NULL. */
+    Tk_Window tkwin;		/* Need the screen the bitmap is used on.*/
+    Tcl_Obj *objPtr;		/* Object describing bitmap; see manual
+				 * entry for legal syntax of string value. */
+{
+    return Tk_GetBitmap(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocFontFromObj -- 
+ *
+ *	Given a string description of a font, map the description to a
+ *	corresponding Tk_Font that represents the font.
+ *
+ * Results:
+ *	The return value is token for the font, or NULL if an error
+ *	prevented the font from being created.  If NULL is returned, an
+ *	error message will be left in interp's result object.
+ *
+ * Side effects:
+ * 	The font is added to an internal database with a reference
+ *	count.  For each call to this procedure, there should eventually
+ *	be a call to Tk_FreeFont() or Tk_FreeFontFromObj() so that the
+ *	database is cleaned up when fonts aren't in use anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_Font
+Tk_AllocFontFromObj(interp, tkwin, objPtr)
+    Tcl_Interp *interp;		/* Interp for database and error return. */
+    Tk_Window tkwin;		/* For screen on which font will be used. */
+    Tcl_Obj *objPtr;		/* Object describing font, as: named font,
+				 * native format, or parseable string. */
+{
+    return Tk_GetFont(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocCursorFromObj --
+ *
+ *	Given a Tcl_Obj *, map the value to a corresponding
+ *	Tk_Cursor structure based on the tkwin given.
+ *
+ * Results:
+ *	The return value is the X identifer for the desired cursor,
+ *	unless objPtr couldn't be parsed correctly.  In this case,
+ *	None is returned and an error message is left in the interp's result.
+ *	The caller should never modify the cursor that is returned, and
+ *	should eventually call Tk_FreeCursorFromObj when the cursor is no 
+ *	longer needed.
+ *
+ * Side effects:
+ *	The cursor is added to an internal database with a reference count.
+ *	For each call to this procedure, there should eventually be a call
+ *	to Tk_FreeCursorFromObj, so that the database can be cleaned up 
+ *	when cursors aren't needed anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Cursor
+Tk_AllocCursorFromObj(interp, tkwin, objPtr)
+    Tcl_Interp *interp;		/* Interp for error results. */
+    Tk_Window tkwin;		/* Window in which the cursor will be used.*/
+    Tcl_Obj *objPtr;		/* Object describing cursor; see manual
+				 * entry for description of legal
+				 * syntax of this obj's string rep. */
+{
+    return Tk_GetCursor(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AllocColorFromObj --
+ *
+ *	Given a Tcl_Obj *, map the value to a corresponding
+ *	XColor structure based on the tkwin given.
+ *
+ * Results:
+ *	The return value is a pointer to an XColor structure that
+ *	indicates the red, blue, and green intensities for the color
+ *	given by the string in objPtr, and also specifies a pixel value 
+ *	to use to draw in that color.  If an error occurs, NULL is 
+ *	returned and an error message will be left in interp's result
+ *	(unless interp is NULL).
+ *
+ * Side effects:
+ *	The color is added to an internal database with a reference count.
+ *	For each call to this procedure, there should eventually be a call
+ *	to Tk_FreeColorFromObj so that the database is cleaned up when colors
+ *	aren't in use anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+XColor *
+Tk_AllocColorFromObj(interp, tkwin, objPtr)
+    Tcl_Interp *interp;		/* Used only for error reporting.  If NULL,
+				 * then no messages are provided. */
+    Tk_Window tkwin;		/* Window in which the color will be used.*/
+    Tcl_Obj *objPtr;		/* Object that describes the color; string
+				 * value is a color name such as "red" or
+				 * "#ff0000".*/
+{
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    return Tk_GetColor(interp, tkwin, Tk_GetUid(string));
+}
+
+#endif /* 8.0 */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_GetPosition --
+ *
+ *	Convert a string representing a numeric position.
+ *	A position can be in one of the following forms.
+ *
+ * 	  number	- number of the item in the hierarchy, indexed
+ *			  from zero.
+ *	  "end"		- last position in the hierarchy.
+ *
+ * Results:
+ *	A standard Tcl result.  If "string" is a valid index, then
+ *	*indexPtr is filled with the corresponding numeric index.
+ *	If "end" was selected then *indexPtr is set to -1.
+ *	Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_GetPositionFromObj(interp, objPtr, indexPtr)
+    Tcl_Interp *interp;		/* Interpreter to report results back
+				 * to. */
+    Tcl_Obj *objPtr;		/* Tcl_Obj representation of the index.
+				 * Can be an integer or "end" to refer
+				 * to the last index. */
+    int *indexPtr;		/* Holds the converted index. */
+{
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+	*indexPtr = -1;		/* Indicates last position in hierarchy. */
+    } else {
+	int position;
+
+	if (Tcl_GetIntFromObj(interp, objPtr, &position) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (position < 0) {
+	    Tcl_AppendResult(interp, "bad position \"", string, "\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	*indexPtr = position;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPixelsFromObj --
+ *
+ *	Like Tk_GetPixelsFromObj, but checks for negative, zero.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetPixelsFromObj(interp, tkwin, objPtr, check, valuePtr)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    Tcl_Obj *objPtr;
+    int check;			/* Can be PIXELS_POSITIVE, PIXELS_NONNEGATIVE,
+				 * or PIXELS_ANY, */
+    int *valuePtr;
+{
+    int length;
+
+    if (Tk_GetPixelsFromObj(interp, tkwin, objPtr, &length) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (length >= SHRT_MAX) {
+	Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr), 
+		 "\": too big to represent", (char *)NULL);
+	return TCL_ERROR;
+    }
+    switch (check) {
+    case PIXELS_NONNEGATIVE:
+	if (length < 0) {
+	    Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr), 
+		     "\": can't be negative", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+
+    case PIXELS_POSITIVE:
+	if (length <= 0) {
+	    Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr), 
+		     "\": must be positive", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	break;
+
+    case PIXELS_ANY:
+	break;
+    }
+    *valuePtr = length;
+    return TCL_OK;
+}
+
+int
+Blt_GetPadFromObj(interp, tkwin, objPtr, padPtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window */
+    Tcl_Obj *objPtr;		/* Pixel value string */
+    Blt_Pad *padPtr;
+{
+    int side1, side2;
+    int objc;
+    Tcl_Obj **objv;
+
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((objc < 1) || (objc > 2)) {
+	Tcl_AppendResult(interp, "wrong # elements in padding list",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Blt_GetPixelsFromObj(interp, tkwin, objv[0], PIXELS_NONNEGATIVE, 
+	     &side1) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    side2 = side1;
+    if ((objc > 1) && 
+	(Blt_GetPixelsFromObj(interp, tkwin, objv[1], PIXELS_NONNEGATIVE, 
+	      &side2) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    /* Don't update the pad structure until we know both values are okay. */
+    padPtr->side1 = side1;
+    padPtr->side2 = side2;
+    return TCL_OK;
+}
+
+int
+Blt_GetShadowFromObj(interp, tkwin, objPtr, shadowPtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window */
+    Tcl_Obj *objPtr;		/* Pixel value string */
+    Shadow *shadowPtr;
+{
+    XColor *colorPtr;
+    int dropOffset;
+    int objc;
+    Tcl_Obj **objv;
+
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc > 2) {
+	Tcl_AppendResult(interp, "wrong # elements in drop shadow value",
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    dropOffset = 0;
+    colorPtr = NULL;
+    if (objc > 0) {
+	colorPtr = Tk_AllocColorFromObj(interp, tkwin, objv[0]);
+	if (colorPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	dropOffset = 1;
+	if (objc == 2) {
+	    if (Blt_GetPixelsFromObj(interp, tkwin, objv[1], PIXELS_NONNEGATIVE,
+				     &dropOffset) != TCL_OK) {
+		Tk_FreeColor(colorPtr);
+		return TCL_ERROR;
+	    }
+	}
+    }
+    if (shadowPtr->color != NULL) {
+	Tk_FreeColor(shadowPtr->color);
+    }
+    shadowPtr->color = colorPtr;
+    shadowPtr->offset = dropOffset;
+    return TCL_OK;
+}
+
+int
+Blt_GetStateFromObj(interp, objPtr, statePtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tcl_Obj *objPtr;		/* Pixel value string */
+    int *statePtr;
+{
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if (strcmp(string, "normal") == 0) {
+	*statePtr = STATE_NORMAL;
+    } else if (strcmp(string, "disabled") == 0) {
+	*statePtr = STATE_DISABLED;
+    } else if (strcmp(string, "active") == 0) {
+	*statePtr = STATE_ACTIVE;
+    } else {
+	Tcl_AppendResult(interp, "bad state \"", string,
+	    "\": should be normal, active, or disabled", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+char *
+Blt_NameOfState(state)
+    int state;
+{
+    switch (state) {
+    case STATE_ACTIVE:
+	return "active";
+    case STATE_DISABLED:
+	return "disabled";
+    case STATE_NORMAL:
+	return "normal";
+    default:
+	return "???";
+    }
+}
+
+#ifdef notdef			/* Replace this routine when Tcl_Obj-based
+				 * configuration comes on-line */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfFill --
+ *
+ *	Converts the integer representing the fill style into a string.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_NameOfFill(fill)
+    int fill;
+{
+    switch (fill) {
+    case FILL_X:
+	return "x";
+    case FILL_Y:
+	return "y";
+    case FILL_NONE:
+	return "none";
+    case FILL_BOTH:
+	return "both";
+    default:
+	return "unknown value";
+    }
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetFillFromObj --
+ *
+ *	Converts the fill style string into its numeric representation.
+ *
+ *	Valid style strings are:
+ *
+ *	  "none"   Use neither plane.
+ * 	  "x"	   X-coordinate plane.
+ *	  "y"	   Y-coordinate plane.
+ *	  "both"   Use both coordinate planes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_GetFillFromObj(interp, objPtr, fillPtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tcl_Obj *objPtr;		/* Fill style string */
+    int *fillPtr;
+{
+    int length;
+    char c;
+    char *string;
+
+    string = Tcl_GetStringFromObj(objPtr, &length);
+    c = string[0];
+    if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+	*fillPtr = FILL_NONE;
+    } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) {
+	*fillPtr = FILL_X;
+    } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) {
+	*fillPtr = FILL_Y;
+    } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+	*fillPtr = FILL_BOTH;
+    } else {
+	Tcl_AppendResult(interp, "bad argument \"", string,
+	    "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetDashesFromObj --
+ *
+ *	Converts a Tcl list of dash values into a dash list ready for
+ *	use with XSetDashes.
+ *
+ * 	A valid list dash values can have zero through 11 elements
+ *	(PostScript limit).  Values must be between 1 and 255. Although
+ *	a list of 0 (like the empty string) means no dashes.
+ *
+ * Results:
+ *	A standard Tcl result. If the list represented a valid dash
+ *	list TCL_OK is returned and *dashesPtr* will contain the
+ *	valid dash list. Otherwise, TCL_ERROR is returned and
+ *	interp->result will contain an error message.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetDashesFromObj(interp, objPtr, dashesPtr)
+    Tcl_Interp *interp;
+    Tcl_Obj *objPtr;
+    Blt_Dashes *dashesPtr;
+{
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if ((string == NULL) || (*string == '\0')) {
+	dashesPtr->values[0] = 0;
+    } else if (strcmp(string, "dash") == 0) {	/* 5 2 */
+	dashesPtr->values[0] = 5;
+	dashesPtr->values[1] = 2;
+	dashesPtr->values[2] = 0;
+    } else if (strcmp(string, "dot") == 0) {	/* 1 */
+	dashesPtr->values[0] = 1;
+	dashesPtr->values[1] = 0;
+    } else if (strcmp(string, "dashdot") == 0) {	/* 2 4 2 */
+	dashesPtr->values[0] = 2;
+	dashesPtr->values[1] = 4;
+	dashesPtr->values[2] = 2;
+	dashesPtr->values[3] = 0;
+    } else if (strcmp(string, "dashdotdot") == 0) {	/* 2 4 2 2 */
+	dashesPtr->values[0] = 2;
+	dashesPtr->values[1] = 4;
+	dashesPtr->values[2] = 2;
+	dashesPtr->values[3] = 2;
+	dashesPtr->values[4] = 0;
+    } else {
+	int objc;
+	Tcl_Obj **objv;
+	int value;
+	register int i;
+
+	if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (objc > 11) {	/* This is the postscript limit */
+	    Tcl_AppendResult(interp, "too many values in dash list \"", 
+			     string, "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < objc; i++) {
+	    if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    /*
+	     * Backward compatibility:
+	     * Allow list of 0 to turn off dashes
+	     */
+	    if ((value == 0) && (objc == 1)) {
+		break;
+	    }
+	    if ((value < 1) || (value > 255)) {
+		Tcl_AppendResult(interp, "dash value \"", 
+			 Tcl_GetString(objv[i]), "\" is out of range", 
+			 (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    dashesPtr->values[i] = (unsigned char)value;
+	}
+	/* Make sure the array ends with a NUL byte  */
+	dashesPtr->values[i] = 0;
+    }
+    return TCL_OK;
+}
+
+char *
+Blt_NameOfSide(side)
+    int side;
+{
+    switch (side) {
+    case SIDE_LEFT:
+	return "left";
+    case SIDE_RIGHT:
+	return "right";
+    case SIDE_BOTTOM:
+	return "bottom";
+    case SIDE_TOP:
+	return "top";
+    }
+    return "unknown side value";
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetSideFromObj --
+ *
+ *	Converts the fill style string into its numeric representation.
+ *
+ *	Valid style strings are "left", "right", "top", or  "bottom".
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED */
+int
+Blt_GetSideFromObj(interp, objPtr, sidePtr)
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tcl_Obj *objPtr;		/* Value string */
+    int *sidePtr;		/* (out) Token representing side:
+				 * either SIDE_LEFT, SIDE_RIGHT,
+				 * SIDE_TOP, or SIDE_BOTTOM. */
+{
+    char c;
+    int length;
+    char *string;
+
+    string = Tcl_GetStringFromObj(objPtr, &length);
+    c = string[0];
+    if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+	*sidePtr = SIDE_LEFT;
+    } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+	*sidePtr = SIDE_RIGHT;
+    } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+	*sidePtr = SIDE_TOP;
+    } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+	*sidePtr = SIDE_BOTTOM;
+    } else {
+	Tcl_AppendResult(interp, "bad side \"", string,
+	    "\": should be left, right, top, or bottom", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_StringToEnum --
+ *
+ *	Converts the string into its enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_ObjToEnum(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Vectors of valid strings. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;
+    char *widgRec;		/* Widget record. */
+    int offset;			/* Offset of field in record */
+{
+    int *enumPtr = (int *)(widgRec + offset);
+    char c;
+    register char **p;
+    register int i;
+    int count;
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    c = string[0];
+    count = 0;
+    for (p = (char **)clientData; *p != NULL; p++) {
+	if ((c == p[0][0]) && (strcmp(string, *p) == 0)) {
+	    *enumPtr = count;
+	    return TCL_OK;
+	}
+	count++;
+    }
+    *enumPtr = -1;
+
+    Tcl_AppendResult(interp, "bad value \"", string, "\": should be ", 
+	(char *)NULL);
+    p = (char **)clientData; 
+    if (count > 0) {
+	Tcl_AppendResult(interp, p[0], (char *)NULL);
+    }
+    for (i = 1; i < (count - 1); i++) {
+	Tcl_AppendResult(interp, " ", p[i], ", ", (char *)NULL);
+    }
+    if (count > 1) {
+	Tcl_AppendResult(interp, " or ", p[count - 1], ".", (char *)NULL);
+    }
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EnumToObj --
+ *
+ *	Returns the string associated with the enumerated type.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+Tcl_Obj *
+Blt_EnumToObj(clientData, interp, tkwin, widgRec, offset)
+    ClientData clientData;	/* List of strings. */
+    Tcl_Interp *interp;
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in widget record */
+{
+    int value = *(int *)(widgRec + offset);
+    char **strings = (char **)clientData;
+    char **p;
+    int count;
+
+    count = 0;
+    for (p = strings; *p != NULL; p++) {
+	if (value == count) {
+	    return Tcl_NewStringObj(*p, -1);
+	}
+	count++;
+    }
+    return Tcl_NewStringObj("unknown value", -1);
+}
+
+/* Configuration option helper routines */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoConfig --
+ *
+ *	This procedure applies a single configuration option
+ *	to a widget record.
+ *
+ * Results:
+ *	A standard Tcl return value.
+ *
+ * Side effects:
+ *	WidgRec is modified as indicated by specPtr and value.
+ *	The old value is recycled, if that is appropriate for
+ *	the value type.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+DoConfig(interp, tkwin, specPtr, objPtr, widgRec)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Tk_Window tkwin;		/* Window containing widget (needed to
+				 * set up X resources). */
+    Blt_ConfigSpec *specPtr;	/* Specifier to apply. */
+    Tcl_Obj *objPtr;		/* Value to use to fill in widgRec. */
+    char *widgRec;		/* Record whose fields are to be
+				 * modified.  Values must be properly
+				 * initialized. */
+{
+    char *ptr;
+    int objIsEmpty;
+
+    objIsEmpty = FALSE;
+    if (objPtr == NULL) {
+	objIsEmpty = TRUE;
+    } else if (specPtr->specFlags & BLT_CONFIG_NULL_OK) {
+	int length;
+
+	if (objPtr->bytes != NULL) {
+	    length = objPtr->length;
+	} else {
+	    Tcl_GetStringFromObj(objPtr, &length);
+	}
+	objIsEmpty = (length == 0);
+    }
+    do {
+	ptr = widgRec + specPtr->offset;
+	switch (specPtr->type) {
+	case BLT_CONFIG_ANCHOR: 
+	    {
+		Tk_Anchor anchor;
+		
+		if (Tk_GetAnchorFromObj(interp, objPtr, &anchor) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(Tk_Anchor *)ptr = anchor;
+	    }
+	    break;
+
+	case BLT_CONFIG_BITMAP: 
+	    {
+		Pixmap newBitmap, oldBitmap;
+		
+		if (objIsEmpty) {
+		    newBitmap = None;
+		} else {
+		    newBitmap = Tk_AllocBitmapFromObj(interp, tkwin, objPtr);
+		    if (newBitmap == None) {
+			return TCL_ERROR;
+		    }
+		}
+		oldBitmap = *(Pixmap *)ptr;
+		if (oldBitmap != None) {
+		    Tk_FreeBitmap(Tk_Display(tkwin), oldBitmap);
+		}
+		*(Pixmap *)ptr = newBitmap;
+	    }
+	    break;
+
+	case BLT_CONFIG_BOOLEAN: 
+	    {
+		int newBool;
+		
+		if (Tcl_GetBooleanFromObj(interp, objPtr, &newBool) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = newBool;
+	    }
+	    break;
+
+	case BLT_CONFIG_BORDER: 
+	    {
+		Tk_3DBorder newBorder, oldBorder;
+		
+		if (objIsEmpty) {
+		    newBorder = NULL;
+		} else {
+		    newBorder = Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr);
+		    if (newBorder == NULL) {
+			return TCL_ERROR;
+		    }
+		}
+		oldBorder = *(Tk_3DBorder *)ptr;
+		if (oldBorder != NULL) {
+		    Tk_Free3DBorder(oldBorder);
+		}
+		*(Tk_3DBorder *)ptr = newBorder;
+	    }
+	    break;
+
+	case BLT_CONFIG_CAP_STYLE: 
+	    {
+		int cap;
+		Tk_Uid value;
+		
+		value = Tk_GetUid(Tcl_GetString(objPtr));
+		if (Tk_GetCapStyle(interp, value, &cap) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = cap;
+	    }
+	    break;
+
+	case BLT_CONFIG_COLOR: 
+	    {
+		XColor *newColor, *oldColor;
+		
+		if (objIsEmpty) {
+		    newColor = NULL;
+		} else {
+		    newColor = Tk_AllocColorFromObj(interp, tkwin, objPtr);
+		    if (newColor == NULL) {
+			return TCL_ERROR;
+		    }
+		}
+		oldColor = *(XColor **)ptr;
+		if (oldColor != NULL) {
+		    Tk_FreeColor(oldColor);
+		}
+		*(XColor **)ptr = newColor;
+	    }
+	    break;
+
+	case BLT_CONFIG_CURSOR:
+	case BLT_CONFIG_ACTIVE_CURSOR: 
+	    {
+		Tk_Cursor newCursor, oldCursor;
+		
+		if (objIsEmpty) {
+		    newCursor = None;
+		} else {
+		    newCursor = Tk_AllocCursorFromObj(interp, tkwin, objPtr);
+		    if (newCursor == None) {
+			return TCL_ERROR;
+		    }
+		}
+		oldCursor = *(Tk_Cursor *)ptr;
+		if (oldCursor != None) {
+		    Tk_FreeCursor(Tk_Display(tkwin), oldCursor);
+		}
+		*(Tk_Cursor *)ptr = newCursor;
+		if (specPtr->type == BLT_CONFIG_ACTIVE_CURSOR) {
+		    Tk_DefineCursor(tkwin, newCursor);
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_CUSTOM: 
+	    {
+		if ((*(char **)ptr != NULL) && 
+		    (specPtr->customPtr->freeProc != NULL)) {
+		    (*specPtr->customPtr->freeProc)
+			(specPtr->customPtr->clientData, Tk_Display(tkwin), 
+			 widgRec, specPtr->offset);
+		    *(char **)ptr = NULL;
+		}
+		if (objIsEmpty) {
+		    *(char **)ptr = NULL;
+		} else {
+		    int result;
+		
+		    result = (*specPtr->customPtr->parseProc)
+			(specPtr->customPtr->clientData, interp, tkwin, objPtr,
+			 widgRec, specPtr->offset);
+		    if (result != TCL_OK) {
+			return TCL_ERROR;
+		    }
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_DOUBLE: 
+	    {
+		double newDouble;
+		
+		if (Tcl_GetDoubleFromObj(interp, objPtr, &newDouble) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(double *)ptr = newDouble;
+	    }
+	    break;
+
+	case BLT_CONFIG_FONT: 
+	    {
+		Tk_Font newFont, oldFont;
+		
+		if (objIsEmpty) {
+		    newFont = NULL;
+		} else {
+		    newFont = Tk_AllocFontFromObj(interp, tkwin, objPtr);
+		    if (newFont == NULL) {
+			return TCL_ERROR;
+		    }
+		}
+		oldFont = *(Tk_Font *)ptr;
+		if (oldFont != NULL) {
+		    Tk_FreeFont(oldFont);
+		}
+		*(Tk_Font *)ptr = newFont;
+	    }
+	    break;
+
+	case BLT_CONFIG_INT: 
+	    {
+		int newInt;
+		
+		if (Tcl_GetIntFromObj(interp, objPtr, &newInt) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = newInt;
+	    }
+	    break;
+
+	case BLT_CONFIG_JOIN_STYLE: 
+	    {
+		int join;
+		Tk_Uid value;
+
+		value = Tk_GetUid(Tcl_GetString(objPtr));
+		if (Tk_GetJoinStyle(interp, value, &join) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = join;
+	    }
+	    break;
+
+	case BLT_CONFIG_JUSTIFY: 
+	    {
+		Tk_Justify justify;
+		
+		if (Tk_GetJustifyFromObj(interp, objPtr, &justify) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(Tk_Justify *)ptr = justify;
+	    }
+	    break;
+
+	case BLT_CONFIG_MM: 
+	    {
+		double mm;
+
+		if (Tk_GetMMFromObj(interp, tkwin, objPtr, &mm) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(double *)ptr = mm;
+	    }
+	    break;
+
+	case BLT_CONFIG_PIXELS: 
+	    {
+		int pixels;
+		
+		if (Tk_GetPixelsFromObj(interp, tkwin, objPtr, &pixels) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = pixels;
+	    }
+	    break;
+
+	case BLT_CONFIG_RELIEF: 
+	    {
+		int relief;
+		
+		if (Tk_GetReliefFromObj(interp, objPtr, &relief) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = relief;
+	    }
+	    break;
+
+	case BLT_CONFIG_STRING: 
+	    {
+		char *oldString, *newString;
+		
+		if (objIsEmpty) {
+		    newString = NULL;
+		} else {
+		    newString = (char *)Blt_Strdup(Tcl_GetString(objPtr));
+		}
+		oldString = *(char **)ptr;
+		if (oldString != NULL) {
+		    Blt_Free(oldString);
+		}
+		*(char **)ptr = newString;
+	    }
+	    break;
+
+	case BLT_CONFIG_UID: 
+	    if (objIsEmpty) {
+		*(Tk_Uid *)ptr = NULL;
+	    } else {
+		*(Tk_Uid *)ptr = Tk_GetUid(Tcl_GetString(objPtr));
+	    }
+	    break;
+
+	case BLT_CONFIG_WINDOW: 
+	    {
+		Tk_Window tkwin2;
+
+		if (objIsEmpty) {
+		    tkwin2 = None;
+		} else {
+		    char *path;
+
+		    path = Tcl_GetString(objPtr);
+		    tkwin2 = Tk_NameToWindow(interp, path, tkwin);
+		    if (tkwin2 == NULL) {
+			return TCL_ERROR;
+		    }
+		}
+		*(Tk_Window *)ptr = tkwin2;
+	    }
+	    break;
+
+	case BLT_CONFIG_BITFLAG: 
+	    {
+		int bool;
+		unsigned int flag;
+
+		
+		if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		flag = (unsigned int)specPtr->customPtr;
+		*(int *)ptr &= ~flag;
+		if (bool) {
+		    *(int *)ptr |= flag;
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_DASHES:
+	    if (Blt_GetDashesFromObj(interp, objPtr, (Blt_Dashes *)ptr) 
+		!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_CONFIG_DISTANCE: 
+	    {
+		int newPixels;
+		
+		if (Blt_GetPixelsFromObj(interp, tkwin, objPtr, 
+			PIXELS_NONNEGATIVE, &newPixels) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = newPixels;
+	    }
+	    break;
+
+	case BLT_CONFIG_FILL:
+	    if (Blt_GetFillFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_CONFIG_FLOAT: 
+	    {
+		double newDouble;
+		
+		if (Tcl_GetDoubleFromObj(interp, objPtr, &newDouble) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(float *)ptr = (float)newDouble;
+	    }
+	    break;
+
+	case BLT_CONFIG_LIST: 
+	    {
+		char **argv;
+		int argc;
+		
+		if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(char ***)ptr = argv;
+	    }
+	    break;
+
+	case BLT_CONFIG_LISTOBJ: 
+	    {
+		Tcl_Obj **objv;
+		Tcl_Obj *listObjPtr;
+		int objc;
+		
+		if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		listObjPtr = Tcl_NewListObj(objc, objv);
+		Tcl_IncrRefCount(listObjPtr);
+		*(Tcl_Obj **)ptr = listObjPtr;
+	    }
+	    break;
+
+	case BLT_CONFIG_PAD:
+	    if (Blt_GetPadFromObj(interp, tkwin, objPtr, (Blt_Pad *)ptr) 
+		!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_CONFIG_POS_DISTANCE: 
+	    {
+		int newPixels;
+		
+		if (Blt_GetPixelsFromObj(interp, tkwin, objPtr, 
+			PIXELS_POSITIVE, &newPixels) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+		*(int *)ptr = newPixels;
+	    }
+	    break;
+
+	case BLT_CONFIG_SHADOW: 
+	    {
+		Shadow *shadowPtr = (Shadow *)ptr;
+		
+		if ((shadowPtr != NULL) && (shadowPtr->color != NULL)) {
+		    Tk_FreeColor(shadowPtr->color);
+		}
+		if (Blt_GetShadowFromObj(interp, tkwin, objPtr, shadowPtr) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_STATE: 
+	    {
+		if (Blt_GetStateFromObj(interp, objPtr, (int *)ptr) 
+		    != TCL_OK) {
+		    return TCL_ERROR;
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_TILE: 
+	    {
+		Blt_Tile newTile, oldTile;
+		
+		if (objIsEmpty) {
+		    newTile = None;
+		} else {
+		    if (Blt_GetTile(interp, tkwin, Tcl_GetString(objPtr), 
+				    &newTile) != TCL_OK) {
+			return TCL_ERROR;
+		    }
+		}
+		oldTile = *(Blt_Tile *)ptr;
+		if (oldTile != NULL) {
+		    Blt_FreeTile(oldTile);
+		}
+		*(Blt_Tile *)ptr = newTile;
+	    }
+	    break;
+
+	case BLT_CONFIG_SIDE:
+	    if (Blt_GetSideFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	default: 
+	    {
+		char buf[200];
+		
+		sprintf(buf, "bad config table: unknown type %d", 
+			specPtr->type);
+		Tcl_SetResult(interp, buf, TCL_VOLATILE);
+		return TCL_ERROR;
+	    }
+	}
+	specPtr++;
+    } while ((specPtr->switchName == NULL) && 
+	     (specPtr->type != BLT_CONFIG_END));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatConfigValue --
+ *
+ *	This procedure formats the current value of a configuration
+ *	option.
+ *
+ * Results:
+ *	The return value is the formatted value of the option given
+ *	by specPtr and widgRec.  If the value is static, so that it
+ *	need not be freed, *freeProcPtr will be set to NULL;  otherwise
+ *	*freeProcPtr will be set to the address of a procedure to
+ *	free the result, and the caller must invoke this procedure
+ *	when it is finished with the result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigValue(interp, tkwin, specPtr, widgRec)
+    Tcl_Interp *interp;		/* Interpreter for use in real conversions. */
+    Tk_Window tkwin;		/* Window corresponding to widget. */
+    Blt_ConfigSpec *specPtr;	/* Pointer to information describing option.
+				 * Must not point to a synonym option. */
+    char *widgRec;		/* Pointer to record holding current
+				 * values of info for widget. */
+{
+    char *ptr;
+    char *string;
+
+    ptr = widgRec + specPtr->offset;
+    string = "";
+    switch (specPtr->type) {
+    case BLT_CONFIG_ANCHOR:
+	string = Tk_NameOfAnchor(*(Tk_Anchor *)ptr);
+	break;
+
+    case BLT_CONFIG_BITMAP: 
+	if (*(Pixmap *)ptr != None) {
+	    string = Tk_NameOfBitmap(Tk_Display(tkwin), *(Pixmap *)ptr);
+	}
+	break;
+
+    case BLT_CONFIG_BOOLEAN: 
+	return Tcl_NewBooleanObj(*(int *)ptr);
+
+    case BLT_CONFIG_BORDER: 
+	if (*(Tk_3DBorder *)ptr != NULL) {
+	    string = Tk_NameOf3DBorder(*(Tk_3DBorder *)ptr);
+	}
+	break;
+
+    case BLT_CONFIG_CAP_STYLE:
+	string = Tk_NameOfCapStyle(*(int *)ptr);
+	break;
+
+    case BLT_CONFIG_COLOR: 
+	if (*(XColor **)ptr != NULL) {
+	    string = Tk_NameOfColor(*(XColor **)ptr);
+	}
+	break;
+
+    case BLT_CONFIG_CURSOR:
+    case BLT_CONFIG_ACTIVE_CURSOR:
+	if (*(Tk_Cursor *)ptr != None) {
+	    string = Tk_NameOfCursor(Tk_Display(tkwin), *(Tk_Cursor *)ptr);
+	}
+	break;
+
+    case BLT_CONFIG_CUSTOM:
+	return (*specPtr->customPtr->printProc)(specPtr->customPtr->clientData,
+		interp, tkwin, widgRec, specPtr->offset);
+
+    case BLT_CONFIG_DOUBLE: 
+	return Tcl_NewDoubleObj(*(double *)ptr);
+
+    case BLT_CONFIG_FONT: 
+	if (*(Tk_Font *)ptr != NULL) {
+	    string = Tk_NameOfFont(*(Tk_Font *)ptr);
+	}
+	break;
+
+    case BLT_CONFIG_INT: 
+	return Tcl_NewIntObj(*(int *)ptr);
+
+    case BLT_CONFIG_JOIN_STYLE:
+	string = Tk_NameOfJoinStyle(*(int *)ptr);
+	break;
+
+    case BLT_CONFIG_JUSTIFY:
+	string = Tk_NameOfJustify(*(Tk_Justify *)ptr);
+	break;
+
+    case BLT_CONFIG_MM:
+	return Tcl_NewDoubleObj(*(double *)ptr);
+
+    case BLT_CONFIG_PIXELS: 
+	return Tcl_NewIntObj(*(int *)ptr);
+
+    case BLT_CONFIG_RELIEF: 
+	string = Tk_NameOfRelief(*(int *)ptr);
+	break;
+
+    case BLT_CONFIG_STRING: 
+    case BLT_CONFIG_UID:
+	if (*(char **)ptr != NULL) {
+	    string = *(char **)ptr;
+	}
+	break;
+
+    case BLT_CONFIG_BITFLAG:
+	{
+	    unsigned int flag;
+
+	    flag = (*(int *)ptr) & (unsigned int)specPtr->customPtr;
+	    return Tcl_NewBooleanObj((flag != 0));
+	}
+
+    case BLT_CONFIG_DASHES: 
+	{
+	    unsigned char *p;
+	    Tcl_Obj *listObjPtr;
+	    Blt_Dashes *dashesPtr = (Blt_Dashes *)ptr;
+	    
+	    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	    for(p = dashesPtr->values; *p != 0; p++) {
+		Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(*p));
+	    }
+	    return listObjPtr;
+	}
+
+    case BLT_CONFIG_DISTANCE:
+    case BLT_CONFIG_POS_DISTANCE:
+	return Tcl_NewIntObj(*(int *)ptr);
+
+    case BLT_CONFIG_FILL: 
+	string = Blt_NameOfFill(*(int *)ptr);
+	break;
+
+    case BLT_CONFIG_FLOAT: 
+	{
+	    double x = *(double *)ptr;
+	    return Tcl_NewDoubleObj(x);
+	}
+
+    case BLT_CONFIG_LIST: 
+	{
+	    Tcl_Obj *objPtr, *listObjPtr;
+	    char **p;
+	    
+	    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	    for (p = *(char ***)ptr; *p != NULL; p++) {
+		objPtr = Tcl_NewStringObj(*p, -1);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    }
+	    return listObjPtr;
+	}
+
+    case BLT_CONFIG_LISTOBJ: 
+	return *(Tcl_Obj **)ptr;
+
+    case BLT_CONFIG_PAD: 
+	{
+	    Blt_Pad *padPtr = (Blt_Pad *)ptr;
+	    Tcl_Obj *objPtr, *listObjPtr;
+	    
+	    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	    objPtr = Tcl_NewIntObj(padPtr->side1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    objPtr = Tcl_NewIntObj(padPtr->side2);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    return listObjPtr;
+	}
+
+    case BLT_CONFIG_SHADOW:
+	{
+	    Shadow *shadowPtr = (Shadow *)ptr;
+	    Tcl_Obj *objPtr, *listObjPtr;
+
+	    if (shadowPtr->color != NULL) {
+		listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+		objPtr = Tcl_NewStringObj(Tk_NameOfColor(shadowPtr->color), -1);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+		objPtr = Tcl_NewIntObj(shadowPtr->offset);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+		return listObjPtr;
+	    }
+	}
+
+    case BLT_CONFIG_STATE: 
+	string = Blt_NameOfState(*(int *)ptr);
+	break;
+
+    case BLT_CONFIG_TILE:
+	string = Blt_NameOfTile((Blt_Tile)ptr);
+	break;
+	
+    case BLT_CONFIG_SIDE: 
+	string = Blt_NameOfSide(*(int *)ptr);
+	break;
+
+    default: 
+	string = "?? unknown type ??";
+    }
+    return Tcl_NewStringObj(string, -1);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FormatConfigInfo --
+ *
+ *	Create a valid Tcl list holding the configuration information
+ *	for a single configuration option.
+ *
+ * Results:
+ *	A Tcl list, dynamically allocated.  The caller is expected to
+ *	arrange for this list to be freed eventually.
+ *
+ * Side effects:
+ *	Memory is allocated.
+ *
+ *--------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigInfo(interp, tkwin, specPtr, widgRec)
+    Tcl_Interp *interp;			/* Interpreter to use for things
+					 * like floating-point precision. */
+    Tk_Window tkwin;			/* Window corresponding to widget. */
+    register Blt_ConfigSpec *specPtr;	/* Pointer to information describing
+					 * option. */
+    char *widgRec;			/* Pointer to record holding current
+					 * values of info for widget. */
+{
+    Tcl_Obj *objv[5];
+    Tcl_Obj *listObjPtr;
+    register int i;
+
+    for (i = 0; i < 5; i++) {
+	objv[i] = bltEmptyStringObjPtr;
+    }
+    if (specPtr->switchName != NULL) {
+	objv[0] = Tcl_NewStringObj(specPtr->switchName, -1);
+    } 
+    if (specPtr->dbName != NULL) {
+	objv[1] = Tcl_NewStringObj(specPtr->dbName, -1);
+    }
+    if (specPtr->type == BLT_CONFIG_SYNONYM) {
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objv[0]);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objv[1]);
+	return listObjPtr;
+    } 
+    if (specPtr->dbClass != NULL) {
+	objv[2] = Tcl_NewStringObj(specPtr->dbClass, -1);
+    }
+    if (specPtr->defValue != NULL) {
+	objv[3] = Tcl_NewStringObj(specPtr->defValue, -1);
+    }
+    objv[4] = FormatConfigValue(interp, tkwin, specPtr, widgRec);
+    return Tcl_NewListObj(5, objv);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FindConfigSpec --
+ *
+ *	Search through a table of configuration specs, looking for
+ *	one that matches a given switchName.
+ *
+ * Results:
+ *	The return value is a pointer to the matching entry, or NULL
+ *	if nothing matched.  In that case an error message is left
+ *	in the interp's result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_ConfigSpec *
+FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags)
+    Tcl_Interp *interp;		/* Used for reporting errors. */
+    Blt_ConfigSpec *specs;	/* Pointer to table of configuration
+				 * specifications for a widget. */
+    Tcl_Obj *objPtr;		/* Name (suitable for use in a "config"
+				 * command) identifying particular option. */
+    int needFlags;		/* Flags that must be present in matching
+				 * entry. */
+    int hateFlags;		/* Flags that must NOT be present in
+				 * matching entry. */
+{
+    register Blt_ConfigSpec *specPtr;
+    register char c;		/* First character of current argument. */
+    Blt_ConfigSpec *matchPtr;	/* Matching spec, or NULL. */
+    int length;
+    char *string;
+
+    string = Tcl_GetStringFromObj(objPtr, &length);
+    c = string[1];
+    matchPtr = NULL;
+    for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	if (specPtr->switchName == NULL) {
+	    continue;
+	}
+	if ((specPtr->switchName[1] != c) || 
+	    (strncmp(specPtr->switchName, string, length) != 0)) {
+	    continue;
+	}
+	if (((specPtr->specFlags & needFlags) != needFlags) || 
+	    (specPtr->specFlags & hateFlags)) {
+	    continue;
+	}
+	if (specPtr->switchName[length] == 0) {
+	    matchPtr = specPtr;
+	    goto gotMatch;
+	}
+	if (matchPtr != NULL) {
+	    if (interp != NULL) {
+	        Tcl_AppendResult(interp, "ambiguous option \"", string, "\"", 
+			     (char *)NULL);
+            }
+	    return (Blt_ConfigSpec *)NULL;
+	}
+	matchPtr = specPtr;
+    }
+
+    if (matchPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "unknown option \"", string, "\"", 
+		(char *)NULL);
+	}
+	return (Blt_ConfigSpec *)NULL;
+    }
+
+    /*
+     * Found a matching entry.  If it's a synonym, then find the
+     * entry that it's a synonym for.
+     */
+
+ gotMatch:
+    specPtr = matchPtr;
+    if (specPtr->type == BLT_CONFIG_SYNONYM) {
+	for (specPtr = specs; ; specPtr++) {
+	    if (specPtr->type == BLT_CONFIG_END) {
+		if (interp != NULL) {
+   		    Tcl_AppendResult(interp, 
+			"couldn't find synonym for option \"", string, 
+			"\"", (char *) NULL);
+		}
+		return (Blt_ConfigSpec *) NULL;
+	    }
+	    if ((specPtr->dbName == matchPtr->dbName) && 
+		(specPtr->type != BLT_CONFIG_SYNONYM) && 
+		((specPtr->specFlags & needFlags) == needFlags) && 
+		!(specPtr->specFlags & hateFlags)) {
+		break;
+	    }
+	}
+    }
+    return specPtr;
+}
+
+/* Public routines */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ConfigureWidgetFromObj --
+ *
+ *	Process command-line options and database options to
+ *	fill in fields of a widget record with resources and
+ *	other parameters.
+ *
+ * Results:
+ *	A standard Tcl return value.  In case of an error,
+ *	the interp's result will hold an error message.
+ *
+ * Side effects:
+ *	The fields of widgRec get filled in with information
+ *	from argc/argv and the option database.  Old information
+ *	in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ConfigureWidgetFromObj(interp, tkwin, specs, objc, objv, widgRec, flags)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Tk_Window tkwin;		/* Window containing widget (needed to
+				 * set up X resources). */
+    Blt_ConfigSpec *specs;	/* Describes legal options. */
+    int objc;			/* Number of elements in argv. */
+    Tcl_Obj *CONST *objv;	/* Command-line options. */
+    char *widgRec;		/* Record whose fields are to be
+				 * modified.  Values must be properly
+				 * initialized. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered.  Also,
+				 * may have BLT_CONFIG_ARGV_ONLY set. */
+{
+    register Blt_ConfigSpec *specPtr;
+    int needFlags;		/* Specs must contain this set of flags
+				 * or else they are not considered. */
+    int hateFlags;		/* If a spec contains any bits here, it's
+				 * not considered. */
+
+    if (tkwin == NULL) {
+	/*
+	 * Either we're not really in Tk, or the main window was destroyed and
+	 * we're on our way out of the application
+	 */
+	Tcl_AppendResult(interp, "NULL main window", (char *)NULL);
+	return TCL_ERROR;
+    }
+
+    needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+    if (Tk_Depth(tkwin) <= 1) {
+	hateFlags = BLT_CONFIG_COLOR_ONLY;
+    } else {
+	hateFlags = BLT_CONFIG_MONO_ONLY;
+    }
+
+    /*
+     * Pass one:  scan through all the option specs, replacing strings
+     * with Tk_Uid structs (if this hasn't been done already) and
+     * clearing the BLT_CONFIG_OPTION_SPECIFIED flags.
+     */
+
+    for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	if (!(specPtr->specFlags & INIT) && (specPtr->switchName != NULL)) {
+	    if (specPtr->dbName != NULL) {
+		specPtr->dbName = Tk_GetUid(specPtr->dbName);
+	    }
+	    if (specPtr->dbClass != NULL) {
+		specPtr->dbClass = Tk_GetUid(specPtr->dbClass);
+	    }
+	    if (specPtr->defValue != NULL) {
+		specPtr->defValue = Tk_GetUid(specPtr->defValue);
+	    }
+	}
+	specPtr->specFlags = 
+	    (specPtr->specFlags & ~BLT_CONFIG_OPTION_SPECIFIED) | INIT;
+    }
+
+    /*
+     * Pass two:  scan through all of the arguments, processing those
+     * that match entries in the specs.
+     */
+    while (objc > 0) {
+	specPtr = FindConfigSpec(interp, specs, objv[0], needFlags, hateFlags);
+	if (specPtr == NULL) {
+	    return TCL_ERROR;
+	}
+
+	/* Process the entry.  */
+	if (objc < 2) {
+	    Tcl_AppendResult(interp, "value for \"", Tcl_GetString(objv[0]),
+		    "\" missing", (char *) NULL);
+	    return TCL_ERROR;
+	}
+	if (DoConfig(interp, tkwin, specPtr, objv[1], widgRec) != TCL_OK) {
+	    char msg[100];
+
+	    sprintf(msg, "\n    (processing \"%.40s\" option)",
+		    specPtr->switchName);
+	    Tcl_AddErrorInfo(interp, msg);
+	    return TCL_ERROR;
+	}
+	specPtr->specFlags |= BLT_CONFIG_OPTION_SPECIFIED;
+	objc -= 2, objv += 2;
+    }
+
+    /*
+     * Pass three:  scan through all of the specs again;  if no
+     * command-line argument matched a spec, then check for info
+     * in the option database.  If there was nothing in the
+     * database, then use the default.
+     */
+
+    if (!(flags & BLT_CONFIG_OBJV_ONLY)) {
+	Tcl_Obj *objPtr;
+
+	for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	    if ((specPtr->specFlags & BLT_CONFIG_OPTION_SPECIFIED) || 
+		(specPtr->switchName == NULL) || 
+		(specPtr->type == BLT_CONFIG_SYNONYM)) {
+		continue;
+	    }
+	    if (((specPtr->specFlags & needFlags) != needFlags) || 
+		(specPtr->specFlags & hateFlags)) {
+		continue;
+	    }
+	    objPtr = NULL;
+	    if (specPtr->dbName != NULL) {
+		Tk_Uid value;
+
+		value = Tk_GetOption(tkwin, specPtr->dbName, specPtr->dbClass);
+		if (value != NULL) {
+		    objPtr = Tcl_NewStringObj(value, -1);
+		}
+	    }
+	    if (objPtr != NULL) {
+		if (DoConfig(interp, tkwin, specPtr, objPtr, widgRec) 
+		    != TCL_OK) {
+		    char msg[200];
+    
+		    sprintf(msg, "\n    (%s \"%.50s\" in widget \"%.50s\")",
+			    "database entry for",
+			    specPtr->dbName, Tk_PathName(tkwin));
+		    Tcl_AddErrorInfo(interp, msg);
+		    return TCL_ERROR;
+		}
+	    } else {
+		if (specPtr->defValue != NULL) {
+		    objPtr = Tcl_NewStringObj(specPtr->defValue, -1);
+		} else {
+		    objPtr = NULL;
+		}
+		if ((objPtr != NULL) && !(specPtr->specFlags
+			& BLT_CONFIG_DONT_SET_DEFAULT)) {
+		    if (DoConfig(interp, tkwin, specPtr, objPtr, widgRec) 
+			!= TCL_OK) {
+			char msg[200];
+	
+			sprintf(msg,
+				"\n    (%s \"%.50s\" in widget \"%.50s\")",
+				"default value for",
+				specPtr->dbName, Tk_PathName(tkwin));
+			Tcl_AddErrorInfo(interp, msg);
+			return TCL_ERROR;
+		    }
+		}
+	    }
+	}
+    }
+
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ConfigureInfoFromObj --
+ *
+ *	Return information about the configuration options
+ *	for a window, and their current values.
+ *
+ * Results:
+ *	Always returns TCL_OK.  The interp's result will be modified
+ *	hold a description of either a single configuration option
+ *	available for "widgRec" via "specs", or all the configuration
+ *	options available.  In the "all" case, the result will
+ *	available for "widgRec" via "specs".  The result will
+ *	be a list, each of whose entries describes one option.
+ *	Each entry will itself be a list containing the option's
+ *	name for use on command lines, database name, database
+ *	class, default value, and current value (empty string
+ *	if none).  For options that are synonyms, the list will
+ *	contain only two values:  name and synonym name.  If the
+ *	"name" argument is non-NULL, then the only information
+ *	returned is that for the named argument (i.e. the corresponding
+ *	entry in the overall list is returned).
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Blt_ConfigureInfoFromObj(interp, tkwin, specs, widgRec, objPtr, flags)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Tk_Window tkwin;		/* Window corresponding to widgRec. */
+    Blt_ConfigSpec *specs;	/* Describes legal options. */
+    char *widgRec;		/* Record whose fields contain current
+				 * values for options. */
+    Tcl_Obj *objPtr;		/* If non-NULL, indicates a single option
+				 * whose info is to be returned.  Otherwise
+				 * info is returned for all options. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered. */
+{
+    register Blt_ConfigSpec *specPtr;
+    int needFlags, hateFlags;
+    char *string;
+    Tcl_Obj *listObjPtr, *valueObjPtr;
+
+    needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+    if (Tk_Depth(tkwin) <= 1) {
+	hateFlags = BLT_CONFIG_COLOR_ONLY;
+    } else {
+	hateFlags = BLT_CONFIG_MONO_ONLY;
+    }
+
+    /*
+     * If information is only wanted for a single configuration
+     * spec, then handle that one spec specially.
+     */
+
+    Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
+    if (objPtr != NULL) {
+	specPtr = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+	if (specPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	valueObjPtr =  FormatConfigInfo(interp, tkwin, specPtr, widgRec);
+	Tcl_SetObjResult(interp, valueObjPtr);
+	return TCL_OK;
+    }
+
+    /*
+     * Loop through all the specs, creating a big list with all
+     * their information.
+     */
+    string = NULL;		/* Suppress compiler warning. */
+    if (objPtr != NULL) {
+	string = Tcl_GetString(objPtr);
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	if ((objPtr != NULL) && (specPtr->switchName != string)) {
+	    continue;
+	}
+	if (((specPtr->specFlags & needFlags) != needFlags) || 
+	    (specPtr->specFlags & hateFlags)) {
+	    continue;
+	}
+	if (specPtr->switchName == NULL) {
+	    continue;
+	}
+	valueObjPtr = FormatConfigInfo(interp, tkwin, specPtr, widgRec);
+	Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureValueFromObj --
+ *
+ *	This procedure returns the current value of a configuration
+ *	option for a widget.
+ *
+ * Results:
+ *	The return value is a standard Tcl completion code (TCL_OK or
+ *	TCL_ERROR).  The interp's result will be set to hold either the value
+ *	of the option given by objPtr (if TCL_OK is returned) or
+ *	an error message (if TCL_ERROR is returned).
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureValueFromObj(interp, tkwin, specs, widgRec, objPtr, flags)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Tk_Window tkwin;		/* Window corresponding to widgRec. */
+    Blt_ConfigSpec *specs;	/* Describes legal options. */
+    char *widgRec;		/* Record whose fields contain current
+				 * values for options. */
+    Tcl_Obj *objPtr;		/* Gives the command-line name for the
+				 * option whose value is to be returned. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered. */
+{
+    Blt_ConfigSpec *specPtr;
+    int needFlags, hateFlags;
+
+    needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+    if (Tk_Depth(tkwin) <= 1) {
+	hateFlags = BLT_CONFIG_COLOR_ONLY;
+    } else {
+	hateFlags = BLT_CONFIG_MONO_ONLY;
+    }
+    specPtr = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+    if (specPtr == NULL) {
+	return TCL_ERROR;
+    }
+    objPtr = FormatConfigValue(interp, tkwin, specPtr, widgRec);
+    Tcl_SetObjResult(interp, objPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeObjOptions --
+ *
+ *	Free up all resources associated with configuration options.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Any resource in widgRec that is controlled by a configuration
+ *	option (e.g. a Tk_3DBorder or XColor) is freed in the appropriate
+ *	fashion.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeObjOptions(specs, widgRec, display, needFlags)
+    Blt_ConfigSpec *specs;	/* Describes legal options. */
+    char *widgRec;		/* Record whose fields contain current
+				 * values for options. */
+    Display *display;		/* X display; needed for freeing some
+				 * resources. */
+    int needFlags;		/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered. */
+{
+    register Blt_ConfigSpec *specPtr;
+    char *ptr;
+
+    for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	if ((specPtr->specFlags & needFlags) != needFlags) {
+	    continue;
+	}
+	ptr = widgRec + specPtr->offset;
+	switch (specPtr->type) {
+	case BLT_CONFIG_STRING:
+	    if (*((char **) ptr) != NULL) {
+		Blt_Free(*((char **) ptr));
+		*((char **) ptr) = NULL;
+	    }
+	    break;
+
+	case BLT_CONFIG_COLOR:
+	    if (*((XColor **) ptr) != NULL) {
+		Tk_FreeColor(*((XColor **) ptr));
+		*((XColor **) ptr) = NULL;
+	    }
+	    break;
+
+	case BLT_CONFIG_FONT:
+	    Tk_FreeFont(*((Tk_Font *) ptr));
+	    *((Tk_Font *) ptr) = NULL;
+	    break;
+
+	case BLT_CONFIG_BITMAP:
+	    if (*((Pixmap *) ptr) != None) {
+		Tk_FreeBitmap(display, *((Pixmap *) ptr));
+		*((Pixmap *) ptr) = None;
+	    }
+	    break;
+
+	case BLT_CONFIG_BORDER:
+	    if (*((Tk_3DBorder *) ptr) != NULL) {
+		Tk_Free3DBorder(*((Tk_3DBorder *) ptr));
+		*((Tk_3DBorder *) ptr) = NULL;
+	    }
+	    break;
+
+	case BLT_CONFIG_CURSOR:
+	case BLT_CONFIG_ACTIVE_CURSOR:
+	    if (*((Tk_Cursor *) ptr) != None) {
+		Tk_FreeCursor(display, *((Tk_Cursor *) ptr));
+		*((Tk_Cursor *) ptr) = None;
+	    }
+	    break;
+
+	case BLT_CONFIG_LISTOBJ:
+	    Tcl_DecrRefCount(*(Tcl_Obj **)ptr);
+	    break;
+
+	case BLT_CONFIG_LIST:
+	    {
+		char **argv;
+		
+		argv = *(char ***)ptr;
+		if (argv != NULL) {
+		    Blt_Free(argv);
+		    *(char ***)ptr = NULL;
+		}
+	    }
+	    break;
+
+	case BLT_CONFIG_TILE: 
+	    if ((Blt_Tile)ptr != NULL) {
+		Blt_FreeTile((Blt_Tile)ptr);
+		*(Blt_Tile *)ptr = NULL;
+	    }
+	    break;
+		
+	case BLT_CONFIG_CUSTOM:
+	    if ((*(char **)ptr != NULL) && 
+		(specPtr->customPtr->freeProc != NULL)) {
+		(*specPtr->customPtr->freeProc)(specPtr->customPtr->clientData,
+			display, widgRec, specPtr->offset);
+		*(char **)ptr = NULL;
+	    }
+	    break;
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ObjConfigModified --
+ *
+ *      Given the configuration specifications and one or more option
+ *	patterns (terminated by a NULL), indicate if any of the matching
+ *	configuration options has been reset.
+ *
+ * Results:
+ *      Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int 
+Blt_ObjConfigModified TCL_VARARGS_DEF(Blt_ConfigSpec *, arg1)
+{
+    va_list argList;
+    Blt_ConfigSpec *specs;
+    register Blt_ConfigSpec *specPtr;
+    register char *option;
+
+    specs = TCL_VARARGS_START(Blt_ConfigSpec *, arg1, argList);
+    while ((option = va_arg(argList, char *)) != NULL) {
+	for (specPtr = specs; specPtr->type != BLT_CONFIG_END; specPtr++) {
+	    if ((Tcl_StringMatch(specPtr->switchName, option)) &&
+		(specPtr->specFlags & BLT_CONFIG_OPTION_SPECIFIED)) {
+		va_end(argList);
+		return 1;
+	    }
+	}
+    }
+    va_end(argList);
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ConfigureComponentFromObj --
+ *
+ *	Configures a component of a widget.  This is useful for
+ *	widgets that have multiple components which aren't uniquely
+ *	identified by a Tk_Window. It allows us, for example, set
+ *	resources for axes of the graph widget. The graph really has
+ *	only one window, but its convenient to specify components in a
+ *	hierarchy of options.
+ *
+ *		*graph.x.logScale yes
+ *		*graph.Axis.logScale yes
+ *		*graph.temperature.scaleSymbols yes
+ *		*graph.Element.scaleSymbols yes
+ *
+ *	This is really a hack to work around the limitations of the Tk
+ *	resource database.  It creates a temporary window, needed to
+ *	call Tk_ConfigureWidget, using the name of the component.
+ *
+ * Results:
+ *      A standard Tcl result.
+ *
+ * Side Effects:
+ *	A temporary window is created merely to pass to Tk_ConfigureWidget.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ConfigureComponentFromObj(interp, parent, name, className, specsPtr,
+	objc, objv, widgRec, flags)
+    Tcl_Interp *interp;
+    Tk_Window parent;		/* Window to associate with component */
+    char *name;			/* Name of component */
+    char *className;
+    Blt_ConfigSpec *specsPtr;
+    int objc;
+    Tcl_Obj *CONST *objv;
+    char *widgRec;
+    int flags;
+{
+    Tk_Window tkwin;
+    int result;
+    char *tmpName;
+    int isTemporary = FALSE;
+
+    tmpName = Blt_Strdup(name);
+
+    /* Window name can't start with an upper case letter */
+    tmpName[0] = tolower(name[0]);
+
+    /*
+     * Create component if a child window by the component's name
+     * doesn't already exist.
+     */
+    tkwin = Blt_FindChild(parent, tmpName);
+    if (tkwin == NULL) {
+	tkwin = Tk_CreateWindow(interp, parent, tmpName, (char *)NULL);
+	isTemporary = TRUE;
+    }
+    if (tkwin == NULL) {
+	Tcl_AppendResult(interp, "can't find window in \"", 
+			 Tk_PathName(parent), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    assert(Tk_Depth(tkwin) == Tk_Depth(parent));
+    Blt_Free(tmpName);
+
+    Tk_SetClass(tkwin, className);
+    result = Blt_ConfigureWidgetFromObj(interp, tkwin, specsPtr, objc, objv, 
+	widgRec, flags);
+    if (isTemporary) {
+	Tk_DestroyWindow(tkwin);
+    }
+    return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ObjIsOption --
+ *
+ *	Indicates whether objPtr is a valid configuration option 
+ *	such as -background.
+ *
+ * Results:
+ *	Returns 1 is a matching option is found and 0 otherwise.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ObjIsOption(specs, objPtr, flags)
+    Blt_ConfigSpec *specs;	/* Describes legal options. */
+    Tcl_Obj *objPtr;		/* Command-line option name. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered.  Also,
+				 * may have BLT_CONFIG_ARGV_ONLY set. */
+{
+    register Blt_ConfigSpec *specPtr;
+    int needFlags;		/* Specs must contain this set of flags
+				 * or else they are not considered. */
+
+    needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+    specPtr = FindConfigSpec((Tcl_Interp *)NULL, specs, objPtr, needFlags, 0);
+    return (specPtr != NULL);
+}
+
+
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
Index: trunk/kitgen/8.x/blt/generic/bltObjConfig.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltObjConfig.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltObjConfig.h	(revision 175)
@@ -0,0 +1,264 @@
+/* 
+ * bltObjConfig.h --
+ *
+ *	This file contains the Tcl_Obj based versions of the old
+ *	Tk_ConfigureWidget procedures. 
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef BLT_OBJCONFIG_H
+#define BLT_OBJCONFIG_H
+
+/*
+ * This is a Tcl_Obj based replacement for the widget configuration
+ * functions in Tk.  
+ *
+ * What not use the new Tk_Option interface?
+ *
+ *	There were design changes in the new Tk_Option interface that
+ *	make it unwieldy.  
+ *
+ *	o You have to dynamically allocate, store, and deallocate
+ *	  your option table.  
+ *      o The Tk_FreeConfigOptions routine requires a tkwin argument.
+ *	  Unfortunately, most widgets save the display pointer and 
+ *	  deference their tkwin when the window is destroyed.  
+ *	o There's no TK_CONFIG_CUSTOM functionality.  This means that
+ *	  save special options must be saved as strings by 
+ *	  Tk_ConfigureWidget and processed later, thus losing the 
+ *	  benefits of Tcl_Objs.  It also make error handling 
+ *	  problematic, since you don't pick up certain errors like 
+ *	  
+ *	    .widget configure -myoption bad -myoption good
+ *        
+ *	  You will never see the first "bad" value.
+ *	o Especially compared to the former Tk_ConfigureWidget calls,
+ *	  the new interface is overly complex.  If there was a big
+ *	  performance win, it might be worth the effort.  But let's 
+ *	  face it, this biggest wins are in processing custom options
+ *	  values with thousands of elements.  Most common resources 
+ *	  (font, color, etc) have string tokens anyways.
+ *
+ *	On the other hand, the replacement functions in this file fell
+ *	into place quite easily both from the aspect of API writer and
+ *	user.  The biggest benefit is that you don't need to change lots
+ *	of working code just to get the benefits of Tcl_Objs.
+ * 
+ */
+#define SIDE_LEFT		(0)
+#define SIDE_TOP		(1)
+#define SIDE_RIGHT		(2)
+#define SIDE_BOTTOM		(3)
+
+#define SIDE_HORIZONTAL(s)	(!((s) & 0x1))
+#define SIDE_VERTICAL(s)	((s) & 0x1)
+
+#ifndef Blt_Offset
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+#endif /* Blt_Offset */
+
+typedef int (Blt_OptionParseProc) _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr, char *widgRec, 
+	int offset));					       
+typedef Tcl_Obj *(Blt_OptionPrintProc) _ANSI_ARGS_((ClientData clientData, 
+	Tcl_Interp *interp, Tk_Window tkwin, char *widgRec, int offset));
+typedef void (Blt_OptionFreeProc) _ANSI_ARGS_((ClientData clientData,
+	Display *display, char *widgRec, int offset));
+
+typedef struct Blt_CustomOption {
+    Blt_OptionParseProc *parseProc;	/* Procedure to call to parse an
+					 * option and store it in converted
+					 * form. */
+    Blt_OptionPrintProc *printProc;	/* Procedure to return a printable
+					 * string describing an existing
+					 * option. */
+    Blt_OptionFreeProc *freeProc;	/* Procedure to free the value. */
+
+    ClientData clientData;		/* Arbitrary one-word value used by
+					 * option parser:  passed to
+					 * parseProc and printProc. */
+} Blt_CustomOption;
+
+/*
+ * Structure used to specify information for Tk_ConfigureWidget.  Each
+ * structure gives complete information for one option, including
+ * how the option is specified on the command line, where it appears
+ * in the option database, etc.
+ */
+
+typedef struct {
+    int type;			/* Type of option, such as BLT_CONFIG_COLOR;
+				 * see definitions below.  Last option in
+				 * table must have type BLT_CONFIG_END. */
+    char *switchName;		/* Switch used to specify option in argv.
+				 * NULL means this spec is part of a group. */
+    Tk_Uid dbName;		/* Name for option in option database. */
+    Tk_Uid dbClass;		/* Class for option in database. */
+    Tk_Uid defValue;		/* Default value for option if not
+				 * specified in command line or database. */
+    int offset;			/* Where in widget record to store value;
+				 * use Tk_Offset macro to generate values
+				 * for this. */
+    int specFlags;		/* Any combination of the values defined
+				 * below;  other bits are used internally
+				 * by tkConfig.c. */
+    Blt_CustomOption *customPtr; /* If type is BLT_CONFIG_CUSTOM then this is
+				 * a pointer to info about how to parse and
+				 * print the option.  Otherwise it is
+				 * irrelevant. */
+} Blt_ConfigSpec;
+
+/*
+ * Type values for Blt_ConfigSpec structures.  See the user
+ * documentation for details.
+ */
+
+typedef enum {
+    BLT_CONFIG_ACTIVE_CURSOR, 
+    BLT_CONFIG_ANCHOR, 
+    BLT_CONFIG_BITMAP,
+    BLT_CONFIG_BOOLEAN, 
+    BLT_CONFIG_BORDER, 
+    BLT_CONFIG_CAP_STYLE, 
+    BLT_CONFIG_COLOR, 
+    BLT_CONFIG_CURSOR, 
+    BLT_CONFIG_CUSTOM, 
+    BLT_CONFIG_DOUBLE, 
+    BLT_CONFIG_FONT, 
+    BLT_CONFIG_INT, 
+    BLT_CONFIG_JOIN_STYLE,
+    BLT_CONFIG_JUSTIFY, 
+    BLT_CONFIG_MM, 
+    BLT_CONFIG_PIXELS, 
+    BLT_CONFIG_RELIEF, 
+    BLT_CONFIG_STRING,
+    BLT_CONFIG_SYNONYM, 
+    BLT_CONFIG_UID, 
+    BLT_CONFIG_WINDOW, 
+
+    BLT_CONFIG_BITFLAG,
+    BLT_CONFIG_DASHES,
+    BLT_CONFIG_DISTANCE,	/*  */
+    BLT_CONFIG_FILL,
+    BLT_CONFIG_FLOAT, 
+    BLT_CONFIG_LIST,
+    BLT_CONFIG_LISTOBJ,
+    BLT_CONFIG_PAD,
+    BLT_CONFIG_POS_DISTANCE,	/*  */
+    BLT_CONFIG_SHADOW,		/*  */
+    BLT_CONFIG_SIDE,
+    BLT_CONFIG_STATE, 
+    BLT_CONFIG_TILE,
+    
+    BLT_CONFIG_END
+} Blt_ConfigTypes;
+
+/*
+ * Possible values for flags argument to Tk_ConfigureWidget:
+ */
+
+#define BLT_CONFIG_OBJV_ONLY	1
+#define BLT_CONFIG_OBJS		0x80
+
+/*
+ * Possible flag values for Blt_ConfigSpec structures.  Any bits at
+ * or above BLT_CONFIG_USER_BIT may be used by clients for selecting
+ * certain entries.  Before changing any values here, coordinate with
+ * tkOldConfig.c (internal-use-only flags are defined there).
+ */
+
+#define BLT_CONFIG_NULL_OK		1
+#define BLT_CONFIG_COLOR_ONLY		2
+#define BLT_CONFIG_MONO_ONLY		4
+#define BLT_CONFIG_DONT_SET_DEFAULT	8
+#define BLT_CONFIG_OPTION_SPECIFIED	0x10
+#define BLT_CONFIG_USER_BIT		0x100
+
+/*
+ * Values for "flags" field of Blt_ConfigSpec structures.  Be sure
+ * to coordinate these values with those defined in tk.h
+ * (BLT_CONFIG_COLOR_ONLY, etc.).  There must not be overlap!
+ *
+ * INIT -		Non-zero means (char *) things have been
+ *			converted to Tk_Uid's.
+ */
+
+#define INIT		0x20
+
+EXTERN int Blt_ConfigureInfoFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Blt_ConfigSpec *specs, char *widgRec, 
+	Tcl_Obj *objPtr, int flags));
+EXTERN int Blt_ConfigureValueFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Blt_ConfigSpec *specs, char * widgRec, 
+	Tcl_Obj *objPtr, int flags));
+EXTERN int Blt_ConfigureWidgetFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Blt_ConfigSpec *specs, int objc, Tcl_Obj *CONST *objv,
+	char *widgRec, int flags));
+EXTERN int Blt_ConfigureComponentFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, char *name, char *className, Blt_ConfigSpec *specs,
+	int objc, Tcl_Obj *CONST *objv, char *widgRec, int flags));
+
+EXTERN int Blt_ObjConfigModified _ANSI_ARGS_(TCL_VARARGS(Blt_ConfigSpec *, specs));
+EXTERN void Blt_FreeObjOptions _ANSI_ARGS_((Blt_ConfigSpec *specs, 
+	char *widgRec, Display *display, int needFlags));
+
+EXTERN int Blt_ObjIsOption _ANSI_ARGS_((Blt_ConfigSpec *specs, Tcl_Obj *objPtr,
+	int flags));
+
+EXTERN int Blt_GetPositionFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, int *indexPtr));
+
+EXTERN int Blt_GetPixelsFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr, int flags, int *valuePtr));
+
+EXTERN int Blt_GetPadFromObj  _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr, Blt_Pad *padPtr));
+
+EXTERN int Blt_GetShadowFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr, Shadow *shadowPtr));
+
+EXTERN int Blt_GetStateFromObj  _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, int *statePtr));
+
+EXTERN int Blt_GetFillFromObj  _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, int *fillPtr));
+
+EXTERN int Blt_GetDashesFromObj  _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, Blt_Dashes *dashesPtr));
+
+
+#if ((TK_VERSION_NUMBER >= _VERSION(8,0,0)) && \
+     (TK_VERSION_NUMBER < _VERSION(8,1,0)))
+EXTERN int Tk_GetAnchorFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, Tk_Anchor *anchorPtr));
+EXTERN int Tk_GetJustifyFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	Tcl_Obj *objPtr, Tk_Justify *justifyPtr));
+EXTERN int Tk_GetReliefFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tcl_Obj *objPtr, int *reliefPtr));
+EXTERN int Tk_GetMMFromObj _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	Tcl_Obj *objPtr, double *doublePtr));
+EXTERN int Tk_GetPixelsFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr, int *intPtr));
+EXTERN Tk_3DBorder Tk_Alloc3DBorderFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Pixmap Tk_AllocBitmapFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Tk_Font Tk_AllocFontFromObj _ANSI_ARGS_((Tcl_Interp *interp, 
+	Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN Tk_Cursor Tk_AllocCursorFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Tcl_Obj *objPtr));
+EXTERN XColor *Tk_AllocColorFromObj _ANSI_ARGS_((Tcl_Interp *interp,
+	Tk_Window tkwin, Tcl_Obj *objPtr));
+#endif /* 8.0 */
+
+#endif /* BLT_OBJCONFIG_H */
Index: trunk/kitgen/8.x/blt/generic/bltParse.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltParse.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltParse.c	(revision 175)
@@ -0,0 +1,543 @@
+/*
+ * tclParse.c --
+ *
+ *	Contains a collection of procedures that are used to parse Tcl
+ *	commands or parts of commands (like quoted strings or nested
+ *	sub-commands).  
+ *
+ *	Since Tcl 8.1.0 these routines have been replaced by ones that
+ *	generate byte-codes.  But since these routines are used in
+ *	vector expressions, where no such byte-compilation is
+ *	necessary, I now include them.  In fact, the byte-compiled
+ *	versions would be slower since the compiled code typically
+ *	runs only one time.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 19941998 Sun Microsystems, Inc.
+ * 
+ */
+
+#include "bltInt.h"
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
+#include "bltInterp.h"
+
+/*
+ * A table used to classify input characters to assist in parsing
+ * Tcl commands.  The table should be indexed with a signed character
+ * using the CHAR_TYPE macro.  The character may have a negative
+ * value.  The CHAR_TYPE macro takes a pointer to a signed character
+ * and a pointer to the last character in the source string.  If the
+ * src pointer is pointing at the terminating null of the string,
+ * CHAR_TYPE returns TCL_COMMAND_END.
+ */
+
+#define STATIC_STRING_SPACE	150
+#define UCHAR(c)		((unsigned char) (c))
+#define TCL_NORMAL		0x01
+#define TCL_SPACE		0x02
+#define TCL_COMMAND_END		0x04
+#define TCL_QUOTE		0x08
+#define TCL_OPEN_BRACKET	0x10
+#define TCL_OPEN_BRACE		0x20
+#define TCL_CLOSE_BRACE		0x40
+#define TCL_BACKSLASH		0x80
+#define TCL_DOLLAR		0x00
+
+/*
+ * The following table assigns a type to each character. Only types
+ * meaningful to Tcl parsing are represented here. The table is
+ * designed to be referenced with either signed or unsigned characters,
+ * so it has 384 entries. The first 128 entries correspond to negative
+ * character values, the next 256 correspond to positive character
+ * values. The last 128 entries are identical to the first 128. The
+ * table is always indexed with a 128-byte offset (the 128th entry
+ * corresponds to a 0 character value).
+ */
+
+static unsigned char tclTypeTable[] =
+{
+ /*
+     * Negative character values, from -128 to -1:
+     */
+
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+     * Positive character values, from 0-127:
+     */
+
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE,
+    TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL,
+    TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET,
+    TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE,
+    TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+     * Large unsigned character values, from 128-255:
+     */
+
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+    TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+};
+
+#define CHAR_TYPE(src,last) \
+	(((src)==(last))?TCL_COMMAND_END:(tclTypeTable+128)[(int)*(src)])
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseNestedCmd --
+ *
+ *	This procedure parses a nested Tcl command between
+ *	brackets, returning the result of the command.
+ *
+ * Results:
+ *	The return value is a standard Tcl result, which is
+ *	TCL_OK unless there was an error while executing the
+ *	nested command.  If an error occurs then interp->result
+ *	contains a standard error message.  *TermPtr is filled
+ *	in with the address of the character just after the
+ *	last one processed;  this is usually the character just
+ *	after the matching close-bracket, or the null character
+ *	at the end of the string if the close-bracket was missing
+ *	(a missing close bracket is an error).  The result returned
+ *	by the command is stored in standard fashion in *parsePtr,
+ *	null-terminated, with parsePtr->next pointing to the null
+ *	character.
+ *
+ * Side effects:
+ *	The storage space at *parsePtr may be expanded.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ParseNestedCmd(interp, string, flags, termPtr, parsePtr)
+    Tcl_Interp *interp;		/* Interpreter to use for nested command
+				 * evaluations and error messages. */
+    char *string;		/* Character just after opening bracket. */
+    int flags;			/* Flags to pass to nested Tcl_Eval. */
+    char **termPtr;		/* Store address of terminating character
+				 * here. */
+    ParseValue *parsePtr;	/* Information about where to place
+				 * result of command. */
+{
+    int result, length, shortfall;
+    Interp *iPtr = (Interp *) interp;
+
+    iPtr->evalFlags = flags | TCL_BRACKET_TERM;
+    result = Tcl_Eval(interp, string);
+    *termPtr = (string + iPtr->termOffset);
+    if (result != TCL_OK) {
+	/*
+	 * The increment below results in slightly cleaner message in
+	 * the errorInfo variable (the close-bracket will appear).
+	 */
+
+	if (**termPtr == ']') {
+	    *termPtr += 1;
+	}
+	return result;
+    }
+    (*termPtr) += 1;
+    length = strlen(iPtr->result);
+    shortfall = length + 1 - (parsePtr->end - parsePtr->next);
+    if (shortfall > 0) {
+	(*parsePtr->expandProc) (parsePtr, shortfall);
+    }
+    strcpy(parsePtr->next, iPtr->result);
+    parsePtr->next += length;
+
+    Tcl_FreeResult(interp);
+    iPtr->result = iPtr->resultSpace;
+    iPtr->resultSpace[0] = '\0';
+    return TCL_OK;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseBraces --
+ *
+ *	This procedure scans the information between matching
+ *	curly braces.
+ *
+ * Results:
+ *	The return value is a standard Tcl result, which is
+ *	TCL_OK unless there was an error while parsing string.
+ *	If an error occurs then interp->result contains a
+ *	standard error message.  *TermPtr is filled
+ *	in with the address of the character just after the
+ *	last one successfully processed;  this is usually the
+ *	character just after the matching close-brace.  The
+ *	information between curly braces is stored in standard
+ *	fashion in *parsePtr, null-terminated with parsePtr->next
+ *	pointing to the terminating null character.
+ *
+ * Side effects:
+ *	The storage space at *parsePtr may be expanded.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Blt_ParseBraces(interp, string, termPtr, parsePtr)
+    Tcl_Interp *interp;		/* Interpreter to use for nested command
+				 * evaluations and error messages. */
+    char *string;		/* Character just after opening bracket. */
+    char **termPtr;		/* Store address of terminating character
+				 * here. */
+    ParseValue *parsePtr;	/* Information about where to place
+				 * result of command. */
+{
+    int level;
+    register char *src, *dest, *end;
+    register char c;
+    char *lastChar = string + strlen(string);
+
+    src = string;
+    dest = parsePtr->next;
+    end = parsePtr->end;
+    level = 1;
+
+    /*
+     * Copy the characters one at a time to the result area, stopping
+     * when the matching close-brace is found.
+     */
+
+    for (;;) {
+	c = *src;
+	src++;
+
+	if (dest == end) {
+	    parsePtr->next = dest;
+	    (*parsePtr->expandProc) (parsePtr, 20);
+	    dest = parsePtr->next;
+	    end = parsePtr->end;
+	}
+	*dest = c;
+	dest++;
+
+	if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+	    continue;
+	} else if (c == '{') {
+	    level++;
+	} else if (c == '}') {
+	    level--;
+	    if (level == 0) {
+		dest--;		/* Don't copy the last close brace. */
+		break;
+	    }
+	} else if (c == '\\') {
+	    int count;
+
+	    /*
+	     * Must always squish out backslash-newlines, even when in
+	     * braces.  This is needed so that this sequence can appear
+	     * anywhere in a command, such as the middle of an expression.
+	     */
+
+	    if (*src == '\n') {
+		dest[-1] = Tcl_Backslash(src - 1, &count);
+		src += count - 1;
+	    } else {
+		Tcl_Backslash(src - 1, &count);
+		while (count > 1) {
+		    if (dest == end) {
+			parsePtr->next = dest;
+			(*parsePtr->expandProc) (parsePtr, 20);
+			dest = parsePtr->next;
+			end = parsePtr->end;
+		    }
+		    *dest = *src;
+		    dest++;
+		    src++;
+		    count--;
+		}
+	    }
+	} else if (c == '\0') {
+	    Tcl_AppendResult(interp, "missing close-brace", (char *)NULL);
+	    *termPtr = string - 1;
+	    return TCL_ERROR;
+	}
+    }
+
+    *dest = '\0';
+    parsePtr->next = dest;
+    *termPtr = src;
+    return TCL_OK;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ExpandParseValue --
+ *
+ *	This procedure is commonly used as the value of the
+ *	expandProc in a ParseValue.  It uses malloc to allocate
+ *	more space for the result of a parse.
+ *
+ * Results:
+ *	The buffer space in *parsePtr is reallocated to something
+ *	larger, and if parsePtr->clientData is non-zero the old
+ *	buffer is freed.  Information is copied from the old
+ *	buffer to the new one.
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_ExpandParseValue(parsePtr, needed)
+    ParseValue *parsePtr;	/* Information about buffer that
+				 * must be expanded.  If the clientData
+				 * in the structure is non-zero, it
+				 * means that the current buffer is
+				 * dynamically allocated. */
+    int needed;			/* Minimum amount of additional space
+				 * to allocate. */
+{
+    int size;
+    char *buffer;
+
+    /*
+     * Either double the size of the buffer or add enough new space
+     * to meet the demand, whichever produces a larger new buffer.
+     */
+    size = (parsePtr->end - parsePtr->buffer) + 1;
+    if (size < needed) {
+	size += needed;
+    } else {
+	size += size;
+    }
+    buffer = Blt_Malloc((unsigned int)size);
+
+    /*
+     * Copy from old buffer to new, free old buffer if needed, and
+     * mark new buffer as malloc-ed.
+     */
+    memcpy((VOID *) buffer, (VOID *) parsePtr->buffer,
+	(size_t) (parsePtr->next - parsePtr->buffer));
+    parsePtr->next = buffer + (parsePtr->next - parsePtr->buffer);
+    if (parsePtr->clientData != 0) {
+	Blt_Free(parsePtr->buffer);
+    }
+    parsePtr->buffer = buffer;
+    parsePtr->end = buffer + size - 1;
+    parsePtr->clientData = (ClientData)1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ParseQuotes --
+ *
+ *	This procedure parses a double-quoted string such as a
+ *	quoted Tcl command argument or a quoted value in a Tcl
+ *	expression.  This procedure is also used to parse array
+ *	element names within parentheses, or anything else that
+ *	needs all the substitutions that happen in quotes.
+ *
+ * Results:
+ *	The return value is a standard Tcl result, which is
+ *	TCL_OK unless there was an error while parsing the
+ *	quoted string.  If an error occurs then interp->result
+ *	contains a standard error message.  *TermPtr is filled
+ *	in with the address of the character just after the
+ *	last one successfully processed;  this is usually the
+ *	character just after the matching close-quote.  The
+ *	fully-substituted contents of the quotes are stored in
+ *	standard fashion in *parsePtr, null-terminated with
+ *	parsePtr->next pointing to the terminating null character.
+ *
+ * Side effects:
+ *	The buffer space in parsePtr may be enlarged by calling its
+ *	expandProc.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ParseQuotes(interp, string, termChar, flags, termPtr, parsePtr)
+    Tcl_Interp *interp;		/* Interpreter to use for nested command
+				 * evaluations and error messages. */
+    char *string;		/* Character just after opening double-
+				 * quote. */
+    int termChar;		/* Character that terminates "quoted" string
+				 * (usually double-quote, but sometimes
+				 * right-paren or something else). */
+    int flags;			/* Flags to pass to nested Tcl_Eval calls. */
+    char **termPtr;		/* Store address of terminating character
+				 * here. */
+    ParseValue *parsePtr;	/* Information about where to place
+				 * fully-substituted result of parse. */
+{
+    register char *src, *dest, c;
+    char *lastChar = string + strlen(string);
+
+    src = string;
+    dest = parsePtr->next;
+
+    for (;;) {
+	if (dest == parsePtr->end) {
+	    /*
+	     * Target buffer space is about to run out.  Make more space.
+	     */
+	    parsePtr->next = dest;
+	    (*parsePtr->expandProc) (parsePtr, 1);
+	    dest = parsePtr->next;
+	}
+	c = *src;
+	src++;
+	if (c == termChar) {
+	    *dest = '\0';
+	    parsePtr->next = dest;
+	    *termPtr = src;
+	    return TCL_OK;
+	} else if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+	  copy:
+	    *dest = c;
+	    dest++;
+	    continue;
+	} else if (c == '$') {
+	    int length;
+	    CONST char *value;
+
+	    value = Tcl_ParseVar(interp, src - 1, termPtr);
+	    if (value == NULL) {
+		return TCL_ERROR;
+	    }
+	    src = *termPtr;
+	    length = strlen(value);
+	    if ((parsePtr->end - dest) <= length) {
+		parsePtr->next = dest;
+		(*parsePtr->expandProc) (parsePtr, length);
+		dest = parsePtr->next;
+	    }
+	    strcpy(dest, value);
+	    dest += length;
+	    continue;
+	} else if (c == '[') {
+	    int result;
+
+	    parsePtr->next = dest;
+	    result = Blt_ParseNestedCmd(interp, src, flags, termPtr, parsePtr);
+	    if (result != TCL_OK) {
+		return result;
+	    }
+	    src = *termPtr;
+	    dest = parsePtr->next;
+	    continue;
+	} else if (c == '\\') {
+	    int nRead;
+
+	    src--;
+	    *dest = Tcl_Backslash(src, &nRead);
+	    dest++;
+	    src += nRead;
+	    continue;
+	} else if (c == '\0') {
+	    char buf[30];
+
+	    Tcl_ResetResult(interp);
+	    sprintf(buf, "missing %c", termChar);
+	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
+	    *termPtr = string - 1;
+	    return TCL_ERROR;
+	} else {
+	    goto copy;
+	}
+    }
+}
+
+#endif /* TCL_VERSION_NUMBER >= _VERSION(8,1,0) */
Index: trunk/kitgen/8.x/blt/generic/bltPool.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltPool.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltPool.c	(revision 175)
@@ -0,0 +1,458 @@
+/*
+ * bltPool.c --
+ *
+ * Copyright 2001 Silicon Metrics, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Silicon Metrics disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltPool.h"
+
+/*
+ * Blt_Pool --
+ *
+ *	Implements a pool memory allocator. 
+ *
+ *	  + It's faster allocating memory since malloc/free are called
+ *	    only a fraction of the normal times.  Fixed size items can 
+ *	    be reused without deallocating/reallocating memory.
+ *	  + You don't have the extra 8-16 byte overhead per malloc. 
+ *	  - Memory is freed only when the entire pool is destroyed.
+ *	  - Memory is allocated in chunks. More memory is allocated 
+ *	    than used.  
+ *	  0 Depending upon allocation/deallocation patterns, locality
+ *	    may be improved or degraded.
+ *
+ *      The pool is a chain of malloc'ed blocks.
+ *
+ *             +---------+  +---------+  +---------+  
+ *       NULL<-| nextPtr |<-| nextPtr |<-| nextPtr |<- headPtr
+ *             |---------|  |---------|  |---------|  
+ *             | chunk1  |  | chunk2  |  | chunk3  |  
+ *             +---------+  |         |  |         |  
+ *                          +---------+  |         |  
+ *                                       |         |  
+ *                                       |         |  
+ *                                       +---------+  
+ *
+ *      Each chunk contains an integral number of fixed size items.
+ *	The number of items doubles until a maximum size is reached
+ *      (each subsequent new chunk will be the maximum).  Chunks
+ *	are allocated only when needed (no more space is available
+ *	in the last chunk).
+ *
+ *	The chain of blocks is only freed when the entire pool is
+ *	destroyed.  
+ *
+ *      A freelist of unused items also maintained. Each freed item
+ *	is prepended to a free list.  Before allocating new chunks
+ *	the freelist is examined to see if an unused items exist.
+ *
+ *               chunk1       chunk2       chunk3
+ *            +---------+  +---------+  +---------+  
+ *      NULL<-| unused  |  |         |  |         |
+ *            +----^----+  +---------+  +---------+  
+ *            | unused  |<-| unused  |<-| unused  |       
+ *            +---------+  +---------+  +----^----+  
+ *            |         |  |         |  | unused  |
+ *            +---------+  |         |  +----^----+
+ *                         |         |  |    |    |
+ *                         +---------+  +----|----+
+ *                                      | usused  |<- freePtr
+ *                                      +---------+  
+ */
+
+#define POOL_MAX_CHUNK_SIZE      ((1<<16) - sizeof(Blt_PoolChain))
+
+#ifndef ALIGN
+#define ALIGN(a) \
+	(((size_t)a + (sizeof(void *) - 1)) & (~(sizeof(void *) - 1)))
+#endif /* ALIGN */
+
+static Blt_PoolAllocProc VariablePoolAllocItem;
+static Blt_PoolFreeProc  VariablePoolFreeItem;
+static Blt_PoolAllocProc FixedPoolAllocItem;
+static Blt_PoolFreeProc  FixedPoolFreeItem;
+static Blt_PoolAllocProc StringPoolAllocItem;
+static Blt_PoolFreeProc  StringPoolFreeItem;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VariablePoolAllocItem --
+ *
+ *      Returns a new item.  First check if there is any more space 
+ *	left in the current chunk.  If there isn't then next check
+ *	the free list for unused items.  Finally allocate a new 
+ *	chunk and return its first item.
+ *
+ * Results:
+ *      Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ *	A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+VariablePoolAllocItem(poolPtr, size)
+    struct Blt_PoolStruct *poolPtr;
+    size_t size;		/* Number of bytes to allocate. */
+{
+    Blt_PoolChain *chainPtr;
+    void *memPtr;
+
+    size = ALIGN(size);
+    if (size >= POOL_MAX_CHUNK_SIZE) {
+	/* 
+	 * Handle oversized requests by allocating a chunk to hold the
+	 * single item and immediately placing it into the in-use list.
+	 */
+	chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + size);
+        if (poolPtr->headPtr == NULL) {
+	    poolPtr->headPtr = chainPtr;
+	} else {
+	    chainPtr->nextPtr = poolPtr->headPtr->nextPtr;
+	    poolPtr->headPtr->nextPtr = chainPtr;
+	}
+	memPtr = (void *)chainPtr;
+    } else {
+	if (poolPtr->bytesLeft >= size) {
+	    poolPtr->bytesLeft -= size;
+	    memPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+	} else {
+	    poolPtr->waste += poolPtr->bytesLeft;
+	    /* Create a new block of items and prepend it to the in-use list */
+	    poolPtr->bytesLeft = POOL_MAX_CHUNK_SIZE;
+	    /* Allocate the requested chunk size, plus the header */
+	    chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+	    chainPtr->nextPtr = poolPtr->headPtr;
+	    poolPtr->headPtr = chainPtr;
+	    /* Peel off a new item. */
+	    poolPtr->bytesLeft -= size;
+	    memPtr = (char *)(chainPtr + 1) + poolPtr->bytesLeft;
+	}
+    }
+    return memPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VariablePoolFreeItem --
+ *
+ *      Placeholder for freeProc routine.  The pool memory is 
+ *	not reclaimed or freed until the entire pool is released.
+ *
+ * Results:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+VariablePoolFreeItem(poolPtr, item) 
+    struct Blt_PoolStruct *poolPtr;
+    void *item;
+{
+    /* Does nothing */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringPoolAllocItem --
+ *
+ *      Returns a new item.  First check if there is any more space 
+ *	left in the current chunk.  If there isn't then next check
+ *	the free list for unused items.  Finally allocate a new 
+ *	chunk and return its first item.
+ *
+ * Results:
+ *      Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ *	A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+StringPoolAllocItem(poolPtr, size)
+    struct Blt_PoolStruct *poolPtr;
+    size_t size;		/* Number of bytes to allocate. */
+{
+    Blt_PoolChain *chainPtr;
+    void *memPtr;
+
+    if (size >= POOL_MAX_CHUNK_SIZE) {
+	/* 
+	 * Handle oversized requests by allocating a chunk to hold the
+	 * single item and immediately placing it into the in-use list.
+	 */
+	chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + size);
+        if (poolPtr->headPtr == NULL) {
+	    poolPtr->headPtr = chainPtr;
+	} else {
+	    chainPtr->nextPtr = poolPtr->headPtr->nextPtr;
+	    poolPtr->headPtr->nextPtr = chainPtr;
+	}
+	memPtr = (void *)chainPtr;
+    } else {
+	if (poolPtr->bytesLeft >= size) {
+	    poolPtr->bytesLeft -= size;
+	    memPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+	} else {
+	    poolPtr->waste += poolPtr->bytesLeft;
+	    /* Create a new block of items and prepend it to the
+	     * in-use list */
+	    poolPtr->bytesLeft = POOL_MAX_CHUNK_SIZE;
+	    /* Allocate the requested chunk size, plus the header */
+	    chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+	    chainPtr->nextPtr = poolPtr->headPtr;
+	    poolPtr->headPtr = chainPtr;
+	    /* Peel off a new item. */
+	    poolPtr->bytesLeft -= size;
+	    memPtr = (char *)(chainPtr + 1) + poolPtr->bytesLeft;
+	}
+    }
+    return memPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringPoolFreeItem --
+ *
+ *      Placeholder for freeProc routine.  String pool memory is 
+ *	not reclaimed or freed until the entire pool is released.
+ *
+ * Results:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+StringPoolFreeItem(poolPtr, item) 
+    struct Blt_PoolStruct *poolPtr;
+    void *item;
+{
+    /* Does nothing */
+}
+
+/*
+ *       The fixed size pool is a chain of malloc'ed blocks.
+ *
+ *             +---------+  +---------+  +---------+  
+ *       NULL<-| nextPtr |<-| nextPtr |<-| nextPtr |<- headPtr
+ *             |---------|  |---------|  |---------|  
+ *             | chunk1  |  | chunk2  |  | chunk3  |  
+ *             +---------+  |         |  |         |  
+ *                          +---------+  |         |  
+ *                                       |         |  
+ *                                       |         |  
+ *                                       +---------+  
+ *
+ *      Each chunk contains an integral number of fixed size items.
+ *	The number of items doubles until a maximum size is reached
+ *      (each subsequent new chunk will be the maximum).  Chunks
+ *	are allocated only when needed (no more space is available
+ *	in the last chunk).
+ *
+ *	The chain of blocks is only freed when the entire pool is
+ *	destroyed.  
+ *
+ *      A freelist of unused items also maintained. Each freed item
+ *	is prepended to a free list.  Before allocating new chunks
+ *	the freelist is examined to see if an unused items exist.
+ *
+ *               chunk1       chunk2       chunk3
+ *            +---------+  +---------+  +---------+  
+ *      NULL<-| unused  |  |         |  |         |
+ *            +----^----+  +---------+  +---------+  
+ *            | unused  |<-| unused  |<-| unused  |       
+ *            +---------+  +---------+  +----^----+  
+ *            |         |  |         |  | unused  |
+ *            +---------+  |         |  +----^----+
+ *                         |         |  |    |    |
+ *                         +---------+  +----|----+
+ *                                      | usused  |<- freePtr
+ *                                      +---------+  
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FixedPoolFreeItem --
+ *
+ *      Returns a new item.  First check if there is any more space 
+ *	left in the current chunk.  If there isn't then next check
+ *	the free list for unused items.  Finally allocate a new 
+ *	chunk and return its first item.
+ *
+ * Results:
+ *      Returns a new (possible reused) item.
+ *
+ * Side Effects:
+ *	A new memory chunk may be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void *
+FixedPoolAllocItem(poolPtr, size)
+    struct Blt_PoolStruct *poolPtr;
+    size_t size;
+{
+    Blt_PoolChain *chainPtr;
+    void *newPtr;
+
+    size = ALIGN(size);
+    if (poolPtr->itemSize == 0) {
+	poolPtr->itemSize = size;
+    }
+    assert(size == poolPtr->itemSize);
+
+    if (poolPtr->bytesLeft > 0) {
+	poolPtr->bytesLeft -= poolPtr->itemSize;
+	newPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+    } else if (poolPtr->freePtr != NULL) { /* Reuse from the free list. */
+	/* Reuse items on the free list */
+	chainPtr = poolPtr->freePtr;
+	poolPtr->freePtr = chainPtr->nextPtr;
+	newPtr = (void *)chainPtr;
+    } else {			/* Allocate another block. */
+	
+	/* Create a new block of items and prepend it to the in-use list */
+	poolPtr->bytesLeft = poolPtr->itemSize * (1 << poolPtr->poolSize);
+	if (poolPtr->bytesLeft < POOL_MAX_CHUNK_SIZE) {
+	    poolPtr->poolSize++; /* Keep doubling the size of the new 
+				  * chunk up to a maximum size. */
+	}
+	/* Allocate the requested chunk size, plus the header */
+	chainPtr = Blt_Malloc(sizeof(Blt_PoolChain) + poolPtr->bytesLeft);
+	chainPtr->nextPtr = poolPtr->headPtr;
+	poolPtr->headPtr = chainPtr;
+
+	/* Peel off a new item. */
+	poolPtr->bytesLeft -= poolPtr->itemSize;
+	newPtr = (char *)(poolPtr->headPtr + 1) + poolPtr->bytesLeft;
+    }
+    return newPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FixedPoolFreeItem --
+ *
+ *      Frees an item.  The actual memory is not freed.  The item
+ *	instead is prepended to a freelist where it may be reclaimed
+ *	and used again.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *	Item is placed on the pool's free list.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FixedPoolFreeItem(poolPtr, item) 
+    struct Blt_PoolStruct *poolPtr;
+    void *item;
+{
+    Blt_PoolChain *chainPtr = (Blt_PoolChain *)item;
+    
+    /* Prepend the newly deallocated item to the free list. */
+    chainPtr->nextPtr = poolPtr->freePtr;
+    poolPtr->freePtr = chainPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PoolCreate --
+ *
+ *      Creates a new memory pool for fixed-size/variable-size/string
+ *      items.  
+ *
+ * Results:
+ *      Returns a pointer to the newly allocated pool.
+ *
+ *---------------------------------------------------------------------- 
+ */
+
+Blt_Pool
+Blt_PoolCreate(type)
+    int type;
+{
+    struct Blt_PoolStruct *poolPtr;
+
+    poolPtr = Blt_Malloc(sizeof(struct Blt_PoolStruct));
+    switch (type) {
+    case BLT_VARIABLE_SIZE_ITEMS:
+	poolPtr->allocProc = VariablePoolAllocItem;
+	poolPtr->freeProc = VariablePoolFreeItem;
+	break;
+    case BLT_FIXED_SIZE_ITEMS:
+	poolPtr->allocProc = FixedPoolAllocItem;
+	poolPtr->freeProc = FixedPoolFreeItem;
+	break;
+    case BLT_STRING_ITEMS:
+ 	poolPtr->allocProc = StringPoolAllocItem;
+	poolPtr->freeProc = StringPoolFreeItem;
+	break;
+    }
+    poolPtr->headPtr = poolPtr->freePtr = NULL;
+    poolPtr->waste = poolPtr->bytesLeft = 0;
+    poolPtr->poolSize = poolPtr->itemSize = 0;
+    return poolPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PoolDestroy --
+ *
+ *      Destroys the given memory pool.  The chain of allocated blocks
+ *	are freed.  The is the only time that memory is actually freed.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *	All memory used by the pool is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+void  
+Blt_PoolDestroy(poolPtr)
+    struct Blt_PoolStruct *poolPtr;
+{
+    register Blt_PoolChain *chainPtr, *nextPtr;
+    
+    for (chainPtr = poolPtr->headPtr; chainPtr != NULL; chainPtr = nextPtr) {
+	nextPtr = chainPtr->nextPtr;
+	Blt_Free(chainPtr);
+    }
+    Blt_Free(poolPtr);
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltPool.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltPool.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltPool.h	(revision 175)
@@ -0,0 +1,36 @@
+#ifndef BLT_POOL_H
+#define BLT_POOL_H
+
+typedef struct Blt_PoolChainStruct {
+   struct Blt_PoolChainStruct *nextPtr;
+} Blt_PoolChain;
+
+#define BLT_STRING_ITEMS		0
+#define BLT_FIXED_SIZE_ITEMS		1
+#define BLT_VARIABLE_SIZE_ITEMS		2
+
+typedef struct Blt_PoolStruct *Blt_Pool;
+
+typedef void *(Blt_PoolAllocProc) _ANSI_ARGS_((Blt_Pool pool, size_t size));
+typedef void (Blt_PoolFreeProc) _ANSI_ARGS_((Blt_Pool pool, void *item));
+
+struct Blt_PoolStruct {
+    Blt_PoolChain *headPtr;	/* Chain of malloc'ed chunks. */
+    Blt_PoolChain *freePtr; 	/* List of deleted items. This is only used
+				 * for fixed size items. */
+    size_t poolSize;		/* Log2 of # of items in the current block. */
+    size_t itemSize;		/* Size of an item. */
+    size_t bytesLeft;		/* # of bytes left in the current chunk. */
+    size_t waste;
+    
+    Blt_PoolAllocProc *allocProc;
+    Blt_PoolFreeProc *freeProc;
+};
+
+EXTERN Blt_Pool Blt_PoolCreate _ANSI_ARGS_((int type));
+EXTERN void Blt_PoolDestroy _ANSI_ARGS_((Blt_Pool pool));
+
+#define Blt_PoolAllocItem(poolPtr, n) (*((poolPtr)->allocProc))(poolPtr, n)
+#define Blt_PoolFreeItem(poolPtr, item) (*((poolPtr)->freeProc))(poolPtr, item)
+
+#endif /* BLT_POOL_H */
Index: trunk/kitgen/8.x/blt/generic/bltPs.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltPs.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltPs.c	(revision 175)
@@ -0,0 +1,1491 @@
+
+/*
+ * bltPs.c --
+ *
+ *      This module implements general PostScript conversion routines.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltPs.h"
+
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define PS_MAXPATH	1500	/* Maximum number of components in a PostScript
+				 * (level 1) path. */
+
+PsToken
+Blt_GetPsToken(interp, tkwin)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+{
+    struct PsTokenStruct *tokenPtr;
+
+    tokenPtr = Blt_Malloc(sizeof(struct PsTokenStruct));
+    assert(tokenPtr);
+
+    tokenPtr->fontVarName = tokenPtr->colorVarName = NULL;
+    tokenPtr->interp = interp;
+    tokenPtr->tkwin = tkwin;
+    tokenPtr->colorMode = PS_MODE_COLOR;
+    Tcl_DStringInit(&(tokenPtr->dString));
+    return tokenPtr;
+}
+
+void
+Blt_ReleasePsToken(tokenPtr)
+    struct PsTokenStruct *tokenPtr;
+{
+    Tcl_DStringFree(&(tokenPtr->dString));
+    Blt_Free(tokenPtr);
+}
+
+char *
+Blt_PostScriptFromToken(tokenPtr)
+    struct PsTokenStruct *tokenPtr;
+{
+    return Tcl_DStringValue(&(tokenPtr->dString));
+}
+
+char *
+Blt_ScratchBufferFromToken(tokenPtr)
+    struct PsTokenStruct *tokenPtr;
+{
+    return tokenPtr->scratchArr;
+}
+
+void
+Blt_AppendToPostScript
+TCL_VARARGS_DEF(PsToken, arg1)
+{
+    va_list argList;
+    struct PsTokenStruct *tokenPtr;
+    char *string;
+
+    tokenPtr = TCL_VARARGS_START(struct PsTokenStruct, arg1, argList);
+    for (;;) {
+	string = va_arg(argList, char *);
+	if (string == NULL) {
+	    break;
+	}
+	Tcl_DStringAppend(&(tokenPtr->dString), string, -1);
+    }
+}
+
+void
+Blt_FormatToPostScript
+TCL_VARARGS_DEF(PsToken, arg1)
+{
+    va_list argList;
+    struct PsTokenStruct *tokenPtr;
+    char *fmt;
+
+    tokenPtr = TCL_VARARGS_START(struct PsTokenStruct, arg1, argList);
+    fmt = va_arg(argList, char *);
+    vsprintf(tokenPtr->scratchArr, fmt, argList);
+    va_end(argList);
+    Tcl_DStringAppend(&(tokenPtr->dString), tokenPtr->scratchArr, -1);
+}
+
+int
+Blt_FileToPostScript(tokenPtr, fileName)
+    struct PsTokenStruct *tokenPtr;
+    char *fileName;
+{
+    Tcl_Channel channel;
+    Tcl_DString dString;
+    Tcl_Interp *interp;
+    char *buf;
+    char *libDir;
+    int nBytes;
+
+    interp = tokenPtr->interp;
+    buf = tokenPtr->scratchArr;
+
+    /*
+     * Read in a standard prolog file from file and append it to the
+     * PostScript output stored in the Tcl_DString in tokenPtr.
+     */
+
+    libDir = (char *)Tcl_GetVar(interp, "blt_library", TCL_GLOBAL_ONLY);
+    if (libDir == NULL) {
+	Tcl_AppendResult(interp, "couldn't find BLT script library:",
+	    "global variable \"blt_library\" doesn't exist", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppend(&dString, libDir, -1);
+    Tcl_DStringAppend(&dString, "/", -1);
+    Tcl_DStringAppend(&dString, fileName, -1);
+    fileName = Tcl_DStringValue(&dString);
+    Blt_AppendToPostScript(tokenPtr, "\n% including file \"", fileName, 
+	"\"\n\n", (char *)NULL);
+    channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+    if (channel == NULL) {
+	Tcl_AppendResult(interp, "couldn't open prologue file \"", fileName,
+		 "\": ", Tcl_PosixError(interp), (char *)NULL);
+	return TCL_ERROR;
+    }
+    for(;;) {
+	nBytes = Tcl_Read(channel, buf, PSTOKEN_BUFSIZ);
+	if (nBytes < 0) {
+	    Tcl_AppendResult(interp, "error reading prologue file \"", 
+		     fileName, "\": ", Tcl_PosixError(interp), 
+		     (char *)NULL);
+	    Tcl_Close(interp, channel);
+	    Tcl_DStringFree(&dString);
+	    return TCL_ERROR;
+	}
+	if (nBytes == 0) {
+	    break;
+	}
+	buf[nBytes] = '\0';
+	Blt_AppendToPostScript(tokenPtr, buf, (char *)NULL);
+    }
+    Tcl_DStringFree(&dString);
+    Tcl_Close(interp, channel);
+    return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * XColorToPostScript --
+ *
+ *	Convert the a XColor (from its RGB values) to a PostScript
+ *	command.  If a Tk color map variable exists, it will be
+ *	consulted for a PostScript translation based upon the color
+ *	name.
+ *
+ *	Maps an X color intensity (0 to 2^16-1) to a floating point
+ *      value [0..1].  Many versions of Tk don't properly handle the 
+ *	the lower 8 bits of the color intensity, so we can only 
+ *	consider the upper 8 bits. 
+ *
+ * Results:
+ *	The string representing the color mode is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+XColorToPostScript(tokenPtr, colorPtr)
+    struct PsTokenStruct *tokenPtr;
+    XColor *colorPtr;		/* Color value to be converted */
+{
+    /* 
+     * Shift off the lower byte before dividing because some versions
+     * of Tk don't fill the lower byte correctly.  
+     */
+    Blt_FormatToPostScript(tokenPtr, "%g %g %g",
+	((double)(colorPtr->red >> 8) / 255.0),
+	((double)(colorPtr->green >> 8) / 255.0),
+	((double)(colorPtr->blue >> 8) / 255.0));
+}
+
+void
+Blt_BackgroundToPostScript(tokenPtr, colorPtr)
+    struct PsTokenStruct *tokenPtr;
+    XColor *colorPtr;
+{
+    /* If the color name exists in Tcl array variable, use that translation */
+    if (tokenPtr->colorVarName != NULL) {
+	CONST char *psColor;
+
+	psColor = Tcl_GetVar2(tokenPtr->interp, tokenPtr->colorVarName,
+	    Tk_NameOfColor(colorPtr), 0);
+	if (psColor != NULL) {
+	    Blt_AppendToPostScript(tokenPtr, " ", psColor, "\n", (char *)NULL);
+	    return;
+	}
+    }
+    XColorToPostScript(tokenPtr, colorPtr);
+    Blt_AppendToPostScript(tokenPtr, " SetBgColor\n", (char *)NULL);
+}
+
+void
+Blt_ForegroundToPostScript(tokenPtr, colorPtr)
+    struct PsTokenStruct *tokenPtr;
+    XColor *colorPtr;
+{
+    /* If the color name exists in Tcl array variable, use that translation */
+    if (tokenPtr->colorVarName != NULL) {
+	CONST char *psColor;
+
+	psColor = Tcl_GetVar2(tokenPtr->interp, tokenPtr->colorVarName,
+	    Tk_NameOfColor(colorPtr), 0);
+	if (psColor != NULL) {
+	    Blt_AppendToPostScript(tokenPtr, " ", psColor, "\n", (char *)NULL);
+	    return;
+	}
+    }
+    XColorToPostScript(tokenPtr, colorPtr);
+    Blt_AppendToPostScript(tokenPtr, " SetFgColor\n", (char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReverseBits --
+ *
+ *	Convert a byte from a X image into PostScript image order.
+ *	This requires not only the nybbles to be reversed but also
+ *	their bit values.
+ *
+ * Results:
+ *	The converted byte is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+INLINE static unsigned char
+ReverseBits(byte)
+    register unsigned char byte;
+{
+    byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
+    byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
+    byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
+    return byte;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ByteToHex --
+ *
+ *	Convert a byte to its ASCII hexidecimal equivalent.
+ *
+ * Results:
+ *	The converted 2 ASCII character string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+INLINE static void
+ByteToHex(byte, string)
+    register unsigned char byte;
+    char *string;
+{
+    static char hexDigits[] = "0123456789ABCDEF";
+
+    string[0] = hexDigits[byte >> 4];
+    string[1] = hexDigits[byte & 0x0F];
+}
+
+#ifdef WIN32
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_BitmapDataToPostScript --
+ *
+ *      Output a PostScript image string of the given bitmap image.
+ *      It is assumed the image is one bit deep and a zero value
+ *      indicates an off-pixel.  To convert to PostScript, the bits
+ *      need to be reversed from the X11 image order.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *      The PostScript image string is appended.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_BitmapDataToPostScript(
+    struct PsTokenStruct *tokenPtr,
+    Display *display,
+    Pixmap bitmap,
+    int width, int height)
+{
+    register unsigned char byte;
+    register int x, y, bitPos;
+    unsigned long pixel;
+    int byteCount;
+    char string[10];
+    unsigned char *srcBits, *srcPtr;
+    int bytesPerRow;
+
+    srcBits = Blt_GetBitmapData(display, bitmap, width, height, &bytesPerRow);
+    if (srcBits == NULL) {
+        OutputDebugString("Can't get bitmap data");
+	return;
+    }
+    Blt_AppendToPostScript(tokenPtr, "\t<", (char *)NULL);
+    byteCount = bitPos = 0;	/* Suppress compiler warning */
+    for (y = height - 1; y >= 0; y--) {
+	srcPtr = srcBits + (bytesPerRow * y);
+	byte = 0;
+	for (x = 0; x < width; x++) {
+	    bitPos = x % 8;
+	    pixel = (*srcPtr & (0x80 >> bitPos));
+	    if (pixel) {
+		byte |= (unsigned char)(1 << bitPos);
+	    }
+	    if (bitPos == 7) {
+		byte = ReverseBits(byte);
+		ByteToHex(byte, string);
+		string[2] = '\0';
+		byteCount++;
+		srcPtr++;
+		byte = 0;
+		if (byteCount >= 30) {
+		    string[2] = '\n';
+		    string[3] = '\t';
+		    string[4] = '\0';
+		    byteCount = 0;
+		}
+		Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+	    }
+	}			/* x */
+	if (bitPos != 7) {
+	    byte = ReverseBits(byte);
+	    ByteToHex(byte, string);
+	    string[2] = '\0';
+	    Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+	    byteCount++;
+	}
+    }				/* y */
+    Blt_Free(srcBits);
+    Blt_AppendToPostScript(tokenPtr, ">\n", (char *)NULL);
+}
+
+#else
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_BitmapDataToPostScript --
+ *
+ *      Output a PostScript image string of the given bitmap image.
+ *      It is assumed the image is one bit deep and a zero value
+ *      indicates an off-pixel.  To convert to PostScript, the bits
+ *      need to be reversed from the X11 image order.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *      The PostScript image string is appended to interp->result.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height)
+    struct PsTokenStruct *tokenPtr;
+    Display *display;
+    Pixmap bitmap;
+    int width, height;
+{
+    register unsigned char byte = 0;
+    register int x, y, bitPos;
+    unsigned long pixel;
+    XImage *imagePtr;
+    int byteCount;
+    char string[10];
+
+    imagePtr = XGetImage(display, bitmap, 0, 0, width, height, 1, ZPixmap);
+    Blt_AppendToPostScript(tokenPtr, "\t<", (char *)NULL);
+    byteCount = bitPos = 0;	/* Suppress compiler warning */
+    for (y = 0; y < height; y++) {
+	byte = 0;
+	for (x = 0; x < width; x++) {
+	    pixel = XGetPixel(imagePtr, x, y);
+	    bitPos = x % 8;
+	    byte |= (unsigned char)(pixel << bitPos);
+	    if (bitPos == 7) {
+		byte = ReverseBits(byte);
+		ByteToHex(byte, string);
+		string[2] = '\0';
+		byteCount++;
+		byte = 0;
+		if (byteCount >= 30) {
+		    string[2] = '\n';
+		    string[3] = '\t';
+		    string[4] = '\0';
+		    byteCount = 0;
+		}
+		Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+	    }
+	}			/* x */
+	if (bitPos != 7) {
+	    byte = ReverseBits(byte);
+	    ByteToHex(byte, string);
+	    string[2] = '\0';
+	    Blt_AppendToPostScript(tokenPtr, string, (char *)NULL);
+	    byteCount++;
+	}
+    }				/* y */
+    Blt_AppendToPostScript(tokenPtr, ">\n", (char *)NULL);
+    XDestroyImage(imagePtr);
+}
+
+#endif /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPsData --
+ *
+ *	Converts a color image to PostScript RGB (3 components)
+ *	or Greyscale (1 component) output.  With 3 components, we
+ *	assume the "colorimage" operator is available.
+ *
+ *	Note that the image converted from bottom to top, to conform
+ *	to the PostScript coordinate system.
+ *
+ * Results:
+ *	The PostScript data comprising the color image is written
+ *	into the dynamic string.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_ColorImageToPsData(image, nComponents, resultPtr, prefix)
+    Blt_ColorImage image;
+    int nComponents;
+    Tcl_DString *resultPtr;
+    char *prefix;
+{
+    char string[10];
+    register int count;
+    register int x, y;
+    register Pix32 *pixelPtr;
+    unsigned char byte;
+    int width, height;
+    int offset;
+    int nLines;
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+
+    nLines = 0;
+    count = 0;
+    offset = (height - 1) * width;
+    if (nComponents == 3) {
+	for (y = (height - 1); y >= 0; y--) {
+	    pixelPtr = Blt_ColorImageBits(image) + offset;
+	    for (x = 0; x < width; x++, pixelPtr++) {
+		if (count == 0) {
+		    Tcl_DStringAppend(resultPtr, prefix, -1);
+		    Tcl_DStringAppend(resultPtr, " ", -1);
+		}
+		count += 6;
+		ByteToHex(pixelPtr->Red, string);
+		ByteToHex(pixelPtr->Green, string + 2);
+		ByteToHex(pixelPtr->Blue, string + 4);
+		string[6] = '\0';
+		if (count >= 60) {
+		    string[6] = '\n';
+		    string[7] = '\0';
+		    count = 0;
+		    nLines++;
+		}
+		Tcl_DStringAppend(resultPtr, string, -1);
+	    }
+	    offset -= width;
+	}
+    } else if (nComponents == 1) {
+	for (y = (height - 1); y >= 0; y--) {
+	    pixelPtr = Blt_ColorImageBits(image) + offset;
+	    for (x = 0; x < width; x++, pixelPtr++) {
+		if (count == 0) {
+		    Tcl_DStringAppend(resultPtr, prefix, -1);
+		    Tcl_DStringAppend(resultPtr, " ", -1);
+		}
+		count += 2;
+		byte = ~(pixelPtr->Red);
+		ByteToHex(byte, string);
+		string[2] = '\0';
+		if (count >= 60) {
+		    string[2] = '\n';
+		    string[3] = '\0';
+		    count = 0;
+		    nLines++;
+		}
+		Tcl_DStringAppend(resultPtr, string, -1);
+	    }
+	    offset -= width;
+	}
+    }
+    if (count != 0) {
+	Tcl_DStringAppend(resultPtr, "\n", -1);
+	nLines++;
+    }
+    return nLines;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfAtom --
+ *
+ *	Wrapper routine for Tk_GetAtomName.  Returns NULL instead of
+ *	"?bad atom?" if the atom can't be found.
+ *
+ * Results:
+ *	The name of the atom is returned if found. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfAtom(tkwin, atom)
+    Tk_Window tkwin;
+    Atom atom;
+{
+    char *result;
+
+    result = Tk_GetAtomName(tkwin, atom);
+    if ((result[0] == '?') && (strcmp(result, "?bad atom?") == 0)) {
+	return NULL;
+    }
+    return result;
+}
+
+
+typedef struct {
+    char *alias;
+    char *fontName;
+} FontMap;
+
+static FontMap psFontMap[] =
+{
+    {"Arial", "Helvetica",},
+    {"AvantGarde", "AvantGarde",},
+    {"Courier New", "Courier",},
+    {"Courier", "Courier",},
+    {"Geneva", "Helvetica",},
+    {"Helvetica", "Helvetica",},
+    {"Monaco", "Courier",},
+    {"New Century Schoolbook", "NewCenturySchlbk",},
+    {"New York", "Times",},
+    {"Palatino", "Palatino",},
+    {"Symbol", "Symbol",},
+    {"Times New Roman", "Times",},
+    {"Times Roman", "Times",},
+    {"Times", "Times",},
+    {"Utopia", "Utopia",},
+    {"ZapfChancery", "ZapfChancery",},
+    {"ZapfDingbats", "ZapfDingbats",},
+};
+
+static int nFontNames = (sizeof(psFontMap) / sizeof(FontMap));
+
+#ifndef  WIN32
+/*
+ * -----------------------------------------------------------------
+ *
+ * XFontStructToPostScript --
+ *
+ *      Map X11 font to a PostScript font. Currently, only fonts whose
+ *      FOUNDRY property are "Adobe" are converted. Simply gets the
+ *      XA_FULL_NAME and XA_FAMILY properties and pieces together a
+ *      PostScript fontname.
+ *
+ * Results:
+ *      Returns the mapped PostScript font name if one is possible.
+ *	Otherwise returns NULL.
+ *
+ * -----------------------------------------------------------------
+ */
+static char *
+XFontStructToPostScript(tkwin, fontPtr)
+    Tk_Window tkwin;		/* Window to query for atoms */
+    XFontStruct *fontPtr;	/* Font structure to map to name */
+{
+    Atom atom;
+    char *fullName, *family, *foundry;
+    register char *src, *dest;
+    int familyLen;
+    char *start;
+    static char string[200];	/* What size? */
+
+    if (XGetFontProperty(fontPtr, XA_FULL_NAME, &atom) == False) {
+	return NULL;
+    }
+    fullName = NameOfAtom(tkwin, atom);
+    if (fullName == NULL) {
+	return NULL;
+    }
+    family = foundry = NULL;
+    if (XGetFontProperty(fontPtr, Tk_InternAtom(tkwin, "FOUNDRY"), &atom)) {
+	foundry = NameOfAtom(tkwin, atom);
+    }
+    if (XGetFontProperty(fontPtr, XA_FAMILY_NAME, &atom)) {
+	family = NameOfAtom(tkwin, atom);
+    }
+    /*
+     * Try to map the font only if the foundry is Adobe
+     */
+    if ((foundry == NULL) || (family == NULL)) {
+	return NULL;
+    }
+    src = NULL;
+    familyLen = strlen(family);
+    if (strncasecmp(fullName, family, familyLen) == 0) {
+	src = fullName + familyLen;
+    }
+    if (strcmp(foundry, "Adobe") != 0) {
+	register int i;
+
+	if (strncasecmp(family, "itc ", 4) == 0) {
+	    family += 4;	/* Throw out the "itc" prefix */
+	}
+	for (i = 0; i < nFontNames; i++) {
+	    if (strcasecmp(family, psFontMap[i].alias) == 0) {
+		family = psFontMap[i].fontName;
+	    }
+	}
+	if (i == nFontNames) {
+	    family = "Helvetica";	/* Default to a known font */
+	}
+    }
+    /*
+     * PostScript font name is in the form <family>-<type face>
+     */
+    sprintf(string, "%s-", family);
+    dest = start = string + strlen(string);
+
+    /*
+     * Append the type face (part of the full name trailing the family name)
+     * to the the PostScript font name, removing any spaces or dashes
+     *
+     * ex. " Bold Italic" ==> "BoldItalic"
+     */
+    if (src != NULL) {
+	while (*src != '\0') {
+	    if ((*src != ' ') && (*src != '-')) {
+		*dest++ = *src;
+	    }
+	    src++;
+	}
+    }
+    if (dest == start) {
+	--dest;			/* Remove '-' to leave just the family name */
+    }
+    *dest = '\0';		/* Make a valid string */
+    return string;
+}
+
+#endif /* !WIN32 */
+
+
+
+/*
+ * -------------------------------------------------------------------
+ * Routines to convert X drawing functions to PostScript commands.
+ * -------------------------------------------------------------------
+ */
+void
+Blt_ClearBackgroundToPostScript(tokenPtr)
+    struct PsTokenStruct *tokenPtr;
+{
+    Blt_AppendToPostScript(tokenPtr, 
+	" 1.0 1.0 1.0 SetBgColor\n", 
+	(char *)NULL);
+}
+
+void
+Blt_CapStyleToPostScript(tokenPtr, capStyle)
+    struct PsTokenStruct *tokenPtr;
+    int capStyle;
+{
+    /*
+     * X11:not last = 0, butt = 1, round = 2, projecting = 3
+     * PS: butt = 0, round = 1, projecting = 2
+     */
+    if (capStyle > 0) {
+	capStyle--;
+    }
+    Blt_FormatToPostScript(tokenPtr, 
+	"%d setlinecap\n", 
+	capStyle);
+}
+
+void
+Blt_JoinStyleToPostScript(tokenPtr, joinStyle)
+    struct PsTokenStruct *tokenPtr;
+    int joinStyle;
+{
+    /*
+     * miter = 0, round = 1, bevel = 2
+     */
+    Blt_FormatToPostScript(tokenPtr, 
+	"%d setlinejoin\n", 
+	joinStyle);
+}
+
+void
+Blt_LineWidthToPostScript(tokenPtr, lineWidth)
+    struct PsTokenStruct *tokenPtr;
+    int lineWidth;
+{
+    if (lineWidth < 1) {
+	lineWidth = 1;
+    }
+    Blt_FormatToPostScript(tokenPtr, 
+	"%d setlinewidth\n", 
+	lineWidth);
+}
+
+void
+Blt_LineDashesToPostScript(tokenPtr, dashesPtr)
+    struct PsTokenStruct *tokenPtr;
+    Blt_Dashes *dashesPtr;
+{
+
+    Blt_AppendToPostScript(tokenPtr, "[ ", (char *)NULL);
+    if (dashesPtr != NULL) {
+	unsigned char *p;
+
+	for (p = dashesPtr->values; *p != 0; p++) {
+	    Blt_FormatToPostScript(tokenPtr, " %d", *p);
+	}
+    }
+    Blt_AppendToPostScript(tokenPtr, "] 0 setdash\n", (char *)NULL);
+}
+
+void
+Blt_LineAttributesToPostScript(tokenPtr, colorPtr, lineWidth, dashesPtr,
+    capStyle, joinStyle)
+    struct PsTokenStruct *tokenPtr;
+    XColor *colorPtr;
+    int lineWidth;
+    Blt_Dashes *dashesPtr;
+    int capStyle, joinStyle;
+{
+    Blt_JoinStyleToPostScript(tokenPtr, joinStyle);
+    Blt_CapStyleToPostScript(tokenPtr, capStyle);
+    Blt_ForegroundToPostScript(tokenPtr, colorPtr);
+    Blt_LineWidthToPostScript(tokenPtr, lineWidth);
+    Blt_LineDashesToPostScript(tokenPtr, dashesPtr);
+    Blt_AppendToPostScript(tokenPtr, "/DashesProc {} def\n", (char *)NULL);
+}
+
+void
+Blt_RectangleToPostScript(tokenPtr, x, y, width, height)
+    struct PsTokenStruct *tokenPtr;
+    double x, y;
+    int width, height;
+{
+    Blt_FormatToPostScript(tokenPtr, 
+	"%g %g %d %d Box fill\n\n", 
+	x, y, width, height);
+}
+
+void
+Blt_RegionToPostScript(tokenPtr, x, y, width, height)
+    struct PsTokenStruct *tokenPtr;
+    double x, y;
+    int width, height;
+{
+    Blt_FormatToPostScript(tokenPtr, "%g %g %d %d Box\n\n", 
+			   x, y, width, height);
+}
+
+void
+Blt_PathToPostScript(tokenPtr, screenPts, nScreenPts)
+    struct PsTokenStruct *tokenPtr;
+    register Point2D *screenPts;
+    int nScreenPts;
+{
+    register Point2D *pointPtr, *endPtr;
+
+    pointPtr = screenPts;
+    Blt_FormatToPostScript(tokenPtr, "newpath %g %g moveto\n", 
+	pointPtr->x, pointPtr->y);
+    pointPtr++;
+    endPtr = screenPts + nScreenPts;
+    while (pointPtr < endPtr) {
+	Blt_FormatToPostScript(tokenPtr, "%g %g lineto\n",
+		pointPtr->x, pointPtr->y);
+	pointPtr++;
+    }
+}
+
+void
+Blt_PolygonToPostScript(tokenPtr, screenPts, nScreenPts)
+    struct PsTokenStruct *tokenPtr;
+    Point2D *screenPts;
+    int nScreenPts;
+{
+    Blt_PathToPostScript(tokenPtr, screenPts, nScreenPts);
+    Blt_FormatToPostScript(tokenPtr, "%g %g ", screenPts[0].x, screenPts[0].y);
+    Blt_AppendToPostScript(tokenPtr, " lineto closepath Fill\n", (char *)NULL);
+}
+
+void
+Blt_SegmentsToPostScript(tokenPtr, segPtr, nSegments)
+    struct PsTokenStruct *tokenPtr;
+    register XSegment *segPtr;
+    int nSegments;
+{
+    register int i;
+
+    for (i = 0; i < nSegments; i++, segPtr++) {
+	Blt_FormatToPostScript(tokenPtr, "%d %d moveto\n", 
+			       segPtr->x1, segPtr->y1);
+	Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n", 
+			       segPtr->x2, segPtr->y2);
+	Blt_AppendToPostScript(tokenPtr, "DashesProc stroke\n", (char *)NULL);
+    }
+}
+
+
+void
+Blt_RectanglesToPostScript(tokenPtr, rectArr, nRects)
+    struct PsTokenStruct *tokenPtr;
+    XRectangle rectArr[];
+    int nRects;
+{
+    register int i;
+
+    for (i = 0; i < nRects; i++) {
+	Blt_RectangleToPostScript(tokenPtr, 
+		  (double)rectArr[i].x, (double)rectArr[i].y, 
+		  (int)rectArr[i].width, (int)rectArr[i].height);
+    }
+}
+
+#ifndef TK_RELIEF_SOLID
+#define TK_RELIEF_SOLID		-1	/* Set the an impossible value. */
+#endif /* TK_RELIEF_SOLID */
+
+void
+Blt_Draw3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+    borderWidth, relief)
+    struct PsTokenStruct *tokenPtr;
+    Tk_3DBorder border;		/* Token for border to draw. */
+    double x, y;		/* Coordinates of rectangle */
+    int width, height;		/* Region to be drawn. */
+    int borderWidth;		/* Desired width for border, in pixels. */
+    int relief;			/* Should be either TK_RELIEF_RAISED or
+                                 * TK_RELIEF_SUNKEN;  indicates position of
+                                 * interior of window relative to exterior. */
+{
+    TkBorder *borderPtr = (TkBorder *) border;
+    XColor lightColor, darkColor;
+    XColor *lightColorPtr, *darkColorPtr;
+    XColor *topColor, *bottomColor;
+    Point2D points[7];
+    int twiceWidth = (borderWidth * 2);
+
+    if ((width < twiceWidth) || (height < twiceWidth)) {
+	return;
+    }
+    if ((relief == TK_RELIEF_SOLID) ||
+	(borderPtr->lightColor == NULL) || (borderPtr->darkColor == NULL)) {
+	if (relief == TK_RELIEF_SOLID) {
+	    darkColor.red = darkColor.blue = darkColor.green = 0x00;
+	    lightColor.red = lightColor.blue = lightColor.green = 0x00;
+	    relief = TK_RELIEF_SUNKEN;
+	} else {
+	    Screen *screenPtr;
+
+	    lightColor = *borderPtr->bgColor;
+	    screenPtr = Tk_Screen(tokenPtr->tkwin);
+	    if (lightColor.pixel == WhitePixelOfScreen(screenPtr)) {
+		darkColor.red = darkColor.blue = darkColor.green = 0x00;
+	    } else {
+		darkColor.red = darkColor.blue = darkColor.green = 0xFF;
+	    }
+	}
+	lightColorPtr = &lightColor;
+	darkColorPtr = &darkColor;
+    } else {
+	lightColorPtr = borderPtr->lightColor;
+	darkColorPtr = borderPtr->darkColor;
+    }
+
+
+    /*
+     * Handle grooves and ridges with recursive calls.
+     */
+
+    if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
+	int halfWidth, insideOffset;
+
+	halfWidth = borderWidth / 2;
+	insideOffset = borderWidth - halfWidth;
+	Blt_Draw3DRectangleToPostScript(tokenPtr, border, (double)x, (double)y,
+	    width, height, halfWidth, 
+	    (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+	Blt_Draw3DRectangleToPostScript(tokenPtr, border, 
+  	    (double)(x + insideOffset), (double)(y + insideOffset), 
+	    width - insideOffset * 2, height - insideOffset * 2, halfWidth,
+	    (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED : TK_RELIEF_SUNKEN);
+	return;
+    }
+    if (relief == TK_RELIEF_RAISED) {
+	topColor = lightColorPtr;
+	bottomColor = darkColorPtr;
+    } else if (relief == TK_RELIEF_SUNKEN) {
+	topColor = darkColorPtr;
+	bottomColor = lightColorPtr;
+    } else {
+	topColor = bottomColor = borderPtr->bgColor;
+    }
+    Blt_BackgroundToPostScript(tokenPtr, bottomColor);
+    Blt_RectangleToPostScript(tokenPtr, x, y + height - borderWidth, width,
+	borderWidth);
+    Blt_RectangleToPostScript(tokenPtr, x + width - borderWidth, y,
+	borderWidth, height);
+    points[0].x = points[1].x = points[6].x = x;
+    points[0].y = points[6].y = y + height;
+    points[1].y = points[2].y = y;
+    points[2].x = x + width;
+    points[3].x = x + width - borderWidth;
+    points[3].y = points[4].y = y + borderWidth;
+    points[4].x = points[5].x = x + borderWidth;
+    points[5].y = y + height - borderWidth;
+    if (relief != TK_RELIEF_FLAT) {
+	Blt_BackgroundToPostScript(tokenPtr, topColor);
+    }
+    Blt_PolygonToPostScript(tokenPtr, points, 7);
+}
+
+void
+Blt_Fill3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+    borderWidth, relief)
+    struct PsTokenStruct *tokenPtr;
+    Tk_3DBorder border;		/* Token for border to draw. */
+    double x, y;		/* Coordinates of top-left of border area */
+    int width, height;		/* Dimension of border to be drawn. */
+    int borderWidth;		/* Desired width for border, in pixels. */
+    int relief;			/* Should be either TK_RELIEF_RAISED or
+                                 * TK_RELIEF_SUNKEN;  indicates position of
+                                 * interior of window relative to exterior. */
+{
+    TkBorder *borderPtr = (TkBorder *) border;
+
+    /*
+     * I'm assuming that the rectangle is to be drawn as a background.
+     * Setting the pen color as foreground or background only affects
+     * the plot when the colormode option is "monochrome".
+     */
+    Blt_BackgroundToPostScript(tokenPtr, borderPtr->bgColor);
+    Blt_RectangleToPostScript(tokenPtr, x, y, width, height);
+    Blt_Draw3DRectangleToPostScript(tokenPtr, border, x, y, width, height,
+	borderWidth, relief);
+}
+
+void
+Blt_StippleToPostScript(tokenPtr, display, bitmap)
+    struct PsTokenStruct *tokenPtr;
+    Display *display;
+    Pixmap bitmap;
+{
+    int width, height;
+
+    Tk_SizeOfBitmap(display, bitmap, &width, &height);
+    Blt_FormatToPostScript(tokenPtr, 
+	"gsave\n  clip\n  %d %d\n", 
+	width, height);
+    Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height);
+    Blt_AppendToPostScript(tokenPtr, 
+	"  StippleFill\ngrestore\n", 
+	(char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPostScript --
+ *
+ *      Translates a color image into 3 component RGB PostScript output.
+ *	Uses PS Language Level 2 operator "colorimage".
+ *
+ * Results:
+ *      The dynamic string will contain the PostScript output.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ColorImageToPostScript(tokenPtr, image, x, y)
+    struct PsTokenStruct *tokenPtr;
+    Blt_ColorImage image;
+    double x, y;
+{
+    int width, height;
+    int tmpSize;
+
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+
+    tmpSize = width;
+    if (tokenPtr->colorMode == PS_MODE_COLOR) {
+	tmpSize *= 3;
+    }
+    Blt_FormatToPostScript(tokenPtr, "\n/tmpStr %d string def\n", tmpSize);
+    Blt_AppendToPostScript(tokenPtr, "gsave\n", (char *)NULL);
+    Blt_FormatToPostScript(tokenPtr, "  %g %g translate\n", x, y);
+    Blt_FormatToPostScript(tokenPtr, "  %d %d scale\n", width, height);
+    Blt_FormatToPostScript(tokenPtr, "  %d %d 8\n", width, height);
+    Blt_FormatToPostScript(tokenPtr, "  [%d 0 0 %d 0 %d] ", width, -height,
+	height);
+    Blt_AppendToPostScript(tokenPtr, 
+	"{\n    currentfile tmpStr readhexstring pop\n  } ",
+	(char *)NULL);
+    if (tokenPtr->colorMode != PS_MODE_COLOR) {
+	Blt_AppendToPostScript(tokenPtr, "image\n", (char *)NULL);
+	Blt_ColorImageToGreyscale(image);
+	Blt_ColorImageToPsData(image, 1, &(tokenPtr->dString), " ");
+    } else {
+	Blt_AppendToPostScript(tokenPtr, 
+		"false 3 colorimage\n", 
+		(char *)NULL);
+	Blt_ColorImageToPsData(image, 3, &(tokenPtr->dString), " ");
+    }
+    Blt_AppendToPostScript(tokenPtr, 
+	"\ngrestore\n\n", 
+	(char *)NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_WindowToPostScript --
+ *
+ *      Converts a Tk window to PostScript.  If the window could not
+ *	be "snapped", then a grey rectangle is drawn in its place.
+ *
+ * Results:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_WindowToPostScript(tokenPtr, tkwin, x, y)
+    struct PsTokenStruct *tokenPtr;
+    Tk_Window tkwin;
+    double x, y;
+{
+    Blt_ColorImage image;
+    int width, height;
+
+    width = Tk_Width(tkwin);
+    height = Tk_Height(tkwin);
+    image = Blt_DrawableToColorImage(tkwin, Tk_WindowId(tkwin), 0, 0, width, 
+	height, GAMMA);
+    if (image == NULL) {
+	/* Can't grab window image so paint the window area grey */
+	Blt_AppendToPostScript(tokenPtr, "% Can't grab window \"",
+	    Tk_PathName(tkwin), "\"\n", (char *)NULL);
+	Blt_AppendToPostScript(tokenPtr, "0.5 0.5 0.5 SetBgColor\n",
+	    (char *)NULL);
+	Blt_RectangleToPostScript(tokenPtr, x, y, width, height);
+	return;
+    }
+    Blt_ColorImageToPostScript(tokenPtr, image, x, y);
+    Blt_FreeColorImage(image);
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * Blt_PhotoToPostScript --
+ *
+ *      Output a PostScript image string of the given photo image.
+ *	The photo is first converted into a color image and then
+ *	translated into PostScript.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *      The PostScript output representing the photo is appended to
+ *	the tokenPtr's dynamic string.
+ *
+ * -------------------------------------------------------------------------
+ */
+void
+Blt_PhotoToPostScript(tokenPtr, photo, x, y)
+    struct PsTokenStruct *tokenPtr;
+    Tk_PhotoHandle photo;
+    double x, y;		/* Origin of photo image */
+{
+    Blt_ColorImage image;
+
+    image = Blt_PhotoToColorImage(photo);
+    Blt_ColorImageToPostScript(tokenPtr, image, x, y);
+    Blt_FreeColorImage(image);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_FontToPostScript --
+ *
+ *      Map the Tk font to a PostScript font and point size.
+ *
+ *	If a Tcl array variable was specified, each element should be
+ *	indexed by the X11 font name and contain a list of 1-2
+ *	elements; the PostScript font name and the desired point size.
+ *	The point size may be omitted and the X font point size will
+ *	be used.
+ *
+ *	Otherwise, if the foundry is "Adobe", we try to do a plausible
+ *	mapping looking at the full name of the font and building a
+ *	string in the form of "Family-TypeFace".
+ *
+ * Returns:
+ *      None.
+ *
+ * Side Effects:
+ *      PostScript commands are output to change the type and the
+ *      point size of the current font.
+ *
+ * -----------------------------------------------------------------
+ */
+
+void
+Blt_FontToPostScript(tokenPtr, font)
+    struct PsTokenStruct *tokenPtr;
+    Tk_Font font;		/* Tk font to query about */
+{
+    XFontStruct *fontPtr = (XFontStruct *)font;
+    Tcl_Interp *interp = tokenPtr->interp;
+    char *fontName;
+    double pointSize;
+#if (TK_MAJOR_VERSION > 4)
+    Tk_Uid family;
+    register int i;
+#endif /* TK_MAJOR_VERSION > 4 */
+
+    fontName = Tk_NameOfFont(font);
+    pointSize = 12.0;
+    /*
+     * Use the font variable information if it exists.
+     */
+    if (tokenPtr->fontVarName != NULL) {
+	char *fontInfo;
+
+	fontInfo = (char *)Tcl_GetVar2(interp, tokenPtr->fontVarName, fontName,
+	       0);
+	if (fontInfo != NULL) {
+	    int nProps;
+	    char **propArr = NULL;
+
+	    if (Tcl_SplitList(interp, fontInfo, &nProps, &propArr) == TCL_OK) {
+		int newSize;
+
+		fontName = propArr[0];
+		if ((nProps == 2) &&
+		    (Tcl_GetInt(interp, propArr[1], &newSize) == TCL_OK)) {
+		    pointSize = (double)newSize;
+		}
+	    }
+	    Blt_FormatToPostScript(tokenPtr, 
+				   "%g /%s SetFont\n", 
+				   pointSize, fontName);
+	    if (propArr != (char **)NULL) {
+		Blt_Free(propArr);
+	    }
+	    return;
+	}
+    }
+#if (TK_MAJOR_VERSION > 4)
+
+    /*
+     * Otherwise do a quick test to see if it's a PostScript font.
+     * Tk_PostScriptFontName will silently generate a bogus PostScript
+     * font description, so we have to check to see if this is really a
+     * PostScript font.
+     */
+    family = ((TkFont *) fontPtr)->fa.family;
+    for (i = 0; i < nFontNames; i++) {
+	if (strncasecmp(psFontMap[i].alias, family, strlen(psFontMap[i].alias))
+		 == 0) {
+	    Tcl_DString dString;
+
+	    Tcl_DStringInit(&dString);
+	    pointSize = (double)Tk_PostscriptFontName(font, &dString);
+	    fontName = Tcl_DStringValue(&dString);
+	    Blt_FormatToPostScript(tokenPtr, "%g /%s SetFont\n", pointSize,
+		fontName);
+	    Tcl_DStringFree(&dString);
+	    return;
+	}
+    }
+
+#endif /* TK_MAJOR_VERSION > 4 */
+
+    /*
+     * Can't find it. Try to use the current point size.
+     */
+    fontName = NULL;
+    pointSize = 12.0;
+
+#ifndef  WIN32
+#if (TK_MAJOR_VERSION > 4)
+    /* Can you believe what I have to go through to get an XFontStruct? */
+    fontPtr = XLoadQueryFont(Tk_Display(tokenPtr->tkwin), Tk_NameOfFont(font));
+#endif
+    if (fontPtr != NULL) {
+	unsigned long fontProp;
+
+	if (XGetFontProperty(fontPtr, XA_POINT_SIZE, &fontProp) != False) {
+	    pointSize = (double)fontProp / 10.0;
+	}
+	fontName = XFontStructToPostScript(tokenPtr->tkwin, fontPtr);
+#if (TK_MAJOR_VERSION > 4)
+	XFreeFont(Tk_Display(tokenPtr->tkwin), fontPtr);
+#endif /* TK_MAJOR_VERSION > 4 */
+    }
+#endif /* !WIN32 */
+    if ((fontName == NULL) || (fontName[0] == '\0')) {
+	fontName = "Helvetica-Bold";	/* Defaulting to a known PS font */
+    }
+    Blt_FormatToPostScript(tokenPtr, "%g /%s SetFont\n", pointSize, fontName);
+}
+
+static void
+TextLayoutToPostScript(tokenPtr, x, y, textPtr)
+    struct PsTokenStruct *tokenPtr;
+    int x, y;
+    TextLayout *textPtr;
+{
+    char *src, *dst, *end;
+    int count;			/* Counts the # of bytes written to
+				 * the intermediate scratch buffer. */
+    TextFragment *fragPtr;
+    int i;
+    unsigned char c;
+#if HAVE_UTF
+    Tcl_UniChar ch;
+#endif
+    int limit;
+
+    limit = PSTOKEN_BUFSIZ - 4; /* High water mark for the scratch
+				   * buffer. */
+    fragPtr = textPtr->fragArr;
+    for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
+	if (fragPtr->count < 1) {
+	    continue;
+	}
+	Blt_AppendToPostScript(tokenPtr, "(", (char *)NULL);
+	count = 0;
+	dst = tokenPtr->scratchArr;
+	src = fragPtr->text;
+	end = fragPtr->text + fragPtr->count;
+	while (src < end) {
+	    if (count > limit) {
+		/* Don't let the scatch buffer overflow */
+		dst = tokenPtr->scratchArr;
+		dst[count] = '\0';
+		Blt_AppendToPostScript(tokenPtr, dst, (char *)NULL);
+		count = 0;
+	    }
+#if HAVE_UTF
+	    /*
+	     * INTL: For now we just treat the characters as binary
+	     * data and display the lower byte.  Eventually this should
+	     * be revised to handle international postscript fonts.
+	     */
+	    src += Tcl_UtfToUniChar(src, &ch);
+	    c = (unsigned char)(ch & 0xff);
+#else 
+	    c = *src++;
+#endif
+
+	    if ((c == '\\') || (c == '(') || (c == ')')) {
+		/*
+		 * If special PostScript characters characters "\", "(",
+		 * and ")" are contained in the text string, prepend
+		 * backslashes to them.
+		 */
+		*dst++ = '\\';
+		*dst++ = c;
+		count += 2;
+	    } else if ((c < ' ') || (c > '~')) {
+		/* 
+		 * Present non-printable characters in their octal
+		 * representation.
+		 */
+		sprintf(dst, "\\%03o", c);
+		dst += 4;
+		count += 4;
+	    } else {
+		*dst++ = c;
+		count++;
+	    }
+	}
+	tokenPtr->scratchArr[count] = '\0';
+	Blt_AppendToPostScript(tokenPtr, tokenPtr->scratchArr, (char *)NULL);
+	Blt_FormatToPostScript(tokenPtr, ") %d %d %d DrawAdjText\n",
+	    fragPtr->width, x + fragPtr->x, y + fragPtr->y);
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TextToPostScript --
+ *
+ *      Output PostScript commands to print a text string. The string
+ *      may be rotated at any arbitrary angle, and placed according
+ *      the anchor type given. The anchor indicates how to interpret
+ *      the window coordinates as an anchor for the text bounding box.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *      Text string is drawn using the given font and GC on the graph
+ *      window at the given coordinates, anchor, and rotation
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_TextToPostScript(tokenPtr, string, tsPtr, x, y)
+    struct PsTokenStruct *tokenPtr;
+    char *string;		/* String to convert to PostScript */
+    TextStyle *tsPtr;		/* Text attribute information */
+    double x, y;		/* Window coordinates where to print text */
+{
+    double theta;
+    double rotWidth, rotHeight;
+    TextLayout *textPtr;
+    Point2D anchorPos;
+
+    if ((string == NULL) || (*string == '\0')) { /* Empty string, do nothing */
+	return;
+    }
+    theta = FMOD(tsPtr->theta, (double)360.0);
+    textPtr = Blt_GetTextLayout(string, tsPtr);
+    Blt_GetBoundingBox(textPtr->width, textPtr->height, theta, &rotWidth, 
+		       &rotHeight, (Point2D *)NULL);
+    /*
+     * Find the center of the bounding box
+     */
+    anchorPos.x = x, anchorPos.y = y;
+    anchorPos = Blt_TranslatePoint(&anchorPos, ROUND(rotWidth), 
+	ROUND(rotHeight), tsPtr->anchor);
+    anchorPos.x += (rotWidth * 0.5);
+    anchorPos.y += (rotHeight * 0.5);
+
+    /* Initialize text (sets translation and rotation) */
+    Blt_FormatToPostScript(tokenPtr, "%d %d %g %g %g BeginText\n", 
+	textPtr->width, textPtr->height, tsPtr->theta, anchorPos.x, 
+	anchorPos.y);
+
+    Blt_FontToPostScript(tokenPtr, tsPtr->font);
+
+    /* All coordinates are now relative to what was set by BeginText */
+    if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+	Blt_ForegroundToPostScript(tokenPtr, tsPtr->shadow.color);
+	TextLayoutToPostScript(tokenPtr, tsPtr->shadow.offset, 
+	       tsPtr->shadow.offset, textPtr);
+    }
+    Blt_ForegroundToPostScript(tokenPtr, (tsPtr->state & STATE_ACTIVE)
+	? tsPtr->activeColor : tsPtr->color);
+    TextLayoutToPostScript(tokenPtr, 0, 0, textPtr);
+    Blt_Free(textPtr);
+    Blt_AppendToPostScript(tokenPtr, "EndText\n", (char *)NULL);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_LineToPostScript --
+ *
+ *      Outputs PostScript commands to print a multi-segmented line.
+ *      It assumes a procedure DashesProc was previously defined.
+ *
+ * Results:
+ *      None.
+ *
+ * Side Effects:
+ *      Segmented line is printed.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_LineToPostScript(tokenPtr, pointPtr, nPoints)
+    struct PsTokenStruct *tokenPtr;
+    register XPoint *pointPtr;
+    int nPoints;
+{
+    register int i;
+
+    if (nPoints <= 0) {
+	return;
+    }
+    Blt_FormatToPostScript(tokenPtr, " newpath %d %d moveto\n", 
+			   pointPtr->x, pointPtr->y);
+    pointPtr++;
+    for (i = 1; i < (nPoints - 1); i++, pointPtr++) {
+	Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n", 
+			       pointPtr->x, pointPtr->y);
+	if ((i % PS_MAXPATH) == 0) {
+	    Blt_FormatToPostScript(tokenPtr,
+		"DashesProc stroke\n newpath  %d %d moveto\n", 
+				   pointPtr->x, pointPtr->y);
+	}
+    }
+    Blt_FormatToPostScript(tokenPtr, " %d %d lineto\n", 
+			   pointPtr->x, pointPtr->y);
+    Blt_AppendToPostScript(tokenPtr, "DashesProc stroke\n", (char *)NULL);
+}
+
+void
+Blt_BitmapToPostScript(tokenPtr, display, bitmap, scaleX, scaleY)
+    struct PsTokenStruct *tokenPtr;
+    Display *display;
+    Pixmap bitmap;		/* Bitmap to be converted to PostScript */
+    double scaleX, scaleY;
+{
+    int width, height;
+    double scaledWidth, scaledHeight;
+
+    Tk_SizeOfBitmap(display, bitmap, &width, &height);
+    scaledWidth = (double)width * scaleX;
+    scaledHeight = (double)height * scaleY;
+    Blt_AppendToPostScript(tokenPtr, "  gsave\n", (char *)NULL);
+    Blt_FormatToPostScript(tokenPtr, "    %g %g translate\n", 
+			   scaledWidth * -0.5, scaledHeight * 0.5);
+    Blt_FormatToPostScript(tokenPtr, "    %g %g scale\n", 
+			   scaledWidth, -scaledHeight);
+    Blt_FormatToPostScript(tokenPtr, "    %d %d true [%d 0 0 %d 0 %d] {", 
+			   width, height, width, -height, height);
+    Blt_BitmapDataToPostScript(tokenPtr, display, bitmap, width, height);
+    Blt_AppendToPostScript(tokenPtr, "    } imagemask\n  grestore\n",
+	(char *)NULL);
+}
+
+void
+Blt_2DSegmentsToPostScript(psToken, segPtr, nSegments)
+    PsToken psToken;
+    register Segment2D *segPtr;
+    int nSegments;
+{
+    register Segment2D *endPtr;
+
+    for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
+	Blt_FormatToPostScript(psToken, "%g %g moveto\n", 
+			       segPtr->p.x, segPtr->p.y);
+	Blt_FormatToPostScript(psToken, " %g %g lineto\n", 
+			       segPtr->q.x, segPtr->q.y);
+	Blt_AppendToPostScript(psToken, "DashesProc stroke\n", (char *)NULL);
+    }
+}
Index: trunk/kitgen/8.x/blt/generic/bltPs.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltPs.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltPs.h	(revision 175)
@@ -0,0 +1,154 @@
+/*
+ * bltPs.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_PS_H
+#define _BLT_PS_H
+
+#include "bltImage.h"
+
+typedef enum {
+    PS_MODE_MONOCHROME,
+    PS_MODE_GREYSCALE,
+    PS_MODE_COLOR
+} PsColorMode;
+
+typedef struct PsTokenStruct *PsToken;
+
+struct PsTokenStruct {
+    Tcl_Interp *interp;		/* Interpreter to report errors to. */
+
+    Tk_Window tkwin;		/* Tk_Window used to get font and color
+				 * information */
+
+    Tcl_DString dString;	/* Dynamic string used to contain the
+				 * PostScript generated. */
+
+    char *fontVarName;		/* Name of a Tcl array variable to convert
+				 * X font names to PostScript fonts. */
+
+    char *colorVarName;		/* Name of a Tcl array variable to convert
+				 * X color names to PostScript. */
+
+    PsColorMode colorMode;	/* Mode: color or greyscale */
+
+#define PSTOKEN_BUFSIZ	((BUFSIZ*2)-1)
+    /*
+     * Utility space for building strings.  Currently used to create
+     * PostScript output for the "postscript" command.
+     */
+    char scratchArr[PSTOKEN_BUFSIZ+1];
+};
+
+extern PsToken Blt_GetPsToken _ANSI_ARGS_((Tcl_Interp *interp, 
+			   Tk_Window tkwin));
+
+extern void Blt_ReleasePsToken _ANSI_ARGS_((PsToken psToken));
+
+extern char *Blt_PostScriptFromToken _ANSI_ARGS_((PsToken psToken));
+extern char *Blt_ScratchBufferFromToken _ANSI_ARGS_((PsToken psToken));
+
+extern void Blt_AppendToPostScript _ANSI_ARGS_(TCL_VARARGS(PsToken, psToken));
+
+extern void Blt_FormatToPostScript _ANSI_ARGS_(TCL_VARARGS(PsToken, psToken));
+
+extern void Blt_Draw3DRectangleToPostScript _ANSI_ARGS_((PsToken psToken,
+	Tk_3DBorder border, double x, double y, int width, int height,
+	int borderWidth, int relief));
+
+extern void Blt_Fill3DRectangleToPostScript _ANSI_ARGS_((PsToken psToken,
+	Tk_3DBorder border, double x, double y, int width, int height,
+	int borderWidth, int relief));
+
+extern void Blt_BackgroundToPostScript _ANSI_ARGS_((PsToken psToken,
+	XColor *colorPtr));
+
+extern void Blt_BitmapDataToPostScript _ANSI_ARGS_((PsToken psToken,
+	Display *display, Pixmap bitmap, int width, int height));
+
+extern void Blt_ClearBackgroundToPostScript _ANSI_ARGS_((PsToken psToken));
+
+extern int Blt_ColorImageToPsData _ANSI_ARGS_((Blt_ColorImage image,
+	int nComponents, Tcl_DString * resultPtr, char *prefix));
+
+extern void Blt_ColorImageToPostScript _ANSI_ARGS_((PsToken psToken,
+	Blt_ColorImage image, double x, double y));
+
+extern void Blt_ForegroundToPostScript _ANSI_ARGS_((PsToken psToken,
+	XColor *colorPtr));
+
+extern void Blt_FontToPostScript _ANSI_ARGS_((PsToken psToken, Tk_Font font));
+
+extern void Blt_WindowToPostScript _ANSI_ARGS_((PsToken psToken,
+	Tk_Window tkwin, double x, double y));
+
+extern void Blt_LineDashesToPostScript _ANSI_ARGS_((PsToken psToken,
+	Blt_Dashes *dashesPtr));
+
+extern void Blt_LineWidthToPostScript _ANSI_ARGS_((PsToken psToken,
+	int lineWidth));
+
+extern void Blt_PathToPostScript _ANSI_ARGS_((PsToken psToken,
+	Point2D *screenPts, int nScreenPts));
+
+extern void Blt_PhotoToPostScript _ANSI_ARGS_((PsToken psToken,
+	Tk_PhotoHandle photoToken, double x, double y));
+
+extern void Blt_PolygonToPostScript _ANSI_ARGS_((PsToken psToken,
+	Point2D *screenPts, int nScreenPts));
+
+extern void Blt_LineToPostScript _ANSI_ARGS_((PsToken psToken, 
+	XPoint *pointArr, int nPoints));
+
+extern void Blt_TextToPostScript _ANSI_ARGS_((PsToken psToken, char *string,
+	TextStyle *attrPtr, double x, double y));
+
+extern void Blt_RectangleToPostScript _ANSI_ARGS_((PsToken psToken, double x,
+	double y, int width, int height));
+
+extern void Blt_RegionToPostScript _ANSI_ARGS_((PsToken psToken, double x,
+	double y, int width, int height));
+
+extern void Blt_RectanglesToPostScript _ANSI_ARGS_((PsToken psToken,
+	XRectangle *rectArr, int nRects));
+
+extern void Blt_BitmapToPostScript _ANSI_ARGS_((PsToken psToken, 
+	Display *display, Pixmap bitmap, double scaleX, double scaleY));
+
+extern void Blt_SegmentsToPostScript _ANSI_ARGS_((PsToken psToken,
+	XSegment *segArr, int nSegs));
+
+extern void Blt_StippleToPostScript _ANSI_ARGS_((PsToken psToken,
+	Display *display, Pixmap bitmap));
+
+extern void Blt_LineAttributesToPostScript _ANSI_ARGS_((PsToken psToken,
+	XColor *colorPtr, int lineWidth, Blt_Dashes *dashesPtr, int capStyle,
+	int joinStyle));
+
+extern int Blt_FileToPostScript _ANSI_ARGS_((PsToken psToken,
+	char *fileName));
+
+extern void Blt_2DSegmentsToPostScript _ANSI_ARGS_((PsToken psToken, 
+	Segment2D *segments, int nSegments));
+
+#endif /* BLT_PS_H */
Index: trunk/kitgen/8.x/blt/generic/bltSpline.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltSpline.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltSpline.c	(revision 175)
@@ -0,0 +1,1434 @@
+#include "bltInt.h"
+
+typedef double TriDiagonalMatrix[3];
+typedef struct {
+    double b, c, d;
+} Cubic2D;
+
+typedef struct {
+    double b, c, d, e, f;
+} Quint2D;
+
+/*
+ * Quadratic spline parameters
+ */
+#define E1	param[0]
+#define E2	param[1]
+#define V1	param[2]
+#define V2	param[3]
+#define W1	param[4]
+#define W2	param[5]
+#define Z1	param[6]
+#define Z2	param[7]
+#define Y1	param[8]
+#define Y2	param[9]
+
+static Tcl_CmdProc SplineCmd;
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Search --
+ *
+ *	Conducts a binary search for a value.  This routine is called
+ *	only if key is between x(0) and x(len - 1).
+ *
+ * Results:
+ *	Returns the index of the largest value in xtab for which
+ *	x[i] < key.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+Search(points, nPoints, key, foundPtr)
+    Point2D points[];		/* Contains the abscissas of the data
+				 * points of interpolation. */
+    int nPoints;			/* Dimension of x. */
+    double key;			/* Value whose relative position in
+				 * x is to be located. */
+    int *foundPtr;		/* (out) Returns 1 if s is found in
+				 * x and 0 otherwise. */
+{
+    int high, low, mid;
+
+    low = 0;
+    high = nPoints - 1;
+
+    while (high >= low) {
+	mid = (high + low) / 2;
+	if (key > points[mid].x) {
+	    low = mid + 1;
+	} else if (key < points[mid].x) {
+	    high = mid - 1;
+	} else {
+	    *foundPtr = 1;
+	    return mid;
+	}
+    }
+    *foundPtr = 0;
+    return low;
+}
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * QuadChoose --
+ *
+ *	Determines the case needed for the computation of the parame-
+ *	ters of the quadratic spline.
+ *
+ * Results:
+ * 	Returns a case number (1-4) which controls how the parameters
+ * 	of the quadratic spline are evaluated.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+QuadChoose(p, q, m1, m2, epsilon)
+    Point2D *p;			/* Coordinates of one of the points of
+				 * interpolation */
+    Point2D *q;			/* Coordinates of one of the points of
+				 * interpolation */
+    double m1;			/* Derivative condition at point P */
+    double m2;			/* Derivative condition at point Q */
+    double epsilon;		/* Error tolerance used to distinguish
+				 * cases when m1 or m2 is relatively
+				 * close to the slope or twice the
+				 * slope of the line segment joining
+				 * the points P and Q.  If
+				 * epsilon is not 0.0, then epsilon
+				 * should be greater than or equal to
+				 * machine epsilon.  */
+{
+    double slope;
+
+    /* Calculate the slope of the line joining P and Q. */
+    slope = (q->y - p->y) / (q->x - p->x);
+
+    if (slope != 0.0) {
+	double relerr;
+	double mref, mref1, mref2, prod1, prod2;
+
+	prod1 = slope * m1;
+	prod2 = slope * m2;
+
+	/* Find the absolute values of the slopes slope, m1, and m2. */
+	mref = FABS(slope);
+	mref1 = FABS(m1);
+	mref2 = FABS(m2);
+
+	/*
+	 * If the relative deviation of m1 or m2 from slope is less than
+	 * epsilon, then choose case 2 or case 3.
+	 */
+	relerr = epsilon * mref;
+	if ((FABS(slope - m1) > relerr) && (FABS(slope - m2) > relerr) &&
+	    (prod1 >= 0.0) && (prod2 >= 0.0)) {
+	    double prod;
+
+	    prod = (mref - mref1) * (mref - mref2);
+	    if (prod < 0.0) {
+		/*
+		 * l1, the line through (x1,y1) with slope m1, and l2,
+		 * the line through (x2,y2) with slope m2, intersect
+		 * at a point whose abscissa is between x1 and x2.
+		 * The abscissa becomes a knot of the spline.
+		 */
+		return 1;
+	    }
+	    if (mref1 > (mref * 2.0)) {
+		if (mref2 <= ((2.0 - epsilon) * mref)) {
+		    return 3;
+		}
+	    } else if (mref2 <= (mref * 2.0)) {
+		/*
+		 * Both l1 and l2 cross the line through
+		 * (x1+x2)/2.0,y1 and (x1+x2)/2.0,y2, which is the
+		 * midline of the rectangle formed by P and Q or both
+		 * m1 and m2 have signs different than the sign of
+		 * slope, or one of m1 and m2 has opposite sign from
+		 * slope and l1 and l2 intersect to the left of x1 or
+		 * to the right of x2.  The point (x1+x2)/2. is a knot
+		 * of the spline.
+		 */
+		return 2;
+	    } else if (mref1 <= ((2.0 - epsilon) * mref)) {
+		/*
+		 * In cases 3 and 4, sign(m1)=sign(m2)=sign(slope).
+		 * Either l1 or l2 crosses the midline, but not both.
+		 * Choose case 4 if mref1 is greater than
+		 * (2.-epsilon)*mref; otherwise, choose case 3.
+		 */
+		return 3;
+	    }
+	    /*
+	     * If neither l1 nor l2 crosses the midline, the spline
+	     * requires two knots between x1 and x2.
+	     */
+	    return 4;
+	} else {
+	    /*
+	     * The sign of at least one of the slopes m1 or m2 does not
+	     * agree with the sign of *slope*.
+	     */
+	    if ((prod1 < 0.0) && (prod2 < 0.0)) {
+		return 2;
+	    } else if (prod1 < 0.0) {
+		if (mref2 > ((epsilon + 1.0) * mref)) {
+		    return 1;
+		} else {
+		    return 2;
+		}
+	    } else if (mref1 > ((epsilon + 1.0) * mref)) {
+		return 1;
+	    } else {
+		return 2;
+	    }
+	}
+    } else if ((m1 * m2) >= 0.0) {
+	return 2;
+    } else {
+	return 1;
+    }
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadCases --
+ *
+ *	Computes the knots and other parameters of the spline on the
+ *	interval PQ.
+ *
+ *
+ * On input--
+ *
+ *	P and Q are the coordinates of the points of interpolation.
+ *
+ *	m1 is the slope at P.
+ *
+ *	m2 is the slope at Q.
+ *
+ *	ncase controls the number and location of the knots.
+ *
+ *
+ * On output--
+ *
+ *	(v1,v2),(w1,w2),(z1,z2), and (e1,e2) are the coordinates of
+ *	the knots and other parameters of the spline on P.
+ *	(e1,e2) and Q are used only if ncase=4.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadCases(p, q, m1, m2, param, which)
+    Point2D *p, *q;
+    double m1, m2;
+    double param[];
+    int which;
+{
+    if ((which == 3) || (which == 4)) {	/* Parameters used in both 3 and 4 */
+	double mbar1, mbar2, mbar3, c1, d1, h1, j1, k1;
+
+	c1 = p->x + (q->y - p->y) / m1;
+	d1 = q->x + (p->y - q->y) / m2;
+	h1 = c1 * 2.0 - p->x;
+	j1 = d1 * 2.0 - q->x;
+	mbar1 = (q->y - p->y) / (h1 - p->x);
+	mbar2 = (p->y - q->y) / (j1 - q->x);
+
+	if (which == 4) {	/* Case 4. */
+	    Y1 = (p->x + c1) / 2.0;
+	    V1 = (p->x + Y1) / 2.0;
+	    V2 = m1 * (V1 - p->x) + p->y;
+	    Z1 = (d1 + q->x) / 2.0;
+	    W1 = (q->x + Z1) / 2.0;
+	    W2 = m2 * (W1 - q->x) + q->y;
+	    mbar3 = (W2 - V2) / (W1 - V1);
+	    Y2 = mbar3 * (Y1 - V1) + V2;
+	    Z2 = mbar3 * (Z1 - V1) + V2;
+	    E1 = (Y1 + Z1) / 2.0;
+	    E2 = mbar3 * (E1 - V1) + V2;
+	} else {		/* Case 3. */
+	    k1 = (p->y - q->y + q->x * mbar2 - p->x * mbar1) / (mbar2 - mbar1);
+	    if (FABS(m1) > FABS(m2)) {
+		Z1 = (k1 + p->x) / 2.0;
+	    } else {
+		Z1 = (k1 + q->x) / 2.0;
+	    }
+	    V1 = (p->x + Z1) / 2.0;
+	    V2 = p->y + m1 * (V1 - p->x);
+	    W1 = (q->x + Z1) / 2.0;
+	    W2 = q->y + m2 * (W1 - q->x);
+	    Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+	}
+    } else if (which == 2) {	/* Case 2. */
+	Z1 = (p->x + q->x) / 2.0;
+	V1 = (p->x + Z1) / 2.0;
+	V2 = p->y + m1 * (V1 - p->x);
+	W1 = (Z1 + q->x) / 2.0;
+	W2 = q->y + m2 * (W1 - q->x);
+	Z2 = (V2 + W2) / 2.0;
+    } else {			/* Case 1. */
+	double ztwo;
+
+	Z1 = (p->y - q->y + m2 * q->x - m1 * p->x) / (m2 - m1);
+	ztwo = p->y + m1 * (Z1 - p->x);
+	V1 = (p->x + Z1) / 2.0;
+	V2 = (p->y + ztwo) / 2.0;
+	W1 = (Z1 + q->x) / 2.0;
+	W2 = (ztwo + q->y) / 2.0;
+	Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+    }
+}
+
+static int
+QuadSelect(p, q, m1, m2, epsilon, param)
+    Point2D *p, *q;
+    double m1, m2;
+    double epsilon;
+    double param[];
+{
+    int ncase;
+
+    ncase = QuadChoose(p, q, m1, m2, epsilon);
+    QuadCases(p, q, m1, m2, param, ncase);
+    return ncase;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadGetImage --
+ *
+ * -----------------------------------------------------------------------
+ */
+INLINE static double
+QuadGetImage(p1, p2, p3, x1, x2, x3)
+    double p1, p2, p3;
+    double x1, x2, x3;
+{
+    double A, B, C;
+    double y;
+
+    A = x1 - x2;
+    B = x2 - x3;
+    C = x1 - x3;
+
+    y = (p1 * (A * A) + p2 * 2.0 * B * A + p3 * (B * B)) / (C * C);
+    return y;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadSpline --
+ *
+ *	Finds the image of a point in x.
+ *
+ *	On input
+ *
+ *	x	Contains the value at which the spline is evaluated.
+ *	leftX, leftY
+ *		Coordinates of the left-hand data point used in the
+ *		evaluation of x values.
+ *	rightX, rightY
+ *		Coordinates of the right-hand data point used in the
+ *		evaluation of x values.
+ *	Z1, Z2, Y1, Y2, E2, W2, V2
+ *		Parameters of the spline.
+ *	ncase	Controls the evaluation of the spline by indicating
+ *		whether one or two knots were placed in the interval
+ *		(xtabs,xtabs1).
+ *
+ * Results:
+ *	The image of the spline at x.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadSpline(intp, left, right, param, ncase)
+    Point2D *intp;		/* Value at which spline is evaluated */
+    Point2D *left;		/* Point to the left of the data point to
+				 * be evaluated */
+    Point2D *right;		/* Point to the right of the data point to
+				 * be evaluated */
+    double param[];		/* Parameters of the spline */
+    int ncase;			/* Controls the evaluation of the
+				 * spline by indicating whether one or
+				 * two knots were placed in the
+				 * interval (leftX,rightX) */
+{
+    double y;
+
+    if (ncase == 4) {
+	/*
+	 * Case 4:  More than one knot was placed in the interval.
+	 */
+
+	/*
+	 * Determine the location of data point relative to the 1st knot.
+	 */
+	if (Y1 > intp->x) {
+	    y = QuadGetImage(left->y, V2, Y2, Y1, intp->x, left->x);
+	} else if (Y1 < intp->x) {
+	    /*
+	     * Determine the location of the data point relative to
+	     * the 2nd knot.
+	     */
+	    if (Z1 > intp->x) {
+		y = QuadGetImage(Y2, E2, Z2, Z1, intp->x, Y1);
+	    } else if (Z1 < intp->x) {
+		y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+	    } else {
+		y = Z2;
+	    }
+	} else {
+	    y = Y2;
+	}
+    } else {
+
+	/*
+	 * Cases 1, 2, or 3:
+	 *
+	 * Determine the location of the data point relative to the
+	 * knot.
+	 */
+	if (Z1 < intp->x) {
+	    y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+	} else if (Z1 > intp->x) {
+	    y = QuadGetImage(left->y, V2, Z2, Z1, intp->x, left->x);
+	} else {
+	    y = Z2;
+	}
+    }
+    intp->y = y;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadSlopes --
+ *
+ * 	Calculates the derivative at each of the data points.  The
+ * 	slopes computed will insure that an osculatory quadratic
+ * 	spline will have one additional knot between two adjacent
+ * 	points of interpolation.  Convexity and monotonicity are
+ * 	preserved wherever these conditions are compatible with the
+ * 	data.
+ *
+ * Results:
+ *	The output array "m" is filled with the derivates at each
+ *	data point.
+ *
+ * -----------------------------------------------------------------------
+ */
+static void
+QuadSlopes(points, m, nPoints)
+    Point2D points[];
+    double *m;			/* (out) To be filled with the first
+				 * derivative at each data point. */
+    int nPoints;		/* Number of data points (dimension of
+				 * x, y, and m). */
+{
+    double xbar, xmid, xhat, ydif1, ydif2;
+    double yxmid;
+    double m1, m2;
+    double m1s, m2s;
+    register int i, n, l;
+
+    m1s = m2s = m1 = m2 = 0;
+    for (l = 0, i = 1, n = 2; i < (nPoints - 1); l++, i++, n++) {
+	/*
+	 * Calculate the slopes of the two lines joining three
+	 * consecutive data points.
+	 */
+	ydif1 = points[i].y - points[l].y;
+	ydif2 = points[n].y - points[i].y;
+	m1 = ydif1 / (points[i].x - points[l].x);
+	m2 = ydif2 / (points[n].x - points[i].x);
+	if (i == 1) {
+	    m1s = m1, m2s = m2;	/* Save slopes of starting point */
+	}
+	/*
+	 * If one of the preceding slopes is zero or if they have opposite
+	 * sign, assign the value zero to the derivative at the middle
+	 * point.
+	 */
+	if ((m1 == 0.0) || (m2 == 0.0) || ((m1 * m2) <= 0.0)) {
+	    m[i] = 0.0;
+	} else if (FABS(m1) > FABS(m2)) {
+	    /*
+	     * Calculate the slope by extending the line with slope m1.
+	     */
+	    xbar = ydif2 / m1 + points[i].x;
+	    xhat = (xbar + points[n].x) / 2.0;
+	    m[i] = ydif2 / (xhat - points[i].x);
+	} else {
+	    /*
+	     * Calculate the slope by extending the line with slope m2.
+	     */
+	    xbar = -ydif1 / m2 + points[i].x;
+	    xhat = (points[l].x + xbar) / 2.0;
+	    m[i] = ydif1 / (points[i].x - xhat);
+	}
+    }
+
+    /* Calculate the slope at the last point, x(n). */
+    i = nPoints - 2;
+    n = nPoints - 1;
+    if ((m1 * m2) < 0.0) {
+	m[n] = m2 * 2.0;
+    } else {
+	xmid = (points[i].x + points[n].x) / 2.0;
+	yxmid = m[i] * (xmid - points[i].x) + points[i].y;
+	m[n] = (points[n].y - yxmid) / (points[n].x - xmid);
+	if ((m[n] * m2) < 0.0) {
+	    m[n] = 0.0;
+	}
+    }
+
+    /* Calculate the slope at the first point, x(0). */
+    if ((m1s * m2s) < 0.0) {
+	m[0] = m1s * 2.0;
+    } else {
+	xmid = (points[0].x + points[1].x) / 2.0;
+	yxmid = m[1] * (xmid - points[1].x) + points[1].y;
+	m[0] = (yxmid - points[0].y) / (xmid - points[0].x);
+	if ((m[0] * m1s) < 0.0) {
+	    m[0] = 0.0;
+	}
+    }
+
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * QuadEval --
+ *
+ * 	QuadEval controls the evaluation of an osculatory quadratic
+ * 	spline.  The user may provide his own slopes at the points of
+ * 	interpolation or use the subroutine 'QuadSlopes' to calculate
+ * 	slopes which are consistent with the shape of the data.
+ *
+ * ON INPUT--
+ *   	intpPts	must be a nondecreasing vector of points at which the
+ *		spline will be evaluated.
+ *   	origPts	contains the abscissas of the data points to be
+ *		interpolated. xtab must be increasing.
+ *   	y	contains the ordinates of the data points to be
+ *		interpolated.
+ *   	m 	contains the slope of the spline at each point of
+ *		interpolation.
+ *   	nPoints	number of data points (dimension of xtab and y).
+ *   	numEval is the number of points of evaluation (dimension of
+ *		xval and yval).
+ *   	epsilon 	is a relative error tolerance used in subroutine
+ *		'QuadChoose' to distinguish the situation m(i) or
+ *		m(i+1) is relatively close to the slope or twice
+ *		the slope of the linear segment between xtab(i) and
+ *		xtab(i+1).  If this situation occurs, roundoff may
+ *		cause a change in convexity or monotonicity of the
+ *   		resulting spline and a change in the case number
+ *		provided by 'QuadChoose'.  If epsilon is not equal to zero,
+ *		then epsilon should be greater than or equal to machine
+ *		epsilon.
+ * ON OUTPUT--
+ * 	yval 	contains the images of the points in xval.
+ *   	err 	is one of the following error codes:
+ *      	0 - QuadEval ran normally.
+ *      	1 - xval(i) is less than xtab(1) for at least one
+ *		    i or xval(i) is greater than xtab(num) for at
+ *		    least one i. QuadEval will extrapolate to provide
+ *		    function values for these abscissas.
+ *      	2 - xval(i+1) < xval(i) for some i.
+ *
+ *
+ *  QuadEval calls the following subroutines or functions:
+ *      Search
+ *      QuadCases
+ *      QuadChoose
+ *      QuadSpline
+ * -----------------------------------------------------------------------
+ */
+static int
+QuadEval(origPts, nOrigPts, intpPts, nIntpPts, m, epsilon)
+    Point2D origPts[];
+    int nOrigPts;
+    Point2D intpPts[];
+    int nIntpPts;
+    double *m;			/* Slope of the spline at each point
+				 * of interpolation. */
+    double epsilon;		/* Relative error tolerance (see choose) */
+{
+    int error;
+    register int i, j;
+    double param[10];
+    int ncase;
+    int start, end;
+    int l, p;
+    register int n;
+    int found;
+
+    /* Initialize indices and set error result */
+    error = 0;
+    l = nOrigPts - 1;
+    p = l - 1;
+    ncase = 1;
+
+    /*
+     * Determine if abscissas of new vector are non-decreasing.
+     */
+    for (j = 1; j < nIntpPts; j++) {
+	if (intpPts[j].x < intpPts[j - 1].x) {
+	    return 2;
+	}
+    }
+    /*
+     * Determine if any of the points in xval are LESS than the
+     * abscissa of the first data point.
+     */
+    for (start = 0; start < nIntpPts; start++) {
+	if (intpPts[start].x >= origPts[0].x) {
+	    break;
+	}
+    }
+    /*
+     * Determine if any of the points in xval are GREATER than the
+     * abscissa of the l data point.
+     */
+    for (end = nIntpPts - 1; end >= 0; end--) {
+	if (intpPts[end].x <= origPts[l].x) {
+	    break;
+	}
+    }
+
+    if (start > 0) {
+	error = 1;		/* Set error value to indicate that
+				 * extrapolation has occurred. */
+	/*
+	 * Calculate the images of points of evaluation whose abscissas
+	 * are less than the abscissa of the first data point.
+	 */
+	ncase = QuadSelect(origPts, origPts + 1, m[0], m[1], epsilon, param);
+	for (j = 0; j < (start - 1); j++) {
+	    QuadSpline(intpPts + j, origPts, origPts + 1, param, ncase);
+	}
+	if (nIntpPts == 1) {
+	    return error;
+	}
+    }
+    if ((nIntpPts == 1) && (end != (nIntpPts - 1))) {
+	goto noExtrapolation;
+    }
+    
+    /*
+     * Search locates the interval in which the first in-range
+     * point of evaluation lies.
+     */
+
+    i = Search(origPts, nOrigPts, intpPts[start].x, &found);
+    
+    n = i + 1;
+    if (n >= nOrigPts) {
+	n = nOrigPts - 1;
+	i = nOrigPts - 2;
+    }
+    /*
+     * If the first in-range point of evaluation is equal to one
+     * of the data points, assign the appropriate value from y.
+     * Continue until a point of evaluation is found which is not
+     * equal to a data point.
+     */
+    if (found) {
+	do {
+	    intpPts[start].y = origPts[i].y;
+	    start++;
+	    if (start >= nIntpPts) {
+		return error;
+	    }
+	} while (intpPts[start - 1].x == intpPts[start].x);
+	
+	for (;;) {
+	    if (intpPts[start].x < origPts[n].x) {
+		break;	/* Break out of for-loop */
+	    }
+	    if (intpPts[start].x == origPts[n].x) {
+		do {
+		    intpPts[start].y = origPts[n].y;
+		    start++;
+		    if (start >= nIntpPts) {
+			return error;
+		    }
+		} while (intpPts[start].x == intpPts[start - 1].x);
+	    }
+	    i++;
+	    n++;
+	}
+    }
+    /*
+     * Calculate the images of all the points which lie within
+     * range of the data.
+     */
+    if ((i > 0) || (error != 1)) {
+	ncase = QuadSelect(origPts + i, origPts + n, m[i], m[n], 
+			   epsilon, param);
+    }
+    for (j = start; j <= end; j++) {
+	/*
+	 * If xx(j) - x(n) is negative, do not recalculate
+	 * the parameters for this section of the spline since
+	 * they are already known.
+	 */
+	if (intpPts[j].x == origPts[n].x) {
+	    intpPts[j].y = origPts[n].y;
+	    continue;
+	} else if (intpPts[j].x > origPts[n].x) {
+	    double delta;
+	    
+	    /* Determine that the routine is in the correct part of
+	       the spline. */
+	    do {
+		i++, n++;
+		delta = intpPts[j].x - origPts[n].x;
+	    } while (delta > 0.0);
+	    
+	    if (delta < 0.0) {
+		ncase = QuadSelect(origPts + i, origPts + n, m[i], 
+			   m[n], epsilon, param);
+	    } else if (delta == 0.0) {
+		intpPts[j].y = origPts[n].y;
+		continue;
+	    }
+	}
+	QuadSpline(intpPts + j, origPts + i, origPts + n, param, ncase);
+    }
+    
+    if (end == (nIntpPts - 1)) {
+	return error;
+    }
+    if ((n == l) && (intpPts[end].x != origPts[l].x)) {
+	goto noExtrapolation;
+    }
+
+    error = 1;			/* Set error value to indicate that
+				 * extrapolation has occurred. */
+    ncase = QuadSelect(origPts + p, origPts + l, m[p], m[l], epsilon, param);
+
+  noExtrapolation:
+    /*
+     * Calculate the images of the points of evaluation whose
+     * abscissas are greater than the abscissa of the last data point.
+     */
+    for (j = (end + 1); j < nIntpPts; j++) {
+	QuadSpline(intpPts + j, origPts + p, origPts + l, param, ncase);
+    }
+    return error;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ *		  Shape preserving quadratic splines
+ *		   by D.F.Mcallister & J.A.Roulier
+ *		    Coded by S.L.Dodd & M.Roulier
+ *			 N.C.State University
+ *
+ * -----------------------------------------------------------------------
+ */
+/*
+ * Driver routine for quadratic spline package
+ * On input--
+ *   X,Y    Contain n-long arrays of data (x is increasing)
+ *   XM     Contains m-long array of x values (increasing)
+ *   eps    Relative error tolerance
+ *   n      Number of input data points
+ *   m      Number of output data points
+ * On output--
+ *   work   Contains the value of the first derivative at each data point
+ *   ym     Contains the interpolated spline value at each data point
+ */
+int
+Blt_QuadraticSpline(origPts, nOrigPts, intpPts, nIntpPts)
+    Point2D origPts[];
+    int nOrigPts;
+    Point2D intpPts[];
+    int nIntpPts;
+{
+    double epsilon;
+    double *work;
+    int result;
+
+    work = Blt_Malloc(nOrigPts * sizeof(double));
+    assert(work);
+
+    epsilon = 0.0;		/* TBA: adjust error via command-line option */
+    /* allocate space for vectors used in calculation */
+    QuadSlopes(origPts, work, nOrigPts);
+    result = QuadEval(origPts, nOrigPts, intpPts, nIntpPts, work, epsilon);
+    Blt_Free(work);
+    if (result > 1) {
+	return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * Reference:
+ *	Numerical Analysis, R. Burden, J. Faires and A. Reynolds.
+ *	Prindle, Weber & Schmidt 1981 pp 112
+ *
+ * Parameters:
+ *	origPts - vector of points, assumed to be sorted along x.
+ *	intpPts - vector of new points.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_NaturalSpline(origPts, nOrigPts, intpPts, nIntpPts)
+    Point2D origPts[];
+    int nOrigPts;
+    Point2D intpPts[];
+    int nIntpPts;
+{
+    Cubic2D *eq;
+    Point2D *iPtr, *endPtr;
+    TriDiagonalMatrix *A;
+    double *dx;		/* vector of deltas in x */
+    double x, dy, alpha;
+    int isKnot;
+    register int i, j, n;
+
+    dx = Blt_Malloc(sizeof(double) * nOrigPts);
+    /* Calculate vector of differences */
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+	dx[i] = origPts[j].x - origPts[i].x;
+	if (dx[i] < 0.0) {
+	    return 0;
+	}
+    }
+    n = nOrigPts - 1;		/* Number of intervals. */
+    A = Blt_Malloc(sizeof(TriDiagonalMatrix) * nOrigPts);
+    if (A == NULL) {
+	Blt_Free(dx);
+	return 0;
+    }
+    /* Vectors to solve the tridiagonal matrix */
+    A[0][0] = A[n][0] = 1.0;
+    A[0][1] = A[n][1] = 0.0;
+    A[0][2] = A[n][2] = 0.0;
+
+    /* Calculate the intermediate results */
+    for (i = 0, j = 1; j < n; j++, i++) {
+	alpha = 3.0 * ((origPts[j + 1].y / dx[j]) - (origPts[j].y / dx[i]) - 
+		       (origPts[j].y / dx[j]) + (origPts[i].y / dx[i]));
+	A[j][0] = 2 * (dx[j] + dx[i]) - dx[i] * A[i][1];
+	A[j][1] = dx[j] / A[j][0];
+	A[j][2] = (alpha - dx[i] * A[i][2]) / A[j][0];
+    }
+    eq = Blt_Malloc(sizeof(Cubic2D) * nOrigPts);
+
+    if (eq == NULL) {
+	Blt_Free(A);
+	Blt_Free(dx);
+	return FALSE;
+    }
+    eq[0].c = eq[n].c = 0.0;
+    for (j = n, i = n - 1; i >= 0; i--, j--) {
+	eq[i].c = A[i][2] - A[i][1] * eq[j].c;
+	dy = origPts[i+1].y - origPts[i].y;
+	eq[i].b = (dy) / dx[i] - dx[i] * (eq[j].c + 2.0 * eq[i].c) / 3.0;
+	eq[i].d = (eq[j].c - eq[i].c) / (3.0 * dx[i]);
+    }
+    Blt_Free(A);
+    Blt_Free(dx);
+
+    endPtr = intpPts + nIntpPts;
+    /* Now calculate the new values */
+    for (iPtr = intpPts; iPtr < endPtr; iPtr++) {
+	iPtr->y = 0.0;
+	x = iPtr->x;
+
+	/* Is it outside the interval? */
+	if ((x < origPts[0].x) || (x > origPts[n].x)) {
+	    continue;
+	}
+	/* Search for the interval containing x in the point array */
+	i = Search(origPts, nOrigPts, x, &isKnot);
+	if (isKnot) {
+	    iPtr->y = origPts[i].y;
+	} else {
+	    i--;
+	    x -= origPts[i].x;
+	    iPtr->y = origPts[i].y + 
+		x * (eq[i].b + x * (eq[i].c + x * eq[i].d));
+	}
+    }
+    Blt_Free(eq);
+    return TRUE;
+}
+
+static Blt_OpSpec splineOps[] =
+{
+    {"natural", 1, (Blt_Op)Blt_NaturalSpline, 6, 6, "x y splx sply",},
+    {"quadratic", 1, (Blt_Op)Blt_QuadraticSpline, 6, 6, "x y splx sply",},
+};
+static int nSplineOps = sizeof(splineOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+SplineCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    Blt_Vector *x, *y, *splX, *splY;
+    double *xArr, *yArr;
+    register int i;
+    Point2D *origPts, *intpPts;
+    int nOrigPts, nIntpPts;
+    
+    proc = Blt_GetOp(interp, nSplineOps, splineOps, BLT_OP_ARG1, argc, argv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    if ((Blt_GetVector(interp, argv[2], &x) != TCL_OK) ||
+	(Blt_GetVector(interp, argv[3], &y) != TCL_OK) ||
+	(Blt_GetVector(interp, argv[4], &splX) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    nOrigPts = Blt_VecLength(x);
+    if (nOrigPts < 3) {
+	Tcl_AppendResult(interp, "length of vector \"", argv[2], "\" is < 3",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (i = 1; i < nOrigPts; i++) {
+	if (Blt_VecData(x)[i] < Blt_VecData(x)[i - 1]) {
+	    Tcl_AppendResult(interp, "x vector \"", argv[2],
+		"\" must be monotonically increasing", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    /* Check that all the data points aren't the same. */
+    if (Blt_VecData(x)[i - 1] <= Blt_VecData(x)[0]) {
+	Tcl_AppendResult(interp, "x vector \"", argv[2],
+	 "\" must be monotonically increasing", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (nOrigPts != Blt_VecLength(y)) {
+	Tcl_AppendResult(interp, "vectors \"", argv[2], "\" and \"", argv[3],
+	    " have different lengths", (char *)NULL);
+	return TCL_ERROR;
+    }
+    nIntpPts = Blt_VecLength(splX);
+    if (Blt_GetVector(interp, argv[5], &splY) != TCL_OK) {
+	/*
+	 * If the named vector to hold the ordinates of the spline
+	 * doesn't exist, create one the same size as the vector
+	 * containing the abscissas.
+	 */
+	if (Blt_CreateVector(interp, argv[5], nIntpPts, &splY) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    } else if (nIntpPts != Blt_VecLength(splY)) {
+	/*
+	 * The x and y vectors differ in size. Make the number of ordinates
+	 * the same as the number of abscissas.
+	 */
+	if (Blt_ResizeVector(splY, nIntpPts) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    origPts = Blt_Malloc(sizeof(Point2D) * nOrigPts);
+    if (origPts == NULL) {
+	Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nOrigPts), 
+		"\" points", (char *)NULL);
+	return TCL_ERROR;
+    }
+    intpPts = Blt_Malloc(sizeof(Point2D) * nIntpPts);
+    if (intpPts == NULL) {
+	Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nIntpPts), 
+		"\" points", (char *)NULL);
+	Blt_Free(origPts);
+	return TCL_ERROR;
+    }
+    xArr = Blt_VecData(x);
+    yArr = Blt_VecData(y);
+    for (i = 0; i < nOrigPts; i++) {
+	origPts[i].x = xArr[i];
+	origPts[i].y = yArr[i];
+    }
+    xArr = Blt_VecData(splX);
+    yArr = Blt_VecData(splY);
+    for (i = 0; i < nIntpPts; i++) {
+	intpPts[i].x = xArr[i];
+	intpPts[i].y = yArr[i];
+    }
+    if (!(*proc) (origPts, nOrigPts, intpPts, nIntpPts)) {
+	Tcl_AppendResult(interp, "error generating spline for \"", 
+		Blt_NameOfVector(splY), "\"", (char *)NULL);
+	Blt_Free(origPts);
+	Blt_Free(intpPts);
+	return TCL_ERROR;
+    }
+    yArr = Blt_VecData(splY);
+    for (i = 0; i < nIntpPts; i++) {
+	yArr[i] = intpPts[i].y;
+    }
+    Blt_Free(origPts);
+    Blt_Free(intpPts);
+
+    /* Finally update the vector. The size of the vector hasn't
+     * changed, just the data. Reset the vector using TCL_STATIC to
+     * indicate this. */
+    if (Blt_ResetVector(splY, Blt_VecData(splY), Blt_VecLength(splY),
+	    Blt_VecSize(splY), TCL_STATIC) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_SplineInit(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpec =
+    {"spline", SplineCmd,};
+
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+#define SQR(x)	((x)*(x))
+
+typedef struct {
+    double t;			/* Arc length of interval. */
+    double x;			/* 2nd derivative of X with respect to T */
+    double y;			/* 2nd derivative of Y with respect to T */
+} CubicSpline;
+
+
+/*
+ * The following two procedures solve the special linear system which arise
+ * in cubic spline interpolation. If x is assumed cyclic ( x[i]=x[n+i] ) the
+ * equations can be written as (i=0,1,...,n-1):
+ *     m[i][0] * x[i-1] + m[i][1] * x[i] + m[i][2] * x[i+1] = b[i] .
+ * In matrix notation one gets A * x = b, where the matrix A is tridiagonal
+ * with additional elements in the upper right and lower left position:
+ *   A[i][0] = A_{i,i-1}  for i=1,2,...,n-1    and    m[0][0] = A_{0,n-1} ,
+ *   A[i][1] = A_{i, i }  for i=0,1,...,n-1
+ *   A[i][2] = A_{i,i+1}  for i=0,1,...,n-2    and    m[n-1][2] = A_{n-1,0}.
+ * A should be symmetric (A[i+1][0] == A[i][2]) and positive definite.
+ * The size of the system is given in n (n>=1).
+ *
+ * In the first procedure the Cholesky decomposition A = C^T * D * C
+ * (C is upper triangle with unit diagonal, D is diagonal) is calculated.
+ * Return TRUE if decomposition exist.
+ */
+static int 
+SolveCubic1(A, n)
+    TriDiagonalMatrix A[];
+    int n;
+{
+    int i;
+    double m_ij, m_n, m_nn, d;
+
+    if (n < 1) {
+	return FALSE;		/* Dimension should be at least 1 */
+    }
+    d = A[0][1];		/* D_{0,0} = A_{0,0} */
+    if (d <= 0.0) {
+	return FALSE;		/* A (or D) should be positive definite */
+    }
+    m_n = A[0][0];		/*  A_{0,n-1}  */
+    m_nn = A[n - 1][1];		/* A_{n-1,n-1} */
+    for (i = 0; i < n - 2; i++) {
+	m_ij = A[i][2];		/*  A_{i,1}  */
+	A[i][2] = m_ij / d;	/* C_{i,i+1} */
+	A[i][0] = m_n / d;	/* C_{i,n-1} */
+	m_nn -= A[i][0] * m_n;	/* to get C_{n-1,n-1} */
+	m_n = -A[i][2] * m_n;	/* to get C_{i+1,n-1} */
+	d = A[i + 1][1] - A[i][2] * m_ij;	/* D_{i+1,i+1} */
+	if (d <= 0.0) {
+	    return FALSE;	/* Elements of D should be positive */
+	}
+	A[i + 1][1] = d;
+    }
+    if (n >= 2) {		/* Complete last column */
+	m_n += A[n - 2][2];	/* add A_{n-2,n-1} */
+	A[n - 2][0] = m_n / d;	/* C_{n-2,n-1} */
+	A[n - 1][1] = d = m_nn - A[n - 2][0] * m_n;	/* D_{n-1,n-1} */
+	if (d <= 0.0) {
+	    return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+/*
+ * The second procedure solves the linear system, with the Cholesky
+ * decomposition calculated above (in m[][]) and the right side b given
+ * in x[]. The solution x overwrites the right side in x[].
+ */
+static void 
+SolveCubic2(A, spline, nIntervals)
+    TriDiagonalMatrix A[];
+    CubicSpline spline[];
+    int nIntervals;
+{
+    int i;
+    double x, y;
+    int n, m;
+
+    n = nIntervals - 2;
+    m = nIntervals - 1;
+
+    /* Division by transpose of C : b = C^{-T} * b */
+    x = spline[m].x;
+    y = spline[m].y;
+    for (i = 0; i < n; i++) {
+	spline[i + 1].x -= A[i][2] * spline[i].x; /* C_{i,i+1} * x(i) */
+	spline[i + 1].y -= A[i][2] * spline[i].y; /* C_{i,i+1} * x(i) */
+	x -= A[i][0] * spline[i].x;	/* C_{i,n-1} * x(i) */
+	y -= A[i][0] * spline[i].y; /* C_{i,n-1} * x(i) */
+    }
+    if (n >= 0) {
+	/* C_{n-2,n-1} * x_{n-1} */
+	spline[m].x = x - A[n][0] * spline[n].x; 
+	spline[m].y = y - A[n][0] * spline[n].y; 
+    }
+    /* Division by D: b = D^{-1} * b */
+    for (i = 0; i < nIntervals; i++) {
+	spline[i].x /= A[i][1];
+	spline[i].y /= A[i][1];
+    }
+
+    /* Division by C: b = C^{-1} * b */
+    x = spline[m].x;
+    y = spline[m].y;
+    if (n >= 0) {
+	/* C_{n-2,n-1} * x_{n-1} */
+	spline[n].x -= A[n][0] * x;	
+	spline[n].y -= A[n][0] * y;	
+    }
+    for (i = (n - 1); i >= 0; i--) {
+	/* C_{i,i+1} * x_{i+1} + C_{i,n-1} * x_{n-1} */
+	spline[i].x -= A[i][2] * spline[i + 1].x + A[i][0] * x;
+	spline[i].y -= A[i][2] * spline[i + 1].y + A[i][0] * y;
+    }
+}
+
+/*
+ * Find second derivatives (x''(t_i),y''(t_i)) of cubic spline interpolation
+ * through list of points (x_i,y_i). The parameter t is calculated as the
+ * length of the linear stroke. The number of points must be at least 3.
+ * Note: For CLOSED_CONTOURs the first and last point must be equal.
+ */
+static CubicSpline *
+CubicSlopes(points, nPoints, isClosed, unitX, unitY)
+    Point2D points[];
+    int nPoints;		/* Number of points (nPoints>=3) */
+    int isClosed;		/* CLOSED_CONTOUR or OPEN_CONTOUR  */
+    double unitX, unitY;	/* Unit length in x and y (norm=1) */
+{
+    CubicSpline *spline;
+    register CubicSpline *s1, *s2;
+    int n, i;
+    double norm, dx, dy;
+    TriDiagonalMatrix *A;	/* The tri-diagonal matrix is saved here. */
+    
+    spline = Blt_Malloc(sizeof(CubicSpline) * nPoints);
+    if (spline == NULL) {
+	return NULL;
+    }
+    A = Blt_Malloc(sizeof(TriDiagonalMatrix) * nPoints);
+    if (A == NULL) {
+	Blt_Free(spline);
+	return NULL;
+    }
+    /*
+     * Calculate first differences in (dxdt2[i], y[i]) and interval lengths
+     * in dist[i]:
+     */
+    s1 = spline;
+    for (i = 0; i < nPoints - 1; i++) {
+	s1->x = points[i+1].x - points[i].x;
+	s1->y = points[i+1].y - points[i].y;
+
+	/*
+	 * The Norm of a linear stroke is calculated in "normal coordinates"
+	 * and used as interval length:
+	 */
+	dx = s1->x / unitX;
+	dy = s1->y / unitY;
+	s1->t = sqrt(dx * dx + dy * dy);
+
+	s1->x /= s1->t;	/* first difference, with unit norm: */
+	s1->y /= s1->t;	/*   || (dxdt2[i], y[i]) || = 1      */
+	s1++;
+    }
+
+    /*
+     * Setup linear System:  Ax = b
+     */
+    n = nPoints - 2;		/* Without first and last point */
+    if (isClosed) {
+	/* First and last points must be equal for CLOSED_CONTOURs */
+	spline[nPoints - 1].t = spline[0].t;
+	spline[nPoints - 1].x = spline[0].x;
+	spline[nPoints - 1].y = spline[0].y;
+	n++;			/* Add last point (= first point) */
+    }
+    s1 = spline, s2 = s1 + 1;
+    for (i = 0; i < n; i++) {
+	/* Matrix A, mainly tridiagonal with cyclic second index 
+	   ("j = j+n mod n") 
+	*/
+	A[i][0] = s1->t;	/* Off-diagonal element A_{i,i-1} */
+	A[i][1] = 2.0 * (s1->t + s2->t);	/* A_{i,i} */
+	A[i][2] = s2->t;	/* Off-diagonal element A_{i,i+1} */
+
+	/* Right side b_x and b_y */
+	s1->x = (s2->x - s1->x) * 6.0;
+	s1->y = (s2->y - s1->y) * 6.0;
+
+	/* 
+	 * If the linear stroke shows a cusp of more than 90 degree,
+	 * the right side is reduced to avoid oscillations in the
+	 * spline: 
+	 */
+	/*
+	 * The Norm of a linear stroke is calculated in "normal coordinates"
+	 * and used as interval length:
+	 */
+	dx = s1->x / unitX;
+	dy = s1->y / unitY;
+	norm = sqrt(dx * dx + dy * dy) / 8.5;
+	if (norm > 1.0) {
+	    /* The first derivative will not be continuous */
+	    s1->x /= norm;
+	    s1->y /= norm;
+	}
+	s1++, s2++;
+    }
+
+    if (!isClosed) {
+	/* Third derivative is set to zero at both ends */
+	A[0][1] += A[0][0];	/* A_{0,0}     */
+	A[0][0] = 0.0;		/* A_{0,n-1}   */
+	A[n-1][1] += A[n-1][2]; /* A_{n-1,n-1} */
+	A[n-1][2] = 0.0;	/* A_{n-1,0}   */
+    }
+    /* Solve linear systems for dxdt2[] and y[] */
+
+    if (SolveCubic1(A, n)) {	/* Cholesky decomposition */
+	SolveCubic2(A, spline, n); /* A * dxdt2 = b_x */
+    } else {			/* Should not happen, but who knows ... */
+	Blt_Free(A);
+	Blt_Free(spline);
+	return NULL;
+    }
+    /* Shift all second derivatives one place right and update the ends. */
+    s2 = spline + n, s1 = s2 - 1;
+    for (/* empty */; s2 > spline; s2--, s1--) {
+	s2->x = s1->x;
+	s2->y = s1->y;
+    }
+    if (isClosed) {
+	spline[0].x = spline[n].x;
+	spline[0].y = spline[n].y;
+    } else {
+	/* Third derivative is 0.0 for the first and last interval. */
+	spline[0].x = spline[1].x; 
+	spline[0].y = spline[1].y; 
+	spline[n + 1].x = spline[n].x;
+	spline[n + 1].y = spline[n].y;
+    }
+    Blt_Free( A);
+    return spline;
+}
+
+
+/*
+ * Calculate interpolated values of the spline function (defined via p_cntr
+ * and the second derivatives dxdt2[] and dydt2[]). The number of tabulated
+ * values is n. On an equidistant grid n_intpol values are calculated.
+ */
+static int
+CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline)
+    Point2D origPts[];
+    int nOrigPts;
+    Point2D intpPts[];
+    int nIntpPts;
+    CubicSpline spline[];
+{
+    double t, tSkip, tMax;
+    Point2D p, q;
+    double d, hx, dx0, dx01, hy, dy0, dy01;
+    register int i, j, count;
+
+    /* Sum the lengths of all the segments (intervals). */
+    tMax = 0.0;
+    for (i = 0; i < nOrigPts - 1; i++) {
+	tMax += spline[i].t;
+    }
+
+    /* Need a better way of doing this... */
+
+    /* The distance between interpolated points */
+    tSkip = (1. - 1e-7) * tMax / (nIntpPts - 1);
+    
+    t = 0.0;			/* Spline parameter value. */
+    q = origPts[0];
+    count = 0;
+
+    intpPts[count++] = q;	/* First point. */
+    t += tSkip;
+    
+    for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+	d = spline[i].t;	/* Interval length */
+	p = q;
+	q = origPts[i+1];
+	hx = (q.x - p.x) / d;
+	hy = (q.y - p.y) / d;
+	dx0 = (spline[j].x + 2 * spline[i].x) / 6.0;
+	dy0 = (spline[j].y + 2 * spline[i].y) / 6.0;
+	dx01 = (spline[j].x - spline[i].x) / (6.0 * d);
+	dy01 = (spline[j].y - spline[i].y) / (6.0 * d);
+	while (t <= spline[i].t) { /* t in current interval ? */
+	    p.x += t * (hx + (t - d) * (dx0 + t * dx01));
+	    p.y += t * (hy + (t - d) * (dy0 + t * dy01));
+	    intpPts[count++] = p;
+	    t += tSkip;
+	}
+	/* Parameter t relative to start of next interval */
+	t -= spline[i].t;
+    }
+    return count;
+}
+
+/*
+ * Generate a cubic spline curve through the points (x_i,y_i) which are
+ * stored in the linked list p_cntr.
+ * The spline is defined as a 2d-function s(t) = (x(t),y(t)), where the
+ * parameter t is the length of the linear stroke.
+ */
+int
+Blt_NaturalParametricSpline(origPts, nOrigPts, extsPtr, isClosed, 
+	intpPts, nIntpPts)
+    Point2D origPts[];
+    int nOrigPts;
+    Extents2D *extsPtr;
+    int isClosed;
+    Point2D *intpPts;
+    int nIntpPts;
+{
+    double unitX, unitY;	/* To define norm (x,y)-plane */
+    CubicSpline *spline;
+    int result;
+
+    if (nOrigPts < 3) {
+	return 0;
+    }
+    if (isClosed) {
+	origPts[nOrigPts].x = origPts[0].x;
+	origPts[nOrigPts].y = origPts[0].y;
+	nOrigPts++;
+    }
+    /* Width and height of the grid is used at unit length (2d-norm) */
+    unitX = extsPtr->right - extsPtr->left;
+    unitY = extsPtr->bottom - extsPtr->top;
+
+    if (unitX < FLT_EPSILON) {
+	unitX = FLT_EPSILON;
+    }
+    if (unitY < FLT_EPSILON) {
+	unitY = FLT_EPSILON;
+    }
+    /* Calculate parameters for cubic spline: 
+     *		t     = arc length of interval.
+     *		dxdt2 = second derivatives of x with respect to t, 
+     *		dydt2 = second derivatives of y with respect to t, 
+     */
+    spline = CubicSlopes(origPts, nOrigPts, isClosed, unitX, unitY);
+    if (spline == NULL) {
+	return 0;
+    }
+    result= CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline);
+    Blt_Free(spline);
+    return result;
+}
+
+static void
+CatromCoeffs(p, a, b, c, d)
+    Point2D *p;
+    Point2D *a, *b, *c, *d;
+{
+    a->x = -p[0].x + 3.0 * p[1].x - 3.0 * p[2].x + p[3].x;
+    b->x = 2.0 * p[0].x - 5.0 * p[1].x + 4.0 * p[2].x - p[3].x;
+    c->x = -p[0].x + p[2].x;
+    d->x = 2.0 * p[1].x;
+    a->y = -p[0].y + 3.0 * p[1].y - 3.0 * p[2].y + p[3].y;
+    b->y = 2.0 * p[0].y - 5.0 * p[1].y + 4.0 * p[2].y - p[3].y;
+    c->y = -p[0].y + p[2].y;
+    d->y = 2.0 * p[1].y;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ParametricCatromSpline --
+ *
+ *	Computes a spline based upon the data points, returning a new
+ *	(larger) coordinate array or points.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_CatromParametricSpline(points, nPoints, intpPts, nIntpPts)
+    Point2D *points;
+    int nPoints;
+    Point2D *intpPts;
+    int nIntpPts;
+{
+    register int i;
+    Point2D *origPts;
+    double t;
+    int interval;
+    Point2D a, b, c, d;
+
+    assert(nPoints > 0);
+    /*
+     * The spline is computed in screen coordinates instead of data
+     * points so that we can select the abscissas of the interpolated
+     * points from each pixel horizontally across the plotting area.
+     */
+    origPts = Blt_Malloc((nPoints + 4) * sizeof(Point2D));
+    memcpy(origPts + 1, points, sizeof(Point2D) * nPoints);
+
+    origPts[0] = origPts[1];
+    origPts[nPoints + 2] = origPts[nPoints + 1] = origPts[nPoints];
+
+    for (i = 0; i < nIntpPts; i++) {
+	interval = (int)intpPts[i].x;
+	t = intpPts[i].y;
+	assert(interval < nPoints);
+	CatromCoeffs(origPts + interval, &a, &b, &c, &d);
+	intpPts[i].x = (d.x + t * (c.x + t * (b.x + t * a.x))) / 2.0;
+	intpPts[i].y = (d.y + t * (c.y + t * (b.y + t * a.y))) / 2.0;
+    }
+    Blt_Free(origPts);
+    return 1;
+}
Index: trunk/kitgen/8.x/blt/generic/bltSwitch.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltSwitch.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltSwitch.c	(revision 175)
@@ -0,0 +1,531 @@
+/*
+ * bltSwitch.c --
+ *
+ *	This module implements command/argument switch parsing 
+ *	procedures for the BLT toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "bltSwitch.h"
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FindSwitchSpec --
+ *
+ *	Search through a table of configuration specs, looking for
+ *	one that matches a given argvName.
+ *
+ * Results:
+ *	The return value is a pointer to the matching entry, or NULL
+ *	if nothing matched.  In that case an error message is left
+ *	in the interp's result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+static Blt_SwitchSpec *
+FindSwitchSpec(interp, specs, name, needFlags, hateFlags)
+    Tcl_Interp *interp;		/* Used for reporting errors. */
+    Blt_SwitchSpec *specs;	/* Pointer to table of configuration
+				 * specifications for a widget. */
+    char *name;			/* Name (suitable for use in a "switch"
+				 * command) identifying particular option. */
+    int needFlags;		/* Flags that must be present in matching
+				 * entry. */
+    int hateFlags;		/* Flags that must NOT be present in
+				 * matching entry. */
+{
+    register Blt_SwitchSpec *specPtr;
+    register char c;		/* First character of current argument. */
+    Blt_SwitchSpec *matchPtr;	/* Matching spec, or NULL. */
+    size_t length;
+
+    c = name[1];
+    length = strlen(name);
+    matchPtr = NULL;
+    
+    for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+	if (specPtr->switchName == NULL) {
+	    continue;
+	}
+	if ((specPtr->switchName[1] != c) 
+	    || (strncmp(specPtr->switchName, name, length) != 0)) {
+	    continue;
+	}
+	if (((specPtr->flags & needFlags) != needFlags)
+	    || (specPtr->flags & hateFlags)) {
+	    continue;
+	}
+	if (specPtr->switchName[length] == 0) {
+	    return specPtr;	/* Stop on a perfect match. */
+	}
+	if (matchPtr != NULL) {
+	    Tcl_AppendResult(interp, "ambiguous option \"", name, "\"", 
+		(char *) NULL);
+	    return (Blt_SwitchSpec *) NULL;
+	}
+	matchPtr = specPtr;
+    }
+
+    if (matchPtr == NULL) {
+	Tcl_AppendResult(interp, "unknown option \"", name, "\"", (char *)NULL);
+	return (Blt_SwitchSpec *) NULL;
+    }
+    return matchPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoSwitch --
+ *
+ *	This procedure applies a single configuration option
+ *	to a widget record.
+ *
+ * Results:
+ *	A standard Tcl return value.
+ *
+ * Side effects:
+ *	WidgRec is modified as indicated by specPtr and value.
+ *	The old value is recycled, if that is appropriate for
+ *	the value type.
+ *
+ *--------------------------------------------------------------
+ */
+static int
+DoSwitch(interp, specPtr, string, record)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Blt_SwitchSpec *specPtr;	/* Specifier to apply. */
+    char *string;		/* Value to use to fill in widgRec. */
+    ClientData record;		/* Record whose fields are to be
+				 * modified.  Values must be properly
+				 * initialized. */
+{
+    char *ptr;
+    int isNull;
+    int count;
+
+    isNull = ((*string == '\0') && (specPtr->flags & BLT_SWITCH_NULL_OK));
+    do {
+	ptr = (char *)record + specPtr->offset;
+	switch (specPtr->type) {
+	case BLT_SWITCH_BOOLEAN:
+	    if (Tcl_GetBoolean(interp, string, (int *)ptr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_SWITCH_INT:
+	    if (Tcl_GetInt(interp, string, (int *)ptr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_SWITCH_INT_NONNEGATIVE:
+	    if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (count < 0) {
+		Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+				 "can't be negative", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    *((int *)ptr) = count;
+	    break;
+
+	case BLT_SWITCH_INT_POSITIVE:
+	    if (Tcl_GetInt(interp, string, &count) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (count <= 0) {
+		Tcl_AppendResult(interp, "bad value \"", string, "\": ",
+			"must be positive", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    *((int *)ptr) = count;
+	    break;
+
+	case BLT_SWITCH_DOUBLE:
+	    if (Tcl_GetDouble(interp, string, (double *)ptr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_SWITCH_STRING: 
+	    {
+		char *old, *new, **strPtr;
+		
+		strPtr = (char **)ptr;
+		if (isNull) {
+		    new = NULL;
+		} else {
+		    new = Blt_Strdup(string);
+		}
+		old = *strPtr;
+		if (old != NULL) {
+		    Blt_Free(old);
+		}
+		*strPtr = new;
+	    }
+	    break;
+
+	case BLT_SWITCH_LIST:
+	    if (Tcl_SplitList(interp, string, &count, (char ***)ptr) 
+		!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	case BLT_SWITCH_CUSTOM:
+	    if ((*specPtr->customPtr->parseProc) \
+		(specPtr->customPtr->clientData, interp, specPtr->switchName,
+			string, record, specPtr->offset) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    break;
+
+	default: 
+	    Tcl_AppendResult(interp, "bad switch table: unknown type \"",
+		 Blt_Itoa(specPtr->type), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	specPtr++;
+    } while ((specPtr->switchName == NULL) && 
+	     (specPtr->type != BLT_SWITCH_END));
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ProcessSwitches --
+ *
+ *	Process command-line options and database options to
+ *	fill in fields of a widget record with resources and
+ *	other parameters.
+ *
+ * Results:
+ *	Returns the number of arguments comsumed by parsing the
+ *	command line.  If an error occurred, -1 will be returned
+ *	and an error messages can be found as the interpreter
+ *	result.
+ *
+ * Side effects:
+ *	The fields of widgRec get filled in with information
+ *	from argc/argv and the option database.  Old information
+ *	in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ProcessSwitches(interp, specs, argc, argv, record, flags)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Blt_SwitchSpec *specs;	/* Describes legal options. */
+    int argc;			/* Number of elements in argv. */
+    char **argv;		/* Command-line options. */
+    char *record;		/* Record whose fields are to be
+				 * modified.  Values must be properly
+				 * initialized. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in switch specs
+				 * for them to be considered.  Also,
+				 * may have BLT_SWITCH_ARGV_ONLY set. */
+{
+    register int count;
+    char *arg;
+    register Blt_SwitchSpec *specPtr;
+    int needFlags;		/* Specs must contain this set of flags
+				 * or else they are not considered. */
+    int hateFlags;		/* If a spec contains any bits here, it's
+				 * not considered. */
+
+    needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
+    hateFlags = 0;
+
+    /*
+     * Pass 1:  Clear the change flags on all the specs so that we 
+     *          can check it later.
+     */
+    for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+	specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
+    }
+    /*
+     * Pass 2:  Process the arguments that match entries in the specs.
+     *		It's an error if the argument doesn't match anything.
+     */
+    for (count = 0; count < argc; count++) {
+	arg = argv[count];
+	if (flags & BLT_SWITCH_OBJV_PARTIAL) {
+	    if ((arg[0] != '-') || ((arg[1] == '-') && (argv[2] == '\0'))) {
+		/* 
+		 * If the argument doesn't start with a '-' (not a switch)
+		 * or is '--', stop processing and return the number of
+		 * arguments comsumed. 
+		 */
+		return count;
+	    }
+	}
+	specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags);
+	if (specPtr == NULL) {
+	    return -1;
+	}
+	if (specPtr->type == BLT_SWITCH_FLAG) {
+	    char *ptr;
+	    
+	    ptr = record + specPtr->offset;
+	    *((int *)ptr) |= specPtr->value;
+	} else if (specPtr->type == BLT_SWITCH_VALUE) {
+	    char *ptr;
+	    
+	    ptr = record + specPtr->offset;
+	    *((int *)ptr) = specPtr->value;
+	} else {
+	    if ((count + 1) == argc) {
+		Tcl_AppendResult(interp, "value for \"", arg, "\" missing", 
+			(char *) NULL);
+		return -1;
+	    }
+	    count++;
+	    if (DoSwitch(interp, specPtr, argv[count], record) != TCL_OK) {
+		char msg[100];
+
+		sprintf(msg, "\n    (processing \"%.40s\" option)", 
+			specPtr->switchName);
+		Tcl_AddErrorInfo(interp, msg);
+		return -1;
+	    }
+	}
+	specPtr->flags |= BLT_SWITCH_SPECIFIED;
+    }
+    return count;
+}
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_ProcessObjSwitches --
+ *
+ *	Process command-line options and database options to
+ *	fill in fields of a widget record with resources and
+ *	other parameters.
+ *
+ * Results:
+ *	Returns the number of arguments comsumed by parsing the
+ *	command line.  If an error occurred, -1 will be returned
+ *	and an error messages can be found as the interpreter
+ *	result.
+ *
+ * Side effects:
+ *	The fields of widgRec get filled in with information
+ *	from argc/argv and the option database.  Old information
+ *	in widgRec's fields gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_ProcessObjSwitches(interp, specs, objc, objv, record, flags)
+    Tcl_Interp *interp;		/* Interpreter for error reporting. */
+    Blt_SwitchSpec *specs;	/* Describes legal options. */
+    int objc;			/* Number of elements in argv. */
+    Tcl_Obj *CONST *objv;	/* Command-line options. */
+    char *record;		/* Record whose fields are to be
+				 * modified.  Values must be properly
+				 * initialized. */
+    int flags;			/* Used to specify additional flags
+				 * that must be present in switch specs
+				 * for them to be considered.  Also,
+				 * may have BLT_SWITCH_ARGV_ONLY set. */
+{
+    register Blt_SwitchSpec *specPtr;
+    register int count;
+    int needFlags;		/* Specs must contain this set of flags
+				 * or else they are not considered. */
+    int hateFlags;		/* If a spec contains any bits here, it's
+				 * not considered. */
+
+    needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
+    hateFlags = 0;
+
+    /*
+     * Pass 1:  Clear the change flags on all the specs so that we 
+     *          can check it later.
+     */
+    for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+	specPtr->flags &= ~BLT_SWITCH_SPECIFIED;
+    }
+    /*
+     * Pass 2:  Process the arguments that match entries in the specs.
+     *		It's an error if the argument doesn't match anything.
+     */
+    for (count = 0; count < objc; count++) {
+	char *arg;
+
+	arg = Tcl_GetString(objv[count]);
+	if (flags & BLT_SWITCH_OBJV_PARTIAL) {
+	    if ((arg[0] != '-') || ((arg[1] == '-') && (arg[2] == '\0'))) {
+		/* 
+		 * If the argument doesn't start with a '-' (not a switch)
+		 * or is '--', stop processing and return the number of
+		 * arguments comsumed. 
+		 */
+		return count;
+	    }
+	}
+	specPtr = FindSwitchSpec(interp, specs, arg, needFlags, hateFlags);
+	if (specPtr == NULL) {
+	    return -1;
+	}
+	if (specPtr->type == BLT_SWITCH_FLAG) {
+	    char *ptr;
+	    
+	    ptr = record + specPtr->offset;
+	    *((int *)ptr) |= specPtr->value;
+	} else if (specPtr->type == BLT_SWITCH_VALUE) {
+	    char *ptr;
+	    
+	    ptr = record + specPtr->offset;
+	    *((int *)ptr) = specPtr->value;
+	} else {
+	    count++;
+	    if (count == objc) {
+		Tcl_AppendResult(interp, "value for \"", arg, "\" missing", 
+				 (char *) NULL);
+		return -1;
+	    }
+	    arg = Tcl_GetString(objv[count]);
+	    if (DoSwitch(interp, specPtr, arg, record) != TCL_OK) {
+		char msg[100];
+
+		sprintf(msg, "\n    (processing \"%.40s\" option)", 
+			specPtr->switchName);
+		Tcl_AddErrorInfo(interp, msg);
+		return -1;
+	    }
+	}
+	specPtr->flags |= BLT_SWITCH_SPECIFIED;
+    }
+    return count;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeSwitches --
+ *
+ *	Free up all resources associated with switch options.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* ARGSUSED */
+void
+Blt_FreeSwitches(specs, record, needFlags)
+    Blt_SwitchSpec *specs;	/* Describes legal options. */
+    char *record;		/* Record whose fields contain current
+				 * values for options. */
+    int needFlags;		/* Used to specify additional flags
+				 * that must be present in config specs
+				 * for them to be considered. */
+{
+    register Blt_SwitchSpec *specPtr;
+
+    for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+	if ((specPtr->flags & needFlags) == needFlags) {
+	    char *ptr;
+
+	    ptr = record + specPtr->offset;
+	    switch (specPtr->type) {
+	    case BLT_SWITCH_STRING:
+	    case BLT_SWITCH_LIST:
+		if (*((char **) ptr) != NULL) {
+		    Blt_Free(*((char **) ptr));
+		    *((char **) ptr) = NULL;
+		}
+		break;
+
+	    case BLT_SWITCH_CUSTOM:
+		if ((*(char **)ptr != NULL) && 
+		    (specPtr->customPtr->freeProc != NULL)) {
+		    (*specPtr->customPtr->freeProc)(*(char **)ptr);
+		    *((char **) ptr) = NULL;
+		}
+		break;
+
+	    default:
+		break;
+	    }
+	}
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SwitchModified --
+ *
+ *      Given the configuration specifications and one or more option
+ *	patterns (terminated by a NULL), indicate if any of the matching
+ *	configuration options has been reset.
+ *
+ * Results:
+ *      Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+int Blt_SwitchChanged
+TCL_VARARGS_DEF(Blt_SwitchSpec *, arg1)
+{
+    va_list argList;
+    Blt_SwitchSpec *specs;
+    register Blt_SwitchSpec *specPtr;
+    register char *switchName;
+
+    specs = TCL_VARARGS_START(Blt_SwitchSpec *, arg1, argList);
+    while ((switchName = va_arg(argList, char *)) != NULL) {
+	for (specPtr = specs; specPtr->type != BLT_SWITCH_END; specPtr++) {
+	    if ((Tcl_StringMatch(specPtr->switchName, switchName)) &&
+		(specPtr->flags & BLT_SWITCH_SPECIFIED)) {
+		va_end(argList);
+		return 1;
+	    }
+	}
+    }
+    va_end(argList);
+    return 0;
+}
Index: trunk/kitgen/8.x/blt/generic/bltSwitch.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltSwitch.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltSwitch.h	(revision 175)
@@ -0,0 +1,78 @@
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+
+typedef int (Blt_SwitchParseProc) _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, char *switchName, char *value, char *record, 
+	int offset));
+
+typedef void (Blt_SwitchFreeProc) _ANSI_ARGS_((char *ptr));
+
+typedef struct {
+    Blt_SwitchParseProc *parseProc;	/* Procedure to parse a switch value
+					 * and store it in its converted 
+					 * form in the data record. */
+    Blt_SwitchFreeProc *freeProc;	/* Procedure to free a switch. */
+    ClientData clientData;		/* Arbitrary one-word value
+					 * used by switch parser,
+					 * passed to parseProc. */
+} Blt_SwitchCustom;
+
+/*
+ * Type values for Blt_SwitchSpec structures.  See the user
+ * documentation for details.
+ */
+typedef enum {
+    BLT_SWITCH_BOOLEAN, BLT_SWITCH_INT, BLT_SWITCH_INT_POSITIVE,
+    BLT_SWITCH_INT_NONNEGATIVE, BLT_SWITCH_DOUBLE, BLT_SWITCH_STRING, 
+    BLT_SWITCH_LIST, BLT_SWITCH_FLAG, BLT_SWITCH_VALUE, BLT_SWITCH_CUSTOM, 
+    BLT_SWITCH_END
+} Blt_SwitchTypes;
+
+typedef struct {
+    Blt_SwitchTypes type;	/* Type of option, such as BLT_SWITCH_COLOR;
+				 * see definitions below.  Last option in
+				 * table must have type BLT_SWITCH_END. */
+    char *switchName;		/* Switch used to specify option in argv.
+				 * NULL means this spec is part of a group. */
+    int offset;			/* Where in widget record to store value;
+				 * use Blt_Offset macro to generate values
+				 * for this. */
+    int flags;			/* Any combination of the values defined
+				 * below. */
+    Blt_SwitchCustom *customPtr; /* If type is BLT_SWITCH_CUSTOM then this is
+				 * a pointer to info about how to parse and
+				 * print the option.  Otherwise it is
+				 * irrelevant. */
+    int value;
+} Blt_SwitchSpec;
+
+#define BLT_SWITCH_ARGV_ONLY		(1<<0)
+#define BLT_SWITCH_OBJV_ONLY		(1<<0)
+#define BLT_SWITCH_ARGV_PARTIAL		(1<<1)
+#define BLT_SWITCH_OBJV_PARTIAL		(1<<1)
+/*
+ * Possible flag values for Blt_SwitchSpec structures.  Any bits at
+ * or above BLT_SWITCH_USER_BIT may be used by clients for selecting
+ * certain entries.  
+ */
+#define BLT_SWITCH_NULL_OK		(1<<0)
+#define BLT_SWITCH_DONT_SET_DEFAULT	(1<<3)
+#define BLT_SWITCH_SPECIFIED		(1<<4)
+#define BLT_SWITCH_USER_BIT		(1<<8)
+
+extern int Blt_ProcessSwitches _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_SwitchSpec *specs, int argc, char **argv, char *record, int flags));
+
+extern void Blt_FreeSwitches _ANSI_ARGS_((Blt_SwitchSpec *specs, char *record, 
+	int flags));
+
+extern int Blt_SwitchChanged _ANSI_ARGS_(TCL_VARARGS(Blt_SwitchSpec *, specs));
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+extern int Blt_ProcessObjSwitches _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_SwitchSpec *specPtr, int objc, Tcl_Obj *CONST *objv, char *record, 
+	int flags));
+#endif
Index: trunk/kitgen/8.x/blt/generic/bltTable.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTable.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTable.c	(revision 175)
@@ -0,0 +1,4973 @@
+/*
+ * bltTable.c --
+ *
+ *	This module implements a table-based geometry manager
+ *	for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "table" geometry manager was created by George Howlett.
+ */
+
+/*
+ * To do:
+ *
+ * 3) No way to detect if widget is already a container of another
+ *    geometry manager.  This one is especially bad with toplevel
+ *    widgets, causing the window manager to lock-up trying to handle the
+ *    myriads of resize requests.
+ *
+ *    Note: This problem continues in Tk 8.x.  It's possible for a widget
+ *	    to be a container for two different geometry managers.  Each manager
+ *	    will set its own requested geometry for the container widget. The
+ *	    winner sets the geometry last (sometimes ad infinitum).
+ *
+ * 7) Relative sizing of partitions?
+ *
+ */
+
+#include "bltInt.h"
+
+#include "bltTable.h"
+
+#define TABLE_THREAD_KEY	"BLT Table Data"
+#define TABLE_DEF_PAD		0
+
+/*
+ * Default values for widget attributes.
+ */
+#define DEF_TABLE_ANCHOR	"center"
+#define DEF_TABLE_COLUMNS 	"0"
+#define DEF_TABLE_FILL		"none"
+#define DEF_TABLE_PAD		"0"
+#define DEF_TABLE_PROPAGATE 	"1"
+#define DEF_TABLE_RESIZE	"both"
+#define DEF_TABLE_ROWS		"0"
+#define DEF_TABLE_SPAN		"1"
+#define DEF_TABLE_CONTROL	"normal"
+#define DEF_TABLE_WEIGHT	"1.0"
+
+#define ENTRY_DEF_PAD		0
+#define ENTRY_DEF_ANCHOR	TK_ANCHOR_CENTER
+#define ENTRY_DEF_FILL		FILL_NONE
+#define ENTRY_DEF_SPAN		1
+#define ENTRY_DEF_CONTROL	CONTROL_NORMAL
+#define ENTRY_DEF_IPAD		0
+
+#define ROWCOL_DEF_RESIZE	(RESIZE_BOTH | RESIZE_VIRGIN)
+#define ROWCOL_DEF_PAD		0
+#define ROWCOL_DEF_WEIGHT	1.0
+
+static Blt_Uid rowUid, columnUid;
+
+static void WidgetGeometryProc _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin));
+static void WidgetCustodyProc _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin));
+
+static Tk_GeomMgr tableMgrInfo =
+{
+    "table",			/* Name of geometry manager used by winfo */
+    WidgetGeometryProc,		/* Procedure to for new geometry requests */
+    WidgetCustodyProc,		/* Procedure when widget is taken away */
+};
+
+static int StringToLimits _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+
+static char *LimitsToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption limitsOption =
+{
+    StringToLimits, LimitsToString, (ClientData)0
+};
+
+static int StringToResize _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *ResizeToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption resizeOption =
+{
+    StringToResize, ResizeToString, (ClientData)0
+};
+
+static int StringToControl _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *ControlToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtr));
+
+static Tk_CustomOption controlOption =
+{
+    StringToControl, ControlToString, (ClientData)0
+};
+
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltDistanceOption;
+
+static Tk_ConfigSpec rowConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+	DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
+	DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
+	TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
+    {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+	DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
+	TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tk_ConfigSpec columnConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+	DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
+	DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
+	TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
+    {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+	DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
+	TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
+    {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec entryConfigSpecs[] =
+{
+    {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
+	DEF_TABLE_ANCHOR, Tk_Offset(Entry, anchor),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL,
+	DEF_TABLE_SPAN, Tk_Offset(Entry, column.span),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-columncontrol", "columnControl", (char *)NULL,
+	DEF_TABLE_CONTROL, Tk_Offset(Entry, column.control),
+	TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
+    {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, column.span), 0},
+    {TK_CONFIG_SYNONYM, "-ccontrol", "columnControl", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, column.control), 0},
+    {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
+	DEF_TABLE_FILL, Tk_Offset(Entry, fill),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_SYNONYM, "-height", "reqHeight", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, reqHeight), 0},
+    {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, padX), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, padY), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-ipadx", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, ipadX), 0, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-ipady", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, ipadY), 0, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-reqheight", "reqHeight", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, reqHeight), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_CUSTOM, "-reqwidth", "reqWidth", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, reqWidth), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL,
+	DEF_TABLE_SPAN, Tk_Offset(Entry, row.span),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-rowcontrol", "rowControl", (char *)NULL,
+	DEF_TABLE_CONTROL, Tk_Offset(Entry, row.control),
+	TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
+    {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, row.span), 0},
+    {TK_CONFIG_SYNONYM, "-rcontrol", "rowControl", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, row.control), 0},
+    {TK_CONFIG_SYNONYM, "-width", "reqWidth", (char *)NULL,
+	(char *)NULL, Tk_Offset(Entry, reqWidth), 0},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec tableConfigSpecs[] =
+{
+    {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
+	DEF_TABLE_PAD, Tk_Offset(Table, padX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
+	DEF_TABLE_PAD, Tk_Offset(Table, padY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_BOOLEAN, "-propagate", (char *)NULL, (char *)NULL,
+	DEF_TABLE_PROPAGATE, Tk_Offset(Table, propagate),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Table, reqHeight), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL,
+	(char *)NULL, Tk_Offset(Table, reqWidth), TK_CONFIG_NULL_OK,
+	&limitsOption},
+    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ * Forward declarations
+ */
+static void ArrangeTable _ANSI_ARGS_((ClientData clientData));
+static void DestroyTable _ANSI_ARGS_((DestroyData dataPtr));
+static void DestroyEntry _ANSI_ARGS_((Entry * entryPtr));
+static void TableEventProc _ANSI_ARGS_((ClientData clientData,
+	XEvent *eventPtr));
+static void BinEntry _ANSI_ARGS_((Table *tablePtr, Entry * entryPtr));
+static RowColumn *InitSpan _ANSI_ARGS_((PartitionInfo * infoPtr, int start,
+	int span));
+
+static EntrySearchProc FindEntry;
+static Tcl_CmdProc TableCmd;
+static Tcl_InterpDeleteProc TableInterpDeleteProc;
+static Tk_EventProc WidgetEventProc;
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToLimits --
+ *
+ *	Converts the list of elements into zero or more pixel values which
+ *	determine the range of pixel values possible.  An element can be in
+ *	any form accepted by Tk_GetPixels. The list has a different meaning
+ *	based upon the number of elements.
+ *
+ *	    # of elements:
+ *
+ *	    0 - the limits are reset to the defaults.
+ *	    1 - the minimum and maximum values are set to this
+ *		value, freezing the range at a single value.
+ *	    2 - first element is the minimum, the second is the
+ *		maximum.
+ *	    3 - first element is the minimum, the second is the
+ *		maximum, and the third is the nominal value.
+ *
+ *	Any element may be the empty string which indicates the default.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  The min and max fields
+ *	of the range are set.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToLimits(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Widget of table */
+    char *string;		/* New width list */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of limits */
+{
+    Limits *limitsPtr = (Limits *)(widgRec + offset);
+    char **elemArr;
+    int nElem;
+    int limArr[3];
+    Tk_Window winArr[3];
+    int flags;
+
+    elemArr = NULL;
+    nElem = 0;
+
+    /* Initialize limits to default values */
+    limArr[2] = LIMITS_NOM;
+    limArr[1] = LIMITS_MAX;
+    limArr[0] = LIMITS_MIN;
+    winArr[0] = winArr[1] = winArr[2] = NULL;
+    flags = 0;
+
+    if (string != NULL) {
+	int size;
+	int i;
+
+	if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (nElem > 3) {
+	    Tcl_AppendResult(interp, "wrong # limits \"", string, "\"",
+		(char *)NULL);
+	    goto error;
+	}
+	for (i = 0; i < nElem; i++) {
+	    if (elemArr[i][0] == '\0') {
+		continue;	/* Empty string: use default value */
+	    }
+	    flags |= (LIMITS_SET_BIT << i);
+	    if ((elemArr[i][0] == '.') &&
+		((elemArr[i][1] == '\0') || isalpha(UCHAR(elemArr[i][1])))) {
+		Tk_Window tkwin2;
+
+		/* Widget specified: save pointer to widget */
+		tkwin2 = Tk_NameToWindow(interp, elemArr[i], tkwin);
+		if (tkwin2 == NULL) {
+		    goto error;
+		}
+		winArr[i] = tkwin2;
+	    } else {
+		if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) {
+		    goto error;
+		}
+		if ((size < LIMITS_MIN) || (size > LIMITS_MAX)) {
+		    Tcl_AppendResult(interp, "bad limits \"", string, "\"",
+			(char *)NULL);
+		    goto error;
+		}
+		limArr[i] = size;
+	    }
+	}
+	Blt_Free(elemArr);
+    }
+    /*
+    * Check the limits specified.  We can't check the requested
+    * size of widgets.
+    */
+    switch (nElem) {
+    case 1:
+	flags |= (LIMITS_SET_MIN | LIMITS_SET_MAX);
+	if (winArr[0] == NULL) {
+	    limArr[1] = limArr[0];	/* Set minimum and maximum to value */
+	} else {
+	    winArr[1] = winArr[0];
+	}
+	break;
+
+    case 2:
+	if ((winArr[0] == NULL) && (winArr[1] == NULL) &&
+	    (limArr[1] < limArr[0])) {
+	    Tcl_AppendResult(interp, "bad range \"", string,
+		"\": min > max", (char *)NULL);
+	    return TCL_ERROR;	/* Minimum is greater than maximum */
+	}
+	break;
+
+    case 3:
+	if ((winArr[0] == NULL) && (winArr[1] == NULL)) {
+	    if (limArr[1] < limArr[0]) {
+		Tcl_AppendResult(interp, "bad range \"", string,
+		    "\": min > max", (char *)NULL);
+		return TCL_ERROR;	/* Minimum is greater than maximum */
+	    }
+	    if ((winArr[2] == NULL) &&
+		((limArr[2] < limArr[0]) || (limArr[2] > limArr[1]))) {
+		Tcl_AppendResult(interp, "nominal value \"", string,
+		    "\" out of range", (char *)NULL);
+		return TCL_ERROR;	/* Nominal is outside of range defined
+					 * by minimum and maximum */
+	    }
+	}
+	break;
+    }
+    limitsPtr->min = limArr[0];
+    limitsPtr->max = limArr[1];
+    limitsPtr->nom = limArr[2];
+    limitsPtr->wMin = winArr[0];
+    limitsPtr->wMax = winArr[1];
+    limitsPtr->wNom = winArr[2];
+    limitsPtr->flags = flags;
+    return TCL_OK;
+  error:
+    Blt_Free(elemArr);
+    return TCL_ERROR;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResetLimits --
+ *
+ *	Resets the limits to their default values.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+INLINE static void
+ResetLimits(limitsPtr)
+    Limits *limitsPtr;		/* Limits to be imposed on the value */
+{
+    limitsPtr->flags = 0;
+    limitsPtr->min = LIMITS_MIN;
+    limitsPtr->max = LIMITS_MAX;
+    limitsPtr->nom = LIMITS_NOM;
+    limitsPtr->wNom = limitsPtr->wMax = limitsPtr->wMin = NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetBoundedWidth --
+ *
+ *	Bounds a given width value to the limits described in the limit
+ *	structure.  The initial starting value may be overridden by the
+ *	nominal value in the limits.
+ *
+ * Results:
+ *	Returns the constrained value.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetBoundedWidth(width, limitsPtr)
+    int width;			/* Initial value to be constrained */
+    Limits *limitsPtr;		/* Limits to be imposed on the value */
+{
+    /*
+     * Check widgets for requested width values;
+     */
+    if (limitsPtr->wMin != NULL) {
+	limitsPtr->min = Tk_ReqWidth(limitsPtr->wMin);
+    }
+    if (limitsPtr->wMax != NULL) {
+	limitsPtr->max = Tk_ReqWidth(limitsPtr->wMax);
+    }
+    if (limitsPtr->wNom != NULL) {
+	limitsPtr->nom = Tk_ReqWidth(limitsPtr->wNom);
+    }
+    if (limitsPtr->flags & LIMITS_SET_NOM) {
+	width = limitsPtr->nom;	/* Override initial value */
+    }
+    if (width < limitsPtr->min) {
+	width = limitsPtr->min;	/* Bounded by minimum value */
+    } else if (width > limitsPtr->max) {
+	width = limitsPtr->max;	/* Bounded by maximum value */
+    }
+    return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetBoundedHeight --
+ *
+ *	Bounds a given value to the limits described in the limit
+ *	structure.  The initial starting value may be overridden by the
+ *	nominal value in the limits.
+ *
+ * Results:
+ *	Returns the constrained value.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetBoundedHeight(height, limitsPtr)
+    int height;			/* Initial value to be constrained */
+    Limits *limitsPtr;		/* Limits to be imposed on the value */
+{
+    /*
+     * Check widgets for requested height values;
+     */
+    if (limitsPtr->wMin != NULL) {
+	limitsPtr->min = Tk_ReqHeight(limitsPtr->wMin);
+    }
+    if (limitsPtr->wMax != NULL) {
+	limitsPtr->max = Tk_ReqHeight(limitsPtr->wMax);
+    }
+    if (limitsPtr->wNom != NULL) {
+	limitsPtr->nom = Tk_ReqHeight(limitsPtr->wNom);
+    }
+    if (limitsPtr->flags & LIMITS_SET_NOM) {
+	height = limitsPtr->nom;/* Override initial value */
+    }
+    if (height < limitsPtr->min) {
+	height = limitsPtr->min;/* Bounded by minimum value */
+    } else if (height > limitsPtr->max) {
+	height = limitsPtr->max;/* Bounded by maximum value */
+    }
+    return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfLimits --
+ *
+ *	Convert the values into a list representing the limits.
+ *
+ * Results:
+ *	The static string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfLimits(limitsPtr)
+    Limits *limitsPtr;
+{
+    Tcl_DString buffer;
+#define STRING_SPACE 200
+    static char string[STRING_SPACE + 1];
+
+    Tcl_DStringInit(&buffer);
+
+    if (limitsPtr->wMin != NULL) {
+	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMin));
+    } else if (limitsPtr->flags & LIMITS_SET_MIN) {
+	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->min));
+    } else {
+	Tcl_DStringAppendElement(&buffer, "");
+    }
+
+    if (limitsPtr->wMax != NULL) {
+	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMax));
+    } else if (limitsPtr->flags & LIMITS_SET_MAX) {
+	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->max));
+    } else {
+	Tcl_DStringAppendElement(&buffer, "");
+    }
+
+    if (limitsPtr->wNom != NULL) {
+	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wNom));
+    } else if (limitsPtr->flags & LIMITS_SET_NOM) {
+	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->nom));
+    } else {
+	Tcl_DStringAppendElement(&buffer, "");
+    }
+    strncpy(string, Tcl_DStringValue(&buffer), STRING_SPACE);
+    string[STRING_SPACE] = '\0';
+    return string;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LimitsToString --
+ *
+ *	Convert the limits of the pixel values allowed into a list.
+ *
+ * Results:
+ *	The string representation of the limits is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+LimitsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Row/column structure record */
+    int offset;			/* Offset of widget RowColumn record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation routine */
+{
+    Limits *limitsPtr = (Limits *)(widgRec + offset);
+
+    return NameOfLimits(limitsPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToResize --
+ *
+ *	Converts the resize mode into its numeric representation.  Valid
+ *	mode strings are "none", "expand", "shrink", or "both".
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToResize(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Resize style string */
+    char *widgRec;		/* Entry structure record */
+    int offset;			/* Offset of style in record */
+{
+    int *resizePtr = (int *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+	*resizePtr = RESIZE_NONE;
+    } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+	*resizePtr = RESIZE_BOTH;
+    } else if ((c == 'e') && (strncmp(string, "expand", length) == 0)) {
+	*resizePtr = RESIZE_EXPAND;
+    } else if ((c == 's') && (strncmp(string, "shrink", length) == 0)) {
+	*resizePtr = RESIZE_SHRINK;
+    } else {
+	Tcl_AppendResult(interp, "bad resize argument \"", string,
+	    "\": should be \"none\", \"expand\", \"shrink\", or \"both\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfResize --
+ *
+ *	Converts the resize value into its string representation.
+ *
+ * Results:
+ *	Returns a pointer to the static name string.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfResize(resize)
+    int resize;
+{
+    switch (resize & RESIZE_BOTH) {
+    case RESIZE_NONE:
+	return "none";
+    case RESIZE_EXPAND:
+	return "expand";
+    case RESIZE_SHRINK:
+	return "shrink";
+    case RESIZE_BOTH:
+	return "both";
+    default:
+	return "unknown resize value";
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResizeToString --
+ *
+ *	Returns resize mode string based upon the resize flags.
+ *
+ * Results:
+ *	The resize mode string is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ResizeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Row/column structure record */
+    int offset;			/* Offset of resize in RowColumn record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int resize = *(int *)(widgRec + offset);
+
+    return NameOfResize(resize);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * StringToControl --
+ *
+ *	Converts the control string into its numeric representation.
+ *	Valid control strings are "none", "normal", and "full".
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToControl(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* Control style string */
+    char *widgRec;		/* Entry structure record */
+    int offset;			/* Offset of style in record */
+{
+    double *controlPtr = (double *)(widgRec + offset);
+    unsigned int length;
+    int bool;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if (Tcl_GetBoolean(NULL, string, &bool) == TCL_OK) {
+	*controlPtr = bool;
+	return TCL_OK;
+    }
+    if ((c == 'n') && (length > 1) &&
+	(strncmp(string, "normal", length) == 0)) {
+	*controlPtr = CONTROL_NORMAL;
+    } else if ((c == 'n') && (length > 1) &&
+	(strncmp(string, "none", length) == 0)) {
+	*controlPtr = CONTROL_NONE;
+    } else if ((c == 'f') && (strncmp(string, "full", length) == 0)) {
+	*controlPtr = CONTROL_FULL;
+    } else {
+	double control;
+
+	if ((Tcl_GetDouble(interp, string, &control) != TCL_OK) ||
+	    (control < 0.0)) {
+	    Tcl_AppendResult(interp, "bad control argument \"", string,
+		"\": should be \"normal\", \"none\", or \"full\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	*controlPtr = control;
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * NameOfControl --
+ *
+ *	Converts the control value into its string representation.
+ *
+ * Results:
+ *	Returns a pointer to the static name string.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static char *
+NameOfControl(control)
+    double control;
+{
+    if (control == CONTROL_NORMAL) {
+	return "normal";
+    } else if (control == CONTROL_NONE) {
+	return "none";
+    } else if (control == CONTROL_FULL) {
+	return "full";
+    } else {
+	static char string[TCL_DOUBLE_SPACE + 1];
+
+	sprintf(string, "%g", control);
+	return string;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ControlToString --
+ *
+ *	Returns control mode string based upon the control flags.
+ *
+ * Results:
+ *	The control mode string is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ControlToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Row/column structure record */
+    int offset;			/* Offset of control in RowColumn record */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    double control = *(double *)(widgRec + offset);
+
+    return NameOfControl(control);
+}
+
+
+static void
+EventuallyArrangeTable(tablePtr)
+    Table *tablePtr;
+{
+    if (!(tablePtr->flags & ARRANGE_PENDING)) {
+	tablePtr->flags |= ARRANGE_PENDING;
+	Tcl_DoWhenIdle(ArrangeTable, tablePtr);
+    }
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TableEventProc --
+ *
+ *	This procedure is invoked by the Tk event handler when the
+ *	container widget is reconfigured or destroyed.
+ *
+ *	The table will be rearranged at the next idle point if the
+ *	container widget has been resized or moved. There's a
+ *	distinction made between parent and non-parent container
+ *	arrangements.  If the container is moved and it's the parent
+ *	of the widgets, they're are moved automatically.  If it's
+ *	not the parent, those widgets need to be moved manually.
+ *	This can be a performance hit in rare cases where we're
+ *	scrolling the container (by moving the window) and there
+ *	are lots of non-child widgets arranged insided.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Arranges for the table associated with tkwin to have its
+ *	layout re-computed and drawn at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TableEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Information about widget */
+    XEvent *eventPtr;		/* Information about event */
+{
+    register Table *tablePtr = clientData;
+
+    if (eventPtr->type == ConfigureNotify) {
+	if ((tablePtr->container.width != Tk_Width(tablePtr->tkwin)) ||
+	    (tablePtr->container.height != Tk_Height(tablePtr->tkwin))
+	    || (tablePtr->flags & NON_PARENT)) {
+	    EventuallyArrangeTable(tablePtr);
+	}
+    } else if (eventPtr->type == DestroyNotify) {
+	if (tablePtr->flags & ARRANGE_PENDING) {
+	    Tcl_CancelIdleCall(ArrangeTable, tablePtr);
+	}
+	tablePtr->tkwin = NULL;
+	Tcl_EventuallyFree(tablePtr, DestroyTable);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetEventProc --
+ *
+ *	This procedure is invoked by the Tk event handler when
+ *	StructureNotify events occur in a widget managed by the table.
+ *	For example, when a managed widget is destroyed, it frees the
+ *	corresponding entry structure and arranges for the table
+ *	layout to be re-computed at the next idle point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	If the managed widget was deleted, the Entry structure gets
+ *	cleaned up and the table is rearranged.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+WidgetEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Pointer to Entry structure for widget
+				 * referred to by eventPtr. */
+    XEvent *eventPtr;		/* Describes what just happened. */
+{
+    Entry *entryPtr = (Entry *) clientData;
+    Table *tablePtr = entryPtr->tablePtr;
+
+    if (eventPtr->type == ConfigureNotify) {
+	int borderWidth;
+
+	tablePtr->flags |= REQUEST_LAYOUT;
+	borderWidth = Tk_Changes(entryPtr->tkwin)->border_width;
+	if (entryPtr->borderWidth != borderWidth) {
+	    entryPtr->borderWidth = borderWidth;
+	    EventuallyArrangeTable(tablePtr);
+	}
+    } else if (eventPtr->type == DestroyNotify) {
+	entryPtr->tkwin = NULL;
+	DestroyEntry(entryPtr);
+	tablePtr->flags |= REQUEST_LAYOUT;
+	EventuallyArrangeTable(tablePtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetCustodyProc --
+ *
+ * 	This procedure is invoked when a widget has been stolen by
+ * 	another geometry manager.  The information and memory
+ * 	associated with the widget is released.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+  *	Arranges for the table to have its layout re-arranged at the
+ *	next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+WidgetCustodyProc(clientData, tkwin)
+    ClientData clientData;	/* Information about the widget */
+    Tk_Window tkwin;		/* Not used. */
+{
+    Entry *entryPtr = (Entry *) clientData;
+    Table *tablePtr = entryPtr->tablePtr;
+
+    if (Tk_IsMapped(entryPtr->tkwin)) {
+	Tk_UnmapWindow(entryPtr->tkwin);
+    }
+    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+    entryPtr->tkwin = NULL;
+    DestroyEntry(entryPtr);
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * WidgetGeometryProc --
+ *
+ *	This procedure is invoked by Tk_GeometryRequest for widgets
+ *	managed by the table geometry manager.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Arranges for the table to have its layout re-computed and
+ *	re-arranged at the next idle point.
+ *
+ * ---------------------------------------------------------------------------- */
+/* ARGSUSED */
+static void
+WidgetGeometryProc(clientData, tkwin)
+    ClientData clientData;	/* Information about widget that got new
+				 * preferred geometry.  */
+    Tk_Window tkwin;		/* Other Tk-related information about the
+			         * widget. */
+{
+    Entry *entryPtr = (Entry *) clientData;
+
+    entryPtr->tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(entryPtr->tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * FindEntry --
+ *
+ *	Searches for the table entry corresponding to the given
+ *	widget.
+ *
+ * Results:
+ *	If a structure associated with the widget exists, a pointer to
+ *	that structure is returned. Otherwise NULL.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Entry *
+FindEntry(tablePtr, tkwin)
+    Table *tablePtr;
+    Tk_Window tkwin;		/* Widget associated with table entry */
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&(tablePtr->entryTable), (char *)tkwin);
+    if (hPtr == NULL) {
+	return NULL;
+    }
+    return (Entry *) Blt_GetHashValue(hPtr);
+}
+
+
+static int
+GetEntry(interp, tablePtr, string, entryPtrPtr)
+    Tcl_Interp *interp;
+    Table *tablePtr;
+    char *string;
+    Entry **entryPtrPtr;
+{
+    Tk_Window tkwin;
+    Entry *entryPtr;
+
+    tkwin = Tk_NameToWindow(interp, string, tablePtr->tkwin);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    entryPtr = FindEntry(tablePtr, tkwin);
+    if (entryPtr == NULL) {
+	Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
+	    "\" is not managed by any table", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *entryPtrPtr = entryPtr;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateEntry --
+ *
+ *	This procedure creates and initializes a new Entry structure
+ *	to hold a widget.  A valid widget has a parent widget that is
+ *	either a) the container widget itself or b) a mutual ancestor
+ *	of the container widget.
+ *
+ * Results:
+ *	Returns a pointer to the new structure describing the new
+ *	widget entry.  If an error occurred, then the return
+ *	value is NULL and an error message is left in interp->result.
+ *
+ * Side effects:
+ *	Memory is allocated and initialized for the Entry structure.
+ *
+ * ---------------------------------------------------------------------------- */
+static Entry *
+CreateEntry(tablePtr, tkwin)
+    Table *tablePtr;
+    Tk_Window tkwin;
+{
+    register Entry *entryPtr;
+    int dummy;
+    Tk_Window parent, ancestor;
+
+    /*
+     * Check that this widget can be managed by this table.  A valid
+     * widget has a parent widget that either
+     *
+     *    1) is the container widget, or
+     *    2) is a mutual ancestor of the container widget.
+     */
+    ancestor = Tk_Parent(tkwin);
+    for (parent = tablePtr->tkwin; (parent != ancestor) &&
+	(!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) {
+	/* empty */
+    }
+    if (ancestor != parent) {
+	Tcl_AppendResult(tablePtr->interp, "can't manage \"",
+	    Tk_PathName(tkwin), "\" in table \"", Tk_PathName(tablePtr->tkwin),
+	    "\"", (char *)NULL);
+	return NULL;
+    }
+    entryPtr = Blt_Calloc(1, sizeof(Entry));
+    assert(entryPtr);
+
+    /* Initialize the entry structure */
+
+    entryPtr->tkwin = tkwin;
+    entryPtr->tablePtr = tablePtr;
+    entryPtr->borderWidth = Tk_Changes(tkwin)->border_width;
+    entryPtr->fill = ENTRY_DEF_FILL;
+    entryPtr->row.control = entryPtr->column.control = ENTRY_DEF_CONTROL;
+    entryPtr->anchor = ENTRY_DEF_ANCHOR;
+    entryPtr->row.span = entryPtr->column.span = ENTRY_DEF_SPAN;
+    ResetLimits(&(entryPtr->reqWidth));
+    ResetLimits(&(entryPtr->reqHeight));
+
+    /*
+     * Add the entry to the following data structures.
+     *
+     * 	1) A chain of widgets managed by the table.
+     *   2) A hash table of widgets managed by the table.
+     */
+    entryPtr->linkPtr = Blt_ChainAppend(tablePtr->chainPtr, entryPtr);
+    entryPtr->hashPtr = Blt_CreateHashEntry(&(tablePtr->entryTable),
+	(char *)tkwin, &dummy);
+    Blt_SetHashValue(entryPtr->hashPtr, entryPtr);
+
+    Tk_CreateEventHandler(tkwin, StructureNotifyMask, WidgetEventProc, 
+	entryPtr);
+    Tk_ManageGeometry(tkwin, &tableMgrInfo, (ClientData)entryPtr);
+
+    return entryPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyEntry --
+ *
+ *	Removes the Entry structure from the hash table and frees
+ *	the memory allocated by it.  If the table is still in use
+ *	(i.e. was not called from DestoryTable), remove its entries
+ *	from the lists of row and column sorted partitions.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the entry is freed up.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyEntry(entryPtr)
+    Entry *entryPtr;
+{
+    Table *tablePtr = entryPtr->tablePtr;
+
+    if (entryPtr->row.linkPtr != NULL) {
+	Blt_ChainDeleteLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
+    }
+    if (entryPtr->column.linkPtr != NULL) {
+	Blt_ChainDeleteLink(entryPtr->column.chainPtr,
+	    entryPtr->column.linkPtr);
+    }
+    if (entryPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(tablePtr->chainPtr, entryPtr->linkPtr);
+    }
+    if (entryPtr->tkwin != NULL) {
+	Tk_DeleteEventHandler(entryPtr->tkwin, StructureNotifyMask,
+	      WidgetEventProc, (ClientData)entryPtr);
+	Tk_ManageGeometry(entryPtr->tkwin, (Tk_GeomMgr *)NULL,
+			  (ClientData)entryPtr);
+	if ((tablePtr->tkwin != NULL) && 
+	    (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin)) {
+	    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+	}
+	if (Tk_IsMapped(entryPtr->tkwin)) {
+	    Tk_UnmapWindow(entryPtr->tkwin);
+	}
+    }
+    if (entryPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&(tablePtr->entryTable), entryPtr->hashPtr);
+    }
+    Blt_Free(entryPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureEntry --
+ *
+ *	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	one or more entries.  Entries hold information about widgets
+ *	managed by the table geometry manager.
+ *
+ * 	Note: You can query only one widget at a time.  But several
+ * 	      can be reconfigured at once.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	The table layout is recomputed and rearranged at the next idle
+ *	point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureEntry(tablePtr, interp, entryPtr, argc, argv)
+    Table *tablePtr;
+    Tcl_Interp *interp;
+    Entry *entryPtr;
+    int argc;			/* Option-value arguments */
+    char **argv;
+{
+    int oldRowSpan, oldColSpan;
+
+    if (entryPtr->tablePtr != tablePtr) {
+	Tcl_AppendResult(interp, "widget  \"", Tk_PathName(entryPtr->tkwin),
+	    "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
+	    "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (argc == 0) {
+	return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
+	    (char *)entryPtr, (char *)NULL, 0);
+    } else if (argc == 1) {
+	return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
+	    (char *)entryPtr, argv[0], 0);
+    }
+    oldRowSpan = entryPtr->row.span;
+    oldColSpan = entryPtr->column.span;
+
+    if (Tk_ConfigureWidget(interp, entryPtr->tkwin, entryConfigSpecs,
+	    argc, argv, (char *)entryPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((entryPtr->column.span < 1) || (entryPtr->column.span > USHRT_MAX)) {
+	Tcl_AppendResult(interp, "bad column span specified for \"",
+	    Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((entryPtr->row.span < 1) || (entryPtr->row.span > USHRT_MAX)) {
+	Tcl_AppendResult(interp, "bad row span specified for \"",
+	    Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((oldColSpan != entryPtr->column.span) ||
+	(oldRowSpan != entryPtr->row.span)) {
+	BinEntry(tablePtr, entryPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * PrintEntry --
+ *
+ *	Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget
+ *	attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+PrintEntry(entryPtr, resultPtr)
+    Entry *entryPtr;
+    Tcl_DString *resultPtr;
+{
+    char string[200];
+
+    sprintf(string, "    %d,%d  ", entryPtr->row.rcPtr->index,
+	entryPtr->column.rcPtr->index);
+    Tcl_DStringAppend(resultPtr, string, -1);
+    Tcl_DStringAppend(resultPtr, Tk_PathName(entryPtr->tkwin), -1);
+    if (entryPtr->ipadX != ENTRY_DEF_PAD) {
+	Tcl_DStringAppend(resultPtr, " -ipadx ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadX), -1);
+    }
+    if (entryPtr->ipadY != ENTRY_DEF_PAD) {
+	Tcl_DStringAppend(resultPtr, " -ipady ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadY), -1);
+    }
+    if (entryPtr->row.span != ENTRY_DEF_SPAN) {
+	Tcl_DStringAppend(resultPtr, " -rowspan ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->row.span), -1);
+    }
+    if (entryPtr->column.span != ENTRY_DEF_SPAN) {
+	Tcl_DStringAppend(resultPtr, " -columnspan ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->column.span), -1);
+    }
+    if (entryPtr->anchor != ENTRY_DEF_ANCHOR) {
+	Tcl_DStringAppend(resultPtr, " -anchor ", -1);
+	Tcl_DStringAppend(resultPtr, Tk_NameOfAnchor(entryPtr->anchor), -1);
+    }
+    if ((entryPtr->padLeft != ENTRY_DEF_PAD) ||
+	(entryPtr->padRight != ENTRY_DEF_PAD)) {
+	Tcl_DStringAppend(resultPtr, " -padx ", -1);
+	sprintf(string, "{%d %d}", entryPtr->padLeft, entryPtr->padRight);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if ((entryPtr->padTop != ENTRY_DEF_PAD) ||
+	(entryPtr->padBottom != ENTRY_DEF_PAD)) {
+	Tcl_DStringAppend(resultPtr, " -pady ", -1);
+	sprintf(string, "{%d %d}", entryPtr->padTop, entryPtr->padBottom);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if (entryPtr->fill != ENTRY_DEF_FILL) {
+	Tcl_DStringAppend(resultPtr, " -fill ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_NameOfFill(entryPtr->fill), -1);
+    }
+    if (entryPtr->column.control != ENTRY_DEF_CONTROL) {
+	Tcl_DStringAppend(resultPtr, " -columncontrol ", -1);
+	Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->column.control), -1);
+    }
+    if (entryPtr->row.control != ENTRY_DEF_CONTROL) {
+	Tcl_DStringAppend(resultPtr, " -rowcontrol ", -1);
+	Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->row.control), -1);
+    }
+    if ((entryPtr->reqWidth.nom != LIMITS_NOM) ||
+	(entryPtr->reqWidth.min != LIMITS_MIN) ||
+	(entryPtr->reqWidth.max != LIMITS_MAX)) {
+	Tcl_DStringAppend(resultPtr, " -reqwidth {", -1);
+	Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqWidth)), -1);
+	Tcl_DStringAppend(resultPtr, "}", -1);
+    }
+    if ((entryPtr->reqHeight.nom != LIMITS_NOM) ||
+	(entryPtr->reqHeight.min != LIMITS_MIN) ||
+	(entryPtr->reqHeight.max != LIMITS_MAX)) {
+	Tcl_DStringAppend(resultPtr, " -reqheight {", -1);
+	Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqHeight)), -1);
+	Tcl_DStringAppend(resultPtr, "}", -1);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoEntry --
+ *
+ *	Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget
+ *	attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoEntry(interp, tablePtr, entryPtr)
+    Tcl_Interp *interp;
+    Table *tablePtr;
+    Entry *entryPtr;
+{
+    Tcl_DString dString;
+
+    if (entryPtr->tablePtr != tablePtr) {
+	Tcl_AppendResult(interp, "widget  \"", Tk_PathName(entryPtr->tkwin),
+	    "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
+	    "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    PrintEntry(entryPtr, &dString);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateRowColumn --
+ *
+ *	Creates and initializes a structure that manages the size of a
+ *	row or column in the table. There will be one of these
+ *	structures allocated for each row and column in the table,
+ *	regardless if a widget is contained in it or not.
+ *
+ * Results:
+ *	Returns a pointer to the newly allocated row or column
+ *	structure.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+CreateRowColumn()
+{
+    RowColumn *rcPtr;
+
+    rcPtr = Blt_Malloc(sizeof(RowColumn));
+    rcPtr->resize = ROWCOL_DEF_RESIZE;
+    ResetLimits(&(rcPtr->reqSize));
+    rcPtr->nomSize = LIMITS_NOM;
+    rcPtr->pad.side1 = rcPtr->pad.side2 = ROWCOL_DEF_PAD;
+    rcPtr->size = rcPtr->index = rcPtr->minSpan = 0;
+    rcPtr->weight = ROWCOL_DEF_WEIGHT;
+    return rcPtr;
+}
+
+static PartitionInfo *
+ParseRowColumn2(tablePtr, string, numberPtr)
+    Table *tablePtr;
+    char *string;
+    int *numberPtr;
+{
+    char c;
+    int n;
+    PartitionInfo *infoPtr;
+
+    c = tolower(string[0]);
+    if (c == 'c') {
+	infoPtr = &(tablePtr->columnInfo);
+    } else if (c == 'r') {
+	infoPtr = &(tablePtr->rowInfo);
+    } else {
+	Tcl_AppendResult(tablePtr->interp, "bad index \"", string,
+	    "\": must start with \"r\" or \"c\"", (char *)NULL);
+	return NULL;
+    }
+    /* Handle row or column configuration queries */
+    if (Tcl_GetInt(tablePtr->interp, string + 1, &n) != TCL_OK) {
+	return NULL;
+    }
+    *numberPtr = (int)n;
+    return infoPtr;
+}
+
+static PartitionInfo *
+ParseRowColumn(tablePtr, string, numberPtr)
+    Table *tablePtr;
+    char *string;
+    int *numberPtr;
+{
+    int n;
+    PartitionInfo *infoPtr;
+
+    infoPtr = ParseRowColumn2(tablePtr, string, &n);
+    if (infoPtr == NULL) {
+	return NULL;
+    }
+    if ((n < 0) || (n >= Blt_ChainGetLength(infoPtr->chainPtr))) {
+	Tcl_AppendResult(tablePtr->interp, "bad ", infoPtr->type, " index \"",
+	    string, "\"", (char *)NULL);
+	return NULL;
+    }
+    *numberPtr = (int)n;
+    return infoPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetRowColumn --
+ *
+ *	Gets the designated row or column from the table.  If the row
+ *	or column index is greater than the size of the table, new
+ *	rows/columns will be automatically allocated.
+ *
+ * Results:
+ *	Returns a pointer to the row or column structure.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+GetRowColumn(infoPtr, n)
+    PartitionInfo *infoPtr;
+    int n;
+{
+    Blt_ChainLink *linkPtr;
+    RowColumn *rcPtr;
+    register int i;
+
+    for (i = Blt_ChainGetLength(infoPtr->chainPtr); i <= n; i++) {
+	rcPtr = CreateRowColumn();
+	rcPtr->index = i;
+	rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
+    }
+    linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, n);
+    if (linkPtr == NULL) {
+	return NULL;
+    }
+    return Blt_ChainGetValue(linkPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DeleteRowColumn --
+ *
+ *	Deletes a span of rows/columns from the table. The number of
+ *	rows/columns to be deleted is given by span.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The size of the column partition array may be extended and
+ *	initialized.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DeleteRowColumn(tablePtr, infoPtr, rcPtr)
+    Table *tablePtr;
+    PartitionInfo *infoPtr;
+    RowColumn *rcPtr;
+{
+    Blt_ChainLink *linkPtr, *nextPtr;
+    Entry *entryPtr;
+
+    /*
+     * Remove any entries that start in the row/column to be deleted.
+     * They point to memory that will be freed.
+     */
+    if (infoPtr->type == rowUid) {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = nextPtr) {
+	    nextPtr = Blt_ChainNextLink(linkPtr);
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    if (entryPtr->row.rcPtr->index == rcPtr->index) {
+		DestroyEntry(entryPtr);
+	    }
+	}
+    } else {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = nextPtr) {
+	    nextPtr = Blt_ChainNextLink(linkPtr);
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    if (entryPtr->column.rcPtr->index == rcPtr->index) {
+		DestroyEntry(entryPtr);
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureRowColumn --
+ *
+ *	This procedure is called to process an argv/argc list in order
+ *	to configure a row or column in the table geometry manager.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result holds an error message.
+ *
+ * Side effects:
+ *	Partition configuration options (bounds, resize flags, etc)
+ *	get set.  New partitions may be created as necessary. The
+ *	table is recalculated and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureRowColumn(tablePtr, infoPtr, pattern, argc, argv)
+    Table *tablePtr;		/* Table to be configured */
+    PartitionInfo *infoPtr;
+    char *pattern;
+    int argc;
+    char **argv;
+{
+    RowColumn *rcPtr;
+    register Blt_ChainLink *linkPtr;
+    char string[200];
+    int nMatches;
+
+    nMatches = 0;
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	sprintf(string, "%c%d", pattern[0], rcPtr->index);
+	if (Tcl_StringMatch(string, pattern)) {
+	    if (argc == 0) {
+		return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
+		    infoPtr->configSpecs, (char *)rcPtr, NULL, 0);
+	    } else if (argc == 1) {
+		return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
+		    infoPtr->configSpecs, (char *)rcPtr, argv[0], 0);
+	    } else {
+		if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
+			infoPtr->configSpecs, argc, argv, (char *)rcPtr,
+			TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+	    }
+	    nMatches++;
+	}
+    }
+    if (nMatches == 0) {
+	int n;
+
+	/* 
+	 * We found no existing partitions matching this pattern, so 
+	 * see if this designates an new partition (one beyond the 
+	 * current range).  
+	 */
+	if ((Tcl_GetInt(NULL, pattern + 1, &n) != TCL_OK) || (n < 0)) {
+	    Tcl_AppendResult(tablePtr->interp, "pattern \"", pattern, 
+		     "\" matches no ", infoPtr->type, " in table \"", 
+		     Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	rcPtr = GetRowColumn(infoPtr, n);
+	assert(rcPtr);
+	if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
+	       infoPtr->configSpecs, argc, argv, (char *)rcPtr,
+	       TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    EventuallyArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+static void
+PrintRowColumn(interp, infoPtr, rcPtr, resultPtr)
+    Tcl_Interp *interp;
+    PartitionInfo *infoPtr;
+    RowColumn *rcPtr;
+    Tcl_DString *resultPtr;
+{
+    char string[200];
+    char *padFmt, *sizeFmt;
+
+    if (infoPtr->type == rowUid) {
+	padFmt = " -pady {%d %d}";
+	sizeFmt = " -height {%s}";
+    } else {
+	padFmt = " -padx {%d %d}";
+	sizeFmt = " -width {%s}";
+    }
+    if (rcPtr->resize != ROWCOL_DEF_RESIZE) {
+	Tcl_DStringAppend(resultPtr, " -resize ", -1);
+	Tcl_DStringAppend(resultPtr, NameOfResize(rcPtr->resize), -1);
+    }
+    if ((rcPtr->pad.side1 != ROWCOL_DEF_PAD) ||
+	(rcPtr->pad.side2 != ROWCOL_DEF_PAD)) {
+	sprintf(string, padFmt, rcPtr->pad.side1, rcPtr->pad.side2);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if (rcPtr->weight != ROWCOL_DEF_WEIGHT) {
+	Tcl_DStringAppend(resultPtr, " -weight ", -1);
+	Tcl_DStringAppend(resultPtr, Blt_Dtoa(interp, rcPtr->weight), -1);
+    }
+    if ((rcPtr->reqSize.min != LIMITS_MIN) ||
+	(rcPtr->reqSize.nom != LIMITS_NOM) ||
+	(rcPtr->reqSize.max != LIMITS_MAX)) {
+	sprintf(string, sizeFmt, NameOfLimits(&(rcPtr->reqSize)));
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoRowColumn --
+ *
+ *	Returns the options of a partition in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the partition
+ *	attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoRowColumn(tablePtr, interp, pattern)
+    Table *tablePtr;
+    Tcl_Interp *interp;
+    char *pattern;
+{
+    RowColumn *rcPtr;
+    char string[200];
+    PartitionInfo *infoPtr;
+    char c;
+    Blt_ChainLink *linkPtr, *lastPtr;
+    Tcl_DString dString;
+
+    c = pattern[0];
+    if ((c == 'r') || (c == 'R')) {
+	infoPtr = &(tablePtr->rowInfo);
+    } else {
+	infoPtr = &(tablePtr->columnInfo);
+    }
+    Tcl_DStringInit(&dString);
+    lastPtr = Blt_ChainLastLink(infoPtr->chainPtr);
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	sprintf(string, "%c%d", infoPtr->type[0], rcPtr->index);
+	if (Tcl_StringMatch(string, pattern)) {
+	    Tcl_DStringAppend(&dString, string, -1);
+	    PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+	    if (linkPtr != lastPtr) {
+		Tcl_DStringAppend(&dString, " \\\n", -1);
+	    } else {
+		Tcl_DStringAppend(&dString, "\n", -1);
+	    }
+	}
+    }
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InitSpan --
+ *
+ *	Checks the size of the column partitions and extends the size
+ *	if a larger array is needed.
+ *
+ * Results:
+ *	Returns 1 if the column exists.  Otherwise 0 is returned and
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	The size of the column partition array may be extended and
+ *	initialized.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static RowColumn *
+InitSpan(infoPtr, start, span)
+    PartitionInfo *infoPtr;
+    int start, span;
+{
+    int length;
+    RowColumn *rcPtr;
+    register int i;
+    Blt_ChainLink *linkPtr;
+
+    length = Blt_ChainGetLength(infoPtr->chainPtr);
+    for (i = length; i < (start + span); i++) {
+	rcPtr = CreateRowColumn();
+	rcPtr->index = i;
+	rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
+    }
+    linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, start);
+    return Blt_ChainGetValue(linkPtr);
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Blt_GetTable --
+ *
+ *	Searches for a table associated by the path name of the widget
+ *	container.
+ *
+ *	Errors may occur because
+ *	  1) pathName isn't a valid for any Tk widget, or
+ *	  2) there's no table associated with that widget as a container.
+ *
+ * Results:
+ *	If a table entry exists, a pointer to the Table structure is
+ *	returned. Otherwise NULL is returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_GetTable(dataPtr, interp, pathName, tablePtrPtr)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors back to. */
+    char *pathName;		/* Path name of the container widget. */
+    Table **tablePtrPtr;
+{
+    Blt_HashEntry *hPtr;
+    Tk_Window tkwin;
+
+    tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_FindHashEntry(&(dataPtr->tableTable), (char *)tkwin);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(interp, "no table associated with widget \"",
+	    pathName, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *tablePtrPtr = (Table *)Blt_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CreateTable --
+ *
+ *	This procedure creates and initializes a new Table structure
+ *	with tkwin as its container widget. The internal structures
+ *	associated with the table are initialized.
+ *
+ * Results:
+ *	Returns the pointer to the new Table structure describing the
+ *	new table geometry manager.  If an error occurred, the return
+ *	value will be NULL and an error message is left in
+ *	interp->result.
+ *
+ * Side effects:
+ *	Memory is allocated and initialized, an event handler is set
+ *	up to watch tkwin, etc.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Table *
+CreateTable(dataPtr, interp, pathName)
+    TableInterpData *dataPtr;
+    Tcl_Interp *interp;		/* Interpreter associated with table. */
+    char *pathName;		/* Path name of the container widget to be
+				 * associated with the new table. */
+{
+    register Table *tablePtr;
+    Tk_Window tkwin;
+    int dummy;
+    Blt_HashEntry *hPtr;
+
+    tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+    if (tkwin == NULL) {
+	return NULL;
+    }
+    tablePtr = Blt_Calloc(1, sizeof(Table));
+    assert(tablePtr);
+    tablePtr->tkwin = tkwin;
+    tablePtr->interp = interp;
+    tablePtr->rowInfo.type = rowUid;
+    tablePtr->rowInfo.configSpecs = rowConfigSpecs;
+    tablePtr->rowInfo.chainPtr = Blt_ChainCreate();
+    tablePtr->columnInfo.type = columnUid;
+    tablePtr->columnInfo.configSpecs = columnConfigSpecs;
+    tablePtr->columnInfo.chainPtr = Blt_ChainCreate();
+    tablePtr->propagate = TRUE;
+
+    tablePtr->arrangeProc = ArrangeTable;
+    Blt_InitHashTable(&(tablePtr->entryTable), BLT_ONE_WORD_KEYS);
+    tablePtr->findEntryProc = FindEntry;
+
+    ResetLimits(&(tablePtr->reqWidth));
+    ResetLimits(&(tablePtr->reqHeight));
+
+    tablePtr->chainPtr = Blt_ChainCreate();
+    tablePtr->rowInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+    tablePtr->columnInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+
+    Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask,
+	TableEventProc, (ClientData)tablePtr);
+    hPtr = Blt_CreateHashEntry(&(dataPtr->tableTable), (char *)tkwin, &dummy);
+    tablePtr->hashPtr = hPtr;
+    tablePtr->tablePtr = &(dataPtr->tableTable);
+    Blt_SetHashValue(hPtr, (ClientData)tablePtr);
+    return tablePtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureTable --
+ *
+ *	This procedure is called to process an argv/argc list in order
+ *	to configure the table geometry manager.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ *	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Table configuration options (-padx, -pady, etc.) get set.  The
+ *	table is recalculated and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ConfigureTable(tablePtr, interp, argc, argv)
+    Table *tablePtr;		/* Table to be configured */
+    Tcl_Interp *interp;		/* Interpreter to report results back to */
+    int argc;
+    char **argv;		/* Option-value pairs */
+{
+    if (argc == 0) {
+	return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
+	    (char *)tablePtr, (char *)NULL, 0);
+    } else if (argc == 1) {
+	return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
+	    (char *)tablePtr, argv[0], 0);
+    }
+    if (Tk_ConfigureWidget(interp, tablePtr->tkwin, tableConfigSpecs,
+	    argc, argv, (char *)tablePtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Arrange for the table layout to be computed at the next idle point. */
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+static void
+PrintTable(tablePtr, resultPtr)
+    Table *tablePtr;
+    Tcl_DString *resultPtr;
+{
+    char string[200];
+
+    if ((tablePtr->padLeft != TABLE_DEF_PAD) ||
+	(tablePtr->padRight != TABLE_DEF_PAD)) {
+	sprintf(string, " -padx {%d %d}", tablePtr->padLeft, tablePtr->padRight);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if ((tablePtr->padTop != TABLE_DEF_PAD) ||
+	(tablePtr->padBottom != TABLE_DEF_PAD)) {
+	sprintf(string, " -pady {%d %d}", tablePtr->padTop, tablePtr->padBottom);
+	Tcl_DStringAppend(resultPtr, string, -1);
+    }
+    if (!tablePtr->propagate) {
+	Tcl_DStringAppend(resultPtr, " -propagate no", -1);
+    }
+    if ((tablePtr->reqWidth.min != LIMITS_MIN) ||
+	(tablePtr->reqWidth.nom != LIMITS_NOM) ||
+	(tablePtr->reqWidth.max != LIMITS_MAX)) {
+	Tcl_DStringAppend(resultPtr, " -reqwidth {%s}", -1);
+	Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqWidth)), -1);
+    }
+    if ((tablePtr->reqHeight.min != LIMITS_MIN) ||
+	(tablePtr->reqHeight.nom != LIMITS_NOM) ||
+	(tablePtr->reqHeight.max != LIMITS_MAX)) {
+	Tcl_DStringAppend(resultPtr, " -reqheight {%s}", -1);
+	Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqHeight)), -1);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyPartitions --
+ *
+ *	Clear each of the lists managing the entries.  The entries in
+ *	the lists of row and column spans are themselves lists which
+ *	need to be cleared.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyPartitions(infoPtr)
+    PartitionInfo *infoPtr;
+{
+    if (infoPtr->list != NULL) {
+	Blt_Chain *chainPtr;
+	Blt_ListNode node;
+
+	for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+	    node = Blt_ListNextNode(node)) {
+	    chainPtr = (Blt_Chain *)Blt_ListGetValue(node);
+	    if (chainPtr != NULL) {
+		Blt_ChainDestroy(chainPtr);
+	    }
+	}
+	Blt_ListDestroy(infoPtr->list);
+    }
+    if (infoPtr->chainPtr != NULL) {
+	Blt_ChainLink *linkPtr;
+	RowColumn *rcPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    Blt_Free(rcPtr);
+	}
+	Blt_ChainDestroy(infoPtr->chainPtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DestroyTable --
+ *
+ *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
+ *	clean up the Table structure at a safe time (when no-one is using
+ *	it anymore).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the table geometry manager is freed up.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+DestroyTable(dataPtr)
+    DestroyData dataPtr;	/* Table structure */
+{
+    Blt_ChainLink *linkPtr;
+    Entry *entryPtr;
+    Table *tablePtr = (Table *)dataPtr;
+
+    /* Release the chain of entries. */
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	entryPtr = Blt_ChainGetValue(linkPtr);
+	entryPtr->linkPtr = NULL; /* Don't disrupt this chain of entries */
+	DestroyEntry(entryPtr);
+    }
+    Blt_ChainDestroy(tablePtr->chainPtr);
+
+    DestroyPartitions(&(tablePtr->rowInfo));
+    DestroyPartitions(&(tablePtr->columnInfo));
+    Blt_DeleteHashTable(&(tablePtr->entryTable));
+    if (tablePtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(tablePtr->tablePtr, tablePtr->hashPtr);
+    }
+    Blt_Free(tablePtr);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BinEntry --
+ *
+ *	Adds the entry to the lists of both row and column spans.  The
+ *	layout of the table is done in order of partition spans, from
+ *	shorted to longest.  The widgets spanning a particular number of
+ *	partitions are stored in a linked list.  Each list is in turn,
+ *	contained within a master list.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The entry is added to both the lists of row and columns spans.
+ *	This will effect the layout of the widgets.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+BinEntry(tablePtr, entryPtr)
+    Table *tablePtr;
+    Entry *entryPtr;
+{
+    Blt_ListNode node;
+    Blt_List list;
+    Blt_Chain *chainPtr;
+    int key;
+
+    /*
+     * Remove the entry from both row and column lists.  It will be
+     * re-inserted into the table at the new position.
+     */
+    if (entryPtr->column.linkPtr != NULL) {
+	Blt_ChainUnlinkLink(entryPtr->column.chainPtr,
+	    entryPtr->column.linkPtr);
+    }
+    if (entryPtr->row.linkPtr != NULL) {
+	Blt_ChainUnlinkLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
+    }
+    list = tablePtr->rowInfo.list;
+    key = 0;			/* Initialize key to bogus span */
+    for (node = Blt_ListFirstNode(list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	key = (int)Blt_ListGetKey(node);
+	if (entryPtr->row.span <= key) {
+	    break;
+	}
+    }
+    if (key != entryPtr->row.span) {
+	Blt_ListNode newNode;
+
+	/*
+	 * Create a new list (bucket) to hold entries of that size
+	 * span and and link it into the list of buckets.
+	 */
+	newNode = Blt_ListCreateNode(list, (char *)entryPtr->row.span);
+	Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
+	Blt_ListLinkBefore(list, newNode, node);
+	node = newNode;
+    }
+    chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+    if (entryPtr->row.linkPtr == NULL) {
+	entryPtr->row.linkPtr = Blt_ChainAppend(chainPtr, entryPtr);
+    } else {
+	Blt_ChainLinkBefore(chainPtr, entryPtr->row.linkPtr, NULL);
+    }
+    entryPtr->row.chainPtr = chainPtr;
+
+    list = tablePtr->columnInfo.list;
+    key = 0;
+    for (node = Blt_ListFirstNode(list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	key = (int)Blt_ListGetKey(node);
+	if (entryPtr->column.span <= key) {
+	    break;
+	}
+    }
+    if (key != entryPtr->column.span) {
+	Blt_ListNode newNode;
+
+	/*
+	 * Create a new list (bucket) to hold entries of that size
+	 * span and and link it into the list of buckets.
+	 */
+	newNode = Blt_ListCreateNode(list, (char *)entryPtr->column.span);
+	Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
+	Blt_ListLinkBefore(list, newNode, node);
+	node = newNode;
+    }
+    chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+    /* Add the new entry to the span bucket */
+    if (entryPtr->column.linkPtr == NULL) {
+	entryPtr->column.linkPtr =
+	    Blt_ChainAppend(chainPtr, entryPtr);
+    } else {
+	Blt_ChainLinkBefore(chainPtr, entryPtr->column.linkPtr, NULL);
+    }
+    entryPtr->column.chainPtr = chainPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ParseIndex --
+ *
+ *	Parse the entry index string and return the row and column
+ *	numbers in their respective parameters.  The format of a table
+ *	entry index is row,column where row is the row number and
+ *	column is the column number.  Rows and columns are numbered
+ *	starting from zero.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If TCL_OK is returned, the row
+ *	and column numbers are returned via rowPtr and columnPtr
+ *	respectively.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ParseIndex(interp, string, rowPtr, columnPtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *rowPtr, *columnPtr;
+{
+    char *comma;
+    long row, column;
+    int result;
+
+    comma = strchr(string, ',');
+    if (comma == NULL) {
+	Tcl_AppendResult(interp, "bad index \"", string,
+	    "\": should be \"row,column\"", (char *)NULL);
+	return TCL_ERROR;
+
+    }
+    *comma = '\0';
+    result = ((Tcl_ExprLong(interp, string, &row) != TCL_OK) ||
+	(Tcl_ExprLong(interp, comma + 1, &column) != TCL_OK));
+    *comma = ',';		/* Repair the argument */
+    if (result) {
+	return TCL_ERROR;
+    }
+    if ((row < 0) || (row > (long)USHRT_MAX)) {
+	Tcl_AppendResult(interp, "bad index \"", string,
+	    "\": row is out of range", (char *)NULL);
+	return TCL_ERROR;
+
+    }
+    if ((column < 0) || (column > (long)USHRT_MAX)) {
+	Tcl_AppendResult(interp, "bad index \"", string,
+	    "\": column is out of range", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *rowPtr = (int)row;
+    *columnPtr = (int)column;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ManageEntry --
+ *
+ *	Inserts the given widget into the table at a given row and
+ *	column position.  The widget can already be managed by this or
+ *	another table.  The widget will be simply moved to the new
+ *	location in this table.
+ *
+ *	The new widget is inserted into both a hash table (this is
+ *	used to locate the information associated with the widget) and
+ *	a list (used to indicate relative ordering of widgets).
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
+ *	returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ *	The table is re-computed and arranged at the next idle point.
+ *
+ * ---------------------------------------------------------------------------- */
+static int
+ManageEntry(interp, tablePtr, tkwin, row, column, argc, argv)
+    Tcl_Interp *interp;
+    Table *tablePtr;
+    Tk_Window tkwin;
+    int row, column;
+    int argc;
+    char **argv;
+{
+    Entry *entryPtr;
+    int result = TCL_OK;
+
+    entryPtr = FindEntry(tablePtr, tkwin);
+    if ((entryPtr != NULL) && (entryPtr->tablePtr != tablePtr)) {
+	/* The entry for the widget already exists. If it's
+	 * managed by another table, delete it.  */
+	DestroyEntry(entryPtr);
+	entryPtr = NULL;
+    }
+    if (entryPtr == NULL) {
+	entryPtr = CreateEntry(tablePtr, tkwin);
+	if (entryPtr == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    if (argc > 0) {
+	result = Tk_ConfigureWidget(tablePtr->interp, entryPtr->tkwin,
+	    entryConfigSpecs, argc, argv, (char *)entryPtr,
+	    TK_CONFIG_ARGV_ONLY);
+    }
+    if ((entryPtr->column.span < 1) || (entryPtr->row.span < 1)) {
+	Tcl_AppendResult(tablePtr->interp, "bad span specified for \"",
+	    Tk_PathName(tkwin), "\"", (char *)NULL);
+	DestroyEntry(entryPtr);
+	return TCL_ERROR;
+    }
+    entryPtr->column.rcPtr = InitSpan(&(tablePtr->columnInfo), column,
+	entryPtr->column.span);
+    entryPtr->row.rcPtr = InitSpan(&(tablePtr->rowInfo), row,
+	entryPtr->row.span);
+    /*
+     * Insert the entry into both the row and column layout lists
+     */
+    BinEntry(tablePtr, entryPtr);
+
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * BuildTable --
+ *
+ *	Processes an argv/argc list of table entries to add and
+ *	configure new widgets into the table.  A table entry consists
+ *	of the widget path name, table index, and optional
+ *	configuration options.  The first argument in the argv list is
+ *	the name of the table.  If no table exists for the given
+ *	widget, a new one is created.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred,
+ *	TCL_ERROR is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side Effects:
+ *	Memory is allocated, a new table is possibly created, etc.
+ *	The table is re-computed and arranged at the next idle point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+BuildTable(tablePtr, interp, argc, argv)
+    Table *tablePtr;		/* Table to manage new widgets */
+    Tcl_Interp *interp;		/* Interpreter to report errors back to */
+    int argc;			/*  */
+    char **argv;		/* List of widgets, indices, and options */
+{
+    Tk_Window tkwin;
+    int row, column;
+    int nextRow, nextColumn;
+    register int i;
+
+    /* Process any options specific to the table */
+    for (i = 2; i < argc; i += 2) {
+	if (argv[i][0] != '-') {
+	    break;
+	}
+    }
+    if (i > argc) {
+	i = argc;
+    }
+    if (i > 2) {
+	if (ConfigureTable(tablePtr, interp, i - 2, argv + 2) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    nextRow = tablePtr->nRows;
+    nextColumn = 0;
+    argc -= i, argv += i;
+    while (argc > 0) {
+	/*
+	 * Allow the name of the widget and row/column index to be
+	 * specified in any order.
+	 */
+	if (argv[0][0] == '.') {
+	    tkwin = Tk_NameToWindow(interp, argv[0], tablePtr->tkwin);
+	    if (tkwin == NULL) {
+		return TCL_ERROR;
+	    }
+	    if ((argc == 1) || (argv[1][0] == '-')) {
+		/* No row,column index, use defaults instead */
+		row = nextRow, column = nextColumn;
+		argc--, argv++;
+	    } else {
+		if (ParseIndex(interp, argv[1], &row, &column) != TCL_OK) {
+		    return TCL_ERROR;	/* Invalid row,column index */
+		}
+		/* Skip over the widget pathname and table index. */
+		argc -= 2, argv += 2;
+	    }
+	} else {
+	    if (ParseIndex(interp, argv[0], &row, &column) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (argc == 1) {
+		Tcl_AppendResult(interp, "missing widget pathname after \"",
+			 argv[0], "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    tkwin = Tk_NameToWindow(interp, argv[1], tablePtr->tkwin);
+	    if (tkwin == NULL) {
+		return TCL_ERROR;
+	    }
+	    /* Skip over the widget pathname and table index. */
+	    argc -= 2, argv += 2;
+	}
+
+	/* Find the end of the widget's option-value pairs */
+	for (i = 0; i < argc; i += 2) {
+	    if (argv[i][0] != '-') {
+		break;
+	    }
+	}
+	if (i > argc) {
+	    i = argc;
+	}
+	if (ManageEntry(interp, tablePtr, tkwin, row,
+		column, i, argv) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	nextColumn = column + 1;
+	argc -= i, argv += i;
+    }
+    /* Arrange for the new table layout to be calculated. */
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+
+    Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ParseItem --
+ *
+ *	Parses a string representing an item in the table.  An item
+ *	may be one of the following:
+ *		Rn	- Row index, where n is the index of row
+ *		Cn	- Column index, where n is the index of column
+ *		r,c	- Cell index, where r is the row index and c
+ *			  is the column index.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If no error occurred, TCL_OK
+ *	is returned.  *RowPtr* will return the row index.  *ColumnPtr*
+ *	will return the column index.  If the row or column index is
+ *	not applicable, -1 is returned via *rowPtr* or *columnPtr*.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ParseItem(tablePtr, string, rowPtr, columnPtr)
+    Table *tablePtr;
+    char *string;
+    int *rowPtr, *columnPtr;
+{
+    char c;
+    long partNum;
+
+    c = tolower(string[0]);
+    *rowPtr = *columnPtr = -1;
+    if (c == 'r') {
+	if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((partNum < 0) || (partNum >= tablePtr->nRows)) {
+	    Tcl_AppendResult(tablePtr->interp, "row index \"", string,
+		"\" is out of range", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	*rowPtr = (int)partNum;
+    } else if (c == 'c') {
+	if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((partNum < 0) || (partNum >= tablePtr->nColumns)) {
+	    Tcl_AppendResult(tablePtr->interp, "column index \"", string,
+		"\" is out of range", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	*columnPtr = (int)partNum;
+    } else {
+	if (ParseIndex(tablePtr->interp, string,
+		rowPtr, columnPtr) != TCL_OK) {
+	    return TCL_ERROR;	/* Invalid row,column index */
+	}
+	if ((*rowPtr < 0) || (*rowPtr >= tablePtr->nRows) ||
+	    (*columnPtr < 0) || (*columnPtr >= tablePtr->nColumns)) {
+	    Tcl_AppendResult(tablePtr->interp, "index \"", string,
+		"\" is out of range", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * 	Translate the coordinates of a given bounding box based upon
+ * 	the anchor specified.  The anchor indicates where the given xy
+ * 	position is in relation to the bounding box.
+ *
+ *  		nw --- n --- ne
+ *  		|            |     x,y ---+
+ *  		w   center   e      |     |
+ *  		|            |      +-----+
+ *  		sw --- s --- se
+ *
+ * Results:
+ *	The translated coordinates of the bounding box are returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
+    int dx, dy;			/* Difference between outer and inner regions
+				 */
+    Tk_Anchor anchor;		/* Direction of the anchor */
+    int *xPtr, *yPtr;
+{
+    int x, y;
+
+    x = y = 0;
+    switch (anchor) {
+    case TK_ANCHOR_NW:		/* Upper left corner */
+	break;
+    case TK_ANCHOR_W:		/* Left center */
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_SW:		/* Lower left corner */
+	y = dy;
+	break;
+    case TK_ANCHOR_N:		/* Top center */
+	x = (dx / 2);
+	break;
+    case TK_ANCHOR_CENTER:	/* Centered */
+	x = (dx / 2);
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_S:		/* Bottom center */
+	x = (dx / 2);
+	y = dy;
+	break;
+    case TK_ANCHOR_NE:		/* Upper right corner */
+	x = dx;
+	break;
+    case TK_ANCHOR_E:		/* Right center */
+	x = dx;
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_SE:		/* Lower right corner */
+	x = dx;
+	y = dy;
+	break;
+    }
+    *xPtr = (*xPtr) + x;
+    *yPtr = (*yPtr) + y;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqWidth --
+ *
+ *	Returns the width requested by the widget starting in the
+ *	given entry.  The requested space also includes any internal
+ *	padding which has been designated for this widget.
+ *
+ *	The requested width of the widget is always bounded by the limits
+ *	set in entryPtr->reqWidth.
+ *
+ * Results:
+ *	Returns the requested width of the widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqWidth(entryPtr)
+    Entry *entryPtr;
+{
+    int width;
+
+    width = Tk_ReqWidth(entryPtr->tkwin) + (2 * entryPtr->ipadX);
+    width = GetBoundedWidth(width, &(entryPtr->reqWidth));
+    return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqHeight --
+ *
+ *	Returns the height requested by the widget starting in the
+ *	given entry.  The requested space also includes any internal
+ *	padding which has been designated for this widget.
+ *
+ *	The requested height of the widget is always bounded by the
+ *	limits set in entryPtr->reqHeight.
+ *
+ * Results:
+ *	Returns the requested height of the widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqHeight(entryPtr)
+    Entry *entryPtr;
+{
+    int height;
+
+    height = Tk_ReqHeight(entryPtr->tkwin) + (2 * entryPtr->ipadY);
+    height = GetBoundedHeight(height, &(entryPtr->reqHeight));
+    return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetTotalSpan --
+ *
+ *	Sums the row/column space requirements for the entire table.
+ *
+ * Results:
+ *	Returns the space currently used in the span of partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetTotalSpan(infoPtr)
+    PartitionInfo *infoPtr;
+{
+    register int spaceUsed;
+    Blt_ChainLink *linkPtr;
+    RowColumn *rcPtr;		/* Start of partitions */
+
+    spaceUsed = 0;
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	spaceUsed += rcPtr->size;
+    }
+    return spaceUsed;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetSpan --
+ *
+ *	Determines the space used by rows/columns for an entry.
+ *
+ * Results:
+ *	Returns the space currently used in the span of partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetSpan(infoPtr, entryPtr)
+    PartitionInfo *infoPtr;
+    Entry *entryPtr;
+{
+    RowColumn *startPtr;
+    register int spaceUsed;
+    int count;
+    Blt_ChainLink *linkPtr;
+    RowColumn *rcPtr;		/* Start of partitions */
+    int span;			/* Number of partitions spanned */
+
+    if (infoPtr->type == rowUid) {
+	rcPtr = entryPtr->row.rcPtr;
+	span = entryPtr->row.span;
+    } else {
+	rcPtr = entryPtr->column.rcPtr;
+	span = entryPtr->column.span;
+    }
+
+    count = spaceUsed = 0;
+    linkPtr = rcPtr->linkPtr;
+    startPtr = Blt_ChainGetValue(linkPtr);
+    for ( /*empty*/ ; (linkPtr != NULL) && (count < span);
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	spaceUsed += rcPtr->size;
+	count++;
+    }
+    /*
+     * Subtract off the padding on either side of the span, since the
+     * widget can't grow into it.
+     */
+    spaceUsed -= (startPtr->pad.side1 + rcPtr->pad.side2 + infoPtr->ePad);
+    return spaceUsed;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GrowSpan --
+ *
+ *	Expand the span by the amount of the extra space needed.  This
+ *	procedure is used in LayoutPartitions to grow the partitions
+ *	to their minimum nominal size, starting from a zero width and
+ *	height space.
+ *
+ *	This looks more complicated than it really is.  The idea is to
+ *	make the size of the partitions correspond to the smallest
+ *	entry spans.  For example, if widget A is in column 1 and
+ *	widget B spans both columns 0 and 1, any extra space needed to
+ *	fit widget B should come from column 0.
+ *
+ *	On the first pass we try to add space to partitions which have
+ *	not been touched yet (i.e. have no nominal size).  Since the
+ *	row and column lists are sorted in ascending order of the
+ *	number of rows or columns spanned, the space is distributed
+ *	amongst the smallest spans first.
+ *
+ *	The second pass handles the case of widgets which have the
+ *	same span.  For example, if A and B, which span the same
+ *	number of partitions are the only widgets to span column 1,
+ *	column 1 would grow to contain the bigger of the two slices of
+ *	space.
+ *
+ *	If there is still extra space after the first two passes, this
+ *	means that there were no partitions of with no widget spans or
+ *	the same order span that could be expanded. The third pass
+ *	will try to remedy this by parcelling out the left over space
+ *	evenly among the rest of the partitions.
+ *
+ *	On each pass, we have to keep iterating over the span, evenly
+ *	doling out slices of extra space, because we may hit partition
+ *	limits as space is donated.  In addition, if there are left
+ *	over pixels because of round-off, this will distribute them as
+ *	evenly as possible.  For the worst case, it will take *span*
+ *	passes to expand the span.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ * 	The partitions in the span may be expanded.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+GrowSpan(infoPtr, entryPtr, growth)
+    PartitionInfo *infoPtr;
+    Entry *entryPtr;
+    int growth;			/* The amount of extra space needed to
+				 * grow the span. */
+{
+    register RowColumn *rcPtr;
+    Blt_ChainLink *linkPtr;
+    int spaceLeft, ration;
+    int nOpen;			/* # of partitions with space available */
+    register int n;
+    RowColumn *startPtr;	/* Starting (column/row) partition  */
+    int span;			/* Number of partitions in the span */
+
+    if (infoPtr->type == rowUid) {
+	startPtr = entryPtr->row.rcPtr;
+	span = entryPtr->row.span;
+    } else {
+	startPtr = entryPtr->column.rcPtr;
+	span = entryPtr->column.span;
+    }
+
+    /*
+     * ------------------------------------------------------------------------
+     *
+     * Pass 1: First add space to rows/columns that haven't determined
+     *	       their nominal sizes yet.
+     *
+     * ------------------------------------------------------------------------
+     */
+
+    nOpen = 0;
+    /* Find out how many partitions have no size yet */
+    linkPtr = startPtr->linkPtr;
+    for (n = 0; n < span; n++) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if ((rcPtr->nomSize == LIMITS_NOM) && (rcPtr->maxSize > rcPtr->size)) {
+	    nOpen++;
+	}
+	linkPtr = Blt_ChainNextLink(linkPtr);
+    }
+
+    while ((nOpen > 0) && (growth > 0)) {
+	ration = growth / nOpen;
+	if (ration == 0) {
+	    ration = 1;
+	}
+	linkPtr = startPtr->linkPtr;
+	for (n = 0; (n < span) && (growth > 0); n++) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    spaceLeft = rcPtr->maxSize - rcPtr->size;
+	    if ((rcPtr->nomSize == LIMITS_NOM) && (spaceLeft > 0)) {
+		if (ration < spaceLeft) {
+		    growth -= ration;
+		    rcPtr->size += ration;
+		} else {
+		    growth -= spaceLeft;
+		    rcPtr->size += spaceLeft;
+		    nOpen--;
+		}
+		rcPtr->minSpan = span;
+		rcPtr->control = entryPtr;
+	    }
+	    linkPtr = Blt_ChainNextLink(linkPtr);
+	}
+    }
+
+    /*
+     * ------------------------------------------------------------------------
+     *
+     * Pass 2: Add space to partitions which have the same minimum span
+     *
+     * ------------------------------------------------------------------------
+     */
+
+    nOpen = 0;
+    linkPtr = startPtr->linkPtr;
+    for (n = 0; n < span; n++) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if ((rcPtr->minSpan == span) && (rcPtr->maxSize > rcPtr->size)) {
+	    nOpen++;
+	}
+	linkPtr = Blt_ChainNextLink(linkPtr);
+    }
+    while ((nOpen > 0) && (growth > 0)) {
+	ration = growth / nOpen;
+	if (ration == 0) {
+	    ration = 1;
+	}
+	linkPtr = startPtr->linkPtr;
+	for (n = 0; (n < span) && (growth > 0); n++) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    spaceLeft = rcPtr->maxSize - rcPtr->size;
+	    if ((rcPtr->minSpan == span) && (spaceLeft > 0)) {
+		if (ration < spaceLeft) {
+		    growth -= ration;
+		    rcPtr->size += ration;
+		} else {
+		    growth -= spaceLeft;
+		    rcPtr->size += spaceLeft;
+		    nOpen--;
+		}
+		rcPtr->control = entryPtr;
+	    }
+	    linkPtr = Blt_ChainNextLink(linkPtr);
+	}
+    }
+
+    /*
+     * ------------------------------------------------------------------------
+     *
+     * Pass 3: Try to expand all the partitions with space still available
+     *
+     * ------------------------------------------------------------------------
+     */
+
+    /* Find out how many partitions still have space available */
+    nOpen = 0;
+    linkPtr = startPtr->linkPtr;
+    for (n = 0; n < span; n++) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if ((rcPtr->resize & RESIZE_EXPAND) && (rcPtr->maxSize > rcPtr->size)) {
+	    nOpen++;
+	}
+	/* Set the nominal size of the row/column. */
+	rcPtr->nomSize = rcPtr->size;
+	linkPtr = Blt_ChainNextLink(linkPtr);
+    }
+    while ((nOpen > 0) && (growth > 0)) {
+	ration = growth / nOpen;
+	if (ration == 0) {
+	    ration = 1;
+	}
+	linkPtr = startPtr->linkPtr;
+	for (n = 0; (n < span) && (growth > 0); n++) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    linkPtr = Blt_ChainNextLink(linkPtr);
+	    if (!(rcPtr->resize & RESIZE_EXPAND)) {
+		continue;
+	    }
+	    spaceLeft = rcPtr->maxSize - rcPtr->size;
+	    if (spaceLeft > 0) {
+		if (ration < spaceLeft) {
+		    growth -= ration;
+		    rcPtr->size += ration;
+		} else {
+		    growth -= spaceLeft;
+		    rcPtr->size += spaceLeft;
+		    nOpen--;
+		}
+		rcPtr->nomSize = rcPtr->size;
+		rcPtr->control = entryPtr;
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * AdjustPartitions --
+ *
+ *	Adjust the span by the amount of the extra space needed.  If
+ *	the amount (adjustSpace) is negative, shrink the span,
+ *	otherwise expand it.  Size constraints on the partitions may
+ *	prevent any or all of the spacing adjustments.
+ *
+ *	This is very much like the GrowSpan procedure, but in this
+ *	case we are shrinking or expanding all the (row or column)
+ *	partitions. It uses a two pass approach, first giving space to
+ *	partitions which not are smaller/larger than their nominal
+ *	sizes. This is because constraints on the partitions may cause
+ *	resizing to be non-linear.
+ *
+ *	If there is still extra space, this means that all partitions
+ *	are at least to their nominal sizes.  The second pass will try
+ *	to add/remove the left over space evenly among all the
+ *	partitions which still have space available.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The size of the partitions in the span may be increased or
+ *	decreased.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+AdjustPartitions(infoPtr, adjustment)
+    PartitionInfo *infoPtr;	/* Array of (column/row) partitions  */
+    int adjustment;		/* The amount of extra space to grow or shrink
+				 * the span. If negative, it represents the
+				 * amount of space to remove */
+{
+    register RowColumn *rcPtr;
+    int ration;			/* Amount of space to ration to each
+				 * row/column. */
+    int delta;			/* Amount of space needed */
+    int spaceLeft;		/* Amount of space still available */
+    int size;			/* Amount of space requested for a particular
+				 * row/column. */
+    int nOpen;			/* Number of rows/columns that still can
+				 * be adjusted. */
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr;
+    double totalWeight;
+
+    chainPtr = infoPtr->chainPtr;
+
+    /*
+     * ------------------------------------------------------------------------
+     *
+     * Pass 1: First adjust the size of rows/columns that still haven't
+     *	      reached their nominal size.
+     *
+     * ------------------------------------------------------------------------
+     */
+    delta = adjustment;
+
+    nOpen = 0;
+    totalWeight = 0.0;
+    for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if (rcPtr->weight > 0.0) {
+	    if (delta < 0) {
+		spaceLeft = rcPtr->size - rcPtr->nomSize;
+	    } else {
+		spaceLeft = rcPtr->nomSize - rcPtr->size;
+	    }
+	    if (spaceLeft > 0) {
+		nOpen++;
+		totalWeight += rcPtr->weight;
+	    }
+	}
+    }
+
+    while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
+	ration = (int)(delta / totalWeight);
+	if (ration == 0) {
+	    ration = (delta > 0) ? 1 : -1;
+	}
+	for (linkPtr = Blt_ChainFirstLink(chainPtr);
+	    (linkPtr != NULL) && (delta != 0);
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    if (rcPtr->weight > 0.0) {
+		spaceLeft = rcPtr->nomSize - rcPtr->size;
+		if (((delta > 0) && (spaceLeft > 0)) ||
+		    ((delta < 0) && (spaceLeft < 0))) {
+		    size = (int)(ration * rcPtr->weight);
+		    if (size > delta) {
+			size = delta;
+		    }
+		    if (ABS(size) < ABS(spaceLeft)) {
+			delta -= size;
+			rcPtr->size += size;
+		    } else {
+			delta -= spaceLeft;
+			rcPtr->size += spaceLeft;
+			nOpen--;
+			totalWeight -= rcPtr->weight;
+		    }
+		}
+	    }
+	}
+    }
+    /*
+     * ------------------------------------------------------------------------
+     *
+     * Pass 2: Adjust the partitions with space still available
+     *
+     * ------------------------------------------------------------------------
+     */
+
+    nOpen = 0;
+    totalWeight = 0.0;
+    for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if (rcPtr->weight > 0.0) {
+	    if (delta > 0) {
+		spaceLeft = rcPtr->maxSize - rcPtr->size;
+	    } else {
+		spaceLeft = rcPtr->size - rcPtr->minSize;
+	    }
+	    if (spaceLeft > 0) {
+		nOpen++;
+		totalWeight += rcPtr->weight;
+	    }
+	}
+    }
+    while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
+	ration = (int)(delta / totalWeight);
+	if (ration == 0) {
+	    ration = (delta > 0) ? 1 : -1;
+	}
+	linkPtr = Blt_ChainFirstLink(chainPtr);
+	for ( /*empty*/ ; (linkPtr != NULL) && (delta != 0);
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    if (rcPtr->weight > 0.0) {
+		if (delta > 0) {
+		    spaceLeft = rcPtr->maxSize - rcPtr->size;
+		} else {
+		    spaceLeft = rcPtr->minSize - rcPtr->size;
+		}
+		if (((delta > 0) && (spaceLeft > 0)) ||
+		    ((delta < 0) && (spaceLeft < 0))) {
+		    size = (int)(ration * rcPtr->weight);
+		    if (size > delta) {
+			size = delta;
+		    }
+		    if (ABS(size) < ABS(spaceLeft)) {
+			delta -= size;
+			rcPtr->size += size;
+		    } else {
+			delta -= spaceLeft;
+			rcPtr->size += spaceLeft;
+			nOpen--;
+			totalWeight -= rcPtr->weight;
+		    }
+		}
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ResetPartitions --
+ *
+ *	Sets/resets the size of each row and column partition to the
+ *	minimum limit of the partition (this is usually zero). This
+ *	routine gets called when new widgets are added, deleted, or
+ *	resized.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ * 	The size of each partition is re-initialized to its minimum
+ * 	size.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ResetPartitions(tablePtr, infoPtr, limitsProc)
+    Table *tablePtr;
+    PartitionInfo *infoPtr;
+    LimitsProc *limitsProc;
+{
+    register RowColumn *rcPtr;
+    register Blt_ChainLink *linkPtr;
+    int pad, size;
+
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+
+	/*
+	 * The constraint procedure below also has the desired
+	 * side-effect of setting the minimum, maximum, and nominal
+	 * values to the requested size of its associated widget (if
+	 * one exists).
+	 */
+	size = (*limitsProc) (0, &(rcPtr->reqSize));
+
+	pad = PADDING(rcPtr->pad) + infoPtr->ePad;
+	if (rcPtr->reqSize.flags & LIMITS_SET_NOM) {
+
+	    /*
+	     * This could be done more cleanly.  We want to ensure
+	     * that the requested nominal size is not overridden when
+	     * determining the normal sizes.  So temporarily fix min
+	     * and max to the nominal size and reset them back later.
+	     */
+	    rcPtr->minSize = rcPtr->maxSize = rcPtr->size =
+		rcPtr->nomSize = size + pad;
+
+	} else {
+	    /* The range defaults to 0..MAXINT */
+	    rcPtr->minSize = rcPtr->reqSize.min + pad;
+	    rcPtr->maxSize = rcPtr->reqSize.max + pad;
+	    rcPtr->nomSize = LIMITS_NOM;
+	    rcPtr->size = pad;
+	}
+	rcPtr->minSpan = 0;
+	rcPtr->control = NULL;
+	rcPtr->count = 0;
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SetNominalSizes
+ *
+ *	Sets the normal sizes for each partition.  The partition size
+ *	is the requested widget size plus an amount of padding.  In
+ *	addition, adjust the min/max bounds of the partition depending
+ *	upon the resize flags (whether the partition can be expanded
+ *	or shrunk from its normal size).
+ *
+ * Results:
+ *	Returns the total space needed for the all the partitions.
+ *
+ * Side Effects:
+ *	The nominal size of each partition is set.  This is later used
+ *	to determine how to shrink or grow the table if the container
+ *	can't be resized to accommodate the exact size requirements
+ *	of all the partitions.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+SetNominalSizes(tablePtr, infoPtr)
+    Table *tablePtr;
+    PartitionInfo *infoPtr;
+{
+    register RowColumn *rcPtr;
+    Blt_ChainLink *linkPtr;
+    int pad, size, total;
+
+    total = 0;
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	pad = PADDING(rcPtr->pad) + infoPtr->ePad;
+
+	/*
+	 * Restore the real bounds after temporarily setting nominal
+	 * size.  These values may have been set in ResetPartitions to
+	 * restrict the size of the paritition to the requested range.
+	 */
+
+	rcPtr->minSize = rcPtr->reqSize.min + pad;
+	rcPtr->maxSize = rcPtr->reqSize.max + pad;
+
+	size = rcPtr->size;
+	if (size > rcPtr->maxSize) {
+	    size = rcPtr->maxSize;
+	} else if (size < rcPtr->minSize) {
+	    size = rcPtr->minSize;
+	}
+	if ((infoPtr->ePad > 0) && (size < tablePtr->editPtr->minSize)) {
+	    size = tablePtr->editPtr->minSize;
+	}
+	rcPtr->nomSize = rcPtr->size = size;
+
+	/*
+	 * If a partition can't be resized (to either expand or
+	 * shrink), hold its respective limit at its normal size.
+	 */
+	if (!(rcPtr->resize & RESIZE_EXPAND)) {
+	    rcPtr->maxSize = rcPtr->nomSize;
+	}
+	if (!(rcPtr->resize & RESIZE_SHRINK)) {
+	    rcPtr->minSize = rcPtr->nomSize;
+	}
+	if (rcPtr->control == NULL) {
+	    /* If a row/column contains no entries, then its size
+	     * should be locked. */
+	    if (rcPtr->resize & RESIZE_VIRGIN) {
+		rcPtr->maxSize = rcPtr->minSize = size;
+	    } else {
+		if (!(rcPtr->resize & RESIZE_EXPAND)) {
+		    rcPtr->maxSize = size;
+		}
+		if (!(rcPtr->resize & RESIZE_SHRINK)) {
+		    rcPtr->minSize = size;
+		}
+	    }
+	    rcPtr->nomSize = size;
+	}
+	total += rcPtr->nomSize;
+    }
+    return total;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LockPartitions
+ *
+ *	Sets the maximum size of a row or column, if the partition
+ *	has a widget that controls it.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+LockPartitions(infoPtr)
+    PartitionInfo *infoPtr;
+{
+    register RowColumn *rcPtr;
+    Blt_ChainLink *linkPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if (rcPtr->control != NULL) {
+	    /* Partition is controlled by this widget */
+	    rcPtr->maxSize = rcPtr->size;
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LayoutPartitions --
+ *
+ *	Calculates the normal space requirements for both the row and
+ *	column partitions.  Each widget is added in order of the
+ *	number of rows or columns spanned, which defines the space
+ *	needed among in the partitions spanned.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *
+ * 	The sum of normal sizes set here will be used as the normal size
+ * 	for the container widget.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+LayoutPartitions(tablePtr)
+    Table *tablePtr;
+{
+    register Blt_ListNode node;
+    Blt_Chain *chainPtr;
+    Blt_ChainLink *linkPtr;
+    register Entry *entryPtr;
+    int needed, used, total;
+    PartitionInfo *infoPtr;
+
+    infoPtr = &(tablePtr->columnInfo);
+
+    ResetPartitions(tablePtr, infoPtr, GetBoundedWidth);
+
+    for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    if (entryPtr->column.control != CONTROL_FULL) {
+		continue;
+	    }
+	    needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
+		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+	    if (needed <= 0) {
+		continue;
+	    }
+	    used = GetSpan(infoPtr, entryPtr);
+	    if (needed > used) {
+		GrowSpan(infoPtr, entryPtr, needed - used);
+	    }
+	}
+    }
+
+    LockPartitions(infoPtr);
+
+    for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+
+	    needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
+		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+
+	    if (entryPtr->column.control >= 0.0) {
+		needed = (int)(needed * entryPtr->column.control);
+	    }
+	    if (needed <= 0) {
+		continue;
+	    }
+	    used = GetSpan(infoPtr, entryPtr);
+	    if (needed > used) {
+		GrowSpan(infoPtr, entryPtr, needed - used);
+	    }
+	}
+    }
+    total = SetNominalSizes(tablePtr, infoPtr);
+    tablePtr->normal.width = GetBoundedWidth(total, &(tablePtr->reqWidth)) +
+	PADDING(tablePtr->padX) +
+	2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
+
+    infoPtr = &(tablePtr->rowInfo);
+
+    ResetPartitions(tablePtr, infoPtr, GetBoundedHeight);
+
+    for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
+
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    if (entryPtr->row.control != CONTROL_FULL) {
+		continue;
+	    }
+	    needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
+		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+	    if (needed <= 0) {
+		continue;
+	    }
+	    used = GetSpan(infoPtr, entryPtr);
+	    if (needed > used) {
+		GrowSpan(infoPtr, entryPtr, needed - used);
+	    }
+	}
+    }
+
+    LockPartitions(&(tablePtr->rowInfo));
+
+    for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
+	node = Blt_ListNextNode(node)) {
+	chainPtr = Blt_ChainGetValue(node);
+
+	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
+		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+	    if (entryPtr->row.control >= 0.0) {
+		needed = (int)(needed * entryPtr->row.control);
+	    }
+	    if (needed <= 0) {
+		continue;
+	    }
+	    used = GetSpan(infoPtr, entryPtr);
+	    if (needed > used) {
+		GrowSpan(infoPtr, entryPtr, needed - used);
+	    }
+	}
+    }
+    total = SetNominalSizes(tablePtr, infoPtr);
+    tablePtr->normal.height = GetBoundedHeight(total, &(tablePtr->reqHeight)) +
+	PADDING(tablePtr->padY) +
+	2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeEntries
+ *
+ *	Places each widget at its proper location.  First determines
+ *	the size and position of the each widget.  It then considers the
+ *	following:
+ *
+ *	  1. translation of widget position its parent widget.
+ *	  2. fill style
+ *	  3. anchor
+ *	  4. external and internal padding
+ *	  5. widget size must be greater than zero
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ * 	The size of each partition is re-initialized its minimum size.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ArrangeEntries(tablePtr)
+    Table *tablePtr;		/* Table widget structure */
+{
+    register Blt_ChainLink *linkPtr;
+    register Entry *entryPtr;
+    register int spanWidth, spanHeight;
+    int x, y;
+    int winWidth, winHeight;
+    int dx, dy;
+    int maxX, maxY;
+    int extra;
+
+    maxX = tablePtr->container.width -
+	(Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padRight +
+	tablePtr->eTablePad);
+    maxY = tablePtr->container.height -
+	(Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padBottom +
+	tablePtr->eTablePad);
+
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	entryPtr = Blt_ChainGetValue(linkPtr);
+
+	x = entryPtr->column.rcPtr->offset +
+	    entryPtr->column.rcPtr->pad.side1 +
+	    entryPtr->padLeft +
+	    Tk_Changes(entryPtr->tkwin)->border_width +
+	    tablePtr->eEntryPad;
+	y = entryPtr->row.rcPtr->offset +
+	    entryPtr->row.rcPtr->pad.side1 +
+	    entryPtr->padTop +
+	    Tk_Changes(entryPtr->tkwin)->border_width +
+	    tablePtr->eEntryPad;
+
+	/*
+	 * Unmap any widgets that start beyond of the right edge of
+	 * the container.
+	 */
+	if ((x >= maxX) || (y >= maxY)) {
+	    if (Tk_IsMapped(entryPtr->tkwin)) {
+		if (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin) {
+		    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+		}
+		Tk_UnmapWindow(entryPtr->tkwin);
+	    }
+	    continue;
+	}
+	extra = 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
+	spanWidth = GetSpan(&(tablePtr->columnInfo), entryPtr) -
+	    (extra + PADDING(entryPtr->padX));
+	spanHeight = GetSpan(&(tablePtr->rowInfo), entryPtr) -
+	    (extra + PADDING(entryPtr->padY));
+
+	winWidth = GetReqWidth(entryPtr);
+	winHeight = GetReqHeight(entryPtr);
+
+	/*
+	 *
+	 * Compare the widget's requested size to the size of the span.
+	 *
+	 * 1) If the widget is larger than the span or if the fill flag
+	 *    is set, make the widget the size of the span. Check that the
+	 *    new size is within the bounds set for the widget.
+	 *
+	 * 2) Otherwise, position the widget in the space according to its
+	 *    anchor.
+	 *
+	 */
+	if ((spanWidth <= winWidth) || (entryPtr->fill & FILL_X)) {
+	    winWidth = spanWidth;
+	    if (winWidth > entryPtr->reqWidth.max) {
+		winWidth = entryPtr->reqWidth.max;
+	    }
+	}
+	if ((spanHeight <= winHeight) || (entryPtr->fill & FILL_Y)) {
+	    winHeight = spanHeight;
+	    if (winHeight > entryPtr->reqHeight.max) {
+		winHeight = entryPtr->reqHeight.max;
+	    }
+	}
+	dx = dy = 0;
+	if (spanWidth > winWidth) {
+	    dx = (spanWidth - winWidth);
+	}
+	if (spanHeight > winHeight) {
+	    dy = (spanHeight - winHeight);
+	}
+	if ((dx > 0) || (dy > 0)) {
+	    TranslateAnchor(dx, dy, entryPtr->anchor, &x, &y);
+	}
+	/*
+	 * Clip the widget at the bottom and/or right edge of the
+	 * container.
+	 */
+	if (winWidth > (maxX - x)) {
+	    winWidth = (maxX - x);
+	}
+	if (winHeight > (maxY - y)) {
+	    winHeight = (maxY - y);
+	}
+
+	/*
+	 * If the widget is too small (i.e. it has only an external
+	 * border) then unmap it.
+	 */
+	if ((winWidth < 1) || (winHeight < 1)) {
+	    if (Tk_IsMapped(entryPtr->tkwin)) {
+		if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
+		    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
+		}
+		Tk_UnmapWindow(entryPtr->tkwin);
+	    }
+	    continue;
+	}
+
+	/*
+	 * Resize and/or move the widget as necessary.
+	 */
+	entryPtr->x = x;
+	entryPtr->y = y;
+
+	if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
+	    Tk_MaintainGeometry(entryPtr->tkwin, tablePtr->tkwin, x, y,
+		winWidth, winHeight);
+	} else {
+	    if ((x != Tk_X(entryPtr->tkwin)) || 
+		(y != Tk_Y(entryPtr->tkwin)) ||
+		(winWidth != Tk_Width(entryPtr->tkwin)) ||
+		(winHeight != Tk_Height(entryPtr->tkwin))) {
+		Tk_MoveResizeWindow(entryPtr->tkwin, x, y, winWidth, winHeight);
+	    }
+	    if (!Tk_IsMapped(entryPtr->tkwin)) {
+		Tk_MapWindow(entryPtr->tkwin);
+	    }
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeTable --
+ *
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ * 	The widgets in the table are possibly resized and redrawn.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+ArrangeTable(clientData)
+    ClientData clientData;
+{
+    Table *tablePtr = clientData;
+    int width, height;
+    int offset;
+    int padX, padY;
+    int outerPad;
+    RowColumn *columnPtr, *rowPtr;
+    Blt_ChainLink *linkPtr;
+
+#ifdef notdef
+    fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin));
+#endif
+    Tcl_Preserve(tablePtr);
+    tablePtr->flags &= ~ARRANGE_PENDING;
+
+    tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad = tablePtr->eTablePad =
+	tablePtr->eEntryPad = 0;
+    if (tablePtr->editPtr != NULL) {
+	tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad =
+	    tablePtr->editPtr->gridLineWidth;
+	tablePtr->eTablePad = tablePtr->editPtr->gridLineWidth;
+	tablePtr->eEntryPad = tablePtr->editPtr->entryPad;
+    }
+    /*
+     * If the table has no children anymore, then don't do anything at
+     * all: just leave the container widget's size as-is.
+     */
+    if ((Blt_ChainGetLength(tablePtr->chainPtr) == 0) ||
+	(tablePtr->tkwin == NULL)) {
+	Tcl_Release(tablePtr);
+	return;
+    }
+    if (tablePtr->flags & REQUEST_LAYOUT) {
+	tablePtr->flags &= ~REQUEST_LAYOUT;
+	LayoutPartitions(tablePtr);
+    }
+    /*
+     * Initially, try to fit the partitions exactly into the container
+     * by resizing the container.  If the widget's requested size is
+     * different, send a request to the container widget's geometry
+     * manager to resize.
+     */
+    if ((tablePtr->propagate) &&
+	((tablePtr->normal.width != Tk_ReqWidth(tablePtr->tkwin)) ||
+	    (tablePtr->normal.height != Tk_ReqHeight(tablePtr->tkwin)))) {
+	Tk_GeometryRequest(tablePtr->tkwin, tablePtr->normal.width,
+	    tablePtr->normal.height);
+	EventuallyArrangeTable(tablePtr);
+	Tcl_Release(tablePtr);
+	return;
+    }
+    /*
+     * Save the width and height of the container so we know when its
+     * size has changed during ConfigureNotify events.
+     */
+    tablePtr->container.width = Tk_Width(tablePtr->tkwin);
+    tablePtr->container.height = Tk_Height(tablePtr->tkwin);
+    outerPad = 2 * (Tk_InternalBorderWidth(tablePtr->tkwin) +
+	tablePtr->eTablePad);
+    padX = outerPad + tablePtr->columnInfo.ePad + PADDING(tablePtr->padX);
+    padY = outerPad + tablePtr->rowInfo.ePad + PADDING(tablePtr->padY);
+
+    width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
+    height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
+
+    /*
+     * If the previous geometry request was not fulfilled (i.e. the
+     * size of the container is different from partitions' space
+     * requirements), try to adjust size of the partitions to fit the
+     * widget.
+     */
+    if (tablePtr->container.width != width) {
+	AdjustPartitions(&(tablePtr->columnInfo),
+	    tablePtr->container.width - width);
+	width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
+    }
+    if (tablePtr->container.height != height) {
+	AdjustPartitions(&(tablePtr->rowInfo),
+	    tablePtr->container.height - height);
+	height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
+    }
+    /*
+     * If after adjusting the size of the partitions the space
+     * required does not equal the size of the widget, do one of the
+     * following:
+     *
+     * 1) If it's smaller, center the table in the widget.
+     * 2) If it's bigger, clip the partitions that extend beyond
+     *    the edge of the container.
+     *
+     * Set the row and column offsets (including the container's
+     * internal border width). To be used later when positioning the
+     * widgets.
+     */
+    offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padLeft +
+	tablePtr->eTablePad;
+    if (width < tablePtr->container.width) {
+	offset += (tablePtr->container.width - width) / 2;
+    }
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	columnPtr->offset = offset + tablePtr->columnInfo.ePad;
+	offset += columnPtr->size;
+    }
+    offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padTop +
+	tablePtr->eTablePad;
+    if (height < tablePtr->container.height) {
+	offset += (tablePtr->container.height - height) / 2;
+    }
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rowPtr = Blt_ChainGetValue(linkPtr);
+	rowPtr->offset = offset + tablePtr->rowInfo.ePad;
+	offset += rowPtr->size;
+    }
+    ArrangeEntries(tablePtr);
+    if (tablePtr->editPtr != NULL) {
+	/* Redraw the editor */
+	(*tablePtr->editPtr->drawProc) (tablePtr->editPtr);
+    }
+    Tcl_Release(tablePtr);
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ArrangeOp --
+ *
+ *	Forces layout of the table geometry manager.  This is useful
+ *	mostly for debugging the geometry manager.  You can get the
+ *	geometry manager to calculate the normal (requested) width and
+ *	height of each row and column.  Otherwise, you need to first
+ *	withdraw the container widget, invoke "update", and then query
+ *	the geometry manager.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If the table is successfully
+ *	rearranged, TCL_OK is returned. Otherwise, TCL_ERROR is returned
+ *	and an error message is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArrangeOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int argc;
+    char **argv;		/* Path name of container associated with
+				 * the table */
+{
+    Table *tablePtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tablePtr->flags |= REQUEST_LAYOUT;
+    ArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *	Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget attributes
+ *	is left in interp->result.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    int length;
+    char c;
+    int n;
+    PartitionInfo *infoPtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (argc == 4) {
+	return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
+	    (char *)tablePtr, argv[3], 0);
+    }
+    c = argv[3][0];
+    length = strlen(argv[3]);
+    if (c == '.') {		/* Configure widget */
+	Entry *entryPtr;
+
+	if (GetEntry(interp, tablePtr, argv[3], &entryPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	return Tk_ConfigureValue(interp, entryPtr->tkwin, entryConfigSpecs,
+	    (char *)entryPtr, argv[4], 0);
+    } else if ((c == 'c') && (strncmp(argv[3], "container", length) == 0)) {
+	return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
+	    (char *)tablePtr, argv[4], 0);
+    }
+    infoPtr = ParseRowColumn(tablePtr, argv[3], &n);
+    if (infoPtr == NULL) {
+	return TCL_ERROR;
+    }
+    return Tk_ConfigureValue(interp, tablePtr->tkwin, infoPtr->configSpecs,
+	(char *)GetRowColumn(infoPtr, n), argv[4], 0);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ *	Returns the name, position and options of a widget in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the table configuration
+ *	option information is left in interp->result.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    int length;
+    char c1, c2;
+    int count;
+    int result;
+    char **items;
+    register int i;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /*
+     * Find the end of the items. Search until we see an option (-).
+     */
+    argc -= 3, argv += 3;
+    for (count = 0; count < argc; count++) {
+	if (argv[count][0] == '-') {
+	    break;
+	}
+    }
+    items = argv;		/* Save the start of the item list */
+    argc -= count;		/* Move beyond the items to the options */
+    argv += count;
+
+    result = TCL_ERROR;		/* Suppress compiler warning */
+
+    if (count == 0) {
+	result = ConfigureTable(tablePtr, interp, argc, argv);
+    }
+    for (i = 0; i < count; i++) {
+	c1 = items[i][0];
+	c2 = items[i][1];
+	length = strlen(items[i]);
+	if (c1 == '.') {		/* Configure widget */
+	    Entry *entryPtr;
+
+	    if (GetEntry(interp, tablePtr, items[i], &entryPtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    result = ConfigureEntry(tablePtr, interp, entryPtr, argc, argv);
+	} else if ((c1 == 'r') || (c1 == 'R')) {
+	    result = ConfigureRowColumn(tablePtr, &(tablePtr->rowInfo),
+		items[i], argc, argv);
+	} else if ((c1 == 'c') && (c2 == 'o') &&
+	    (strncmp(argv[3], "container", length) == 0)) {
+	    result = ConfigureTable(tablePtr, interp, argc, argv);
+	} else if ((c1 == 'c') || (c1 == 'C')) {
+	    result = ConfigureRowColumn(tablePtr, &(tablePtr->columnInfo),
+		items[i], argc, argv);
+	} else {
+	    Tcl_AppendResult(interp, "unknown item \"", items[i],
+		"\": should be widget, row or column index, or \"container\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (result == TCL_ERROR) {
+	    break;
+	}
+	if ((i + 1) < count) {
+	    Tcl_AppendResult(interp, "\n", (char *)NULL);
+	}
+    }
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes the specified rows and/or columns from the table.
+ *	Note that the row/column indices can be fixed only after
+ *	all the deletions have occurred.
+ *
+ *		table delete .f r0 r1 r4 c0
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    char c;
+    Blt_ChainLink *linkPtr, *nextPtr;
+    PartitionInfo *infoPtr;
+    char string[200];
+    int matches;
+    register int i;
+    RowColumn *rcPtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (i = 3; i < argc; i++) {
+	c = tolower(argv[i][0]);
+	if ((c != 'r') && (c != 'c')) {
+	    Tcl_AppendResult(interp, "bad index \"", argv[i],
+		"\": must start with \"r\" or \"c\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    matches = 0;
+    for (i = 3; i < argc; i++) {
+	c = tolower(argv[i][0]);
+	infoPtr = (c == 'r') ? &(tablePtr->rowInfo) : &(tablePtr->columnInfo);
+	for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = nextPtr) {
+	    nextPtr = Blt_ChainNextLink(linkPtr);
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    sprintf(string, "%c%d", argv[i][0], rcPtr->index);
+	    if (Tcl_StringMatch(string, argv[i])) {
+		matches++;
+		DeleteRowColumn(tablePtr, infoPtr, rcPtr);
+		Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
+	    }
+	}
+    }
+    if (matches > 0) {		/* Fix indices */
+	i = 0;
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
+	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    rcPtr->index = i++;
+	}
+	i = 0;
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
+	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    rcPtr = Blt_ChainGetValue(linkPtr);
+	    rcPtr->index = i++;
+	}
+	tablePtr->flags |= REQUEST_LAYOUT;
+	EventuallyArrangeTable(tablePtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * JoinOp --
+ *
+ *	Joins the specified span of rows/columns together into a
+ *	partition.  The row/column indices can be fixed only after
+ *	all the deletions have occurred.
+ *
+ *		table join .f r0 r3
+ *		table join .f c2 c4
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+JoinOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    Blt_ChainLink *linkPtr, *nextPtr, *fromPtr;
+    PartitionInfo *infoPtr, *info2Ptr;
+    Entry *entryPtr;
+    int from, to;		/* Indices marking the span of
+				 * partitions to be joined together.  */
+    int start, end;		/* Entry indices. */
+    register int i;
+    RowColumn *rcPtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    infoPtr = ParseRowColumn(tablePtr, argv[3], &from);
+    if (infoPtr == NULL) {
+	return TCL_ERROR;
+    }
+    info2Ptr = ParseRowColumn(tablePtr, argv[4], &to);
+    if (info2Ptr == NULL) {
+	return TCL_ERROR;
+    }
+    if (infoPtr != info2Ptr) {
+	Tcl_AppendResult(interp,
+	    "\"from\" and \"to\" must both be rows or columns",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (from >= to) {
+	return TCL_OK;		/* No-op. */
+    }
+    fromPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, from);
+    rcPtr = Blt_ChainGetValue(fromPtr);
+
+    /*
+     * ---------------------------------------------------------------
+     *
+     *	Reduce the span of all entries that currently cross any of the
+     *	trailing rows/columns.  Also, if the entry starts in one of
+     *	these rows/columns, moved it to the designated "joined"
+     *	row/column.
+     *
+     * ---------------------------------------------------------------
+     */
+    if (infoPtr->type == rowUid) {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    start = entryPtr->row.rcPtr->index + 1;
+	    end = entryPtr->row.rcPtr->index + entryPtr->row.span - 1;
+	    if ((end < from) || ((start > to))) {
+		continue;
+	    }
+	    entryPtr->row.span -= to - start + 1;
+	    if (start >= from) {/* Entry starts in a trailing partition. */
+		entryPtr->row.rcPtr = rcPtr;
+	    }
+	}
+    } else {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    start = entryPtr->column.rcPtr->index + 1;
+	    end = entryPtr->column.rcPtr->index + entryPtr->column.span - 1;
+	    if ((end < from) || ((start > to))) {
+		continue;
+	    }
+	    entryPtr->column.span -= to - start + 1;
+	    if (start >= from) {/* Entry starts in a trailing partition. */
+		entryPtr->column.rcPtr = rcPtr;
+	    }
+	}
+    }
+    linkPtr = Blt_ChainNextLink(fromPtr);
+    for (i = from + 1; i <= to; i++) {
+	nextPtr = Blt_ChainNextLink(linkPtr);
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	DeleteRowColumn(tablePtr, infoPtr, rcPtr);
+	Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
+	linkPtr = nextPtr;
+    }
+    i = 0;
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	rcPtr->index = i++;
+    }
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ExtentsOp --
+ *
+ *	Returns a list of all the pathnames of the widgets managed by
+ *	a table.  The table is determined from the name of the
+ *	container widget associated with the table.
+ *
+ *		table extents .frame r0 c0 container
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
+ *	returned and a list of widgets managed by the table is left in
+ *	interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExtentsOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to return results to. */
+    int argc;			/* # of arguments */
+    char **argv;		/* Command line arguments. */
+{
+    Table *tablePtr;
+    Blt_ChainLink *linkPtr;
+    RowColumn *rcPtr;
+    RowColumn *c1Ptr, *r1Ptr, *c2Ptr, *r2Ptr;
+    PartitionInfo *infoPtr;
+    int x, y, width, height;
+    char string[200];
+    char c;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    c = tolower(argv[3][0]);
+    if (c == 'r') {
+	infoPtr = &(tablePtr->rowInfo);
+    } else if (c == 'c') {
+	infoPtr = &(tablePtr->columnInfo);
+    } else {
+	Tcl_AppendResult(interp, "unknown item \"", argv[3],
+	    "\": should be widget, row, or column", (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	sprintf(string, "%c%d", argv[3][0], rcPtr->index);
+	if (Tcl_StringMatch(string, argv[3])) {
+	    if (c == 'r') {
+		r1Ptr = r2Ptr = rcPtr;
+		c1Ptr = GetRowColumn(&(tablePtr->columnInfo), 0);
+		c2Ptr = GetRowColumn(&(tablePtr->columnInfo), 
+				     tablePtr->nColumns - 1);
+	    } else {
+		c1Ptr = c2Ptr = rcPtr;
+		r1Ptr = GetRowColumn(&(tablePtr->rowInfo), 0);
+		r2Ptr = GetRowColumn(&(tablePtr->rowInfo), 
+				     tablePtr->nRows - 1);
+	    }
+	    x = c1Ptr->offset;
+	    y = r1Ptr->offset;
+	    width = c2Ptr->offset + c2Ptr->size - x;
+	    height = r2Ptr->offset + r2Ptr->size - y;
+	    sprintf(string, "%c%d %d %d %d %d\n", argv[3][0], rcPtr->index,
+		x, y, width, height);
+	    Tcl_AppendResult(interp, string, (char *)NULL);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ForgetOp --
+ *
+ *	Processes an argv/argc list of widget names and purges their
+ *	entries from their respective tables.  The widgets are unmapped and
+ *	the tables are rearranged at the next idle point.  Note that all
+ *	the named widgets do not need to exist in the same table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
+ *	returned and an error message is left in interp->result.
+ *
+ * Side Effects:
+ *	Memory is deallocated (the entry is destroyed), etc.  The
+ *	affected tables are is re-computed and arranged at the next idle
+ *	point.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+ForgetOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Entry *entryPtr;
+    register int i;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Table *tablePtr;
+    Tk_Window tkwin, mainWindow;
+    
+    tablePtr = NULL;
+    mainWindow = Tk_MainWindow(interp);
+    for (i = 2; i < argc; i++) {
+	entryPtr = NULL;
+	tkwin = Tk_NameToWindow(interp, argv[i], mainWindow);
+	if (tkwin == NULL) {
+	    return TCL_ERROR;
+	}
+	for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tablePtr = (Table *)Blt_GetHashValue(hPtr);
+	    if (tablePtr->interp != interp) {
+		continue;
+	    }
+	    entryPtr = FindEntry(tablePtr, tkwin);
+	    if (entryPtr != NULL) {
+		break;
+	    }
+	}
+	if (entryPtr == NULL) {
+	    Tcl_AppendResult(interp, "\"", argv[i],
+		"\" is not managed by any table", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Tk_IsMapped(entryPtr->tkwin)) {
+	    Tk_UnmapWindow(entryPtr->tkwin);
+	}
+	/* Arrange for the call back here in the loop, because the
+	 * widgets may not belong to the same table.  */
+	tablePtr->flags |= REQUEST_LAYOUT;
+	EventuallyArrangeTable(tablePtr);
+	DestroyEntry(entryPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InfoOp --
+ *
+ *	Returns the options of a widget or partition in the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget attributes
+ *	is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InfoOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    int result;
+    char c;
+    register int i;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (i = 3; i < argc; i++) {
+	c = argv[i][0];
+	if (c == '.') {		/* Entry information */
+	    Entry *entryPtr;
+
+	    if (GetEntry(interp, tablePtr, argv[i], &entryPtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    result = InfoEntry(interp, tablePtr, entryPtr);
+	} else if ((c == 'r') || (c == 'R') || (c == 'c') || (c == 'C')) {
+	    result = InfoRowColumn(tablePtr, interp, argv[i]);
+	} else {
+	    Tcl_AppendResult(interp, "unknown item \"", argv[i],
+		"\": should be widget, row, or column", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((i + 1) < argc) {
+	    Tcl_AppendResult(interp, "\n", (char *)NULL);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *	Inserts a span of rows/columns into the table.
+ *
+ *		table insert .f r0 2
+ *		table insert .f c0 5
+ *
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget
+ *	attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{ 
+    Table *tablePtr;
+    long int span;
+    int before;
+    PartitionInfo *infoPtr;
+    RowColumn *rcPtr;
+    register int i;
+    Blt_ChainLink *beforePtr, *linkPtr;
+    int linkBefore;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    linkBefore = TRUE;
+    if (argv[3][0] == '-') {
+	if (strcmp(argv[3], "-before") == 0) {
+	    linkBefore = TRUE;
+	    argv++; argc--;
+	} else if (strcmp(argv[3], "-after") == 0) {
+	    linkBefore = FALSE;
+	    argv++; argc--;
+	}	    
+    } 
+    if (argc == 3) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+			 "insert ", argv[2], "row|column ?span?", (char *)NULL);
+	return TCL_ERROR;
+    }
+    infoPtr = ParseRowColumn(tablePtr, argv[3], &before);
+    if (infoPtr == NULL) {
+	return TCL_ERROR;
+    }
+    span = 1;
+    if ((argc > 4) && (Tcl_ExprLong(interp, argv[4], &span) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (span < 1) {
+	Tcl_AppendResult(interp, "span value \"", argv[4],
+	    "\" can't be negative", (char *)NULL);
+	return TCL_ERROR;
+    }
+    beforePtr = Blt_ChainGetNthLink(infoPtr->chainPtr, before);
+    /*
+     * Insert the new rows/columns from the designated point in the
+     * chain.
+     */
+    for (i = 0; i < span; i++) {
+	rcPtr = CreateRowColumn();
+	linkPtr = Blt_ChainNewLink();
+	Blt_ChainSetValue(linkPtr, rcPtr);
+	if (linkBefore) {
+	    Blt_ChainLinkBefore(infoPtr->chainPtr, linkPtr, beforePtr);
+	} else {
+	    Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, beforePtr);
+	}
+	rcPtr->linkPtr = linkPtr;
+    }
+    i = 0;
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	/* Reset the indices of the trailing rows/columns.  */
+	rcPtr->index = i++;
+    }
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SplitOp --
+ *
+ *	Splits a single row/column into multiple partitions. Any
+ *	widgets that span this row/column will be automatically
+ *	corrected to include the new rows/columns.
+ *
+ *		table split .f r0 3
+ *		table split .f c2 2
+ * Results:
+ *	Returns a standard Tcl result.  A list of the widget
+ *	attributes is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SplitOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    int number, split;
+    int start, end;
+    PartitionInfo *infoPtr;
+    RowColumn *rcPtr;
+    register int i;
+    Blt_ChainLink *afterPtr, *linkPtr;
+    Entry *entryPtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    infoPtr = ParseRowColumn(tablePtr, argv[3], &number);
+    if (infoPtr == NULL) {
+	return TCL_ERROR;
+    }
+    split = 2;
+    if (argc > 4) {
+	if (Tcl_GetInt(interp, argv[4], &split) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (split < 2) {
+	Tcl_AppendResult(interp, "bad split value \"", argv[4],
+	    "\": should be 2 or greater", (char *)NULL);
+	return TCL_ERROR;
+    }
+    afterPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, number);
+
+    /*
+     * Append (split - 1) additional rows/columns starting
+     * from the current point in the chain.
+     */
+
+    for (i = 1; i < split; i++) {
+	rcPtr = CreateRowColumn();
+	linkPtr = Blt_ChainNewLink();
+	Blt_ChainSetValue(linkPtr, rcPtr);
+	Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, afterPtr);
+	rcPtr->linkPtr = linkPtr;
+    }
+
+    /*
+     * Also increase the span of all entries that span this
+     * row/column by split - 1.
+     */
+    if (infoPtr->type == rowUid) {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    start = entryPtr->row.rcPtr->index;
+	    end = entryPtr->row.rcPtr->index + entryPtr->row.span;
+	    if ((start <= number) && (number < end)) {
+		entryPtr->row.span += (split - 1);
+	    }
+	}
+    } else {
+	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    start = entryPtr->column.rcPtr->index;
+	    end = entryPtr->column.rcPtr->index + entryPtr->column.span;
+	    if ((start <= number) && (number < end)) {
+		entryPtr->column.span += (split - 1);
+	    }
+	}
+    }
+    /*
+     * Be careful to renumber the rows or columns only after
+     * processing each entry.  Otherwise row/column numbering
+     * will be out of sync with the index.
+     */
+    i = number;
+    for (linkPtr = afterPtr; linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	rcPtr->index = i++;	/* Renumber the trailing indices.  */
+    }
+
+    tablePtr->flags |= REQUEST_LAYOUT;
+    EventuallyArrangeTable(tablePtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RowColumnSearch --
+ *
+ * 	Searches for the row or column designated by an x or y
+ *	coordinate.
+ *
+ * Results:
+ *	Returns a pointer to the row/column containing the given point.
+ *	If no row/column contains the coordinate, NULL is returned.
+ *
+ * ----------------------------------------------------------------------
+ */
+static RowColumn *
+RowColumnSearch(infoPtr, x)
+    PartitionInfo *infoPtr;
+    int x;			/* Search coordinate  */
+{
+    Blt_ChainLink *linkPtr;
+    RowColumn *rcPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	if (x > (rcPtr->offset + rcPtr->size)) {
+	    break;		/* Too far, can't find row/column. */
+	}
+	if (x > rcPtr->offset) {
+	    return rcPtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LocateOp --
+ *
+ *
+ *	Returns the row,column index given a screen coordinate.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+LocateOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int x, y;
+    RowColumn *rowPtr, *columnPtr;
+    Table *tablePtr;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Blt_GetPixels(interp, tablePtr->tkwin, argv[3], PIXELS_ANY, &x)
+	!= TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Blt_GetPixels(interp, tablePtr->tkwin, argv[4], PIXELS_ANY, &y)
+	!= TCL_OK) {
+	return TCL_ERROR;
+    }
+    rowPtr = RowColumnSearch(&(tablePtr->rowInfo), y);
+    if (rowPtr == NULL) {
+	return TCL_OK;
+    }
+    columnPtr = RowColumnSearch(&(tablePtr->columnInfo), x);
+    if (columnPtr == NULL) {
+	return TCL_OK;
+    }
+    Tcl_AppendElement(interp, Blt_Itoa(rowPtr->index));
+    Tcl_AppendElement(interp, Blt_Itoa(columnPtr->index));
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ContainersOp --
+ *
+ *	Returns a list of tables currently in use. A table is
+ *	associated by the name of its container widget.  All tables
+ *	matching a given pattern are included in this list.  If no
+ *	pattern is present (argc == 0), all tables are included.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
+ *	returned and a list of tables is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ContainersOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to return list of names to */
+    int argc;
+    char **argv;		/* Contains 0-1 arguments: search pattern */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    register Table *tablePtr;
+    char *pattern;
+
+    pattern = NULL;
+    if (argc > 2) {
+	if (argv[2][0] == '-') {
+	    unsigned int length;
+
+	    length = strlen(argv[2]);
+	    if ((length > 1) && (argv[2][1] == 'p') &&
+		(strncmp(argv[2], "-pattern", length) == 0)) {
+		pattern = argv[3];
+		goto search;
+	    } else if ((length > 1) && (argv[2][1] == 's') &&
+		(strncmp(argv[2], "-slave", length) == 0)) {
+		Tk_Window tkwin;
+
+		if (argc != 4) {
+		    Tcl_AppendResult(interp, "needs widget argument for \"",
+			argv[2], "\"", (char *)NULL);
+		    return TCL_ERROR;
+		}
+		tkwin = Tk_NameToWindow(interp, argv[3],
+		    Tk_MainWindow(interp));
+		if (tkwin == NULL) {
+		    return TCL_ERROR;
+		}
+		for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+		    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+		    tablePtr = (Table *)Blt_GetHashValue(hPtr);
+		    if (FindEntry(tablePtr, tkwin) != NULL) {
+			Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
+		    }
+		}
+		return TCL_OK;
+	    } else {
+		Tcl_AppendResult(interp, "bad switch \"", argv[2], "\" : \
+should be \"-pattern\", or \"-slave\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	} else {
+	    pattern = argv[2];
+	}
+    }
+  search:
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	tablePtr = (Table *)Blt_GetHashValue(hPtr);
+	if (tablePtr->interp == interp) {
+	    if ((pattern == NULL) ||
+		(Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), pattern))) {
+		Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SaveOp --
+ *
+ *	Returns a list of all the commands necessary to rebuild the
+ *	the table.  This includes the layout of the widgets and any
+ *	row, column, or table options set.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
+ *	returned and a list of widget path names is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SaveOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Table *tablePtr;
+    Blt_ChainLink *linkPtr, *lastPtr;
+    Entry *entryPtr;
+    PartitionInfo *infoPtr;
+    RowColumn *rcPtr;
+    Tcl_DString dString;
+    int start, last;
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppend(&dString, "\n# Table widget layout\n\n", -1);
+    Tcl_DStringAppend(&dString, argv[0], -1);
+    Tcl_DStringAppend(&dString, " ", -1);
+    Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+    Tcl_DStringAppend(&dString, " \\\n", -1);
+    lastPtr = Blt_ChainLastLink(tablePtr->chainPtr);
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	entryPtr = Blt_ChainGetValue(linkPtr);
+	PrintEntry(entryPtr, &dString);
+	if (linkPtr != lastPtr) {
+	    Tcl_DStringAppend(&dString, " \\\n", -1);
+	}
+    }
+    Tcl_DStringAppend(&dString, "\n\n# Row configuration options\n\n", -1);
+    infoPtr = &(tablePtr->rowInfo);
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	start = Tcl_DStringLength(&dString);
+	Tcl_DStringAppend(&dString, argv[0], -1);
+	Tcl_DStringAppend(&dString, " configure ", -1);
+	Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+	Tcl_DStringAppend(&dString, " r", -1);
+	Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
+	last = Tcl_DStringLength(&dString);
+	PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+	if (Tcl_DStringLength(&dString) == last) {
+	    Tcl_DStringSetLength(&dString, start);
+	} else {
+	    Tcl_DStringAppend(&dString, "\n", -1);
+	}
+    }
+    Tcl_DStringAppend(&dString, "\n\n# Column configuration options\n\n", -1);
+    infoPtr = &(tablePtr->columnInfo);
+    for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	rcPtr = Blt_ChainGetValue(linkPtr);
+	start = Tcl_DStringLength(&dString);
+	Tcl_DStringAppend(&dString, argv[0], -1);
+	Tcl_DStringAppend(&dString, " configure ", -1);
+	Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+	Tcl_DStringAppend(&dString, " c", -1);
+	Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
+	last = Tcl_DStringLength(&dString);
+	PrintRowColumn(interp, infoPtr, rcPtr, &dString);
+	if (Tcl_DStringLength(&dString) == last) {
+	    Tcl_DStringSetLength(&dString, start);
+	} else {
+	    Tcl_DStringAppend(&dString, "\n", -1);
+	}
+    }
+    start = Tcl_DStringLength(&dString);
+    Tcl_DStringAppend(&dString, "\n\n# Table configuration options\n\n", -1);
+    Tcl_DStringAppend(&dString, argv[0], -1);
+    Tcl_DStringAppend(&dString, " configure ", -1);
+    Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
+    last = Tcl_DStringLength(&dString);
+    PrintTable(tablePtr, &dString);
+    if (Tcl_DStringLength(&dString) == last) {
+	Tcl_DStringSetLength(&dString, start);
+    } else {
+	Tcl_DStringAppend(&dString, "\n", -1);
+    }
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ *	Returns a list of all the pathnames of the widgets managed by
+ *	a table geometry manager.  The table is given by the path name of a
+ *	container widget associated with the table.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
+ *	returned and a list of widget path names is left in interp->result.
+ *
+ * ----------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(dataPtr, interp, argc, argv)
+    TableInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;		/* Interpreter to return list of names to */
+    int argc;			/* Number of arguments */
+    char **argv;		/* Contains 1-2 arguments: pathname of container
+				 * widget associated with the table and search
+				 * pattern */
+{
+    Table *tablePtr;
+    Blt_ChainLink *linkPtr;
+    Entry *entryPtr;
+    int rspan, cspan, rstart, cstart;
+    char *pattern;
+    char c;
+    int flags;
+    register int i;
+
+#define	MATCH_PATTERN		(1<<0)	/* Find widgets whose path names
+					 * match a given pattern */
+#define	MATCH_INDEX_SPAN	(1<<1)	/* Find widgets that span index  */
+#define	MATCH_INDEX_START	(1<<2)	/* Find widgets that start at index */
+
+
+    if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    flags = 0;
+    pattern = NULL;
+    rspan = cspan = rstart = cstart = 0;
+
+    /* Parse switches and arguments first */
+    for (i = 3; i < argc; i += 2) {
+	if (argv[i][0] == '-') {
+	    unsigned int length;
+
+	    if ((i + 1) == argc) {
+		Tcl_AppendResult(interp, "switch \"", argv[i], "\" needs value",
+		    (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    length = strlen(argv[i]);
+	    c = argv[i][1];
+	    if ((c == 'p') && (length > 1) &&
+		(strncmp(argv[3], "-pattern", length) == 0)) {
+		flags |= MATCH_PATTERN;
+		pattern = argv[4];
+	    } else if ((c == 's') && (length > 2) &&
+		(strncmp(argv[i], "-start", length) == 0)) {
+		flags |= MATCH_INDEX_START;
+		if (ParseItem(tablePtr, argv[i + 1],
+			&rstart, &cstart) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+	    } else if ((c == 's') && (length > 2) &&
+		(strncmp(argv[i], "-span", length) == 0)) {
+		flags |= MATCH_INDEX_SPAN;
+		if (ParseItem(tablePtr, argv[4],
+			&rspan, &cspan) != TCL_OK) {
+		    return TCL_ERROR;
+		}
+	    } else {
+		Tcl_AppendResult(interp, "bad switch \"", argv[3], "\" : \
+should be \"-pattern\", \"-span\", or \"-start\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	} else {
+	    if ((i + 1) == argc) {
+		pattern = argv[i];
+		flags |= MATCH_PATTERN;
+	    }
+	}
+    }
+
+    /* Then try to match entries with the search criteria */
+
+    for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	entryPtr = Blt_ChainGetValue(linkPtr);
+	if ((flags & MATCH_PATTERN) && (pattern != NULL)) {
+	    if (Tcl_StringMatch(Tk_PathName(entryPtr->tkwin), pattern)) {
+		goto match;
+	    }
+	}
+	if (flags & MATCH_INDEX_SPAN) {
+	    if ((rspan >= 0) && ((entryPtr->row.rcPtr->index <= rspan) ||
+		    ((entryPtr->row.rcPtr->index + entryPtr->row.span) > rspan))) {
+		goto match;
+	    }
+	    if ((cspan >= 0) && ((entryPtr->column.rcPtr->index <= cspan) ||
+		    ((entryPtr->column.rcPtr->index + entryPtr->column.span)
+			> cspan))) {
+		goto match;
+	    }
+	}
+	if (flags & MATCH_INDEX_START) {
+	    if ((rstart >= 0) && (entryPtr->row.rcPtr->index == rstart)) {
+		goto match;
+	    }
+	    if ((cstart >= 0) && (entryPtr->column.rcPtr->index == cstart)) {
+		goto match;
+	    }
+	}
+	continue;
+      match:
+	Tcl_AppendElement(interp, Tk_PathName(entryPtr->tkwin));
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Table operations.
+ *
+ * The fields for Blt_OpSpec are as follows:
+ *
+ *   - operation name
+ *   - minimum number of characters required to disambiguate the operation name.
+ *   - function associated with operation.
+ *   - minimum number of arguments required.
+ *   - maximum number of arguments allowed (0 indicates no limit).
+ *   - usage string
+ *
+ * ----------------------------------------------------------------------------
+ */
+static Blt_OpSpec operSpecs[] =
+{
+    {"arrange", 1, (Blt_Op)ArrangeOp, 3, 3, "container",},
+    {"cget", 2, (Blt_Op)CgetOp, 4, 5,
+	"container ?row|column|widget? option",},
+    {"configure", 3, (Blt_Op)ConfigureOp, 3, 0,
+	"container ?row|column|widget?... ?option value?...",},
+    {"containers", 3, (Blt_Op)ContainersOp, 2, 4, "?switch? ?arg?",},
+    {"delete", 1, (Blt_Op)DeleteOp, 3, 0,
+	"container row|column ?row|column?",},
+    {"extents", 1, (Blt_Op)ExtentsOp, 4, 4,
+	"container row|column|widget",},
+    {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "widget ?widget?...",},
+    {"info", 3, (Blt_Op)InfoOp, 3, 0,
+	"container ?row|column|widget?...",},
+    {"insert", 3, (Blt_Op)InsertOp, 4, 6,
+	"container ?-before|-after? row|column ?count?",},
+    {"join", 1, (Blt_Op)JoinOp, 5, 5, "container first last",},
+    {"locate", 2, (Blt_Op)LocateOp, 5, 5, "container x y",},
+    {"save", 2, (Blt_Op)SaveOp, 3, 3, "container",},
+    {"search", 2, (Blt_Op)SearchOp, 3, 0, "container ?switch arg?...",},
+    {"split", 2, (Blt_Op)SplitOp, 4, 5, "container row|column div",},
+};
+
+static int nSpecs = sizeof(operSpecs) / sizeof(Blt_OpSpec);
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TableCmd --
+ *
+ *	This procedure is invoked to process the Tcl command that
+ *	corresponds to the table geometry manager.  See the user
+ *	documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+TableCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    TableInterpData *dataPtr = clientData;
+    Blt_Op proc;
+    int result;
+
+    if ((argc > 1) && (argv[1][0] == '.')) {
+	Table *tablePtr;
+
+	if (Blt_GetTable(clientData, interp, argv[1], &tablePtr) != TCL_OK) {
+	    Tcl_ResetResult(interp);
+	    tablePtr = CreateTable(dataPtr, interp, argv[1]);
+	    if (tablePtr == NULL) {
+		return TCL_ERROR;
+	    }
+	}
+	return BuildTable(tablePtr, interp, argc, argv);
+    }
+    proc = Blt_GetOp(interp, nSpecs, operSpecs, BLT_OP_ARG1, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (dataPtr, interp, argc, argv);
+    return result;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TableInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting the table command 
+ *	is destroyed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys all the hash table maintaining the names of the table
+ *	geomtry managers.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TableInterpDeleteProc(clientData, interp)
+    ClientData clientData;	/* Thread-specific data. */
+    Tcl_Interp *interp;
+{
+    TableInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Table *tablePtr;
+
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	tablePtr = (Table *)Blt_GetHashValue(hPtr);
+	tablePtr->hashPtr = NULL;
+	DestroyTable((DestroyData)tablePtr);
+    }
+    Blt_DeleteHashTable(&(dataPtr->tableTable));
+    Tcl_DeleteAssocData(interp, TABLE_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+static TableInterpData *
+GetTableInterpData(interp)
+     Tcl_Interp *interp;
+{
+    TableInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (TableInterpData *)
+	Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(TableInterpData));
+	assert(dataPtr);
+	Tcl_SetAssocData(interp, TABLE_THREAD_KEY, TableInterpDeleteProc, 
+		dataPtr);
+	Blt_InitHashTable(&(dataPtr->tableTable), BLT_ONE_WORD_KEYS);
+    }
+    return dataPtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Blt_TableInit --
+ *
+ *	This procedure is invoked to initialize the Tcl command that
+ *	corresponds to the table geometry manager.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Creates the new command and adds an entry into a global Tcl
+ *	associative array.
+ *
+ * ---------------------------------------------------------------------------
+ */
+int
+Blt_TableInit(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpec = {"table", TableCmd, };
+    TableInterpData *dataPtr;
+
+    dataPtr = GetTableInterpData(interp);
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    rowUid = (Blt_Uid)Tk_GetUid("row");
+    columnUid = (Blt_Uid)Tk_GetUid("column");
+    return TCL_OK;
+}
Index: trunk/kitgen/8.x/blt/generic/bltTable.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTable.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTable.h	(revision 175)
@@ -0,0 +1,390 @@
+/*
+ * bltTable.h --
+ *
+ *	This module implements a table-based geometry manager
+ *	for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The table geometry manager was created by George Howlett.
+ */
+
+#ifndef _BLT_TABLE_H
+#define _BLT_TABLE_H
+
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltList.h"
+
+typedef struct {
+    Blt_HashTable tableTable;	/* Hash table of table structures keyed by 
+				 * the address of the reference Tk window */
+} TableInterpData;
+
+
+typedef struct EditorStruct Editor;
+typedef void (EditorDrawProc) _ANSI_ARGS_((Editor *editor));
+typedef void (EditorDestroyProc) _ANSI_ARGS_((DestroyData destroyData));
+
+struct EditorStruct {
+    int gridLineWidth;
+    int buttonHeight;
+    int entryPad;
+    int minSize;		/* Minimum size to allow any partition */
+
+    EditorDrawProc *drawProc;
+    EditorDestroyProc *destroyProc;
+};
+
+#define nRows		rowInfo.chainPtr->nLinks
+#define nColumns	columnInfo.chainPtr->nLinks
+
+/*
+ * Limits --
+ *
+ * 	Defines the bounding of a size (width or height) in the table.
+ * 	It may be related to the partition, entry, or table size.  The
+ * 	widget pointers are used to associate sizes with the requested
+ * 	size of other widgets.
+ */
+
+typedef struct {
+    int flags;			/* Flags indicate whether using default
+				 * values for limits or not. See flags
+				 * below. */
+    int max, min;		/* Values for respective limits. */
+    int nom;			/* Nominal starting value. */
+    Tk_Window wMax, wMin;	/* If non-NULL, represents widgets whose
+				 * requested sizes will be set as limits. */
+    Tk_Window wNom;		/* If non-NULL represents widget whose
+				 * requested size will be the nominal
+				 * size. */
+} Limits;
+
+#define LIMITS_SET_BIT	1
+#define LIMITS_SET_MIN  (LIMITS_SET_BIT<<0)
+#define LIMITS_SET_MAX  (LIMITS_SET_BIT<<1)
+#define LIMITS_SET_NOM  (LIMITS_SET_BIT<<2)
+
+#define LIMITS_MIN	0	/* Default minimum limit  */
+#define LIMITS_MAX	SHRT_MAX/* Default maximum limit */
+#define LIMITS_NOM	-1000	/* Default nomimal value.  Indicates if a
+				 * partition has received any space yet */
+
+typedef int (LimitsProc) _ANSI_ARGS_((int value, Limits *limitsPtr));
+
+/*
+ * Resize --
+ *
+ *	These flags indicate in what ways each partition in a table
+ *	can be resized from its default dimensions.  The normal size of
+ *	a row/column is the minimum amount of space needed to hold the
+ *	widgets that span it.  The table may then be stretched or
+ *	shrunk depending if the container is larger or smaller than
+ *	the table. This can occur if 1) the user resizes the toplevel
+ *	widget, or 2) the container is in turn packed into a larger
+ *	widget and the "fill" option is set.
+ *
+ * 	  RESIZE_NONE 	  - No resizing from normal size.
+ *	  RESIZE_EXPAND   - Do not allow the size to decrease.
+ *			    The size may increase however.
+ *        RESIZE_SHRINK   - Do not allow the size to increase.
+ *			    The size may decrease however.
+ *	  RESIZE_BOTH     - Allow the size to increase or
+ *			    decrease from the normal size.
+ *	  RESIZE_VIRGIN   - Special case of the resize flag.  Used to
+ *			    indicate the initial state of the flag.
+ *			    Empty rows/columns are treated differently
+ *			    if this row/column is set.
+ */
+
+#define RESIZE_NONE	0
+#define RESIZE_EXPAND	(1<<0)
+#define RESIZE_SHRINK	(1<<1)
+#define RESIZE_BOTH	(RESIZE_EXPAND | RESIZE_SHRINK)
+#define RESIZE_VIRGIN	(1<<2)
+
+/*
+ * Control --
+ */
+#define CONTROL_NORMAL	1.0	/* Consider the widget when
+				 * calculating the row heights and
+				 * column widths.  */
+#define CONTROL_NONE	0.0	/* Ignore the widget.  The height and
+				 * width of the rows/columns spanned
+				 * by this widget will not affected by
+				 * the size of the widget.
+				 */
+#define CONTROL_FULL	-1.0	/* Consider only this widget when
+				 * determining the column widths
+				 * and row heights of the partitions
+				 * it spans. */
+#define EXCL_PAD 	0
+#define INCL_PAD	1
+
+typedef struct TableStruct Table;
+typedef struct RowColumnStruct RowColumn;
+
+/*
+ * Entry --
+ *
+ *	An entry holds a widget and describes how the widget should
+ *	appear in a range of cells.
+ *	 1. padding.
+ *	 2. how many rows/columns the entry spans.
+ *	 3. size bounds for the widget.
+ *
+ *	Several entries may start at the same cell in
+ *	the table, but a entry can hold only one widget.
+ */
+
+typedef struct  {
+    Tk_Window tkwin;		/* Widget to be managed. */
+
+    Table *tablePtr;		/* Table managing this widget */
+
+    int borderWidth;		/* The external border width of
+				 * the widget. This is needed to check if
+				 * Tk_Changes(tkwin)->border_width changes.
+				 */
+
+    int manageWhenNeeded;	/* If non-zero, allow joint custody of
+				 * the widget.  This is for cases
+				 * where the same widget may be shared
+				 * between two different tables
+				 * (e.g. same graph on two different
+				 * notebook pages).  Claim the widget
+				 * only when the table is
+				 * mapped. Don't destroy the entry if
+				 * the table loses custody of the
+				 * widget. */
+
+    Limits reqWidth, reqHeight;	/* Bounds for width and height requests
+				 * made by the widget. */
+    struct PositionInfo {
+	RowColumn *rcPtr;	/* Row or column where this entry starts. */
+
+	int span;		/* Number of rows or columns spanned. */
+	double control;		/* Weight of widget in the row or column. */
+
+	Blt_ChainLink *linkPtr;	/* Link to widget in the chain of spans */
+
+	Blt_Chain *chainPtr;	/* Pointer to the chain of spans. */
+    } row, column;
+
+    Tk_Anchor anchor;		/* Anchor type: indicates how the
+				 * widget is positioned if extra space
+				 * is available in the entry */
+
+    Blt_Pad padX;		/* Extra padding placed left and right of the
+				 * widget. */
+    Blt_Pad padY;		/* Extra padding placed above and below the
+				 * widget */
+
+    int ipadX, ipadY;		/* Extra padding added to the interior of
+				 * the widget (i.e. adds to the requested
+				 * size of the widget) */
+
+    int fill;			/* Indicates how the widget should
+				 * fill the span of cells it occupies. */
+
+    int x, y;			/* Origin of widget wrt container. */
+
+    Blt_ChainLink *linkPtr;	/* Pointer into list of entries. */
+
+    Blt_HashEntry *hashPtr;	/* Pointer into table of entries. */
+
+} Entry;
+
+/*
+ * RowColumn --
+ *
+ * 	Creates a definable space (row or column) in the table. It may
+ * 	have both requested minimum or maximum values which constrain
+ * 	the size of it.
+ */
+
+struct RowColumnStruct {
+    int index;			/* Index of row or column */
+
+    int size;			/* Current size of the partition. This size
+				 * is bounded by minSize and maxSize. */
+
+    /*
+     * nomSize and size perform similar duties.  I need to keep track
+     * of the amount of space allocated to the partition (using size).
+     * But at the same time, I need to indicate that space can be
+     * parcelled out to this partition.  If a nominal size was set for
+     * this partition, I don't want to add space.
+     */
+
+    int nomSize;		/* The nominal size (neither expanded
+				 * nor shrunk) of the partition based
+				 * upon the requested sizes of the
+				 * widgets spanning this partition. */
+
+    int minSize, maxSize;	/* Size constraints on the partition */
+
+    int offset;			/* Offset of the partition (in pixels)
+				 * from the origin of the container. */
+
+    int minSpan;		/* Minimum spanning widget in
+				 * partition. Used for bookkeeping
+				 * when growing a span of partitions
+				 * */
+
+    double weight;		/* Weight of row or column */
+
+    Entry *control;		/* Pointer to the entry that is
+				 * determining the size of this
+				 * partition.  This is used to know
+				 * when a partition is occupied. */
+
+    int resize;			/* Indicates if the partition should
+				 * shrink or expand from its nominal
+				 * size. */
+
+    Blt_Pad pad;		/* Pads the partition beyond its nominal
+				 * size */
+
+    Limits reqSize;		/* Requested bounds for the size of
+				 * the partition. The partition will
+				 * not expand or shrink beyond these
+				 * limits, regardless of how it was
+				 * specified (max widget size).  This
+				 * includes any extra padding which
+				 * may be specified. */
+
+    int maxSpan;		/* Maximum spanning widget to consider
+				 * when growing a span of partitions.
+				 * A value of zero indicates that all
+				 * spans should be considered. */
+
+    int count;
+
+    Blt_ChainLink *linkPtr;
+
+};
+
+#define DEF_TBL_RESIZE	"both"
+#define DEF_TBL_PAD	"0"
+#define DEF_TBL_MAXSPAN	"0"
+
+
+/*
+ * This is the default number of elements in the statically
+ * pre-allocated column and row arrays.  This number should reflect a
+ * useful number of row and columns, which fit most applications.
+ */
+#define DEF_ARRAY_SIZE	32
+
+typedef Entry *(EntrySearchProc) _ANSI_ARGS_((Table *tablePtr, 
+	Tk_Window tkwin));
+
+/*
+ * PartitionInfo --
+ *
+ * 	Manages the rows or columns of the table.  Contains
+ *	a chain of partitions (representing the individiual
+ *	rows or columns).
+ *
+ */
+typedef struct PartitionInfo {
+    char *type;			/* String identifying the type of
+				 * partition: "row" or "column". */
+    Blt_Chain *chainPtr;
+    Blt_List list;		/* Linked list of bins of widgets
+				 * keyed by increasing span. */
+    Tk_ConfigSpec *configSpecs;
+    int reqLength;
+    int ePad;			/* Extra padding for row/column
+				 * needed to display editor marks */
+} PartitionInfo;
+
+/*
+ * Table structure
+ */
+struct TableStruct {
+    int flags;			/* See the flags definitions below. */
+    Tk_Window tkwin;		/* The container widget into which
+				 * other widgets are arranged. */
+    Tcl_Interp *interp;		/* Interpreter associated with all
+				 * widgets */
+
+    Blt_Chain *chainPtr;	/* Chain of entries in the table. */
+
+    Blt_HashTable entryTable;	/* Table of entries.  Serves as a
+				 * directory to look up entries from
+				 * widget their names. */
+    Blt_Pad padX, padY;
+
+    int propagate;		/* If non-zero, the table will make a
+				 * geometry request on behalf of the
+				 * container widget. */
+
+    int eTablePad, eEntryPad;
+
+    PartitionInfo columnInfo;
+    PartitionInfo rowInfo;	/* Manages row and column partitions */
+
+    Dim2D container;		/* Last known dimenion of the container. */
+    Dim2D normal;		/* Normal dimensions of the table */
+    Limits reqWidth, reqHeight;	/* Constraints on the table's normal
+				 * width and height */
+    Editor *editPtr;		/* If non-NULL, indicates that the
+				 * table is currently being edited */
+    Tcl_IdleProc *arrangeProc;
+    EntrySearchProc *findEntryProc;
+    Blt_HashEntry *hashPtr;	/* Used to delete the table from its
+				 * hashtable. */
+    Blt_HashTable *tablePtr;
+};
+
+/*
+ * Table flags definitions
+ */
+#define ARRANGE_PENDING (1<<0)	/* A call to ArrangeTable is
+				 * pending. This flag allows multiple
+				 * layout changes to be requested
+				 * before the table is actually
+				 * reconfigured. */
+#define REQUEST_LAYOUT 	(1<<1)	/* Get the requested sizes of the
+				 * widgets before expanding/shrinking
+				 * the size of the container.  It's
+				 * necessary to recompute the layout
+				 * every time a partition or entry is
+				 * added, reconfigured, or deleted,
+				 * but not when the container is
+				 * resized. */
+#define NON_PARENT	(1<<2)	/* The table is managing widgets that
+				 * arern't children of the container.
+				 * This requires that they are
+				 * manually moved when the container
+				 * is moved (a definite performance
+				 * hit). */
+/*
+ * Forward declarations
+ */
+
+extern int Blt_GetTable _ANSI_ARGS_((TableInterpData *dataPtr, 
+	Tcl_Interp *interp, char *pathName, Table **tablePtrPtr));
+
+#endif /* _BLT_TABLE_H */
Index: trunk/kitgen/8.x/blt/generic/bltTabnotebook.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTabnotebook.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTabnotebook.c	(revision 175)
@@ -0,0 +1,5760 @@
+/*
+ * bltTabnotebook.c --
+ *
+ *	This module implements a tab notebook widget for the BLT toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	Tabnotebook widget created by George A. Howlett (gah@bell-labs.com)
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TABNOTEBOOK
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltTile.h"
+
+#if (TK_MAJOR_VERSION == 4)
+#define TK_REPARENTED           0x2000
+#endif
+
+#define INVALID_FAIL	0
+#define INVALID_OK	1
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define CLAMP(val,low,hi)	\
+	(((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+#define GAP			3
+#define SELECT_PADX		4
+#define SELECT_PADY		2
+#define OUTER_PAD		2
+#define LABEL_PAD		1
+#define LABEL_PADX		2
+#define LABEL_PADY		2
+#define IMAGE_PAD		1
+#define CORNER_OFFSET		3
+
+#define TAB_SCROLL_OFFSET	10
+
+#define SLANT_NONE		0
+#define SLANT_LEFT		1
+#define SLANT_RIGHT		2
+#define SLANT_BOTH		(SLANT_LEFT | SLANT_RIGHT)
+
+#define END			(-1)
+#define ODD(x)			((x) | 0x01)
+
+#define TABWIDTH(s, t)		\
+  ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+#define TABHEIGHT(s, t)		\
+  ((s)->side & SIDE_VERTICAL) ? (t)->height : (t)->width)
+
+#define VPORTWIDTH(s)		 \
+  (((s)->side & SIDE_HORIZONTAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+   (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define VPORTHEIGHT(s)		 \
+  (((s)->side & SIDE_VERTICAL) ? (Tk_Width((s)->tkwin) - 2 * (s)->inset) : \
+   (Tk_Height((s)->tkwin) - 2 * (s)->inset))
+
+#define GETATTR(t,attr)		\
+   (((t)->attr != NULL) ? (t)->attr : (t)->nbPtr->defTabStyle.attr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ *  Internal widget flags:
+ *
+ *	TNB_LAYOUT		The layout of the widget needs to be
+ *				recomputed.
+ *
+ *	TNB_REDRAW		A redraw request is pending for the widget.
+ *
+ *	TNB_SCROLL		A scroll request is pending.
+ *
+ *	TNB_FOCUS		The widget is receiving keyboard events.
+ *				Draw the focus highlight border around the
+ *				widget.
+ *
+ *	TNB_MULTIPLE_TIER	Notebook is using multiple tiers.
+ *
+ *	TNB_STATIC		Notebook does not scroll.
+ *
+ * ---------------------------------------------------------------------------
+ */
+#define TNB_LAYOUT		(1<<0)
+#define TNB_REDRAW		(1<<1)
+#define TNB_SCROLL		(1<<2)
+#define TNB_FOCUS		(1<<4)
+
+#define TNB_STATIC		(1<<8)
+#define TNB_MULTIPLE_TIER	(1<<9)
+
+#define PERFORATION_ACTIVE	(1<<10)
+
+#define SIDE_TOP		(1<<0)
+#define SIDE_RIGHT		(1<<1)
+#define SIDE_LEFT		(1<<2)
+#define SIDE_BOTTOM		(1<<3)
+
+#define SIDE_VERTICAL	(SIDE_LEFT | SIDE_RIGHT)
+#define SIDE_HORIZONTAL (SIDE_TOP | SIDE_BOTTOM)
+
+#define TAB_LABEL		(ClientData)0
+#define TAB_PERFORATION		(ClientData)1
+
+#define DEF_TNB_ACTIVE_BACKGROUND	RGB_GREY90
+#define DEF_TNB_ACTIVE_BG_MONO	STD_ACTIVE_BG_MONO
+#define DEF_TNB_ACTIVE_FOREGROUND	STD_ACTIVE_FOREGROUND
+#define DEF_TNB_ACTIVE_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_TNB_BG_MONO		STD_NORMAL_BG_MONO
+#define DEF_TNB_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_TNB_BORDERWIDTH	"1"
+#define DEF_TNB_COMMAND		(char *)NULL
+#define DEF_TNB_CURSOR		(char *)NULL
+#define DEF_TNB_DASHES		"1"
+#define DEF_TNB_FOREGROUND	STD_NORMAL_FOREGROUND
+#define DEF_TNB_FG_MONO		STD_NORMAL_FG_MONO
+#define DEF_TNB_FONT		STD_FONT
+#define DEF_TNB_GAP		"3"
+#define DEF_TNB_HEIGHT		"0"
+#define DEF_TNB_HIGHLIGHT_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_TNB_HIGHLIGHT_BG_MONO	STD_NORMAL_BG_MONO
+#define DEF_TNB_HIGHLIGHT_COLOR	RGB_BLACK
+#define DEF_TNB_HIGHLIGHT_WIDTH	"2"
+#define DEF_TNB_NORMAL_BACKGROUND STD_NORMAL_BACKGROUND
+#define DEF_TNB_NORMAL_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_TNB_OUTER_PAD	"3"
+#define DEF_TNB_RELIEF		"sunken"
+#define DEF_TNB_ROTATE		"0.0"
+#define DEF_TNB_SCROLL_INCREMENT "0"
+#define DEF_TNB_SELECT_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_TNB_SELECT_BG_MONO 	STD_SELECT_BG_MONO
+#define DEF_TNB_SELECT_BORDERWIDTH 	"1"
+#define DEF_TNB_SELECT_CMD	(char *)NULL
+#define DEF_TNB_SELECT_FOREGROUND STD_SELECT_FOREGROUND
+#define DEF_TNB_SELECT_FG_MONO  STD_SELECT_FG_MONO
+#define DEF_TNB_SELECT_MODE	"multiple"
+#define DEF_TNB_SELECT_RELIEF	"raised"
+#define DEF_TNB_SELECT_PAD	"5"
+#define DEF_TNB_SHADOW_COLOR	RGB_BLACK
+#define DEF_TNB_SIDE		"top"
+#define DEF_TNB_SLANT		"none"
+#define DEF_TNB_TAB_BACKGROUND	RGB_GREY82
+#define DEF_TNB_TAB_BG_MONO	STD_SELECT_BG_MONO
+#define DEF_TNB_TAB_RELIEF	"raised"
+#define DEF_TNB_TAKE_FOCUS	"1"
+#define DEF_TNB_TEXT_COLOR	STD_NORMAL_FOREGROUND
+#define DEF_TNB_TEXT_MONO	STD_NORMAL_FG_MONO
+#define DEF_TNB_TEXT_SIDE	"left"
+#define DEF_TNB_TIERS		"1"
+#define DEF_TNB_TILE		(char *)NULL
+#define DEF_TNB_WIDTH		"0"
+#define DEF_TNB_SAME_WIDTH	"yes"
+#define DEF_TNB_TEAROFF		"yes"
+#define DEF_TNB_PAGE_WIDTH	"0"
+#define DEF_TNB_PAGE_HEIGHT	"0"
+
+#define DEF_TAB_ACTIVE_BG	(char *)NULL
+#define DEF_TAB_ACTIVE_FG	(char *)NULL
+#define DEF_TAB_ANCHOR		"center"
+#define DEF_TAB_BG		(char *)NULL
+#define DEF_TAB_COMMAND		(char *)NULL
+#define DEF_TAB_DATA		(char *)NULL
+#define DEF_TAB_FG		(char *)NULL
+#define DEF_TAB_FILL		"none"
+#define DEF_TAB_FONT		(char *)NULL
+#define DEF_TAB_HEIGHT		"0"
+#define DEF_TAB_IMAGE		(char *)NULL
+#define DEF_TAB_IPAD		"0"
+#define DEF_TAB_PAD		"3"
+#define DEF_TAB_PERF_COMMAND	(char *)NULL
+#define DEF_TAB_SELECT_BG	(char *)NULL
+#define DEF_TAB_SELECT_BORDERWIDTH 	"1"
+#define DEF_TAB_SELECT_CMD	(char *)NULL
+#define DEF_TAB_SELECT_FG	(char *)NULL
+#define DEF_TAB_SHADOW		(char *)NULL
+#define DEF_TAB_STATE		"normal"
+#define DEF_TAB_STIPPLE		"BLT"
+#define DEF_TAB_BIND_TAGS	"all"
+#define DEF_TAB_TEXT		(char *)NULL
+#define DEF_TAB_VISUAL		(char *)NULL
+#define DEF_TAB_WIDTH		"0"
+#define DEF_TAB_WINDOW		(char *)NULL
+
+typedef struct NotebookStruct Notebook;
+
+static void EmbeddedWidgetGeometryProc _ANSI_ARGS_((ClientData, Tk_Window));
+static void EmbeddedWidgetCustodyProc _ANSI_ARGS_((ClientData, Tk_Window));
+
+static Tk_GeomMgr tabMgrInfo =
+{
+    "notebook",			/* Name of geometry manager used by winfo */
+    EmbeddedWidgetGeometryProc,	/* Procedure to for new geometry requests */
+    EmbeddedWidgetCustodyProc,	/* Procedure when window is taken away */
+};
+
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltFillOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltPositiveDistanceOption;
+extern Tk_CustomOption bltPositiveCountOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltPadOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltStateOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltUidOption;
+
+static int StringToImage _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *ImageToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToWindow _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *WindowToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSide _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *SideToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtrPtr));
+
+static int StringToSlant _ANSI_ARGS_((ClientData clientData,
+	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
+	int offset));
+static char *SlantToString _ANSI_ARGS_((ClientData clientData,
+	Tk_Window tkwin, char *widgRec, int offset,
+	Tcl_FreeProc **freeProcPtrPtr));
+
+/*
+ * Contains a pointer to the widget that's currently being configured.
+ * This is used in the custom configuration parse routine for images.
+ */
+static Notebook *lastNotebookInstance;
+
+static Tk_CustomOption imageOption =
+{
+    StringToImage, ImageToString, (ClientData)&lastNotebookInstance,
+};
+
+static Tk_CustomOption sideOption =
+{
+    StringToSide, SideToString, (ClientData)0,
+};
+
+static Tk_CustomOption windowOption =
+{
+    StringToWindow, WindowToString, (ClientData)0,
+};
+
+static Tk_CustomOption slantOption =
+{
+    StringToSlant, SlantToString, (ClientData)0,
+};
+
+/*
+ * TabImage --
+ *
+ *	When multiple instances of an image are displayed in the
+ *	same widget, this can be inefficient in terms of both memory
+ *	and time.  We only need one instance of each image, regardless
+ *	of number of times we use it.  And searching/deleting instances
+ *	can be very slow as the list gets large.
+ *
+ *	The workaround, employed below, is to maintain a hash table of
+ *	images that maintains a reference count for each image.
+ */
+
+typedef struct TabImageStruct {
+    int refCount;		/* Reference counter for this image. */
+    Tk_Image tkImage;		/* The Tk image being cached. */
+    int width, height;		/* Dimensions of the cached image. */
+    Blt_HashEntry *hashPtr;	/* Hash table pointer to the image. */
+
+} *TabImage;
+
+#define ImageHeight(image)	((image)->height)
+#define ImageWidth(image)	((image)->width)
+#define ImageBits(image)	((image)->tkImage)
+
+#define TAB_VISIBLE	(1<<0)
+#define TAB_REDRAW	(1<<2)
+
+typedef struct {
+    char *name;			/* Identifier for tab entry */
+    int state;			/* State of the tab: Disabled, active, or
+				 * normal. */
+    unsigned int flags;
+
+    int tier;			/* Index of tier [1..numTiers] containing
+				 * this tab. */
+
+    int worldX, worldY;		/* Position of the tab in world coordinates. */
+    int worldWidth, worldHeight;/* Dimensions of the tab, corrected for
+				 * orientation (-side).  It includes the
+				 * border, padding, label, etc. */
+    int screenX, screenY;
+    short int screenWidth, screenHeight;	/*  */
+
+    Notebook *nbPtr;		/* Notebook that includes this
+				 * tab. Needed for callbacks can pass
+				 * only a tab pointer.  */
+    Blt_Uid tags;
+
+    /*
+     * Tab label:
+     */
+    Blt_Uid text;		/* String displayed as the tab's label. */
+    TabImage image;		/* Image displayed as the label. */
+
+    short int textWidth, textHeight;
+    short int labelWidth, labelHeight;
+    Blt_Pad iPadX, iPadY;	/* Internal padding around the text */
+
+    Tk_Font font;
+
+    /*
+     * Normal:
+     */
+    XColor *textColor;		/* Text color */
+    Tk_3DBorder border;		/* Background color and border for tab.*/
+
+    /*
+     * Selected: Tab is currently selected.
+     */
+    XColor *selColor;		/* Selected text color */
+    Tk_3DBorder selBorder;	/* 3D border of selected folder. */
+
+    /*
+     * Active: Mouse passes over the tab.
+     */
+    Tk_3DBorder activeBorder;	/* Active background color. */
+    XColor *activeFgColor;	/* Active text color */
+
+    Shadow shadow;
+    Pixmap stipple;		/* Stipple for outline of embedded window
+				 * when torn off. */
+    /*
+     * Embedded widget information:
+     */
+    Tk_Window tkwin;		/* Widget to be mapped when the tab is
+				 * selected.  If NULL, don't make
+				 * space for the page. */
+
+    int reqWidth, reqHeight;	/* If non-zero, overrides the
+				 * requested dimensions of the
+				 * embedded widget. */
+
+    Tk_Window container;	/* The window containing the embedded
+				 * widget.  Does not necessarily have
+				 * to be the parent. */
+
+    Tk_Anchor anchor;		/* Anchor: indicates how the embedded
+				 * widget is positioned within the
+				 * extra space on the page. */
+
+    Blt_Pad padX, padY;		/* Padding around embedded widget */
+
+    int fill;			/* Indicates how the window should
+				 * fill the page. */
+
+    /*
+     * Auxillary information:
+     */
+    Blt_Uid command;		/* Command (malloc-ed) invoked when the tab
+				 * is selected */
+    Blt_Uid data;		/* This value isn't used in C code.
+				 * It may be used by clients in Tcl bindings
+				 * to associate extra data (other than the
+				 * label or name) with the tab. */
+
+    Blt_ChainLink *linkPtr;	/* Pointer to where the tab resides in the
+				 * list of tabs. */
+    Blt_Uid perfCommand;		/* Command (malloc-ed) invoked when the tab
+				 * is selected */
+    GC textGC;
+    GC backGC;
+
+    Blt_Tile tile;
+
+} Tab;
+
+static Tk_ConfigSpec tabConfigSpecs[] =
+{
+    {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+	"ActiveBackground", DEF_TAB_ACTIVE_BG, 
+	Tk_Offset(Tab, activeBorder), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+	"ActiveForeground", DEF_TAB_ACTIVE_FG, 
+	Tk_Offset(Tab, activeFgColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+	DEF_TAB_ANCHOR, Tk_Offset(Tab, anchor), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_TAB_BG, Tk_Offset(Tab, border), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_TAB_BIND_TAGS, Tk_Offset(Tab, tags),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_CUSTOM, "-command", "command", "Command",
+	DEF_TAB_COMMAND, Tk_Offset(Tab, command),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_CUSTOM, "-data", "data", "data",
+	DEF_TAB_DATA, Tk_Offset(Tab, data),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+	DEF_TAB_FILL, Tk_Offset(Tab, fill),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_TAB_FG, Tk_Offset(Tab, textColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_FONT, "-font", "font", "Font",
+	DEF_TAB_FONT, Tk_Offset(Tab, font), 0},
+    {TK_CONFIG_CUSTOM, "-image", "image", "image",
+	DEF_TAB_IMAGE, Tk_Offset(Tab, image),
+	TK_CONFIG_NULL_OK, &imageOption},
+    {TK_CONFIG_CUSTOM, "-ipadx", "iPadX", "PadX",
+	DEF_TAB_IPAD, Tk_Offset(Tab, iPadX),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-ipady", "iPadY", "PadY",
+	DEF_TAB_IPAD, Tk_Offset(Tab, iPadY),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX",
+	DEF_TAB_PAD, Tk_Offset(Tab, padX), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY",
+	DEF_TAB_PAD, Tk_Offset(Tab, padY), 0, &bltPadOption},
+    {TK_CONFIG_CUSTOM, "-perforationcommand", "perforationcommand", 
+	"PerforationCommand",
+	DEF_TAB_PERF_COMMAND, Tk_Offset(Tab, perfCommand),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+	DEF_TAB_SELECT_BG, Tk_Offset(Tab, selBorder), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+	DEF_TAB_SELECT_FG, Tk_Offset(Tab, selColor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
+	DEF_TAB_SHADOW, Tk_Offset(Tab, shadow),
+	TK_CONFIG_NULL_OK, &bltShadowOption},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State",
+	DEF_TAB_STATE, Tk_Offset(Tab, state), 
+	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
+    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+	DEF_TAB_STIPPLE, Tk_Offset(Tab, stipple), 0},
+    {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+	(char *)NULL, Tk_Offset(Tab, tile), TK_CONFIG_NULL_OK,
+	&bltTileOption},
+    {TK_CONFIG_CUSTOM, "-text", "Text", "Text",
+	DEF_TAB_TEXT, Tk_Offset(Tab, text),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_CUSTOM, "-window", "window", "Window",
+	DEF_TAB_WINDOW, Tk_Offset(Tab, tkwin),
+	TK_CONFIG_NULL_OK, &windowOption},
+    {TK_CONFIG_CUSTOM, "-windowheight", "windowHeight", "WindowHeight",
+	DEF_TAB_HEIGHT, Tk_Offset(Tab, reqHeight),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-windowwidth", "windowWidth", "WindowWidth",
+	DEF_TAB_WIDTH, Tk_Offset(Tab, reqWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+/*
+ * TabAttributes --
+ */
+typedef struct {
+    Tk_Window tkwin;		/* Default window to map pages. */
+
+    int reqWidth, reqHeight;	/* Requested tab size. */
+    int constWidth;
+    int borderWidth;		/* Width of 3D border around the tab's
+				 * label. */
+    int pad;			/* Extra padding of a tab entry */
+
+    XColor *activeFgColor;	/* Active foreground. */
+    Tk_3DBorder activeBorder;	/* Active background. */
+    XColor *selColor;		/* Selected foreground. */
+    Tk_Font font;
+    XColor *textColor;
+
+    Tk_3DBorder border;		/* Normal background. */
+    Tk_3DBorder selBorder;	/* Selected background. */
+
+    Blt_Dashes dashes;
+    GC normalGC, activeGC;
+    int relief;
+    char *command;
+    char *perfCommand;		/* Command (malloc-ed) invoked when the tab
+				 * is selected */
+    double rotate;
+    int textSide;
+
+} TabAttributes;
+
+struct NotebookStruct {
+    Tk_Window tkwin;		/* Window that embodies the widget.
+                                 * NULL means that the window has been
+                                 * destroyed but the data structures
+                                 * haven't yet been cleaned up.*/
+
+    Display *display;		/* Display containing widget; needed,
+                                 * among other things, to release
+                                 * resources after tkwin has already
+                                 * gone away. */
+
+    Tcl_Interp *interp;		/* Interpreter associated with widget. */
+
+    Tcl_Command cmdToken;	/* Token for widget's command. */
+
+    unsigned int flags;		/* For bitfield definitions, see below */
+
+    int inset;			/* Total width of all borders, including
+				 * traversal highlight and 3-D border.
+				 * Indicates how much interior stuff must
+				 * be offset from outside edges to leave
+				 * room for borders. */
+
+    int inset2;			/* Total width of 3-D folder border + corner,
+				 * Indicates how much interior stuff must
+				 * be offset from outside edges of folder.*/
+
+    int yPad;			/* Extra offset for selected tab. Only
+				 * for single tiers. */
+
+    int pageTop;		/* Offset from top of notebook to the
+				 * start of the page. */
+
+    Tk_Cursor cursor;		/* X Cursor */
+
+    Tk_3DBorder border;		/* 3D border surrounding the window. */
+    int borderWidth;		/* Width of 3D border. */
+    int relief;			/* 3D border relief. */
+
+    XColor *shadowColor;	/* Shadow color around folder. */
+    /*
+     * Focus highlight ring
+     */
+    int highlightWidth;		/* Width in pixels of highlight to draw
+				 * around widget when it has the focus.
+				 * <= 0 means don't draw a highlight. */
+    XColor *highlightBgColor;	/* Color for drawing traversal highlight
+				 * area when highlight is off. */
+    XColor *highlightColor;	/* Color for drawing traversal highlight. */
+
+    GC highlightGC;		/* GC for focus highlight. */
+
+    char *takeFocus;		/* Says whether to select this widget during
+				 * tab traveral operations.  This value isn't
+				 * used in C code, but for the widget's Tcl
+				 * bindings. */
+
+
+    int side;			/* Orientation of the notebook: either
+				 * SIDE_LEFT, SIDE_RIGHT, SIDE_TOP, or
+				 * SIDE_BOTTOM. */
+
+    int slant;
+    int overlap;
+    int gap;
+    int tabWidth, tabHeight;
+    int xSelectPad, ySelectPad;	/* Padding around label of the selected tab. */
+    int outerPad;		/* Padding around the exterior of the notebook
+				 * and folder. */
+
+    TabAttributes defTabStyle;	/* Global attribute information specific to
+				 * tabs. */
+    Blt_Tile tile;
+
+    int reqWidth, reqHeight;	/* Requested dimensions of the notebook
+				 * window. */
+    int pageWidth, pageHeight;	/* Dimensions of a page in the folder. */
+    int reqPageWidth, reqPageHeight;	/* Requested dimensions of a page. */
+
+    int lastX, lastY;
+    /*
+     * Scrolling information:
+     */
+    int worldWidth;
+    int scrollOffset;		/* Offset of viewport in world coordinates. */
+    char *scrollCmdPrefix;	/* Command strings to control scrollbar.*/
+
+    int scrollUnits;		/* Smallest unit of scrolling for tabs. */
+
+    /*
+     * Scanning information:
+     */
+    int scanAnchor;		/* Scan anchor in screen coordinates. */
+    int scanOffset;		/* Offset of the start of the scan in world
+				 * coordinates.*/
+
+
+    int corner;			/* Number of pixels to offset next point
+				 * when drawing corners of the folder. */
+    int reqTiers;		/* Requested number of tiers. Zero means to
+				 * dynamically scroll if there are too many
+				 * tabs to be display on a single tier. */
+    int nTiers;			/* Actual number of tiers. */
+
+    Blt_HashTable imageTable;
+
+
+    Tab *selectPtr;		/* The currently selected tab.
+				 * (i.e. its page is displayed). */
+
+    Tab *activePtr;		/* Tab last located under the pointer.
+				 * It is displayed with its active
+				 * foreground/background colors.  */
+
+    Tab *focusPtr;		/* Tab currently receiving focus. */
+
+    Tab *startPtr;		/* The first tab on the first tier. */
+
+    Blt_Chain *chainPtr;	/* List of tab entries. Used to
+				 * arrange placement of tabs. */
+
+    Blt_HashTable tabTable;	/* Hash table of tab entries. Used for
+				 * lookups of tabs by name. */
+    int nextId;
+
+    int nVisible;		/* Number of tabs that are currently visible
+				 * in the view port. */
+
+    Blt_BindTable bindTable;	/* Tab binding information */
+    Blt_HashTable tagTable;	/* Table of bind tags. */
+
+    int tearoff;
+};
+
+static Tk_ConfigSpec configSpecs[] =
+{
+    {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+	"activeBackground",
+	DEF_TNB_ACTIVE_BACKGROUND, Tk_Offset(Notebook, defTabStyle.activeBorder),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+	"activeBackground",
+	DEF_TNB_ACTIVE_BG_MONO, Tk_Offset(Notebook, defTabStyle.activeBorder),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+	"activeForeground", DEF_TNB_ACTIVE_FOREGROUND, 
+	Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-activeforeground", "activeForeground",
+	"activeForeground", DEF_TNB_ACTIVE_FG_MONO, 
+	Tk_Offset(Notebook, defTabStyle.activeFgColor), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_TNB_BG_MONO, Tk_Offset(Notebook, border), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_TNB_BACKGROUND, Tk_Offset(Notebook, border), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_TNB_CURSOR, Tk_Offset(Notebook, cursor), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, borderWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+	DEF_TNB_DASHES, Tk_Offset(Notebook, defTabStyle.dashes),
+	TK_CONFIG_NULL_OK, &bltDashesOption},
+    {TK_CONFIG_SYNONYM, "-fg", "tabForeground", (char *)NULL, 
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_FONT, "-font", "font", "Font",
+	DEF_TNB_FONT, Tk_Offset(Notebook, defTabStyle.font), 0},
+    {TK_CONFIG_SYNONYM, "-foreground", "tabForeground", (char *)NULL, 
+	(char *)NULL, 0, 0},
+    {TK_CONFIG_PIXELS, "-gap", "gap", "Gap",
+	DEF_TNB_GAP, Tk_Offset(Notebook, gap),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-height", "height", "Height",
+	DEF_TNB_HEIGHT, Tk_Offset(Notebook, reqHeight),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+	"HighlightBackground",
+	DEF_TNB_HIGHLIGHT_BACKGROUND, Tk_Offset(Notebook, highlightBgColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+	"HighlightBackground",
+	DEF_TNB_HIGHLIGHT_BG_MONO, Tk_Offset(Notebook, highlightBgColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+	DEF_TNB_HIGHLIGHT_COLOR, Tk_Offset(Notebook, highlightColor), 0},
+    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+	"HighlightThickness",
+	DEF_TNB_HIGHLIGHT_WIDTH, Tk_Offset(Notebook, highlightWidth),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-outerpad", "outerPad", "OuterPad",
+	DEF_TNB_OUTER_PAD, Tk_Offset(Notebook, outerPad),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-pageheight", "pageHeight", "PageHeight",
+	DEF_TNB_PAGE_HEIGHT, Tk_Offset(Notebook, reqPageHeight),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_CUSTOM, "-pagewidth", "pageWidth", "PageWidth",
+	DEF_TNB_PAGE_WIDTH, Tk_Offset(Notebook, reqPageWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_STRING, "-perforationcommand", "perforationcommand", 
+	"PerforationCommand",
+	DEF_TAB_PERF_COMMAND, Tk_Offset(Notebook, defTabStyle.perfCommand),
+	TK_CONFIG_NULL_OK, &bltUidOption},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_TNB_RELIEF, Tk_Offset(Notebook, relief), 0},
+    {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
+	DEF_TNB_ROTATE, Tk_Offset(Notebook, defTabStyle.rotate),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_BOOLEAN, "-samewidth", "sameWidth", "SameWidth",
+	DEF_TNB_SAME_WIDTH, Tk_Offset(Notebook, defTabStyle.constWidth),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
+	(char *)NULL, Tk_Offset(Notebook, scrollCmdPrefix), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_CUSTOM, "-scrollincrement", "scrollIncrement",
+	"ScrollIncrement",
+	DEF_TNB_SCROLL_INCREMENT, Tk_Offset(Notebook, scrollUnits),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption},
+    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+	DEF_TNB_SELECT_BG_MONO, Tk_Offset(Notebook, defTabStyle.selBorder),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+	DEF_TNB_SELECT_BACKGROUND, Tk_Offset(Notebook, defTabStyle.selBorder),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+	DEF_TNB_SELECT_CMD, Tk_Offset(Notebook, defTabStyle.command),
+	TK_CONFIG_NULL_OK},
+    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+	DEF_TNB_SELECT_FG_MONO, Tk_Offset(Notebook, defTabStyle.selColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+	DEF_TNB_SELECT_FOREGROUND, Tk_Offset(Notebook, defTabStyle.selColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_CUSTOM, "-selectpad", "selectPad", "SelectPad",
+	DEF_TNB_SELECT_PAD, Tk_Offset(Notebook, xSelectPad),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-shadowcolor", "shadowColor", "ShadowColor",
+	DEF_TNB_SHADOW_COLOR, Tk_Offset(Notebook, shadowColor), 0},
+    {TK_CONFIG_CUSTOM, "-side", "side", "side",
+	DEF_TNB_SIDE, Tk_Offset(Notebook, side),
+	TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+    {TK_CONFIG_CUSTOM, "-slant", "slant", "Slant",
+	DEF_TNB_SLANT, Tk_Offset(Notebook, slant),
+	TK_CONFIG_DONT_SET_DEFAULT, &slantOption},
+    {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+	DEF_TNB_TAB_BG_MONO, Tk_Offset(Notebook, defTabStyle.border),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_BORDER, "-tabbackground", "tabBackground", "Background",
+	DEF_TNB_TAB_BACKGROUND, Tk_Offset(Notebook, defTabStyle.border),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_CUSTOM, "-tabborderwidth", "tabBorderWidth", "BorderWidth",
+	DEF_TNB_BORDERWIDTH, Tk_Offset(Notebook, defTabStyle.borderWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+	DEF_TNB_TEXT_COLOR, Tk_Offset(Notebook, defTabStyle.textColor),
+	TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_COLOR, "-tabforeground", "tabForeground", "Foreground",
+	DEF_TNB_TEXT_MONO, Tk_Offset(Notebook, defTabStyle.textColor),
+	TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_RELIEF, "-tabrelief", "tabRelief", "TabRelief",
+	DEF_TNB_TAB_RELIEF, Tk_Offset(Notebook, defTabStyle.relief), 0},
+    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+	DEF_TNB_TAKE_FOCUS, Tk_Offset(Notebook, takeFocus), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-tearoff", "tearoff", "Tearoff",
+	DEF_TNB_TEAROFF, Tk_Offset(Notebook, tearoff),
+	TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_CUSTOM, "-textside", "textSide", "TextSide",
+	DEF_TNB_TEXT_SIDE, Tk_Offset(Notebook, defTabStyle.textSide),
+	TK_CONFIG_DONT_SET_DEFAULT, &sideOption},
+    {TK_CONFIG_CUSTOM, "-tiers", "tiers", "Tiers",
+	DEF_TNB_TIERS, Tk_Offset(Notebook, reqTiers),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
+    {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
+	(char *)NULL, Tk_Offset(Notebook, tile), TK_CONFIG_NULL_OK,
+	&bltTileOption},
+    {TK_CONFIG_CUSTOM, "-width", "width", "Width",
+	DEF_TNB_WIDTH, Tk_Offset(Notebook, reqWidth),
+	TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static void DestroyNotebook _ANSI_ARGS_((DestroyData dataPtr));
+static void DestroyTearoff _ANSI_ARGS_((DestroyData dataPtr));
+static void EmbeddedWidgetEventProc _ANSI_ARGS_((ClientData clientdata,
+	XEvent *eventPtr));
+static void TearoffEventProc _ANSI_ARGS_((ClientData clientdata,
+	XEvent *eventPtr));
+static void NotebookEventProc _ANSI_ARGS_((ClientData clientdata,
+	XEvent *eventPtr));
+static void DrawLabel _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
+	Drawable drawable));
+static void DrawFolder _ANSI_ARGS_((Notebook *nbPtr, Tab *tabPtr,
+	Drawable drawable));
+static void DisplayNotebook _ANSI_ARGS_((ClientData clientData));
+static void DisplayTearoff _ANSI_ARGS_((ClientData clientData));
+static void NotebookInstDeletedCmd _ANSI_ARGS_((ClientData clientdata));
+static int NotebookInstCmd _ANSI_ARGS_((ClientData clientdata,
+	Tcl_Interp *interp, int argc, char **argv));
+static void GetWindowRectangle _ANSI_ARGS_((Tab *tabPtr, Tk_Window parent,
+	int tearOff, XRectangle *rectPtr));
+static void ArrangeWindow _ANSI_ARGS_((Tk_Window tkwin, XRectangle *rectPtr,
+	int force));
+static void EventuallyRedraw _ANSI_ARGS_((Notebook *nbPtr));
+static void EventuallyRedrawTearoff _ANSI_ARGS_((Tab *tabPtr));
+static void ComputeLayout _ANSI_ARGS_((Notebook *nbPtr));
+static void DrawOuterBorders _ANSI_ARGS_((Notebook *nbPtr, 
+	Drawable drawable));
+
+static Tk_ImageChangedProc ImageChangedProc;
+static Blt_TileChangedProc TileChangedProc;
+static Blt_BindTagProc GetTags;
+static Blt_BindPickProc PickTab;
+static Tcl_IdleProc AdoptWindow;
+static Tcl_CmdProc NotebookCmd;
+
+static ClientData
+MakeTag(nbPtr, tagName)
+    Notebook *nbPtr;
+    char *tagName;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    hPtr = Blt_CreateHashEntry(&(nbPtr->tagTable), tagName, &isNew);
+    assert(hPtr);
+    return Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WorldToScreen --
+ *
+ *	Converts world coordinates to screen coordinates. Note that
+ *	the world view is always tabs up.
+ *
+ * Results:
+ *	The screen coordinates are returned via *xScreenPtr and *yScreenPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+WorldToScreen(nbPtr, x, y, xScreenPtr, yScreenPtr)
+    Notebook *nbPtr;
+    int x, y;
+    int *xScreenPtr, *yScreenPtr;
+{
+    int sx, sy;
+
+    sx = sy = 0;		/* Suppress compiler warning. */
+
+    /* Translate world X-Y to screen coordinates */
+    /*
+     * Note that the world X-coordinate is translated by the selected label's
+     * X padding. This is done only to keep the scroll range is between
+     * 0.0 and 1.0, rather adding/subtracting the pad in various locations.
+     * It may be changed back in the future.
+     */
+    x += (nbPtr->inset + 
+	  nbPtr->xSelectPad - 
+	  nbPtr->scrollOffset);
+    y += nbPtr->inset + nbPtr->yPad;
+
+    switch (nbPtr->side) {
+    case SIDE_TOP:
+	sx = x, sy = y;		/* Do nothing */
+	break;
+    case SIDE_RIGHT:
+	sx = Tk_Width(nbPtr->tkwin) - y;
+	sy = x;
+	break;
+    case SIDE_LEFT:
+	sx = y, sy = x;		/* Flip coordinates */
+	break;
+    case SIDE_BOTTOM:
+	sx = x;
+	sy = Tk_Height(nbPtr->tkwin) - y;
+	break;
+    }
+    *xScreenPtr = sx;
+    *yScreenPtr = sy;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ *	Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Information gets redisplayed.  Right now we don't do selective
+ *	redisplays:  the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(nbPtr)
+    Notebook *nbPtr;
+{
+    if ((nbPtr->tkwin != NULL) && !(nbPtr->flags & TNB_REDRAW)) {
+	nbPtr->flags |= TNB_REDRAW;
+	Tcl_DoWhenIdle(DisplayNotebook, nbPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawTearoff --
+ *
+ *	Queues a request to redraw the tearoff at the next idle point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Information gets redisplayed.  Right now we don't do selective
+ *	redisplays:  the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedrawTearoff(tabPtr)
+    Tab *tabPtr;
+{
+    if ((tabPtr->tkwin != NULL) && !(tabPtr->flags & TAB_REDRAW)) {
+	tabPtr->flags |= TAB_REDRAW;
+	Tcl_DoWhenIdle(DisplayTearoff, tabPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *	This routine is called whenever an image displayed in a tab
+ *	changes.  In this case, we assume that everything will change
+ *	and queue a request to re-layout and redraw the entire notebook.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+    ClientData clientData;
+    int x, y, width, height;	/* Not used. */
+    int imageWidth, imageHeight;/* Not used. */
+{
+    Notebook *nbPtr = clientData;
+
+    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    EventuallyRedraw(nbPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetImage --
+ *
+ *	This is a wrapper procedure for Tk_GetImage. The problem is
+ *	that if the same image is used repeatedly in the same widget,
+ *	the separate instances are saved in a linked list.  This makes
+ *	it especially slow to destroy the widget.  As a workaround,
+ *	this routine hashes the image and maintains a reference count
+ *	for it.
+ *
+ * Results:
+ *	Returns a pointer to the new image.
+ *
+ *----------------------------------------------------------------------
+ */
+static TabImage
+GetImage(nbPtr, interp, tkwin, name)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    char *name;
+{
+    struct TabImageStruct *imagePtr;
+    int isNew;
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_CreateHashEntry(&(nbPtr->imageTable), name, &isNew);
+    if (isNew) {
+	Tk_Image tkImage;
+	int width, height;
+
+	tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, nbPtr);
+	if (tkImage == NULL) {
+	    Blt_DeleteHashEntry(&(nbPtr->imageTable), hPtr);
+	    return NULL;
+	}
+	Tk_SizeOfImage(tkImage, &width, &height);
+	imagePtr = Blt_Malloc(sizeof(struct TabImageStruct));
+	imagePtr->tkImage = tkImage;
+	imagePtr->hashPtr = hPtr;
+	imagePtr->refCount = 1;
+	imagePtr->width = width;
+	imagePtr->height = height;
+	Blt_SetHashValue(hPtr, imagePtr);
+    } else {
+	imagePtr = (struct TabImageStruct *)Blt_GetHashValue(hPtr);
+	imagePtr->refCount++;
+    }
+    return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeImage --
+ *
+ *	Releases the image if it's not being used anymore by this
+ *	widget.  Note there may be several uses of the same image
+ *	by many tabs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The reference count is decremented and the image is freed
+ *	is it's not being used anymore.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeImage(nbPtr, imagePtr)
+    Notebook *nbPtr;
+    struct TabImageStruct *imagePtr;
+{
+    imagePtr->refCount--;
+    if (imagePtr->refCount == 0) {
+	Blt_DeleteHashEntry(&(nbPtr->imageTable), imagePtr->hashPtr);
+	Tk_FreeImage(imagePtr->tkImage);
+	Blt_Free(imagePtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToImage --
+ *
+ *	Converts an image name into a Tk image token.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToImage(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Contains a pointer to the notebook containing
+				 * this image. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Window associated with the notebook. */
+    char *string;		/* String representation */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset to field in structure */
+{
+    Notebook *nbPtr = *(Notebook **)clientData;
+    TabImage *imagePtr = (TabImage *) (widgRec + offset);
+    TabImage image;
+
+    image = NULL;
+    if ((string != NULL) && (*string != '\0')) {
+	image = GetImage(nbPtr, interp, tkwin, string);
+	if (image == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    if (*imagePtr != NULL) {
+	FreeImage(nbPtr, *imagePtr);
+    }
+    *imagePtr = image;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageToString --
+ *
+ *	Converts the Tk image back to its string representation (i.e.
+ *	its name).
+ *
+ * Results:
+ *	The name of the image is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ImageToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Pointer to notebook containing image. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Notebook *nbPtr = *(Notebook **)clientData;
+    TabImage *imagePtr = (TabImage *) (widgRec + offset);
+
+    if (*imagePtr == NULL) {
+	return "";
+    }
+    return Blt_GetHashKey(&(nbPtr->imageTable), (*imagePtr)->hashPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToWindow --
+ *
+ *	Converts a window name into Tk window.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToWindow(clientData, interp, parent, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window parent;		/* Parent window */
+    char *string;		/* String representation. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset to field in structure */
+{
+    Tab *tabPtr = (Tab *)widgRec;
+    Tk_Window *tkwinPtr = (Tk_Window *)(widgRec + offset);
+    Tk_Window old, tkwin;
+    Notebook *nbPtr;
+
+    old = *tkwinPtr;
+    tkwin = NULL;
+    nbPtr = tabPtr->nbPtr;
+    if ((string != NULL) && (*string != '\0')) {
+	tkwin = Tk_NameToWindow(interp, string, parent);
+	if (tkwin == NULL) {
+	    return TCL_ERROR;
+	}
+	if (tkwin == old) {
+	    return TCL_OK;
+	}
+	/*
+	 * Allow only widgets that are children of the notebook to be
+	 * embedded into the page.  This way we can make assumptions about
+	 * the window based upon its parent; either it's the notebook window
+	 * or it has been torn off.
+	 */
+	parent = Tk_Parent(tkwin);
+	if (parent != nbPtr->tkwin) {
+	    Tcl_AppendResult(interp, "can't manage \"", Tk_PathName(tkwin),
+		"\" in notebook \"", Tk_PathName(nbPtr->tkwin), "\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	Tk_ManageGeometry(tkwin, &tabMgrInfo, tabPtr);
+	Tk_CreateEventHandler(tkwin, StructureNotifyMask, 
+		EmbeddedWidgetEventProc, tabPtr);
+
+	/*
+	 * We need to make the window to exist immediately.  If the
+	 * window is torn off (placed into another container window),
+	 * the timing between the container and the its new child
+	 * (this window) gets tricky.  This should work for Tk 4.2.
+	 */
+	Tk_MakeWindowExist(tkwin);
+    }
+    if (old != NULL) {
+	if (tabPtr->container != NULL) {
+	    Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+	}
+	Tk_DeleteEventHandler(old, StructureNotifyMask, 
+	      EmbeddedWidgetEventProc, tabPtr);
+	Tk_ManageGeometry(old, (Tk_GeomMgr *) NULL, tabPtr);
+	Tk_UnmapWindow(old);
+    }
+    *tkwinPtr = tkwin;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowToString --
+ *
+ *	Converts the Tk window back to its string representation (i.e.
+ *	its name).
+ *
+ * Results:
+ *	The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+WindowToString(clientData, parent, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window parent;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    Tk_Window tkwin = *(Tk_Window *)(widgRec + offset);
+
+    if (tkwin == NULL) {
+	return "";
+    }
+    return Tk_PathName(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSide --
+ *
+ *	Converts "left", "right", "top", "bottom", into a numeric token
+ *	designating the side of the notebook which to display tabs.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED */
+static int
+StringToSide(clientData, interp, parent, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window parent;		/* Parent window */
+    char *string;		/* Option value string */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset to field in structure */
+{
+    int *sidePtr = (int *)(widgRec + offset);
+    char c;
+    unsigned int length;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+	*sidePtr = SIDE_LEFT;
+    } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+	*sidePtr = SIDE_RIGHT;
+    } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+	*sidePtr = SIDE_TOP;
+    } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+	*sidePtr = SIDE_BOTTOM;
+    } else {
+	Tcl_AppendResult(interp, "bad side \"", string,
+	    "\": should be left, right, top, or bottom", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SideToString --
+ *
+ *	Converts the window into its string representation (its name).
+ *
+ * Results:
+ *	The name of the window is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SideToString(clientData, parent, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window parent;		/* Not used. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* offset of windows array in record */
+    Tcl_FreeProc **freeProcPtr;	/* Memory deallocation scheme to use */
+{
+    int side = *(int *)(widgRec + offset);
+
+    switch (side) {
+    case SIDE_LEFT:
+	return "left";
+    case SIDE_RIGHT:
+	return "right";
+    case SIDE_BOTTOM:
+	return "bottom";
+    case SIDE_TOP:
+	return "top";
+    }
+    return "unknown side value";
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSlant --
+ *
+ *	Converts the slant style string into its numeric representation.
+ *
+ *	Valid style strings are:
+ *
+ *	  "none"   Both sides are straight.
+ * 	  "left"   Left side is slanted.
+ *	  "right"  Right side is slanted.
+ *	  "both"   Both sides are slanted.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSlant(clientData, interp, tkwin, string, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    char *string;		/* String representation of attribute. */
+    char *widgRec;		/* Widget record */
+    int offset;			/* Offset of field in widget record. */
+{
+    int *slantPtr = (int *)(widgRec + offset);
+    unsigned int length;
+    char c;
+
+    c = string[0];
+    length = strlen(string);
+    if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+	*slantPtr = SLANT_NONE;
+    } else if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+	*slantPtr = SLANT_LEFT;
+    } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+	*slantPtr = SLANT_RIGHT;
+    } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+	*slantPtr = SLANT_BOTH;
+    } else {
+	Tcl_AppendResult(interp, "bad argument \"", string,
+	    "\": should be \"none\", \"left\", \"right\", or \"both\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SlantToString --
+ *
+ *	Returns the slant style string based upon the slant flags.
+ *
+ * Results:
+ *	The slant style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SlantToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;	/* Not used. */
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;		/* Widget structure record. */
+    int offset;			/* Offset of field in widget record. */
+    Tcl_FreeProc **freeProcPtr;	/* Not used. */
+{
+    int slant = *(int *)(widgRec + offset);
+
+    switch (slant) {
+    case SLANT_LEFT:
+	return "left";
+    case SLANT_RIGHT:
+	return "right";
+    case SLANT_NONE:
+	return "none";
+    case SLANT_BOTH:
+	return "both";
+    default:
+	return "unknown value";
+    }
+}
+
+
+
+static int
+WorldY(tabPtr)
+    Tab *tabPtr;
+{
+    int tier;
+
+    tier = tabPtr->nbPtr->nTiers - tabPtr->tier;
+    return tier * tabPtr->nbPtr->tabHeight;
+}
+
+static int
+TabIndex(nbPtr, tabPtr) 
+    Notebook *nbPtr;
+    Tab *tabPtr;
+{
+    Tab *t2Ptr;
+    int count;
+    Blt_ChainLink *linkPtr;
+    
+    count = 0;
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	t2Ptr = Blt_ChainGetValue(linkPtr);
+	if (t2Ptr == tabPtr) {
+	    return count;
+	}
+	count++;
+    }
+    return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * RenumberTiers --
+ *
+ *	In multi-tier mode, we need to find the start of the tier
+ *	containing the newly selected tab.
+ *
+ *	Tiers are draw from the last tier to the first, so that
+ *	the the lower-tiered tabs will partially cover the bottoms
+ *	of tab directly above it.  This simplifies the drawing of
+ *	tabs because we don't worry how tabs are clipped by their
+ *	neighbors.
+ *
+ *	In addition, tabs are re-marked with the correct tier number.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Renumbering the tab's tier will change the vertical placement
+ *	of the tab (i.e. shift tiers).
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+RenumberTiers(nbPtr, tabPtr)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+{
+    int tier;
+    Tab *prevPtr;
+    Blt_ChainLink *linkPtr, *lastPtr;
+
+    nbPtr->focusPtr = nbPtr->selectPtr = tabPtr;
+    Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
+
+    tier = tabPtr->tier;
+    for (linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr); linkPtr != NULL;
+	linkPtr = lastPtr) {
+	lastPtr = Blt_ChainPrevLink(linkPtr);
+	prevPtr = Blt_ChainGetValue(linkPtr);
+	if ((prevPtr == NULL) || (prevPtr->tier != tier)) {
+	    break;
+	}
+	tabPtr = prevPtr;
+    }
+    nbPtr->startPtr = tabPtr;
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	tabPtr->tier = (tabPtr->tier - tier + 1);
+	if (tabPtr->tier < 1) {
+	    tabPtr->tier += nbPtr->nTiers;
+	}
+	tabPtr->worldY = WorldY(tabPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PickTab --
+ *
+ *	Searches the tab located within the given screen X-Y coordinates
+ *	in the viewport.  Note that tabs overlap slightly, so that its
+ *	important to search from the innermost tier out.
+ *
+ * Results:
+ *	Returns the pointer to the tab.  If the pointer isn't contained
+ *	by any tab, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static ClientData
+PickTab(clientData, x, y, contextPtr)
+    ClientData clientData;
+    int x, y;			/* Screen coordinates to test. */
+    ClientData *contextPtr;	
+{
+    Notebook *nbPtr = clientData;
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (contextPtr != NULL) {
+	*contextPtr = NULL;
+    }
+    tabPtr = nbPtr->selectPtr;
+    if ((nbPtr->tearoff) && (tabPtr != NULL) && 
+	(tabPtr->container == NULL) && (tabPtr->tkwin != NULL)) {
+	int top, bottom, left, right;
+	int sx, sy;
+
+	/* Check first for perforation on the selected tab. */
+	WorldToScreen(nbPtr, tabPtr->worldX + 2, 
+	      tabPtr->worldY + tabPtr->worldHeight + 4, &sx, &sy);
+	if (nbPtr->side & SIDE_HORIZONTAL) {
+	    left = sx - 2;
+	    right = left + tabPtr->screenWidth;
+	    top = sy - 4;
+	    bottom = sy + 4;
+	} else {
+	    left = sx - 4;
+	    right = sx + 4;
+	    top = sy - 2;
+	    bottom = top + tabPtr->screenHeight;
+	}
+	if ((x >= left) && (y >= top) && (x < right) && (y < bottom)) {
+	    if (contextPtr != NULL) {
+		*contextPtr = TAB_PERFORATION;
+	    }
+	    return nbPtr->selectPtr;
+	}
+    } 
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	if (!(tabPtr->flags & TAB_VISIBLE)) {
+	    continue;
+	}
+	if ((x >= tabPtr->screenX) && (y >= tabPtr->screenY) &&
+	    (x <= (tabPtr->screenX + tabPtr->screenWidth)) &&
+	    (y < (tabPtr->screenY + tabPtr->screenHeight))) {
+	    if (contextPtr != NULL) {
+		*contextPtr = TAB_LABEL;
+	    }
+	    return tabPtr;
+	}
+    }
+    return NULL;
+}
+
+static Tab *
+TabLeft(tabPtr)
+    Tab *tabPtr;
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+    if (linkPtr != NULL) {
+	Tab *newPtr;
+
+	newPtr = Blt_ChainGetValue(linkPtr);
+	/* Move only if the next tab is on another tier. */
+	if (newPtr->tier == tabPtr->tier) {
+	    tabPtr = newPtr;
+	}
+    }
+    return tabPtr;
+}
+
+static Tab *
+TabRight(tabPtr)
+    Tab *tabPtr;
+{
+    Blt_ChainLink *linkPtr;
+
+    linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+    if (linkPtr != NULL) {
+	Tab *newPtr;
+
+	newPtr = Blt_ChainGetValue(linkPtr);
+	/* Move only if the next tab is on another tier. */
+	if (newPtr->tier == tabPtr->tier) {
+	    tabPtr = newPtr;
+	}
+    }
+    return tabPtr;
+}
+
+static Tab *
+TabUp(tabPtr)
+    Tab *tabPtr;
+{
+    if (tabPtr != NULL) {
+	Notebook *nbPtr;
+	int x, y;
+	int worldX, worldY;
+	
+	nbPtr = tabPtr->nbPtr;
+	worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+	worldY = tabPtr->worldY - (nbPtr->tabHeight / 2);
+	WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+	
+	tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	if (tabPtr == NULL) {
+	    /*
+	     * We might have inadvertly picked the gap between two tabs,
+	     * so if the first pick fails, try again a little to the left.
+	     */
+	    WorldToScreen(nbPtr, worldX + nbPtr->gap, worldY, &x, &y);
+	    tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	}
+	if ((tabPtr == NULL) &&
+	    (nbPtr->focusPtr->tier < (nbPtr->nTiers - 1))) {
+	    worldY -= nbPtr->tabHeight;
+	    WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+	    tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	}
+	if (tabPtr == NULL) {
+	    tabPtr = nbPtr->focusPtr;
+	}
+    }
+    return tabPtr;
+}
+
+static Tab *
+TabDown(tabPtr)
+    Tab *tabPtr;
+{
+    if (tabPtr != NULL) {
+	Notebook *nbPtr;
+	int x, y;
+	int worldX, worldY;
+
+	nbPtr = tabPtr->nbPtr;
+	worldX = tabPtr->worldX + (tabPtr->worldWidth / 2);
+	worldY = tabPtr->worldY + (3 * nbPtr->tabHeight) / 2;
+	WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+	tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	if (tabPtr == NULL) {
+	    /*
+	     * We might have inadvertly picked the gap between two tabs,
+	     * so if the first pick fails, try again a little to the left.
+	     */
+	    WorldToScreen(nbPtr, worldX - nbPtr->gap, worldY, &x, &y);
+	    tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	}
+	if ((tabPtr == NULL) && (nbPtr->focusPtr->tier > 2)) {
+	    worldY += nbPtr->tabHeight;
+	    WorldToScreen(nbPtr, worldX, worldY, &x, &y);
+	    tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	}
+	if (tabPtr == NULL) {
+	    tabPtr = nbPtr->focusPtr;
+	}
+    }
+    return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTab --
+ *
+ *	Converts a string representing a tab index into a tab pointer.
+ *	The index may be in one of the following forms:
+ *
+ *	 number		Tab at position in the list of tabs.
+ *	 @x,y		Tab closest to the specified X-Y screen coordinates.
+ *	 "active"	Tab mouse is located over.
+ *	 "focus"	Tab is the widget's focus.
+ *	 "select"	Currently selected tab.
+ *	 "right"	Next tab from the focus tab.
+ *	 "left"		Previous tab from the focus tab.
+ *	 "up"		Next tab from the focus tab.
+ *	 "down"		Previous tab from the focus tab.
+ *	 "end"		Last tab in list.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	The pointer to the node is returned via tabPtrPtr.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetTab(nbPtr, string, tabPtrPtr, allowNull)
+    Notebook *nbPtr;
+    char *string;
+    Tab **tabPtrPtr;
+    int allowNull;	/* Allow NULL tabPtr */
+{
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+    int position;
+    char c;
+
+    c = string[0];
+    tabPtr = NULL;
+    if (nbPtr->focusPtr == NULL) {
+	nbPtr->focusPtr = nbPtr->selectPtr;
+	Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
+    }
+    if ((isdigit(UCHAR(c))) &&
+	(Tcl_GetInt(nbPtr->interp, string, &position) == TCL_OK)) {
+	linkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
+	if (linkPtr == NULL) {
+	    Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
+		"\" in \"", Tk_PathName(nbPtr->tkwin), 
+		"\": no such index", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	tabPtr = Blt_ChainGetValue(linkPtr);
+    } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+	tabPtr = nbPtr->activePtr;
+    } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+	tabPtr = (Tab *)Blt_GetCurrentItem(nbPtr->bindTable);
+    } else if ((c == 's') && (strcmp(string, "select") == 0)) {
+	tabPtr = nbPtr->selectPtr;
+    } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+	tabPtr = nbPtr->focusPtr;
+    } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+	switch (nbPtr->side) {
+	case SIDE_LEFT:
+	case SIDE_RIGHT:
+	    tabPtr = TabLeft(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_BOTTOM:
+	    tabPtr = TabDown(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_TOP:
+	    tabPtr = TabUp(nbPtr->focusPtr);
+	    break;
+	}
+    } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+	switch (nbPtr->side) {
+	case SIDE_LEFT:
+	case SIDE_RIGHT:
+	    tabPtr = TabRight(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_BOTTOM:
+	    tabPtr = TabUp(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_TOP:
+	    tabPtr = TabDown(nbPtr->focusPtr);
+	    break;
+	}
+    } else if ((c == 'l') && (strcmp(string, "left") == 0)) {
+	switch (nbPtr->side) {
+	case SIDE_LEFT:
+	    tabPtr = TabUp(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_RIGHT:
+	    tabPtr = TabDown(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_BOTTOM:
+	case SIDE_TOP:
+	    tabPtr = TabLeft(nbPtr->focusPtr);
+	    break;
+	}
+    } else if ((c == 'r') && (strcmp(string, "right") == 0)) {
+	switch (nbPtr->side) {
+	case SIDE_LEFT:
+	    tabPtr = TabDown(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_RIGHT:
+	    tabPtr = TabUp(nbPtr->focusPtr);
+	    break;
+	    
+	case SIDE_BOTTOM:
+	case SIDE_TOP:
+	    tabPtr = TabRight(nbPtr->focusPtr);
+	    break;
+	}
+    } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+	linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
+	if (linkPtr != NULL) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	}
+    } else if (c == '@') {
+	int x, y;
+
+	if (Blt_GetXY(nbPtr->interp, nbPtr->tkwin, string, &x, &y) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+    } else {
+	Blt_HashEntry *hPtr;
+
+	hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), string);
+	if (hPtr != NULL) {
+	    tabPtr = (Tab *)Blt_GetHashValue(hPtr);
+	}
+    }
+    *tabPtrPtr = tabPtr;
+    Tcl_ResetResult(nbPtr->interp);
+
+    if ((!allowNull) && (tabPtr == NULL)) {
+	Tcl_AppendResult(nbPtr->interp, "can't find tab \"", string,
+	    "\" in \"", Tk_PathName(nbPtr->tkwin), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }	
+    return TCL_OK;
+}
+
+static Tab *
+NextOrLastTab(tabPtr)
+    Tab *tabPtr;
+{
+    if (tabPtr->linkPtr != NULL) {
+	Blt_ChainLink *linkPtr;
+
+	linkPtr = Blt_ChainNextLink(tabPtr->linkPtr);
+	if (linkPtr == NULL) {
+	    linkPtr = Blt_ChainPrevLink(tabPtr->linkPtr);
+	}
+	if (linkPtr != NULL) {
+	    return Blt_ChainGetValue(linkPtr);
+	}
+    }
+    return NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * EmbeddedWidgetEventProc --
+ *
+ * 	This procedure is invoked by the Tk dispatcher for various
+ * 	events on embedded widgets contained in the notebook.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When an embedded widget gets deleted, internal structures get
+ *	cleaned up.  When it gets resized, the notebook is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+EmbeddedWidgetEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Information about the tab window. */
+    XEvent *eventPtr;		/* Information about event. */
+{
+    Tab *tabPtr = clientData;
+
+    if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+	return;
+    }
+    switch (eventPtr->type) {
+    case ConfigureNotify:
+	/*
+	 * If the window's requested size changes, redraw the window.
+	 * But only if it's currently the selected page.
+	 */
+	if ((tabPtr->container == NULL) && (Tk_IsMapped(tabPtr->tkwin)) &&
+	    (tabPtr->nbPtr->selectPtr == tabPtr)) {
+	    EventuallyRedraw(tabPtr->nbPtr);
+	}
+	break;
+
+    case DestroyNotify:
+	/*
+	 * Mark the tab as deleted by dereferencing the Tk window
+	 * pointer. Redraw the window only if the tab is currently
+	 * visible.
+	 */
+	if ((Tk_IsMapped(tabPtr->tkwin)) &&
+	    (tabPtr->nbPtr->selectPtr == tabPtr)) {
+	    EventuallyRedraw(tabPtr->nbPtr);
+	}
+	Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+	    EmbeddedWidgetEventProc, tabPtr);
+	tabPtr->tkwin = NULL;
+	break;
+
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * EmbeddedWidgetCustodyProc --
+ *
+ *	This procedure is invoked when a tab window has been
+ *	stolen by another geometry manager.  The information and
+ *	memory associated with the tab window is released.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Arranges for the widget formerly associated with the tab
+ *	window to have its layout re-computed and arranged at the
+ *	next idle point.
+ *
+ * ---------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetCustodyProc(clientData, tkwin)
+    ClientData clientData;	/* Information about the former tab window. */
+    Tk_Window tkwin;		/* Not used. */
+{
+    Tab *tabPtr = clientData;
+    Notebook *nbPtr;
+
+    if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+	return;
+    }
+    nbPtr = tabPtr->nbPtr;
+    if (tabPtr->container != NULL) {
+	Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+    }
+    /*
+     * Mark the tab as deleted by dereferencing the Tk window
+     * pointer. Redraw the window only if the tab is currently
+     * visible.
+     */
+    if (tabPtr->tkwin != NULL) {
+	if (Tk_IsMapped(tabPtr->tkwin) && (nbPtr->selectPtr == tabPtr)) {
+	    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+	    EventuallyRedraw(nbPtr);
+	}
+	Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask,
+	    EmbeddedWidgetEventProc, tabPtr);
+	tabPtr->tkwin = NULL;
+    }
+}
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ * EmbeddedWidgetGeometryProc --
+ *
+ *	This procedure is invoked by Tk_GeometryRequest for tab
+ *	windows managed by the widget.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Arranges for tkwin, and all its managed siblings, to be
+ *	repacked and drawn at the next idle point.
+ *
+ * ------------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+EmbeddedWidgetGeometryProc(clientData, tkwin)
+    ClientData clientData;	/* Information about window that got new
+			         * preferred geometry.  */
+    Tk_Window tkwin;		/* Other Tk-related information about the
+			         * window. */
+{
+    Tab *tabPtr = clientData;
+
+    if ((tabPtr == NULL) || (tabPtr->tkwin == NULL)) {
+	fprintf(stderr, "%s: line %d \"tkwin is null\"", __FILE__, __LINE__);
+	return;
+    }
+    tabPtr->nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    EventuallyRedraw(tabPtr->nbPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTab --
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTab(nbPtr, tabPtr)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+{
+    Blt_HashEntry *hPtr;
+
+    if (tabPtr->flags & TAB_REDRAW) {
+	Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+    }
+    if (tabPtr->container != NULL) {
+	Tk_DestroyWindow(tabPtr->container);
+    }
+    if (tabPtr->tkwin != NULL) {
+	Tk_ManageGeometry(tabPtr->tkwin, (Tk_GeomMgr *)NULL, tabPtr);
+	Tk_DeleteEventHandler(tabPtr->tkwin, StructureNotifyMask, 
+		EmbeddedWidgetEventProc, tabPtr);
+	if (Tk_IsMapped(tabPtr->tkwin)) {
+	    Tk_UnmapWindow(tabPtr->tkwin);
+	}
+    }
+    if (tabPtr == nbPtr->activePtr) {
+	nbPtr->activePtr = NULL;
+    }
+    if (tabPtr == nbPtr->selectPtr) {
+	nbPtr->selectPtr = NextOrLastTab(tabPtr);
+    }
+    if (tabPtr == nbPtr->focusPtr) {
+	nbPtr->focusPtr = nbPtr->selectPtr;
+	Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
+    }
+    if (tabPtr == nbPtr->startPtr) {
+	nbPtr->startPtr = NULL;
+    }
+    Tk_FreeOptions(tabConfigSpecs, (char *)tabPtr, nbPtr->display, 0);
+    if (tabPtr->text != NULL) {
+	Blt_FreeUid(tabPtr->text);
+    }
+    hPtr = Blt_FindHashEntry(&(nbPtr->tabTable), tabPtr->name);
+    assert(hPtr);
+    Blt_DeleteHashEntry(&(nbPtr->tabTable), hPtr);
+
+    if (tabPtr->image != NULL) {
+	FreeImage(nbPtr, tabPtr->image);
+    }
+    if (tabPtr->name != NULL) {
+	Blt_Free(tabPtr->name);
+    }
+    if (tabPtr->textGC != NULL) {
+	Tk_FreeGC(nbPtr->display, tabPtr->textGC);
+    }
+    if (tabPtr->backGC != NULL) {
+	Tk_FreeGC(nbPtr->display, tabPtr->backGC);
+    }
+    if (tabPtr->command != NULL) {
+	Blt_FreeUid(tabPtr->command);
+    }
+    if (tabPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(nbPtr->chainPtr, tabPtr->linkPtr);
+    }
+    if (tabPtr->tags != NULL) {
+	Blt_FreeUid(tabPtr->tags);
+    }
+    Blt_DeleteBindings(nbPtr->bindTable, tabPtr);
+    Blt_Free(tabPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTab --
+ *
+ *	Creates a new tab structure.  A tab contains information about
+ *	the state of the tab and its embedded window.
+ *
+ * Results:
+ *	Returns a pointer to the new tab structure.
+ *
+ * ----------------------------------------------------------------------
+ */
+static Tab *
+CreateTab(nbPtr)
+    Notebook *nbPtr;
+{
+    Tab *tabPtr;
+    Blt_HashEntry *hPtr;
+    int isNew;
+    char string[200];
+
+    tabPtr = Blt_Calloc(1, sizeof(Tab));
+    assert(tabPtr);
+    tabPtr->nbPtr = nbPtr;
+    sprintf(string, "tab%d", nbPtr->nextId++);
+    tabPtr->name = Blt_Strdup(string);
+    tabPtr->text = Blt_GetUid(string);
+    tabPtr->fill = FILL_NONE;
+    tabPtr->anchor = TK_ANCHOR_CENTER;
+    tabPtr->container = NULL;
+    tabPtr->state = STATE_NORMAL;
+    hPtr = Blt_CreateHashEntry(&(nbPtr->tabTable), string, &isNew);
+    Blt_SetHashValue(hPtr, tabPtr);
+    return tabPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ *	Stub for image change notifications.  Since we immediately draw
+ *	the image into a pixmap, we don't really care about image changes.
+ *
+ *	It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+    ClientData clientData;
+    Blt_Tile tile;		/* Not used. */
+{
+    Notebook *nbPtr = clientData;
+
+    if (nbPtr->tkwin != NULL) {
+	EventuallyRedraw(nbPtr);
+    }
+}
+
+static int
+ConfigureTab(nbPtr, tabPtr)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+{
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+    int labelWidth, labelHeight;
+    Tk_Font font;
+    Tk_3DBorder border;
+
+    font = GETATTR(tabPtr, font);
+    labelWidth = labelHeight = 0;
+    if (tabPtr->text != NULL) {
+	TextStyle ts;
+	double rotWidth, rotHeight;
+
+	Blt_InitTextStyle(&ts);
+	ts.font = font;
+	ts.shadow.offset = tabPtr->shadow.offset;
+	ts.padX.side1 = ts.padX.side2 = 2;
+	Blt_GetTextExtents(&ts, tabPtr->text, &labelWidth, &labelHeight);
+	Blt_GetBoundingBox(labelWidth, labelHeight, nbPtr->defTabStyle.rotate,
+	    &rotWidth, &rotHeight, (Point2D *)NULL);
+	labelWidth = ROUND(rotWidth);
+	labelHeight = ROUND(rotHeight);
+    }
+    tabPtr->textWidth = (short int)labelWidth;
+    tabPtr->textHeight = (short int)labelHeight;
+    if (tabPtr->image != NULL) {
+	int width, height;
+
+	width = ImageWidth(tabPtr->image) + 2 * IMAGE_PAD;
+	height = ImageHeight(tabPtr->image) + 2 * IMAGE_PAD;
+	if (nbPtr->defTabStyle.textSide & SIDE_VERTICAL) {
+	    labelWidth += width;
+	    labelHeight = MAX(labelHeight, height);
+	} else {
+	    labelHeight += height;
+	    labelWidth = MAX(labelWidth, width);
+	}
+    }
+    labelWidth += PADDING(tabPtr->iPadX);
+    labelHeight += PADDING(tabPtr->iPadY);
+
+    tabPtr->labelWidth = ODD(labelWidth);
+    tabPtr->labelHeight = ODD(labelHeight);
+
+    newGC = NULL;
+    if (tabPtr->text != NULL) {
+	XColor *colorPtr;
+
+	gcMask = GCForeground | GCFont;
+	colorPtr = GETATTR(tabPtr, textColor);
+	gcValues.foreground = colorPtr->pixel;
+	gcValues.font = Tk_FontId(font);
+	newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+    }
+    if (tabPtr->textGC != NULL) {
+	Tk_FreeGC(nbPtr->display, tabPtr->textGC);
+    }
+    tabPtr->textGC = newGC;
+
+    gcMask = GCForeground | GCStipple | GCFillStyle;
+    gcValues.fill_style = FillStippled;
+    border = GETATTR(tabPtr, border);
+    gcValues.foreground = Tk_3DBorderColor(border)->pixel;
+    gcValues.stipple = tabPtr->stipple;
+    newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+    if (tabPtr->backGC != NULL) {
+	Tk_FreeGC(nbPtr->display, tabPtr->backGC);
+    }
+    tabPtr->backGC = newGC;
+    /*
+     * GC for tiled background.
+     */
+    if (tabPtr->tile != NULL) {
+	Blt_SetTileChangedProc(tabPtr->tile, TileChangedProc, nbPtr);
+    }
+    if (tabPtr->flags & TAB_VISIBLE) {
+	EventuallyRedraw(nbPtr);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TearoffEventProc --
+ *
+ * 	This procedure is invoked by the Tk dispatcher for various
+ * 	events on the tearoff widget.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the tearoff gets deleted, internal structures get
+ *	cleaned up.  When it gets resized or exposed, it's redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TearoffEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Information about the tab window. */
+    XEvent *eventPtr;		/* Information about event. */
+{
+    Tab *tabPtr = clientData;
+
+    if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+	(tabPtr->container == NULL)) {
+	return;
+    }
+    switch (eventPtr->type) {
+    case Expose:
+	if (eventPtr->xexpose.count == 0) {
+	    EventuallyRedrawTearoff(tabPtr);
+	}
+	break;
+
+    case ConfigureNotify:
+	EventuallyRedrawTearoff(tabPtr);
+	break;
+
+    case DestroyNotify:
+	if (tabPtr->flags & TAB_REDRAW) {
+	    tabPtr->flags &= ~TAB_REDRAW;
+	    Tcl_CancelIdleCall(DisplayTearoff, clientData);
+	}
+	Tk_DestroyWindow(tabPtr->container);
+	tabPtr->container = NULL;
+	break;
+
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqWidth --
+ *
+ *	Returns the width requested by the embedded tab window and
+ *	any requested padding around it. This represents the requested
+ *	width of the page.
+ *
+ * Results:
+ *	Returns the requested width of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqWidth(tabPtr)
+    Tab *tabPtr;
+{
+    int width;
+
+    if (tabPtr->reqWidth > 0) {
+	width = tabPtr->reqWidth;
+    } else {
+	width = Tk_ReqWidth(tabPtr->tkwin);
+    }
+    width += PADDING(tabPtr->padX) +
+	2 * Tk_Changes(tabPtr->tkwin)->border_width;
+    if (width < 1) {
+	width = 1;
+    }
+    return width;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * GetReqHeight --
+ *
+ *	Returns the height requested by the window and padding around
+ *	the window. This represents the requested height of the page.
+ *
+ * Results:
+ *	Returns the requested height of the page.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static int
+GetReqHeight(tabPtr)
+    Tab *tabPtr;
+{
+    int height;
+
+    if (tabPtr->reqHeight > 0) {
+	height = tabPtr->reqHeight;
+    } else {
+	height = Tk_ReqHeight(tabPtr->tkwin);
+    }
+    height += PADDING(tabPtr->padY) +
+	2 * Tk_Changes(tabPtr->tkwin)->border_width;
+    if (height < 1) {
+	height = 1;
+    }
+    return height;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * TranslateAnchor --
+ *
+ * 	Translate the coordinates of a given bounding box based upon the
+ * 	anchor specified.  The anchor indicates where the given xy position
+ * 	is in relation to the bounding box.
+ *
+ *  		nw --- n --- ne
+ *  		|            |     x,y ---+
+ *  		w   center   e      |     |
+ *  		|            |      +-----+
+ *  		sw --- s --- se
+ *
+ * Results:
+ *	The translated coordinates of the bounding box are returned.
+ *
+ * ----------------------------------------------------------------------------
+ */
+static void
+TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
+    int dx, dy;			/* Difference between outer and inner regions
+				 */
+    Tk_Anchor anchor;		/* Direction of the anchor */
+    int *xPtr, *yPtr;
+{
+    int x, y;
+
+    x = y = 0;
+    switch (anchor) {
+    case TK_ANCHOR_NW:		/* Upper left corner */
+	break;
+    case TK_ANCHOR_W:		/* Left center */
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_SW:		/* Lower left corner */
+	y = dy;
+	break;
+    case TK_ANCHOR_N:		/* Top center */
+	x = (dx / 2);
+	break;
+    case TK_ANCHOR_CENTER:	/* Centered */
+	x = (dx / 2);
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_S:		/* Bottom center */
+	x = (dx / 2);
+	y = dy;
+	break;
+    case TK_ANCHOR_NE:		/* Upper right corner */
+	x = dx;
+	break;
+    case TK_ANCHOR_E:		/* Right center */
+	x = dx;
+	y = (dy / 2);
+	break;
+    case TK_ANCHOR_SE:		/* Lower right corner */
+	x = dx;
+	y = dy;
+	break;
+    }
+    *xPtr = (*xPtr) + x;
+    *yPtr = (*yPtr) + y;
+}
+
+
+static void
+GetWindowRectangle(tabPtr, parent, tearoff, rectPtr)
+    Tab *tabPtr;
+    Tk_Window parent;
+    int tearoff;
+    XRectangle *rectPtr;
+{
+    int pad;
+    Notebook *nbPtr;
+    int cavityWidth, cavityHeight;
+    int width, height;
+    int dx, dy;
+    int x, y;
+
+    nbPtr = tabPtr->nbPtr;
+    pad = nbPtr->inset + nbPtr->inset2;
+
+    if (!tearoff) {
+	switch (nbPtr->side) {
+	case SIDE_RIGHT:
+	case SIDE_BOTTOM:
+	    x = nbPtr->inset + nbPtr->inset2;
+	    y = nbPtr->inset + nbPtr->inset2;
+	    break;
+
+	case SIDE_LEFT:
+	    x = nbPtr->pageTop;
+	    y = nbPtr->inset + nbPtr->inset2;
+	    break;
+
+	case SIDE_TOP:
+	    x = nbPtr->inset + nbPtr->inset2;
+	    y = nbPtr->pageTop;
+	    break;
+	}
+
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    cavityWidth = Tk_Width(nbPtr->tkwin) - (nbPtr->pageTop + pad);
+	    cavityHeight = Tk_Height(nbPtr->tkwin) - (2 * pad);
+	} else {
+	    cavityWidth = Tk_Width(nbPtr->tkwin) - (2 * pad);
+	    cavityHeight = Tk_Height(nbPtr->tkwin) - (nbPtr->pageTop + pad);
+	}
+
+    } else {
+	x = nbPtr->inset + nbPtr->inset2;
+#define TEAR_OFF_TAB_SIZE	5
+	y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad + nbPtr->outerPad +
+	    TEAR_OFF_TAB_SIZE;
+	cavityWidth = Tk_Width(parent) - (2 * pad);
+	cavityHeight = Tk_Height(parent) - (y + pad);
+    }
+    cavityWidth -= PADDING(tabPtr->padX);
+    cavityHeight -= PADDING(tabPtr->padY);
+    if (cavityWidth < 1) {
+	cavityWidth = 1;
+    }
+    if (cavityHeight < 1) {
+	cavityHeight = 1;
+    }
+    width = GetReqWidth(tabPtr);
+    height = GetReqHeight(tabPtr);
+
+    /*
+     * Resize the embedded window is of the following is true:
+     *
+     *	1) It's been torn off.
+     *  2) The -fill option (horizontal or vertical) is set.
+     *  3) the window is bigger than the cavity.
+     */
+    if ((tearoff) || (cavityWidth < width) || (tabPtr->fill & FILL_X)) {
+	width = cavityWidth;
+    }
+    if ((tearoff) || (cavityHeight < height) || (tabPtr->fill & FILL_Y)) {
+	height = cavityHeight;
+    }
+    dx = (cavityWidth - width);
+    dy = (cavityHeight - height);
+    if ((dx > 0) || (dy > 0)) {
+	TranslateAnchor(dx, dy, tabPtr->anchor, &x, &y);
+    }
+    /* Remember that X11 windows must be at least 1 pixel. */
+    if (width < 1) {
+	width = 1;
+    }
+    if (height < 1) {
+	height = 1;
+    }
+    rectPtr->x = (short)(x + tabPtr->padLeft);
+    rectPtr->y = (short)(y + tabPtr->padTop);
+    rectPtr->width = (short)width;
+    rectPtr->height = (short)height;
+}
+
+static void
+ArrangeWindow(tkwin, rectPtr, force)
+    Tk_Window tkwin;
+    XRectangle *rectPtr;
+    int force;
+{
+    if ((force) ||
+	(rectPtr->x != Tk_X(tkwin)) || 
+	(rectPtr->y != Tk_Y(tkwin)) ||
+	(rectPtr->width != Tk_Width(tkwin)) ||
+	(rectPtr->height != Tk_Height(tkwin))) {
+	Tk_MoveResizeWindow(tkwin, rectPtr->x, rectPtr->y, 
+			    rectPtr->width, rectPtr->height);
+    }
+    if (!Tk_IsMapped(tkwin)) {
+	Tk_MapWindow(tkwin);
+    }
+}
+
+
+/*ARGSUSED*/
+static void
+GetTags(table, object, context, list)
+    Blt_BindTable table;
+    ClientData object;
+    ClientData context;
+    Blt_List list;
+{
+    Tab *tabPtr = (Tab *)object;
+    Notebook *nbPtr;
+
+    nbPtr = (Notebook *)table->clientData;
+    if (context == TAB_PERFORATION) {
+	Blt_ListAppend(list, MakeTag(nbPtr, "Perforation"), 0);
+    } else if (context == TAB_LABEL) {
+	Blt_ListAppend(list, MakeTag(nbPtr, tabPtr->name), 0);
+	if (tabPtr->tags != NULL) {
+	    int nNames;
+	    char **names;
+	    register char **p;
+	    
+	    /* 
+	     * This is a space/time trade-off in favor of space.  The tags
+	     * are stored as character strings in a hash table.  That way,
+	     * tabs can share the strings. It's likely that they will.  The
+	     * down side is that the same string is split over and over again. 
+	     */
+	    if (Tcl_SplitList((Tcl_Interp *)NULL, tabPtr->tags, &nNames, 
+		&names) == TCL_OK) {
+		for (p = names; *p != NULL; p++) {
+		    Blt_ListAppend(list, MakeTag(nbPtr, *p), 0);
+		}
+		Blt_Free(names);
+	    }
+	}
+    }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NotebookEventProc --
+ *
+ * 	This procedure is invoked by the Tk dispatcher for various
+ * 	events on notebook widgets.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	When the window gets deleted, internal structures get
+ *	cleaned up.  When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+NotebookEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Information about window. */
+    XEvent *eventPtr;		/* Information about event. */
+{
+    Notebook *nbPtr = clientData;
+
+    switch (eventPtr->type) {
+    case Expose:
+	if (eventPtr->xexpose.count == 0) {
+	    EventuallyRedraw(nbPtr);
+	}
+	break;
+
+    case ConfigureNotify:
+	nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+	EventuallyRedraw(nbPtr);
+	break;
+
+    case FocusIn:
+    case FocusOut:
+	if (eventPtr->xfocus.detail != NotifyInferior) {
+	    if (eventPtr->type == FocusIn) {
+		nbPtr->flags |= TNB_FOCUS;
+	    } else {
+		nbPtr->flags &= ~TNB_FOCUS;
+	    }
+	    EventuallyRedraw(nbPtr);
+	}
+	break;
+
+    case DestroyNotify:
+	if (nbPtr->tkwin != NULL) {
+	    nbPtr->tkwin = NULL;
+	    Tcl_DeleteCommandFromToken(nbPtr->interp, nbPtr->cmdToken);
+	}
+	if (nbPtr->flags & TNB_REDRAW) {
+	    Tcl_CancelIdleCall(DisplayNotebook, nbPtr);
+	}
+	Tcl_EventuallyFree(nbPtr, DestroyNotebook);
+	break;
+
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyNotebook --
+ *
+ * 	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * 	to clean up the internal structure of the widget at a safe
+ * 	time (when no-one is using it anymore).
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyNotebook(dataPtr)
+    DestroyData dataPtr;	/* Pointer to the widget record. */
+{
+    Notebook *nbPtr = (Notebook *)dataPtr;
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (nbPtr->highlightGC != NULL) {
+	Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
+    }
+    if (nbPtr->tile != NULL) {
+	Blt_FreeTile(nbPtr->tile);
+    }
+    if (nbPtr->defTabStyle.activeGC != NULL) {
+	Blt_FreePrivateGC(nbPtr->display, nbPtr->defTabStyle.activeGC);
+    }
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	tabPtr->linkPtr = NULL;
+	DestroyTab(nbPtr, tabPtr);
+    }
+    Blt_ChainDestroy(nbPtr->chainPtr);
+    Blt_DestroyBindingTable(nbPtr->bindTable);
+    Blt_DeleteHashTable(&(nbPtr->tabTable));
+    Blt_DeleteHashTable(&(nbPtr->tagTable));
+    Tk_FreeOptions(configSpecs, (char *)nbPtr, nbPtr->display, 0);
+    Blt_Free(nbPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateNotebook --
+ *
+ * ----------------------------------------------------------------------
+ */
+static Notebook *
+CreateNotebook(interp, tkwin)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+{
+    Notebook *nbPtr;
+
+    nbPtr = Blt_Calloc(1, sizeof(Notebook));
+    assert(nbPtr);
+
+    Tk_SetClass(tkwin, "Tabnotebook");
+    nbPtr->tkwin = tkwin;
+    nbPtr->display = Tk_Display(tkwin);
+    nbPtr->interp = interp;
+
+    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    nbPtr->side = SIDE_TOP;
+    nbPtr->borderWidth = nbPtr->highlightWidth = 2;
+    nbPtr->ySelectPad = SELECT_PADY;
+    nbPtr->xSelectPad = SELECT_PADX;
+    nbPtr->relief = TK_RELIEF_SUNKEN;
+    nbPtr->defTabStyle.relief = TK_RELIEF_RAISED;
+    nbPtr->defTabStyle.borderWidth = 1;
+    nbPtr->defTabStyle.constWidth = TRUE;
+    nbPtr->defTabStyle.textSide = SIDE_LEFT;
+    nbPtr->scrollUnits = 2;
+    nbPtr->corner = CORNER_OFFSET;
+    nbPtr->gap = GAP;
+    nbPtr->outerPad = OUTER_PAD;
+    nbPtr->slant = SLANT_NONE;
+    nbPtr->overlap = 0;
+    nbPtr->tearoff = TRUE;
+    nbPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, nbPtr, PickTab, 
+	GetTags);
+    nbPtr->chainPtr = Blt_ChainCreate();
+    Blt_InitHashTable(&(nbPtr->tabTable), BLT_STRING_KEYS);
+    Blt_InitHashTable(&(nbPtr->imageTable), BLT_STRING_KEYS);
+    Blt_InitHashTable(&(nbPtr->tagTable), BLT_STRING_KEYS);
+#if (TK_MAJOR_VERSION > 4)
+    Blt_SetWindowInstanceData(tkwin, nbPtr);
+#endif
+    return nbPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ConfigureNotebook --
+ *
+ * 	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	the widget.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ * 	returned, then interp->result contains an error message.
+ *
+ * Side Effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for nbPtr; old resources get freed, if there
+ *	were any.  The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ConfigureNotebook(interp, nbPtr, argc, argv, flags)
+    Tcl_Interp *interp;		/* Interpreter to report errors. */
+    Notebook *nbPtr;		/* Information about widget; may or
+			         * may not already have values for
+			         * some fields. */
+    int argc;
+    char **argv;
+    int flags;
+{
+    XGCValues gcValues;
+    unsigned long gcMask;
+    GC newGC;
+
+    lastNotebookInstance = nbPtr;
+    if (Tk_ConfigureWidget(interp, nbPtr->tkwin, configSpecs, argc, argv,
+	    (char *)nbPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Blt_ConfigModified(configSpecs, "-width", "-height", "-side", "-gap",
+	    "-slant", (char *)NULL)) {
+	nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    }
+    if ((nbPtr->reqHeight > 0) && (nbPtr->reqWidth > 0)) {
+	Tk_GeometryRequest(nbPtr->tkwin, nbPtr->reqWidth, 
+		nbPtr->reqHeight);
+    }
+    /*
+     * GC for focus highlight.
+     */
+    gcMask = GCForeground;
+    gcValues.foreground = nbPtr->highlightColor->pixel;
+    newGC = Tk_GetGC(nbPtr->tkwin, gcMask, &gcValues);
+    if (nbPtr->highlightGC != NULL) {
+	Tk_FreeGC(nbPtr->display, nbPtr->highlightGC);
+    }
+    nbPtr->highlightGC = newGC;
+
+    /*
+     * GC for tiled background.
+     */
+    if (nbPtr->tile != NULL) {
+	Blt_SetTileChangedProc(nbPtr->tile, TileChangedProc, nbPtr);
+    }
+    /*
+     * GC for active line.
+     */
+    gcMask = GCForeground | GCLineWidth | GCLineStyle | GCCapStyle;
+    gcValues.foreground = nbPtr->defTabStyle.activeFgColor->pixel;
+    gcValues.line_width = 0;
+    gcValues.cap_style = CapProjecting;
+    gcValues.line_style = (LineIsDashed(nbPtr->defTabStyle.dashes))
+	? LineOnOffDash : LineSolid;
+
+    newGC = Blt_GetPrivateGC(nbPtr->tkwin, gcMask, &gcValues);
+    if (LineIsDashed(nbPtr->defTabStyle.dashes)) {
+	nbPtr->defTabStyle.dashes.offset = 2;
+	Blt_SetDashes(nbPtr->display, newGC, &(nbPtr->defTabStyle.dashes));
+    }
+    if (nbPtr->defTabStyle.activeGC != NULL) {
+	Blt_FreePrivateGC(nbPtr->display, 
+			  nbPtr->defTabStyle.activeGC);
+    }
+    nbPtr->defTabStyle.activeGC = newGC;
+
+    nbPtr->defTabStyle.rotate = FMOD(nbPtr->defTabStyle.rotate, 360.0);
+    if (nbPtr->defTabStyle.rotate < 0.0) {
+	nbPtr->defTabStyle.rotate += 360.0;
+    }
+    nbPtr->inset = nbPtr->highlightWidth + nbPtr->borderWidth + nbPtr->outerPad;
+    if (Blt_ConfigModified(configSpecs, "-font", "-*foreground", "-rotate",
+	    "-*background", "-side", (char *)NULL)) {
+	Blt_ChainLink *linkPtr;
+	Tab *tabPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    ConfigureTab(nbPtr, tabPtr);
+	}
+	nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    }
+    nbPtr->inset2 = nbPtr->defTabStyle.borderWidth + nbPtr->corner;
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Notebook operations
+ *
+ * --------------------------------------------------------------
+ */
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ *	Selects the tab to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ActivateOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (argv[2][0] == '\0') {
+	tabPtr = NULL;
+    } else if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tabPtr != NULL) && (tabPtr->state == STATE_DISABLED)) {
+	tabPtr = NULL;
+    }
+    if (tabPtr != nbPtr->activePtr) {
+	nbPtr->activePtr = tabPtr;
+	EventuallyRedraw(nbPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *	  .t bind index sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    if (argc == 2) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	char *tagName;
+
+	for (hPtr = Blt_FirstHashEntry(&(nbPtr->tagTable), &cursor);
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tagName = Blt_GetHashKey(&(nbPtr->tagTable), hPtr);
+	    Tcl_AppendElement(interp, tagName);
+	}
+	return TCL_OK;
+    }
+    return Blt_ConfigureBindings(interp, nbPtr->bindTable,
+	MakeTag(nbPtr, argv[2]), argc - 3, argv + 3);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    lastNotebookInstance = nbPtr;
+    return Tk_ConfigureValue(interp, nbPtr->tkwin, configSpecs,
+	(char *)nbPtr, argv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * 	This procedure is called to process an argv/argc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	the widget.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side Effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for nbPtr; old resources get freed, if there
+ *	were any.  The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+
+    lastNotebookInstance = nbPtr;
+    if (argc == 2) {
+	return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
+	    (char *)nbPtr, (char *)NULL, 0);
+    } else if (argc == 3) {
+	return Tk_ConfigureInfo(interp, nbPtr->tkwin, configSpecs,
+	    (char *)nbPtr, argv[2], 0);
+    }
+    if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2,
+	    TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes tab from the set. Deletes either a range of
+ *	tabs or a single node.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *firstPtr, *lastPtr;
+
+    lastPtr = NULL;
+    if (GetTab(nbPtr, argv[2], &firstPtr, INVALID_FAIL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((argc == 4) && 
+	(GetTab(nbPtr, argv[3], &lastPtr, INVALID_FAIL) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (lastPtr == NULL) {
+	DestroyTab(nbPtr, firstPtr);
+    } else {
+	Tab *tabPtr;
+	Blt_ChainLink *linkPtr, *nextLinkPtr;
+
+	tabPtr = NULL;		/* Suppress compiler warning. */
+
+	/* Make sure that the first tab is before the last. */
+	for (linkPtr = firstPtr->linkPtr; linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    if (tabPtr == lastPtr) {
+		break;
+	    }
+	}
+	if (tabPtr != lastPtr) {
+	    return TCL_OK;
+	}
+	linkPtr = firstPtr->linkPtr;
+	while (linkPtr != NULL) {
+	    nextLinkPtr = Blt_ChainNextLink(linkPtr);
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    DestroyTab(nbPtr, tabPtr);
+	    linkPtr = nextLinkPtr;
+	    if (tabPtr == lastPtr) {
+		break;
+	    }
+	}
+    }
+    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FocusOp --
+ *
+ *	Selects the tab to get focus.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FocusOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_FAIL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tabPtr != NULL) {
+	nbPtr->focusPtr = tabPtr;
+	Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
+	EventuallyRedraw(nbPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *	Converts a string representing a tab index.
+ *
+ * Results:
+ *	A standard Tcl result.  Interp->result will contain the
+ *	identifier of each index found. If an index could not be found,
+ *	then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tabPtr != NULL) {
+	Tcl_SetResult(interp, Blt_Itoa(TabIndex(nbPtr, tabPtr)), 
+		TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IdOp --
+ *
+ *	Converts a tab index into the tab identifier.
+ *
+ * Results:
+ *	A standard Tcl result.  Interp->result will contain the
+ *	identifier of each index found. If an index could not be found,
+ *	then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IdOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tabPtr == NULL) {
+	Tcl_SetResult(interp, "", TCL_STATIC);
+    } else {
+	Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *	Add new entries into a tab set.
+ *
+ *	.t insert end label option-value label option-value...
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr, *beforeLinkPtr;
+    char c;
+
+    c = argv[2][0];
+    if ((c == 'e') && (strcmp(argv[2], "end") == 0)) {
+	beforeLinkPtr = NULL;
+    } else if (isdigit(UCHAR(c))) {
+	int position;
+
+	if (Tcl_GetInt(interp, argv[2], &position) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (position < 0) {
+	    beforeLinkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+	} else if (position > Blt_ChainGetLength(nbPtr->chainPtr)) {
+	    beforeLinkPtr = NULL;
+	} else {
+	    beforeLinkPtr = Blt_ChainGetNthLink(nbPtr->chainPtr, position);
+	}
+    } else {
+	Tab *beforePtr;
+
+	if (GetTab(nbPtr, argv[2], &beforePtr, INVALID_FAIL) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	beforeLinkPtr = beforePtr->linkPtr;
+    }
+    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    EventuallyRedraw(nbPtr);
+    tabPtr = CreateTab(nbPtr);
+    if (tabPtr == NULL) {
+	return TCL_ERROR;
+    }
+    lastNotebookInstance = nbPtr;
+    if (Blt_ConfigureWidgetComponent(interp, nbPtr->tkwin, tabPtr->name,
+	"Tab", tabConfigSpecs, argc - 3, argv + 3, (char *)tabPtr, 0) 
+	!= TCL_OK) {
+	DestroyTab(nbPtr, tabPtr);
+	return TCL_ERROR;
+    }
+    if (ConfigureTab(nbPtr, tabPtr) != TCL_OK) {
+	DestroyTab(nbPtr, tabPtr);
+	return TCL_ERROR;
+    }
+    linkPtr = Blt_ChainNewLink();
+    if (beforeLinkPtr == NULL) {
+	Blt_ChainAppendLink(nbPtr->chainPtr, linkPtr);
+    } else {
+	Blt_ChainLinkBefore(nbPtr->chainPtr, linkPtr, beforeLinkPtr);
+    }
+    tabPtr->linkPtr = linkPtr;
+    Blt_ChainSetValue(linkPtr, tabPtr);
+    Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+    return TCL_OK;
+
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+static void
+PercentSubst(nbPtr, tabPtr, command, resultPtr)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    char *command;
+    Tcl_DString *resultPtr;
+{
+    register char *last, *p;
+    /*
+     * Get the full path name of the node, in case we need to
+     * substitute for it.
+     */
+    Tcl_DStringInit(resultPtr);
+    for (last = p = command; *p != '\0'; p++) {
+	if (*p == '%') {
+	    char *string;
+	    char buf[3];
+
+	    if (p > last) {
+		*p = '\0';
+		Tcl_DStringAppend(resultPtr, last, -1);
+		*p = '%';
+	    }
+	    switch (*(p + 1)) {
+	    case '%':		/* Percent sign */
+		string = "%";
+		break;
+	    case 'W':		/* Widget name */
+		string = Tk_PathName(nbPtr->tkwin);
+		break;
+	    case 'i':		/* Tab Index */
+		string = Blt_Itoa(TabIndex(nbPtr, tabPtr));
+		break;
+	    case 'n':		/* Tab name */
+		string = tabPtr->name;
+		break;
+	    default:
+		if (*(p + 1) == '\0') {
+		    p--;
+		}
+		buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+		string = buf;
+		break;
+	    }
+	    Tcl_DStringAppend(resultPtr, string, -1);
+	    p++;
+	    last = p + 1;
+	}
+    }
+    if (p > last) {
+	*p = '\0';
+	Tcl_DStringAppend(resultPtr, last, -1);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeOp --
+ *
+ * 	This procedure is called to invoke a selection command.
+ *
+ *	  .h invoke index
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side Effects:
+ *	Configuration information, such as text string, colors, font,
+ * 	etc. get set;  old resources get freed, if there were any.
+ * 	The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvokeOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Tab *tabPtr;
+    char *command;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+	return TCL_OK;
+    }
+    Tcl_Preserve(tabPtr);
+    command = GETATTR(tabPtr, command);
+    if (command != NULL) {
+	Tcl_DString dString;
+	int result;
+
+	PercentSubst(nbPtr, tabPtr, command, &dString);
+	result = Tcl_GlobalEval(nbPtr->interp, 
+		Tcl_DStringValue(&dString));
+	Tcl_DStringFree(&dString);
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    Tcl_Release(tabPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ *	Moves a tab to a new location.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr, *linkPtr;
+    int before;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+	return TCL_OK;
+    }
+    if ((argv[3][0] == 'b') && (strcmp(argv[3], "before") == 0)) {
+	before = 1;
+    } else if ((argv[3][0] == 'a') && (strcmp(argv[3], "after") == 0)) {
+	before = 0;
+    } else {
+	Tcl_AppendResult(interp, "bad key word \"", argv[3],
+	    "\": should be \"after\" or \"before\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (GetTab(nbPtr, argv[4], &linkPtr, INVALID_FAIL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tabPtr == linkPtr) {
+	return TCL_OK;
+    }
+    Blt_ChainUnlinkLink(nbPtr->chainPtr, tabPtr->linkPtr);
+    if (before) {
+	Blt_ChainLinkBefore(nbPtr->chainPtr, tabPtr->linkPtr, 
+	    linkPtr->linkPtr);
+    } else {
+	Blt_ChainLinkAfter(nbPtr->chainPtr, tabPtr->linkPtr, 
+	    linkPtr->linkPtr);
+    }
+    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int x, y;			/* Screen coordinates of the test point. */
+    Tab *tabPtr;
+
+    if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[2], &x) != TCL_OK) ||
+	(Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (nbPtr->nVisible > 0) {
+	tabPtr = (Tab *)PickTab(nbPtr, x, y, NULL);
+	if (tabPtr != NULL) {
+	    Tcl_SetResult(interp, tabPtr->name, TCL_VOLATILE);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectOp --
+ *
+ * 	This procedure is called to selecta tab.
+ *
+ *	  .h select index
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side Effects:
+ *	Configuration information, such as text string, colors, font,
+ * 	etc. get set;  old resources get freed, if there were any.
+ * 	The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tabPtr == NULL) || (tabPtr->state == STATE_DISABLED)) {
+	return TCL_OK;
+    }
+    if ((nbPtr->selectPtr != NULL) && (nbPtr->selectPtr != tabPtr) &&
+	(nbPtr->selectPtr->tkwin != NULL)) {
+	if (nbPtr->selectPtr->container == NULL) {
+	    if (Tk_IsMapped(nbPtr->selectPtr->tkwin)) {
+		Tk_UnmapWindow(nbPtr->selectPtr->tkwin);
+	    }
+	} else {
+	    /* Redraw now unselected container. */
+	    EventuallyRedrawTearoff(nbPtr->selectPtr);
+	}
+    }
+    nbPtr->selectPtr = tabPtr;
+    if ((nbPtr->nTiers > 1) && (tabPtr->tier != nbPtr->startPtr->tier)) {
+	RenumberTiers(nbPtr, tabPtr);
+	Blt_PickCurrentItem(nbPtr->bindTable);
+    }
+    nbPtr->flags |= (TNB_SCROLL);
+    if (tabPtr->container != NULL) {
+	EventuallyRedrawTearoff(tabPtr);
+    }
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+static int
+ViewOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int width;
+
+    width = VPORTWIDTH(nbPtr);
+    if (argc == 2) {
+	double fract;
+
+	/*
+	 * Note: we are bounding the fractions between 0.0 and 1.0 to
+	 * support the "canvas"-style of scrolling.
+	 */
+
+	fract = (double)nbPtr->scrollOffset / nbPtr->worldWidth;
+	Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+	fract = (double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth;
+	Tcl_AppendElement(interp, Blt_Dtoa(interp, CLAMP(fract, 0.0, 1.0)));
+	return TCL_OK;
+    }
+    if (Blt_GetScrollInfo(interp, argc - 2, argv + 2, &(nbPtr->scrollOffset),
+	    nbPtr->worldWidth, width, nbPtr->scrollUnits, 
+	    BLT_SCROLL_MODE_CANVAS) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    nbPtr->flags |= TNB_SCROLL;
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+
+static void
+AdoptWindow(clientData)
+    ClientData clientData;
+{
+    Tab *tabPtr = clientData;
+    int x, y;
+    Notebook *nbPtr = tabPtr->nbPtr;
+
+    x = nbPtr->inset + nbPtr->inset2 + tabPtr->padLeft;
+#define TEAR_OFF_TAB_SIZE	5
+    y = nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
+	nbPtr->outerPad + TEAR_OFF_TAB_SIZE + tabPtr->padTop;
+    Blt_RelinkWindow(tabPtr->tkwin, tabPtr->container, x, y);
+    Tk_MapWindow(tabPtr->tkwin);
+}
+
+static void
+DestroyTearoff(dataPtr)
+    DestroyData dataPtr;
+{
+    Tab *tabPtr = (Tab *)dataPtr;
+
+    if (tabPtr->container != NULL) {
+	Notebook *nbPtr;
+	Tk_Window tkwin;
+	nbPtr = tabPtr->nbPtr;
+
+	tkwin = tabPtr->container;
+	if (tabPtr->flags & TAB_REDRAW) {
+	    Tcl_CancelIdleCall(DisplayTearoff, tabPtr);
+	}
+	Tk_DeleteEventHandler(tkwin, StructureNotifyMask, TearoffEventProc,
+	    tabPtr);
+	if (tabPtr->tkwin != NULL) {
+	    XRectangle rect;
+
+	    GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
+	    Blt_RelinkWindow(tabPtr->tkwin, nbPtr->tkwin, rect.x, rect.y);
+	    if (tabPtr == nbPtr->selectPtr) {
+		ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+	    } else {
+		Tk_UnmapWindow(tabPtr->tkwin);
+	    }
+	}
+	Tk_DestroyWindow(tkwin);
+	tabPtr->container = NULL;
+    }
+}
+
+static int
+CreateTearoff(nbPtr, name, tabPtr)
+    Notebook *nbPtr;
+    char *name;
+    Tab *tabPtr;
+{
+    Tk_Window tkwin;
+    int width, height;
+
+    tkwin = Tk_CreateWindowFromPath(nbPtr->interp, nbPtr->tkwin, name,
+	(char *)NULL);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    tabPtr->container = tkwin;
+    if (Tk_WindowId(tkwin) == None) {
+	Tk_MakeWindowExist(tkwin);
+    }
+    Tk_SetClass(tkwin, "Tearoff");
+    Tk_CreateEventHandler(tkwin, (ExposureMask | StructureNotifyMask),
+	TearoffEventProc, tabPtr);
+    if (Tk_WindowId(tabPtr->tkwin) == None) {
+	Tk_MakeWindowExist(tabPtr->tkwin);
+    }
+    width = Tk_Width(tabPtr->tkwin);
+    if (width < 2) {
+	width = (tabPtr->reqWidth > 0)
+	    ? tabPtr->reqWidth : Tk_ReqWidth(tabPtr->tkwin);
+    }
+    width += PADDING(tabPtr->padX) + 2 *
+	Tk_Changes(tabPtr->tkwin)->border_width;
+    width += 2 * (nbPtr->inset2 + nbPtr->inset);
+#define TEAR_OFF_TAB_SIZE	5
+    height = Tk_Height(tabPtr->tkwin);
+    if (height < 2) {
+	height = (tabPtr->reqHeight > 0)
+	    ? tabPtr->reqHeight : Tk_ReqHeight(tabPtr->tkwin);
+    }
+    height += PADDING(tabPtr->padY) +
+	2 * Tk_Changes(tabPtr->tkwin)->border_width;
+    height += nbPtr->inset + nbPtr->inset2 + nbPtr->yPad +
+	TEAR_OFF_TAB_SIZE + nbPtr->outerPad;
+    Tk_GeometryRequest(tkwin, width, height);
+    Tk_UnmapWindow(tabPtr->tkwin);
+    /* Tk_MoveWindow(tabPtr->tkwin, 0, 0); */
+    Tcl_SetResult(nbPtr->interp, Tk_PathName(tkwin), TCL_VOLATILE);
+#ifdef WIN32
+    AdoptWindow(tabPtr);
+#else
+    Tcl_DoWhenIdle(AdoptWindow, tabPtr);
+#endif
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabCgetOp --
+ *
+ *	  .h tab cget index option
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabCgetOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_FAIL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    lastNotebookInstance = nbPtr;
+    return Tk_ConfigureValue(interp, nbPtr->tkwin, tabConfigSpecs,
+	(char *)tabPtr, argv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the options for
+ *	one or more tabs in the widget.
+ *
+ *	  .h tab configure index ?index...? ?option value?...
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side Effects:
+ *	Configuration information, such as text string, colors, font,
+ * 	etc. get set;  old resources get freed, if there were any.
+ * 	The widget is redisplayed if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TabConfigureOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int nTabs, nOpts, result;
+    char **options;
+    register int i;
+    Tab *tabPtr;
+
+    /* Figure out where the option value pairs begin */
+    argc -= 3;
+    argv += 3;
+    for (i = 0; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    break;
+	}
+	if (GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL) != TCL_OK) {
+	    return TCL_ERROR;	/* Can't find node. */
+	}
+    }
+    nTabs = i;			/* Number of tab indices specified */
+    nOpts = argc - i;		/* Number of options specified */
+    options = argv + i;		/* Start of options in argv  */
+
+    for (i = 0; i < nTabs; i++) {
+	GetTab(nbPtr, argv[i], &tabPtr, INVALID_FAIL);
+	if (argc == 1) {
+	    return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
+		(char *)tabPtr, (char *)NULL, 0);
+	} else if (argc == 2) {
+	    return Tk_ConfigureInfo(interp, nbPtr->tkwin, tabConfigSpecs,
+		(char *)tabPtr, argv[2], 0);
+	}
+	Tcl_Preserve(tabPtr);
+	lastNotebookInstance = nbPtr;
+	result = Tk_ConfigureWidget(interp, nbPtr->tkwin, tabConfigSpecs,
+	    nOpts, options, (char *)tabPtr, TK_CONFIG_ARGV_ONLY);
+	if (result == TCL_OK) {
+	    result = ConfigureTab(nbPtr, tabPtr);
+	}
+	Tcl_Release(tabPtr);
+	if (result == TCL_ERROR) {
+	    return TCL_ERROR;
+	}
+	if (tabPtr->flags & TAB_VISIBLE) {
+	    nbPtr->flags |= (TNB_LAYOUT | TNB_SCROLL);
+	    EventuallyRedraw(nbPtr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabDockallOp --
+ *
+ *	  .h tab dockall
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabDockallOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+{
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	if (tabPtr->container != NULL) {
+	    Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabNamesOp --
+ *
+ *	  .h tab names pattern
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabNamesOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+{
+    Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+
+    if (argc == 3) {
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    Tcl_AppendElement(interp, tabPtr->name);
+	}
+    } else {
+	register int i;
+
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    for (i = 3; i < argc; i++) {
+		if (Tcl_StringMatch(tabPtr->name, argv[i])) {
+		    Tcl_AppendElement(interp, tabPtr->name);
+		    break;
+		}
+	    }
+	}
+    }
+    return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabTearoffOp --
+ *
+ *	  .h tab tearoff index ?title?
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TabTearoffOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    Tab *tabPtr;
+    int result;
+    Tk_Window tkwin;
+
+    if (GetTab(nbPtr, argv[3], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tabPtr == NULL) || (tabPtr->tkwin == NULL) ||
+	(tabPtr->state == STATE_DISABLED)) {
+	return TCL_OK;		/* No-op */
+    }
+    if (argc == 4) {
+	Tk_Window parent;
+
+	parent = (tabPtr->container == NULL)
+	    ? nbPtr->tkwin : tabPtr->container;
+	Tcl_SetResult(nbPtr->interp, Tk_PathName(parent), TCL_VOLATILE);
+	return TCL_OK;
+    }
+    Tcl_Preserve(tabPtr);
+    result = TCL_OK;
+
+    tkwin = Tk_NameToWindow(interp, argv[4], nbPtr->tkwin);
+    Tcl_ResetResult(interp);
+
+    if (tabPtr->container != NULL) {
+	Tcl_EventuallyFree(tabPtr, DestroyTearoff);
+    }
+    if ((tkwin != nbPtr->tkwin) && (tabPtr->container == NULL)) {
+	result = CreateTearoff(nbPtr, argv[4], tabPtr);
+    }
+    Tcl_Release(tabPtr);
+    EventuallyRedraw(nbPtr);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TabOp --
+ *
+ *	This procedure handles tab operations.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec tabOps[] =
+{
+    {"cget", 2, (Blt_Op)TabCgetOp, 5, 5, "nameOrIndex option",},
+    {"configure", 2, (Blt_Op)TabConfigureOp, 4, 0,
+	"nameOrIndex ?option value?...",},
+    {"dockall", 1, (Blt_Op)TabDockallOp, 3, 3, "" }, 
+    {"names", 1, (Blt_Op)TabNamesOp, 3, 0, "?pattern...?",},
+    {"tearoff", 1, (Blt_Op)TabTearoffOp, 4, 5, "index ?parent?",},
+};
+
+static int nTabOps = sizeof(tabOps) / sizeof(Blt_OpSpec);
+
+static int
+TabOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nTabOps, tabOps, BLT_OP_ARG2, argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (nbPtr, interp, argc, argv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationActivateOp --
+ *
+ * 	This procedure is called to highlight the perforation.
+ *
+ *	  .h perforation highlight boolean
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationActivateOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    int bool;
+
+    if (Tcl_GetBoolean(interp, argv[3], &bool) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (bool) {
+	nbPtr->flags |= PERFORATION_ACTIVE;
+    } else {
+	nbPtr->flags &= ~PERFORATION_ACTIVE;
+    }
+    EventuallyRedraw(nbPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationInvokeOp --
+ *
+ * 	This procedure is called to invoke a perforation command.
+ *
+ *	  .t perforation invoke
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PerforationInvokeOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+
+    if (nbPtr->selectPtr != NULL) {
+	char *cmd;
+	
+	cmd = GETATTR(nbPtr->selectPtr, perfCommand);
+	if (cmd != NULL) {
+	    Tcl_DString dString;
+	    int result;
+	    
+	    PercentSubst(nbPtr, nbPtr->selectPtr, cmd, &dString);
+	    Tcl_Preserve(nbPtr);
+	    result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
+	    Tcl_Release(nbPtr);
+	    Tcl_DStringFree(&dString);
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PerforationOp --
+ *
+ *	This procedure handles tab operations.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec perforationOps[] =
+{
+    {"activate", 1, (Blt_Op)PerforationActivateOp, 4, 4, "boolean" }, 
+    {"invoke", 1, (Blt_Op)PerforationInvokeOp, 3, 3, "",},
+};
+
+static int nPerforationOps = sizeof(perforationOps) / sizeof(Blt_OpSpec);
+
+static int
+PerforationOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOp(interp, nPerforationOps, perforationOps, BLT_OP_ARG2, 
+		     argc, argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (nbPtr, interp, argc, argv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ *	Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int x, y;
+    char c;
+    unsigned int length;
+    int oper;
+
+#define SCAN_MARK	1
+#define SCAN_DRAGTO	2
+    c = argv[2][0];
+    length = strlen(argv[2]);
+    if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
+	oper = SCAN_MARK;
+    } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
+	oper = SCAN_DRAGTO;
+    } else {
+	Tcl_AppendResult(interp, "bad scan operation \"", argv[2],
+	    "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((Tk_GetPixels(interp, nbPtr->tkwin, argv[3], &x) != TCL_OK) ||
+	(Tk_GetPixels(interp, nbPtr->tkwin, argv[4], &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (oper == SCAN_MARK) {
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    nbPtr->scanAnchor = y;
+	} else {
+	    nbPtr->scanAnchor = x;
+	}
+	nbPtr->scanOffset = nbPtr->scrollOffset;
+    } else {
+	int offset, delta;
+
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    delta = nbPtr->scanAnchor - y;
+	} else {
+	    delta = nbPtr->scanAnchor - x;
+	}
+	offset = nbPtr->scanOffset + (10 * delta);
+	offset = Blt_AdjustViewport(offset, nbPtr->worldWidth,
+	    VPORTWIDTH(nbPtr), nbPtr->scrollUnits, BLT_SCROLL_MODE_CANVAS);
+	nbPtr->scrollOffset = offset;
+	nbPtr->flags |= TNB_SCROLL;
+	EventuallyRedraw(nbPtr);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    Tab *tabPtr;
+
+    if (GetTab(nbPtr, argv[2], &tabPtr, INVALID_OK) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tabPtr != NULL) {
+	int left, right, width;
+
+	width = VPORTWIDTH(nbPtr);
+	left = nbPtr->scrollOffset + nbPtr->xSelectPad;
+	right = nbPtr->scrollOffset + width - nbPtr->xSelectPad;
+
+	/* If the tab is partially obscured, scroll so that it's
+	 * entirely in view. */
+	if (tabPtr->worldX < left) {
+	    nbPtr->scrollOffset = tabPtr->worldX;
+	    if (TabIndex(nbPtr, tabPtr) > 0) {
+		nbPtr->scrollOffset -= TAB_SCROLL_OFFSET;
+	    }
+	} else if ((tabPtr->worldX + tabPtr->worldWidth) >= right) {
+	    Blt_ChainLink *linkPtr;
+
+	    nbPtr->scrollOffset = tabPtr->worldX + tabPtr->worldWidth -
+		(width - 2 * nbPtr->xSelectPad);
+	    linkPtr = Blt_ChainNextLink(tabPtr->linkPtr); 
+	    if (linkPtr != NULL) {
+		Tab *nextPtr;
+
+		nextPtr = Blt_ChainGetValue(linkPtr);
+		if (nextPtr->tier == tabPtr->tier) {
+		    nbPtr->scrollOffset += TAB_SCROLL_OFFSET;
+		}
+	    }
+	}
+	nbPtr->flags |= TNB_SCROLL;
+	EventuallyRedraw(nbPtr);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SizeOp(nbPtr, interp, argc, argv)
+    Notebook *nbPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;		/* Not used. */
+{
+    Tcl_SetResult(interp, Blt_Itoa(Blt_ChainGetLength(nbPtr->chainPtr)),
+	TCL_VOLATILE);
+    return TCL_OK;
+}
+
+
+
+static int
+CountTabs(nbPtr)
+    Notebook *nbPtr;
+{
+    int count;
+    int width, height;
+    Blt_ChainLink *linkPtr;
+    register Tab *tabPtr;
+    register int pageWidth, pageHeight;
+    int labelWidth, labelHeight;
+    int tabWidth, tabHeight;
+
+    pageWidth = pageHeight = 0;
+    count = 0;
+
+    labelWidth = labelHeight = 0;
+
+    /*
+     * Pass 1:  Figure out the maximum area needed for a label and a
+     *		page.  Both the label and page dimensions are adjusted
+     *		for orientation.  In addition, reset the visibility
+     *		flags and reorder the tabs.
+     */
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+
+	/* Reset visibility flag and order of tabs. */
+
+	tabPtr->flags &= ~TAB_VISIBLE;
+	count++;
+
+	if (tabPtr->tkwin != NULL) {
+	    width = GetReqWidth(tabPtr);
+	    if (pageWidth < width) {
+		pageWidth = width;
+	    }
+	    height = GetReqHeight(tabPtr);
+	    if (pageHeight < height) {
+		pageHeight = height;
+	    }
+	}
+	if (labelWidth < tabPtr->labelWidth) {
+	    labelWidth = tabPtr->labelWidth;
+	}
+	if (labelHeight < tabPtr->labelHeight) {
+	    labelHeight = tabPtr->labelHeight;
+	}
+    }
+
+    nbPtr->overlap = 0;
+
+    /*
+     * Pass 2:	Set the individual sizes of each tab.  This is different
+     *		for constant and variable width tabs.  Add the extra space
+     *		needed for slanted tabs, now that we know maximum tab
+     *		height.
+     */
+    if (nbPtr->defTabStyle.constWidth) {
+	int slant;
+
+	tabWidth = 2 * nbPtr->inset2;
+	tabHeight = nbPtr->inset2 /* + 4 */;
+
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    tabWidth += labelHeight;
+	    tabHeight += labelWidth;
+	    slant = labelWidth;
+	} else {
+	    tabWidth += labelWidth;
+	    tabHeight += labelHeight;
+	    slant = labelHeight;
+	}
+	if (nbPtr->slant & SLANT_LEFT) {
+	    tabWidth += slant;
+	    nbPtr->overlap += tabHeight / 2;
+	}
+	if (nbPtr->slant & SLANT_RIGHT) {
+	    tabWidth += slant;
+	    nbPtr->overlap += tabHeight / 2;
+	}
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    tabPtr->worldWidth = tabWidth;
+	    tabPtr->worldHeight = tabHeight;
+	}
+    } else {
+	int slant;
+
+	tabWidth = tabHeight = 0;
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+
+	    width = 2 * nbPtr->inset2;
+	    height = nbPtr->inset2 /* + 4 */;
+	    if (nbPtr->side & SIDE_VERTICAL) {
+		width += tabPtr->labelHeight;
+		height += labelWidth;
+		slant = labelWidth;
+	    } else {
+		width += tabPtr->labelWidth;
+		height += labelHeight;
+		slant = labelHeight;
+	    }
+	    width += (nbPtr->slant & SLANT_LEFT) ? slant : nbPtr->corner;
+	    width += (nbPtr->slant & SLANT_RIGHT) ? slant : nbPtr->corner;
+
+	    tabPtr->worldWidth = width; /* + 2 * (nbPtr->corner + nbPtr->xSelectPad) */ ;
+	    tabPtr->worldHeight = height;
+
+	    if (tabWidth < width) {
+		tabWidth = width;
+	    }
+	    if (tabHeight < height) {
+		tabHeight = height;
+	    }
+	}
+	if (nbPtr->slant & SLANT_LEFT) {
+	    nbPtr->overlap += tabHeight / 2;
+	}
+	if (nbPtr->slant & SLANT_RIGHT) {
+	    nbPtr->overlap += tabHeight / 2;
+	}
+    }
+
+    nbPtr->tabWidth = tabWidth;
+    nbPtr->tabHeight = tabHeight;
+
+    /*
+     * Let the user override any page dimension.
+     */
+    nbPtr->pageWidth = pageWidth;
+    nbPtr->pageHeight = pageHeight;
+    if (nbPtr->reqPageWidth > 0) {
+	nbPtr->pageWidth = nbPtr->reqPageWidth;
+    }
+    if (nbPtr->reqPageHeight > 0) {
+	nbPtr->pageHeight = nbPtr->reqPageHeight;
+    }
+    return count;
+}
+
+
+static void
+WidenTabs(nbPtr, startPtr, nTabs, adjustment)
+    Notebook *nbPtr;
+    Tab *startPtr;
+    int nTabs;
+    int adjustment;
+{
+    register Tab *tabPtr;
+    register int i;
+    int ration;
+    Blt_ChainLink *linkPtr;
+    int x;
+
+    x = startPtr->tier;
+    while (adjustment > 0) {
+	ration = adjustment / nTabs;
+	if (ration == 0) {
+	    ration = 1;
+	}
+	linkPtr = startPtr->linkPtr;
+	for (i = 0; (linkPtr != NULL) && (i < nTabs) && (adjustment > 0); i++) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    adjustment -= ration;
+	    tabPtr->worldWidth += ration;
+	    assert(x == tabPtr->tier);
+	    linkPtr = Blt_ChainNextLink(linkPtr);
+	}
+    }
+    /*
+     * Go back and reset the world X-coordinates of the tabs,
+     * now that their widths have changed.
+     */
+    x = 0;
+    linkPtr = startPtr->linkPtr;
+    for (i = 0; (i < nTabs) && (linkPtr != NULL); i++) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	tabPtr->worldX = x;
+	x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+	linkPtr = Blt_ChainNextLink(linkPtr);
+    }
+}
+
+
+static void
+AdjustTabSizes(nbPtr, nTabs)
+    Notebook *nbPtr;
+    int nTabs;
+{
+    int tabsPerTier;
+    int total, count, extra;
+    Tab *startPtr, *nextPtr;
+    Blt_ChainLink *linkPtr;
+    register Tab *tabPtr;
+    int x, maxWidth;
+
+    tabsPerTier = (nTabs + (nbPtr->nTiers - 1)) / nbPtr->nTiers;
+    x = 0;
+    maxWidth = 0;
+    if (nbPtr->defTabStyle.constWidth) {
+	register int i;
+
+	linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+	count = 1;
+	while (linkPtr != NULL) {
+	    for (i = 0; i < tabsPerTier; i++) {
+		tabPtr = Blt_ChainGetValue(linkPtr);
+		tabPtr->tier = count;
+		tabPtr->worldX = x;
+		x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+		linkPtr = Blt_ChainNextLink(linkPtr);
+		if (x > maxWidth) {
+		    maxWidth = x;
+		}
+		if (linkPtr == NULL) {
+		    goto done;
+		}
+	    }
+	    count++;
+	    x = 0;
+	}
+    }
+  done:
+    /* Add to tab widths to fill out row. */
+    if (((nTabs % tabsPerTier) != 0) && (nbPtr->defTabStyle.constWidth)) {
+	return;
+    }
+    startPtr = NULL;
+    count = total = 0;
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	/*empty*/ ) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	if (startPtr == NULL) {
+	    startPtr = tabPtr;
+	}
+	count++;
+	total += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+	linkPtr = Blt_ChainNextLink(linkPtr);
+	if (linkPtr != NULL) {
+	    nextPtr = Blt_ChainGetValue(linkPtr);
+	    if (tabPtr->tier == nextPtr->tier) {
+		continue;
+	    }
+	}
+	total += nbPtr->overlap;
+	extra = nbPtr->worldWidth - total;
+	assert(count > 0);
+	if (extra > 0) {
+	    WidenTabs(nbPtr, startPtr, count, extra);
+	}
+	count = total = 0;
+	startPtr = NULL;
+    }
+}
+
+/*
+ *
+ * tabWidth = textWidth + gap + (2 * (pad + outerBW));
+ *
+ * tabHeight = textHeight + 2 * (pad + outerBW) + topMargin;
+ *
+ */
+static void
+ComputeLayout(nbPtr)
+    Notebook *nbPtr;
+{
+    int width;
+    Blt_ChainLink *linkPtr;
+    Tab *tabPtr;
+    int x, extra;
+    int nTiers, nTabs;
+
+    nbPtr->nTiers = 0;
+    nbPtr->pageTop = 0;
+    nbPtr->worldWidth = 1;
+    nbPtr->yPad = 0;
+
+    nTabs = CountTabs(nbPtr);
+    if (nTabs == 0) {
+	return;
+    }
+    /* Reset the pointers to the selected and starting tab. */
+    if (nbPtr->selectPtr == NULL) {
+	linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+	if (linkPtr != NULL) {
+	    nbPtr->selectPtr = Blt_ChainGetValue(linkPtr);
+	}
+    }
+    if (nbPtr->startPtr == NULL) {
+	nbPtr->startPtr = nbPtr->selectPtr;
+    }
+    if (nbPtr->focusPtr == NULL) {
+	nbPtr->focusPtr = nbPtr->selectPtr;
+	Blt_SetFocusItem(nbPtr->bindTable, nbPtr->focusPtr, NULL);
+    }
+
+    if (nbPtr->side & SIDE_VERTICAL) {
+        width = Tk_Height(nbPtr->tkwin) - 2 * 
+	    (nbPtr->corner + nbPtr->xSelectPad);
+    } else {
+        width = Tk_Width(nbPtr->tkwin) - (2 * nbPtr->inset) -
+		nbPtr->xSelectPad - nbPtr->corner;
+    }
+    nbPtr->flags |= TNB_STATIC;
+    if (nbPtr->reqTiers > 1) {
+	int total, maxWidth;
+
+	/* Static multiple tier mode. */
+
+	/* Sum tab widths and determine the number of tiers needed. */
+	nTiers = 1;
+	total = x = 0;
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    if ((x + tabPtr->worldWidth) > width) {
+		nTiers++;
+		x = 0;
+	    }
+	    tabPtr->worldX = x;
+	    tabPtr->tier = nTiers;
+	    extra = tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+	    total += extra, x += extra;
+	}
+	maxWidth = width;
+
+	if (nTiers > nbPtr->reqTiers) {
+	    /*
+	     * The tabs do not fit into the requested number of tiers.
+             * Go into scrolling mode.
+	     */
+	    width = ((total + nbPtr->tabWidth) / nbPtr->reqTiers);
+	    x = 0;
+	    nTiers = 1;
+	    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr);
+		linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+		tabPtr = Blt_ChainGetValue(linkPtr);
+		tabPtr->tier = nTiers;
+		/*
+		 * Keep adding tabs to a tier until we overfill it.
+		 */
+		tabPtr->worldX = x;
+		x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+		if (x > width) {
+		    nTiers++;
+		    if (x > maxWidth) {
+			maxWidth = x;
+		    }
+		    x = 0;
+		}
+	    }
+	    nbPtr->flags &= ~TNB_STATIC;
+	}
+	nbPtr->worldWidth = maxWidth;
+	nbPtr->nTiers = nTiers;
+
+	if (nTiers > 1) {
+	    AdjustTabSizes(nbPtr, nTabs);
+	}
+	if (nbPtr->flags & TNB_STATIC) {
+	    nbPtr->worldWidth = VPORTWIDTH(nbPtr);
+	} else {
+	    /* Do you add an offset ? */
+	    nbPtr->worldWidth += (nbPtr->xSelectPad + nbPtr->corner);
+	}
+	nbPtr->worldWidth += nbPtr->overlap;
+	if (nbPtr->selectPtr != NULL) {
+	    RenumberTiers(nbPtr, nbPtr->selectPtr);
+	}
+    } else {
+	/*
+	 * Scrollable single tier mode.
+	 */
+	nTiers = 1;
+	x = 0;
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    tabPtr->tier = nTiers;
+	    tabPtr->worldX = x;
+	    tabPtr->worldY = 0;
+	    x += tabPtr->worldWidth + nbPtr->gap - nbPtr->overlap;
+	}
+	nbPtr->worldWidth = x + nbPtr->corner - nbPtr->gap +
+	    nbPtr->xSelectPad + nbPtr->overlap;
+	nbPtr->flags &= ~TNB_STATIC;
+    }
+    if (nTiers == 1) {
+	nbPtr->yPad = nbPtr->ySelectPad;
+    }
+    nbPtr->nTiers = nTiers;
+    nbPtr->pageTop = nbPtr->inset + nbPtr->yPad /* + 4 */ +
+	(nbPtr->nTiers * nbPtr->tabHeight) + nbPtr->inset2;
+
+    if (nbPtr->side & SIDE_VERTICAL) {
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    tabPtr->screenWidth = (short int)nbPtr->tabHeight;
+	    tabPtr->screenHeight = (short int)tabPtr->worldWidth;
+	}
+    } else {
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    tabPtr->screenWidth = (short int)tabPtr->worldWidth;
+	    tabPtr->screenHeight = (short int)nbPtr->tabHeight;
+	}
+    }
+}
+
+static void
+ComputeVisibleTabs(nbPtr)
+    Notebook *nbPtr;
+{
+    int nVisibleTabs;
+    register Tab *tabPtr;
+    Blt_ChainLink *linkPtr;
+
+    nbPtr->nVisible = 0;
+    if (Blt_ChainGetLength(nbPtr->chainPtr) == 0) {
+	return;
+    }
+    nVisibleTabs = 0;
+    if (nbPtr->flags & TNB_STATIC) {
+
+	/* Static multiple tier mode. */
+
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    tabPtr->flags |= TAB_VISIBLE;
+	    nVisibleTabs++;
+	}
+    } else {
+	int width, offset;
+	/*
+	 * Scrollable (single or multiple) tier mode.
+	 */
+	offset = nbPtr->scrollOffset - (nbPtr->outerPad + nbPtr->xSelectPad);
+	width = VPORTWIDTH(nbPtr) + nbPtr->scrollOffset +
+	    2 * nbPtr->outerPad;
+	for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    if ((tabPtr->worldX >= width) ||
+		((tabPtr->worldX + tabPtr->worldWidth) < offset)) {
+		tabPtr->flags &= ~TAB_VISIBLE;
+	    } else {
+		tabPtr->flags |= TAB_VISIBLE;
+		nVisibleTabs++;
+	    }
+	}
+    }
+    for (linkPtr = Blt_ChainFirstLink(nbPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tabPtr = Blt_ChainGetValue(linkPtr);
+	tabPtr->screenX = tabPtr->screenY = -1000;
+	if (tabPtr->flags & TAB_VISIBLE) {
+	    WorldToScreen(nbPtr, tabPtr->worldX, tabPtr->worldY,
+		&(tabPtr->screenX), &(tabPtr->screenY));
+	    switch (nbPtr->side) {
+	    case SIDE_RIGHT:
+		tabPtr->screenX -= nbPtr->tabHeight;
+		break;
+
+	    case SIDE_BOTTOM:
+		tabPtr->screenY -= nbPtr->tabHeight;
+		break;
+	    }
+	}
+    }
+    nbPtr->nVisible = nVisibleTabs;
+    Blt_PickCurrentItem(nbPtr->bindTable);
+}
+
+
+static void
+Draw3DFolder(nbPtr, tabPtr, drawable, side, pointArr, nPoints)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    Drawable drawable;
+    int side;
+    XPoint pointArr[];
+    int nPoints;
+{
+    GC gc;
+    int relief, borderWidth;
+    Tk_3DBorder border;
+
+    if (tabPtr == nbPtr->selectPtr) {
+	border = GETATTR(tabPtr, selBorder);
+    } else if (tabPtr->border != NULL) {
+	border = tabPtr->border;
+    } else {
+	border = nbPtr->defTabStyle.border;
+    }
+    relief = nbPtr->defTabStyle.relief;
+    if ((side == SIDE_RIGHT) || (side == SIDE_TOP)) {
+	borderWidth = -nbPtr->defTabStyle.borderWidth;
+	if (relief == TK_RELIEF_SUNKEN) {
+	    relief = TK_RELIEF_RAISED;
+	} else if (relief == TK_RELIEF_RAISED) {
+	    relief = TK_RELIEF_SUNKEN;
+	}
+    } else {
+	borderWidth = nbPtr->defTabStyle.borderWidth;
+    }
+    {
+	int i;
+
+#ifndef notdef
+	int dx, dy;
+	int oldType, newType;
+	int start;
+
+	dx = pointArr[0].x - pointArr[1].x;
+	dy = pointArr[0].y - pointArr[1].y;
+	oldType = ((dy < 0) || (dx > 0));
+	start = 0;
+	for (i = 1; i < nPoints; i++) {
+	    dx = pointArr[i - 1].x - pointArr[i].x;
+	    dy = pointArr[i - 1].y - pointArr[i].y;
+	    newType = ((dy < 0) || (dx > 0));
+	    if (newType != oldType) {
+		if (oldType) {
+		    gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
+		}  else {
+		    gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC);
+		}		    
+		XDrawLines(nbPtr->display, drawable, gc, pointArr + start, 
+			   i - start, CoordModeOrigin);
+		start = i - 1;
+		oldType = newType;
+	    }
+	}
+	if (start != i) {
+	    if (oldType) {
+		gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
+	    }  else {
+		gc = Tk_3DBorderGC(nbPtr->tkwin, border, TK_3D_FLAT_GC);
+	    }		    
+	    XDrawLines(nbPtr->display, drawable, gc, pointArr + start, 
+		       i - start, CoordModeOrigin);
+	}
+#else
+	/* Draw the outline of the folder. */
+	gc = Tk_GCForColor(nbPtr->shadowColor, drawable);
+	XDrawLines(nbPtr->display, drawable, gc, pointArr, nPoints, 
+		   CoordModeOrigin);
+#endif
+    }
+    /* And the folder itself. */
+    if (tabPtr->tile != NULL) {
+#ifdef notdef
+	Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+	    borderWidth, relief);
+#endif
+	Blt_TilePolygon(nbPtr->tkwin, drawable, tabPtr->tile, pointArr, 
+			nPoints);
+#ifdef notdef
+	Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+	    borderWidth, relief);
+#endif
+    } else {
+	Tk_Fill3DPolygon(nbPtr->tkwin, drawable, border, pointArr, nPoints,
+	    borderWidth, relief);
+    }
+}
+
+/*
+ *   x,y
+ *    |1|2|3|   4    |3|2|1|
+ *
+ *   1. tab border width
+ *   2. corner offset
+ *   3. label pad
+ *   4. label width
+ *
+ *
+ */
+static void
+DrawLabel(nbPtr, tabPtr, drawable)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    Drawable drawable;
+{
+    int x, y, dx, dy;
+    int tx, ty, ix, iy;
+    int imgWidth, imgHeight;
+    int active, selected;
+    XColor *fgColor, *bgColor;
+    Tk_3DBorder border;
+    GC gc;
+
+    if (!(tabPtr->flags & TAB_VISIBLE)) {
+	return;
+    }
+    x = tabPtr->screenX;
+    y = tabPtr->screenY;
+
+    active = (nbPtr->activePtr == tabPtr);
+    selected = (nbPtr->selectPtr == tabPtr);
+
+    fgColor = GETATTR(tabPtr, textColor);
+    border = GETATTR(tabPtr, border);
+    if (selected) {
+	border = GETATTR(tabPtr, selBorder);
+    }
+    bgColor = Tk_3DBorderColor(border);
+    if (active) {
+	Tk_3DBorder activeBorder;
+
+	activeBorder = GETATTR(tabPtr, activeBorder);
+	bgColor = Tk_3DBorderColor(activeBorder);
+    }
+    dx = (tabPtr->screenWidth - tabPtr->labelWidth) / 2;
+    dy = (tabPtr->screenHeight - tabPtr->labelHeight) / 2;
+
+
+    /*
+     * The label position is computed with screen coordinates.  This
+     * is because both text and image components are oriented in
+     * screen space, and not according to the orientation of the tabs
+     * themselves.  That's why we have to consider the side when
+     * correcting for left/right slants.
+     */
+    switch (nbPtr->side) {
+    case SIDE_TOP:
+    case SIDE_BOTTOM:
+	if (nbPtr->slant == SLANT_LEFT) {
+	    x += nbPtr->overlap;
+	} else if (nbPtr->slant == SLANT_RIGHT) {
+	    x -= nbPtr->overlap;
+	}
+	break;
+    case SIDE_LEFT:
+    case SIDE_RIGHT:
+	if (nbPtr->slant == SLANT_LEFT) {
+	    y += nbPtr->overlap;
+	} else if (nbPtr->slant == SLANT_RIGHT) {
+	    y -= nbPtr->overlap;
+	}
+	break;
+    }
+
+    /*
+     * Draw the active or normal background color over the entire
+     * label area.  This includes both the tab's text and image.
+     * The rectangle should be 2 pixels wider/taller than this
+     * area. So if the label consists of just an image, we get an
+     * halo around the image when the tab is active.
+     */
+    gc = Tk_GCForColor(bgColor, drawable);
+    XFillRectangle(nbPtr->display, drawable, gc, x + dx, y + dy,
+	tabPtr->labelWidth, tabPtr->labelHeight);
+
+    if ((nbPtr->flags & TNB_FOCUS) && (nbPtr->focusPtr == tabPtr)) {
+	XDrawRectangle(nbPtr->display, drawable, nbPtr->defTabStyle.activeGC,
+	    x + dx, y + dy, tabPtr->labelWidth - 1, tabPtr->labelHeight - 1);
+    }
+    tx = ty = ix = iy = 0;	/* Suppress compiler warning. */
+
+    imgWidth = imgHeight = 0;
+    if (tabPtr->image != NULL) {
+	imgWidth = ImageWidth(tabPtr->image);
+	imgHeight = ImageHeight(tabPtr->image);
+    }
+    switch (nbPtr->defTabStyle.textSide) {
+    case SIDE_LEFT:
+	tx = x + dx + tabPtr->iPadX.side1;
+	ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+	ix = tx + tabPtr->textWidth + IMAGE_PAD;
+	iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+	break;
+    case SIDE_RIGHT:
+	ix = x + dx + tabPtr->iPadX.side1 + IMAGE_PAD;
+	iy = y + (tabPtr->screenHeight - imgHeight) / 2;
+	tx = ix + imgWidth;
+	ty = y + (tabPtr->screenHeight - tabPtr->textHeight) / 2;
+	break;
+    case SIDE_BOTTOM:
+	iy = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+	ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+	ty = iy + imgHeight;
+	tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+	break;
+    case SIDE_TOP:
+	tx = x + (tabPtr->screenWidth - tabPtr->textWidth) / 2;
+	ty = y + dy + tabPtr->iPadY.side1 + IMAGE_PAD;
+	ix = x + (tabPtr->screenWidth - imgWidth) / 2;
+	iy = ty + tabPtr->textHeight;
+	break;
+    }
+    if (tabPtr->image != NULL) {
+	Tk_RedrawImage(ImageBits(tabPtr->image), 0, 0, imgWidth, imgHeight,
+	    drawable, ix, iy);
+    }
+    if (tabPtr->text != NULL) {
+	TextStyle ts;
+	XColor *activeColor;
+
+	activeColor = fgColor;
+	if (selected) {
+	    activeColor = GETATTR(tabPtr, selColor);
+	} else if (active) {
+	    activeColor = GETATTR(tabPtr, activeFgColor);
+	}
+	Blt_SetDrawTextStyle(&ts, GETATTR(tabPtr, font), tabPtr->textGC,
+	    fgColor, activeColor, tabPtr->shadow.color,
+	    nbPtr->defTabStyle.rotate, TK_ANCHOR_NW, TK_JUSTIFY_LEFT,
+	    0, tabPtr->shadow.offset);
+	ts.state = tabPtr->state;
+	ts.border = border;
+	ts.padX.side1 = ts.padX.side2 = 2;
+	if (selected || active) {
+	    ts.state |= STATE_ACTIVE;
+	}
+	Blt_DrawText(nbPtr->tkwin, drawable, tabPtr->text, &ts, tx, ty);
+    }
+}
+
+
+static void
+DrawPerforation(nbPtr, tabPtr, drawable)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    Drawable drawable;
+{
+    XPoint pointArr[2];
+    int x, y;
+    int segmentWidth, max;
+    Tk_3DBorder border, perfBorder;
+
+    if ((tabPtr->container != NULL) || (tabPtr->tkwin == NULL)) {
+	return;
+    }
+    WorldToScreen(nbPtr, tabPtr->worldX + 2, 
+	  tabPtr->worldY + tabPtr->worldHeight + 2, &x, &y);
+    border = GETATTR(tabPtr, selBorder);
+    segmentWidth = 3;
+    if (nbPtr->flags & PERFORATION_ACTIVE) {
+	perfBorder = GETATTR(tabPtr, activeBorder);
+    } else {
+	perfBorder = GETATTR(tabPtr, selBorder);
+    }	
+    if (nbPtr->side & SIDE_HORIZONTAL) {
+	pointArr[0].x = x;
+	pointArr[0].y = pointArr[1].y = y;
+	max = tabPtr->screenX + tabPtr->screenWidth - 2;
+	Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
+	       x - 2, y - 4, tabPtr->screenWidth, 8, 0, TK_RELIEF_FLAT);
+	while (pointArr[0].x < max) {
+	    pointArr[1].x = pointArr[0].x + segmentWidth;
+	    if (pointArr[1].x > max) {
+		pointArr[1].x = max;
+	    }
+	    Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
+			     TK_RELIEF_RAISED);
+	    pointArr[0].x += 2 * segmentWidth;
+	}
+    } else {
+	pointArr[0].x = pointArr[1].x = x;
+	pointArr[0].y = y;
+	max  = tabPtr->screenY + tabPtr->screenHeight - 2;
+	Blt_Fill3DRectangle(nbPtr->tkwin, drawable, perfBorder,
+	       x - 4, y - 2, 8, tabPtr->screenHeight, 0, TK_RELIEF_FLAT);
+	while (pointArr[0].y < max) {
+	    pointArr[1].y = pointArr[0].y + segmentWidth;
+	    if (pointArr[1].y > max) {
+		pointArr[1].y = max;
+	    }
+	    Tk_Draw3DPolygon(nbPtr->tkwin, drawable, border, pointArr, 2, 1,
+			     TK_RELIEF_RAISED);
+	    pointArr[0].y += 2 * segmentWidth;
+	}
+    }
+}
+
+#define NextPoint(px, py) \
+	pointPtr->x = (px), pointPtr->y = (py), pointPtr++, nPoints++
+#define EndPoint(px, py) \
+	pointPtr->x = (px), pointPtr->y = (py), nPoints++
+
+#define BottomLeft(px, py) \
+	NextPoint((px) + nbPtr->corner, (py)), \
+	NextPoint((px), (py) - nbPtr->corner)
+
+#define TopLeft(px, py) \
+	NextPoint((px), (py) + nbPtr->corner), \
+	NextPoint((px) + nbPtr->corner, (py))
+
+#define TopRight(px, py) \
+	NextPoint((px) - nbPtr->corner, (py)), \
+	NextPoint((px), (py) + nbPtr->corner)
+
+#define BottomRight(px, py) \
+	NextPoint((px), (py) - nbPtr->corner), \
+	NextPoint((px) - nbPtr->corner, (py))
+
+
+/*
+ * From the left edge:
+ *
+ *   |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f|    j    |e|d|c|b|a|
+ *
+ *	a. highlight ring
+ *	b. notebook 3D border
+ *	c. outer gap
+ *      d. page border
+ *	e. page corner
+ *	f. gap + select pad
+ *	g. label pad x (worldX)
+ *	h. internal pad x
+ *	i. label width
+ *	j. rest of page width
+ *
+ *  worldX, worldY
+ *          |
+ *          |
+ *          * 4+ . . +5
+ *          3+         +6
+ *           .         .
+ *           .         .
+ *   1+. . .2+         +7 . . . .+8
+ * 0+                              +9
+ *  .                              .
+ *  .                              .
+ *13+                              +10
+ *  12+-------------------------+11
+ *
+ */
+static void
+DrawFolder(nbPtr, tabPtr, drawable)
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    Drawable drawable;
+{
+    XPoint pointArr[16];
+    XPoint *pointPtr;
+    int width, height;
+    int left, bottom, right, top, yBot, yTop;
+    int x, y;
+    register int i;
+    int nPoints;
+
+    width = VPORTWIDTH(nbPtr);
+    height = VPORTHEIGHT(nbPtr);
+
+    x = tabPtr->worldX;
+    y = tabPtr->worldY;
+
+    nPoints = 0;
+    pointPtr = pointArr;
+
+    /* Remember these are all world coordinates. */
+    /*
+     * x	Left side of tab.
+     * y	Top of tab.
+     * yTop	Top of folder.
+     * yBot	Bottom of the tab.
+     * left	Left side of the folder.
+     * right	Right side of the folder.
+     * top	Top of folder.
+     * bottom	Bottom of folder.
+     */
+    left = nbPtr->scrollOffset - nbPtr->xSelectPad;
+    right = left + width;
+    yTop = y + tabPtr->worldHeight;
+    yBot = nbPtr->pageTop - (nbPtr->inset + nbPtr->yPad);
+    top = yBot - nbPtr->inset2 /* - 4 */;
+
+    if (nbPtr->pageHeight == 0) {
+	bottom = yBot + 2 * nbPtr->corner;
+    } else {
+	bottom = height - nbPtr->yPad - 1;
+    }
+    if (tabPtr != nbPtr->selectPtr) {
+
+	/*
+	 * Case 1: Unselected tab
+	 *
+	 * * 3+ . . +4
+	 * 2+         +5
+	 *  .         .
+	 * 1+         +6
+	 *   0+ . . +7
+	 *
+	 */
+	
+	if (nbPtr->slant & SLANT_LEFT) {
+	    NextPoint(x, yBot);
+	    NextPoint(x, yTop);
+	    NextPoint(x + nbPtr->tabHeight, y);
+	} else {
+	    BottomLeft(x, yBot);
+	    TopLeft(x, y);
+	}
+	x += tabPtr->worldWidth;
+	if (nbPtr->slant & SLANT_RIGHT) {
+	    NextPoint(x - nbPtr->tabHeight, y);
+	    NextPoint(x, yTop);
+	    NextPoint(x, yBot);
+	} else {
+	    TopRight(x, y);
+	    BottomRight(x, yBot);
+	}
+    } else if (!(tabPtr->flags & TAB_VISIBLE)) {
+
+	/*
+	 * Case 2: Selected tab not visible in viewport.  Draw folder only.
+	 *
+	 * * 3+ . . +4
+	 * 2+         +5
+	 *  .         .
+	 * 1+         +6
+	 *   0+------+7
+	 *
+	 */
+
+	TopLeft(left, top);
+	TopRight(right, top);
+	BottomRight(right, bottom);
+	BottomLeft(left, bottom);
+    } else {
+	int flags;
+	int tabWidth;
+
+	x -= nbPtr->xSelectPad;
+	y -= nbPtr->yPad;
+	tabWidth = tabPtr->worldWidth + 2 * nbPtr->xSelectPad;
+
+#define CLIP_NONE	0
+#define CLIP_LEFT	(1<<0)
+#define CLIP_RIGHT	(1<<1)
+	flags = 0;
+	if (x < left) {
+	    flags |= CLIP_LEFT;
+	}
+	if ((x + tabWidth) > right) {
+	    flags |= CLIP_RIGHT;
+	}
+	switch (flags) {
+	case CLIP_NONE:
+
+	    /*
+	     *  worldX, worldY
+	     *          |
+	     *          * 4+ . . +5
+	     *          3+         +6
+	     *           .         .
+	     *           .         .
+	     *   1+. . .2+---------+7 . . . .+8
+	     * 0+                              +9
+	     *  .                              .
+	     *  .                              .
+	     *13+                              +10
+	     *  12+ . . . . . . . . . . . . +11
+	     */
+
+	    if (x < (left + nbPtr->corner)) {
+		NextPoint(left, top);
+	    } else {
+		TopLeft(left, top);
+	    }
+	    if (nbPtr->slant & SLANT_LEFT) {
+		NextPoint(x, yTop);
+		NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+	    } else {
+		NextPoint(x, top);
+		TopLeft(x, y);
+	    }
+	    x += tabWidth;
+	    if (nbPtr->slant & SLANT_RIGHT) {
+		NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+		NextPoint(x, yTop);
+	    } else {
+		TopRight(x, y);
+		NextPoint(x, top);
+	    }
+	    if (x > (right - nbPtr->corner)) {
+		NextPoint(right, top + nbPtr->corner);
+	    } else {
+		TopRight(right, top);
+	    }
+	    BottomRight(right, bottom);
+	    BottomLeft(left, bottom);
+	    break;
+
+	case CLIP_LEFT:
+
+	    /*
+	     *  worldX, worldY
+	     *          |
+	     *          * 4+ . . +5
+	     *          3+         +6
+	     *           .         .
+	     *           .         .
+	     *          2+--------+7 . . . .+8
+	     *            1+ . . . +0          +9
+	     *                     .           .
+	     *                     .           .
+	     *                   13+           +10
+	     *                     12+ . . . .+11
+	     */
+
+	    NextPoint(left, yBot);
+	    if (nbPtr->slant & SLANT_LEFT) {
+		NextPoint(x, yBot);
+		NextPoint(x, yTop);
+		NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+	    } else {
+		BottomLeft(x, yBot);
+		TopLeft(x, y);
+	    }
+
+	    x += tabWidth;
+	    if (nbPtr->slant & SLANT_RIGHT) {
+		NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+		NextPoint(x, yTop);
+		NextPoint(x, top);
+	    } else {
+		TopRight(x, y);
+		NextPoint(x, top);
+	    }
+	    if (x > (right - nbPtr->corner)) {
+		NextPoint(right, top + nbPtr->corner);
+	    } else {
+		TopRight(right, top);
+	    }
+	    BottomRight(right, bottom);
+	    BottomLeft(left, bottom);
+	    break;
+
+	case CLIP_RIGHT:
+
+	    /*
+	     *              worldX, worldY
+	     *                     |
+	     *                     * 9+ . . +10
+	     *                     8+         +11
+	     *                      .         .
+	     *                      .         .
+	     *           6+ . . . .7+---------+12
+	     *         5+          0+ . . . +13
+	     *          .           .
+	     *          .           .
+	     *         4+           +1
+	     *           3+ . . . +2
+	     */
+
+	    NextPoint(right, yBot);
+	    BottomRight(right, bottom);
+	    BottomLeft(left, bottom);
+	    if (x < (left + nbPtr->corner)) {
+		NextPoint(left, top);
+	    } else {
+		TopLeft(left, top);
+	    }
+	    NextPoint(x, top);
+
+	    if (nbPtr->slant & SLANT_LEFT) {
+		NextPoint(x, yTop);
+		NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+	    } else {
+		TopLeft(x, y);
+	    }
+	    x += tabWidth;
+	    if (nbPtr->slant & SLANT_RIGHT) {
+		NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+		NextPoint(x, yTop);
+		NextPoint(x, yBot);
+	    } else {
+		TopRight(x, y);
+		BottomRight(x, yBot);
+	    }
+	    break;
+
+	case (CLIP_LEFT | CLIP_RIGHT):
+
+	    /*
+	     *  worldX, worldY
+	     *     |
+	     *     * 4+ . . . . . . . . +5
+	     *     3+                     +6
+	     *      .                     .
+	     *      .                     .
+	     *     1+---------------------+7
+	     *       2+ 0+          +9 .+8
+	     *           .          .
+	     *           .          .
+	     *         13+          +10
+	     *          12+ . . . +11
+	     */
+
+	    NextPoint(left, yBot);
+	    if (nbPtr->slant & SLANT_LEFT) {
+		NextPoint(x, yBot);
+		NextPoint(x, yTop);
+		NextPoint(x + nbPtr->tabHeight + nbPtr->yPad, y);
+	    } else {
+		BottomLeft(x, yBot);
+		TopLeft(x, y);
+	    }
+	    x += tabPtr->worldWidth;
+	    if (nbPtr->slant & SLANT_RIGHT) {
+		NextPoint(x - nbPtr->tabHeight - nbPtr->yPad, y);
+		NextPoint(x, yTop);
+		NextPoint(x, yBot);
+	    } else {
+		TopRight(x, y);
+		BottomRight(x, yBot);
+	    }
+	    NextPoint(right, yBot);
+	    BottomRight(right, bottom);
+	    BottomLeft(left, bottom);
+	    break;
+	}
+    }
+    EndPoint(pointArr[0].x, pointArr[0].y);
+    for (i = 0; i < nPoints; i++) {
+	WorldToScreen(nbPtr, pointArr[i].x, pointArr[i].y, &x, &y);
+	pointArr[i].x = x;
+	pointArr[i].y = y;
+    }
+    Draw3DFolder(nbPtr, tabPtr, drawable, nbPtr->side, pointArr, nPoints);
+    DrawLabel(nbPtr, tabPtr, drawable);
+    if (tabPtr->container != NULL) {
+	XRectangle rect;
+
+	/* Draw a rectangle covering the spot representing the window  */
+	GetWindowRectangle(tabPtr, nbPtr->tkwin, FALSE, &rect);
+	XFillRectangles(nbPtr->display, drawable, tabPtr->backGC,
+	    &rect, 1);
+    }
+}
+
+static void
+DrawOuterBorders(nbPtr, drawable)
+    Notebook *nbPtr;
+    Drawable drawable;
+{
+    /*
+     * Draw 3D border just inside of the focus highlight ring.  We
+     * draw the border even if the relief is flat so that any tabs
+     * that hang over the edge will be clipped.
+     */
+    if (nbPtr->borderWidth > 0) {
+	Blt_Draw3DRectangle(nbPtr->tkwin, drawable, nbPtr->border,
+	    nbPtr->highlightWidth, nbPtr->highlightWidth,
+	    Tk_Width(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
+	    Tk_Height(nbPtr->tkwin) - 2 * nbPtr->highlightWidth,
+	    nbPtr->borderWidth, nbPtr->relief);
+    }
+    /* Draw focus highlight ring. */
+    if (nbPtr->highlightWidth > 0) {
+	XColor *color;
+	GC gc;
+
+	color = (nbPtr->flags & TNB_FOCUS)
+	    ? nbPtr->highlightColor : nbPtr->highlightBgColor;
+	gc = Tk_GCForColor(color, drawable);
+	Tk_DrawFocusHighlight(nbPtr->tkwin, gc, nbPtr->highlightWidth, 
+	      drawable);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayNotebook --
+ *
+ * 	This procedure is invoked to display the widget.
+ *
+ *      Recomputes the layout of the widget if necessary. This is
+ *	necessary if the world coordinate system has changed.
+ *	Sets the vertical and horizontal scrollbars.  This is done
+ *	here since the window width and height are needed for the
+ *	scrollbar calculations.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ * 	The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayNotebook(clientData)
+    ClientData clientData;	/* Information about widget. */
+{
+    Notebook *nbPtr = clientData;
+    Pixmap drawable;
+    int width, height;
+
+    nbPtr->flags &= ~TNB_REDRAW;
+    if (nbPtr->tkwin == NULL) {
+	return;			/* Window has been destroyed. */
+    }
+    if (nbPtr->flags & TNB_LAYOUT) {
+	ComputeLayout(nbPtr);
+	nbPtr->flags &= ~TNB_LAYOUT;
+    }
+    if ((nbPtr->reqHeight == 0) || (nbPtr->reqWidth == 0)) {
+	width = height = 0;
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    height = nbPtr->worldWidth;
+	} else {
+	    width = nbPtr->worldWidth;
+	}
+	if (nbPtr->reqWidth > 0) {
+	    width = nbPtr->reqWidth;
+	} else if (nbPtr->pageWidth > 0) {
+	    width = nbPtr->pageWidth;
+	}
+	if (nbPtr->reqHeight > 0) {
+	    height = nbPtr->reqHeight;
+	} else if (nbPtr->pageHeight > 0) {
+	    height = nbPtr->pageHeight;
+	}
+	if (nbPtr->side & SIDE_VERTICAL) {
+	    width += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
+	    height += nbPtr->inset + nbPtr->inset2;
+	} else {
+	    height += nbPtr->pageTop + nbPtr->inset + nbPtr->inset2;
+	    width += nbPtr->inset + nbPtr->inset2;
+	}
+	if ((Tk_ReqWidth(nbPtr->tkwin) != width) ||
+	    (Tk_ReqHeight(nbPtr->tkwin) != height)) {
+	    Tk_GeometryRequest(nbPtr->tkwin, width, height);
+	}
+    }
+    if (nbPtr->flags & TNB_SCROLL) {
+	width = VPORTWIDTH(nbPtr);
+	nbPtr->scrollOffset = Blt_AdjustViewport(nbPtr->scrollOffset,
+	    nbPtr->worldWidth, width, nbPtr->scrollUnits, 
+	    BLT_SCROLL_MODE_CANVAS);
+	if (nbPtr->scrollCmdPrefix != NULL) {
+	    Blt_UpdateScrollbar(nbPtr->interp, nbPtr->scrollCmdPrefix,
+		(double)nbPtr->scrollOffset / nbPtr->worldWidth,
+		(double)(nbPtr->scrollOffset + width) / nbPtr->worldWidth);
+	}
+	ComputeVisibleTabs(nbPtr);
+	nbPtr->flags &= ~TNB_SCROLL;
+    }
+    if (!Tk_IsMapped(nbPtr->tkwin)) {
+	return;
+    }
+    height = Tk_Height(nbPtr->tkwin);
+    drawable = Tk_GetPixmap(nbPtr->display, Tk_WindowId(nbPtr->tkwin),
+	Tk_Width(nbPtr->tkwin), Tk_Height(nbPtr->tkwin),
+	Tk_Depth(nbPtr->tkwin));
+
+    /*
+     * Clear the background either by tiling a pixmap or filling with
+     * a solid color. Tiling takes precedence.
+     */
+    if (nbPtr->tile != NULL) {
+	Blt_SetTileOrigin(nbPtr->tkwin, nbPtr->tile, 0, 0);
+	Blt_TileRectangle(nbPtr->tkwin, drawable, nbPtr->tile, 0, 0,
+	    Tk_Width(nbPtr->tkwin), height);
+    } else {
+	Blt_Fill3DRectangle(nbPtr->tkwin, drawable, nbPtr->border, 0, 0,
+	    Tk_Width(nbPtr->tkwin), height, 0, TK_RELIEF_FLAT);
+    }
+
+    if (nbPtr->nVisible > 0) {
+	register int i;
+	register Tab *tabPtr;
+	Blt_ChainLink *linkPtr;
+
+	linkPtr = nbPtr->startPtr->linkPtr;
+	for (i = 0; i < Blt_ChainGetLength(nbPtr->chainPtr); i++) {
+	    linkPtr = Blt_ChainPrevLink(linkPtr);
+	    if (linkPtr == NULL) {
+		linkPtr = Blt_ChainLastLink(nbPtr->chainPtr);
+	    }
+	    tabPtr = Blt_ChainGetValue(linkPtr);
+	    if ((tabPtr != nbPtr->selectPtr) &&
+		(tabPtr->flags & TAB_VISIBLE)) {
+		DrawFolder(nbPtr, tabPtr, drawable);
+	    }
+	}
+	DrawFolder(nbPtr, nbPtr->selectPtr, drawable);
+	if (nbPtr->tearoff) {
+	    DrawPerforation(nbPtr, nbPtr->selectPtr, drawable);
+	}
+
+	if ((nbPtr->selectPtr->tkwin != NULL) &&
+	    (nbPtr->selectPtr->container == NULL)) {
+	    XRectangle rect;
+
+	    GetWindowRectangle(nbPtr->selectPtr, nbPtr->tkwin, FALSE, &rect);
+	    ArrangeWindow(nbPtr->selectPtr->tkwin, &rect, 0);
+	}
+    }
+    DrawOuterBorders(nbPtr, drawable);
+    XCopyArea(nbPtr->display, drawable, Tk_WindowId(nbPtr->tkwin),
+	nbPtr->highlightGC, 0, 0, Tk_Width(nbPtr->tkwin), height, 0, 0);
+    Tk_FreePixmap(nbPtr->display, drawable);
+}
+
+/*
+ * From the left edge:
+ *
+ *   |a|b|c|d|e| f |d|e|g|h| i |h|g|e|d|f|    j    |e|d|c|b|a|
+ *
+ *	a. highlight ring
+ *	b. notebook 3D border
+ *	c. outer gap
+ *      d. page border
+ *	e. page corner
+ *	f. gap + select pad
+ *	g. label pad x (worldX)
+ *	h. internal pad x
+ *	i. label width
+ *	j. rest of page width
+ *
+ *  worldX, worldY
+ *          |
+ *          |
+ *          * 4+ . . +5
+ *          3+         +6
+ *           .         .
+ *           .         .
+ *   1+. . .2+         +7 . . . .+8
+ * 0+                              +9
+ *  .                              .
+ *  .                              .
+ *13+                              +10
+ *  12+-------------------------+11
+ *
+ */
+static void
+DisplayTearoff(clientData)
+    ClientData clientData;
+{
+    Notebook *nbPtr;
+    Tab *tabPtr;
+    Drawable drawable;
+    XPoint pointArr[16];
+    XPoint *pointPtr;
+    int width, height;
+    int left, bottom, right, top;
+    int x, y;
+    int nPoints;
+    Tk_Window tkwin;
+    Tk_Window parent;
+    XRectangle rect;
+
+    tabPtr = clientData;
+    if (tabPtr == NULL) {
+	return;
+    }
+    tabPtr->flags &= ~TAB_REDRAW;
+    nbPtr = tabPtr->nbPtr;
+    if (nbPtr->tkwin == NULL) {
+	return;
+    }
+    tkwin = tabPtr->container;
+    drawable = Tk_WindowId(tkwin);
+
+    /*
+     * Clear the background either by tiling a pixmap or filling with
+     * a solid color. Tiling takes precedence.
+     */
+    if (nbPtr->tile != NULL) {
+	Blt_SetTileOrigin(tkwin, nbPtr->tile, 0, 0);
+	Blt_TileRectangle(tkwin, drawable, nbPtr->tile, 0, 0, Tk_Width(tkwin), 
+		Tk_Height(tkwin));
+    } else {
+	Blt_Fill3DRectangle(tkwin, drawable, nbPtr->border, 0, 0, 
+		Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
+    }
+
+    width = Tk_Width(tkwin) - 2 * nbPtr->inset;
+    height = Tk_Height(tkwin) - 2 * nbPtr->inset;
+    x = nbPtr->inset + nbPtr->gap + nbPtr->corner;
+    y = nbPtr->inset;
+
+    left = nbPtr->inset;
+    right = nbPtr->inset + width;
+    top = nbPtr->inset + nbPtr->corner + nbPtr->xSelectPad;
+    bottom = nbPtr->inset + height;
+
+    /*
+     *  worldX, worldY
+     *          |
+     *          * 4+ . . +5
+     *          3+         +6
+     *           .         .
+     *           .         .
+     *   1+. . .2+         +7 . . . .+8
+     * 0+                              +9
+     *  .                              .
+     *  .                              .
+     *13+                              +10
+     *  12+-------------------------+11
+     */
+
+    nPoints = 0;
+    pointPtr = pointArr;
+
+    TopLeft(left, top);
+    NextPoint(x, top);
+    TopLeft(x, y);
+    x += tabPtr->worldWidth;
+    TopRight(x, y);
+    NextPoint(x, top);
+    TopRight(right, top);
+    BottomRight(right, bottom);
+    BottomLeft(left, bottom);
+    EndPoint(pointArr[0].x, pointArr[0].y);
+    Draw3DFolder(nbPtr, tabPtr, drawable, SIDE_TOP, pointArr, nPoints);
+
+    parent = (tabPtr->container == NULL) ? nbPtr->tkwin : tabPtr->container;
+    GetWindowRectangle(tabPtr, parent, TRUE, &rect);
+    ArrangeWindow(tabPtr->tkwin, &rect, TRUE);
+
+    /* Draw 3D border. */
+    if ((nbPtr->borderWidth > 0) && (nbPtr->relief != TK_RELIEF_FLAT)) {
+	Blt_Draw3DRectangle(tkwin, drawable, nbPtr->border, 0, 0,
+	    Tk_Width(tkwin), Tk_Height(tkwin), nbPtr->borderWidth,
+	    nbPtr->relief);
+    }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NotebookCmd --
+ *
+ * 	This procedure is invoked to process the "notebook" command.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec notebookOps[] =
+{
+    {"activate", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+    {"bind", 1, (Blt_Op)BindOp, 2, 5, "index ?sequence command?",},
+    {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+    {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "first ?last?",},
+    {"focus", 1, (Blt_Op)FocusOp, 3, 3, "index",},
+    {"highlight", 1, (Blt_Op)ActivateOp, 3, 3, "index",},
+    {"id", 2, (Blt_Op)IdOp, 3, 3, "index",},
+    {"index", 3, (Blt_Op)IndexOp, 3, 5, "string",},
+    {"insert", 3, (Blt_Op)InsertOp, 3, 0, "index ?option value?",},
+    {"invoke", 3, (Blt_Op)InvokeOp, 3, 3, "index",},
+    {"move", 1, (Blt_Op)MoveOp, 5, 5, "name after|before index",},
+    {"nearest", 1, (Blt_Op)NearestOp, 4, 4, "x y",},
+    {"perforation", 1, (Blt_Op)PerforationOp, 2, 0, "args",},
+    {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+    {"see", 3, (Blt_Op)SeeOp, 3, 3, "index",},
+    {"select", 3, (Blt_Op)SelectOp, 3, 3, "index",},
+    {"size", 2, (Blt_Op)SizeOp, 2, 2, "",},
+    {"tab", 1, (Blt_Op)TabOp, 2, 0, "oper args",},
+    {"view", 1, (Blt_Op)ViewOp, 2, 5,
+	"?moveto fract? ?scroll number what?",},
+};
+
+static int nNotebookOps = sizeof(notebookOps) / sizeof(Blt_OpSpec);
+
+static int
+NotebookInstCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Information about the widget. */
+    Tcl_Interp *interp;		/* Interpreter to report errors back to. */
+    int argc;			/* Number of arguments. */
+    char **argv;		/* Vector of argument strings. */
+{
+    Blt_Op proc;
+    Notebook *nbPtr = clientData;
+    int result;
+
+    proc = Blt_GetOp(interp, nNotebookOps, notebookOps, BLT_OP_ARG1, argc, 
+	argv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(nbPtr);
+    result = (*proc) (nbPtr, interp, argc, argv);
+    Tcl_Release(nbPtr);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotebookInstDeletedCmd --
+ *
+ *	This procedure can be called if the window was destroyed
+ *	(tkwin will be NULL) and the command was deleted
+ *	automatically.  In this case, we need to do nothing.
+ *
+ *	Otherwise this routine was called because the command was
+ *	deleted.  Then we need to clean-up and destroy the widget.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotebookInstDeletedCmd(clientData)
+    ClientData clientData;	/* Pointer to widget record for widget. */
+{
+    Notebook *nbPtr = clientData;
+
+    if (nbPtr->tkwin != NULL) {
+	Tk_Window tkwin;
+
+	tkwin = nbPtr->tkwin;
+	nbPtr->tkwin = NULL;
+	Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+	Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+    }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * NotebookCmd --
+ *
+ * 	This procedure is invoked to process the Tcl command that
+ * 	corresponds to a widget managed by this module. See the user
+ * 	documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side Effects:
+ *	See the user documentation.
+ *
+ * -----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+NotebookCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Main window associated with interpreter. */
+    Tcl_Interp *interp;		/* Current interpreter. */
+    int argc;			/* Number of arguments. */
+    char **argv;		/* Argument strings. */
+{
+    Notebook *nbPtr;
+    Tk_Window tkwin;
+    unsigned int mask;
+    Tcl_CmdInfo cmdInfo;
+
+    if (argc < 2) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+	    " pathName ?option value?...\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
+	(char *)NULL);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    nbPtr = CreateNotebook(interp, tkwin);
+    if (ConfigureNotebook(interp, nbPtr, argc - 2, argv + 2, 0) != TCL_OK) {
+	Tk_DestroyWindow(nbPtr->tkwin);
+	return TCL_ERROR;
+    }
+    mask = (ExposureMask | StructureNotifyMask | FocusChangeMask);
+    Tk_CreateEventHandler(tkwin, mask, NotebookEventProc, nbPtr);
+    nbPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], NotebookInstCmd,
+	nbPtr, NotebookInstDeletedCmd);
+#ifdef ITCL_NAMESPACES
+    Itk_SetWidgetCommand(nbPtr->tkwin, nbPtr->cmdToken);
+#endif
+
+    /*
+     * Try to invoke a procedure to initialize various bindings on
+     * tabs.  Source the file containing the procedure now if the
+     * procedure isn't currently defined.  We deferred this to now so
+     * that the user could set the variable "blt_library" within the
+     * script.
+     */
+    if (!Tcl_GetCommandInfo(interp, "blt::TabnotebookInit", &cmdInfo)) {
+	static char initCmd[] = 
+	    "source [file join $blt_library tabnotebook.tcl]";
+
+	if (Tcl_GlobalEval(interp, initCmd) != TCL_OK) {
+	    char info[200];
+
+	    sprintf(info, "\n    (while loading bindings for %s)", argv[0]);
+	    Tcl_AddErrorInfo(interp, info);
+	    Tk_DestroyWindow(nbPtr->tkwin);
+	    return TCL_ERROR;
+	}
+    }
+    if (Tcl_VarEval(interp, "blt::TabnotebookInit ", argv[1], (char *)NULL)
+	!= TCL_OK) {
+	Tk_DestroyWindow(nbPtr->tkwin);
+	return TCL_ERROR;
+    }
+    Tcl_SetResult(interp, Tk_PathName(nbPtr->tkwin), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+int
+Blt_TabnotebookInit(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpec =
+    {
+	"tabnotebook", NotebookCmd,
+    };
+
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+#endif /* NO_TABNOTEBOOK */
Index: trunk/kitgen/8.x/blt/generic/bltText.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltText.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltText.c	(revision 175)
@@ -0,0 +1,921 @@
+
+/*
+ * bltText.c --
+ *
+ *	This module implements multi-line, rotate-able text for the BLT toolkit.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include <X11/Xutil.h>
+#include "bltImage.h"
+
+#define WINDEBUG	0
+
+static Blt_HashTable bitmapGCTable;
+static int initialized;
+
+static void
+DrawTextLayout(display, drawable, gc, font, x, y, textPtr)
+    Display *display;
+    Drawable drawable;
+    GC gc;
+    Tk_Font font;
+    register int x, y;		/* Origin of text */
+    TextLayout *textPtr;
+{
+    register TextFragment *fragPtr;
+    register int i;
+
+    fragPtr = textPtr->fragArr;
+    for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
+#if HAVE_UTF
+	Tk_DrawChars(display, drawable, gc, font, fragPtr->text,
+	    fragPtr->count, x + fragPtr->x, y + fragPtr->y);
+#else
+	XDrawString(display, drawable, gc, x + fragPtr->x, y + fragPtr->y,
+	    fragPtr->text, fragPtr->count);
+#endif /*HAVE_UTF*/
+    }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetTextLayout --
+ *
+ *	Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ *	Returns via *widthPtr* and *heightPtr* the dimensions of
+ *	the text string.
+ *
+ * -----------------------------------------------------------------
+ */
+TextLayout *
+Blt_GetTextLayout(string, tsPtr)
+    char string[];
+    TextStyle *tsPtr;
+{
+    int maxHeight, maxWidth;
+    int count;			/* Count # of characters on each line */
+    int nFrags;
+    int width;			/* Running dimensions of the text */
+    TextFragment *fragPtr;
+    TextLayout *textPtr;
+    int lineHeight;
+    int size;
+    register char *p;
+    register int i;
+    Tk_FontMetrics fontMetrics;
+
+    Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
+    lineHeight = fontMetrics.linespace + 
+	tsPtr->leader + tsPtr->shadow.offset;
+    nFrags = 0;
+    for (p = string; *p != '\0'; p++) {
+	if (*p == '\n') {
+	    nFrags++;
+	}
+    }
+    if ((p != string) && (*(p - 1) != '\n')) {
+	nFrags++;
+    }
+    size = sizeof(TextLayout) + (sizeof(TextFragment) * (nFrags - 1));
+    textPtr = Blt_Calloc(1, size);
+    textPtr->nFrags = nFrags;
+    nFrags = count = 0;
+    width = maxWidth = 0;
+    maxHeight = tsPtr->padTop;
+    fragPtr = textPtr->fragArr;
+    for (p = string; *p != '\0'; p++) {
+	if (*p == '\n') {
+	    if (count > 0) {
+		width = Tk_TextWidth(tsPtr->font, string, count) +
+		    tsPtr->shadow.offset;
+		if (width > maxWidth) {
+		    maxWidth = width;
+		}
+	    }
+	    fragPtr->width = width;
+	    fragPtr->count = count;
+	    fragPtr->y = maxHeight + fontMetrics.ascent;
+	    fragPtr->text = string;
+	    fragPtr++;
+	    nFrags++;
+	    maxHeight += lineHeight;
+	    string = p + 1;	/* Start the string on the next line */
+	    count = 0;		/* Reset to indicate the start of a new line */
+	    continue;
+	}
+	count++;
+    }
+    if (nFrags < textPtr->nFrags) {
+	width = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
+	if (width > maxWidth) {
+	    maxWidth = width;
+	}
+	fragPtr->width = width;
+	fragPtr->count = count;
+	fragPtr->y = maxHeight + fontMetrics.ascent;
+	fragPtr->text = string;
+	maxHeight += lineHeight;
+	nFrags++;
+    }
+    maxHeight += tsPtr->padBottom;
+    maxWidth += PADDING(tsPtr->padX);
+    fragPtr = textPtr->fragArr;
+    for (i = 0; i < nFrags; i++, fragPtr++) {
+	switch (tsPtr->justify) {
+	default:
+	case TK_JUSTIFY_LEFT:
+	    /* No offset for left justified text strings */
+	    fragPtr->x = tsPtr->padLeft;
+	    break;
+	case TK_JUSTIFY_RIGHT:
+	    fragPtr->x = (maxWidth - fragPtr->width) - tsPtr->padRight;
+	    break;
+	case TK_JUSTIFY_CENTER:
+	    fragPtr->x = (maxWidth - fragPtr->width) / 2;
+	    break;
+	}
+    }
+    textPtr->width = maxWidth;
+    textPtr->height = maxHeight - tsPtr->leader;
+    return textPtr;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetTextExtents --
+ *
+ *	Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ *	Returns via *widthPtr* and *heightPtr* the dimensions of
+ *	the text string.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_GetTextExtents(tsPtr, string, widthPtr, heightPtr)
+    TextStyle *tsPtr;
+    char *string;
+    int *widthPtr, *heightPtr;
+{
+    int count;			/* Count # of characters on each line */
+    int width, height;
+    int w, lineHeight;
+    register char *p;
+    Tk_FontMetrics fontMetrics;
+
+    if (string == NULL) {
+	return;			/* NULL string? */
+    }
+    Tk_GetFontMetrics(tsPtr->font, &fontMetrics);
+    lineHeight = fontMetrics.linespace + tsPtr->leader + tsPtr->shadow.offset;
+    count = 0;
+    width = height = 0;
+    for (p = string; *p != '\0'; p++) {
+	if (*p == '\n') {
+	    if (count > 0) {
+		w = Tk_TextWidth(tsPtr->font, string, count) +
+		    tsPtr->shadow.offset;
+		if (w > width) {
+		    width = w;
+		}
+	    }
+	    height += lineHeight;
+	    string = p + 1;	/* Start the string on the next line */
+	    count = 0;		/* Reset to indicate the start of a new line */
+	    continue;
+	}
+	count++;
+    }
+    if ((count > 0) && (*(p - 1) != '\n')) {
+	height += lineHeight;
+	w = Tk_TextWidth(tsPtr->font, string, count) + tsPtr->shadow.offset;
+	if (w > width) {
+	    width = w;
+	}
+    }
+    *widthPtr = width + PADDING(tsPtr->padX);
+    *heightPtr = height + PADDING(tsPtr->padY);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_GetBoundingBox
+ *
+ *	Computes the dimensions of the bounding box surrounding a
+ *	rectangle rotated about its center.  If pointArr isn't NULL,
+ *	the coordinates of the rotated rectangle are also returned.
+ *
+ *	The dimensions are determined by rotating the rectangle, and
+ *	doubling the maximum x-coordinate and y-coordinate.
+ *
+ *		w = 2 * maxX,  h = 2 * maxY
+ *
+ *	Since the rectangle is centered at 0,0, the coordinates of
+ *	the bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2).
+ *
+ *  		0 ------- 1
+ *  		|         |
+ *  		|    x    |
+ *  		|         |
+ *  		3 ------- 2
+ *
+ * Results:
+ *	The width and height of the bounding box containing the
+ *	rotated rectangle are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_GetBoundingBox(width, height, theta, rotWidthPtr, rotHeightPtr, bbox)
+    int width, height;		/* Unrotated region */
+    double theta;		/* Rotation of box */
+    double *rotWidthPtr, *rotHeightPtr;	/* (out) Bounding box region */
+    Point2D *bbox;		/* (out) Points of the rotated box */
+{
+    register int i;
+    double sinTheta, cosTheta;
+    double xMax, yMax;
+    register double x, y;
+    Point2D corner[4];
+
+    theta = FMOD(theta, 360.0);
+    if (FMOD(theta, (double)90.0) == 0.0) {
+	int ll, ur, ul, lr;
+	double rotWidth, rotHeight;
+	int quadrant;
+
+	/* Handle right-angle rotations specifically */
+
+	quadrant = (int)(theta / 90.0);
+	switch (quadrant) {
+	case ROTATE_270:	/* 270 degrees */
+	    ul = 3, ur = 0, lr = 1, ll = 2;
+	    rotWidth = (double)height;
+	    rotHeight = (double)width;
+	    break;
+	case ROTATE_90:		/* 90 degrees */
+	    ul = 1, ur = 2, lr = 3, ll = 0;
+	    rotWidth = (double)height;
+	    rotHeight = (double)width;
+	    break;
+	case ROTATE_180:	/* 180 degrees */
+	    ul = 2, ur = 3, lr = 0, ll = 1;
+	    rotWidth = (double)width;
+	    rotHeight = (double)height;
+	    break;
+	default:
+	case ROTATE_0:		/* 0 degrees */
+	    ul = 0, ur = 1, lr = 2, ll = 3;
+	    rotWidth = (double)width;
+	    rotHeight = (double)height;
+	    break;
+	}
+	if (bbox != NULL) {
+	    x = rotWidth * 0.5;
+	    y = rotHeight * 0.5;
+	    bbox[ll].x = bbox[ul].x = -x;
+	    bbox[ur].y = bbox[ul].y = -y;
+	    bbox[lr].x = bbox[ur].x = x;
+	    bbox[ll].y = bbox[lr].y = y;
+	}
+	*rotWidthPtr = rotWidth;
+	*rotHeightPtr = rotHeight;
+	return;
+    }
+    /* Set the four corners of the rectangle whose center is the origin */
+
+    corner[1].x = corner[2].x = (double)width *0.5;
+    corner[0].x = corner[3].x = -corner[1].x;
+    corner[2].y = corner[3].y = (double)height *0.5;
+    corner[0].y = corner[1].y = -corner[2].y;
+
+    theta = (-theta / 180.0) * M_PI;
+    sinTheta = sin(theta), cosTheta = cos(theta);
+    xMax = yMax = 0.0;
+
+    /* Rotate the four corners and find the maximum X and Y coordinates */
+
+    for (i = 0; i < 4; i++) {
+	x = (corner[i].x * cosTheta) - (corner[i].y * sinTheta);
+	y = (corner[i].x * sinTheta) + (corner[i].y * cosTheta);
+	if (x > xMax) {
+	    xMax = x;
+	}
+	if (y > yMax) {
+	    yMax = y;
+	}
+	if (bbox != NULL) {
+	    bbox[i].x = x;
+	    bbox[i].y = y;
+	}
+    }
+
+    /*
+     * By symmetry, the width and height of the bounding box are
+     * twice the maximum x and y coordinates.
+     */
+    *rotWidthPtr = xMax + xMax;
+    *rotHeightPtr = yMax + yMax;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TranslateAnchor --
+ *
+ * 	Translate the coordinates of a given bounding box based
+ *	upon the anchor specified.  The anchor indicates where
+ *	the given xy position is in relation to the bounding box.
+ *
+ *  		nw --- n --- ne
+ *  		|            |
+ *  		w   center   e
+ *  		|            |
+ *  		sw --- s --- se
+ *
+ * 	The coordinates returned are translated to the origin of the
+ * 	bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ *	The translated coordinates of the bounding box are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_TranslateAnchor(x, y, width, height, anchor, transXPtr, transYPtr)
+    int x, y;			/* Window coordinates of anchor */
+    int width, height;		/* Extents of the bounding box */
+    Tk_Anchor anchor;		/* Direction of the anchor */
+    int *transXPtr, *transYPtr;
+{
+    switch (anchor) {
+    case TK_ANCHOR_NW:		/* Upper left corner */
+	break;
+    case TK_ANCHOR_W:		/* Left center */
+	y -= (height / 2);
+	break;
+    case TK_ANCHOR_SW:		/* Lower left corner */
+	y -= height;
+	break;
+    case TK_ANCHOR_N:		/* Top center */
+	x -= (width / 2);
+	break;
+    case TK_ANCHOR_CENTER:	/* Center */
+	x -= (width / 2);
+	y -= (height / 2);
+	break;
+    case TK_ANCHOR_S:		/* Bottom center */
+	x -= (width / 2);
+	y -= height;
+	break;
+    case TK_ANCHOR_NE:		/* Upper right corner */
+	x -= width;
+	break;
+    case TK_ANCHOR_E:		/* Right center */
+	x -= width;
+	y -= (height / 2);
+	break;
+    case TK_ANCHOR_SE:		/* Lower right corner */
+	x -= width;
+	y -= height;
+	break;
+    }
+    *transXPtr = x;
+    *transYPtr = y;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_TranslatePoint --
+ *
+ * 	Translate the coordinates of a given bounding box based
+ *	upon the anchor specified.  The anchor indicates where
+ *	the given xy position is in relation to the bounding box.
+ *
+ *  		nw --- n --- ne
+ *  		|            |
+ *  		w   center   e
+ *  		|            |
+ *  		sw --- s --- se
+ *
+ * 	The coordinates returned are translated to the origin of the
+ * 	bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ *	The translated coordinates of the bounding box are returned.
+ *
+ * -----------------------------------------------------------------
+ */
+Point2D
+Blt_TranslatePoint(pointPtr, width, height, anchor)
+    Point2D *pointPtr;		/* Window coordinates of anchor */
+    int width, height;		/* Extents of the bounding box */
+    Tk_Anchor anchor;		/* Direction of the anchor */
+{
+    Point2D trans;
+
+    trans = *pointPtr;
+    switch (anchor) {
+    case TK_ANCHOR_NW:		/* Upper left corner */
+	break;
+    case TK_ANCHOR_W:		/* Left center */
+	trans.y -= (height * 0.5);
+	break;
+    case TK_ANCHOR_SW:		/* Lower left corner */
+	trans.y -= height;
+	break;
+    case TK_ANCHOR_N:		/* Top center */
+	trans.x -= (width * 0.5);
+	break;
+    case TK_ANCHOR_CENTER:	/* Center */
+	trans.x -= (width * 0.5);
+	trans.y -= (height * 0.5);
+	break;
+    case TK_ANCHOR_S:		/* Bottom center */
+	trans.x -= (width * 0.5);
+	trans.y -= height;
+	break;
+    case TK_ANCHOR_NE:		/* Upper right corner */
+	trans.x -= width;
+	break;
+    case TK_ANCHOR_E:		/* Right center */
+	trans.x -= width;
+	trans.y -= (height * 0.5);
+	break;
+    case TK_ANCHOR_SE:		/* Lower right corner */
+	trans.x -= width;
+	trans.y -= height;
+	break;
+    }
+    return trans;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_CreateTextBitmap --
+ *
+ *	Draw a bitmap, using the the given window coordinates
+ *	as an anchor for the text bounding box.
+ *
+ * Results:
+ *	Returns the bitmap representing the text string.
+ *
+ * Side Effects:
+ *	Bitmap is drawn using the given font and GC in the
+ *	drawable at the given coordinates, anchor, and rotation.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, bmWidthPtr, bmHeightPtr)
+    Tk_Window tkwin;
+    TextLayout *textPtr;	/* Text string to draw */
+    TextStyle *tsPtr;		/* Text attributes: rotation, color, font,
+				 * linespacing, justification, etc. */
+    int *bmWidthPtr;
+    int *bmHeightPtr;		/* Extents of rotated text string */
+{
+    int width, height;
+    Pixmap bitmap;
+    Display *display;
+    Window root;
+    GC gc;
+#ifdef WIN32
+    HDC hDC;
+    TkWinDCState state;
+#endif
+    display = Tk_Display(tkwin);
+
+    width = textPtr->width;
+    height = textPtr->height;
+
+    /* Create a temporary bitmap to contain the text string */
+    root = RootWindow(display, Tk_ScreenNumber(tkwin));
+    bitmap = Tk_GetPixmap(display, root, width, height, 1);
+    assert(bitmap != None);
+    if (bitmap == None) {
+	return None;		/* Can't allocate pixmap. */
+    }
+    /* Clear the pixmap and draw the text string into it */
+    gc = Blt_GetBitmapGC(tkwin);
+#ifdef WIN32
+    hDC = TkWinGetDrawableDC(display, bitmap, &state);
+    PatBlt(hDC, 0, 0, width, height, WHITENESS);
+    TkWinReleaseDrawableDC(bitmap, hDC, &state);
+#else
+    XSetForeground(display, gc, 0);
+    XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+#endif /* WIN32 */
+
+    XSetFont(display, gc, Tk_FontId(tsPtr->font));
+    XSetForeground(display, gc, 1);
+    DrawTextLayout(display, bitmap, gc, tsPtr->font, 0, 0, textPtr);
+
+#ifdef WIN32
+    /*
+     * Under Win32 when drawing into a bitmap, the bits are
+     * reversed. Which is why we are inverting the bitmap here.
+     */
+    hDC = TkWinGetDrawableDC(display, bitmap, &state);
+    PatBlt(hDC, 0, 0, width, height, DSTINVERT);
+    TkWinReleaseDrawableDC(bitmap, hDC, &state);
+#endif
+    if (tsPtr->theta != 0.0) {
+	Pixmap rotBitmap;
+
+	/* Replace the text pixmap with a rotated one */
+
+	rotBitmap = Blt_RotateBitmap(tkwin, bitmap, width, height, 
+		tsPtr->theta, bmWidthPtr, bmHeightPtr);
+	assert(rotBitmap);
+	if (rotBitmap != None) {
+	    Tk_FreePixmap(display, bitmap);
+	    return rotBitmap;
+	}
+    }
+    *bmWidthPtr = textPtr->width, *bmHeightPtr = textPtr->height;
+    return bitmap;
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InitTextStyle(tsPtr)
+    TextStyle *tsPtr;
+{
+    /* Initialize these attributes to zero */
+    tsPtr->activeColor = (XColor *)NULL;
+    tsPtr->anchor = TK_ANCHOR_CENTER;
+    tsPtr->color = (XColor *)NULL;
+    tsPtr->font = NULL;
+    tsPtr->justify = TK_JUSTIFY_CENTER;
+    tsPtr->leader = 0;
+    tsPtr->padLeft = tsPtr->padRight = 0;
+    tsPtr->padTop = tsPtr->padBottom = 0;
+    tsPtr->shadow.color = (XColor *)NULL;
+    tsPtr->shadow.offset = 0;
+    tsPtr->state = 0;
+    tsPtr->theta = 0.0;
+}
+
+void
+Blt_SetDrawTextStyle(tsPtr, font, gc, normalColor, activeColor, shadowColor, 
+	theta, anchor, justify, leader, shadowOffset)
+    TextStyle *tsPtr;
+    Tk_Font font;
+    GC gc;
+    XColor *normalColor, *activeColor, *shadowColor;
+    double theta;
+    Tk_Anchor anchor;
+    Tk_Justify justify;
+    int leader, shadowOffset;
+{
+    Blt_InitTextStyle(tsPtr);
+    tsPtr->activeColor = activeColor;
+    tsPtr->anchor = anchor;
+    tsPtr->color = normalColor;
+    tsPtr->font = font;
+    tsPtr->gc = gc;
+    tsPtr->justify = justify;
+    tsPtr->leader = leader;
+    tsPtr->shadow.color = shadowColor;
+    tsPtr->shadow.offset = shadowOffset;
+    tsPtr->theta = theta;
+}
+
+void
+Blt_SetPrintTextStyle(tsPtr, font, fgColor, activeColor, shadowColor, theta, 
+	anchor, justify, leader, shadowOffset)
+    TextStyle *tsPtr;
+    Tk_Font font;
+    XColor *fgColor, *activeColor, *shadowColor;
+    double theta;
+    Tk_Anchor anchor;
+    Tk_Justify justify;
+    int leader, shadowOffset;
+{
+    Blt_InitTextStyle(tsPtr);
+    tsPtr->color = fgColor;
+    tsPtr->activeColor = activeColor;
+    tsPtr->shadow.color = shadowColor;
+    tsPtr->font = font;
+    tsPtr->theta = theta;
+    tsPtr->anchor = anchor;
+    tsPtr->justify = justify;
+    tsPtr->leader = leader;
+    tsPtr->shadow.offset = shadowOffset;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_DrawTextLayout --
+ *
+ *	Draw a text string, possibly rotated, using the the given
+ *	window coordinates as an anchor for the text bounding box.
+ *	If the text is not rotated, simply use the X text drawing
+ *	routines. Otherwise, generate a bitmap of the rotated text.
+ *
+ * Results:
+ *	Returns the x-coordinate to the right of the text.
+ *
+ * Side Effects:
+ *	Text string is drawn using the given font and GC at the
+ *	the given window coordinates.
+ *
+ *      The Stipple, FillStyle, and TSOrigin fields of the GC are
+ *      modified for rotated text.  This assumes the GC is private,
+ *      *not* shared (via Tk_GetGC)
+ *
+ * -----------------------------------------------------------------
+ */
+void
+Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y)
+    Tk_Window tkwin;
+    Drawable drawable;
+    TextLayout *textPtr;
+    TextStyle *tsPtr;		/* Text attribute information */
+    int x, y;			/* Window coordinates to draw text */
+{
+    int width, height;
+    double theta;
+    Display *display;
+    Pixmap bitmap;
+    int active;
+
+    if (!textPtr)
+	return;
+    
+    display = Tk_Display(tkwin);
+    theta = FMOD(tsPtr->theta, (double)360.0);
+    if (theta < 0.0) {
+	theta += 360.0;
+    }
+    active = tsPtr->state & STATE_ACTIVE;
+    if (theta == 0.0) {
+
+	/*
+	 * This is the easy case of no rotation. Simply draw the text
+	 * using the standard drawing routines.  Handle offset printing
+	 * for engraved (disabled) and shadowed text.
+	 */
+	width = textPtr->width, height = textPtr->height;
+	Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
+	if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+	    TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+	    XColor *color1, *color2;
+
+	    color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+	    if (tsPtr->state & STATE_EMPHASIS) {
+		XColor *hold;
+
+		hold = color1, color1 = color2, color2 = hold;
+	    }
+	    if (color1 != NULL) {
+		XSetForeground(display, tsPtr->gc, color1->pixel);
+	    }
+	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x + 1, 
+		y + 1, textPtr);
+	    if (color2 != NULL) {
+		XSetForeground(display, tsPtr->gc, color2->pixel);
+	    }
+	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y, 
+		textPtr);
+
+	    /* Reset the foreground color back to its original setting,
+	     * so not to invalidate the GC cache. */
+	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+
+	    return;		/* Done */
+	}
+	if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+	    XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
+	    DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, 
+		   x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, textPtr);
+	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+	}
+	if (active) {
+	    XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
+	}
+	DrawTextLayout(display, drawable, tsPtr->gc, tsPtr->font, x, y, 
+		textPtr);
+	if (active) {
+	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+	}
+	return;			/* Done */
+    }
+#ifdef WIN32
+    if (Blt_DrawRotatedText(display, drawable, x, y, theta, tsPtr, textPtr)) {
+	return;
+    }
+#endif
+    /*
+     * Rotate the text by writing the text into a bitmap and rotating
+     * the bitmap.  Set the clip mask and origin in the GC first.  And
+     * make sure we restore the GC because it may be shared.
+     */
+    tsPtr->theta = theta;
+    bitmap = Blt_CreateTextBitmap(tkwin, textPtr, tsPtr, &width, &height);
+    if (bitmap == None) {
+	return;
+    }
+    Blt_TranslateAnchor(x, y, width, height, tsPtr->anchor, &x, &y);
+#ifdef notdef
+    theta = FMOD(theta, (double)90.0);
+#endif
+    XSetClipMask(display, tsPtr->gc, bitmap);
+
+    if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+	TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+	XColor *color1, *color2;
+
+	color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+	if (tsPtr->state & STATE_EMPHASIS) {
+	    XColor *hold;
+
+	    hold = color1, color1 = color2, color2 = hold;
+	}
+	if (color1 != NULL) {
+	    XSetForeground(display, tsPtr->gc, color1->pixel);
+	}
+	XSetClipOrigin(display, tsPtr->gc, x + 1, y + 1);
+	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, 
+		height, x + 1, y + 1, 1);
+	if (color2 != NULL) {
+	    XSetForeground(display, tsPtr->gc, color2->pixel);
+	}
+	XSetClipOrigin(display, tsPtr->gc, x, y);
+	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, 
+		height, x, y, 1);
+	XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+    } else {
+	if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+	    XSetClipOrigin(display, tsPtr->gc, x + tsPtr->shadow.offset,
+			   y + tsPtr->shadow.offset);
+	    XSetForeground(display, tsPtr->gc, tsPtr->shadow.color->pixel);
+	    XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, 
+		height, x + tsPtr->shadow.offset, y + tsPtr->shadow.offset, 1);
+	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+	}
+	if (active) {
+	    XSetForeground(display, tsPtr->gc, tsPtr->activeColor->pixel);
+	}
+	XSetClipOrigin(display, tsPtr->gc, x, y);
+	XCopyPlane(display, bitmap, drawable, tsPtr->gc, 0, 0, width, height, 
+		x, y, 1);
+	if (active) {
+	    XSetForeground(display, tsPtr->gc, tsPtr->color->pixel);
+	}
+    }
+    XSetClipMask(display, tsPtr->gc, None);
+    Tk_FreePixmap(display, bitmap);
+}
+
+void
+Blt_DrawText2(tkwin, drawable, string, tsPtr, x, y, areaPtr)
+    Tk_Window tkwin;
+    Drawable drawable;
+    char string[];
+    TextStyle *tsPtr;		/* Text attribute information */
+    int x, y;			/* Window coordinates to draw text */
+    Dim2D *areaPtr;
+{
+    TextLayout *textPtr;
+    int width, height;
+    double theta;
+
+    if ((string == NULL) || (*string == '\0')) {
+	return;			/* Empty string, do nothing */
+    }
+    textPtr = Blt_GetTextLayout(string, tsPtr);
+    Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
+    theta = FMOD(tsPtr->theta, (double)360.0);
+    if (theta < 0.0) {
+	theta += 360.0;
+    }
+    width = textPtr->width;
+    height = textPtr->height;
+    if (theta != 0.0) {
+	double rotWidth, rotHeight;
+
+	Blt_GetBoundingBox(width, height, theta, &rotWidth, &rotHeight, 
+	   (Point2D *)NULL);
+	width = ROUND(rotWidth);
+	height = ROUND(rotHeight);
+    }
+    areaPtr->width = width;
+    areaPtr->height = height;
+    Blt_Free(textPtr);
+}
+
+void
+Blt_DrawText(tkwin, drawable, string, tsPtr, x, y)
+    Tk_Window tkwin;
+    Drawable drawable;
+    char string[];
+    TextStyle *tsPtr;		/* Text attribute information */
+    int x, y;			/* Window coordinates to draw text */
+{
+    TextLayout *textPtr;
+
+    if ((string == NULL) || (*string == '\0')) {
+	return;			/* Empty string, do nothing */
+    }
+    textPtr = Blt_GetTextLayout(string, tsPtr);
+    Blt_DrawTextLayout(tkwin, drawable, textPtr, tsPtr, x, y);
+    Blt_Free(textPtr);
+}
+
+GC
+Blt_GetBitmapGC(tkwin)
+    Tk_Window tkwin;
+{
+    int isNew;
+    GC gc;
+    Display *display;
+    Blt_HashEntry *hPtr;
+
+    if (!initialized) {
+	Blt_InitHashTable(&bitmapGCTable, BLT_ONE_WORD_KEYS);
+	initialized = TRUE;
+    }
+    display = Tk_Display(tkwin);
+    hPtr = Blt_CreateHashEntry(&bitmapGCTable, (char *)display, &isNew);
+    if (isNew) {
+	Pixmap bitmap;
+	XGCValues gcValues;
+	unsigned long gcMask;
+	Window root;
+
+	root = RootWindow(display, Tk_ScreenNumber(tkwin));
+	bitmap = Tk_GetPixmap(display, root, 1, 1, 1);
+	gcValues.foreground = gcValues.background = 0;
+	gcMask = (GCForeground | GCBackground);
+	gc = Blt_GetPrivateGCFromDrawable(display, bitmap, gcMask, &gcValues);
+	Tk_FreePixmap(display, bitmap);
+	Blt_SetHashValue(hPtr, gc);
+    } else {
+	gc = (GC)Blt_GetHashValue(hPtr);
+    }
+    return gc;
+}
+
+void
+Blt_ResetTextStyle(tkwin, tsPtr)
+    Tk_Window tkwin;
+    TextStyle *tsPtr;
+{
+    GC newGC;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    gcMask = GCFont;
+    gcValues.font = Tk_FontId(tsPtr->font);
+    if (tsPtr->color != NULL) {
+	gcMask |= GCForeground;
+	gcValues.foreground = tsPtr->color->pixel;
+    }
+    newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+    if (tsPtr->gc != NULL) {
+	Tk_FreeGC(Tk_Display(tkwin), tsPtr->gc);
+    }
+    tsPtr->gc = newGC;
+}
+
+void
+Blt_FreeTextStyle(display, tsPtr)
+    Display *display;
+    TextStyle *tsPtr;
+{
+    if (tsPtr->gc != NULL) {
+	Tk_FreeGC(display, tsPtr->gc);
+    }
+}
Index: trunk/kitgen/8.x/blt/generic/bltText.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltText.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltText.h	(revision 175)
@@ -0,0 +1,212 @@
+/*
+ * bltText.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_TEXT_H
+#define _BLT_TEXT_H
+
+#if (TK_MAJOR_VERSION == 4)
+
+/*
+ * The following structure is used by Tk_GetFontMetrics() to return
+ * information about the properties of a Tk_Font.
+ */
+typedef struct {
+    int ascent;			/* The amount in pixels that the tallest
+				 * letter sticks up above the baseline, plus
+				 * any extra blank space added by the designer
+				 * of the font. */
+    int descent;		/* The largest amount in pixels that any
+				 * letter sticks below the baseline, plus any
+				 * extra blank space added by the designer of
+				 * the font. */
+    int linespace;		/* The sum of the ascent and descent.  How
+				 * far apart two lines of text in the same
+				 * font should be placed so that none of the
+				 * characters in one line overlap any of the
+				 * characters in the other line. */
+} Tk_FontMetrics;
+
+typedef XFontStruct *Tk_Font;
+
+#define Tk_FontId(font)			((font)->fid)
+#define Tk_TextWidth(font, str, len)	(XTextWidth((font),(str),(len)))
+#define Tk_GetFontMetrics(font, fmPtr)  \
+	((fmPtr)->ascent = (font)->ascent, \
+	(fmPtr)->descent = (font)->descent, \
+	(fmPtr)->linespace = (font)->ascent + (font)->descent)
+
+#define Tk_NameOfFont(font) (Tk_NameOfFontStruct(font))
+#define Tk_DrawChars(dpy, draw, gc, font, str, len, x, y) \
+    TkDisplayChars((dpy),(draw),(gc),(font),(str),(len),(x),(y), 0, DEF_TEXT_FLAGS)
+
+#define Tk_MeasureChars(font, text, len, maxPixels, flags, lenPtr) \
+    TkMeasureChars((font),(text), (len), 0, maxPixels, 0,(flags), (lenPtr))
+
+extern int TkMeasureChars _ANSI_ARGS_((Tk_Font font, char *source,
+	int maxChars, int startX, int maxX, int tabOrigin, int flags,
+	int *nextXPtr));
+extern void TkDisplayChars _ANSI_ARGS_((Display *display, Drawable drawable,
+	GC gc, Tk_Font font, char *string, int length, int x, int y,
+	int tabOrigin, int flags));
+/*
+ * FLAGS passed to TkMeasureChars:
+ */
+#define TK_WHOLE_WORDS			(1<<0)
+#define TK_AT_LEAST_ONE			(1<<1)
+#define TK_PARTIAL_OK			(1<<2)
+#define TK_IGNORE_NEWLINES		(1<<3)
+#define TK_IGNORE_TABS			(1<<4)
+#define NO_FLAGS			0
+
+#endif /* TK_MAJOR_VERSION == 4 */
+
+#define DEF_TEXT_FLAGS (TK_PARTIAL_OK | TK_IGNORE_NEWLINES)
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextFragment --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    char *text;			/* Text to be displayed */
+
+    short int x, y;		/* X-Y offset of the baseline from the
+				 * upper-left corner of the bbox. */
+
+    short int sx, sy;		/* See bltWinUtil.c */
+
+    short int count;		/* Number of bytes in text. The actual
+				 * character count may differ because of
+				 * multi-byte UTF encodings. */
+
+    short int width;		/* Width of segment in pixels. This
+				 * information is used to draw
+				 * PostScript strings the same width
+				 * as X. */
+} TextFragment;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextLayout --
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    int nFrags;			/* # fragments of text */
+    short int width, height;	/* Dimensions of text bounding box */
+    TextFragment fragArr[1];	/* Information about each fragment of text */
+} TextLayout;
+
+typedef struct {
+    XColor *color;
+    int offset;
+} Shadow;
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TextStyle --
+ *
+ * 	Represents a convenient structure to hold text attributes
+ *	which determine how a text string is to be displayed on the
+ *	window, or drawn with PostScript commands.  The alternative
+ *	is to pass lots of parameters to the drawing and printing
+ *	routines. This seems like a more efficient and less cumbersome
+ *	way of passing parameters.
+ *
+ * ----------------------------------------------------------------------
+ */
+typedef struct {
+    unsigned int state;		/* If non-zero, indicates to draw text
+				 * in the active color */
+    short int width, height;	/* Extents of text */
+
+    XColor *color;		/* Normal color */
+    XColor *activeColor;	/* Active color */
+    Tk_Font font;		/* Font to use to draw text */
+    Tk_3DBorder border;		/* Background color of text.  This is also
+				 * used for drawing disabled text. */
+    Shadow shadow;		/* Drop shadow color and offset */
+    Tk_Justify justify;		/* Justification of the text string. This
+				 * only matters if the text is composed
+				 * of multiple lines. */
+    GC gc;			/* GC used to draw the text */
+    double theta;		/* Rotation of text in degrees. */
+    Tk_Anchor anchor;		/* Indicates how the text is anchored around
+				 * its x and y coordinates. */
+    Blt_Pad padX, padY;		/* # pixels padding of around text region */
+    short int leader;		/* # pixels spacing between lines of text */
+
+} TextStyle;
+
+
+extern TextLayout *Blt_GetTextLayout _ANSI_ARGS_((char *string,
+	TextStyle *stylePtr));
+
+extern void Blt_GetTextExtents _ANSI_ARGS_((TextStyle *stylePtr,
+	char *text, int *widthPtr, int *heightPtr));
+
+extern void Blt_InitTextStyle _ANSI_ARGS_((TextStyle *stylePtr));
+
+extern void Blt_ResetTextStyle _ANSI_ARGS_((Tk_Window tkwin,
+	TextStyle *stylePtr));
+
+extern void Blt_FreeTextStyle _ANSI_ARGS_((Display *display,
+	TextStyle *stylePtr));
+
+extern void Blt_SetDrawTextStyle _ANSI_ARGS_((TextStyle *stylePtr,
+	Tk_Font font, GC gc, XColor *normalColor, XColor *activeColor,
+	XColor *shadowColor, double theta, Tk_Anchor anchor, Tk_Justify justify,
+	int leader, int shadowOffset));
+
+extern void Blt_SetPrintTextStyle _ANSI_ARGS_((TextStyle *stylePtr,
+	Tk_Font font, XColor *fgColor, XColor *bgColor, XColor *shadowColor,
+	double theta, Tk_Anchor anchor, Tk_Justify justify, int leader,
+	int shadowOffset));
+
+extern void Blt_DrawText _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	char *string, TextStyle *stylePtr, int x, int y));
+
+extern void Blt_DrawTextLayout _ANSI_ARGS_((Tk_Window tkwin,
+	Drawable drawable, TextLayout *textPtr, TextStyle *stylePtr,
+	int x, int y));
+
+extern void Blt_DrawText2 _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	char *string, TextStyle *stylePtr, int x, int y,
+	Dim2D * dimPtr));
+
+extern Pixmap Blt_CreateTextBitmap _ANSI_ARGS_((Tk_Window tkwin,
+	TextLayout *textPtr, TextStyle *stylePtr, int *widthPtr,
+	int *heightPtr));
+
+extern int Blt_DrawRotatedText _ANSI_ARGS_((Display *display,
+	Drawable drawable, int x, int y, double theta,
+	TextStyle *stylePtr, TextLayout *textPtr));
+
+#endif /* _BLT_TEXT_H */
Index: trunk/kitgen/8.x/blt/generic/bltTile.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTile.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTile.c	(revision 175)
@@ -0,0 +1,1296 @@
+/*
+ * bltTile.c --
+ *
+ *	This module manages images for tiled backgrounds for the BLT toolkit.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+#include "bltHash.h"
+#include "bltImage.h"
+#include <X11/Xutil.h>
+
+#include "bltTile.h"
+
+#define TILE_THREAD_KEY	"BLT Tile Data"
+#define TILE_MAGIC ((unsigned int) 0x46170277)
+
+typedef struct {
+    Blt_HashTable tileTable;	/* Hash table of tile structures keyed by 
+				 * the name of the image. */
+    Tcl_Interp *interp;
+} TileInterpData;
+
+typedef struct {
+    char *name;			/* Name of image used to generate the pixmap.*/
+    Display *display;		/* Display where pixmap was created. */
+    int flags;			/* See definitions below. */
+    Tcl_Interp *interp;
+    Blt_HashEntry *hashPtr;	/* Pointer to hash table location */
+    Blt_HashTable *tablePtr;
+
+    Pixmap pixmap;		/* Pixmap generated from image. */
+    Pixmap mask;		/* Monochrome pixmap used as
+				 * transparency mask. */
+    GC gc;			/* GC */
+    Tk_Image tkImage;		/* Tk image token. */
+    Blt_Chain *clients;		/* Chain of clients sharing this tile. */
+    int width, height;		
+} Tile;
+
+#define NOTIFY_PENDING	1	/* If set, indicates that the image
+				 * associated with the tile has been
+				 * updated or deleted.  The tile pixmap
+				 * will be changed and the clients of the
+				 * tile will be notified (if they supplied
+				 * a TileChangedProc routine. */
+
+typedef struct Blt_TileClientStruct {
+    unsigned int magic;
+    Tk_Window tkwin;		/* Client window. */
+    int xOrigin, yOrigin;	/* Tiling origin in relation to the
+				 * client window. */
+    Blt_TileChangedProc *notifyProc; /* If non-NULL, routine to
+				 * call to when tile image changes. */
+    ClientData clientData;	/* Data to pass to when calling the above
+				 * routine. */
+    Tile *tilePtr;		/* Pointer to actual tile information */
+    Blt_ChainLink *linkPtr;	/* Pointer to client entry in the server's
+				 * client list.  Used to delete the client */
+} TileClient;
+
+typedef struct {
+    Display *display;
+    Tk_Uid nameId;
+    int depth;
+} TileKey;
+    
+static TileInterpData *GetTileInterpData _ANSI_ARGS_((Tcl_Interp *interp));
+
+static Tcl_IdleProc UpdateTile;
+static Tk_ImageChangedProc ImageChangedProc;
+static Tcl_InterpDeleteProc TileInterpDeleteProc;
+
+static void DestroyClient _ANSI_ARGS_((TileClient *clientPtr));
+static void DestroyTile _ANSI_ARGS_((Tile *tilePtr));
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RedrawTile --
+ *
+ *	Generates a pixmap and draws the tile image into it.  Also
+ *	a tranparency mask is possibly generated from the image.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+RedrawTile(tkwin, tilePtr)
+    Tk_Window tkwin;
+    Tile *tilePtr;
+{
+    GC newGC;
+    Tk_PhotoHandle photo;
+    XGCValues gcValues;
+    int width, height;
+    unsigned int gcMask;
+    
+    Tk_SizeOfImage(tilePtr->tkImage, &width, &height);
+
+    Tk_MakeWindowExist(tkwin);
+    if ((width != tilePtr->width) || (height != tilePtr->height)) {
+	Pixmap pixmap;
+
+	/*
+	 * Create the new pixmap *before* destroying the old one.  I don't
+	 * why this happens, but if you delete the old pixmap first, the
+	 * old pixmap sometimes gets used in the client's GCs.  I suspect
+	 * it has something to do with the way Tk reallocates X resource
+	 * identifiers.  
+	 */
+	pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), width, 
+			      height, Tk_Depth(tkwin));
+	if (tilePtr->pixmap != None) {
+	    Tk_FreePixmap(Tk_Display(tkwin), tilePtr->pixmap);
+	}
+	tilePtr->pixmap = pixmap;
+    }
+    Tk_RedrawImage(tilePtr->tkImage, 0, 0, width, height, tilePtr->pixmap, 
+	0, 0);
+    
+    gcMask = (GCTile | GCFillStyle);
+    gcValues.fill_style = FillTiled;
+    gcValues.tile = tilePtr->pixmap;
+    newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+    if (tilePtr->gc != NULL) {
+	Tk_FreeGC(Tk_Display(tkwin), tilePtr->gc);
+    }
+    tilePtr->gc = newGC;
+    tilePtr->width = width;
+    tilePtr->height = height;
+
+    if (tilePtr->mask != None) {
+#ifdef WIN32
+	Tk_FreePixmap(Tk_Display(tkwin), tilePtr->mask);
+#else 
+	XFreePixmap(Tk_Display(tkwin), tilePtr->mask);
+#endif /* WIN32 */
+	tilePtr->mask = None;
+    }
+    photo = Blt_FindPhoto(tilePtr->interp, 
+			  Blt_NameOfImage(tilePtr->tkImage));
+    if (photo != NULL) {
+	Tk_PhotoImageBlock src;
+
+	Tk_PhotoGetImage(photo, &src);
+	if ((src.offset[3] < src.pixelSize) && (src.offset[3] >= 0)) {
+	    tilePtr->mask = Blt_PhotoImageMask(tkwin, src);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateTile --
+ *
+ *	It would be better if Tk checked for NULL proc pointers.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UpdateTile(clientData)
+    ClientData clientData;
+{
+    Tile *tilePtr = (Tile *)clientData;
+    TileClient *clientPtr;
+    Blt_ChainLink *linkPtr;
+
+    tilePtr->flags &= ~NOTIFY_PENDING;
+    if (Tk_ImageIsDeleted(tilePtr->tkImage)) {
+	if (tilePtr->pixmap != None) {
+	    Tk_FreePixmap(tilePtr->display, tilePtr->pixmap);
+	}
+	tilePtr->pixmap = None;
+    } else {
+	/* Pick any client window to generate the new pixmap. */
+	linkPtr = Blt_ChainFirstLink(tilePtr->clients);
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	RedrawTile(clientPtr->tkwin, tilePtr);
+    }
+
+    /* Notify each of the tile's clients that the pixmap has changed. */
+
+    for (linkPtr = Blt_ChainFirstLink(tilePtr->clients); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	if (clientPtr->notifyProc != NULL) {
+	    (*clientPtr->notifyProc) (clientPtr->clientData, clientPtr);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *	The Tk image has changed or been deleted, redraw the pixmap
+ *	tile.
+ *
+ *	Note:	As of Tk 4.2 (rechecked in 8.3), if you redraw Tk 
+ *		images from a Tk_ImageChangedProc you'll get a 
+ *		coredump.  As a workaround, we have to simulate 
+ *		how the Tk widgets use images and redraw within 
+ *		an idle event.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight)
+    ClientData clientData;
+    int x, y, width, height;	/* Not used. */
+    int imageWidth, imageHeight; /* Not used. */
+{
+    Tile *tilePtr = (Tile *) clientData;
+
+    if (!(tilePtr->flags & NOTIFY_PENDING)) {
+	Tcl_DoWhenIdle(UpdateTile, tilePtr);
+	tilePtr->flags |= NOTIFY_PENDING;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyTile --
+ *
+ *	Deletes the core tile structure, including the pixmap
+ *	representing the tile.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyTile(Tile *tilePtr)
+{
+    Blt_ChainLink *linkPtr;
+    TileClient *clientPtr;
+
+    if (tilePtr->flags & NOTIFY_PENDING) {
+	Tcl_CancelIdleCall(UpdateTile, tilePtr);
+    }
+    for (linkPtr = Blt_ChainFirstLink(tilePtr->clients); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	Blt_Free(clientPtr);
+    }
+    Blt_ChainDestroy(tilePtr->clients);
+
+    if (tilePtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(tilePtr->tablePtr, tilePtr->hashPtr);
+    }
+    if (tilePtr->pixmap != None) {
+	Tk_FreePixmap(tilePtr->display, tilePtr->pixmap);
+    }
+    Tk_FreeImage(tilePtr->tkImage);
+
+    if (tilePtr->gc != NULL) {
+	Tk_FreeGC(tilePtr->display, tilePtr->gc);
+    }
+    if (tilePtr->name != NULL) {
+	Blt_Free(tilePtr->name);
+    }
+    Blt_Free(tilePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateTile --
+ *
+ *	Creates a tile server.  A tile server manages a single image,
+ *	possibly shared by several clients.  Clients will be updated
+ *	(if requested) by the server if the image changes, so they
+ *	know to redraw themselves.  For X11 the image is drawn into a
+ *	pixmap that is used in a new GC as its tile.  For Windows we
+ *	have to do the tiling ourselves by redrawing the image across
+ *	the drawing area (see Blt_TileRectangle and Blt_TilePolygon).
+ *
+ * Results:
+ *	Returns a pointer to the new tile server.  If the image name
+ *	does not represent a Tk image, NULL is returned.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Tile *
+CreateTile(
+    Tcl_Interp *interp,
+    Tk_Window tkwin,
+    char *imageName)
+{
+    Tile *tilePtr;
+    Tk_Image tkImage;
+
+    tilePtr = Blt_Calloc(1, sizeof(Tile));
+    assert(tilePtr);
+    /*
+     * Get the image. Funnel all change notifications to a single routine.
+     */
+    tkImage = Tk_GetImage(interp, tkwin, imageName, ImageChangedProc,
+	tilePtr);
+    if (tkImage == NULL) {
+	Blt_Free(tilePtr);
+	return NULL;
+    }
+
+    /*
+     * Initialize the tile server.
+     */
+    tilePtr->display = Tk_Display(tkwin);
+    tilePtr->interp = interp;
+    tilePtr->name = Blt_Strdup(imageName);
+    tilePtr->clients = Blt_ChainCreate();
+    tilePtr->tkImage = tkImage;
+    RedrawTile(tkwin, tilePtr);
+    return tilePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyClient --
+ *
+ *	Removes the client from the servers's list of clients and
+ *	memory used by the client token is released.  When the last
+ *	client is deleted, the server is also removed.
+ *
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+DestroyClient(TileClient *clientPtr)
+{
+    Tile *tilePtr;
+    tilePtr = clientPtr->tilePtr;
+
+    /* Remove the client from the server's list */
+    if (clientPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(tilePtr->clients, clientPtr->linkPtr);
+    }
+    if (Blt_ChainGetLength(tilePtr->clients) == 0) {
+	/*
+	 * If there are no more clients of the tile, then remove the
+	 * pixmap, image, and the server record.
+	 */
+	DestroyTile(tilePtr);
+    }
+    Blt_Free(clientPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateClient --
+ *
+ *	Returns a token to a tile (possibly shared by many clients).
+ *	A client uses the token to query or display the tile.  Clients
+ *	request tiles by their image names.  Each tile is known by its
+ *	display, screen depth, and image name.  The tile server tracks
+ *	what clients are using the tile and notifies them (via a
+ *	callback) whenever the tile changes. If no server exists
+ *	already, one is created on-the-fly.
+ *
+ * Results:
+ *	A pointer to the newly created client (i.e. tile).
+ *
+ *---------------------------------------------------------------------- 
+ */
+static TileClient *
+CreateClient(
+    Tcl_Interp *interp,
+    Tk_Window tkwin,
+    char *name)
+{
+    TileClient *clientPtr;
+    Tile *tilePtr;
+    TileInterpData *dataPtr;
+    Blt_HashEntry *hPtr;
+    int isNew;
+    TileKey key;
+
+    dataPtr = GetTileInterpData(interp);
+
+    key.nameId = Tk_GetUid(name);
+    key.display = Tk_Display(tkwin);
+    key.depth = Tk_Depth(tkwin);
+    hPtr = Blt_CreateHashEntry(&dataPtr->tileTable, (char *)&key, &isNew);
+    if (isNew) {
+	tilePtr = CreateTile(interp, tkwin, name);
+	if (tilePtr == NULL) {
+	    Blt_DeleteHashEntry(&(dataPtr->tileTable), hPtr);
+	    return NULL;
+	}
+	tilePtr->hashPtr = hPtr;
+	tilePtr->tablePtr = &(dataPtr->tileTable);
+	Blt_SetHashValue(hPtr, tilePtr);
+    } else {
+	tilePtr = Blt_GetHashValue(hPtr);
+    }
+    clientPtr = Blt_Calloc(1, sizeof(TileClient));
+    assert(clientPtr);
+
+    /* Initialize client information. */
+    clientPtr->magic = TILE_MAGIC;
+    clientPtr->tkwin = tkwin;
+    clientPtr->linkPtr = Blt_ChainAppend(tilePtr->clients, clientPtr);
+    clientPtr->tilePtr = tilePtr;
+    return clientPtr;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TileInterpDeleteProc --
+ *
+ *	This is called when the interpreter is deleted. All the tiles
+ *	are specific to that interpreter are destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys the tile table.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TileInterpDeleteProc(
+    ClientData clientData,	/* Thread-specific data. */
+    Tcl_Interp *interp)
+{
+    TileInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Tile *tilePtr;
+    
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->tileTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	tilePtr = Blt_GetHashValue(hPtr);
+	tilePtr->hashPtr = NULL;
+	DestroyTile(tilePtr);
+    }
+    Blt_DeleteHashTable(&(dataPtr->tileTable));
+    Tcl_DeleteAssocData(interp, TILE_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+static TileInterpData *
+GetTileInterpData(interp)
+     Tcl_Interp *interp;
+{
+    TileInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (TileInterpData *)
+	Tcl_GetAssocData(interp, TILE_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(TileInterpData));
+	assert(dataPtr);
+	dataPtr->interp = interp;
+	Tcl_SetAssocData(interp, TILE_THREAD_KEY, TileInterpDeleteProc, 
+		dataPtr);
+	Blt_InitHashTable(&(dataPtr->tileTable), sizeof(TileKey)/sizeof(int));
+    }
+    return dataPtr;
+}
+
+
+
+/* Public API for tiles. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetTile
+ *
+ *	Convert the named image into a tile.
+ *
+ * Results:
+ *	If the image is valid, a new tile is returned.  If the name
+ *	does not represent a proper image, an error message is left in
+ *	interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_GetTile(
+    Tcl_Interp *interp,		/* Interpreter to report results back to */
+    Tk_Window tkwin,		/* Window on the same display as tile */
+    char *imageName,		/* Name of image */
+    Blt_Tile *tokenPtr)		/* (out) Returns the allocated tile token. */
+{
+    TileClient *clientPtr;
+
+    clientPtr = CreateClient(interp, tkwin, imageName);
+    if (clientPtr == NULL) {
+	return TCL_ERROR;
+    } 
+    *tokenPtr = clientPtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeTile
+ *
+ *	Release the resources associated with the tile.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Memory and X resources are freed.  Bookkeeping information
+ *	about the tile (i.e. width, height, and name) is discarded.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_FreeTile(TileClient *clientPtr) /* Tile to be deleted */
+{
+    if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+	return;			/* No tile */
+    }
+    DestroyClient(clientPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_NameOfTile
+ *
+ *	Returns the name of the image from which the tile was
+ *	generated.
+ *
+ * Results:
+ *	The name of the image is returned.  The name is not unique.
+ *	Many tiles may use the same image.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+char *
+Blt_NameOfTile(TileClient *clientPtr) /* Tile to query */
+{
+    if (clientPtr == NULL) {
+	return "";
+    }
+    if (clientPtr->magic != TILE_MAGIC) {
+	return "not a tile";
+    }
+    return clientPtr->tilePtr->name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PixmapOfTile
+ *
+ *	Returns the pixmap of the tile.
+ *
+ * Results:
+ *	The X pixmap used as the tile is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Pixmap
+Blt_PixmapOfTile(TileClient *clientPtr) /* Tile to query */
+{
+    if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+	return None;
+    }
+    return clientPtr->tilePtr->pixmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SizeOfTile
+ *
+ *	Returns the width and height of the tile.
+ *
+ * Results:
+ *	The width and height of the tile are returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_SizeOfTile(
+    TileClient *clientPtr,	/* Tile to query */
+    int *widthPtr, 
+    int *heightPtr)		/* Returned dimensions of the tile (out) */
+{
+    if ((clientPtr == NULL) || (clientPtr->magic != TILE_MAGIC)) {
+	*widthPtr = *heightPtr = 0;
+	return;			/* No tile given. */
+    }
+    *widthPtr = clientPtr->tilePtr->width;
+    *heightPtr = clientPtr->tilePtr->height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SetTileChangedProc
+ *
+ *	Sets the routine to called when an image changes.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The designated routine will be called the next time the
+ *	image associated with the tile changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+void
+Blt_SetTileChangedProc(
+    TileClient *clientPtr,		/* Tile to query */
+    Blt_TileChangedProc *notifyProc,
+    ClientData clientData)
+{
+    if ((clientPtr != NULL) && (clientPtr->magic == TILE_MAGIC)) {
+	clientPtr->notifyProc = notifyProc;
+	clientPtr->clientData = clientData;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_SetTileOrigin --
+ *
+ *	Set the pattern origin of the tile to a common point (i.e. the
+ *	origin (0,0) of the top level window) so that tiles from two
+ *	different widgets will match up.  This done by setting the
+ *	GCTileStipOrigin field is set to the translated origin of the
+ *	toplevel window in the hierarchy.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The GCTileStipOrigin is reset in the GC.  This will cause the
+ *	tile origin to change when the GC is used for drawing.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+void
+Blt_SetTileOrigin(
+    Tk_Window tkwin,
+    TileClient *clientPtr,
+    int x, int y)
+{
+    while (!Tk_IsTopLevel(tkwin)) {
+	x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+	y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+	tkwin = Tk_Parent(tkwin);
+    }
+    XSetTSOrigin(Tk_Display(tkwin), clientPtr->tilePtr->gc, -x, -y);
+    clientPtr->xOrigin = -x;
+    clientPtr->yOrigin = -y;
+}
+
+void
+Blt_SetTSOrigin(
+    Tk_Window tkwin,
+    TileClient *clientPtr,
+    int x, int y)
+{
+    XSetTSOrigin(Tk_Display(tkwin), clientPtr->tilePtr->gc, x, y);
+    clientPtr->xOrigin = x;
+    clientPtr->yOrigin = y;
+}
+
+#ifdef WIN32
+static int tkpWinRopModes[] =
+{
+    R2_BLACK,			/* GXclear */
+    R2_MASKPEN,			/* GXand */
+    R2_MASKPENNOT,		/* GXandReverse */
+    R2_COPYPEN,			/* GXcopy */
+    R2_MASKNOTPEN,		/* GXandInverted */
+    R2_NOT,			/* GXnoop */
+    R2_XORPEN,			/* GXxor */
+    R2_MERGEPEN,		/* GXor */
+    R2_NOTMERGEPEN,		/* GXnor */
+    R2_NOTXORPEN,		/* GXequiv */
+    R2_NOT,			/* GXinvert */
+    R2_MERGEPENNOT,		/* GXorReverse */
+    R2_NOTCOPYPEN,		/* GXcopyInverted */
+    R2_MERGENOTPEN,		/* GXorInverted */
+    R2_NOTMASKPEN,		/* GXnand */
+    R2_WHITE			/* GXset */
+};
+#define MASKPAT		0x00E20746 /* dest = (src & pat) | (!src & dst) */
+#define COPYFG		0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG		0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+
+static void
+TileRegion(
+    HDC srcDC,			/* Source device context. */
+    HDC destDC,			/* Destination device context. */
+    HDC maskDC,			/* If non-NULL, device context of the
+				 * mask tile mask. */
+    TileClient *clientPtr,
+    int x, int y, 
+    int width, int height)
+{
+    Tile *tilePtr = clientPtr->tilePtr;
+    int destX, destY;
+    int destWidth, destHeight;
+    int srcX, srcY;
+    int startX, startY;		/* Starting upper left corner of region. */
+    int delta;
+    int left, top, right, bottom;
+
+    startX = x;
+    if (x < clientPtr->xOrigin) {
+	delta = (clientPtr->xOrigin - x) % tilePtr->width;
+	if (delta > 0) {
+	    startX -= (tilePtr->width - delta);
+	}
+    } else if (x > clientPtr->xOrigin) {
+	delta = (x - clientPtr->xOrigin) % tilePtr->width;
+	if (delta > 0) {
+	    startX -= delta;
+	}
+    }
+    startY = y;
+    if (y < clientPtr->yOrigin) {
+	delta = (clientPtr->yOrigin - y) % tilePtr->height;
+	if (delta > 0) {
+	    startY -= (tilePtr->height - delta);
+	}
+    } else if (y >= clientPtr->yOrigin) {
+	delta = (y - clientPtr->yOrigin) % tilePtr->height;
+	if (delta > 0) {
+	    startY -= delta;
+	}
+    }
+#ifdef notdef
+    PurifyPrintf("tile is (%d,%d,%d,%d)\n", 
+		 clientPtr->xOrigin, clientPtr->yOrigin, 
+		 tilePtr->width, tilePtr->height);
+    PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+    PurifyPrintf("starting at %d,%d\n", startX, startY);
+#endif
+    left = x;
+    right = x + width;
+    top = y;
+    bottom = y + height;
+    for (y = startY; y < bottom; y += tilePtr->height) {
+	srcY = 0;
+	destY = y;
+	destHeight = tilePtr->height;
+	if (y < top) {
+	    srcY = (top - y);
+	    destHeight = tilePtr->height - srcY;
+	    destY = top;
+	} 
+	if ((destY + destHeight) > bottom) {
+	    destHeight = (bottom - destY);
+	}
+	for (x = startX; x < right; x += tilePtr->width) {
+	    srcX = 0;
+	    destX = x;
+	    destWidth = tilePtr->width;
+	    if (x < left) {
+		srcX = (left - x);
+		destWidth = tilePtr->width - srcX;
+		destX = left;
+	    } 
+	    if ((destX + destWidth) > right) {
+		destWidth = (right - destX);
+	    }
+#ifdef notdef
+	    PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+		 srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+	    if (tilePtr->mask != None) { /* With transparency. */
+#ifdef notdef
+		HDC maskDC;
+		TkWinDCState maskState;
+
+		maskDC = TkWinGetDrawableDC(tilePtr->display, 
+			tilePtr->mask, &maskState);
+		SetBkColor(destDC, RGB(255, 255, 255));      
+		SetTextColor(destDC, RGB(0, 0, 0));
+#endif
+		BitBlt(destDC, destX, destY, destWidth, destHeight, maskDC, 
+		       0, 0, SRCAND);
+		BitBlt(destDC, destX, destY, destWidth, destHeight, srcDC, 
+		       srcX, srcY, SRCPAINT);
+#ifdef notdef
+		TkWinReleaseDrawableDC(tilePtr->mask, maskDC, &maskState);
+#endif
+	    } else {		/* Opaque tile. */
+	        BitBlt(destDC, destX, destY, destWidth, destHeight, 
+		       srcDC, srcX, srcY, SRCCOPY);
+	    }
+	}
+    }
+}
+
+void
+Blt_TilePolygon(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    XPoint pointArr[],
+    int nPoints)
+{
+    HBITMAP oldBitmap;
+    HDC hDC, memDC;
+    HRGN hRgn;
+    POINT *p, *winPts;
+    Region2D bbox;
+    Tile *tilePtr;
+    TkWinDCState state;
+    TkWinDrawable *twdPtr;
+    XPoint *endPtr, *pointPtr;
+    int fillMode;    
+    int width, height;
+    
+    if (drawable == None) {
+	return;
+    }
+    tilePtr = clientPtr->tilePtr;
+
+    /* Determine the bounding box of the polygon. */
+    bbox.left = bbox.right = pointArr[0].x;
+    bbox.top = bbox.bottom = pointArr[0].y;
+    
+    endPtr = pointArr + nPoints;
+    for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+	if (pointPtr->x < bbox.left) {
+	    bbox.left = pointPtr->x;
+	} 
+	if (pointPtr->x > bbox.right) {
+	    bbox.right = pointPtr->x;
+	}
+	if (pointPtr->y < bbox.top) {
+	    bbox.top = pointPtr->y;
+	} 
+	if (pointPtr->y > bbox.bottom) {
+	    bbox.bottom = pointPtr->y;
+	}
+    }
+    width = bbox.right - bbox.left + 1;
+    height = bbox.bottom - bbox.top + 1;
+
+
+    /* Allocate and fill an array of POINTS to create the polygon path. */
+    p = winPts = Blt_Malloc(sizeof(POINT) * nPoints);
+    for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+	p->x = pointPtr->x - bbox.left;
+	p->y = pointPtr->y - bbox.top;
+	p++;
+    }
+
+    hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+    SetROP2(hDC, tkpWinRopModes[tilePtr->gc->function]);
+    fillMode = (tilePtr->gc->fill_rule == EvenOddRule) ? ALTERNATE : WINDING;
+    /* Use the polygon as a clip path. */
+    LPtoDP(hDC, winPts, nPoints);
+    hRgn = CreatePolygonRgn(winPts, nPoints, fillMode);
+    SelectClipRgn(hDC, hRgn);
+    OffsetClipRgn(hDC, bbox.left, bbox.top);
+    Blt_Free(winPts);
+
+    twdPtr = (TkWinDrawable *)tilePtr->pixmap;
+    memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+    
+    /* Tile the bounding box. */
+    if (tilePtr->mask != None) {
+	TkWinDCState maskState;
+	HDC maskDC;
+
+	maskDC = TkWinGetDrawableDC(tilePtr->display, tilePtr->mask, 
+	    &maskState);
+	SetBkColor(hDC, RGB(255, 255, 255));      
+	SetTextColor(hDC, RGB(0, 0, 0));
+	TileRegion(memDC, hDC, maskDC, clientPtr, bbox.left, bbox.top, width, 
+		   height);
+	TkWinReleaseDrawableDC(tilePtr->mask, maskDC, &maskState);
+    } else {
+	TileRegion(memDC, hDC, NULL, clientPtr, bbox.left, bbox.top, width, 
+	   height);
+    }
+    SelectBitmap(memDC, oldBitmap);
+    DeleteDC(memDC);
+    SelectClipRgn(hDC, NULL);
+    DeleteRgn(hRgn);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_TileRectangle(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    int x, int y,
+    unsigned int width, 
+    unsigned int height)
+{
+    HBITMAP oldBitmap;
+    HDC hDC, memDC;
+    Tile *tilePtr;
+    TkWinDCState state;
+    TkWinDrawable *twdPtr;
+
+    if (drawable == None) {
+	return;
+    }
+    tilePtr = clientPtr->tilePtr;
+    hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+    SetROP2(hDC, tkpWinRopModes[tilePtr->gc->function]);
+
+    twdPtr = (TkWinDrawable *)tilePtr->pixmap;
+    memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+
+    /* Tile the bounding box. */
+    if (tilePtr->mask != None) {
+	TkWinDCState maskState;
+	HDC maskDC;
+
+	maskDC = TkWinGetDrawableDC(tilePtr->display, tilePtr->mask, 
+	    &maskState);
+	SetBkColor(hDC, RGB(255, 255, 255));      
+	SetTextColor(hDC, RGB(0, 0, 0));
+	TileRegion(memDC, hDC, maskDC, clientPtr, x, y, width, height);
+	TkWinReleaseDrawableDC(tilePtr->mask, maskDC, &maskState);
+    } else {
+	TileRegion(memDC, hDC, NULL, clientPtr, x, y, width, height);
+    }
+    SelectBitmap(memDC, oldBitmap);
+    DeleteDC(memDC);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_TileRectangles(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    XRectangle rectArr[],
+    int nRectangles)
+{
+    HBITMAP oldBitmap;
+    HDC hDC, memDC;
+    Tile *tilePtr;
+    TkWinDCState state;
+    TkWinDrawable *twdPtr;
+    XRectangle *rectPtr, *endPtr;
+
+    if (drawable == None) {
+	return;
+    }
+    tilePtr = clientPtr->tilePtr;
+    hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+    SetROP2(hDC, tkpWinRopModes[tilePtr->gc->function]);
+
+    twdPtr = (TkWinDrawable *)tilePtr->pixmap;
+    memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+
+    endPtr = rectArr + nRectangles;
+    /* Tile the bounding box. */
+    if (tilePtr->mask != None) {
+	TkWinDCState maskState;
+	HDC maskDC;
+
+	maskDC = TkWinGetDrawableDC(tilePtr->display, tilePtr->mask, 
+	    &maskState);
+	SetBkColor(hDC, RGB(255, 255, 255));      
+	SetTextColor(hDC, RGB(0, 0, 0));
+	for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    TileRegion(memDC, hDC, maskDC, clientPtr, (int)rectPtr->x, 
+		(int)rectPtr->y, (int)rectPtr->width, (int)rectPtr->height);
+	}
+	TkWinReleaseDrawableDC(tilePtr->mask, maskDC, &maskState);
+    } else {
+	for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    TileRegion(memDC, hDC, NULL, clientPtr, (int)rectPtr->x, 
+		(int)rectPtr->y, (int)rectPtr->width, (int)rectPtr->height);
+	}
+    }
+    SelectBitmap(memDC, oldBitmap);
+    DeleteDC(memDC);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+#else 
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RectangleMask --
+ *
+ *	Creates a rectangular mask also stippled by the mask of the 
+ *	tile.  This is used to draw the tiled polygon images with 
+ *	transparent areas.
+ *
+ * Results:
+ *	A bitmap mask is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pixmap
+RectangleMask(display, drawable, x, y, width, height, mask, xOrigin, yOrigin)
+    Display *display;
+    Drawable drawable;
+    int x, y;
+    unsigned int width, height;
+    Pixmap mask;
+    int xOrigin, yOrigin;
+{
+    GC gc;
+    Pixmap bitmap;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    bitmap = Tk_GetPixmap(display, drawable, width, height, 1);
+    gcMask = (GCForeground | GCBackground | GCFillStyle | 
+	      GCTileStipXOrigin | GCTileStipYOrigin | GCStipple);
+    gcValues.foreground = 0x1;
+    gcValues.background = 0x0;
+    gcValues.fill_style = FillOpaqueStippled;
+    gcValues.ts_x_origin = xOrigin - x;
+    gcValues.ts_y_origin = yOrigin - y;
+    gcValues.stipple = mask;
+    gc = XCreateGC(display, bitmap, gcMask, &gcValues);
+    XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+    Blt_FreePrivateGC(display, gc);
+    return bitmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TileRectangle --
+ *
+ *	Draws a rectangle filled by a tiled image.  This differs from 
+ *	the normal XFillRectangle call in that we also try to handle 
+ *	a transparency mask. 
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws the rectangle.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TileRectangle(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    int x, int y,
+    unsigned int width, 
+    unsigned int height)
+{
+    Tile *tilePtr;
+    Display *display;
+
+    display = Tk_Display(tkwin);
+    tilePtr = clientPtr->tilePtr;
+    if (clientPtr->tilePtr->mask != None) {
+	Pixmap mask;
+
+	mask = RectangleMask(display, drawable, x, y, width, height,
+		tilePtr->mask, clientPtr->xOrigin, clientPtr->yOrigin);
+	XSetClipMask(display, tilePtr->gc, mask);
+	XSetClipOrigin(display, tilePtr->gc, x, y);
+	XFillRectangle(display, drawable, tilePtr->gc, x, y, width, height);
+	XSetClipMask(display, tilePtr->gc, None);
+	XSetClipOrigin(display, tilePtr->gc, 0, 0);
+	Tk_FreePixmap(display, mask);
+    } else {
+	XFillRectangle(display, drawable, tilePtr->gc, x, y, width, height);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TileRectangles --
+ *
+ *	Draws rectangles filled by a tiled image.  This differs from 
+ *	the normal XFillRectangles call in that we also try to handle 
+ *	a transparency mask. 
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws the given rectangles.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TileRectangles(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    XRectangle rectArr[],
+    int nRectangles)
+{
+    Tile *tilePtr;
+
+    tilePtr = clientPtr->tilePtr;
+    if (tilePtr->mask != None) {
+	XRectangle *rectPtr, *endPtr;
+
+	endPtr = rectArr + nRectangles;
+	for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    Blt_TileRectangle(tkwin, drawable, clientPtr, rectPtr->x, 
+		rectPtr->y, rectPtr->width, rectPtr->height);
+	}
+    } else {
+	XFillRectangles(Tk_Display(tkwin), drawable, tilePtr->gc, rectArr, 
+		nRectangles);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PolygonMask --
+ *
+ *	Creates a polygon shaped mask also stippled by the mask
+ *	of the tile.  This is used to draw the tiled polygon images
+ *	with transparent areas.
+ *
+ * Results:
+ *	A bitmap mask is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static Pixmap
+PolygonMask(display, pointArr, nPoints, regionPtr, mask, xOrigin, yOrigin)
+    Display *display;
+    XPoint *pointArr;
+    int nPoints;
+    Region2D *regionPtr;
+    Pixmap mask;
+    int xOrigin, yOrigin;
+{
+    unsigned int width, height;
+    Pixmap bitmap;
+    GC gc;
+    XPoint *destArr;
+    register XPoint *srcPtr, *destPtr, *endPtr;
+
+    width = regionPtr->right - regionPtr->left + 1;
+    height = regionPtr->bottom - regionPtr->top + 1;
+    bitmap = 
+	Tk_GetPixmap(display, DefaultRootWindow(display), width, height, 1);
+
+    destArr = Blt_Malloc(sizeof(XPoint) * nPoints);
+    endPtr = destArr + nPoints;
+    srcPtr = pointArr;
+    for (destPtr = destArr; destPtr < endPtr; destPtr++) {
+	destPtr->x = srcPtr->x - regionPtr->left;
+	destPtr->y = srcPtr->y - regionPtr->top;
+	srcPtr++;
+    }
+    gc = XCreateGC(display, bitmap, 0, NULL);
+    XFillRectangle(display, bitmap, gc, 0, 0, width, height);
+    XSetForeground(display, gc, 0x01);
+    XSetFillStyle(display, gc, FillStippled);
+    XSetTSOrigin(display, gc, xOrigin - regionPtr->left, 
+		 yOrigin - regionPtr->top);
+    XSetStipple(display, gc, mask);
+    XFillPolygon(display, bitmap, gc, destArr, nPoints, Complex, 
+		 CoordModeOrigin);
+    XFreeGC(display, gc);
+    Blt_Free(destArr);
+    return bitmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TilePolygon --
+ *
+ *	Draws a polygon filled by a tiled image.  This differs from 
+ *	the normal XFillPolygon call in that we also try to handle 
+ *	a transparency mask. 
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Draws the polygon.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TilePolygon(
+    Tk_Window tkwin,
+    Drawable drawable,
+    TileClient *clientPtr,
+    XPoint pointArr[],
+    int nPoints)
+{
+    Tile *tilePtr;
+    Display *display;
+    
+    display = Tk_Display(tkwin);
+    tilePtr = clientPtr->tilePtr;
+    if (tilePtr->mask != None) {
+	XPoint *pointPtr, *endPtr;
+	Region2D region;
+	Pixmap mask;
+
+	/* Determine the bounding box of the polygon. */
+	pointPtr = pointArr;
+	region.left = region.right = pointPtr->x;
+	region.top = region.bottom = pointPtr->y;
+	
+	endPtr = pointArr + nPoints;
+	for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+	    if (region.left > pointPtr->x) {
+		region.left = pointPtr->x;
+	    } else if (region.right < pointPtr->x) {
+		region.right = pointPtr->x;
+	    }
+	    if (region.top > pointPtr->y) {
+		region.top = pointPtr->y;
+	    } else if (region.bottom < pointPtr->y) {
+		region.bottom = pointPtr->y;
+	    }
+	}
+	mask = PolygonMask(display, pointArr, nPoints, &region, 
+		   tilePtr->mask, clientPtr->xOrigin, clientPtr->yOrigin);
+	XSetClipMask(display, tilePtr->gc, mask);
+	XSetClipOrigin(display, tilePtr->gc, region.left, region.top);
+	XFillPolygon(display, drawable, tilePtr->gc, pointArr, 
+		     nPoints, Complex, CoordModeOrigin);
+	XSetClipMask(display, tilePtr->gc, None);
+	XSetClipOrigin(display, tilePtr->gc, 0, 0);
+	Tk_FreePixmap(display, mask);
+    } else {
+	XFillPolygon(display, drawable, tilePtr->gc, pointArr, 
+		     nPoints, Complex, CoordModeOrigin);
+    }
+}
+#endif
Index: trunk/kitgen/8.x/blt/generic/bltTile.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTile.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTile.h	(revision 175)
@@ -0,0 +1,63 @@
+/*
+ * bltTile.h --
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef BLT_TILE_H
+#define BLT_TILE_H
+
+#define TILE_THREAD_KEY	"BLT Tile Data"
+#define TILE_MAGIC ((unsigned int) 0x46170277)
+
+typedef struct Blt_TileClientStruct *Blt_Tile; /* Opaque type for tiles */
+
+typedef void (Blt_TileChangedProc) _ANSI_ARGS_((ClientData clientData,
+	Blt_Tile tile));
+
+extern int Blt_GetTile _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin,
+	char *imageName, Blt_Tile *tilePtr));
+
+extern void Blt_FreeTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern char *Blt_NameOfTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern void Blt_SetTileChangedProc _ANSI_ARGS_((Blt_Tile tile,
+	Blt_TileChangedProc *changeProc, ClientData clientData));
+
+extern void Blt_TileRectangle _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	Blt_Tile tile, int x, int y, unsigned int width, unsigned int height));
+extern void Blt_TileRectangles _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable,
+	Blt_Tile tile, XRectangle *rectArr, int nRects));
+extern void Blt_TilePolygon _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, 
+	Blt_Tile tile, XPoint *pointArr, int nPoints));
+extern Pixmap Blt_PixmapOfTile _ANSI_ARGS_((Blt_Tile tile));
+
+extern void Blt_SizeOfTile _ANSI_ARGS_((Blt_Tile tile, int *widthPtr,
+	int *heightPtr));
+
+extern void Blt_SetTileOrigin _ANSI_ARGS_((Tk_Window tkwin, Blt_Tile tile, 
+	int x, int y));
+
+extern void Blt_SetTSOrigin _ANSI_ARGS_((Tk_Window tkwin, Blt_Tile tile, 
+	int x, int y));
+
+#endif /* BLT_TILE_H */
Index: trunk/kitgen/8.x/blt/generic/bltTkInt.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTkInt.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTkInt.h	(revision 175)
@@ -0,0 +1,240 @@
+/*
+ * bltTkInt.h --
+ *
+ *	Contains copies of internal Tk structures.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_TKINT_H
+#define _BLT_TKINT_H
+
+typedef struct {
+    Tk_Uid family;		/* Font family. The most important field. */
+    int pointsize;		/* Pointsize of font, 0 for default size, or
+				 * negative number meaning pixel size. */
+    int weight;			/* Weight flag; see below for def'n. */
+    int slant;			/* Slant flag; see below for def'n. */
+    int underline;		/* Non-zero for underline font. */
+    int overstrike;		/* Non-zero for overstrike font. */
+} TkFontAttributes;
+
+typedef struct {
+    int ascent;			/* From baseline to top of font. */
+    int descent;		/* From baseline to bottom of font. */
+    int maxWidth;		/* Width of widest character in font. */
+    int fixed;			/* Non-zero if this is a fixed-width font,
+				 * 0 otherwise. */
+} TkFontMetrics;
+
+
+typedef struct TkFontStruct {
+    /*
+     * Fields used and maintained exclusively by generic code.
+     */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+    int resourceRefCount;	/* Number of active uses of this font (each
+				 * active use corresponds to a call to
+				 * Tk_AllocFontFromTable or Tk_GetFont).
+				 * If this count is 0, then this TkFont
+				 * structure is no longer valid and it isn't
+				 * present in a hash table: it is being
+				 * kept around only because there are objects
+				 * referring to it.  The structure is freed
+				 * when resourceRefCount and objRefCount
+				 * are both 0. */
+    int objRefCount;		/* The number of Tcl objects that reference
+				 * this structure. */
+#else
+    int refCount;		/* Number of users of the TkFont. */
+#endif
+    Tcl_HashEntry *cacheHashPtr;/* Entry in font cache for this structure,
+				 * used when deleting it. */
+    Tcl_HashEntry *namedHashPtr;/* Pointer to hash table entry that
+				 * corresponds to the named font that the
+				 * tkfont was based on, or NULL if the tkfont
+				 * was not based on a named font. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+    Screen *screen;		/* The screen where this font is valid. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+    int tabWidth;		/* Width of tabs in this font (pixels). */
+    int underlinePos;		/* Offset from baseline to origin of
+				 * underline bar (used for drawing underlines
+				 * on a non-underlined font). */
+    int underlineHeight;	/* Height of underline bar (used for drawing
+				 * underlines on a non-underlined font). */
+
+    /*
+     * Fields in the generic font structure that are filled in by
+     * platform-specific code.
+     */
+
+    Font fid;			/* For backwards compatibility with XGCValues
+				 * structures.  Remove when TkGCValues is
+				 * implemented.  */
+    TkFontAttributes fa;	/* Actual font attributes obtained when the
+				 * the font was created, as opposed to the
+				 * desired attributes passed in to
+				 * TkpGetFontFromAttributes().  The desired
+				 * metrics can be determined from the string
+				 * that was used to create this font. */
+    TkFontMetrics fm;		/* Font metrics determined when font was
+				 * created. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+    struct TkFontStruct *nextPtr;	/* Points to the next TkFont structure with
+				 * the same name.  All fonts with the
+				 * same name (but different displays) are
+				 * chained together off a single entry in
+				 * a hash table. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+} TkFont;
+
+/*
+ * This structure is used by the Mac and Window porting layers as
+ * the internal representation of a clip_mask in a GC.
+ */
+typedef struct TkRegionStruct *TkRegion;
+
+typedef struct {
+    int type;			/* One of TKP_CLIP_PIXMAP or TKP_CLIP_REGION */
+    union {
+	Pixmap pixmap;
+	TkRegion region;
+    } value;
+} TkpClipMask;
+
+#define TKP_CLIP_PIXMAP 0
+#define TKP_CLIP_REGION 1
+
+#ifdef WIN32
+/*
+ * The TkWinDrawable is the internal implementation of an X Drawable (either
+ * a Window or a Pixmap).  The following constants define the valid Drawable
+ * types.
+ */
+
+#define TWD_BITMAP	1
+#define TWD_WINDOW	2
+#define TWD_WINDC	3
+
+typedef struct TkWindowStruct TkWindow;
+
+typedef struct {
+    int type;
+    HWND handle;
+    TkWindow *winPtr;
+} TkWinWindow;
+
+typedef struct {
+    int type;
+    HBITMAP handle;
+    Colormap colormap;
+    int depth;
+} TkWinBitmap;
+
+typedef struct {
+    int type;
+    HDC hdc;
+} TkWinDC;
+
+typedef union {
+    int type;
+    TkWinWindow window;
+    TkWinBitmap bitmap;
+    TkWinDC winDC;
+} TkWinDrawable;
+
+/*
+ * The TkWinDCState is used to save the state of a device context
+ * so that it can be restored later.
+ */
+
+typedef struct {
+    HPALETTE palette;
+    int bkmode;			/* This field was added in Tk
+				 * 8.3.1. Be careful that you don't 
+				 * use this structure in a context
+				 * where its size is important.  */
+} TkWinDCState;
+
+extern HDC TkWinGetDrawableDC(Display *display, Drawable drawable,
+    TkWinDCState * state);
+extern HDC TkWinReleaseDrawableDC(Drawable drawable, HDC dc,
+    TkWinDCState * state);
+
+extern HWND Tk_GetHWND _ANSI_ARGS_((Window window));
+
+extern HINSTANCE Tk_GetHINSTANCE _ANSI_ARGS_((void));
+
+extern Window Tk_AttachHWND _ANSI_ARGS_((Tk_Window tkwin, HWND hWnd));
+
+#endif /* WIN32 */
+
+/*
+ * The Border structure used internally by the Tk_3D* routines.
+ * The following is a copy of it from tk3d.c.
+ */
+
+typedef struct TkBorderStruct {
+    Screen *screen;		/* Screen on which the border will be used. */
+    Visual *visual;		/* Visual for all windows and pixmaps using
+				 * the border. */
+    int depth;			/* Number of bits per pixel of drawables where
+				 * the border will be used. */
+    Colormap colormap;		/* Colormap out of which pixels are
+				 * allocated. */
+    int refCount;		/* Number of different users of
+				 * this border.  */
+#if (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+    int objRefCount;		/* The number of Tcl objects that reference
+				 * this structure. */
+#endif /* TK_VERSION_NUMBER >= 8.1.0 */
+    XColor *bgColor;		/* Background color (intensity between 
+				 * lightColorPtr and darkColorPtr). */
+    XColor *darkColor;		/* Color for darker areas (must free when
+				 * deleting structure). NULL means shadows
+				 * haven't been allocated yet.*/
+    XColor *lightColor;		/* Color used for lighter areas of border
+				 * (must free this when deleting structure).
+				 * NULL means shadows haven't been allocated
+				 * yet. */
+    Pixmap shadow;		/* Stipple pattern to use for drawing
+				 * shadows areas.  Used for displays with
+				 * <= 64 colors or where colormap has filled
+				 * up. */
+    GC bgGC;			/* Used (if necessary) to draw areas in
+				 * the background color. */
+    GC darkGC;			/* Used to draw darker parts of the
+				 * border. None means the shadow colors
+				 * haven't been allocated yet.*/
+    GC lightGC;			/* Used to draw lighter parts of
+				 * the border. None means the shadow colors
+				 * haven't been allocated yet. */
+    Tcl_HashEntry *hashPtr;	/* Entry in borderTable (needed in
+				 * order to delete structure). */
+    struct TkBorderStruct *nextPtr; /* Points to the next TkBorder structure with
+				 * the same color name.  Borders with the
+				 * same name but different screens or
+				 * colormaps are chained together off a
+				 * single entry in borderTable. */
+} TkBorder;
+
+#endif /* BLT_TKINT_H */
Index: trunk/kitgen/8.x/blt/generic/bltTree.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTree.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTree.c	(revision 175)
@@ -0,0 +1,3060 @@
+
+/*
+ * bltTree.c --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "tree" data object was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+/* TODO:
+ *	traces and notifiers should be in one list in tree object.
+ *	notifier is always fired.
+ *	incorporate first/next tag routines ?
+ */
+
+
+#ifndef NO_TREE
+
+#include "bltTree.h"
+
+static Tcl_InterpDeleteProc TreeInterpDeleteProc;
+static Blt_TreeApplyProc SizeApplyProc;
+static Tcl_IdleProc NotifyIdleProc;
+
+#define TREE_THREAD_KEY		"BLT Tree Data"
+#define TREE_MAGIC		((unsigned int) 0x46170277)
+#define TREE_DESTROYED		(1<<0)
+
+typedef struct Blt_TreeNodeStruct Node;
+typedef struct Blt_TreeClientStruct TreeClient;
+typedef struct Blt_TreeObjectStruct TreeObject;
+typedef struct Blt_TreeValueStruct Value;
+
+/*
+ * Blt_TreeValue --
+ *
+ *	Tree nodes contain heterogeneous data fields, represented as a
+ *	chain of these structures.  Each field contains the key of the
+ *	field (Blt_TreeKey) and the value (Tcl_Obj) containing the
+ *	actual data representations.
+ * 
+ */
+struct Blt_TreeValueStruct {
+    Blt_TreeKey key;		/* String identifying the data field */
+    Tcl_Obj *objPtr;		/* Data representation. */
+    Blt_Tree owner;		/* Non-NULL if privately owned. */
+    Blt_TreeValue next;		/* Next value in the chain. */
+};
+
+#include <stdio.h>
+#include <string.h>
+/* The following header is required for LP64 compilation */
+#include <stdlib.h>
+
+#include "bltHash.h"
+
+static void TreeDestroyValues _ANSI_ARGS_((Blt_TreeNode node));
+
+static Value *TreeFindValue _ANSI_ARGS_((Blt_TreeNode node,
+	Blt_TreeKey key));
+static Value *TreeCreateValue _ANSI_ARGS_((Blt_TreeNode node,
+	Blt_TreeKey key, int *newPtr));
+
+static int TreeDeleteValue _ANSI_ARGS_((Blt_TreeNode node, 
+	Blt_TreeValue value));
+
+static Value *TreeFirstValue _ANSI_ARGS_((Blt_TreeNode, 
+	Blt_TreeKeySearch *searchPtr));
+
+static Value *TreeNextValue _ANSI_ARGS_((Blt_TreeKeySearch *srchPtr));
+
+/*
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+
+#define REBUILD_MULTIPLIER	3
+
+#if (SIZEOF_VOID_P == 8)
+#define RANDOM_INDEX(i)		HashOneWord(mask, downshift, i)
+#define BITSPERWORD		64
+#else 
+
+#define START_LOGSIZE		5 /* Initial hash table size is 32. */
+#define MAX_LIST_VALUES		20 /* Convert to hash table when node
+				    * value list gets bigger than this
+				    * many values. */
+
+/*
+ * The following macro takes a preliminary integer hash value and
+ * produces an index into a hash tables bucket list.  The idea is
+ * to make it so that preliminary values that are arbitrarily similar
+ * will end up in different buckets.  The hash function was taken
+ * from a random-number generator.
+ */
+#define RANDOM_INDEX(i) \
+    (((((long) (i))*1103515245) >> downshift) & mask)
+#define BITSPERWORD		32
+#endif
+
+#define DOWNSHIFT_START		(BITSPERWORD - 2) 
+
+/*
+ * Procedure prototypes for static procedures in this file:
+ */
+
+
+#if (SIZEOF_VOID_P == 8)
+static Blt_Hash HashOneWord _ANSI_ARGS_((uint64_t mask, unsigned int downshift,
+	CONST void *key));
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ * The hash table below is used to keep track of all the Blt_TreeKeys
+ * created so far.
+ */
+static Blt_HashTable keyTable;
+static int keyTableInitialized = 0;
+
+typedef struct {
+    Blt_HashTable treeTable;	/* Table of trees. */
+    unsigned int nextId;
+    Tcl_Interp *interp;
+} TreeInterpData;
+
+typedef struct {
+    Tcl_Interp *interp;
+    ClientData clientData;
+    Blt_TreeKey key;
+    unsigned int mask;
+    Blt_TreeNotifyEventProc *proc;
+    Blt_TreeNotifyEvent event;
+    int notifyPending;
+} EventHandler;
+
+typedef struct {
+    ClientData clientData;
+    char *keyPattern;
+    char *withTag;
+    Node *nodePtr;
+    unsigned int mask;
+    Blt_TreeTraceProc *proc;
+    TreeClient *clientPtr;
+    Blt_ChainLink *linkPtr;
+} TraceHandler;
+
+/*
+ * --------------------------------------------------------------
+ *
+ * GetTreeInterpData --
+ *
+ *	Creates or retrieves data associated with tree data objects
+ *	for a particular thread.  We're using Tcl_GetAssocData rather
+ *	than the Tcl thread routines so BLT can work with pre-8.0 
+ *	Tcl versions that don't have thread support.
+ *
+ * Results:
+ *	Returns a pointer to the tree interpreter data.
+ *
+ * -------------------------------------------------------------- 
+ */
+static TreeInterpData *
+GetTreeInterpData(Tcl_Interp *interp)
+{
+    Tcl_InterpDeleteProc *proc;
+    TreeInterpData *dataPtr;
+
+    dataPtr = (TreeInterpData *)
+	Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(TreeInterpData));
+	assert(dataPtr);
+	dataPtr->interp = interp;
+	Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
+		 dataPtr);
+	Blt_InitHashTable(&dataPtr->treeTable, BLT_STRING_KEYS);
+    }
+    return dataPtr;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewNode --
+ *
+ *	Creates a new node in the tree without installing it.  The
+ *	number of nodes in the tree is incremented and a unique serial
+ *	number is generated for the node. 
+ *
+ *	Also, all nodes have a label.  If no label was provided (name
+ *	is NULL) then automatically generate one in the form "nodeN"
+ *	where N is the serial number of the node.
+ *
+ * Results:
+ *	Returns a pointer to the new node.
+ *
+ * -------------------------------------------------------------- 
+ */
+static Node *
+NewNode(TreeObject *treeObjPtr, CONST char *name, int inode)
+{
+    Node *nodePtr;
+
+    /* Create the node structure */
+    nodePtr = Blt_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
+    nodePtr->inode = inode;
+    nodePtr->treeObject = treeObjPtr;
+    nodePtr->parent = NULL;
+    nodePtr->depth = 0;
+    nodePtr->flags = 0;
+    nodePtr->next = nodePtr->prev = NULL;
+    nodePtr->first = nodePtr->last = NULL;
+    nodePtr->nChildren = 0;
+    nodePtr->values = NULL;     
+    nodePtr->logSize = 0;
+    nodePtr->nValues = 0;
+    nodePtr->label = NULL;
+    if (name != NULL) {
+	nodePtr->label = Blt_TreeGetKey(name);
+    }
+    treeObjPtr->nNodes++;
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReleaseTagTable --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+ReleaseTagTable(Blt_TreeTagTable *tablePtr)
+{
+    tablePtr->refCount--;
+    if (tablePtr->refCount <= 0) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+
+	for(hPtr = Blt_FirstHashEntry(&tablePtr->tagTable, &cursor); 
+	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    Blt_TreeTagEntry *tPtr;
+
+	    tPtr = Blt_GetHashValue(hPtr);
+	    Blt_DeleteHashTable(&tPtr->nodeTable);
+	    Blt_Free(tPtr);
+	}
+	Blt_DeleteHashTable(&tablePtr->tagTable);
+	Blt_Free(tablePtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetDepths --
+ *
+ *	Called after moving a node, resets the depths of each node
+ *	for the entire branch (node and it's decendants).  
+ *
+ * Results: 
+ *	None.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+static void
+ResetDepths(
+    Node *nodePtr,		/* Root node. */
+    int depth)			/* Depth of the node. */
+{
+    nodePtr->depth = depth;
+    /* Also reset the depth for each descendant node. */
+    for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+	ResetDepths(nodePtr, depth + 1);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinkBefore --
+ *
+ *	Inserts a link preceding a given link.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LinkBefore(
+    Node *parentPtr,	/* Parent to hold the new entry. */
+    Node *nodePtr,	/* New node to be inserted. */
+    Node *beforePtr)	/* Node to link before. */
+{
+    if (parentPtr->first == NULL) {
+	parentPtr->last = parentPtr->first = nodePtr;
+    } else if (beforePtr == NULL) { /* Append onto the end of the chain */
+	nodePtr->next = NULL;
+	nodePtr->prev = parentPtr->last;
+	parentPtr->last->next = nodePtr;
+	parentPtr->last = nodePtr;
+    } else {
+	nodePtr->prev = beforePtr->prev;
+	nodePtr->next = beforePtr;
+	if (beforePtr == parentPtr->first) {
+	    parentPtr->first = nodePtr;
+	} else {
+	    beforePtr->prev->next = nodePtr;
+	}
+	beforePtr->prev = nodePtr;
+    }
+    parentPtr->nChildren++;
+    nodePtr->parent = parentPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkNode --
+ *
+ *	Unlinks a link from the chain. The link is not deallocated, 
+ *	but only removed from the chain.
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UnlinkNode(Node *nodePtr)
+{
+    Node *parentPtr;
+    int unlinked;		/* Indicates if the link is actually
+				 * removed from the chain. */
+    parentPtr = nodePtr->parent;
+    unlinked = FALSE;
+    if (parentPtr->first == nodePtr) {
+	parentPtr->first = nodePtr->next;
+	unlinked = TRUE;
+    }
+    if (parentPtr->last == nodePtr) {
+	parentPtr->last = nodePtr->prev;
+	unlinked = TRUE;
+    }
+    if (nodePtr->next != NULL) {
+	nodePtr->next->prev = nodePtr->prev;
+	unlinked = TRUE;
+    }
+    if (nodePtr->prev != NULL) {
+	nodePtr->prev->next = nodePtr->next;
+	unlinked = TRUE;
+    }
+    if (unlinked) {
+	parentPtr->nChildren--;
+    }
+    nodePtr->prev = nodePtr->next = NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * FreeNode --
+ *
+ *	Unlinks a given node from the tree, removes its data, and
+ *	frees memory allocated to the node.
+ *
+ * Results:
+ *	None.
+ *
+ * -------------------------------------------------------------- 
+ */
+static void
+FreeNode(TreeObject *treeObjPtr, Node *nodePtr)
+{
+    Blt_HashEntry *hPtr;
+
+    /*
+     * Destroy any data fields associated with this node.
+     */
+    TreeDestroyValues(nodePtr);
+    UnlinkNode(nodePtr);
+    treeObjPtr->nNodes--;
+    hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)nodePtr->inode);
+    assert(hPtr);
+    Blt_DeleteHashEntry(&treeObjPtr->nodeTable, hPtr);
+    Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewTreeObject --
+ *
+ *	Creates and initializes a new tree object. Trees always
+ *	contain a root node, so one is allocated here.
+ *
+ * Results:
+ *	Returns a pointer to the new tree object is successful, NULL
+ *	otherwise.  If a tree can't be generated, interp->result will
+ *	contain an error message.
+ *
+ * -------------------------------------------------------------- */
+static TreeObject *
+NewTreeObject(TreeInterpData *dataPtr, Tcl_Interp *interp, CONST char *treeName)
+{
+    TreeObject *treeObjPtr;
+    int isNew;
+    Blt_HashEntry *hPtr;
+
+    treeObjPtr = Blt_Calloc(1, sizeof(TreeObject));
+    if (treeObjPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate tree", (char *)NULL);
+	return NULL;
+    }
+    treeObjPtr->name = Blt_Strdup(treeName);
+    treeObjPtr->interp = interp;
+    treeObjPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+    treeObjPtr->nodePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+    treeObjPtr->clients = Blt_ChainCreate();
+    treeObjPtr->depth = 1;
+    treeObjPtr->notifyFlags = 0;
+    Blt_InitHashTableWithPool(&treeObjPtr->nodeTable, BLT_ONE_WORD_KEYS);
+
+    hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
+    treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
+    Blt_SetHashValue(hPtr, treeObjPtr->root);
+
+    treeObjPtr->tablePtr = &dataPtr->treeTable;
+    treeObjPtr->hashPtr = Blt_CreateHashEntry(treeObjPtr->tablePtr, treeName, 
+	&isNew);
+    Blt_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
+
+    return treeObjPtr;
+}
+
+static TreeObject *
+FindTreeInNamespace(
+    TreeInterpData *dataPtr,	/* Interpreter-specific data. */
+    Tcl_Namespace *nsPtr,
+    CONST char *treeName)
+{
+    Tcl_DString dString;
+    char *name;
+    Blt_HashEntry *hPtr;
+
+    name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+    hPtr = Blt_FindHashEntry(&dataPtr->treeTable, name);
+    Tcl_DStringFree(&dString);
+    if (hPtr != NULL) {
+	return Blt_GetHashValue(hPtr);
+    }
+    return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetTreeObject --
+ *
+ *	Searches for the tree object associated by the name given.
+ *
+ * Results:
+ *	Returns a pointer to the tree if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static TreeObject *
+GetTreeObject(Tcl_Interp *interp, CONST char *name, int flags)
+{
+    CONST char *treeName;
+    Tcl_Namespace *nsPtr;	/* Namespace associated with the tree object.
+				 * If NULL, indicates to look in first the
+				 * current namespace and then the global
+				 * for the tree. */
+    TreeInterpData *dataPtr;	/* Interpreter-specific data. */
+    TreeObject *treeObjPtr;
+
+    treeObjPtr = NULL;
+    if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+	Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"", 
+		(char *)NULL);
+	return NULL;
+    }
+    dataPtr = GetTreeInterpData(interp);
+    if (nsPtr != NULL) { 
+	treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+    } else { 
+	if (flags & NS_SEARCH_CURRENT) {
+	    /* Look first in the current namespace. */
+	    nsPtr = Tcl_GetCurrentNamespace(interp);
+	    treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+	}
+	if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+	    nsPtr = Tcl_GetGlobalNamespace(interp);
+	    treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+	}
+    }
+    return treeObjPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TeardownTree --
+ *
+ *	Destroys an entire branch.  This is a special purpose routine
+ *	used to speed up the final clean up of the tree.
+ *
+ * Results: 
+ *	None.
+ *
+ * ---------------------------------------------------------------------- 
+ */
+static void
+TeardownTree(TreeObject *treeObjPtr, Node *nodePtr)
+{
+    if (nodePtr->first != NULL) {
+	Node *childPtr, *nextPtr;
+	
+	for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+	    nextPtr = childPtr->next;
+	    TeardownTree(treeObjPtr, childPtr);
+	}
+    }
+    if (nodePtr->values != NULL) {
+	TreeDestroyValues(nodePtr);
+    }
+    Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+static void
+DestroyTreeObject(TreeObject *treeObjPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeClient *clientPtr;
+
+    treeObjPtr->flags |= TREE_DESTROYED;
+    treeObjPtr->nNodes = 0;
+
+    /* Remove the remaining clients. */
+    for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	Blt_ChainDestroy(clientPtr->events);
+	Blt_ChainDestroy(clientPtr->traces);
+	Blt_Free(clientPtr);
+    }
+    Blt_ChainDestroy(treeObjPtr->clients);
+
+    TeardownTree(treeObjPtr, treeObjPtr->root);
+    Blt_PoolDestroy(treeObjPtr->nodePool);
+    Blt_PoolDestroy(treeObjPtr->valuePool);
+    Blt_DeleteHashTable(&treeObjPtr->nodeTable);
+
+    if (treeObjPtr->hashPtr != NULL) {
+	/* Remove the entry from the global tree table. */
+	Blt_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr); 
+	if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
+	    keyTableInitialized = FALSE;
+	    Blt_DeleteHashTable(&keyTable);
+	}
+    }
+    if (treeObjPtr->name != NULL) {
+	Blt_Free(treeObjPtr->name);
+    }
+    Blt_Free(treeObjPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TreeInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting the tree object
+ *	is deleted from the interpreter.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys all remaining trees and removes the hash table
+ *	used to register tree names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TreeInterpDeleteProc(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp)
+{
+    TreeInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    TreeObject *treeObjPtr;
+    
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	treeObjPtr = (TreeObject *)Blt_GetHashValue(hPtr);
+	treeObjPtr->hashPtr = NULL;
+	DestroyTreeObject(treeObjPtr);
+    }
+    if (keyTableInitialized) {
+	keyTableInitialized = FALSE;
+	Blt_DeleteHashTable(&keyTable);
+    }
+    Blt_DeleteHashTable(&dataPtr->treeTable);
+    Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyIdleProc --
+ *
+ *	Used to invoke event handler routines at some idle point.
+ *	This routine is called from the Tcl event loop.  Errors
+ *	generated by the event handler routines are backgrounded.
+ *	
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+NotifyIdleProc(ClientData clientData)
+{
+    EventHandler *notifyPtr = clientData;
+    int result;
+
+    notifyPtr->notifyPending = FALSE;
+    notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
+    result = (*notifyPtr->proc)(notifyPtr->clientData, &notifyPtr->event);
+    notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+    if (result != TCL_OK) {
+	Tcl_BackgroundError(notifyPtr->interp);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckEventHandlers --
+ *
+ *	Traverses the list of client event callbacks and checks
+ *	if one matches the given event.  A client may trigger an
+ *	action that causes the tree to notify it.  The can be
+ *	prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
+ *	the event handler.
+ *
+ *	If a matching handler is found, a callback may be called either
+ *	immediately or at the next idle time depending upon the
+ *	TREE_NOTIFY_WHENIDLE bit.  
+ *
+ *	Since a handler routine may trigger yet another call to
+ *	itself, callbacks are ignored while the event handler is
+ *	executing.
+ *	
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+CheckEventHandlers(
+    TreeClient *clientPtr,
+    int isSource,		/* Indicates if the client is the source
+				 * of the event. */
+    Blt_TreeNotifyEvent *eventPtr)
+{
+    Blt_ChainLink *linkPtr, *nextPtr;
+    EventHandler *notifyPtr;
+
+    eventPtr->tree = clientPtr;
+    for (linkPtr = Blt_ChainFirstLink(clientPtr->events); 
+	linkPtr != NULL; linkPtr = nextPtr) {
+	nextPtr = Blt_ChainNextLink(linkPtr);
+	notifyPtr = Blt_ChainGetValue(linkPtr);
+	if ((notifyPtr->mask & TREE_NOTIFY_ACTIVE) ||
+	    (notifyPtr->mask & eventPtr->type) == 0) {
+	    continue;		/* Ignore callbacks that are generated
+				 * inside of a notify handler routine. */
+	}
+	if ((isSource) && (notifyPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
+	    continue;		/* Don't notify yourself. */
+	}
+	if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
+	    if (!notifyPtr->notifyPending) {
+		notifyPtr->notifyPending = TRUE;
+		notifyPtr->event = *eventPtr;
+		Tcl_DoWhenIdle(NotifyIdleProc, notifyPtr);
+	    }
+	} else {
+	    int result;
+
+	    notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
+	    result = (*notifyPtr->proc) (notifyPtr->clientData, eventPtr);
+	    notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+	    if (result != TCL_OK) {
+		Tcl_BackgroundError(notifyPtr->interp);
+	    }
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyClients --
+ *
+ *	Traverses the list of clients for a particular tree and
+ *	notifies each client that an event occurred.  Clients 
+ *	indicate interest in a particular event through a bit
+ *	flag.  
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+NotifyClients(
+    TreeClient *sourcePtr,
+    TreeObject *treeObjPtr,
+    Node *nodePtr,
+    int eventFlag)
+{
+    Blt_ChainLink *linkPtr;
+    Blt_TreeNotifyEvent event;
+    TreeClient *clientPtr;
+    int isSource;
+
+    event.type = eventFlag;
+    event.inode = nodePtr->inode;
+
+    /* 
+     * Issue callbacks to each client indicating that a new node has
+     * been created.
+     */
+    for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients);
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	isSource = (clientPtr == sourcePtr);
+	CheckEventHandlers(clientPtr, isSource, &event);
+    }
+}
+
+static void
+FreeValue(Node *nodePtr, Value *valuePtr)
+{
+    if (valuePtr->objPtr != NULL) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+    }
+    Blt_PoolFreeItem(nodePtr->treeObject->valuePool, valuePtr);
+}
+
+
+
+/* Public Routines */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeGetKey --
+ *
+ *	Given a string, returns a unique identifier for the string.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeKey
+Blt_TreeGetKey(string)
+    CONST char *string;		/* String to convert. */
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    if (!keyTableInitialized) {
+	Blt_InitHashTable(&keyTable, BLT_STRING_KEYS);
+	keyTableInitialized = 1;
+    }
+    hPtr = Blt_CreateHashEntry(&keyTable, string, &isNew);
+    return (Blt_TreeKey)Blt_GetHashKey(&keyTable, hPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNode --
+ *
+ *	Creates a new node in the given parent node.  The name and
+ *	position in the parent are also provided.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNode(
+    TreeClient *clientPtr,	/* The tree client that is creating
+				 * this node.  If NULL, indicates to
+				 * trigger notify events on behalf of
+				 * the initiating client also. */
+    Node *parentPtr,		/* Parent node where the new node will
+				 * be inserted. */
+    CONST char *name,		/* Name of node. */
+    int pos)			/* Position in the parent's list of children
+				 * where to insert the new node. */
+{
+    Blt_HashEntry *hPtr;
+    Node *beforePtr;
+    Node *nodePtr;	/* Node to be inserted. */
+    TreeObject *treeObjPtr;
+    int inode;
+    int isNew;
+
+    treeObjPtr = parentPtr->treeObject;
+
+    /* Generate an unique serial number for this node.  */
+    do {
+	inode = treeObjPtr->nextInode++;
+	hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, 
+		   &isNew);
+    } while (!isNew);
+    nodePtr = NewNode(treeObjPtr, name, inode);
+    Blt_SetHashValue(hPtr, nodePtr);
+
+    if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
+	beforePtr = NULL;
+    } else {
+	beforePtr = parentPtr->first;
+	while ((pos > 0) && (beforePtr != NULL)) {
+	    pos--;
+	    beforePtr = beforePtr->next;
+	}
+    }
+    LinkBefore(parentPtr, nodePtr, beforePtr);
+    nodePtr->depth = parentPtr->depth + 1;
+    /* 
+     * Issue callbacks to each client indicating that a new node has
+     * been created.
+     */
+    NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNodeWithId --
+ *
+ *	Like Blt_TreeCreateNode, but provides a specific id to use
+ *	for the node.  If the tree already contains a node by that
+ *	id, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNodeWithId(
+    TreeClient *clientPtr,
+    Node *parentPtr,		/* Parent node where the new node will
+				 * be inserted. */
+    CONST char *name,		/* Name of node. */
+    int inode,			/* Requested id of the new node. If a
+				 * node by this id already exists in the
+				 * tree, no node is created. */
+    int position)		/* Position in the parent's list of children
+				 * where to insert the new node. */
+{
+    Blt_HashEntry *hPtr;
+    Node *beforePtr;
+    Node *nodePtr;	/* Node to be inserted. */
+    TreeObject *treeObjPtr;
+    int isNew;
+
+    treeObjPtr = parentPtr->treeObject;
+    hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
+    if (!isNew) {
+	return NULL;
+    }
+    nodePtr = NewNode(treeObjPtr, name, inode);
+    Blt_SetHashValue(hPtr, nodePtr);
+
+    if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
+	beforePtr = NULL;
+    } else {
+	beforePtr = parentPtr->first;
+	while ((position > 0) && (beforePtr != NULL)) {
+	    position--;
+	    beforePtr = beforePtr->next;
+	}
+    }
+    LinkBefore(parentPtr, nodePtr, beforePtr);
+    nodePtr->depth = parentPtr->depth + 1;
+    /* 
+     * Issue callbacks to each client indicating that a new node has
+     * been created.
+     */
+    NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeMoveNode --
+ *
+ *	Move an entry into a new location in the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_TreeMoveNode(
+    TreeClient *clientPtr,
+    Node *nodePtr, 
+    Node *parentPtr, 
+    Node *beforePtr)
+{
+    TreeObject *treeObjPtr = nodePtr->treeObject;
+    int newDepth;
+
+    if (nodePtr == beforePtr) {
+	return TCL_ERROR;
+    }
+    if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
+	return TCL_ERROR;
+    }
+    if (nodePtr->parent == NULL) {
+	return TCL_ERROR;	/* Can't move root. */
+    }
+    /* Verify that the node isn't an ancestor of the new parent. */
+    if (Blt_TreeIsAncestor(nodePtr, parentPtr)) {
+	return TCL_ERROR;
+    }
+    UnlinkNode(nodePtr);
+    /* 
+     * Relink the node as a child of the new parent.
+     */
+    LinkBefore(parentPtr, nodePtr, beforePtr);
+    newDepth = parentPtr->depth + 1;
+    if (nodePtr->depth != newDepth) { 
+	/* Reset the depths of all descendant nodes. */
+	ResetDepths(nodePtr, newDepth);
+    }
+
+    /* 
+     * Issue callbacks to each client indicating that a node has
+     * been moved.
+     */
+    NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE);
+    return TCL_OK;
+}
+
+int
+Blt_TreeDeleteNode(TreeClient *clientPtr, Node *nodePtr)
+{
+    TreeObject *treeObjPtr = nodePtr->treeObject;
+    Node *childPtr, *nextPtr;
+
+    /* In depth-first order, delete each descendant node. */
+    for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+	nextPtr = childPtr->next;
+	Blt_TreeDeleteNode(clientPtr, childPtr);
+    }
+    /* 
+     * Issue callbacks to each client indicating that the node can
+     * no longer be used.  
+     */
+    NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE);
+
+    /* Now remove the actual node. */
+    FreeNode(treeObjPtr, nodePtr);
+    return TCL_OK;
+}
+
+Blt_TreeNode
+Blt_TreeGetNode(TreeClient *clientPtr, unsigned int inode)
+{
+    TreeObject *treeObjPtr = clientPtr->treeObject;
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
+    if (hPtr != NULL) {
+	return (Blt_TreeNode)Blt_GetHashValue(hPtr);
+    }
+    return NULL;
+}
+
+Blt_TreeTrace
+Blt_TreeCreateTrace(
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    CONST char *keyPattern,
+    CONST char *tagName,
+    unsigned int mask,
+    Blt_TreeTraceProc *proc,
+    ClientData clientData)
+{
+    TraceHandler *tracePtr;
+
+    tracePtr = Blt_Calloc(1, sizeof (TraceHandler));
+    assert(tracePtr);
+    tracePtr->linkPtr = Blt_ChainAppend(clientPtr->traces, tracePtr);
+    if (keyPattern != NULL) {
+	tracePtr->keyPattern = Blt_Strdup(keyPattern);
+    }
+    if (tagName != NULL) {
+	tracePtr->withTag = Blt_Strdup(tagName);
+    }
+    tracePtr->clientPtr = clientPtr;
+    tracePtr->proc = proc;
+    tracePtr->clientData = clientData;
+    tracePtr->mask = mask;
+    tracePtr->nodePtr = nodePtr;
+    return (Blt_TreeTrace)tracePtr;
+}
+
+void
+Blt_TreeDeleteTrace(Blt_TreeTrace trace)
+{
+    TraceHandler *tracePtr = (TraceHandler *)trace;
+
+    Blt_ChainDeleteLink(tracePtr->clientPtr->traces, tracePtr->linkPtr);
+    if (tracePtr->keyPattern != NULL) {
+	Blt_Free(tracePtr->keyPattern);
+    }
+    if (tracePtr->withTag != NULL) {
+	Blt_Free(tracePtr->withTag);
+    }
+    Blt_Free(tracePtr);
+}
+
+void
+Blt_TreeRelabelNode(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
+{
+    nodePtr->label = Blt_TreeGetKey(string);
+    /* 
+     * Issue callbacks to each client indicating that a new node has
+     * been created.
+     */
+    NotifyClients(clientPtr, clientPtr->treeObject, nodePtr, 
+		  TREE_NOTIFY_RELABEL);
+}
+
+void
+Blt_TreeRelabelNode2(Node *nodePtr, CONST char *string)
+{
+    nodePtr->label = Blt_TreeGetKey(string);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeFindChild --
+ *
+ *	Searches for the named node in a parent's chain of siblings.  
+ *
+ *
+ * Results:
+ *	If found, the child node is returned, otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeFindChild(Node *parentPtr, CONST char *string)
+{
+    Blt_TreeKey label;
+    register Node *nodePtr;
+    
+    label = Blt_TreeGetKey(string);
+    for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+	if (label == nodePtr->label) {
+	    return nodePtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePosition --
+ *
+ *	Returns the position of the node in its parent's list of
+ *	children.  The root's position is 0.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeNodePosition(Node *nodePtr)
+{
+    Node *parentPtr;
+    int count;
+
+    count = 0;
+    parentPtr = nodePtr->parent;
+    if (parentPtr != NULL) {
+	Node *childPtr;
+
+	for (childPtr = parentPtr->first; childPtr != NULL; 
+	     childPtr = childPtr->next) {
+	    if (nodePtr == childPtr) {
+		break;
+	    }
+	    count++;
+	}
+    }
+    return count;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreePrevNode --
+ *
+ *	Returns the "previous" node in the tree.  This node (in 
+ *	depth-first order) is its parent, if the node has no siblings
+ *	that are previous to it.  Otherwise it is the last descendant 
+ *	of the last sibling.  In this case, descend the sibling's
+ *	hierarchy, using the last child at any ancestor, with we
+ *	we find a leaf.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreePrevNode(Node *rootPtr, Node *nodePtr)
+{
+    Node *prevPtr;
+
+    if (nodePtr == rootPtr) {
+	return NULL;		/* The root is the first node. */
+    }
+    prevPtr = nodePtr->prev;
+    if (prevPtr == NULL) {
+	/* There are no siblings previous to this one, so pick the parent. */
+	return nodePtr->parent;
+    }
+    /*
+     * Traverse down the right-most thread, in order to select the
+     * next entry.  Stop when we reach a leaf.
+     */
+    nodePtr = prevPtr;
+    while ((prevPtr = nodePtr->last) != NULL) {
+	nodePtr = prevPtr;
+    }
+    return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNextNode --
+ *
+ *	Returns the "next" node in relation to the given node.  
+ *	The next node (in depth-first order) is either the first 
+ *	child of the given node the next sibling if the node has
+ *	no children (the node is a leaf).  If the given node is the 
+ *	last sibling, then try it's parent next sibling.  Continue
+ *	until we either find a next sibling for some ancestor or 
+ *	we reach the root node.  In this case the current node is 
+ *	the last node in the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeNextNode(Node *rootPtr, Node *nodePtr)
+{
+    Node *nextPtr;
+
+    /* Pick the first sub-node. */
+    nextPtr = nodePtr->first;
+    if (nextPtr != NULL) {
+	return nextPtr;
+    }
+    /* 
+     * Back up until we can find a level where we can pick a 
+     * "next sibling".  For the last entry we'll thread our 
+     * way back to the root.  
+     */
+    while (nodePtr != rootPtr) {
+	nextPtr = nodePtr->next;
+	if (nextPtr != NULL) {
+	    return nextPtr;
+	}
+	nodePtr = nodePtr->parent;
+    }
+    return NULL;		/* At root, no next node. */
+}
+
+
+int
+Blt_TreeIsBefore(Node *n1Ptr, Node *n2Ptr)
+{
+    int depth;
+    register int i;
+    Node *nodePtr;
+
+    if (n1Ptr == n2Ptr) {
+	return FALSE;
+    }
+    depth = MIN(n1Ptr->depth, n2Ptr->depth);
+    if (depth == 0) {		/* One of the nodes is root. */
+	return (n1Ptr->parent == NULL);
+    }
+    /* 
+     * Traverse back from the deepest node, until both nodes are at
+     * the same depth.  Check if this ancestor node is the same for
+     * both nodes.
+     */
+    for (i = n1Ptr->depth; i > depth; i--) {
+	n1Ptr = n1Ptr->parent;
+    }
+    if (n1Ptr == n2Ptr) {
+	return FALSE;
+    }
+    for (i = n2Ptr->depth; i > depth; i--) {
+	n2Ptr = n2Ptr->parent;
+    }
+    if (n2Ptr == n1Ptr) {
+	return TRUE;
+    }
+
+    /* 
+     * First find the mutual ancestor of both nodes.  Look at each
+     * preceding ancestor level-by-level for both nodes.  Eventually
+     * we'll find a node that's the parent of both ancestors.  Then
+     * find the first ancestor in the parent's list of subnodes.  
+     */
+    for (i = depth; i > 0; i--) {
+	if (n1Ptr->parent == n2Ptr->parent) {
+	    break;
+	}
+	n1Ptr = n1Ptr->parent;
+	n2Ptr = n2Ptr->parent;
+    }
+    for (nodePtr = n1Ptr->parent->first; nodePtr != NULL; 
+	 nodePtr = nodePtr->next) {
+	if (nodePtr == n1Ptr) {
+	    return TRUE;
+	} else if (nodePtr == n2Ptr) {
+	    return FALSE;
+	}
+    }
+    return FALSE;
+}
+
+static void
+CallTraces(
+    Tcl_Interp *interp,
+    TreeClient *sourcePtr,	/* Client holding a reference to the
+				 * tree.  If NULL, indicates to
+				 * execute all handlers, including
+				 * those of the caller. */
+    TreeObject *treeObjPtr,	/* Tree that was changed. */
+    Node *nodePtr,		/* Node that received the event. */
+    Blt_TreeKey key,
+    unsigned int flags)
+{
+    Blt_ChainLink *l1Ptr, *l2Ptr;
+    TreeClient *clientPtr;
+    TraceHandler *tracePtr;	
+
+    for(l1Ptr = Blt_ChainFirstLink(treeObjPtr->clients); 
+	l1Ptr != NULL; l1Ptr = Blt_ChainNextLink(l1Ptr)) {
+	clientPtr = Blt_ChainGetValue(l1Ptr);
+	for(l2Ptr = Blt_ChainFirstLink(clientPtr->traces); 
+	    l2Ptr != NULL; l2Ptr = Blt_ChainNextLink(l2Ptr)) {
+	    tracePtr = Blt_ChainGetValue(l2Ptr);
+	    if ((tracePtr->keyPattern != NULL) && 
+		(!Tcl_StringMatch(key, tracePtr->keyPattern))) {
+		continue;	/* Key pattern doesn't match. */
+	    }
+	    if ((tracePtr->withTag != NULL) && 
+		(!Blt_TreeHasTag(clientPtr, nodePtr, tracePtr->withTag))) {
+		continue;	/* Doesn't have the tag. */
+	    }
+	    if ((tracePtr->mask & flags) == 0) {
+		continue;	/* Flags don't match. */
+	    }
+	    if ((clientPtr == sourcePtr) && 
+		(tracePtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
+		continue;	/* This client initiated the trace. */
+	    }
+	    if ((tracePtr->nodePtr != NULL) && (tracePtr->nodePtr != nodePtr)) {
+		continue;	/* Nodes don't match. */
+	    }
+	    nodePtr->flags |= TREE_TRACE_ACTIVE;
+	    if ((*tracePtr->proc) (tracePtr->clientData, treeObjPtr->interp, 
+		nodePtr, key, flags) != TCL_OK) {
+		if (interp != NULL) {
+		    Tcl_BackgroundError(interp);
+		}
+	    }
+	    nodePtr->flags &= ~TREE_TRACE_ACTIVE;
+	}
+    }
+}
+
+static Value *
+GetTreeValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    Blt_TreeKey key)
+{
+    register Value *valuePtr;
+
+    valuePtr = TreeFindValue(nodePtr, key); 
+    if (valuePtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find field \"", key, "\"", 
+			     (char *)NULL);
+	}
+	return NULL;
+    }	
+    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't access private field \"", 
+			     key, "\"", (char *)NULL);
+	}
+	return NULL;
+    }
+    return valuePtr;
+}
+
+int
+Blt_TreePrivateValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    Blt_TreeKey key)
+{
+    Value *valuePtr;
+
+    valuePtr = TreeFindValue(nodePtr, key); 
+    if (valuePtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find field \"", key, "\"", 
+			     (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    valuePtr->owner = clientPtr;
+    return TCL_OK;
+}
+
+int
+Blt_TreePublicValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    Blt_TreeKey key)
+{
+    Value *valuePtr;
+
+    valuePtr = TreeFindValue(nodePtr, key); 
+    if (valuePtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find field \"", key, "\"", 
+			     (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    if (valuePtr->owner != clientPtr) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "not the owner of \"", key, "\"", 
+		     (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    valuePtr->owner = NULL;
+    return TCL_OK;
+}
+
+int
+Blt_TreeValueExistsByKey(clientPtr, nodePtr, key)
+    TreeClient *clientPtr;
+    Node *nodePtr;
+    Blt_TreeKey key;
+{
+    register Value *valuePtr;
+
+    valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+    if (valuePtr == NULL) {
+	return FALSE;
+    }
+    return TRUE;
+}
+
+int
+Blt_TreeGetValueByKey(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    Blt_TreeKey key,
+    Tcl_Obj **objPtrPtr)
+{
+    register Value *valuePtr;
+    TreeObject *treeObjPtr = nodePtr->treeObject;
+
+    valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+    if (valuePtr == NULL) {
+	return TCL_ERROR;
+    }
+    *objPtrPtr = valuePtr->objPtr;
+    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+	CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, 
+		   TREE_TRACE_READ);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeSetValueByKey(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    Blt_TreeKey key,		/* Identifies the field key. */
+    Tcl_Obj *objPtr)		/* New value of field. */
+{
+    TreeObject *treeObjPtr = nodePtr->treeObject;
+    Value *valuePtr;
+    unsigned int flags;
+    int isNew;
+
+    assert(objPtr != NULL);
+    valuePtr = TreeCreateValue(nodePtr, key, &isNew);
+    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't set private field \"", 
+			     key, "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    if (objPtr != valuePtr->objPtr) {
+	Tcl_IncrRefCount(objPtr);
+	if (valuePtr->objPtr != NULL) {
+	    Tcl_DecrRefCount(valuePtr->objPtr);
+	}
+	valuePtr->objPtr = objPtr;
+    }
+    flags = TREE_TRACE_WRITE;
+    if (isNew) {
+	flags |= TREE_TRACE_CREATE;
+    }
+    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+	CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key, 
+		flags);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeUnsetValueByKey(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    Blt_TreeKey key)		/* Name of field in node. */
+{
+    TreeObject *treeObjPtr = nodePtr->treeObject;
+    Value *valuePtr;
+
+    valuePtr = TreeFindValue(nodePtr, key);
+    if (valuePtr == NULL) {
+	return TCL_OK;		/* It's okay to unset values that don't
+				 * exist in the node. */
+    }
+    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't unset private field \"", 
+			     key, "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    TreeDeleteValue(nodePtr, valuePtr);
+    CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET);
+    return TCL_OK;
+}
+
+static int
+ParseParentheses(
+    Tcl_Interp *interp,
+    CONST char *string,
+    char **leftPtr, 
+    char **rightPtr)
+{
+    register char *p;
+    char *left, *right;
+
+    left = right = NULL;
+    for (p = (char *)string; *p != '\0'; p++) {
+	if (*p == '(') {
+	    left = p;
+	} else if (*p == ')') {
+	    right = p;
+	}
+    }
+    if (left != right) {
+	if (((left != NULL) && (right == NULL)) ||
+	    ((left == NULL) && (right != NULL)) ||
+	    (left > right) || (right != (p - 1))) {
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "bad array specification \"", string,
+			     "\"", (char *)NULL);
+	    }
+	    return TCL_ERROR;
+	}
+    }
+    *leftPtr = left;
+    *rightPtr = right;
+    return TCL_OK;
+}
+
+int
+Blt_TreeGetValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    CONST char *string,		/* String identifying the field in node. */
+    Tcl_Obj **objPtrPtr)
+{
+    char *left, *right;
+    int result;
+
+    if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (left != NULL) {
+	*left = *right = '\0';
+	result = Blt_TreeGetArrayValue(interp, clientPtr, nodePtr, string, 
+		left + 1, objPtrPtr);
+	*left = '(', *right = ')';
+    } else {
+	result = Blt_TreeGetValueByKey(interp, clientPtr, nodePtr, 
+		Blt_TreeGetKey(string), objPtrPtr);
+    }
+    return result;
+}
+
+int
+Blt_TreeSetValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    CONST char *string,		/* String identifying the field in node. */
+    Tcl_Obj *valueObjPtr)	/* New value of field. If NULL, field
+				 * is deleted. */
+{
+    char *left, *right;
+    int result;
+
+    if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (left != NULL) {
+	*left = *right = '\0';
+	result = Blt_TreeSetArrayValue(interp, clientPtr, nodePtr, string, 
+		left + 1, valueObjPtr);
+	*left = '(', *right = ')';
+    } else {
+	result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr, 
+			       Blt_TreeGetKey(string), valueObjPtr);
+    }
+    return result;
+}
+
+int
+Blt_TreeUnsetValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    CONST char *string)		/* String identifying the field in node. */
+{
+    char *left, *right;
+    int result;
+
+    if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (left != NULL) {
+	*left = *right = '\0';
+	result = Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr, string, 
+		left + 1);
+	*left = '(', *right = ')';
+    } else {
+	result = Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr, 
+		Blt_TreeGetKey(string));
+    }
+    return result;
+}
+
+int
+Blt_TreeValueExists(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
+{
+    char *left, *right;
+    int result;
+
+    if (ParseParentheses((Tcl_Interp *)NULL, string, &left, &right) != TCL_OK) {
+	return FALSE;
+    }
+    if (left != NULL) {
+	*left = *right = '\0';
+	result = Blt_TreeArrayValueExists(clientPtr, nodePtr, string, left + 1);
+	*left = '(', *right = ')';
+    } else {
+	result = Blt_TreeValueExistsByKey(clientPtr, nodePtr, 
+		Blt_TreeGetKey(string));
+    }
+    return result;
+}
+
+Blt_TreeKey
+Blt_TreeFirstKey(
+    TreeClient *clientPtr, 
+    Node *nodePtr, 
+    Blt_TreeKeySearch *iterPtr)
+{
+    Value *valuePtr;
+    
+    valuePtr = TreeFirstValue(nodePtr, iterPtr);
+    if (valuePtr == NULL) {
+	return NULL;
+    }
+    while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	valuePtr = TreeNextValue(iterPtr);
+	if (valuePtr == NULL) {
+	    return NULL;
+	}
+    }
+    return valuePtr->key;
+}
+
+Blt_TreeKey
+Blt_TreeNextKey(TreeClient *clientPtr, Blt_TreeKeySearch *iterPtr)
+{
+    Value *valuePtr;
+
+    valuePtr = TreeNextValue(iterPtr);
+    if (valuePtr == NULL) {
+	return NULL;
+    }
+    while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	valuePtr = TreeNextValue(iterPtr);
+	if (valuePtr == NULL) {
+	    return NULL;
+	}
+    }
+    return valuePtr->key;
+}
+
+int
+Blt_TreeIsAncestor(Node *n1Ptr, Node *n2Ptr)
+{
+    if (n2Ptr != NULL) {
+	n2Ptr = n2Ptr->parent;
+	while (n2Ptr != NULL) {
+	    if (n2Ptr == n1Ptr) {
+		return TRUE;
+	    }
+	    n2Ptr = n2Ptr->parent;
+	}
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeSortNode --
+ *
+ *	Sorts the subnodes at a given node.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeSortNode(
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    Blt_TreeCompareNodesProc *proc)
+{
+    Node **nodeArr;
+    Node *childPtr;
+    int nNodes;
+    register Node **p;
+
+    nNodes = nodePtr->nChildren;
+    if (nNodes < 2) {
+	return TCL_OK;
+    }
+    nodeArr = Blt_Malloc((nNodes + 1) * sizeof(Node *));
+    if (nodeArr == NULL) {
+	return TCL_ERROR;	/* Out of memory. */
+    }
+    for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL; 
+	 childPtr = childPtr->next, p++) {
+	*p = childPtr;
+    }
+    *p = NULL;
+
+    qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
+    for (p = nodeArr; *p != NULL; p++) {
+	UnlinkNode(*p);
+	LinkBefore(nodePtr, *p, (Blt_TreeNode)NULL);
+    }
+    Blt_Free(nodeArr);
+    NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
+    return TCL_OK;
+}
+
+#define TEST_RESULT(result) \
+	switch (result) { \
+	case TCL_CONTINUE: \
+	    return TCL_OK; \
+	case TCL_OK: \
+	    break; \
+	default: \
+	    return (result); \
+	}
+
+int
+Blt_TreeApply(
+    Node *nodePtr,		/* Root node of subtree. */
+    Blt_TreeApplyProc *proc,	/* Procedure to call for each node. */
+    ClientData clientData)	/* One-word of data passed when calling
+				 * proc. */
+{
+    Node *childPtr, *nextPtr;
+    int result;
+
+    for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+
+	/* 
+	 * Get the next link in the chain before calling Blt_TreeApply
+	 * recursively.  This is because the apply callback may delete
+	 * the node and its link.  
+	 */
+
+	nextPtr = childPtr->next;
+	result = Blt_TreeApply(childPtr, proc, clientData);
+	TEST_RESULT(result);
+    }
+    return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+}
+
+int
+Blt_TreeApplyDFS(
+    Node *nodePtr,		/* Root node of subtree. */
+    Blt_TreeApplyProc *proc,	/* Procedure to call for each node. */
+    ClientData clientData,	/* One-word of data passed when calling
+				 * proc. */
+    int order)			/* Order of traversal. */
+{
+    Node *childPtr, *nextPtr;
+    int result;
+
+    if (order & TREE_PREORDER) {
+	result = (*proc) (nodePtr, clientData, TREE_PREORDER);
+	TEST_RESULT(result);
+    }
+    childPtr = nodePtr->first;
+    if (order & TREE_INORDER) {
+	if (childPtr != NULL) {
+	    result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+	    TEST_RESULT(result);
+	    childPtr = childPtr->next;
+	}
+	result = (*proc) (nodePtr, clientData, TREE_INORDER);
+	TEST_RESULT(result);
+    }
+    for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
+	/* 
+	 * Get the next link in the chain before calling
+	 * Blt_TreeApply recursively.  This is because the 
+	 * apply callback may delete the node and its link.  
+	 */
+	nextPtr = childPtr->next;
+	result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+	TEST_RESULT(result);
+    }
+    if (order & TREE_POSTORDER) {
+	return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeApplyBFS(nodePtr, proc, clientData)
+    Node *nodePtr;		/* Root node of subtree. */
+    Blt_TreeApplyProc *proc;	/* Procedure to call for each node. */
+    ClientData clientData;	/* One-word of data passed when calling
+				 * proc. */
+{
+    Blt_Chain *queuePtr;
+    Blt_ChainLink *linkPtr, *nextPtr;
+    Node *childPtr;
+    int result;
+
+    queuePtr = Blt_ChainCreate();
+    linkPtr = Blt_ChainAppend(queuePtr, nodePtr);
+    while (linkPtr != NULL) {
+	nodePtr = Blt_ChainGetValue(linkPtr);
+	/* Add the children to the queue. */
+	for (childPtr = nodePtr->first; childPtr != NULL; 
+	     childPtr = childPtr->next) {
+	    Blt_ChainAppend(queuePtr, childPtr);
+	}
+	/* Process the node. */
+	result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
+	switch (result) { 
+	case TCL_CONTINUE: 
+	    Blt_ChainDestroy(queuePtr);
+	    return TCL_OK; 
+	case TCL_OK: 
+	    break; 
+	default: 
+	    Blt_ChainDestroy(queuePtr);
+	    return result; 
+	}
+	/* Remove the node from the queue. */
+	nextPtr = Blt_ChainNextLink(linkPtr);
+	Blt_ChainDeleteLink(queuePtr, linkPtr);
+	linkPtr = nextPtr;
+    }
+    Blt_ChainDestroy(queuePtr);
+    return TCL_OK;
+}
+
+static TreeClient *
+NewTreeClient(TreeObject *treeObjPtr)
+{
+    TreeClient *clientPtr;
+
+    clientPtr = Blt_Calloc(1, sizeof(TreeClient));
+    if (clientPtr != NULL) {
+	Blt_TreeTagTable *tablePtr;
+
+	clientPtr->magic = TREE_MAGIC;
+	clientPtr->linkPtr = Blt_ChainAppend(treeObjPtr->clients, clientPtr);
+	clientPtr->events = Blt_ChainCreate();
+	clientPtr->traces = Blt_ChainCreate();
+	clientPtr->treeObject = treeObjPtr;
+	clientPtr->root = treeObjPtr->root;
+	tablePtr = Blt_Malloc(sizeof(Blt_TreeTagTable));
+	Blt_InitHashTable(&tablePtr->tagTable, BLT_STRING_KEYS);
+	tablePtr->refCount = 1;
+	clientPtr->tagTablePtr = tablePtr;
+    }
+    return clientPtr;
+}
+
+int
+Blt_TreeCreate(
+    Tcl_Interp *interp,		/* Interpreter to report errors back to. */
+    CONST char *name,		/* Name of tree in namespace.  Tree
+				 * must not already exist. */
+    TreeClient **clientPtrPtr)	/* (out) Client token of newly created
+				 * tree.  Releasing the token will
+				 * free the tree.  If NULL, no token
+				 * is generated. */
+{
+    Tcl_DString dString;
+    Tcl_Namespace *nsPtr;
+    TreeInterpData *dataPtr;
+    TreeObject *treeObjPtr;
+    CONST char *treeName;
+    char string[200];
+
+    dataPtr = GetTreeInterpData(interp);
+    if (name != NULL) {
+	/* Check if this tree already exists the current namespace. */
+	treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
+	if (treeObjPtr != NULL) {
+	    Tcl_AppendResult(interp, "a tree object \"", name,
+			     "\" already exists", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    } else {
+	/* Generate a unique tree name in the current namespace. */
+	do  {
+	    sprintf(string, "tree%d", dataPtr->nextId++);
+	} while (GetTreeObject(interp, name, NS_SEARCH_CURRENT) != NULL);
+	name = string;
+    } 
+    /* 
+     * Tear apart and put back together the namespace-qualified name 
+     * of the tree. This is to ensure that naming is consistent.
+     */ 
+    treeName = name;
+    if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+	Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    if (nsPtr == NULL) {
+	/* 
+	 * Note: Unlike Tcl_CreateCommand, an unqualified name 
+	 * doesn't imply the global namespace, but the current one.
+	 */
+	nsPtr = Tcl_GetCurrentNamespace(interp);
+    }
+    name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+    treeObjPtr = NewTreeObject(dataPtr, interp, name);
+    if (treeObjPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate tree \"", name, "\"", 
+		(char *)NULL);
+	Tcl_DStringFree(&dString);
+	return TCL_ERROR;
+    }
+    Tcl_DStringFree(&dString);
+    if (clientPtrPtr != NULL) {
+	TreeClient *clientPtr;
+	
+	clientPtr = NewTreeClient(treeObjPtr);
+	if (clientPtr == NULL) {
+	    Tcl_AppendResult(interp, "can't allocate tree token",(char *)NULL);
+	    return TCL_ERROR;
+	}
+	*clientPtrPtr = clientPtr;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeGetToken(
+    Tcl_Interp *interp,		/* Interpreter to report errors back to. */
+    CONST char *name,		/* Name of tree in namespace. */
+    TreeClient **clientPtrPtr)
+{
+    TreeClient *clientPtr;
+    TreeObject *treeObjPtr;
+
+    treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+    if (treeObjPtr == NULL) {
+	Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"", 
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    clientPtr = NewTreeClient(treeObjPtr);
+    if (clientPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate token for tree \"", 
+			 name, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *clientPtrPtr = clientPtr;
+    return TCL_OK;
+}
+
+void
+Blt_TreeReleaseToken(TreeClient *clientPtr)
+{
+    TreeObject *treeObjPtr;
+    Blt_ChainLink *linkPtr;
+    EventHandler *notifyPtr;
+    TraceHandler *tracePtr;
+
+    if (clientPtr->magic != TREE_MAGIC) {
+	fprintf(stderr, "invalid tree object token 0x%lx\n", 
+		(unsigned long)clientPtr);
+	return;
+    }
+    /* Remove any traces that may be set. */
+    for (linkPtr = Blt_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	tracePtr = Blt_ChainGetValue(linkPtr);
+	if (tracePtr->keyPattern != NULL) {
+	    Blt_Free(tracePtr->keyPattern);
+	}
+	Blt_Free(tracePtr);
+    }
+    Blt_ChainDestroy(clientPtr->traces);
+    /* And any event handlers. */
+    for(linkPtr = Blt_ChainFirstLink(clientPtr->events); 
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	notifyPtr = Blt_ChainGetValue(linkPtr);
+	if (notifyPtr->notifyPending) {
+	    Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
+	}
+	Blt_Free(notifyPtr);
+    }
+    if (clientPtr->tagTablePtr != NULL) {
+	ReleaseTagTable(clientPtr->tagTablePtr);
+    }
+    Blt_ChainDestroy(clientPtr->events);
+    treeObjPtr = clientPtr->treeObject;
+    if (treeObjPtr != NULL) {
+	/* Remove the client from the server's list */
+	Blt_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
+	if (Blt_ChainGetLength(treeObjPtr->clients) == 0) {
+	    DestroyTreeObject(treeObjPtr);
+	}
+    }
+    clientPtr->magic = 0;
+    Blt_Free(clientPtr);
+}
+
+int
+Blt_TreeExists(interp, name)
+    Tcl_Interp *interp;		/* Interpreter to report errors back to. */
+    CONST char *name;		/* Name of tree in designated namespace. */
+{
+    TreeObject *treeObjPtr;
+
+    treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+    if (treeObjPtr == NULL) {
+	Tcl_ResetResult(interp);
+	return 0;
+    }
+    return 1;
+}
+
+/*ARGSUSED*/
+static int
+SizeApplyProc(
+    Node *nodePtr,		/* Not used. */
+    ClientData clientData,
+    int order)			/* Not used. */
+{
+    int *sumPtr = clientData;
+    *sumPtr = *sumPtr + 1;
+    return TCL_OK;
+}
+
+int
+Blt_TreeSize(Node *nodePtr)
+{
+    int sum;
+
+    sum = 0;
+    Blt_TreeApply(nodePtr, SizeApplyProc, &sum);
+    return sum;
+}
+
+
+void
+Blt_TreeCreateEventHandler(
+    TreeClient *clientPtr,
+    unsigned int mask,
+    Blt_TreeNotifyEventProc *proc,
+    ClientData clientData)
+{
+    Blt_ChainLink *linkPtr;
+    EventHandler *notifyPtr;
+
+    notifyPtr = NULL;		/* Suppress compiler warning. */
+
+    /* Check if the event is already handled. */
+    for(linkPtr = Blt_ChainFirstLink(clientPtr->events); 
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	notifyPtr = Blt_ChainGetValue(linkPtr);
+	if ((notifyPtr->proc == proc) && 
+	    (notifyPtr->mask == mask) &&
+	    (notifyPtr->clientData == clientData)) {
+	    break;
+	}
+    }
+    if (linkPtr == NULL) {
+	notifyPtr = Blt_Malloc(sizeof (EventHandler));
+	assert(notifyPtr);
+	linkPtr = Blt_ChainAppend(clientPtr->events, notifyPtr);
+    }
+    if (proc == NULL) {
+	Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+	Blt_Free(notifyPtr);
+    } else {
+	notifyPtr->proc = proc;
+	notifyPtr->clientData = clientData;
+	notifyPtr->mask = mask;
+	notifyPtr->notifyPending = FALSE;
+	notifyPtr->interp = clientPtr->treeObject->interp;
+    }
+}
+
+void
+Blt_TreeDeleteEventHandler(
+    TreeClient *clientPtr,
+    unsigned int mask,
+    Blt_TreeNotifyEventProc *proc,
+    ClientData clientData)
+{
+    Blt_ChainLink *linkPtr;
+    EventHandler *notifyPtr;
+
+    for(linkPtr = Blt_ChainFirstLink(clientPtr->events); 
+	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	notifyPtr = Blt_ChainGetValue(linkPtr);
+	if ((notifyPtr->proc == proc) && (notifyPtr->mask == mask) &&
+	    (notifyPtr->clientData == clientData)) {
+	    if (notifyPtr->notifyPending) {
+		Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
+	    }
+	    Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+	    Blt_Free(notifyPtr);
+	    return;
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePath --
+ *
+ *---------------------------------------------------------------------- 
+ */
+char *
+Blt_TreeNodePath(Node *nodePtr, Tcl_DString *resultPtr)
+{
+    char **nameArr;		/* Used to stack the component names. */
+    char *staticSpace[64];
+    int nLevels;
+    register int i;
+
+    nLevels = nodePtr->depth;
+    if (nLevels > 64) {
+	nameArr = Blt_Malloc(nLevels * sizeof(char *));
+	assert(nameArr);
+    } else {
+	nameArr = staticSpace;
+    }
+    for (i = nLevels - 1; i >= 0; i--) {
+	/* Save the name of each ancestor in the name array. 
+	 * Note that we ignore the root. */
+	nameArr[i] = nodePtr->label;
+	nodePtr = nodePtr->parent;
+    }
+    /* Append each the names in the array. */
+    Tcl_DStringInit(resultPtr);
+    for (i = 0; i < nLevels; i++) {
+	Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+    }
+    if (nameArr != staticSpace) {
+	Blt_Free(nameArr);
+    }
+    return Tcl_DStringValue(resultPtr);
+}
+
+int
+Blt_TreeArrayValueExists(
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    CONST char *arrayName, 
+    CONST char *elemName)
+{
+    Blt_TreeKey key;
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    register Value *valuePtr;
+
+    key = Blt_TreeGetKey(arrayName);
+    valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+    if (valuePtr == NULL) {
+	return FALSE;
+    }
+    if (Tcl_IsShared(valuePtr->objPtr)) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+	valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+    }
+    if (Blt_GetArrayFromObj((Tcl_Interp *)NULL, valuePtr->objPtr, &tablePtr) 
+	!= TCL_OK) {
+	return FALSE;
+    }
+    hPtr = Blt_FindHashEntry(tablePtr, elemName);
+    return (hPtr != NULL);
+}
+
+int
+Blt_TreeGetArrayValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    CONST char *arrayName,
+    CONST char *elemName,
+    Tcl_Obj **valueObjPtrPtr)
+{
+    Blt_TreeKey key;
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    register Value *valuePtr;
+
+    key = Blt_TreeGetKey(arrayName);
+    valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+    if (valuePtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (Tcl_IsShared(valuePtr->objPtr)) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+	valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+    }
+    if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_FindHashEntry(tablePtr, elemName);
+    if (hPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
+			     elemName, ")\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *valueObjPtrPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+
+    /* Reading any element of the array can cause a trace to fire. */
+    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+	CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key, 
+		   TREE_TRACE_READ);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeSetArrayValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    CONST char *arrayName,
+    CONST char *elemName,
+    Tcl_Obj *valueObjPtr)	/* New value of element. */
+{
+    Blt_TreeKey key;
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    register Value *valuePtr;
+    unsigned int flags;
+    int isNew;
+
+    assert(valueObjPtr != NULL);
+
+    /* 
+     * Search for the array in the list of data fields.  If one
+     * doesn't exist, create it.
+     */
+    key = Blt_TreeGetKey(arrayName);
+    valuePtr = TreeCreateValue(nodePtr, key, &isNew);
+    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't set private field \"", 
+			     key, "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    flags = TREE_TRACE_WRITE;
+    if (isNew) {
+	valuePtr->objPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+	flags |= TREE_TRACE_CREATE;
+    } else if (Tcl_IsShared(valuePtr->objPtr)) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+	valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+    }
+    if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_InvalidateStringRep(valuePtr->objPtr);
+    hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew);
+    assert(hPtr);
+
+    Tcl_IncrRefCount(valueObjPtr);
+    if (!isNew) {
+	Tcl_Obj *oldValueObjPtr;
+
+	/* An element by the same name already exists. Decrement the
+	 * reference count of the old value. */
+
+	oldValueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+	if (oldValueObjPtr != NULL) {
+	    Tcl_DecrRefCount(oldValueObjPtr);
+	}
+    }
+    Blt_SetHashValue(hPtr, valueObjPtr);
+
+    /*
+     * We don't handle traces on a per array element basis.  Setting
+     * any element can fire traces for the value.
+     */
+    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+	CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, 
+		valuePtr->key, flags);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeUnsetArrayValue(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,		/* Node to be updated. */
+    CONST char *arrayName,
+    CONST char *elemName)
+{
+    Blt_TreeKey key;		/* Name of field in node. */
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    Tcl_Obj *valueObjPtr;
+    Value *valuePtr;
+
+    key = Blt_TreeGetKey(arrayName);
+    valuePtr = TreeFindValue(nodePtr, key);
+    if (valuePtr == NULL) {
+	return TCL_OK;
+    }
+    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't unset private field \"", 
+			     key, "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    if (Tcl_IsShared(valuePtr->objPtr)) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+	valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+    }
+    if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    hPtr = Blt_FindHashEntry(tablePtr, elemName);
+    if (hPtr == NULL) {
+	return TCL_OK;		/* Element doesn't exist, Ok. */
+    }
+    valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+    Tcl_DecrRefCount(valueObjPtr);
+    Blt_DeleteHashEntry(tablePtr, hPtr);
+
+    /*
+     * Un-setting any element in the array can cause the trace on the value
+     * to fire.
+     */
+    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+	CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, 
+		valuePtr->key, TREE_TRACE_WRITE);
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeArrayNames(
+    Tcl_Interp *interp,
+    TreeClient *clientPtr,
+    Node *nodePtr,
+    CONST char *arrayName,
+    Tcl_Obj *listObjPtr)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_HashTable *tablePtr;
+    Tcl_Obj *objPtr;
+    Value *valuePtr;
+    char *key;
+
+    key = Blt_TreeGetKey(arrayName);
+    valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+    if (valuePtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (Tcl_IsShared(valuePtr->objPtr)) {
+	Tcl_DecrRefCount(valuePtr->objPtr);
+	valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+	Tcl_IncrRefCount(valuePtr->objPtr);
+    }
+    if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tablePtr = (Blt_HashTable *)valuePtr->objPtr;
+    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL; 
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	objPtr = Tcl_NewStringObj(Blt_GetHashKey(tablePtr, hPtr), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeShareTagTable --
+ *
+ *---------------------------------------------------------------------- 
+ */
+int
+Blt_TreeShareTagTable(
+    TreeClient *sourcePtr,
+    TreeClient *targetPtr)
+{
+    sourcePtr->tagTablePtr->refCount++;
+    if (targetPtr->tagTablePtr != NULL) {
+	ReleaseTagTable(targetPtr->tagTablePtr);
+    }
+    targetPtr->tagTablePtr = sourcePtr->tagTablePtr;
+    return TCL_OK;
+}
+
+int
+Blt_TreeTagTableIsShared(TreeClient *clientPtr)
+{
+    return (clientPtr->tagTablePtr->refCount > 1);
+}   
+
+void
+Blt_TreeClearTags(TreeClient *clientPtr, Blt_TreeNode node)
+{
+    Blt_HashEntry *hPtr, *h2Ptr;
+    Blt_HashSearch cursor;
+    
+    for (hPtr = Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, &cursor); 
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	Blt_TreeTagEntry *tPtr;
+
+	tPtr = Blt_GetHashValue(hPtr);
+	h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
+	if (h2Ptr != NULL) {
+	    Blt_DeleteHashEntry(&tPtr->nodeTable, h2Ptr);
+	}
+    }
+}
+
+int
+Blt_TreeHasTag(
+    TreeClient *clientPtr,
+    Blt_TreeNode node, 
+    CONST char *tagName)
+{
+    Blt_HashEntry *hPtr;
+    Blt_TreeTagEntry *tPtr;
+
+    if (strcmp(tagName, "all") == 0) {
+	return TRUE;
+    }
+    if ((strcmp(tagName, "root") == 0) && 
+	(node == Blt_TreeRootNode(clientPtr))) {
+	return TRUE;
+    }
+    hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
+    if (hPtr == NULL) {
+	return FALSE;
+    }
+    tPtr = Blt_GetHashValue(hPtr);
+    hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
+    if (hPtr == NULL) {
+	return FALSE;
+    }
+    return TRUE;
+}
+
+void
+Blt_TreeAddTag(
+    TreeClient *clientPtr,
+    Blt_TreeNode node,
+    CONST char *tagName)
+{
+    int isNew;
+    Blt_HashEntry *hPtr;
+    Blt_HashTable *tablePtr;
+    Blt_TreeTagEntry *tPtr;
+
+    if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)) {
+	return;
+    }
+    tablePtr = &clientPtr->tagTablePtr->tagTable;
+    hPtr = Blt_CreateHashEntry(tablePtr, tagName, &isNew);
+    assert(hPtr);
+    if (isNew) {
+
+	tPtr = Blt_Malloc(sizeof(Blt_TreeTagEntry));
+	Blt_InitHashTable(&tPtr->nodeTable, BLT_ONE_WORD_KEYS);
+	Blt_SetHashValue(hPtr, tPtr);
+	tPtr->hashPtr = hPtr;
+	tPtr->tagName = Blt_GetHashKey(tablePtr, hPtr);
+    } else {
+	tPtr = Blt_GetHashValue(hPtr);
+    }
+    hPtr = Blt_CreateHashEntry(&tPtr->nodeTable, (char *)node, &isNew);
+    assert(hPtr);
+    if (isNew) {
+	Blt_SetHashValue(hPtr, node);
+    }
+}
+
+void
+Blt_TreeForgetTag(TreeClient *clientPtr, CONST char *tagName)
+{
+    if ((strcmp(tagName, "all") != 0) && (strcmp(tagName, "root") != 0)) {
+	Blt_HashEntry *hPtr;
+	
+	hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
+	if (hPtr != NULL) {
+	    Blt_TreeTagEntry *tPtr;
+	    
+	    Blt_DeleteHashEntry(&clientPtr->tagTablePtr->tagTable, hPtr);
+	    tPtr = Blt_GetHashValue(hPtr);
+	    Blt_DeleteHashTable(&tPtr->nodeTable);
+	    Blt_Free(tPtr);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeTagHashTable --
+ *
+ *---------------------------------------------------------------------- 
+ */
+Blt_HashTable *
+Blt_TreeTagHashTable(TreeClient *clientPtr, CONST char *tagName)
+{
+    Blt_HashEntry *hPtr;
+   
+    hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
+    if (hPtr != NULL) {
+	Blt_TreeTagEntry *tPtr;
+	
+	tPtr = Blt_GetHashValue(hPtr);
+	return &tPtr->nodeTable;
+    }
+    return NULL;
+}
+
+Blt_HashEntry *
+Blt_TreeFirstTag(TreeClient *clientPtr, Blt_HashSearch *cursorPtr)
+{
+    return Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, cursorPtr);
+}
+
+#if (SIZEOF_VOID_P == 8)
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashOneWord --
+ *
+ *	Compute a one-word hash value of a 64-bit word, which then can
+ *	be used to generate a hash index.
+ *
+ *	From Knuth, it's a multiplicative hash.  Multiplies an unsigned
+ *	64-bit value with the golden ratio (sqrt(5) - 1) / 2.  The
+ *	downshift value is 64 - n, when n is the log2 of the size of
+ *	the hash table.
+ *		
+ * Results:
+ *	The return value is a one-word summary of the information in
+ *	64 bit word.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_Hash
+HashOneWord(
+    uint64_t mask,
+    unsigned int downshift,	        
+    CONST void *key)
+{
+    uint64_t a0, a1;
+    uint64_t y0, y1;
+    uint64_t y2, y3; 
+    uint64_t p1, p2;
+    uint64_t result;
+
+    /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
+    a0 = (uint64_t)key & 0x00000000FFFFFFFF; 
+    a1 = (uint64_t)key >> 32;
+    
+    y0 = a0 * 0x000000007f4a7c13;
+    y1 = a0 * 0x000000009e3779b9; 
+    y2 = a1 * 0x000000007f4a7c13;
+    y3 = a1 * 0x000000009e3779b9; 
+    y1 += y0 >> 32;		/* Can't carry */ 
+    y1 += y2;			/* Might carry */
+    if (y1 < y2) {
+	y3 += (1LL << 32);	/* Propagate */ 
+    }
+
+    /* 128-bit product: p1 = loword, p2 = hiword */
+    p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
+    p2 = y3 + (y1 >> 32);
+    
+    /* Left shift the value downward by the size of the table */
+    if (downshift > 0) { 
+	if (downshift < 64) { 
+	    result = ((p2 << (64 - downshift)) | (p1 >> (downshift & 63))); 
+	} else { 
+	    result = p2 >> (downshift & 63); 
+	} 
+    } else { 
+	result = p1;
+    } 
+    /* Finally mask off the high bits */
+    return (Blt_Hash)(result & mask);
+}
+
+#endif /* SIZEOF_VOID_P == 8 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildTable --
+ *
+ *	This procedure is invoked when the ratio of entries to hash
+ *	buckets becomes too large.  It creates a new table with a
+ *	larger bucket array and moves all of the entries into the
+ *	new table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory gets reallocated and entries get re-hashed to new
+ *	buckets.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+RebuildTable(Node *nodePtr)		/* Table to enlarge. */
+{
+    Value **newBucketPtr, **oldBuckets;
+    register Value **bucketPtr, **endPtr;
+    register Value *valuePtr, *nextPtr;
+    unsigned int downshift;
+    unsigned long mask;
+    Value **buckets;
+    size_t nBuckets;
+
+    oldBuckets = (Value **)nodePtr->values;
+    nBuckets = (1 << nodePtr->logSize);
+    endPtr = oldBuckets + nBuckets;
+
+    /*
+     * Allocate and initialize the new bucket array, and set up
+     * hashing constants for new array size.
+     */
+    nodePtr->logSize += 2;
+    nBuckets = (1 << nodePtr->logSize);
+    buckets = Blt_Calloc(nBuckets, sizeof(Value *));
+
+    /*
+     * Move all of the existing entries into the new bucket array,
+     * based on their hash values.  
+     */
+    mask = nBuckets - 1;
+    downshift = DOWNSHIFT_START - nodePtr->logSize;
+    for (bucketPtr = oldBuckets; bucketPtr < endPtr; bucketPtr++) {
+	for (valuePtr = *bucketPtr; valuePtr != NULL; valuePtr = nextPtr) {
+	    nextPtr = valuePtr->next;
+	    newBucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
+	    valuePtr->next = *newBucketPtr;
+	    *newBucketPtr = valuePtr;
+	}
+    }
+    nodePtr->values = (Value *)buckets;
+    Blt_Free(oldBuckets);
+}
+
+static void
+ConvertValues(Node *nodePtr)
+{
+    unsigned int nBuckets;
+    Value **buckets;
+    unsigned int mask;
+    int downshift;
+    Value *valuePtr, *nextPtr, **bucketPtr;
+
+    /*
+     * Convert list of values into a hash table.
+     */
+    nodePtr->logSize = START_LOGSIZE;
+    nBuckets = 1 << nodePtr->logSize;
+    buckets = Blt_Calloc(nBuckets, sizeof(Value *));
+    mask = nBuckets - 1;
+    downshift = DOWNSHIFT_START - nodePtr->logSize;
+    for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+	nextPtr = valuePtr->next;
+	bucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
+	valuePtr->next = *bucketPtr;
+	*bucketPtr = valuePtr;
+    }
+    nodePtr->values = (Value *)buckets;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeDeleteValue --
+ *
+ *	Remove a single entry from a hash table.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The entry given by entryPtr is deleted from its table and
+ *	should never again be used by the caller.  It is up to the
+ *	caller to free the clientData field of the entry, if that
+ *	is relevant.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TreeDeleteValue(Node *nodePtr, Blt_TreeValue value)
+{
+    Value *valuePtr = value;
+    register Value *prevPtr;
+    
+    if (nodePtr->logSize > 0) {
+	Value **bucketPtr;
+	unsigned int downshift;
+	unsigned long mask;
+
+	mask = (1 << nodePtr->logSize) - 1;
+	downshift = DOWNSHIFT_START - nodePtr->logSize;
+	
+	bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX(valuePtr->key);
+	if (*bucketPtr == valuePtr) {
+	    *bucketPtr = valuePtr->next;
+	} else {
+	    for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->next) {
+		if (prevPtr == NULL) {
+		    return TCL_ERROR; /* Can't find value in hash bucket. */
+		}
+		if (prevPtr->next == valuePtr) {
+		    prevPtr->next = valuePtr->next;
+		    break;
+		}
+	    }
+	}
+    } else {
+	prevPtr = NULL;
+	for (valuePtr = nodePtr->values; valuePtr != NULL; 
+	     valuePtr = valuePtr->next) {
+	    if (valuePtr == value) {
+		break;
+	    }
+	    prevPtr = valuePtr;
+	}
+	if (valuePtr == NULL) {
+	    return TCL_ERROR;	/* Can't find value in list. */
+	}
+	if (prevPtr == NULL) {
+	    nodePtr->values = valuePtr->next;
+	} else {
+	    prevPtr->next = valuePtr->next;
+	}
+    }
+    nodePtr->nValues--;
+    FreeValue(nodePtr, valuePtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeDestroyValues --
+ *
+ *	Free up everything associated with a hash table except for
+ *	the record for the table itself.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The hash table is no longer useable.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TreeDestroyValues(Node *nodePtr)
+{
+    register Value *valuePtr;
+    Value *nextPtr;
+
+    /*
+     * Free up all the entries in the table.
+     */
+    if (nodePtr->values != NULL) { 
+	return;
+    }
+    if (nodePtr->logSize > 0) {
+	Value **buckets;
+	int nBuckets;
+	int i;
+
+	buckets = (Value **)nodePtr->values;
+	nBuckets = (1 << nodePtr->logSize);
+	for (i = 0; i < nBuckets; i++) {
+	    for (valuePtr = buckets[i]; valuePtr != NULL; valuePtr = nextPtr) {
+		nextPtr = valuePtr->next;
+		FreeValue(nodePtr, valuePtr);
+	    }
+	}
+	Blt_Free(buckets);
+    } else {
+	for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+	    nextPtr = valuePtr->next;
+	    FreeValue(nodePtr, valuePtr);
+	}
+    }
+    nodePtr->values = NULL;
+    nodePtr->nValues = 0;
+    nodePtr->logSize = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeFirstValue --
+ *
+ *	Locate the first entry in a hash table and set up a record
+ *	that can be used to step through all the remaining entries
+ *	of the table.
+ *
+ * Results:
+ *	The return value is a pointer to the first value in tablePtr,
+ *	or NULL if tablePtr has no entries in it.  The memory at
+ *	*searchPtr is initialized so that subsequent calls to
+ *	Blt_TreeNextValue will return all of the values in the table,
+ *	one at a time.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Value *
+TreeFirstValue(
+    Node *nodePtr,
+    Blt_TreeKeySearch *searchPtr) /* Place to store information about
+				   * progress through the table. */
+{
+    searchPtr->node = nodePtr;
+    searchPtr->nextIndex = 0;
+    if (nodePtr->logSize > 0) {
+	searchPtr->nextValue = NULL;
+    } else {
+	searchPtr->nextValue = nodePtr->values;
+    }
+    return TreeNextValue(searchPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeNextValue --
+ *
+ *	Once a hash table enumeration has been initiated by calling
+ *	Blt_TreeFirstValue, this procedure may be called to return
+ *	successive elements of the table.
+ *
+ * Results:
+ *	The return value is the next entry in the hash table being
+ *	enumerated, or NULL if the end of the table is reached.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Value *
+TreeNextValue(
+    Blt_TreeKeySearch *searchPtr) /* Place to store information about
+				   * progress through the table.  Must
+				   * have been initialized by calling
+				   * Blt_TreeFirstValue. */
+{
+    Value *valuePtr;
+
+    if (searchPtr->node->logSize > 0) {
+	size_t nBuckets;
+	Value **buckets;
+
+	nBuckets = (1 << searchPtr->node->logSize);
+	buckets = (Value **)searchPtr->node->values;
+	while (searchPtr->nextValue == NULL) {
+	    if (searchPtr->nextIndex >= nBuckets) {
+		return NULL;
+	    }
+	    searchPtr->nextValue = buckets[searchPtr->nextIndex];
+	    searchPtr->nextIndex++;
+	}
+    }
+    valuePtr = searchPtr->nextValue;
+    if (valuePtr != NULL) {
+	searchPtr->nextValue = valuePtr->next;
+    }
+    return valuePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeFindValue --
+ *
+ *	Given a hash table with one-word keys, and a one-word key, find
+ *	the entry with a matching key.
+ *
+ * Results:
+ *	The return value is a token for the matching entry in the
+ *	hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Value *
+TreeFindValue(
+    Node *nodePtr,
+    Blt_TreeKey key)		/* Key to use to find matching entry. */
+{
+    register Value *valuePtr;
+    Value *bucket;
+
+    if (nodePtr->logSize > 0) {
+	unsigned int downshift;
+	unsigned long mask;
+
+	mask = (1 << nodePtr->logSize) - 1;
+	downshift = DOWNSHIFT_START - nodePtr->logSize;
+	bucket = ((Value **)(nodePtr->values))[RANDOM_INDEX((void *)key)];
+    } else {
+	bucket = nodePtr->values; /* Single chain list. */
+    }
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+    for (valuePtr = bucket; valuePtr != NULL; valuePtr = valuePtr->next) {
+	if (valuePtr->key == key) {
+	    return valuePtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateValue --
+ *
+ *	Find the value with a matching key.  If there is no matching 
+ *	value, then create a new one.
+ *
+ * Results:
+ *	The return value is a pointer to the matching value.  If this
+ *	is a newly-created value, then *newPtr will be set to a non-zero
+ *	value;  otherwise *newPtr will be set to 0.  
+ *
+ * Side effects:
+ *	A new value may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+static Value *
+TreeCreateValue(
+    Node *nodePtr,
+    Blt_TreeKey key,		/* Key to use to find or create matching
+				 * entry. */
+    int *newPtr)		/* Store info here telling whether a new
+				 * entry was created. */
+{
+    register Value *valuePtr;
+    
+    /* 
+     * Check if there as so many values that storage should be
+     * converted from a hash table instead of a list. 
+     */
+    if ((nodePtr->logSize == 0) && (nodePtr->nValues > MAX_LIST_VALUES)) {
+	ConvertValues(nodePtr);
+    }
+    if (nodePtr->logSize > 0) {
+	Value **bucketPtr;
+	size_t nBuckets;
+	unsigned int downshift;
+	unsigned long mask;
+
+	nBuckets = (1 << nodePtr->logSize);
+	mask = nBuckets - 1;
+	downshift = DOWNSHIFT_START - nodePtr->logSize;
+	bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX((void *)key);
+	
+	*newPtr = FALSE;
+	for (valuePtr = *bucketPtr; valuePtr != NULL; 
+	     valuePtr = valuePtr->next) {
+	    if (valuePtr->key == key) {
+		return valuePtr;
+	    }
+	}
+	
+	/* Value not found. Add a new value to the bucket. */
+	*newPtr = TRUE;
+	valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool, 
+		sizeof(Value));
+	valuePtr->key = key;
+	valuePtr->owner = NULL;
+	valuePtr->next = *bucketPtr;
+	valuePtr->objPtr = NULL;
+	*bucketPtr = valuePtr;
+	nodePtr->nValues++;
+	
+	/*
+	 * If the table has exceeded a decent size, rebuild it with many
+	 * more buckets.
+	 */
+	if ((unsigned int)nodePtr->nValues >= (nBuckets * 3)) {
+	    RebuildTable(nodePtr);
+	}
+    } else {
+	Value *prevPtr;
+
+	prevPtr = NULL;
+	*newPtr = FALSE;
+	for (valuePtr = nodePtr->values; valuePtr != NULL; 
+	     valuePtr = valuePtr->next) {
+	    if (valuePtr->key == key) {
+		return valuePtr;
+	    }
+	    prevPtr = valuePtr;
+	}
+	/* Value not found. Add a new value to the list. */
+	*newPtr = TRUE;
+	valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool, 
+		sizeof(Value));
+	valuePtr->key = key;
+	valuePtr->owner = NULL;
+	valuePtr->next = NULL;
+	valuePtr->objPtr = NULL;
+	if (prevPtr == NULL) {
+	    nodePtr->values = valuePtr;
+	} else {
+	    prevPtr->next = valuePtr;
+	}
+	nodePtr->nValues++;
+    }
+    return valuePtr;
+}
+
+#endif /* NO_TREE */
Index: trunk/kitgen/8.x/blt/generic/bltTree.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTree.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTree.h	(revision 175)
@@ -0,0 +1,454 @@
+
+/*
+ * bltTree.h --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "tree" data object was created by George A. Howlett.
+ */
+
+#ifndef _BLT_TREE_H
+#define _BLT_TREE_H
+
+#include <bltChain.h>
+#include <bltHash.h>
+#include <bltPool.h>
+
+typedef struct Blt_TreeNodeStruct *Blt_TreeNode;
+typedef struct Blt_TreeObjectStruct *Blt_TreeObject;
+typedef struct Blt_TreeClientStruct *Blt_Tree;
+typedef struct Blt_TreeTraceStruct *Blt_TreeTrace;
+typedef struct Blt_TreeValueStruct *Blt_TreeValue;
+typedef struct Blt_TreeTagEntryStruct Blt_TreeTagEntry;
+typedef struct Blt_TreeTagTableStruct Blt_TreeTagTable;
+
+typedef char *Blt_TreeKey;
+
+#define TREE_PREORDER		(1<<0)
+#define TREE_POSTORDER		(1<<1)
+#define TREE_INORDER		(1<<2)
+#define TREE_BREADTHFIRST	(1<<3)
+
+#define TREE_TRACE_UNSET	(1<<3)
+#define TREE_TRACE_WRITE	(1<<4)
+#define TREE_TRACE_READ		(1<<5)
+#define TREE_TRACE_CREATE	(1<<6)
+#define TREE_TRACE_ALL		\
+    (TREE_TRACE_UNSET | TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_CREATE)
+#define TREE_TRACE_MASK		(TREE_TRACE_ALL)
+
+#define TREE_TRACE_FOREIGN_ONLY	(1<<8)
+#define TREE_TRACE_ACTIVE	(1<<9)
+
+#define TREE_NOTIFY_CREATE	(1<<0)
+#define TREE_NOTIFY_DELETE	(1<<1)
+#define TREE_NOTIFY_MOVE	(1<<2)
+#define TREE_NOTIFY_SORT	(1<<3)
+#define TREE_NOTIFY_RELABEL	(1<<4)
+#define TREE_NOTIFY_ALL		\
+    (TREE_NOTIFY_CREATE | TREE_NOTIFY_DELETE | TREE_NOTIFY_MOVE | \
+	TREE_NOTIFY_SORT | TREE_NOTIFY_RELABEL)
+#define TREE_NOTIFY_MASK	(TREE_NOTIFY_ALL)
+
+#define TREE_NOTIFY_WHENIDLE	 (1<<8)
+#define TREE_NOTIFY_FOREIGN_ONLY (1<<9)
+#define TREE_NOTIFY_ACTIVE	 (1<<10)
+
+typedef struct {
+    int type;
+    Blt_Tree tree;
+    int inode;			/* Node of event */
+    Tcl_Interp *interp;
+} Blt_TreeNotifyEvent;
+
+typedef struct {
+    Blt_TreeNode node;		/* Node being searched. */
+    unsigned long nextIndex;	/* Index of next bucket to be
+				 * enumerated after present one. */
+    Blt_TreeValue nextValue;	/* Next entry to be enumerated in the
+				 * the current bucket. */
+} Blt_TreeKeySearch;
+
+/*
+ * Blt_TreeObject --
+ *
+ *	Structure providing the internal representation of the tree
+ *	object.	A tree is uniquely identified by a combination of 
+ *	its name and originating namespace.  Two trees in the same 
+ *	interpreter can have the same names but reside in different 
+ *	namespaces.
+ *
+ *	The tree object represents a general-ordered tree of nodes.
+ *	Each node may contain a heterogeneous collection of data
+ *	values. Each value is identified by a field name and nodes 
+ *	do not need to contain the same data fields. Data field
+ *	names are saved as reference counted strings and can be 
+ *	shared among nodes.
+ *
+ *	The tree is threaded.  A node contains both a pointer to 
+ *	back its parents and another to its siblings.  Therefore
+ *	the tree maybe traversed non-recursively.
+ * 
+ *	A tree object can be shared by several clients.  When a
+ *	client wants to use a tree object, it is given a token
+ *	that represents the tree.  The tree object uses the tokens
+ *	to keep track of its clients.  When all clients have 
+ *	released their tokens the tree is automatically destroyed.
+ */
+struct Blt_TreeObjectStruct {
+    Tcl_Interp *interp;		/* Interpreter associated with this tree. */
+
+    char *name;
+
+    Tcl_Namespace *nsPtr;
+
+    Blt_HashEntry *hashPtr;	/* Pointer to this tree object in tree
+				 * object hash table. */
+    Blt_HashTable *tablePtr;
+
+    Blt_TreeNode root;		/* Root of the entire tree. */
+
+    char *sortNodesCmd;		/* Tcl command to invoke to sort entries */
+
+    Blt_Chain *clients;		/* List of clients using this tree */
+
+    Blt_Pool nodePool;
+    Blt_Pool valuePool;
+
+    Blt_HashTable nodeTable;	/* Table of node identifiers. Used to
+				 * search for a node pointer given an inode.*/
+    unsigned int nextInode;
+
+    unsigned int nNodes;	/* Always counts root node. */
+
+    unsigned int depth;		/* Maximum depth of the tree. */
+
+    unsigned int flags;		/* Internal flags. See definitions
+				 * below. */
+    unsigned int notifyFlags;	/* Notification flags. See definitions
+				 * below. */
+
+};
+
+/*
+ * Blt_TreeNodeStruct --
+ *
+ *	Structure representing a node in a general ordered tree.
+ *	Nodes are identified by their index, or inode.  Nodes also
+ *	have names, but nodes names are not unique and can be 
+ *	changed.  Inodes are valid even if the node is moved.
+ *
+ *	Each node can contain a list of data fields.  Fields are
+ *	name-value pairs.  The values are represented by Tcl_Objs.
+ *	
+ */
+struct Blt_TreeNodeStruct {
+    Blt_TreeNode parent;	/* Parent node. If NULL, then this is
+				   the root node. */
+    Blt_TreeNode next;		/* Next sibling node. */
+    Blt_TreeNode prev;		/* Previous sibling node. */
+    Blt_TreeNode first;		/* First child node. */
+    Blt_TreeNode last;		/* Last child node. */
+
+    Blt_TreeKey label;		/* Node label (doesn't have to be
+				 * unique). */
+
+    Blt_TreeObject treeObject;
+
+    Blt_TreeValue values;	/* Depending upon the number of values
+				 * stored, this is either a chain or
+				 * hash table of Blt_TreeValue
+				 * structures.  (Note: if logSize is 
+				 * 0, then this is a list).  Each
+				 * value contains key/value data
+				 * pair.  The data is a Tcl_Obj. */
+
+    unsigned short nValues;	/* # of values for this node. */
+
+    unsigned short logSize;	/* Size of hash table indicated as a
+				 * power of 2 (e.g. if logSize=3, then
+				 * table size is 8). If 0, this
+				 * indicates that the node's values
+				 * are stored as a list. */
+
+    unsigned int nChildren;	/* # of children for this node. */
+    unsigned int inode;		/* Serial number of the node. */
+
+    unsigned short depth;	/* The depth of this node in the tree. */
+
+    unsigned short flags;
+};
+
+struct Blt_TreeTagEntryStruct {
+    char *tagName;
+    Blt_HashEntry *hashPtr;
+    Blt_HashTable nodeTable;
+};
+
+struct Blt_TreeTagTableStruct {
+    Blt_HashTable tagTable;
+    int refCount;
+};
+
+/*
+ * Blt_TreeClientStruct --
+ *
+ *	A tree can be shared by several clients.  Each client allocates
+ *	this structure which acts as a ticket for using the tree.  Clients
+ *	can designate notifier routines that are automatically invoked
+ *	by the tree object whenever the tree is changed is specific
+ *	ways by other clients.
+ */
+
+struct Blt_TreeClientStruct {
+    unsigned int magic;		/* Magic value indicating whether a
+				 * generic pointer is really a tree
+				 * token or not. */
+    Blt_ChainLink *linkPtr;	/* Pointer into the server's chain of
+				 * clients. */
+    Blt_TreeObject treeObject;	/* Pointer to the structure containing
+				 * the master information about the
+				 * tree used by the client.  If NULL,
+				 * this indicates that the tree has
+				 * been destroyed (but as of yet, this
+				 * client hasn't recognized it). */
+
+    Blt_Chain *events;		/* Chain of node event handlers. */
+    Blt_Chain *traces;		/* Chain of data field callbacks. */
+    Blt_TreeNode root;		/* Designated root for this client */
+    Blt_TreeTagTable *tagTablePtr;
+};
+
+
+typedef int (Blt_TreeNotifyEventProc) _ANSI_ARGS_((ClientData clientData, 
+	Blt_TreeNotifyEvent *eventPtr));
+
+typedef int (Blt_TreeTraceProc) _ANSI_ARGS_((ClientData clientData, 
+	Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key, 
+	unsigned int flags));
+
+typedef int (Blt_TreeEnumProc) _ANSI_ARGS_((Blt_TreeNode node, Blt_TreeKey key,
+	Tcl_Obj *valuePtr));
+
+typedef int (Blt_TreeCompareNodesProc) _ANSI_ARGS_((Blt_TreeNode *n1Ptr, 
+	Blt_TreeNode *n2Ptr));
+
+typedef int (Blt_TreeApplyProc) _ANSI_ARGS_((Blt_TreeNode node, 
+	ClientData clientData, int order));
+
+struct Blt_TreeTraceStruct {
+    ClientData clientData;
+    Blt_TreeKey key;
+    Blt_TreeNode node;
+    unsigned int mask;
+    Blt_TreeTraceProc *proc;
+};
+
+/*
+ * Structure definition for information used to keep track of searches
+ * through hash tables:p
+ */
+struct Blt_TreeKeySearchStruct {
+    Blt_TreeNode node;		/* Table being searched. */
+    unsigned long nextIndex;	/* Index of next bucket to be
+				 * enumerated after present one. */
+    Blt_TreeValue nextValue;	/* Next entry to be enumerated in the
+				 * the current bucket. */
+};
+
+EXTERN Blt_TreeKey Blt_TreeGetKey _ANSI_ARGS_((CONST char *string));
+
+EXTERN Blt_TreeNode Blt_TreeCreateNode _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode parent, CONST char *name, int position)); 
+EXTERN Blt_TreeNode Blt_TreeCreateNodeWithId _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode parent, CONST char *name, int position, int inode)); 
+
+EXTERN int Blt_TreeDeleteNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node));
+
+EXTERN int Blt_TreeMoveNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	Blt_TreeNode parent, Blt_TreeNode before));
+
+EXTERN Blt_TreeNode Blt_TreeGetNode _ANSI_ARGS_((Blt_Tree tree, 
+	unsigned int inode));
+
+EXTERN Blt_TreeNode Blt_TreeFindChild _ANSI_ARGS_((Blt_TreeNode parent, 
+	CONST char *name));
+
+EXTERN Blt_TreeNode Blt_TreeFirstChild _ANSI_ARGS_((Blt_TreeNode parent));
+
+EXTERN Blt_TreeNode Blt_TreeNextSibling _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeLastChild _ANSI_ARGS_((Blt_TreeNode parent));
+
+EXTERN Blt_TreeNode Blt_TreePrevSibling _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeNextNode _ANSI_ARGS_((Blt_TreeNode root, 
+	Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreePrevNode _ANSI_ARGS_((Blt_TreeNode root,
+	Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeChangeRoot _ANSI_ARGS_((Blt_Tree tree,
+	Blt_TreeNode node));
+
+EXTERN Blt_TreeNode Blt_TreeEndNode _ANSI_ARGS_((Blt_TreeNode node,
+	unsigned int nodeFlags));
+
+EXTERN int Blt_TreeIsBefore _ANSI_ARGS_((Blt_TreeNode node1, 
+	Blt_TreeNode node2));
+
+EXTERN int Blt_TreeIsAncestor _ANSI_ARGS_((Blt_TreeNode node1, 
+	Blt_TreeNode node2));
+
+EXTERN int Blt_TreePrivateValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+	Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreePublicValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree,
+	Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreeGetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *string, Tcl_Obj **valuePtr));
+
+EXTERN int Blt_TreeValueExists _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	CONST char *string));
+
+EXTERN int Blt_TreeSetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *string, Tcl_Obj *valuePtr));
+
+EXTERN int Blt_TreeUnsetValue _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *string));
+
+EXTERN int Blt_TreeGetArrayValue _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, CONST char *arrayName, 
+	CONST char *elemName, Tcl_Obj **valueObjPtrPtr));
+
+EXTERN int Blt_TreeSetArrayValue _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, CONST char *arrayName, 
+	CONST char *elemName, Tcl_Obj *valueObjPtr));
+
+EXTERN int Blt_TreeUnsetArrayValue _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, CONST char *arrayName, 
+	CONST char *elemName));
+
+EXTERN int Blt_TreeArrayValueExists _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *arrayName, CONST char *elemName));
+
+EXTERN int Blt_TreeArrayNames _ANSI_ARGS_((Tcl_Interp *interp, Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *arrayName, Tcl_Obj *listObjPtr));
+
+EXTERN int Blt_TreeGetValueByKey _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key, 
+	Tcl_Obj **valuePtr));
+
+EXTERN int Blt_TreeSetValueByKey _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key, Tcl_Obj *valuePtr));
+
+EXTERN int Blt_TreeUnsetValueByKey _ANSI_ARGS_((Tcl_Interp *interp, 
+	Blt_Tree tree, Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN int Blt_TreeValueExistsByKey _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode node, Blt_TreeKey key));
+
+EXTERN Blt_TreeKey Blt_TreeFirstKey _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode node, Blt_TreeKeySearch *cursorPtr));
+
+EXTERN Blt_TreeKey Blt_TreeNextKey _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeKeySearch *cursorPtr));
+
+EXTERN int Blt_TreeApply _ANSI_ARGS_((Blt_TreeNode root, 
+	Blt_TreeApplyProc *proc, ClientData clientData));
+
+EXTERN int Blt_TreeApplyDFS _ANSI_ARGS_((Blt_TreeNode root, 
+	Blt_TreeApplyProc *proc, ClientData clientData, int order));
+
+EXTERN int Blt_TreeApplyBFS _ANSI_ARGS_((Blt_TreeNode root, 
+	Blt_TreeApplyProc *proc, ClientData clientData));
+
+EXTERN int Blt_TreeSortNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	Blt_TreeCompareNodesProc *proc));
+
+EXTERN int Blt_TreeCreate _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name,
+	Blt_Tree *treePtr));
+
+EXTERN int Blt_TreeExists _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name));
+
+EXTERN int Blt_TreeGetToken _ANSI_ARGS_((Tcl_Interp *interp, CONST char *name, 
+	Blt_Tree *treePtr));
+
+EXTERN void Blt_TreeReleaseToken _ANSI_ARGS_((Blt_Tree tree));
+
+EXTERN int Blt_TreeSize _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN Blt_TreeTrace Blt_TreeCreateTrace _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_TreeNode node, CONST char *keyPattern, CONST char *tagName,
+	unsigned int mask, Blt_TreeTraceProc *proc, ClientData clientData));
+
+EXTERN void Blt_TreeDeleteTrace _ANSI_ARGS_((Blt_TreeTrace token));
+
+EXTERN void Blt_TreeCreateEventHandler _ANSI_ARGS_((Blt_Tree tree, 
+	unsigned int mask, Blt_TreeNotifyEventProc *proc, 
+	ClientData clientData));
+
+EXTERN void Blt_TreeDeleteEventHandler _ANSI_ARGS_((Blt_Tree tree, 
+	unsigned int mask, Blt_TreeNotifyEventProc *proc, 
+	ClientData clientData));
+
+EXTERN void Blt_TreeRelabelNode _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	CONST char *string));
+EXTERN void Blt_TreeRelabelNode2 _ANSI_ARGS_((Blt_TreeNode node, 
+	CONST char *string));
+EXTERN char *Blt_TreeNodePath _ANSI_ARGS_((Blt_TreeNode node, 
+	Tcl_DString *resultPtr));	
+EXTERN int Blt_TreeNodePosition _ANSI_ARGS_((Blt_TreeNode node));
+
+EXTERN void Blt_TreeClearTags _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node));
+EXTERN int Blt_TreeHasTag _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	CONST char *tagName));
+EXTERN void Blt_TreeAddTag _ANSI_ARGS_((Blt_Tree tree, Blt_TreeNode node, 
+	CONST char *tagName));
+EXTERN void Blt_TreeForgetTag _ANSI_ARGS_((Blt_Tree tree, CONST char *tagName));
+EXTERN Blt_HashTable *Blt_TreeTagHashTable _ANSI_ARGS_((Blt_Tree tree, 
+	CONST char *tagName));
+EXTERN int Blt_TreeTagTableIsShared _ANSI_ARGS_((Blt_Tree tree));
+EXTERN int Blt_TreeShareTagTable _ANSI_ARGS_((Blt_Tree src, Blt_Tree target));
+EXTERN Blt_HashEntry *Blt_TreeFirstTag _ANSI_ARGS_((Blt_Tree tree, 
+	Blt_HashSearch *searchPtr));
+
+#define Blt_TreeName(token)	((token)->treeObject->name)
+#define Blt_TreeRootNode(token)	((token)->root)
+#define Blt_TreeChangeRoot(token, node) ((token)->root = (node))
+
+#define Blt_TreeNodeDepth(token, node)	((node)->depth - (token)->root->depth)
+#define Blt_TreeNodeLabel(node)	 ((node)->label)
+#define Blt_TreeNodeId(node)	 ((node)->inode)
+#define Blt_TreeNodeParent(node) ((node)->parent)
+#define Blt_TreeNodeDegree(node) ((node)->nChildren)
+
+#define Blt_TreeIsLeaf(node)     ((node)->nChildren == 0)
+#define Blt_TreeNextNodeId(token)     ((token)->treeObject->nextInode)
+
+#define Blt_TreeFirstChild(node) ((node)->first)
+#define Blt_TreeLastChild(node) ((node)->last)
+#define Blt_TreeNextSibling(node) (((node) == NULL) ? NULL : (node)->next)
+#define Blt_TreePrevSibling(node) (((node) == NULL) ? NULL : (node)->prev)
+
+#endif /* _BLT_TREE_H */
+
Index: trunk/kitgen/8.x/blt/generic/bltTreeCmd.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeCmd.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeCmd.c	(revision 175)
@@ -0,0 +1,5757 @@
+
+/*
+ *
+ * bltTreeCmd.c --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "tree" data object was created by George A. Howlett.
+ */
+
+/*
+  tree create t0 t1 t2
+  tree names
+  t0 destroy
+     -or-
+  tree destroy t0
+  tree copy tree@node tree@node -recurse -tags
+
+  tree move node after|before|into t2@node
+
+  $t apply -recurse $root command arg arg			
+
+  $t attach treename				
+
+  $t children $n
+  t0 copy node1 node2 node3 node4 node5 destName 
+  $t delete $n...				
+  $t depth $n
+  $t dump $root
+  $t dumpfile $root fileName
+  $t dup $t2		
+  $t find $root -name pat -name pattern
+  $t firstchild $n
+  $t get $n $key
+  $t get $n $key(abc)
+  $t index $n
+  $t insert $parent $switches?
+  $t isancestor $n1 $n2
+  $t isbefore $n1 $n2
+  $t isleaf $n
+  $t lastchild $n
+  $t move $n1 after|before|into $n2
+  $t next $n
+  $t nextsibling $n
+  $t path $n1 $n2 $n3...
+  $t parent $n
+  $t previous $n
+  $t prevsibling $n
+  $t restore $root data -overwrite
+  $t root ?$n?
+
+  $t set $n $key $value ?$key $value?
+  $t size $n
+  $t slink $n $t2@$node				???
+  $t sort -recurse $root		
+
+  $t tag delete tag1 tag2 tag3...
+  $t tag names
+  $t tag nodes $tag
+  $t tag set $n tag1 tag2 tag3...
+  $t tag unset $n tag1 tag2 tag3...
+
+  $t trace create $n $key how command		
+  $t trace delete id1 id2 id3...
+  $t trace names
+  $t trace info $id
+
+  $t unset $n key1 key2 key3...
+  
+  $t notify create -oncreate -ondelete -onmove command 
+  $t notify create -oncreate -ondelete -onmove -onsort command arg arg arg 
+  $t notify delete id1 id2 id3
+  $t notify names
+  $t notify info id
+
+  for { set n [$t firstchild $node] } { $n >= 0 } { 
+        set n [$t nextsibling $n] } {
+  }
+  foreach n [$t children $node] { 
+	  
+  }
+  set n [$t next $node]
+  set n [$t previous $node]
+
+*/
+
+#include <bltInt.h>
+
+#ifndef NO_TREE
+
+#include <bltTree.h>
+#include <bltHash.h>
+#include <bltList.h>
+#include "bltSwitch.h"
+#include <ctype.h>
+
+#define TREE_THREAD_KEY "BLT Tree Command Data"
+#define TREE_MAGIC ((unsigned int) 0x46170277)
+
+enum TagTypes { TAG_TYPE_NONE, TAG_TYPE_ALL, TAG_TYPE_TAG };
+
+typedef struct {
+    Blt_HashTable treeTable;	/* Hash table of trees keyed by address. */
+    Tcl_Interp *interp;
+} TreeCmdInterpData;
+
+typedef struct {
+    Tcl_Interp *interp;
+    Tcl_Command cmdToken;	/* Token for tree's Tcl command. */
+    Blt_Tree tree;		/* Token holding internal tree. */
+
+    Blt_HashEntry *hashPtr;
+    Blt_HashTable *tablePtr;
+
+    TreeCmdInterpData *dataPtr;	/*  */
+
+    int traceCounter;		/* Used to generate trace id strings.  */
+    Blt_HashTable traceTable;	/* Table of active traces. Maps trace ids
+				 * back to their TraceInfo records. */
+
+    int notifyCounter;		/* Used to generate notify id strings. */
+    Blt_HashTable notifyTable;	/* Table of event handlers. Maps notify ids
+				 * back to their NotifyInfo records. */
+} TreeCmd;
+
+typedef struct {
+    TreeCmd *cmdPtr;
+    Blt_TreeNode node;
+
+    Blt_TreeTrace traceToken;
+
+    char *withTag;		/* If non-NULL, the event or trace was
+				 * specified with this tag.  This
+				 * value is saved for informational
+				 * purposes.  The tree's trace
+				 * matching routines do the real
+				 * checking, not the client's
+				 * callback.  */
+
+    char command[1];		/* Command prefix for the trace or notify
+				 * Tcl callback.  Extra arguments will be
+				 * appended to the end. Extra space will
+				 * be allocated for the length of the string
+				 */
+} TraceInfo;
+
+typedef struct {
+    TreeCmd *cmdPtr;
+    int mask;
+    Tcl_Obj **objv;
+    int objc;
+    Blt_TreeNode node;		/* Node affected by event. */
+    Blt_TreeTrace notifyToken;
+} NotifyInfo;
+
+
+typedef struct {
+    int mask;
+} NotifyData;
+
+static Blt_SwitchSpec notifySwitches[] = 
+{
+    {BLT_SWITCH_FLAG, "-create", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_CREATE},
+    {BLT_SWITCH_FLAG, "-delete", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_DELETE},
+    {BLT_SWITCH_FLAG, "-move", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_MOVE},
+    {BLT_SWITCH_FLAG, "-sort", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_SORT},
+    {BLT_SWITCH_FLAG, "-relabel", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_RELABEL},
+    {BLT_SWITCH_FLAG, "-allevents", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_ALL},
+    {BLT_SWITCH_FLAG, "-whenidle", Blt_Offset(NotifyData, mask), 0, 0, 
+	TREE_NOTIFY_WHENIDLE},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToChild;
+#define INSERT_BEFORE	(ClientData)0
+#define INSERT_AFTER	(ClientData)1
+static Blt_SwitchCustom beforeSwitch =
+{
+    StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
+};
+static Blt_SwitchCustom afterSwitch =
+{
+    StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
+};
+
+typedef struct {
+    char *label;
+    int insertPos;
+    int inode;
+    char **tags;
+    char **dataPairs;
+    Blt_TreeNode parent;
+} InsertData;
+
+static Blt_SwitchSpec insertSwitches[] = 
+{
+    {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0, 
+	&afterSwitch},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
+    {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
+	&beforeSwitch},
+    {BLT_SWITCH_LIST, "-data", Blt_Offset(InsertData, dataPairs), 0},
+    {BLT_SWITCH_STRING, "-label", Blt_Offset(InsertData, label), 0},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-node", Blt_Offset(InsertData, inode), 0},
+    {BLT_SWITCH_LIST, "-tags", Blt_Offset(InsertData, tags), 0},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+#define PATTERN_EXACT	(1)
+#define PATTERN_GLOB	(2)
+#define PATTERN_MASK	(0x3)
+#define PATTERN_NONE	(0)
+#define PATTERN_REGEXP	(3)
+#define MATCH_INVERT		(1<<8)
+#define MATCH_LEAFONLY		(1<<4)
+#define MATCH_NOCASE		(1<<5)
+#define MATCH_PATHNAME		(1<<6)
+
+typedef struct {
+    TreeCmd *cmdPtr;		/* Tree to examine. */
+    Tcl_Obj *listObjPtr;	/* List to accumulate the indices of 
+				 * matching nodes. */
+    Tcl_Obj **objv;		/* Command converted into an array of 
+				 * Tcl_Obj's. */
+    int objc;			/* Number of Tcl_Objs in above array. */
+
+    int nMatches;		/* Current number of matches. */
+
+    unsigned int flags;		/* See flags definitions above. */
+
+    /* Integer options. */
+    int maxMatches;		/* If > 0, stop after this many matches. */
+    int maxDepth;		/* If > 0, don't descend more than this
+				 * many levels. */
+    int order;			/* Order of search: Can be either
+				 * TREE_PREORDER, TREE_POSTORDER,
+				 * TREE_INORDER, TREE_BREADTHFIRST. */
+    /* String options. */
+    Blt_List patternList;	/* List of patterns to compare with labels
+				 * or values.  */
+    char *addTag;		/* If non-NULL, tag added to selected nodes. */
+
+    char **command;		/* Command split into a Tcl list. */
+
+    Blt_List keyList;		/* List of key name patterns. */
+    char *withTag;
+
+} FindData;
+
+static Blt_SwitchParseProc StringToOrder;
+static Blt_SwitchCustom orderSwitch =
+{
+    StringToOrder, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+static Blt_SwitchParseProc StringToPattern;
+static Blt_SwitchFreeProc FreePatterns;
+
+static Blt_SwitchCustom regexpSwitch =
+{
+    StringToPattern, FreePatterns, (ClientData)PATTERN_REGEXP,
+};
+static Blt_SwitchCustom globSwitch =
+{
+    StringToPattern, FreePatterns, (ClientData)PATTERN_GLOB,
+};
+static Blt_SwitchCustom exactSwitch =
+{
+    StringToPattern, FreePatterns, (ClientData)PATTERN_EXACT,
+};
+
+
+static Blt_SwitchSpec findSwitches[] = 
+{
+    {BLT_SWITCH_STRING, "-addtag", Blt_Offset(FindData, addTag), 0},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-count", Blt_Offset(FindData, maxMatches), 0},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(FindData, maxDepth), 0},
+    {BLT_SWITCH_CUSTOM, "-exact", Blt_Offset(FindData, patternList), 0,
+        &exactSwitch},
+    {BLT_SWITCH_LIST, "-exec", Blt_Offset(FindData, command), 0},
+    {BLT_SWITCH_CUSTOM, "-glob", Blt_Offset(FindData, patternList), 0, 
+	&globSwitch},
+    {BLT_SWITCH_FLAG, "-invert", Blt_Offset(FindData, flags), 0, 0, 
+	MATCH_INVERT},
+    {BLT_SWITCH_CUSTOM, "-key", Blt_Offset(FindData, keyList), 0, &exactSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyexact", Blt_Offset(FindData, keyList), 0, 
+	&exactSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyglob", Blt_Offset(FindData, keyList), 0, 
+	&globSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyregexp", Blt_Offset(FindData, keyList), 0, 
+	&regexpSwitch},
+    {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(FindData, flags), 0, 0, 
+	MATCH_LEAFONLY},
+    {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(FindData, flags), 0, 0, 
+	MATCH_NOCASE},
+    {BLT_SWITCH_CUSTOM, "-order", Blt_Offset(FindData, order), 0, &orderSwitch},
+    {BLT_SWITCH_FLAG, "-path", Blt_Offset(FindData, flags), 0, 0, 
+	MATCH_PATHNAME},
+    {BLT_SWITCH_CUSTOM, "-regexp", Blt_Offset(FindData, patternList), 0, 
+	&regexpSwitch},
+    {BLT_SWITCH_STRING, "-tag", Blt_Offset(FindData, withTag), 0},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToNode;
+static Blt_SwitchCustom nodeSwitch =
+{
+    StringToNode, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+    TreeCmd *cmdPtr;		/* Tree to move nodes. */
+    Blt_TreeNode node;
+    int movePos;
+} MoveData;
+
+static Blt_SwitchSpec moveSwitches[] = 
+{
+    {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(MoveData, node), 0, &nodeSwitch},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(MoveData, movePos), 0},
+    {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(MoveData, node), 0, &nodeSwitch},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+    Blt_TreeNode srcNode, destNode;
+    Blt_Tree srcTree, destTree;
+    TreeCmd *srcPtr, *destPtr;
+    unsigned int flags;
+    char *label;
+} CopyData;
+
+#define COPY_RECURSE	(1<<0)
+#define COPY_TAGS	(1<<1)
+#define COPY_OVERWRITE	(1<<2)
+
+static Blt_SwitchSpec copySwitches[] = 
+{
+    {BLT_SWITCH_STRING, "-label", Blt_Offset(CopyData, label), 0},
+    {BLT_SWITCH_FLAG, "-recurse", Blt_Offset(CopyData, flags), 0, 0, 
+	COPY_RECURSE},
+    {BLT_SWITCH_FLAG, "-tags", Blt_Offset(CopyData, flags), 0, 0, 
+	COPY_TAGS},
+    {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(CopyData, flags), 0, 0, 
+	COPY_OVERWRITE},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+    TreeCmd *cmdPtr;		/* Tree to examine. */
+    Tcl_Obj **preObjv;		/* Command converted into an array of 
+				 * Tcl_Obj's. */
+    int preObjc;		/* Number of Tcl_Objs in above array. */
+
+    Tcl_Obj **postObjv;		/* Command converted into an array of 
+				 * Tcl_Obj's. */
+    int postObjc;		/* Number of Tcl_Objs in above array. */
+
+    unsigned int flags;		/* See flags definitions above. */
+
+    int maxDepth;		/* If > 0, don't descend more than this
+				 * many levels. */
+    /* String options. */
+    Blt_List patternList;	/* List of label or value patterns. */
+    char **preCmd;		/* Pre-command split into a Tcl list. */
+    char **postCmd;		/* Post-command split into a Tcl list. */
+
+    Blt_List keyList;		/* List of key-name patterns. */
+    char *withTag;
+} ApplyData;
+
+static Blt_SwitchSpec applySwitches[] = 
+{
+    {BLT_SWITCH_LIST, "-precommand", Blt_Offset(ApplyData, preCmd), 0},
+    {BLT_SWITCH_LIST, "-postcommand", Blt_Offset(ApplyData, postCmd), 0},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(ApplyData, maxDepth), 0},
+    {BLT_SWITCH_CUSTOM, "-exact", Blt_Offset(ApplyData, patternList), 0,
+	&exactSwitch},
+    {BLT_SWITCH_CUSTOM, "-glob", Blt_Offset(ApplyData, patternList), 0, 
+	&globSwitch},
+    {BLT_SWITCH_FLAG, "-invert", Blt_Offset(ApplyData, flags), 0, 0, 
+	MATCH_INVERT},
+    {BLT_SWITCH_CUSTOM, "-key", Blt_Offset(ApplyData, keyList), 0, 
+	&exactSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyexact", Blt_Offset(ApplyData, keyList), 0, 
+	&exactSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyglob", Blt_Offset(ApplyData, keyList), 0, 
+	&globSwitch},
+    {BLT_SWITCH_CUSTOM, "-keyregexp", Blt_Offset(ApplyData, keyList), 0, 
+	&regexpSwitch},
+    {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(ApplyData, flags), 0, 0, 
+	MATCH_LEAFONLY},
+    {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(ApplyData, flags), 0, 0, 
+	MATCH_NOCASE},
+    {BLT_SWITCH_FLAG, "-path", Blt_Offset(ApplyData, flags), 0, 0, 
+	MATCH_PATHNAME},
+    {BLT_SWITCH_CUSTOM, "-regexp", Blt_Offset(ApplyData, patternList), 0,
+	&regexpSwitch},
+    {BLT_SWITCH_STRING, "-tag", Blt_Offset(ApplyData, withTag), 0},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+typedef struct {
+    unsigned int flags;
+    Blt_HashTable idTable;
+    Blt_TreeNode root;
+} RestoreData;
+
+#define RESTORE_NO_TAGS		(1<<0)
+#define RESTORE_OVERWRITE	(1<<1)
+
+static Blt_SwitchSpec restoreSwitches[] = 
+{
+    {BLT_SWITCH_FLAG, "-notags", Blt_Offset(RestoreData, flags), 0, 0, 
+	RESTORE_NO_TAGS},
+    {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(RestoreData, flags), 0, 0, 
+	RESTORE_OVERWRITE},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc StringToFormat;
+static Blt_SwitchCustom formatSwitch =
+{
+    StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
+};
+
+typedef struct {
+    int sort;			/* If non-zero, sort the nodes.  */
+    int withParent;		/* If non-zero, add the parent node id 
+				 * to the output of the command.*/
+    int withId;			/* If non-zero, echo the node id in the
+				 * output of the command. */
+} PositionData;
+
+#define POSITION_SORTED		(1<<0)
+
+static Blt_SwitchSpec positionSwitches[] = 
+{
+    {BLT_SWITCH_FLAG, "-sort", Blt_Offset(PositionData, sort), 0, 0,
+       POSITION_SORTED},
+    {BLT_SWITCH_CUSTOM, "-format", 0, 0, &formatSwitch},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+
+static Tcl_InterpDeleteProc TreeInterpDeleteProc;
+static Blt_TreeApplyProc MatchNodeProc, SortApplyProc;
+static Blt_TreeApplyProc ApplyNodeProc;
+static Blt_TreeTraceProc TreeTraceProc;
+static Tcl_CmdDeleteProc TreeInstDeleteProc;
+static Blt_TreeCompareNodesProc CompareNodes;
+
+static Tcl_ObjCmdProc TreeObjCmd;
+static Tcl_ObjCmdProc CompareDictionaryCmd;
+static Tcl_ObjCmdProc ExitCmd;
+static Blt_TreeNotifyEventProc TreeEventProc;
+
+static int GetNode _ANSI_ARGS_((TreeCmd *cmdPtr, Tcl_Obj *objPtr, 
+	Blt_TreeNode *nodePtr));
+
+static int nLines;
+
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToChild --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToChild(
+    ClientData clientData,	/* Flag indicating if the node is
+				 * considered before or after the
+				 * insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    char *switchName,		/* Not used. */
+    char *string,		/* String representation */
+    char *record,		/* Structure record */
+    int offset)			/* Offset to field in structure */
+{
+    InsertData *dataPtr = (InsertData *)record;
+    Blt_TreeNode node;
+    
+    node = Blt_TreeFindChild(dataPtr->parent, string);
+    if (node == NULL) {
+	Tcl_AppendResult(interp, "can't find a child named \"", string, 
+		 "\" in \"", Blt_TreeNodeLabel(dataPtr->parent), "\"",
+		 (char *)NULL);	 
+	return TCL_ERROR;
+    }			  
+    dataPtr->insertPos = Blt_TreeNodeDegree(node);
+    if (clientData == INSERT_AFTER) {
+	dataPtr->insertPos++;
+    } 
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToNode --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToNode(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    char *switchName,		/* Not used. */
+    char *string,		/* String representation */
+    char *record,		/* Structure record */
+    int offset)			/* Offset to field in structure */
+{
+    MoveData *dataPtr = (MoveData *)record;
+    Blt_TreeNode node;
+    Tcl_Obj *objPtr;
+    TreeCmd *cmdPtr = dataPtr->cmdPtr;
+    int result;
+
+    objPtr = Tcl_NewStringObj(string, -1);
+    result = GetNode(cmdPtr, objPtr, &node);
+    Tcl_DecrRefCount(objPtr);
+    if (result != TCL_OK) {
+	return TCL_ERROR;
+    }
+    dataPtr->node = node;
+    return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToOrder --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToOrder(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    char *switchName,		/* Not used. */
+    char *string,		/* String representation */
+    char *record,		/* Structure record */
+    int offset)			/* Offset to field in structure */
+{
+    int *orderPtr = (int *)(record + offset);
+    char c;
+
+    c = string[0];
+    if ((c == 'b') && (strcmp(string, "breadthfirst") == 0)) {
+	*orderPtr = TREE_BREADTHFIRST;
+    } else if ((c == 'i') && (strcmp(string, "inorder") == 0)) {
+	*orderPtr = TREE_INORDER;
+    } else if ((c == 'p') && (strcmp(string, "preorder") == 0)) {
+	*orderPtr = TREE_PREORDER;
+    } else if ((c == 'p') && (strcmp(string, "postorder") == 0)) {
+	*orderPtr = TREE_POSTORDER;
+    } else {
+	Tcl_AppendResult(interp, "bad order \"", string, 
+		 "\": should be breadthfirst, inorder, preorder, or postorder",
+		 (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPattern --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPattern(
+    ClientData clientData,	/* Flag indicating type of pattern. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    char *switchName,		/* Not used. */
+    char *string,		/* String representation */
+    char *record,		/* Structure record */
+    int offset)			/* Offset to field in structure */
+{
+    Blt_List *listPtr = (Blt_List *)(record + offset);
+
+    if (*listPtr == NULL) {
+	*listPtr = Blt_ListCreate(BLT_STRING_KEYS);
+    }
+    Blt_ListAppend(*listPtr, string, clientData);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreePatterns --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreePatterns(char *object)
+{
+    Blt_List list = (Blt_List)object;
+
+    if (list != NULL) {
+	Blt_ListDestroy(list);
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToFormat --
+ *
+ *	Convert a string represent a node number into its integer
+ *	value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToFormat(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    char *switchName,		/* Not used. */
+    char *string,		/* String representation */
+    char *record,		/* Structure record */
+    int offset)			/* Offset to field in structure */
+{
+    PositionData *dataPtr = (PositionData *)record;
+
+    if (strcmp(string, "position") == 0) {
+	dataPtr->withParent = FALSE;
+	dataPtr->withId = FALSE;
+    } else if (strcmp(string, "id+position") == 0) {
+	dataPtr->withParent = FALSE;
+	dataPtr->withId = TRUE;
+    } else if (strcmp(string, "parent-at-position") == 0) {
+	dataPtr->withParent = TRUE;
+	dataPtr->withId = FALSE;
+    } else if (strcmp(string, "id+parent-at-position") == 0) {
+	dataPtr->withParent = TRUE;
+	dataPtr->withId  = TRUE;
+    } else {
+	Tcl_AppendResult(interp, "bad format \"", string, 
+ "\": should be position, parent-at-position, id+position, or id+parent-at-position",
+		 (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTreeCmdInterpData --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static TreeCmdInterpData *
+GetTreeCmdInterpData(Tcl_Interp *interp)
+{
+    TreeCmdInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (TreeCmdInterpData *)
+	Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(TreeCmdInterpData));
+	assert(dataPtr);
+	dataPtr->interp = interp;
+	Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
+		 dataPtr);
+	Blt_InitHashTable(&dataPtr->treeTable, BLT_ONE_WORD_KEYS);
+    }
+    return dataPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTreeCmd --
+ *
+ *	Find the tree command associated with the Tcl command "string".
+ *	
+ *	We have to do multiple lookups to get this right.  
+ *
+ *	The first step is to generate a canonical command name.  If an
+ *	unqualified command name (i.e. no namespace qualifier) is
+ *	given, we should search first the current namespace and then
+ *	the global one.  Most Tcl commands (like Tcl_GetCmdInfo) look
+ *	only at the global namespace.
+ *
+ *	Next check if the string is 
+ *		a) a Tcl command and 
+ *		b) really is a command for a tree object.  
+ *	Tcl_GetCommandInfo will get us the objClientData field that 
+ *	should be a cmdPtr.  We can verify that by searching our hashtable 
+ *	of cmdPtr addresses.
+ *
+ * Results:
+ *	A pointer to the tree command.  If no associated tree command
+ *	can be found, NULL is returned.  It's up to the calling routines
+ *	to generate an error message.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static TreeCmd *
+GetTreeCmd(
+    TreeCmdInterpData *dataPtr, 
+    Tcl_Interp *interp, 
+    CONST char *string)
+{
+    CONST char *name;
+    Tcl_Namespace *nsPtr;
+    Tcl_CmdInfo cmdInfo;
+    Blt_HashEntry *hPtr;
+    Tcl_DString dString;
+    char *treeName;
+    int result;
+
+    /* Put apart the tree name and put is back together in a standard
+     * format. */
+    if (Blt_ParseQualifiedName(interp, string, &nsPtr, &name) != TCL_OK) {
+	return NULL;		/* No such parent namespace. */
+    }
+    if (nsPtr == NULL) {
+	nsPtr = Tcl_GetCurrentNamespace(interp);
+    }
+    /* Rebuild the fully qualified name. */
+    treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+    result = Tcl_GetCommandInfo(interp, treeName, &cmdInfo);
+    Tcl_DStringFree(&dString);
+
+    if (!result) {
+	return NULL;
+    }
+    hPtr = Blt_FindHashEntry(&dataPtr->treeTable, 
+			     (char *)(cmdInfo.objClientData));
+    if (hPtr == NULL) {
+	return NULL;
+    }
+    return Blt_GetHashValue(hPtr);
+}
+
+static Blt_TreeNode 
+ParseModifiers(
+    Tcl_Interp *interp,
+    Blt_Tree tree,
+    Blt_TreeNode node,
+    char *modifiers)
+{
+    char *p, *np;
+
+    p = modifiers;
+    do {
+	p += 2;			/* Skip the initial "->" */
+	np = strstr(p, "->");
+	if (np != NULL) {
+	    *np = '\0';
+	}
+	if ((*p == 'p') && (strcmp(p, "parent") == 0)) {
+	    node = Blt_TreeNodeParent(node);
+	} else if ((*p == 'f') && (strcmp(p, "firstchild") == 0)) {
+	    node = Blt_TreeFirstChild(node);
+	} else if ((*p == 'l') && (strcmp(p, "lastchild") == 0)) {
+	    node = Blt_TreeLastChild(node);
+	} else if ((*p == 'n') && (strcmp(p, "next") == 0)) {
+	    node = Blt_TreeNextNode(Blt_TreeRootNode(tree), node);
+	} else if ((*p == 'n') && (strcmp(p, "nextsibling") == 0)) {
+	    node = Blt_TreeNextSibling(node);
+	} else if ((*p == 'p') && (strcmp(p, "previous") == 0)) {
+	    node = Blt_TreePrevNode(Blt_TreeRootNode(tree), node);
+	} else if ((*p == 'p') && (strcmp(p, "prevsibling") == 0)) {
+	    node = Blt_TreePrevSibling(node);
+	} else if (isdigit(UCHAR(*p))) {
+	    int inode;
+	    
+	    if (Tcl_GetInt(interp, p, &inode) != TCL_OK) {
+		node = NULL;
+	    } else {
+		node = Blt_TreeGetNode(tree, inode);
+	    }
+	} else {
+	    char *endp;
+
+	    if (np != NULL) {
+		endp = np - 1;
+	    } else {
+		endp = p + strlen(p) - 1;
+	    }
+	    if ((*p == '"') && (*endp == '"')) {
+		*endp = '\0';
+		node = Blt_TreeFindChild(node, p + 1);
+		*endp = '"';
+	    } else {
+		node = Blt_TreeFindChild(node, p);
+	    }		
+	}
+	if (node == NULL) {
+	    goto error;
+	}
+	if (np != NULL) {
+	    *np = '-';		/* Repair the string */
+	}
+	p = np;
+    } while (np != NULL);
+    return node;
+ error:
+    if (np != NULL) {
+	*np = '-';		/* Repair the string */
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetForeignNode --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int 
+GetForeignNode(
+    Tcl_Interp *interp,
+    Blt_Tree tree,
+    Tcl_Obj *objPtr,
+    Blt_TreeNode *nodePtr)
+{
+    char c;
+    Blt_TreeNode node;
+    char *string;
+    char *p;
+
+    string = Tcl_GetString(objPtr);
+    c = string[0];
+
+    /* 
+     * Check if modifiers are present.
+     */
+    p = strstr(string, "->");
+    if (isdigit(UCHAR(c))) {
+	int inode;
+
+	if (p != NULL) {
+	    char save;
+	    int result;
+
+	    save = *p;
+	    *p = '\0';
+	    result = Tcl_GetInt(interp, string, &inode);
+	    *p = save;
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	} else {
+	    if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	node = Blt_TreeGetNode(tree, inode);
+	if (p != NULL) {
+	    node = ParseModifiers(interp, tree, node, p);
+	}
+	if (node != NULL) {
+	    *nodePtr = node;
+	    return TCL_OK;
+	}
+    }
+    Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+	 Blt_TreeName(tree), (char *)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNode --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int 
+GetNode(TreeCmd *cmdPtr, Tcl_Obj *objPtr, Blt_TreeNode *nodePtr)
+{
+    Tcl_Interp *interp = cmdPtr->interp;
+    Blt_Tree tree = cmdPtr->tree;
+    char c;
+    Blt_TreeNode node;
+    char *string;
+    char *p;
+
+    string = Tcl_GetString(objPtr);
+    c = string[0];
+
+    /* 
+     * Check if modifiers are present.
+     */
+    p = strstr(string, "->");
+    if (isdigit(UCHAR(c))) {
+	int inode;
+
+	if (p != NULL) {
+	    char save;
+	    int result;
+
+	    save = *p;
+	    *p = '\0';
+	    result = Tcl_GetInt(interp, string, &inode);
+	    *p = save;
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	} else {
+	    if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	node = Blt_TreeGetNode(tree, inode);
+    }  else if (cmdPtr != NULL) {
+	char save;
+
+	save = '\0';		/* Suppress compiler warning. */
+	if (p != NULL) {
+	    save = *p;
+	    *p = '\0';
+	}
+	if (strcmp(string, "all") == 0) {
+	    if (Blt_TreeSize(Blt_TreeRootNode(tree)) > 1) {
+		Tcl_AppendResult(interp, "more than one node tagged as \"", 
+				 string, "\"", (char *)NULL);
+		if (p != NULL) {
+		    *p = save;
+		}
+		return TCL_ERROR;
+	    }
+	    node = Blt_TreeRootNode(tree);
+	} else if (strcmp(string, "root") == 0) {
+	    node = Blt_TreeRootNode(tree);
+	} else {
+	    Blt_HashTable *tablePtr;
+	    Blt_HashSearch cursor;
+	    Blt_HashEntry *hPtr;
+	    int result;
+
+	    node = NULL;
+	    result = TCL_ERROR;
+	    tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
+	    if (tablePtr == NULL) {
+		Tcl_AppendResult(interp, "can't find tag or id \"", string, 
+			"\" in ", Blt_TreeName(cmdPtr->tree), (char *)NULL);
+	    } else if (tablePtr->numEntries > 1) {
+		Tcl_AppendResult(interp, "more than one node tagged as \"", 
+			 string, "\"", (char *)NULL);
+	    } else if (tablePtr->numEntries > 0) {
+		hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
+		node = Blt_GetHashValue(hPtr);
+		result = TCL_OK;
+	    }
+	    if (result == TCL_ERROR) {
+		if (p != NULL) {
+		    *p = save;
+		}
+		return TCL_ERROR;
+	    }
+	}
+	if (p != NULL) {
+	    *p = save;
+	}
+    }
+    if (node != NULL) {
+	if (p != NULL) {
+	    node = ParseModifiers(interp, tree, node, p);
+	    if (node == NULL) {
+		goto error;
+	    }
+	}
+	*nodePtr = node;
+	return TCL_OK;
+    }
+ error:
+    Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
+		 Blt_TreeName(tree), (char *)NULL);
+    return TCL_ERROR;
+}
+
+typedef struct {
+    int tagType;
+    Blt_TreeNode root;
+    Blt_HashSearch cursor;
+} TagSearch;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FirstTaggedNode --
+ *
+ *	Returns the id of the first node tagged by the given tag in
+ *	objPtr.  It basically hides much of the cumbersome special
+ *	case details.  For example, the special tags "root" and "all"
+ *	always exist, so they don't have entries in the tag hashtable.
+ *	If it's a hashed tag (not "root" or "all"), we have to save
+ *	the place of where we are in the table for the next call to
+ *	NextTaggedNode.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_TreeNode
+FirstTaggedNode(
+    Tcl_Interp *interp,
+    TreeCmd *cmdPtr,
+    Tcl_Obj *objPtr,
+    TagSearch *cursorPtr)
+{
+    Blt_TreeNode node, root;
+    char *string;
+
+    node = NULL;
+
+    root = Blt_TreeRootNode(cmdPtr->tree);
+    string = Tcl_GetString(objPtr);
+    cursorPtr->tagType = TAG_TYPE_NONE;
+    cursorPtr->root = root;
+
+    /* Process strings with modifiers or digits as simple ids, not
+     * tags. */
+    if ((strstr(string, "->") != NULL) || (isdigit(UCHAR(*string)))) {
+	if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) {
+	    return NULL;
+	}
+	return node;
+    }
+    if (strcmp(string, "all") == 0) {
+	cursorPtr->tagType = TAG_TYPE_ALL;
+	return root;
+    } else if (strcmp(string, "root") == 0)  {
+	return root;
+    } else {
+	Blt_HashTable *tablePtr;
+	
+	tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
+	if (tablePtr != NULL) {
+	    Blt_HashEntry *hPtr;
+	    
+	    cursorPtr->tagType = TAG_TYPE_TAG;
+	    hPtr = Blt_FirstHashEntry(tablePtr, &(cursorPtr->cursor)); 
+	    if (hPtr == NULL) {
+		return NULL;
+	    }
+	    node = Blt_GetHashValue(hPtr);
+	    return node;
+	}
+    }
+    Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", 
+	Blt_TreeName(cmdPtr->tree), (char *)NULL);
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextTaggedNode --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_TreeNode
+NextTaggedNode(Blt_TreeNode node, TagSearch *cursorPtr)
+{
+    if (cursorPtr->tagType == TAG_TYPE_ALL) {
+	return Blt_TreeNextNode(cursorPtr->root, node);
+    }
+    if (cursorPtr->tagType == TAG_TYPE_TAG) {
+	Blt_HashEntry *hPtr;
+
+	hPtr = Blt_NextHashEntry(&(cursorPtr->cursor));
+	if (hPtr == NULL) {
+	    return NULL;
+	}
+	return Blt_GetHashValue(hPtr);
+    }
+    return NULL;
+}
+
+static int
+AddTag(TreeCmd *cmdPtr, Blt_TreeNode node, CONST char *tagName)
+{
+    if (strcmp(tagName, "root") == 0) {
+	Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
+			 tagName, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Blt_TreeAddTag(cmdPtr->tree, node, tagName);
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteNode --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+DeleteNode(TreeCmd *cmdPtr, Blt_TreeNode node)
+{
+    Blt_TreeNode root;
+
+    if (!Blt_TreeTagTableIsShared(cmdPtr->tree)) {
+	Blt_TreeClearTags(cmdPtr->tree, node);
+    }
+    root = Blt_TreeRootNode(cmdPtr->tree);
+    if (node == root) {
+	Blt_TreeNode next;
+	/* Don't delete the root node. Simply clean out the tree. */
+	for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
+	    next = Blt_TreeNextSibling(node);
+	    Blt_TreeDeleteNode(cmdPtr->tree, node);
+	}	    
+    } else if (Blt_TreeIsAncestor(root, node)) {
+	Blt_TreeDeleteNode(cmdPtr->tree, node);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNodePath --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static char *
+GetNodePath(
+    TreeCmd *cmdPtr,
+    Blt_TreeNode root, 
+    Blt_TreeNode node,
+    int rootFlag,		/* If non-zero, indicates to include
+				 * the root name in the path */
+    Tcl_DString *resultPtr)
+{
+    char **nameArr;		/* Used to stack the component names. */
+    char *staticSpace[64];
+    register int i;
+    int nLevels;
+
+    nLevels = Blt_TreeNodeDepth(cmdPtr->tree, node) -
+	Blt_TreeNodeDepth(cmdPtr->tree, root);
+    if (rootFlag) {
+	nLevels++;
+    }
+    if (nLevels > 64) {
+	nameArr = Blt_Malloc(nLevels * sizeof(char *));
+	assert(nameArr);
+    } else {
+	nameArr = staticSpace;
+    }
+    for (i = nLevels; i > 0; i--) {
+	/* Save the name of each ancestor in the name array. 
+	 * Note that we ignore the root. */
+	nameArr[i - 1] = Blt_TreeNodeLabel(node);
+	node = Blt_TreeNodeParent(node);
+    }
+    /* Append each the names in the array. */
+    Tcl_DStringInit(resultPtr);
+    for (i = 0; i < nLevels; i++) {
+	Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+    }
+    if (nameArr != staticSpace) {
+	Blt_Free(nameArr);
+    }
+    return Tcl_DStringValue(resultPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseNode5 --
+ *
+ *	Parses and creates a node based upon the first 3 fields of
+ *	a five field entry.  This is the new restore file format.
+ *
+ *		parentId nodeId pathList dataList tagList
+ *
+ *	The purpose is to attempt to save and restore the node ids
+ *	embedded in the restore file information.  The old format
+ *	could not distinquish between two sibling nodes with the same
+ *	label unless they were both leaves.  I'm trying to avoid
+ *	dependencies upon labels.  
+ *
+ *	If you're starting from an empty tree, this obviously should
+ *	work without a hitch.  We only need to map the file's root id
+ *	to 0.  It's a little more complicated when adding node to an
+ *	already full tree.  
+ *
+ *	First see if the node id isn't already in use.  Otherwise, map
+ *	the node id (via a hashtable) to the real node. We'll need it
+ *	later when subsequent entries refer to their parent id.
+ *
+ *	If a parent id is unknown (the restore file may be out of
+ *	order), then follow plan B and use its path.
+ *	
+ *---------------------------------------------------------------------- 
+ */
+static Blt_TreeNode
+ParseNode5(TreeCmd *cmdPtr, char **argv, RestoreData *dataPtr)
+{
+    Blt_HashEntry *hPtr;
+    Blt_TreeNode node, parent;
+    char **names;
+    int nNames, isNew;
+    int parentId, nodeId;
+
+    if ((Tcl_GetInt(cmdPtr->interp, argv[0], &parentId) != TCL_OK) ||
+	(Tcl_GetInt(cmdPtr->interp, argv[1], &nodeId) != TCL_OK) ||
+	(Tcl_SplitList(cmdPtr->interp, argv[2], &nNames, &names) != TCL_OK)) {
+	return NULL;
+    }    
+
+    if (parentId == -1) {	/* Dump marks root's parent as -1. */
+	node = dataPtr->root;
+	/* Create a mapping between the old id and the new node */
+	hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId, 
+		   &isNew);
+	Blt_SetHashValue(hPtr, node);
+	Blt_TreeRelabelNode(cmdPtr->tree, node, names[0]);
+    } else {
+	/* 
+	 * Check if the parent has been translated to another id.
+	 * This can happen when there's a id collision with an
+	 * existing node. 
+	 */
+	hPtr = Blt_FindHashEntry(&dataPtr->idTable, (char *)parentId);
+	if (hPtr != NULL) {
+	    parent = Blt_GetHashValue(hPtr);
+	} else {
+	    /* Check if the id already exists in the tree. */
+	    parent = Blt_TreeGetNode(cmdPtr->tree, parentId);
+	    if (parent == NULL) {
+		/* Parent id doesn't exist (partial restore?). 
+		 * Plan B: Use the path to create/find the parent with 
+		 *	   the requested parent id. */
+		if (nNames > 1) {
+		    int i;
+
+		    for (i = 1; i < (nNames - 2); i++) {
+			node = Blt_TreeFindChild(parent, names[i]);
+			if (node == NULL) {
+			    node = Blt_TreeCreateNode(cmdPtr->tree, parent, 
+			      names[i], -1);
+			}
+			parent = node;
+		    }
+		    node = Blt_TreeFindChild(parent, names[nNames - 2]);
+		    if (node == NULL) {
+			node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, 
+				names[nNames - 2], parentId, -1);
+		    }
+		    parent = node;
+		} else {
+		    parent = dataPtr->root;
+		}
+	    }
+	} 
+	/* Check if old node id already in use. */
+	node = NULL;
+	if (dataPtr->flags & RESTORE_OVERWRITE) {
+	    node = Blt_TreeFindChild(parent, names[nNames - 1]);
+	    /* Create a mapping between the old id and the new node */
+	    hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId, 
+				       &isNew);
+	    Blt_SetHashValue(hPtr, node);
+	}
+	if (node == NULL) {
+	    node = Blt_TreeGetNode(cmdPtr->tree, nodeId);
+	    if (node != NULL) {
+		node = Blt_TreeCreateNode(cmdPtr->tree, parent, 
+					  names[nNames - 1], -1);
+		/* Create a mapping between the old id and the new node */
+		hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
+					   &isNew);
+		Blt_SetHashValue(hPtr, node);
+	    } else {
+		/* Otherwise create a new node with the requested id. */
+		node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, 
+						names[nNames - 1], nodeId, -1);
+	    }
+	}
+    }
+    Blt_Free(names);
+    return node;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseNode3 --
+ *
+ *	Parses and creates a node based upon the first field of
+ *	a three field entry.  This is the old restore file format.
+ *
+ *		pathList dataList tagList
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_TreeNode
+ParseNode3(TreeCmd *cmdPtr, char **argv, RestoreData *dataPtr)
+{
+    Blt_TreeNode node, parent;
+    char **names;
+    int i;
+    int nNames;
+    
+    if (Tcl_SplitList(cmdPtr->interp, argv[0], &nNames, &names) != TCL_OK) {
+	return NULL;
+    }
+    node = parent = dataPtr->root;
+    /*  Automatically create nodes as needed except for the last node.  */
+    for (i = 0; i < (nNames - 1); i++) {
+	node = Blt_TreeFindChild(parent, names[i]);
+	if (node == NULL) {
+	    node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
+	}
+	parent = node;
+    }
+    if (nNames > 0) {
+	/* 
+	 * By default, create duplicate nodes (two sibling nodes with
+	 * the same label), unless the -overwrite flag was set.
+	 */
+	node = NULL;
+	if (dataPtr->flags & RESTORE_OVERWRITE) {
+	    node = Blt_TreeFindChild(parent, names[i]);
+	}
+	if (node == NULL) {
+	    node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
+	}
+    }
+    Blt_Free(names);
+    return node;
+}
+
+static int
+RestoreNode(TreeCmd *cmdPtr, int argc, char **argv, RestoreData *dataPtr)
+{
+    Blt_TreeNode node;
+    Tcl_Obj *valueObjPtr;
+    char **elemArr;
+    int nElem, result;
+    register int i;
+
+    if ((argc != 3) && (argc != 5)) {
+	Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines), 
+		": wrong # elements in restore entry", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Parse the path name. */
+    if (argc == 3) {
+	node = ParseNode3(cmdPtr, argv, dataPtr);
+	argv++;
+    } else if (argc == 5) {
+	node = ParseNode5(cmdPtr, argv, dataPtr);
+	argv += 3;
+    } else {
+	Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines), 
+		": wrong # elements in restore entry", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (node == NULL) {
+	return TCL_ERROR;
+    }
+    /* Parse the key-value list. */
+    if (Tcl_SplitList(cmdPtr->interp, argv[0], &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (i = 0; i < nElem; i += 2) {
+	if ((i + 1) < nElem) {
+	    valueObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
+	} else {
+	    valueObjPtr = bltEmptyStringObjPtr;
+	}
+	Tcl_IncrRefCount(valueObjPtr);
+	result = Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, 
+			  elemArr[i], valueObjPtr);
+	Tcl_DecrRefCount(valueObjPtr);
+	if (result != TCL_OK) {
+	    Blt_Free(elemArr);
+	    return TCL_ERROR;
+	}
+    }
+    Blt_Free(elemArr);
+    if (!(dataPtr->flags & RESTORE_NO_TAGS)) {
+	/* Parse the tag list. */
+	if (Tcl_SplitList(cmdPtr->interp, argv[1], &nElem, &elemArr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < nElem; i++) {
+	    if (AddTag(cmdPtr, node, elemArr[i]) != TCL_OK) {
+		Blt_Free(elemArr);
+		return TCL_ERROR;
+	    }
+	}
+	Blt_Free(elemArr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PrintNode --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+PrintNode(
+    TreeCmd *cmdPtr, 
+    Blt_TreeNode root, 
+    Blt_TreeNode node, 
+    Tcl_DString *resultPtr)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    char *pathName;
+    Tcl_DString dString;
+    Tcl_Obj *valueObjPtr;
+    register Blt_TreeKey key;
+    Blt_TreeTagEntry *tPtr;
+    Blt_TreeKeySearch keyIter;
+
+    if (node == root) {
+	Tcl_DStringAppendElement(resultPtr, "-1");
+    } else {
+	Blt_TreeNode parent;
+
+	parent = Blt_TreeNodeParent(node);
+	Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(parent)));
+    }	
+    Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(node)));
+
+    pathName = GetNodePath(cmdPtr, root, node, TRUE, &dString);
+    Tcl_DStringAppendElement(resultPtr, pathName);
+    Tcl_DStringStartSublist(resultPtr);
+    for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL; 
+	 key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
+	if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, cmdPtr->tree, node, 
+		key, &valueObjPtr) == TCL_OK) {
+	    Tcl_DStringAppendElement(resultPtr, key);
+	    Tcl_DStringAppendElement(resultPtr, Tcl_GetString(valueObjPtr));
+	}
+    }	    
+    Tcl_DStringEndSublist(resultPtr);
+    Tcl_DStringStartSublist(resultPtr);
+    for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL; 
+	hPtr = Blt_NextHashEntry(&cursor)) {
+	tPtr = Blt_GetHashValue(hPtr);
+	if (Blt_FindHashEntry(&tPtr->nodeTable, (char *)node) != NULL) {
+	    Tcl_DStringAppendElement(resultPtr, tPtr->tagName);
+	}
+    }
+    Tcl_DStringEndSublist(resultPtr);
+    Tcl_DStringAppend(resultPtr, "\n", -1);
+    Tcl_DStringFree(&dString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PrintTraceFlags --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+PrintTraceFlags(unsigned int flags, char *string)
+{
+    register char *p;
+
+    p = string;
+    if (flags & TREE_TRACE_READ) {
+	*p++ = 'r';
+    } 
+    if (flags & TREE_TRACE_WRITE) {
+	*p++ = 'w';
+    } 
+    if (flags & TREE_TRACE_UNSET) {
+	*p++ = 'u';
+    } 
+    if (flags & TREE_TRACE_CREATE) {
+	*p++ = 'c';
+    } 
+    *p = '\0';
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTraceFlags --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+GetTraceFlags(char *string)
+{
+    register char *p;
+    unsigned int flags;
+
+    flags = 0;
+    for (p = string; *p != '\0'; p++) {
+	switch (toupper(*p)) {
+	case 'R':
+	    flags |= TREE_TRACE_READ;
+	    break;
+	case 'W':
+	    flags |= TREE_TRACE_WRITE;
+	    break;
+	case 'U':
+	    flags |= TREE_TRACE_UNSET;
+	    break;
+	case 'C':
+	    flags |= TREE_TRACE_CREATE;
+	    break;
+	default:
+	    return -1;
+	}
+    }
+    return flags;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetValues --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+SetValues(TreeCmd *cmdPtr, Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv)
+{
+    register int i;
+    char *string;
+
+    for (i = 0; i < objc; i += 2) {
+	string = Tcl_GetString(objv[i]);
+	if ((i + 1) == objc) {
+	    Tcl_AppendResult(cmdPtr->interp, "missing value for field \"", 
+		string, "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, string, 
+			     objv[i + 1]) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnsetValues --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+UnsetValues(TreeCmd *cmdPtr, Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv)
+{
+    if (objc == 0) {
+	Blt_TreeKey key;
+	Blt_TreeKeySearch cursor;
+
+	for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor); key != NULL;
+	     key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
+	    if (Blt_TreeUnsetValueByKey(cmdPtr->interp, cmdPtr->tree, node, 
+			key) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    } else {
+	register int i;
+
+	for (i = 0; i < objc; i ++) {
+	    if (Blt_TreeUnsetValue(cmdPtr->interp, cmdPtr->tree, node, 
+		Tcl_GetString(objv[i])) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+static int
+ComparePatternList(Blt_List patternList, char *string, int nocase)
+{
+    Blt_ListNode node;
+    int result, type;
+    char *pattern;
+
+    if (nocase) {
+	string = Blt_Strdup(string);
+	strtolower(string);
+    }
+    result = FALSE;
+    for (node = Blt_ListFirstNode(patternList); node != NULL; 
+	node = Blt_ListNextNode(node)) {
+		
+	type = (int)Blt_ListGetValue(node);
+	pattern = (char *)Blt_ListGetKey(node);
+	switch (type) {
+	case PATTERN_EXACT:
+	    result = (strcmp(string, pattern) == 0);
+	    break;
+	    
+	case PATTERN_GLOB:
+	    result = Tcl_StringMatch(string, pattern);
+	    break;
+		    
+	case PATTERN_REGEXP:
+	    result = Tcl_RegExpMatch((Tcl_Interp *)NULL, string, pattern); 
+	    break;
+	}
+    }
+    if (nocase) {
+	Blt_Free(string);
+    }
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MatchNodeProc --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+MatchNodeProc(Blt_TreeNode node, ClientData clientData, int order)
+{
+    FindData *dataPtr = clientData;
+    Tcl_DString dString;
+    TreeCmd *cmdPtr = dataPtr->cmdPtr;
+    Tcl_Interp *interp = dataPtr->cmdPtr->interp;
+    int result, invert;
+
+    if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
+	return TCL_OK;
+    }
+    if ((dataPtr->maxDepth >= 0) &&
+	(dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
+	return TCL_OK;
+    }
+    result = TRUE;
+    Tcl_DStringInit(&dString);
+    if (dataPtr->keyList != NULL) {
+	Blt_TreeKey key;
+	Blt_TreeKeySearch cursor;
+
+	result = FALSE;		/* It's false if no keys match. */
+	for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor);
+	     key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
+	    
+	    result = ComparePatternList(dataPtr->keyList, key, 0);
+	    if (!result) {
+		continue;
+	    }
+	    if (dataPtr->patternList != NULL) {
+		char *string;
+		Tcl_Obj *objPtr;
+
+		Blt_TreeGetValue(interp, cmdPtr->tree, node, key, &objPtr);
+		string = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+		result = ComparePatternList(dataPtr->patternList, string, 
+			 dataPtr->flags & MATCH_NOCASE);
+		if (!result) {
+		    continue;
+		}
+	    }
+	    break;
+	}
+    } else if (dataPtr->patternList != NULL) {	    
+	char *string;
+
+	if (dataPtr->flags & MATCH_PATHNAME) {
+	    string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
+		 node, FALSE, &dString);
+	} else {
+	    string = Blt_TreeNodeLabel(node);
+	}
+	result = ComparePatternList(dataPtr->patternList, string, 
+		dataPtr->flags & MATCH_NOCASE);		     
+    }
+    if ((dataPtr->withTag != NULL) && 
+	(!Blt_TreeHasTag(cmdPtr->tree, node, dataPtr->withTag))) {
+	result = FALSE;
+    }
+    Tcl_DStringFree(&dString);
+    invert = (dataPtr->flags & MATCH_INVERT) ? TRUE : FALSE;
+    if (result != invert) {
+	Tcl_Obj *objPtr;
+
+	if (dataPtr->addTag != NULL) {
+	    if (AddTag(cmdPtr, node, dataPtr->addTag) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+	Tcl_ListObjAppendElement(interp, dataPtr->listObjPtr, objPtr);
+	if (dataPtr->objv != NULL) {
+	    dataPtr->objv[dataPtr->objc - 1] = objPtr;
+	    Tcl_IncrRefCount(objPtr);
+	    result = Tcl_EvalObjv(interp, dataPtr->objc, dataPtr->objv, 0);
+	    Tcl_DecrRefCount(objPtr);
+	    dataPtr->objv[dataPtr->objc - 1] = NULL;
+	    if (result != TCL_OK) {
+		return result;
+	    }
+	}
+	dataPtr->nMatches++;
+	if ((dataPtr->maxMatches > 0) && 
+	    (dataPtr->nMatches >= dataPtr->maxMatches)) {
+	    return TCL_BREAK;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ApplyNodeProc --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ApplyNodeProc(Blt_TreeNode node, ClientData clientData, int order)
+{
+    ApplyData *dataPtr = clientData;
+    TreeCmd *cmdPtr = dataPtr->cmdPtr;
+    Tcl_Interp *interp = cmdPtr->interp;
+    int invert, result;
+    Tcl_DString dString;
+
+    if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
+	return TCL_OK;
+    }
+    if ((dataPtr->maxDepth >= 0) &&
+	(dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
+	return TCL_OK;
+    }
+    Tcl_DStringInit(&dString);
+    result = TRUE;
+    if (dataPtr->keyList != NULL) {
+	Blt_TreeKey key;
+	Blt_TreeKeySearch cursor;
+
+	result = FALSE;		/* It's false if no keys match. */
+	for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor);
+	     key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
+	    
+	    result = ComparePatternList(dataPtr->keyList, key, 0);
+	    if (!result) {
+		continue;
+	    }
+	    if (dataPtr->patternList != NULL) {
+		char *string;
+		Tcl_Obj *objPtr;
+
+		Blt_TreeGetValue(interp, cmdPtr->tree, node, key, &objPtr);
+		string = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
+		result = ComparePatternList(dataPtr->patternList, string, 
+			 dataPtr->flags & MATCH_NOCASE);
+		if (!result) {
+		    continue;
+		}
+	    }
+	    break;
+	}
+    } else if (dataPtr->patternList != NULL) {	    
+	char *string;
+
+	if (dataPtr->flags & MATCH_PATHNAME) {
+	    string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
+		 node, FALSE, &dString);
+	} else {
+	    string = Blt_TreeNodeLabel(node);
+	}
+	result = ComparePatternList(dataPtr->patternList, string, 
+		dataPtr->flags & MATCH_NOCASE);		     
+    }
+    Tcl_DStringFree(&dString);
+    if ((dataPtr->withTag != NULL) && 
+	(!Blt_TreeHasTag(cmdPtr->tree, node, dataPtr->withTag))) {
+	result = FALSE;
+    }
+    invert = (dataPtr->flags & MATCH_INVERT) ? 1 : 0;
+    if (result != invert) {
+	Tcl_Obj *objPtr;
+
+	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+	if (order == TREE_PREORDER) {
+	    dataPtr->preObjv[dataPtr->preObjc - 1] = objPtr;
+	    return Tcl_EvalObjv(interp, dataPtr->preObjc, dataPtr->preObjv, 0);
+	} else if (order == TREE_POSTORDER) {
+	    dataPtr->postObjv[dataPtr->postObjc - 1] = objPtr;
+	    return Tcl_EvalObjv(interp, dataPtr->postObjc, dataPtr->postObjv,0);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReleaseTreeObject --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+ReleaseTreeObject(TreeCmd *cmdPtr)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    TraceInfo *tracePtr;
+    NotifyInfo *notifyPtr;
+    int i;
+
+    Blt_TreeReleaseToken(cmdPtr->tree);
+    /* 
+     * When the tree token is released, all the traces and
+     * notification events are automatically removed.  But we still
+     * need to clean up the bookkeeping kept for traces. Clear all
+     * the tags and trace information.  
+     */
+    for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	tracePtr = Blt_GetHashValue(hPtr);
+	Blt_Free(tracePtr);
+    }
+    for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	notifyPtr = Blt_GetHashValue(hPtr);
+	for (i = 0; i < notifyPtr->objc - 2; i++) {
+	    Tcl_DecrRefCount(notifyPtr->objv[i]);
+	}
+	Blt_Free(notifyPtr->objv);
+	Blt_Free(notifyPtr);
+    }
+    cmdPtr->tree = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeTraceProc --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TreeTraceProc(
+    ClientData clientData,
+    Tcl_Interp *interp,
+    Blt_TreeNode node,		/* Node that has just been updated. */
+    Blt_TreeKey key,		/* Field that's updated. */
+    unsigned int flags)
+{
+    TraceInfo *tracePtr = clientData; 
+    Tcl_DString dsCmd, dsName;
+    char string[5];
+    char *qualName;
+    int result;
+
+    Tcl_DStringInit(&dsCmd);
+    Tcl_DStringAppend(&dsCmd, tracePtr->command, -1);
+    Tcl_DStringInit(&dsName);
+    qualName = Blt_GetQualifiedName(
+	Blt_GetCommandNamespace(interp, tracePtr->cmdPtr->cmdToken), 
+	Tcl_GetCommandName(interp, tracePtr->cmdPtr->cmdToken), &dsName);
+    Tcl_DStringAppendElement(&dsCmd, qualName);
+    Tcl_DStringFree(&dsName);
+    if (node != NULL) {
+	Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(node)));
+    } else {
+	Tcl_DStringAppendElement(&dsCmd, "");
+    }
+    Tcl_DStringAppendElement(&dsCmd, key);
+    PrintTraceFlags(flags, string);
+    Tcl_DStringAppendElement(&dsCmd, string);
+    result = Tcl_Eval(interp, Tcl_DStringValue(&dsCmd));
+    Tcl_DStringFree(&dsCmd);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeEventProc --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TreeEventProc(ClientData clientData, Blt_TreeNotifyEvent *eventPtr)
+{
+    TreeCmd *cmdPtr = clientData; 
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    NotifyInfo *notifyPtr;
+    Blt_TreeNode node;
+    char *string;
+
+    switch (eventPtr->type) {
+    case TREE_NOTIFY_CREATE:
+	string = "-create";
+	break;
+
+    case TREE_NOTIFY_DELETE:
+	node = Blt_TreeGetNode(cmdPtr->tree, eventPtr->inode);
+	if (node != NULL) {
+	    Blt_TreeClearTags(cmdPtr->tree, node);
+	}
+	string = "-delete";
+	break;
+
+    case TREE_NOTIFY_MOVE:
+	string = "-move";
+	break;
+
+    case TREE_NOTIFY_SORT:
+	string = "-sort";
+	break;
+
+    case TREE_NOTIFY_RELABEL:
+	string = "-relabel";
+	break;
+
+    default:
+	/* empty */
+	string = "???";
+	break;
+    }	
+
+    for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	notifyPtr = Blt_GetHashValue(hPtr);
+	if (notifyPtr->mask & eventPtr->type) {
+	    int result;
+	    Tcl_Obj *flagObjPtr, *nodeObjPtr;
+
+	    flagObjPtr = Tcl_NewStringObj(string, -1);
+	    nodeObjPtr = Tcl_NewIntObj(eventPtr->inode);
+	    Tcl_IncrRefCount(flagObjPtr);
+	    Tcl_IncrRefCount(nodeObjPtr);
+	    notifyPtr->objv[notifyPtr->objc - 2] = flagObjPtr;
+	    notifyPtr->objv[notifyPtr->objc - 1] = nodeObjPtr;
+	    result = Tcl_EvalObjv(cmdPtr->interp, notifyPtr->objc, 
+		notifyPtr->objv, 0);
+	    Tcl_DecrRefCount(nodeObjPtr);
+	    Tcl_DecrRefCount(flagObjPtr);
+	    if (result != TCL_OK) {
+		Tcl_BackgroundError(cmdPtr->interp);
+		return TCL_ERROR;
+	    }
+	    Tcl_ResetResult(cmdPtr->interp);
+	}
+    }
+    return TCL_OK;
+}
+
+
+
+/* Tree command operations. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ApplyOp --
+ *
+ * t0 apply root -precommand {command} -postcommand {command}
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ApplyOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    int result;
+    Blt_TreeNode node;
+    int i;
+    Tcl_Obj **objArr;
+    int count;
+    ApplyData data;
+    int order;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    memset(&data, 0, sizeof(data));
+    data.maxDepth = -1;
+    data.cmdPtr = cmdPtr;
+    
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, applySwitches, objc - 3, objv + 3, 
+	     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    order = 0;
+    if (data.flags & MATCH_NOCASE) {
+	Blt_ListNode listNode;
+
+	for (listNode = Blt_ListFirstNode(data.patternList); listNode != NULL;
+	     listNode = Blt_ListNextNode(listNode)) {
+	    strtolower((char *)Blt_ListGetKey(listNode));
+	}
+    }
+    if (data.preCmd != NULL) {
+	char **p;
+
+	count = 0;
+	for (p = data.preCmd; *p != NULL; p++) {
+	    count++;
+	}
+	objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+	for (i = 0; i < count; i++) {
+	    objArr[i] = Tcl_NewStringObj(data.preCmd[i], -1);
+	    Tcl_IncrRefCount(objArr[i]);
+	}
+	data.preObjv = objArr;
+	data.preObjc = count + 1;
+	order |= TREE_PREORDER;
+    }
+    if (data.postCmd != NULL) {
+	char **p;
+
+	count = 0;
+	for (p = data.postCmd; *p != NULL; p++) {
+	    count++;
+	}
+	objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
+	for (i = 0; i < count; i++) {
+	    objArr[i] = Tcl_NewStringObj(data.postCmd[i], -1);
+	    Tcl_IncrRefCount(objArr[i]);
+	}
+	data.postObjv = objArr;
+	data.postObjc = count + 1;
+	order |= TREE_POSTORDER;
+    }
+    result = Blt_TreeApplyDFS(node, ApplyNodeProc, &data, order);
+    if (data.preObjv != NULL) {
+	for (i = 0; i < (data.preObjc - 1); i++) {
+	    Tcl_DecrRefCount(data.preObjv[i]);
+	}
+	Blt_Free(data.preObjv);
+    }
+    if (data.postObjv != NULL) {
+	for (i = 0; i < (data.postObjc - 1); i++) {
+	    Tcl_DecrRefCount(data.postObjv[i]);
+	}
+	Blt_Free(data.postObjv);
+    }
+    Blt_FreeSwitches(applySwitches, (char *)&data, 0);
+    if (result == TCL_ERROR) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+AncestorOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    int d1, d2, minDepth;
+    register int i;
+    Blt_TreeNode ancestor, node1, node2;
+
+    if ((GetNode(cmdPtr, objv[2], &node1) != TCL_OK) ||
+	(GetNode(cmdPtr, objv[3], &node2) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (node1 == node2) {
+	ancestor = node1;
+	goto done;
+    }
+    d1 = Blt_TreeNodeDepth(cmdPtr->tree, node1);
+    d2 = Blt_TreeNodeDepth(cmdPtr->tree, node2);
+    minDepth = MIN(d1, d2);
+    if (minDepth == 0) {	/* One of the nodes is root. */
+	ancestor = Blt_TreeRootNode(cmdPtr->tree);
+	goto done;
+    }
+    /* 
+     * Traverse back from the deepest node, until the both nodes are
+     * at the same depth.  Check if the ancestor node found is the
+     * other node.  
+     */
+    for (i = d1; i > minDepth; i--) {
+	node1 = Blt_TreeNodeParent(node1);
+    }
+    if (node1 == node2) {
+	ancestor = node2;
+	goto done;
+    }
+    for (i = d2; i > minDepth; i--) {
+	node2 = Blt_TreeNodeParent(node2);
+    }
+    if (node2 == node1) {
+	ancestor = node1;
+	goto done;
+    }
+
+    /* 
+     * First find the mutual ancestor of both nodes.  Look at each
+     * preceding ancestor level-by-level for both nodes.  Eventually
+     * we'll find a node that's the parent of both ancestors.  Then
+     * find the first ancestor in the parent's list of subnodes.  
+     */
+    for (i = minDepth; i > 0; i--) {
+	node1 = Blt_TreeNodeParent(node1);
+	node2 = Blt_TreeNodeParent(node2);
+	if (node1 == node2) {
+	    ancestor = node2;
+	    goto done;
+	}
+    }
+    Tcl_AppendResult(interp, "unknown ancestor", (char *)NULL);
+    return TCL_ERROR;
+ done:
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(ancestor));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AttachOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+AttachOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    if (objc == 3) {
+	CONST char *treeName;
+	CONST char *name;
+	Blt_Tree token;
+	Tcl_Namespace *nsPtr;
+	Tcl_DString dString;
+	int result;
+
+	treeName = Tcl_GetString(objv[2]);
+	if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) 
+	    != TCL_OK) {
+	    Tcl_AppendResult(interp, "can't find namespace in \"", treeName, 
+			     "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (nsPtr == NULL) {
+	    nsPtr = Tcl_GetCurrentNamespace(interp);
+	}
+	treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+	result = Blt_TreeGetToken(interp, treeName, &token);
+	Tcl_DStringFree(&dString);
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	ReleaseTreeObject(cmdPtr);
+	cmdPtr->tree = token;
+    }
+    Tcl_SetResult(interp, Blt_TreeName(cmdPtr->tree), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChildrenOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ChildrenOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 3) {
+	Tcl_Obj *objPtr, *listObjPtr;
+
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+	for (node = Blt_TreeFirstChild(node); node != NULL;
+	     node = Blt_TreeNextSibling(node)) {
+	    objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+    } else if (objc == 4) {
+	int childPos;
+	int inode, count;
+	
+	/* Get the node at  */
+	if (Tcl_GetIntFromObj(interp, objv[3], &childPos) != TCL_OK) {
+		return TCL_ERROR;
+	}
+	count = 0;
+	inode = -1;
+	for (node = Blt_TreeFirstChild(node); node != NULL;
+	     node = Blt_TreeNextSibling(node)) {
+	    if (count == childPos) {
+		inode = Blt_TreeNodeId(node);
+		break;
+	    }
+	    count++;
+	}
+	Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+	return TCL_OK;
+    } else if (objc == 5) {
+	int firstPos, lastPos, count;
+	Tcl_Obj *objPtr, *listObjPtr;
+	char *string;
+
+	firstPos = lastPos = Blt_TreeNodeDegree(node) - 1;
+	string = Tcl_GetString(objv[3]);
+	if ((strcmp(string, "end") != 0) &&
+	    (Tcl_GetIntFromObj(interp, objv[3], &firstPos) != TCL_OK)) {
+	    return TCL_ERROR;
+	}
+	string = Tcl_GetString(objv[4]);
+	if ((strcmp(string, "end") != 0) &&
+	    (Tcl_GetIntFromObj(interp, objv[4], &lastPos) != TCL_OK)) {
+	    return TCL_ERROR;
+	}
+
+	count = 0;
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+	for (node = Blt_TreeFirstChild(node); node != NULL;
+	     node = Blt_TreeNextSibling(node)) {
+	    if ((count >= firstPos) && (count <= lastPos)) {
+		objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    }
+	    count++;
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+    }
+    return TCL_OK;
+}
+
+
+static Blt_TreeNode 
+CopyNodes(
+    CopyData *dataPtr,
+    Blt_TreeNode node,		/* Node to be copied. */
+    Blt_TreeNode parent)	/* New parent for the copied node. */
+{
+    Blt_TreeNode newNode;	/* Newly created copy. */
+    char *label;
+
+    newNode = NULL;
+    label = Blt_TreeNodeLabel(node);
+    if (dataPtr->flags & COPY_OVERWRITE) {
+	newNode = Blt_TreeFindChild(parent, label);
+    }
+    if (newNode == NULL) {	/* Create node in new parent. */
+	newNode = Blt_TreeCreateNode(dataPtr->destTree, parent, label, -1);
+    }
+    /* Copy the data fields. */
+    {
+	Blt_TreeKey key;
+	Tcl_Obj *objPtr;
+	Blt_TreeKeySearch cursor;
+
+	for (key = Blt_TreeFirstKey(dataPtr->srcTree, node, &cursor); 
+	     key != NULL; key = Blt_TreeNextKey(dataPtr->srcTree, &cursor)) {
+	    if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, dataPtr->srcTree, 
+			node, key, &objPtr) == TCL_OK) {
+		Blt_TreeSetValueByKey((Tcl_Interp *)NULL, dataPtr->destTree, 
+			newNode, key, objPtr);
+	    } 
+	}
+    }
+    /* Add tags to destination tree command. */
+    if ((dataPtr->destPtr != NULL) && (dataPtr->flags & COPY_TAGS)) {
+	Blt_TreeTagEntry *tPtr;
+	Blt_HashEntry *hPtr, *h2Ptr;
+	Blt_HashSearch cursor;
+
+	for (hPtr = Blt_TreeFirstTag(dataPtr->srcPtr->tree, &cursor); 
+		hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	    tPtr = Blt_GetHashValue(hPtr);
+	    h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
+	    if (h2Ptr != NULL) {
+		if (AddTag(dataPtr->destPtr, newNode, tPtr->tagName)!= TCL_OK) {
+		    return NULL;
+		}
+	    }
+	}
+    }
+    if (dataPtr->flags & COPY_RECURSE) {
+	Blt_TreeNode child;
+
+	for (child = Blt_TreeFirstChild(node); child != NULL;
+	     child = Blt_TreeNextSibling(child)) {
+	    if (CopyNodes(dataPtr, child, newNode) == NULL) {
+		return NULL;
+	    }
+	}
+    }
+    return newNode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CopyOp --
+ * 
+ *	t0 copy node tree node 
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+CopyOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TreeCmd *srcPtr, *destPtr;
+    Blt_Tree srcTree, destTree;
+    Blt_TreeNode srcNode, destNode;
+    CopyData data;
+    int nArgs, nSwitches;
+    char *string;
+    Blt_TreeNode root;
+    register int i;
+
+    if (GetNode(cmdPtr, objv[2], &srcNode) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    srcTree = cmdPtr->tree;
+    srcPtr = cmdPtr;
+
+    /* Find the first switch. */
+    for(i = 3; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (string[0] == '-') {
+	    break;
+	}
+    }
+    nArgs = i - 2;
+    nSwitches = objc - i;
+    if (nArgs < 2) {
+	string = Tcl_GetString(objv[0]);
+	Tcl_AppendResult(interp, "must specify source and destination nodes: ",
+			 "should be \"", string, 
+			 " copy srcNode ?destTree? destNode ?switches?", 
+			 (char *)NULL);
+	return TCL_ERROR;
+	
+    }
+    if (nArgs == 3) {
+	/* 
+	 * The tree name is either the name of a tree command (first choice)
+	 * or an internal tree object.  
+	 */
+	string = Tcl_GetString(objv[3]);
+	destPtr = GetTreeCmd(cmdPtr->dataPtr, interp, string);
+	if (destPtr != NULL) {
+	    destTree = destPtr->tree;
+	} else {
+	    /* Try to get the tree as an internal tree data object. */
+	    if (Blt_TreeGetToken(interp, string, &destTree) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	objv++;
+    } else {
+	destPtr = cmdPtr;
+	destTree = destPtr->tree;
+    }
+
+    root = NULL;
+    if (destPtr == NULL) {
+	if (GetForeignNode(interp, destTree, objv[3], &destNode) != TCL_OK) {
+	    goto error;
+	}
+    } else {
+	if (GetNode(destPtr, objv[3], &destNode) != TCL_OK) {
+	    goto error;
+	}
+    }
+    if (srcNode == destNode) {
+	Tcl_AppendResult(interp, "source and destination nodes are the same",
+		 (char *)NULL);	     
+	goto error;
+    }
+    memset((char *)&data, 0, sizeof(data));
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, copySwitches, nSwitches, objv + 4, 
+	     (char *)&data, 0) < 0) {
+	goto error;
+    }
+    data.destPtr = destPtr;
+    data.destTree = destTree;
+    data.srcPtr = srcPtr;
+    data.srcTree = srcTree;
+
+    if ((srcTree == destTree) && (data.flags & COPY_RECURSE) &&
+	(Blt_TreeIsAncestor(srcNode, destNode))) {    
+	Tcl_AppendResult(interp, "can't make cyclic copy: ",
+			 "source node is an ancestor of the destination",
+			 (char *)NULL);	     
+	goto error;
+    }
+
+    /* Copy nodes to destination. */
+    root = CopyNodes(&data, srcNode, destNode);
+    if (root != NULL) {
+	Tcl_Obj *objPtr;
+
+	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(root));
+	if (data.label != NULL) {
+	    Blt_TreeRelabelNode(data.destTree, root, data.label);
+	}
+	Tcl_SetObjResult(interp, objPtr);
+    }
+ error:
+    if (destPtr == NULL) {
+	Blt_TreeReleaseToken(destTree);
+    }
+    return (root == NULL) ? TCL_ERROR : TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DepthOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+DegreeOp(cmdPtr, interp, objc, objv)
+    TreeCmd *cmdPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode node;
+    int degree;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    degree = Blt_TreeNodeDegree(node);
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), degree);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes one or more nodes from the tree.  Nodes may be
+ *	specified by their id (a number) or a tag.
+ *	
+ *	Tags have to be handled carefully here.  We can't use the
+ *	normal GetTaggedNode, NextTaggedNode, etc. routines because
+ *	they walk hashtables while we're deleting nodes.  Also,
+ *	remember that deleting a node recursively deletes all its
+ *	children. If a parent and its children have the same tag, its
+ *	possible that the tag list may contain nodes than no longer
+ *	exist. So save the node indices in a list and then delete 
+ *	then in a second pass.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+DeleteOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int i;
+    char *string;
+
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (isdigit(UCHAR(string[0]))) {
+	    if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    DeleteNode(cmdPtr, node);
+	} else {
+	    Blt_HashEntry *hPtr;
+	    Blt_HashTable *tablePtr;
+	    Blt_HashSearch cursor;
+	    Blt_Chain *chainPtr;
+	    Blt_ChainLink *linkPtr, *nextPtr;
+	    int inode;
+
+	    if ((strcmp(string, "all") == 0) ||
+		(strcmp(string, "root") == 0)) {
+		node = Blt_TreeRootNode(cmdPtr->tree);
+		DeleteNode(cmdPtr, node);
+		continue;
+	    }
+	    tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
+	    if (tablePtr == NULL) {
+		goto error;
+	    }
+	    /* 
+	     * Generate a list of tagged nodes. Save the inode instead
+	     * of the node itself since a pruned branch may contain
+	     * more tagged nodes.  
+	     */
+	    chainPtr = Blt_ChainCreate();
+	    for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); 
+		hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+		node = Blt_GetHashValue(hPtr);
+		Blt_ChainAppend(chainPtr, (ClientData)Blt_TreeNodeId(node));
+	    }   
+	    /*  
+	     * Iterate through this list to delete the nodes.  By
+	     * side-effect the tag table is deleted and Uids are
+	     * released.  
+	     */
+	    for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+		 linkPtr = nextPtr) {
+		nextPtr = Blt_ChainNextLink(linkPtr);
+		inode = (int)Blt_ChainGetValue(linkPtr);
+		node = Blt_TreeGetNode(cmdPtr->tree, inode);
+		if (node != NULL) {
+		    DeleteNode(cmdPtr, node);
+		}
+	    }
+	    Blt_ChainDestroy(chainPtr);
+	}
+    }
+    return TCL_OK;
+ error:
+    Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", 
+		     Blt_TreeName(cmdPtr->tree), (char *)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DepthOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+DepthOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int depth;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    depth = Blt_TreeNodeDepth(cmdPtr->tree, node);
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), depth);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DumpOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+DumpOp(cmdPtr, interp, objc, objv)
+    TreeCmd *cmdPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode top;
+    Tcl_DString dString;
+    register Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+	PrintNode(cmdPtr, top, node, &dString);
+    }
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DumpfileOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+DumpfileOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode top;
+    Tcl_Channel channel;
+    Tcl_DString dString;
+    char *fileName;
+    int result;
+    register Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    fileName = Tcl_GetString(objv[3]);
+    channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666);
+    if (channel == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+	PrintNode(cmdPtr, top, node, &dString);
+    }
+    result = Tcl_Write(channel, Tcl_DStringValue(&dString), -1);
+    Tcl_Close(interp, channel);
+    Tcl_DStringFree(&dString);
+    if (result <= 0) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ExistsOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int bool;
+    
+    bool = TRUE;
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	bool = FALSE;
+    } else if (objc == 4) { 
+	Tcl_Obj *valueObjPtr;
+	char *string;
+	
+	string = Tcl_GetString(objv[3]);
+	if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, 
+			     string, &valueObjPtr) != TCL_OK) {
+	    bool = FALSE;
+	}
+    } 
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+FindOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    FindData data;
+    int result;
+    Tcl_Obj **objArr;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    memset(&data, 0, sizeof(data));
+    data.maxDepth = -1;
+    data.order = TREE_POSTORDER;
+    objArr = NULL;
+
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, findSwitches, objc - 3, objv + 3, 
+		     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    if (data.maxDepth >= 0) {
+	data.maxDepth += Blt_TreeNodeDepth(cmdPtr->tree, node);
+    }
+    if (data.flags & MATCH_NOCASE) {
+	Blt_ListNode listNode;
+
+	for (listNode = Blt_ListFirstNode(data.patternList); listNode != NULL;
+	     listNode = Blt_ListNextNode(listNode)) {
+	    strtolower((char *)Blt_ListGetKey(listNode));
+	}
+    }
+    if (data.command != NULL) {
+	int count;
+	char **p;
+	register int i;
+
+	count = 0;
+	for (p = data.command; *p != NULL; p++) {
+	    count++;
+	}
+	/* Leave room for node Id argument to be appended */
+	objArr = Blt_Calloc(count + 2, sizeof(Tcl_Obj *));
+	for (i = 0; i < count; i++) {
+	    objArr[i] = Tcl_NewStringObj(data.command[i], -1);
+	    Tcl_IncrRefCount(objArr[i]);
+	}
+	data.objv = objArr;
+	data.objc = count + 1;
+    }
+    data.listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    data.cmdPtr = cmdPtr;
+    if (data.order == TREE_BREADTHFIRST) {
+	result = Blt_TreeApplyBFS(node, MatchNodeProc, &data);
+    } else {
+	result = Blt_TreeApplyDFS(node, MatchNodeProc, &data, data.order);
+    }
+    if (data.command != NULL) {
+	Tcl_Obj **objPtrPtr;
+
+	for (objPtrPtr = objArr; *objPtrPtr != NULL; objPtrPtr++) {
+	    Tcl_DecrRefCount(*objPtrPtr);
+	}
+	Blt_Free(objArr);
+    }
+    Blt_FreeSwitches(findSwitches, (char *)&data, 0);
+    if (result == TCL_ERROR) {
+	return TCL_ERROR;
+    }
+    Tcl_SetObjResult(interp, data.listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindChildOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+FindChildOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node, child;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    child = Blt_TreeFindChild(node, Tcl_GetString(objv[3]));
+    if (child != NULL) {
+	inode = Blt_TreeNodeId(child);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FirstChildOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+FirstChildOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreeFirstChild(node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+GetOp(
+    TreeCmd *cmdPtr, 
+    Tcl_Interp *interp, 
+    int objc, 
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 3) {
+	Blt_TreeKey key;
+	Tcl_Obj *valueObjPtr, *listObjPtr;
+	Blt_TreeKeySearch cursor;
+
+	/* Add the key-value pairs to a new Tcl_Obj */
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+	for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor); key != NULL; 
+	     key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
+	    if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, key,
+				 &valueObjPtr) == TCL_OK) {
+		Tcl_Obj *objPtr;
+
+		objPtr = Tcl_NewStringObj(key, -1);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+		Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
+	    }
+	}	    
+	Tcl_SetObjResult(interp, listObjPtr);
+	return TCL_OK;
+    } else {
+	Tcl_Obj *valueObjPtr;
+	char *string;
+
+	string = Tcl_GetString(objv[3]); 
+	if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, string,
+		     &valueObjPtr) != TCL_OK) {
+	    if (objc == 4) {
+		Tcl_DString dString;
+		char *path;
+
+		path = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), 
+		   node, FALSE, &dString);		
+		Tcl_AppendResult(interp, "can't find field \"", string, 
+			"\" in \"", path, "\"", (char *)NULL);
+		Tcl_DStringFree(&dString);
+		return TCL_ERROR;
+	    } 
+	    /* Default to given value */
+	    valueObjPtr = objv[4];
+	} 
+	Tcl_SetObjResult(interp, valueObjPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+IndexOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    inode = -1;
+    if (GetNode(cmdPtr, objv[2], &node) == TCL_OK) {
+	inode = Blt_TreeNodeId(node);
+    } else {
+	register int i;
+	int nObjs;
+	Tcl_Obj **objArr;
+	Blt_TreeNode parent;
+	char *string;
+
+	if (Tcl_ListObjGetElements(interp, objv[2], &nObjs, &objArr) 
+	    != TCL_OK) {
+	    goto done;		/* Can't split object. */
+	}
+	parent = Blt_TreeRootNode(cmdPtr->tree);
+	for (i = 0; i < nObjs; i++) {
+	    string = Tcl_GetString(objArr[i]);
+	    if (string[0] == '\0') {
+		continue;
+	    }
+	    node = Blt_TreeFindChild(parent, string);
+	    if (node == NULL) {
+		goto done;	/* Can't find component */
+	    }
+	    parent = node;
+	}
+	inode = Blt_TreeNodeId(node);
+    }
+ done:
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+
+static int
+InsertOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode parent, child;
+    InsertData data;
+
+    child = NULL;
+    if (GetNode(cmdPtr, objv[2], &parent) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Initialize switch flags */
+    memset(&data, 0, sizeof(data));
+    data.insertPos = -1;	/* Default to append node. */
+    data.parent = parent;
+    data.inode = -1;
+
+    if (Blt_ProcessObjSwitches(interp, insertSwitches, objc - 3, objv + 3, 
+	     (char *)&data, 0) < 0) {
+	goto error;
+    }
+    if (data.inode > 0) {
+	Blt_TreeNode node;
+
+	node = Blt_TreeGetNode(cmdPtr->tree, data.inode);
+	if (node != NULL) {
+	    Tcl_AppendResult(interp, "can't reissue node id \"", 
+		Blt_Itoa(data.inode), "\": already exists.", (char *)NULL);
+	    goto error;
+	}
+	child = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, data.label, 
+		data.inode, data.insertPos);
+    } else {
+	child = Blt_TreeCreateNode(cmdPtr->tree, parent, data.label, 
+		data.insertPos);
+    }
+    if (child == NULL) {
+	Tcl_AppendResult(interp, "can't allocate new node", (char *)NULL);
+	goto error;
+    }
+    if (data.label == NULL) {
+	char string[200];
+
+	sprintf(string, "node%d", Blt_TreeNodeId(child));
+	Blt_TreeRelabelNode2(child, string);
+    } 
+    if (data.tags != NULL) {
+	register char **p;
+
+	for (p = data.tags; *p != NULL; p++) {
+	    if (AddTag(cmdPtr, child, *p) != TCL_OK) {
+		goto error;
+	    }
+	}
+    }
+    if (data.dataPairs != NULL) {
+	register char **p;
+	char *key;
+	Tcl_Obj *objPtr;
+
+	for (p = data.dataPairs; *p != NULL; p++) {
+	    key = *p;
+	    p++;
+	    if (*p == NULL) {
+		Tcl_AppendResult(interp, "missing value for \"", key, "\"",
+				 (char *)NULL);
+		goto error;
+	    }
+	    objPtr = Tcl_NewStringObj(*p, -1);
+	    if (Blt_TreeSetValue(interp, cmdPtr->tree, child, key, objPtr) 
+		!= TCL_OK) {
+		Tcl_DecrRefCount(objPtr);
+		goto error;
+	    }
+	}
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(child));
+    Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
+    return TCL_OK;
+
+ error:
+    if (child != NULL) {
+	Blt_TreeDeleteNode(cmdPtr->tree, child);
+    }
+    Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsAncestorOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+IsAncestorOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node1, node2;
+    int bool;
+
+    if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
+	(GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    bool = Blt_TreeIsAncestor(node1, node2);
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsBeforeOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+IsBeforeOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node1, node2;
+    int bool;
+
+    if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
+	(GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    bool = Blt_TreeIsBefore(node1, node2);
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsLeafOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+IsLeafOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeIsLeaf(node));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsRootOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+IsRootOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int bool;
+
+    if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    bool = (node == Blt_TreeRootNode(cmdPtr->tree));
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec isOps[] =
+{
+    {"ancestor", 1, (Blt_Op)IsAncestorOp, 5, 5, "node1 node2",},
+    {"before", 1, (Blt_Op)IsBeforeOp, 5, 5, "node1 node2",},
+    {"leaf", 1, (Blt_Op)IsLeafOp, 4, 4, "node",},
+    {"root", 1, (Blt_Op)IsRootOp, 4, 4, "node",},
+};
+
+static int nIsOps = sizeof(isOps) / sizeof(Blt_OpSpec);
+
+static int
+IsOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nIsOps, isOps, BLT_OP_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (cmdPtr, interp, objc, objv);
+    return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * KeysOp --
+ *
+ *	Returns the key names of values for a node or array value.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+KeysOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch tagSearch;
+    Blt_HashTable keyTable;
+    Blt_TreeKey key;
+    Blt_TreeKeySearch keyIter;
+    Blt_TreeNode node;
+    TagSearch tagIter;
+    Tcl_Obj *listObjPtr, *objPtr;
+    register int i;
+    int isNew;
+
+    Blt_InitHashTableWithPool(&keyTable, BLT_ONE_WORD_KEYS);
+    for (i = 2; i < objc; i++) {
+	node = FirstTaggedNode(interp, cmdPtr, objv[i], &tagIter);
+	if (node == NULL) {
+	    return TCL_ERROR;
+	}
+	for (/* empty */; node != NULL; node = NextTaggedNode(node, &tagIter)) {
+	    for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); 
+		 key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
+		Blt_CreateHashEntry(&keyTable, key, &isNew);
+	    }
+	}
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (hPtr = Blt_FirstHashEntry(&keyTable, &tagSearch); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&tagSearch)) {
+	objPtr = Tcl_NewStringObj(Blt_GetHashKey(&keyTable, hPtr), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    Blt_DeleteHashTable(&keyTable);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LabelOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+LabelOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 4) {
+	Blt_TreeRelabelNode(cmdPtr->tree, node, Tcl_GetString(objv[3]));
+    }
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), Blt_TreeNodeLabel(node), -1);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LastChildOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+LastChildOp(cmdPtr, interp, objc, objv)
+    TreeCmd *cmdPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreeLastChild(node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ *	The big trick here is to not consider the node to be moved in
+ *	determining it's new location.  Ideally, you would temporarily
+ *	pull from the tree and replace it (back in its old location if
+ *	something went wrong), but you could still pick the node by 
+ *	its serial number.  So here we make lots of checks for the 
+ *	node to be moved.
+ * 
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+MoveOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode root, parent, node;
+    Blt_TreeNode before;
+    MoveData data;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (GetNode(cmdPtr, objv[3], &parent) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    root = Blt_TreeRootNode(cmdPtr->tree);
+    if (node == root) {
+	Tcl_AppendResult(interp, "can't move root node", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (parent == node) {
+	Tcl_AppendResult(interp, "can't move node to self", (char *)NULL);
+	return TCL_ERROR;
+    }
+    data.node = NULL;
+    data.cmdPtr = cmdPtr;
+    data.movePos = -1;
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, moveSwitches, objc - 4, objv + 4, 
+	     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    /* Verify they aren't ancestors. */
+    if (Blt_TreeIsAncestor(node, parent)) {
+	Tcl_AppendResult(interp, "can't move node: \"", 
+		 Tcl_GetString(objv[2]), (char *)NULL);
+	Tcl_AppendResult(interp, "\" is an ancestor of \"", 
+		 Tcl_GetString(objv[3]), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    before = NULL;		/* If before is NULL, this appends the
+				 * node to the parent's child list.  */
+
+    if (data.node != NULL) {	/* -before or -after */
+	if (Blt_TreeNodeParent(data.node) != parent) {
+	    Tcl_AppendResult(interp, Tcl_GetString(objv[2]), 
+		     " isn't the parent of ", Blt_TreeNodeLabel(data.node),
+		     (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Blt_SwitchChanged(moveSwitches, "-before", (char *)NULL)) {
+	    before = data.node;
+	    if (before == node) {
+		Tcl_AppendResult(interp, "can't move node before itself", 
+				 (char *)NULL);
+		return TCL_ERROR;
+	    }
+	} else {
+	    before = Blt_TreeNextSibling(data.node);
+	    if (before == node) {
+		Tcl_AppendResult(interp, "can't move node after itself", 
+				 (char *)NULL);
+		return TCL_ERROR;
+	    }
+	}
+    } else if (data.movePos >= 0) { /* -at */
+	int count;		/* Tracks the current list index. */
+	Blt_TreeNode child;
+
+	/* 
+	 * If the node is in the list, ignore it when determining the
+	 * "before" node using the -at index.  An index of -1 means to
+	 * append the node to the list.
+	 */
+	count = 0;
+	for(child = Blt_TreeFirstChild(parent); child != NULL; 
+	    child = Blt_TreeNextSibling(child)) {
+	    if (child == node) {
+		continue;	/* Ignore the node to be moved. */
+	    }
+	    if (count == data.movePos) {
+		before = child;
+		break;		
+	    }
+	    count++;	
+	}
+    }
+    if (Blt_TreeMoveNode(cmdPtr->tree, node, parent, before) != TCL_OK) {
+	Tcl_AppendResult(interp, "can't move node ", Tcl_GetString(objv[2]), 
+		 " to ", Tcl_GetString(objv[3]), (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+NextOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreeNextNode(Blt_TreeRootNode(cmdPtr->tree), node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NextSiblingOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+NextSiblingOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreeNextSibling(node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyCreateOp --
+ *
+ *	tree0 notify create ?flags? command arg
+ *---------------------------------------------------------------------- 
+ */
+static int
+NotifyCreateOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    NotifyInfo *notifyPtr;
+    NotifyData data;
+    char *string;
+    char idString[200];
+    int isNew, nArgs;
+    Blt_HashEntry *hPtr;
+    int count;
+    register int i;
+
+    count = 0;
+    for (i = 3; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (string[0] != '-') {
+	    break;
+	}
+	count++;
+    }
+    data.mask = 0;
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, notifySwitches, count, objv + 3, 
+	     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    notifyPtr = Blt_Malloc(sizeof(NotifyInfo));
+
+    nArgs = objc - i;
+
+    /* Stash away the command in structure and pass that to the notifier. */
+    notifyPtr->objv = Blt_Malloc((nArgs + 2) * sizeof(Tcl_Obj *));
+    for (count = 0; i < objc; i++, count++) {
+	Tcl_IncrRefCount(objv[i]);
+	notifyPtr->objv[count] = objv[i];
+    }
+    notifyPtr->objc = nArgs + 2;
+    notifyPtr->cmdPtr = cmdPtr;
+    if (data.mask == 0) {
+	data.mask = TREE_NOTIFY_ALL;
+    }
+    notifyPtr->mask = data.mask;
+
+    sprintf(idString, "notify%d", cmdPtr->notifyCounter++);
+    hPtr = Blt_CreateHashEntry(&(cmdPtr->notifyTable), idString, &isNew);
+    Blt_SetHashValue(hPtr, notifyPtr);
+
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyDeleteOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+NotifyDeleteOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    NotifyInfo *notifyPtr;
+    Blt_HashEntry *hPtr;
+    register int i, j;
+    char *string;
+
+    for (i = 3; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
+	if (hPtr == NULL) {
+	    Tcl_AppendResult(interp, "unknown notify name \"", string, "\"", 
+			     (char *)NULL);
+	    return TCL_ERROR;
+	}
+	notifyPtr = Blt_GetHashValue(hPtr);
+	Blt_DeleteHashEntry(&(cmdPtr->notifyTable), hPtr);
+	for (j = 0; j < (notifyPtr->objc - 2); j++) {
+	    Tcl_DecrRefCount(notifyPtr->objv[j]);
+	}
+	Blt_Free(notifyPtr->objv);
+	Blt_Free(notifyPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyInfoOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+NotifyInfoOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    NotifyInfo *notifyPtr;
+    Blt_HashEntry *hPtr;
+    Tcl_DString dString;
+    char *string;
+    int i;
+
+    string = Tcl_GetString(objv[3]);
+    hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(interp, "unknown notify name \"", string, "\"", 
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    notifyPtr = Blt_GetHashValue(hPtr);
+
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppendElement(&dString, string);	/* Copy notify Id */
+    Tcl_DStringStartSublist(&dString);
+    if (notifyPtr->mask & TREE_NOTIFY_CREATE) {
+	Tcl_DStringAppendElement(&dString, "-create");
+    }
+    if (notifyPtr->mask & TREE_NOTIFY_DELETE) {
+	Tcl_DStringAppendElement(&dString, "-delete");
+    }
+    if (notifyPtr->mask & TREE_NOTIFY_MOVE) {
+	Tcl_DStringAppendElement(&dString, "-move");
+    }
+    if (notifyPtr->mask & TREE_NOTIFY_SORT) {
+	Tcl_DStringAppendElement(&dString, "-sort");
+    }
+    if (notifyPtr->mask & TREE_NOTIFY_RELABEL) {
+	Tcl_DStringAppendElement(&dString, "-relabel");
+    }
+    if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
+	Tcl_DStringAppendElement(&dString, "-whenidle");
+    }
+    Tcl_DStringEndSublist(&dString);
+    Tcl_DStringStartSublist(&dString);
+    for (i = 0; i < (notifyPtr->objc - 2); i++) {
+	Tcl_DStringAppendElement(&dString, Tcl_GetString(notifyPtr->objv[i]));
+    }
+    Tcl_DStringEndSublist(&dString);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyNamesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+NotifyNamesOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Tcl_Obj *objPtr, *listObjPtr;
+    char *notifyId;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	notifyId = Blt_GetHashKey(&(cmdPtr->notifyTable), hPtr);
+	objPtr = Tcl_NewStringObj(notifyId, -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec notifyOps[] =
+{
+    {"create", 1, (Blt_Op)NotifyCreateOp, 4, 0, "?flags? command",},
+    {"delete", 1, (Blt_Op)NotifyDeleteOp, 3, 0, "notifyId...",},
+    {"info", 1, (Blt_Op)NotifyInfoOp, 4, 4, "notifyId",},
+    {"names", 1, (Blt_Op)NotifyNamesOp, 3, 3, "",},
+};
+
+static int nNotifyOps = sizeof(notifyOps) / sizeof(Blt_OpSpec);
+
+static int
+NotifyOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nNotifyOps, notifyOps, BLT_OP_ARG2, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (cmdPtr, interp, objc, objv);
+    return result;
+}
+
+
+/*ARGSUSED*/
+static int
+ParentOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreeNodeParent(node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PathOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+PathOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    Tcl_DString dString;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node, FALSE, &dString);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+
+static int
+ComparePositions(Blt_TreeNode *n1Ptr, Blt_TreeNode *n2Ptr)
+{
+    if (*n1Ptr == *n2Ptr) {
+        return 0;
+    }
+    if (Blt_TreeIsBefore(*n1Ptr, *n2Ptr)) {
+        return -1;
+    }
+    return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PositionOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+PositionOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    PositionData data;
+    Blt_TreeNode *nodeArr, *nodePtr;
+    Blt_TreeNode node;
+    Blt_TreeNode parent, lastParent;
+    Tcl_Obj *listObjPtr, *objPtr;
+    int i;
+    int position;
+    Tcl_DString dString;
+    int n;
+
+    memset((char *)&data, 0, sizeof(data));
+    /* Process switches  */
+    n = Blt_ProcessObjSwitches(interp, positionSwitches, objc - 2, objv + 2, 
+	     (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
+    if (n < 0) {
+	return TCL_ERROR;
+    }
+    objc -= n + 2, objv += n + 2;
+
+    /* Collect the node ids into an array */
+    nodeArr = Blt_Malloc((objc + 1) * sizeof(Blt_TreeNode));
+    for (i = 0; i < objc; i++) {
+	if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+	    Blt_Free(nodeArr);
+	    return TCL_ERROR;
+	}
+	nodeArr[i] = node;
+    }
+    nodeArr[i] = NULL;
+
+    if (data.sort) {		/* Sort the nodes by depth-first order 
+				 * if requested. */
+	qsort((char *)nodeArr, objc, sizeof(Blt_TreeNode), 
+	      (QSortCompareProc *)ComparePositions);
+    }
+
+    position = 0;		/* Suppress compiler warning. */
+    lastParent = NULL;
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    Tcl_DStringInit(&dString);
+    for (nodePtr = nodeArr; *nodePtr != NULL; nodePtr++) {
+	parent = Blt_TreeNodeParent(*nodePtr);
+	if ((parent != NULL) && (parent == lastParent)) {
+	    /* 
+	     * Since we've sorted the nodes already, we can safely
+	     * assume that if two consecutive nodes have the same
+	     * parent, the first node came before the second. If
+	     * this is the case, use the last node as a starting
+	     * point.  
+	     */
+	    
+	    /*
+	     * Note that we start comparing from the last node,
+	     * not its successor.  Some one may give us the same
+	     * node more than once.  
+	     */
+	    node = *(nodePtr - 1); /* Can't get here unless there's
+				    * more than one node. */
+	    for(/*empty*/; node != NULL; node = Blt_TreeNextSibling(node)) {
+		if (node == *nodePtr) {
+		    break;
+		}
+		position++;
+	    }
+	} else {
+	    /* The fallback is to linearly search through the
+	     * parent's list of children, counting the number of
+	     * preceding siblings. Except for nodes with many
+	     * siblings (100+), this should be okay. */
+	    position = Blt_TreeNodePosition(*nodePtr);
+	}
+	if (data.sort) {
+	    lastParent = parent; /* Update the last parent. */
+	}	    
+	/* 
+	 * Add an element in the form "parent -at position" to the
+	 * list that we're generating.
+	 */
+	if (data.withId) {
+	    objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*nodePtr));
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+	if (data.withParent) {
+	    char *string;
+
+	    Tcl_DStringSetLength(&dString, 0); /* Clear the string. */
+	    string = (parent == NULL) ? "" : Blt_Itoa(Blt_TreeNodeId(parent));
+	    Tcl_DStringAppendElement(&dString, string);
+	    Tcl_DStringAppendElement(&dString, "-at");
+	    Tcl_DStringAppendElement(&dString, Blt_Itoa(position));
+	    objPtr = Tcl_NewStringObj(Tcl_DStringValue(&dString), -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	} else {
+	    objPtr = Tcl_NewIntObj(position);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+    }
+    Tcl_DStringFree(&dString);
+    Blt_Free(nodeArr);
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PreviousOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+PreviousOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreePrevNode(Blt_TreeRootNode(cmdPtr->tree), node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+PrevSiblingOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    int inode;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    inode = -1;
+    node = Blt_TreePrevSibling(node);
+    if (node != NULL) {
+	inode = Blt_TreeNodeId(node);
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
+    return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestoreOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+RestoreOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode root;
+    RestoreData data;
+    Tcl_DString dString;
+    char *entry, *eol, *next;
+    char saved;
+    int result;
+
+    if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    memset((char *)&data, 0, sizeof(data));
+    Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
+    data.root = root;
+
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4, 
+	     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    result = TCL_OK;
+    nLines = 0;
+    Tcl_DStringInit(&dString);
+    entry = eol = Tcl_GetString(objv[3]);
+    next = entry;
+    while (*eol != '\0') {
+	/* Find the next end of line. */
+	for (eol = next; (*eol != '\n') && (*eol != '\0'); eol++) {
+	    /*empty*/
+	}
+	/* 
+	 * Since we don't own the string (the Tcl_Obj could be shared),
+	 * save the current end-of-line character (it's either a NUL
+	 * or NL) so we can NUL-terminate the line for the call to
+	 * Tcl_SplitList and repair it when we're done.
+	 */
+	saved = *eol;
+	*eol = '\0';
+	next = eol + 1;
+	nLines++;
+	if (Tcl_CommandComplete(entry)) {
+	    char **elemArr;
+	    int nElem;
+	    
+	    if (Tcl_SplitList(interp, entry, &nElem, &elemArr) != TCL_OK) {
+		*eol = saved;
+		return TCL_ERROR;
+	    }
+	    if (nElem > 0) {
+		result = RestoreNode(cmdPtr, nElem, elemArr, &data);
+		Blt_Free(elemArr);
+		if (result != TCL_OK) {
+		    *eol = saved;
+		    break;
+		}
+	    }
+	    entry = next;
+	}
+	*eol = saved;
+    }
+    Blt_DeleteHashTable(&data.idTable);
+    return result;
+}
+
+static int
+ReadEntry(
+    Tcl_Interp *interp,
+    Tcl_Channel channel,
+    int *argcPtr,
+    char ***argvPtr)
+{
+    Tcl_DString dString;
+    int result;
+    char *entry;
+
+    if (*argvPtr != NULL) {
+	Blt_Free(*argvPtr);
+	*argvPtr = NULL;
+    }
+    Tcl_DStringInit(&dString);
+    entry = NULL;
+    while (Tcl_Gets(channel, &dString) >= 0) {
+	nLines++;
+	Tcl_DStringAppend(&dString, "\n", 1);
+	entry = Tcl_DStringValue(&dString);
+	if (Tcl_CommandComplete(entry)) {
+	    result = Tcl_SplitList(interp, entry, argcPtr, argvPtr);
+	    Tcl_DStringFree(&dString);
+	    return result;
+	}
+    }
+    Tcl_DStringFree(&dString);
+    if (entry == NULL) {
+	*argcPtr = 0;		/* EOF */
+	return TCL_OK;
+    }
+    Tcl_AppendResult(interp, "error reading file: ", 
+		     Tcl_PosixError(interp), (char *)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RestorefileOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+RestorefileOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode root;
+    int nElem;
+    char **elemArr;
+    char *fileName;
+    int result;
+    Tcl_Channel channel;
+    RestoreData data;
+
+    if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    fileName = Tcl_GetString(objv[3]);
+    channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+    if (channel == NULL) {
+	return TCL_ERROR;
+    }
+    memset((char *)&data, 0, sizeof(data));
+    Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
+    data.root = root;
+
+    /* Process switches  */
+    if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4, 
+	     (char *)&data, 0) < 0) {
+	Tcl_Close(interp, channel);
+	return TCL_ERROR;
+    }
+    elemArr = NULL;
+    nLines = 0;
+    for (;;) {
+	result = ReadEntry(interp, channel, &nElem, &elemArr);
+	if ((result != TCL_OK) || (nElem == 0)) {
+	    break;
+	}
+	result = RestoreNode(cmdPtr, nElem, elemArr, &data);
+	if (result != TCL_OK) {
+	    break;
+	}
+    } 
+    if (elemArr != NULL) {
+	Blt_Free(elemArr);
+    }
+    Tcl_Close(interp, channel);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RootOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+RootOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode root;
+
+    if (objc == 3) {
+	Blt_TreeNode node;
+
+	if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Blt_TreeChangeRoot(cmdPtr->tree, node);
+    }
+    root = Blt_TreeRootNode(cmdPtr->tree);
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(root));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+SetOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    char *string;
+    TagSearch cursor;
+	
+    string = Tcl_GetString(objv[2]);
+    if (isdigit(UCHAR(*string))) {
+	if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    } else {
+	node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
+	if (node == NULL) {
+	    return TCL_ERROR;
+	}
+	for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+	    if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SizeOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+SizeOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeSize(node));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagForgetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TagForgetOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    register int i;
+
+    for (i = 3; i < objc; i++) {
+	Blt_TreeForgetTag(cmdPtr->tree, Tcl_GetString(objv[i]));
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNamesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagNamesOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashSearch cursor;
+    Blt_TreeTagEntry *tPtr;
+    Tcl_Obj *listObjPtr, *objPtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    objPtr = Tcl_NewStringObj("all", -1);
+    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    if (objc == 3) {
+	Blt_HashEntry *hPtr;
+
+	objPtr = Tcl_NewStringObj("root", -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL; 
+	     hPtr = Blt_NextHashEntry(&cursor)) {
+	    tPtr = Blt_GetHashValue(hPtr);
+	    objPtr = Tcl_NewStringObj(tPtr->tagName, -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+    } else {
+	register int i;
+	Blt_TreeNode node;
+	Blt_HashEntry *hPtr, *h2Ptr;
+	Blt_HashTable uniqTable;
+	int isNew;
+
+	Blt_InitHashTable(&uniqTable, BLT_STRING_KEYS);
+	for (i = 3; i < objc; i++) {
+	    if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
+		goto error;
+	    }
+	    if (node == Blt_TreeRootNode(cmdPtr->tree)) {
+		Blt_CreateHashEntry(&uniqTable, "root", &isNew);
+	    }
+	    for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL;
+		 hPtr = Blt_NextHashEntry(&cursor)) {
+		tPtr = Blt_GetHashValue(hPtr);
+		h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
+		if (h2Ptr != NULL) {
+		    Blt_CreateHashEntry(&uniqTable, tPtr->tagName, &isNew);
+		}
+	    }
+	}
+	for (hPtr = Blt_FirstHashEntry(&uniqTable, &cursor); hPtr != NULL;
+	     hPtr = Blt_NextHashEntry(&cursor)) {
+	    objPtr = Tcl_NewStringObj(Blt_GetHashKey(&uniqTable, hPtr), -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+	Blt_DeleteHashTable(&uniqTable);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+ error:
+    Tcl_DecrRefCount(listObjPtr);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNodesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagNodesOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_HashTable nodeTable;
+    Blt_TreeNode node;
+    Tcl_Obj *listObjPtr;
+    Tcl_Obj *objPtr;
+    char *string;
+    int isNew;
+    register int i;
+	
+    Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
+    for (i = 3; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (strcmp(string, "all") == 0) {
+	    break;
+	} else if (strcmp(string, "root") == 0) {
+	    Blt_CreateHashEntry(&nodeTable, 
+		(char *)Blt_TreeRootNode(cmdPtr->tree), &isNew);
+	    continue;
+	} else {
+	    Blt_HashTable *tablePtr;
+	    
+	    tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
+	    if (tablePtr != NULL) {
+		for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); 
+		     hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+		    node = Blt_GetHashValue(hPtr);
+		    Blt_CreateHashEntry(&nodeTable, (char *)node, &isNew);
+		}
+		continue;
+	    }
+	}
+	Tcl_AppendResult(interp, "can't find a tag \"", string, "\"",
+			 (char *)NULL);
+	goto error;
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL; 
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
+	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    Blt_DeleteHashTable(&nodeTable);
+    return TCL_OK;
+
+ error:
+    Blt_DeleteHashTable(&nodeTable);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagAddOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagAddOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    register int i;
+    char *string;
+    TagSearch cursor;
+
+    string = Tcl_GetString(objv[3]);
+    if (isdigit(UCHAR(string[0]))) {
+	Tcl_AppendResult(interp, "bad tag \"", string, 
+		 "\": can't start with a digit", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
+	Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
+			 string, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (i = 4; i < objc; i++) {
+	node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+	if (node == NULL) {
+	    return TCL_ERROR;
+	}
+	for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+	    if (AddTag(cmdPtr, node, string) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDeleteOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagDeleteOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    char *string;
+    Blt_HashTable *tablePtr;
+
+    string = Tcl_GetString(objv[3]);
+    if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
+	Tcl_AppendResult(interp, "can't delete reserved tag \"", string, "\"", 
+			 (char *)NULL);
+        return TCL_ERROR;
+    }
+    tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
+    if (tablePtr != NULL) {
+        register int i;
+        Blt_TreeNode node;
+        TagSearch cursor;
+        Blt_HashEntry *hPtr;
+      
+        for (i = 4; i < objc; i++) {
+	    node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+	    if (node == NULL) {
+	        return TCL_ERROR;
+	    }
+	    for (/* empty */; node != NULL; 	
+		node = NextTaggedNode(node, &cursor)) {
+	        hPtr = Blt_FindHashEntry(tablePtr, (char *)node);
+	        if (hPtr != NULL) {
+		    Blt_DeleteHashEntry(tablePtr, hPtr);
+	        }
+	   }
+       }
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDumpOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagDumpOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    register Blt_TreeNode root, node;
+    Tcl_DString dString;
+    TagSearch cursor;
+    register int i;
+
+    Tcl_DStringInit(&dString);
+    root = Blt_TreeRootNode(cmdPtr->tree);
+    for (i = 3; i < objc; i++) {
+	node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
+	if (node == NULL) {
+	    Tcl_DStringFree(&dString);
+	    return TCL_ERROR;
+	}
+	for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+	    PrintNode(cmdPtr, root, node, &dString);
+	}
+    }
+    Tcl_DStringResult(interp, &dString);
+    Tcl_DStringFree(&dString);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec tagOps[] = {
+    {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag node...",},
+    {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag node...",},
+    {"dump", 2, (Blt_Op)TagDumpOp, 4, 0, "tag...",},
+    {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
+    {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?node...?",},
+    {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
+};
+
+static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
+
+static int
+TagOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv, 
+	0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (cmdPtr, interp, objc, objv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceCreateOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TraceCreateOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashEntry *hPtr;
+    Blt_TreeNode node;
+    TraceInfo *tracePtr;
+    char *string, *key, *command;
+    char *tagName;
+    char idString[200];
+    int flags, isNew;
+    int length;
+
+    string = Tcl_GetString(objv[3]);
+    if (isdigit(UCHAR(*string))) {
+	if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	tagName = NULL;
+    } else {
+	tagName = Blt_Strdup(string);
+	node = NULL;
+    }
+    key = Tcl_GetString(objv[4]);
+    string = Tcl_GetString(objv[5]);
+    flags = GetTraceFlags(string);
+    if (flags < 0) {
+	Tcl_AppendResult(interp, "unknown flag in \"", string, "\"", 
+		     (char *)NULL);
+	return TCL_ERROR;
+    }
+    command = Tcl_GetStringFromObj(objv[6], &length);
+    /* Stash away the command in structure and pass that to the trace. */
+    tracePtr = Blt_Malloc(length + sizeof(TraceInfo));
+    strcpy(tracePtr->command, command);
+    tracePtr->cmdPtr = cmdPtr;
+    tracePtr->withTag = tagName;
+    tracePtr->node = node;
+    tracePtr->traceToken = Blt_TreeCreateTrace(cmdPtr->tree, node, key, tagName,
+	flags, TreeTraceProc, tracePtr);
+
+    sprintf(idString, "trace%d", cmdPtr->traceCounter++);
+    hPtr = Blt_CreateHashEntry(&(cmdPtr->traceTable), idString, &isNew);
+    Blt_SetHashValue(hPtr, tracePtr);
+
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceDeleteOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TraceDeleteOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TraceInfo *tracePtr;
+    Blt_HashEntry *hPtr;
+    register int i;
+    char *key;
+
+    for (i = 3; i < objc; i++) {
+	key = Tcl_GetString(objv[i]);
+	hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
+	if (hPtr == NULL) {
+	    Tcl_AppendResult(interp, "unknown trace \"", key, "\"", 
+			     (char *)NULL);
+	    return TCL_ERROR;
+	}
+	tracePtr = Blt_GetHashValue(hPtr);
+	Blt_DeleteHashEntry(&(cmdPtr->traceTable), hPtr); 
+	Blt_TreeDeleteTrace(tracePtr->traceToken);
+	if (tracePtr->withTag != NULL) {
+	    Blt_Free(tracePtr->withTag);
+	}
+	Blt_Free(tracePtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceNamesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TraceNamesOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+
+    for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	Tcl_AppendElement(interp, Blt_GetHashKey(&(cmdPtr->traceTable), hPtr));
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceInfoOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TraceInfoOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    TraceInfo *tracePtr;
+    struct Blt_TreeTraceStruct *tokenPtr;
+    Blt_HashEntry *hPtr;
+    Tcl_DString dString;
+    char string[5];
+    char *key;
+
+    key = Tcl_GetString(objv[3]);
+    hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(interp, "unknown trace \"", key, "\"", 
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    tracePtr = Blt_GetHashValue(hPtr);
+    if (tracePtr->withTag != NULL) {
+	Tcl_DStringAppendElement(&dString, tracePtr->withTag);
+    } else {
+	int inode;
+
+	inode = Blt_TreeNodeId(tracePtr->node);
+	Tcl_DStringAppendElement(&dString, Blt_Itoa(inode));
+    }
+    tokenPtr = (struct Blt_TreeTraceStruct *)tracePtr->traceToken;
+    Tcl_DStringAppendElement(&dString, tokenPtr->key);
+    PrintTraceFlags(tokenPtr->mask, string);
+    Tcl_DStringAppendElement(&dString, string);
+    Tcl_DStringAppendElement(&dString, tracePtr->command);
+    Tcl_DStringResult(interp, &dString);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TraceOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec traceOps[] =
+{
+    {"create", 1, (Blt_Op)TraceCreateOp, 7, 7, "node key how command",},
+    {"delete", 1, (Blt_Op)TraceDeleteOp, 3, 0, "id...",},
+    {"info", 1, (Blt_Op)TraceInfoOp, 4, 4, "id",},
+    {"names", 1, (Blt_Op)TraceNamesOp, 3, 3, "",},
+};
+
+static int nTraceOps = sizeof(traceOps) / sizeof(Blt_OpSpec);
+
+static int
+TraceOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nTraceOps, traceOps, BLT_OP_ARG2, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (cmdPtr, interp, objc, objv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TypeOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    Tcl_Obj *valueObjPtr;
+    char *string;
+
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+
+    string = Tcl_GetString(objv[3]);
+    if (Blt_TreeGetValue(interp, cmdPtr->tree, node, string, &valueObjPtr) 
+	!= TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (valueObjPtr->typePtr != NULL) {
+	Tcl_SetResult(interp, valueObjPtr->typePtr->name, TCL_VOLATILE);
+    } else {
+	Tcl_SetResult(interp, "string", TCL_STATIC);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnsetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+UnsetOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    char *string;
+	
+    string = Tcl_GetString(objv[2]);
+    if (isdigit(UCHAR(*string))) {
+	if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    } else {
+	TagSearch cursor;
+
+	node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
+	if (node == NULL) {
+	    return TCL_ERROR;
+	}
+	for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
+	    if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+
+typedef struct {
+    TreeCmd *cmdPtr;
+    unsigned int flags;
+    int type;
+    int mode;
+    char *key;
+    char *command;
+} SortData;
+
+#define SORT_RECURSE		(1<<2)
+#define SORT_DECREASING		(1<<3)
+#define SORT_PATHNAME		(1<<4)
+
+enum SortTypes { SORT_DICTIONARY, SORT_REAL, SORT_INTEGER, SORT_ASCII, 
+	SORT_COMMAND };
+
+enum SortModes { SORT_FLAT, SORT_REORDER };
+
+static Blt_SwitchSpec sortSwitches[] = 
+{
+    {BLT_SWITCH_VALUE, "-ascii", Blt_Offset(SortData, type), 0, 0, 
+	SORT_ASCII},
+    {BLT_SWITCH_STRING, "-command", Blt_Offset(SortData, command), 0},
+    {BLT_SWITCH_FLAG, "-decreasing", Blt_Offset(SortData, flags), 0, 0, 
+	SORT_DECREASING},
+    {BLT_SWITCH_VALUE, "-dictionary", Blt_Offset(SortData, type), 0, 0, 
+	SORT_DICTIONARY},
+    {BLT_SWITCH_VALUE, "-integer", Blt_Offset(SortData, type), 0, 0, 
+	SORT_INTEGER},
+    {BLT_SWITCH_STRING, "-key", Blt_Offset(SortData, key), 0},
+    {BLT_SWITCH_FLAG, "-path", Blt_Offset(SortData, flags), 0, 0, 
+	SORT_PATHNAME},
+    {BLT_SWITCH_VALUE, "-real", Blt_Offset(SortData, type), 0, 0, 
+	SORT_REAL},
+    {BLT_SWITCH_VALUE, "-recurse", Blt_Offset(SortData, flags), 0, 0, 
+	SORT_RECURSE},
+    {BLT_SWITCH_VALUE, "-reorder", Blt_Offset(SortData, mode), 0, 0, 
+	SORT_REORDER},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static SortData sortData;
+
+static int
+CompareNodes(Blt_TreeNode *n1Ptr, Blt_TreeNode *n2Ptr)
+{
+    TreeCmd *cmdPtr = sortData.cmdPtr;
+    char *s1, *s2;
+    int result;
+    Tcl_DString dString1, dString2;
+
+    s1 = s2 = "";
+    result = 0;
+
+    if (sortData.flags & SORT_PATHNAME) {
+	Tcl_DStringInit(&dString1);
+	Tcl_DStringInit(&dString2);
+    }
+    if (sortData.key != NULL) {
+	Tcl_Obj *valueObjPtr;
+
+	if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n1Ptr, 
+	     sortData.key, &valueObjPtr) == TCL_OK) {
+	    s1 = Tcl_GetString(valueObjPtr);
+	}
+	if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n2Ptr, 
+	     sortData.key, &valueObjPtr) == TCL_OK) {
+	    s2 = Tcl_GetString(valueObjPtr);
+	}
+    } else if (sortData.flags & SORT_PATHNAME)  {
+	Blt_TreeNode root;
+	
+	root = Blt_TreeRootNode(cmdPtr->tree);
+	s1 = GetNodePath(cmdPtr, root, *n1Ptr, FALSE, &dString1);
+	s2 = GetNodePath(cmdPtr, root, *n2Ptr, FALSE, &dString2);
+    } else {
+	s1 = Blt_TreeNodeLabel(*n1Ptr);
+	s2 = Blt_TreeNodeLabel(*n2Ptr);
+    }
+    switch (sortData.type) {
+    case SORT_ASCII:
+	result = strcmp(s1, s2);
+	break;
+
+    case SORT_COMMAND:
+	if (sortData.command == NULL) {
+	    result = Blt_DictionaryCompare(s1, s2);
+	} else {
+	    Tcl_DString dsCmd, dsName;
+	    char *qualName;
+
+	    result = 0;	/* Hopefully this will be Ok even if the
+			 * Tcl command fails to return the correct
+			 * result. */
+	    Tcl_DStringInit(&dsCmd);
+	    Tcl_DStringAppend(&dsCmd, sortData.command, -1);
+	    Tcl_DStringInit(&dsName);
+	    qualName = Blt_GetQualifiedName(
+		Blt_GetCommandNamespace(cmdPtr->interp, cmdPtr->cmdToken), 
+		Tcl_GetCommandName(cmdPtr->interp, cmdPtr->cmdToken), &dsName);
+	    Tcl_DStringAppendElement(&dsCmd, qualName);
+	    Tcl_DStringFree(&dsName);
+	    Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n1Ptr)));
+	    Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n2Ptr)));
+	    Tcl_DStringAppendElement(&dsCmd, s1);
+	    Tcl_DStringAppendElement(&dsCmd, s2);
+	    result = Tcl_GlobalEval(cmdPtr->interp, Tcl_DStringValue(&dsCmd));
+	    Tcl_DStringFree(&dsCmd);
+	    
+	    if ((result != TCL_OK) ||
+		(Tcl_GetInt(cmdPtr->interp, 
+		    Tcl_GetStringResult(cmdPtr->interp), &result) != TCL_OK)) {
+		Tcl_BackgroundError(cmdPtr->interp);
+	    }
+	    Tcl_ResetResult(cmdPtr->interp);
+	}
+	break;
+
+    case SORT_DICTIONARY:
+	result = Blt_DictionaryCompare(s1, s2);
+	break;
+
+    case SORT_INTEGER:
+	{
+	    int i1, i2;
+
+	    if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) {
+		if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+		    result = i1 - i2;
+		} else {
+		    result = -1;
+		} 
+	    } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
+		result = 1;
+	    } else {
+		result = Blt_DictionaryCompare(s1, s2);
+	    }
+	}
+	break;
+
+    case SORT_REAL:
+	{
+	    double r1, r2;
+
+	    if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) {
+		if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+		    result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
+		} else {
+		    result = -1;
+		} 
+	    } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
+		result = 1;
+	    } else {
+		result = Blt_DictionaryCompare(s1, s2);
+	    }
+	}
+	break;
+    }
+    if (result == 0) {
+	result = Blt_TreeNodeId(*n1Ptr) - Blt_TreeNodeId(*n2Ptr);
+    }
+    if (sortData.flags & SORT_DECREASING) {
+	result = -result;
+    } 
+    if (sortData.flags & SORT_PATHNAME) {
+	Tcl_DStringFree(&dString1);
+	Tcl_DStringFree(&dString2);
+    }
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortApplyProc --
+ *
+ *	Sorts the subnodes at a given node.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortApplyProc(
+    Blt_TreeNode node,
+    ClientData clientData,
+    int order)			/* Not used. */
+{
+    TreeCmd *cmdPtr = clientData;
+
+    if (!Blt_TreeIsLeaf(node)) {
+	Blt_TreeSortNode(cmdPtr->tree, node, CompareNodes);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortOp --
+ *  
+ *---------------------------------------------------------------------- 
+ */
+static int
+SortOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode top;
+    SortData data;
+    int result;
+
+    if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Process switches  */
+    memset(&data, 0, sizeof(data));
+    data.cmdPtr = cmdPtr;
+    if (Blt_ProcessObjSwitches(interp, sortSwitches, objc - 3, objv + 3, 
+	     (char *)&data, 0) < 0) {
+	return TCL_ERROR;
+    }
+    if (data.command != NULL) {
+	data.type = SORT_COMMAND;
+    }
+    data.cmdPtr = cmdPtr;
+    sortData = data;
+    if (data.mode == SORT_FLAT) {
+	Blt_TreeNode *p, *nodeArr, node;
+	int nNodes;
+	Tcl_Obj *objPtr, *listObjPtr;
+	int i;
+
+	if (data.flags & SORT_RECURSE) {
+	    nNodes = Blt_TreeSize(top);
+	} else {
+	    nNodes = Blt_TreeNodeDegree(top);
+	}
+	nodeArr = Blt_Malloc(nNodes * sizeof(Blt_TreeNode));
+	assert(nodeArr);
+	p = nodeArr;
+	if (data.flags & SORT_RECURSE) {
+	    for(node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
+		*p++ = node;
+	    }
+	} else {
+	    for (node = Blt_TreeFirstChild(top); node != NULL;
+		 node = Blt_TreeNextSibling(node)) {
+		*p++ = node;
+	    }
+	}
+	qsort((char *)nodeArr, nNodes, sizeof(Blt_TreeNode),
+	      (QSortCompareProc *)CompareNodes);
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+	for (p = nodeArr, i = 0; i < nNodes; i++, p++) {
+	    objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*p));
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+	Blt_Free(nodeArr);
+	result = TCL_OK;
+    } else {
+	if (data.flags & SORT_RECURSE) {
+	    result = Blt_TreeApply(top, SortApplyProc, cmdPtr);
+	} else {
+	    result = SortApplyProc(top, cmdPtr, TREE_PREORDER);
+	}
+    }
+    Blt_FreeSwitches(sortSwitches, (char *)&data, 0);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ValuesOp --
+ *
+ *	Returns the names of values for a node or array value.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+ValuesOp(
+    TreeCmd *cmdPtr,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_TreeNode node;
+    Tcl_Obj *listObjPtr;
+    
+    if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    if (objc == 4) { 
+	char *string;
+
+	string = Tcl_GetString(objv[3]);
+	if (Blt_TreeArrayNames(interp, cmdPtr->tree, node, string, listObjPtr)
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    } else {
+	Blt_TreeKey key;
+	Tcl_Obj *objPtr;
+	Blt_TreeKeySearch keyIter;
+
+	for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL; 
+	     key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
+	    objPtr = Tcl_NewStringObj(key, -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}	    
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeInstObjCmd --
+ *
+ * 	This procedure is invoked to process commands on behalf of
+ *	the tree object.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec treeOps[] =
+{
+    {"ancestor", 2, (Blt_Op)AncestorOp, 4, 4, "node1 node2",},
+    {"apply", 1, (Blt_Op)ApplyOp, 3, 0, "node ?switches?",},
+    {"attach", 2, (Blt_Op)AttachOp, 2, 3, "?tree?",},
+    {"children", 2, (Blt_Op)ChildrenOp, 3, 5, "node ?first? ?last?",},
+    {"copy", 2, (Blt_Op)CopyOp, 4, 0, 
+	"srcNode ?destTree? destNode ?switches?",},
+    {"degree", 2, (Blt_Op)DegreeOp, 3, 0, "node",},
+    {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "node ?node...?",},
+    {"depth", 3, (Blt_Op)DepthOp, 3, 3, "node",},
+    {"dump", 4, (Blt_Op)DumpOp, 3, 3, "node",},
+    {"dumpfile", 5, (Blt_Op)DumpfileOp, 4, 4, "node fileName",},
+    {"exists", 1, (Blt_Op)ExistsOp, 3, 4, "node ?key?",},
+    {"find", 4, (Blt_Op)FindOp, 3, 0, "node ?switches?",},
+    {"findchild", 5, (Blt_Op)FindChildOp, 4, 4, "node name",},
+    {"firstchild", 3, (Blt_Op)FirstChildOp, 3, 3, "node",},
+    {"get", 1, (Blt_Op)GetOp, 3, 5, "node ?key? ?defaultValue?",},
+    {"index", 3, (Blt_Op)IndexOp, 3, 3, "name",},
+    {"insert", 3, (Blt_Op)InsertOp, 3, 0, "parent ?switches?",},
+    {"is", 2, (Blt_Op)IsOp, 2, 0, "oper args...",},
+    {"keys", 1, (Blt_Op)KeysOp, 3, 0, "node ?node...?",},
+    {"label", 3, (Blt_Op)LabelOp, 3, 4, "node ?newLabel?",},
+    {"lastchild", 3, (Blt_Op)LastChildOp, 3, 3, "node",},
+    {"move", 1, (Blt_Op)MoveOp, 4, 0, "node newParent ?switches?",},
+    {"next", 4, (Blt_Op)NextOp, 3, 3, "node",},
+    {"nextsibling", 5, (Blt_Op)NextSiblingOp, 3, 3, "node",},
+    {"notify", 2, (Blt_Op)NotifyOp, 2, 0, "args...",},
+    {"parent", 3, (Blt_Op)ParentOp, 3, 3, "node",},
+    {"path", 3, (Blt_Op)PathOp, 3, 3, "node",},
+    {"position", 2, (Blt_Op)PositionOp, 3, 0, "?switches? node...",},
+    {"previous", 5, (Blt_Op)PreviousOp, 3, 3, "node",},
+    {"prevsibling", 5, (Blt_Op)PrevSiblingOp, 3, 3, "node",},
+    {"restore", 7, (Blt_Op)RestoreOp, 4, 4, "node dataString",},
+    {"restorefile", 8, (Blt_Op)RestorefileOp, 4, 4, "node fileName",},
+    {"root", 2, (Blt_Op)RootOp, 2, 3, "?node?",},
+    {"set", 3, (Blt_Op)SetOp, 3, 0, "node ?key value...?",},
+    {"size", 2, (Blt_Op)SizeOp, 3, 3, "node",},
+    {"sort", 2, (Blt_Op)SortOp, 3, 0, "node ?flags...?",},
+    {"tag", 2, (Blt_Op)TagOp, 3, 0, "args...",},
+    {"trace", 2, (Blt_Op)TraceOp, 2, 0, "args...",},
+    {"type", 2, (Blt_Op)TypeOp, 4, 4, "node key",},
+    {"unset", 3, (Blt_Op)UnsetOp, 3, 0, "node ?key...?",},
+    {"values", 1, (Blt_Op)ValuesOp, 3, 4, "node ?key?",},
+};
+
+static int nTreeOps = sizeof(treeOps) / sizeof(Blt_OpSpec);
+
+static int
+TreeInstObjCmd(
+    ClientData clientData,	/* Information about the widget. */
+    Tcl_Interp *interp,		/* Interpreter to report errors back to. */
+    int objc,			/* Number of arguments. */
+    Tcl_Obj *CONST *objv)	/* Vector of argument strings. */
+{
+    Blt_Op proc;
+    TreeCmd *cmdPtr = clientData;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nTreeOps, treeOps, BLT_OP_ARG1, objc, objv,
+ 	BLT_OP_LINEAR_SEARCH);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(cmdPtr);
+    result = (*proc) (cmdPtr, interp, objc, objv);
+    Tcl_Release(cmdPtr);
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TreeInstDeleteProc --
+ *
+ *	Deletes the command associated with the tree.  This is
+ *	called only when the command associated with the tree is
+ *	destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TreeInstDeleteProc(ClientData clientData)
+{
+    TreeCmd *cmdPtr = clientData;
+
+    ReleaseTreeObject(cmdPtr);
+    if (cmdPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(cmdPtr->tablePtr, cmdPtr->hashPtr);
+    }
+    Blt_DeleteHashTable(&(cmdPtr->traceTable));
+    Blt_Free(cmdPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GenerateName --
+ *
+ *	Generates an unique tree command name.  Tree names are in
+ *	the form "treeN", where N is a non-negative integer. Check 
+ *	each name generated to see if it is already a tree. We want
+ *	to recycle names if possible.
+ *	
+ * Results:
+ *	Returns the unique name.  The string itself is stored in the
+ *	dynamic string passed into the routine.
+ *
+ * ----------------------------------------------------------------------
+ */
+static CONST char *
+GenerateName(
+    Tcl_Interp *interp,
+    CONST char *prefix, 
+    CONST char *suffix,
+    Tcl_DString *resultPtr)
+{
+
+    int n;
+    Tcl_Namespace *nsPtr;
+    char string[200];
+    Tcl_CmdInfo cmdInfo;
+    Tcl_DString dString;
+    CONST char *treeName, *name;
+
+    /* 
+     * Parse the command and put back so that it's in a consistent
+     * format.  
+     *
+     *	t1         <current namespace>::t1
+     *	n1::t1     <current namespace>::n1::t1
+     *	::t1	   ::t1
+     *  ::n1::t1   ::n1::t1
+     */
+    treeName = NULL;		/* Suppress compiler warning. */
+    for (n = 0; n < INT_MAX; n++) {
+	Tcl_DStringInit(&dString);
+	Tcl_DStringAppend(&dString, prefix, -1);
+	sprintf(string, "tree%d", n);
+	Tcl_DStringAppend(&dString, string, -1);
+	Tcl_DStringAppend(&dString, suffix, -1);
+	treeName = Tcl_DStringValue(&dString);
+	if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) != TCL_OK) {
+	    Tcl_AppendResult(interp, "can't find namespace in \"", treeName, 
+		"\"", (char *)NULL);
+	    return NULL;
+	}
+	if (nsPtr == NULL) {
+	    nsPtr = Tcl_GetCurrentNamespace(interp);
+	}
+	treeName = Blt_GetQualifiedName(nsPtr, name, resultPtr);
+	/* 
+	 * Check if the command already exists. 
+	 */
+	if (Tcl_GetCommandInfo(interp, (char *)treeName, &cmdInfo)) {
+	    continue;
+	}
+	if (!Blt_TreeExists(interp, treeName)) {
+	    /* 
+	     * We want the name of the tree command and the underlying
+	     * tree object to be the same. Check that the free command
+	     * name isn't an already a tree object name.  
+	     */
+	    break;
+	}
+    }
+    return treeName;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeCreateOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TreeCreateOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TreeCmdInterpData *dataPtr = clientData;
+    CONST char *treeName;
+    Tcl_DString dString;
+    Blt_Tree token;
+
+    treeName = NULL;
+    if (objc == 3) {
+	treeName = Tcl_GetString(objv[2]);
+    }
+    Tcl_DStringInit(&dString);
+    if (treeName == NULL) {
+	treeName = GenerateName(interp, "", "", &dString);
+    } else {
+	char *p;
+
+	p = strstr(treeName, "#auto");
+	if (p != NULL) {
+	    *p = '\0';
+	    treeName = GenerateName(interp, treeName, p + 5, &dString);
+	    *p = '#';
+	} else {
+	    CONST char *name;
+	    Tcl_CmdInfo cmdInfo;
+	    Tcl_Namespace *nsPtr;
+
+	    nsPtr = NULL;
+	    /* 
+	     * Parse the command and put back so that it's in a consistent
+	     * format.  
+	     *
+	     *	t1         <current namespace>::t1
+	     *	n1::t1     <current namespace>::n1::t1
+	     *	::t1	   ::t1
+	     *  ::n1::t1   ::n1::t1
+	     */
+	    if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) 
+		!= TCL_OK) {
+		Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
+			 "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    if (nsPtr == NULL) {
+		nsPtr = Tcl_GetCurrentNamespace(interp);
+	    }
+	    treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
+	    /* 
+	     * Check if the command already exists. 
+	     */
+	    if (Tcl_GetCommandInfo(interp, (char *)treeName, &cmdInfo)) {
+		Tcl_AppendResult(interp, "a command \"", treeName,
+				 "\" already exists", (char *)NULL);
+		goto error;
+	    }
+	    if (Blt_TreeExists(interp, treeName)) {
+		Tcl_AppendResult(interp, "a tree \"", treeName, 
+			"\" already exists", (char *)NULL);
+		goto error;
+	    }
+	} 
+    } 
+    if (treeName == NULL) {
+	goto error;
+    }
+    if (Blt_TreeCreate(interp, treeName, &token) == TCL_OK) {
+	int isNew;
+	TreeCmd *cmdPtr;
+
+	cmdPtr = Blt_Calloc(1, sizeof(TreeCmd));
+	assert(cmdPtr);
+	cmdPtr->dataPtr = dataPtr;
+	cmdPtr->tree = token;
+	cmdPtr->interp = interp;
+	Blt_InitHashTable(&(cmdPtr->traceTable), BLT_STRING_KEYS);
+	Blt_InitHashTable(&(cmdPtr->notifyTable), BLT_STRING_KEYS);
+	cmdPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)treeName, 
+		(Tcl_ObjCmdProc *)TreeInstObjCmd, cmdPtr, TreeInstDeleteProc);
+	cmdPtr->tablePtr = &dataPtr->treeTable;
+	cmdPtr->hashPtr = Blt_CreateHashEntry(cmdPtr->tablePtr, (char *)cmdPtr,
+	      &isNew);
+	Blt_SetHashValue(cmdPtr->hashPtr, cmdPtr);
+	Tcl_SetResult(interp, (char *)treeName, TCL_VOLATILE);
+	Tcl_DStringFree(&dString);
+	Blt_TreeCreateEventHandler(cmdPtr->tree, TREE_NOTIFY_ALL, 
+	     TreeEventProc, cmdPtr);
+	return TCL_OK;
+    }
+ error:
+    Tcl_DStringFree(&dString);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeDestroyOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TreeDestroyOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TreeCmdInterpData *dataPtr = clientData;
+    TreeCmd *cmdPtr;
+    char *string;
+    register int i;
+
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	cmdPtr = GetTreeCmd(dataPtr, interp, string);
+	if (cmdPtr == NULL) {
+	    Tcl_AppendResult(interp, "can't find a tree named \"", string,
+			     "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	Tcl_DeleteCommandFromToken(interp, cmdPtr->cmdToken);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeNamesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TreeNamesOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TreeCmdInterpData *dataPtr = clientData;
+    TreeCmd *cmdPtr;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Tcl_Obj *objPtr, *listObjPtr;
+    Tcl_DString dString;
+    char *qualName;
+
+    Tcl_DStringInit(&dString);
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	cmdPtr = Blt_GetHashValue(hPtr);
+	qualName = Blt_GetQualifiedName(
+		Blt_GetCommandNamespace(interp, cmdPtr->cmdToken), 
+		Tcl_GetCommandName(interp, cmdPtr->cmdToken), &dString);
+	if (objc == 3) {
+	    if (!Tcl_StringMatch(qualName, Tcl_GetString(objv[2]))) {
+		continue;
+	    }
+	}
+	objPtr = Tcl_NewStringObj(qualName, -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    Tcl_DStringFree(&dString);
+    return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeObjCmd --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec treeCmdOps[] =
+{
+    {"create", 1, (Blt_Op)TreeCreateOp, 2, 3, "?name?",},
+    {"destroy", 1, (Blt_Op)TreeDestroyOp, 3, 0, "name...",},
+    {"names", 1, (Blt_Op)TreeNamesOp, 2, 3, "?pattern?...",},
+};
+
+static int nCmdOps = sizeof(treeCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+TreeObjCmd(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+
+    proc = Blt_GetOpFromObj(interp, nCmdOps, treeCmdOps, BLT_OP_ARG1, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TreeInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting the "tree" command
+ *	is deleted.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Removes the hash table managing all tree names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TreeInterpDeleteProc(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp)
+{
+    TreeCmdInterpData *dataPtr = clientData;
+
+    /* All tree instances should already have been destroyed when
+     * their respective Tcl commands were deleted. */
+    Blt_DeleteHashTable(&dataPtr->treeTable);
+    Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+/*ARGSUSED*/
+static int
+CompareDictionaryCmd(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    int result;
+    char *s1, *s2;
+
+    s1 = Tcl_GetString(objv[1]);
+    s2 = Tcl_GetString(objv[2]);
+    result = Blt_DictionaryCompare(s1, s2);
+    result = (result > 0) ? -1 : (result < 0) ? 1 : 0;
+    Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ExitCmd(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    int code;
+
+    if (Tcl_GetIntFromObj(interp, objv[1], &code) != TCL_OK) {
+	return TCL_ERROR;
+    }
+#ifdef TCL_THREADS
+    Tcl_Exit(code);
+#else 
+    exit(code);
+#endif
+    /*NOTREACHED*/
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_TreeInit --
+ *
+ *	This procedure is invoked to initialize the "tree" command.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Creates the new command and adds a new entry into a global Tcl
+ *	associative array.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_TreeInit(Tcl_Interp *interp)
+{
+    TreeCmdInterpData *dataPtr;	/* Interpreter-specific data. */
+    static Blt_ObjCmdSpec cmdSpec = { 
+	"tree", TreeObjCmd, 
+    };
+    static Blt_ObjCmdSpec compareSpec = { 
+	"compare", CompareDictionaryCmd, 
+    };
+    static Blt_ObjCmdSpec exitSpec = { 
+	"exit", ExitCmd, 
+    };
+    if (Blt_InitObjCmd(interp, "blt::util", &compareSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    if (Blt_InitObjCmd(interp, "blt::util", &exitSpec) == NULL) {
+	return TCL_ERROR;
+    }
+
+    dataPtr = GetTreeCmdInterpData(interp);
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeCmdGetToken(
+    Tcl_Interp *interp,
+    CONST char *string,
+    Blt_Tree  *treePtr)
+{
+    TreeCmdInterpData *dataPtr;
+    TreeCmd *cmdPtr;
+
+    dataPtr = GetTreeCmdInterpData(interp);
+    cmdPtr = GetTreeCmd(dataPtr, interp, string);
+    if (cmdPtr == NULL) {
+	Tcl_AppendResult(interp, "can't find a tree associated with \"",
+		 string, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *treePtr = cmdPtr->tree;
+    return TCL_OK;
+}
+
+/* Dump tree to dbm */
+/* Convert node data to datablock */
+
+#endif /* NO_TREE */
+
Index: trunk/kitgen/8.x/blt/generic/bltTreeView.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeView.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeView.c	(revision 175)
@@ -0,0 +1,5159 @@
+
+/*
+ * bltTreeView.c --
+ *
+ *	This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ *   1.  "open" operation should change scroll offset so that as many
+ *	 new entries (up to half a screen) can be seen.
+ *   2.  "open" needs to adjust the scrolloffset so that the same entry
+ *	 is seen at the same place.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+
+#define BUTTON_PAD		2
+#define BUTTON_IPAD		1
+#define BUTTON_SIZE		7
+#define COLUMN_PAD		2
+#define FOCUS_WIDTH		1
+#define ICON_PADX		2
+#define ICON_PADY		1
+#define INSET_PAD		0
+#define LABEL_PADX		3
+#define LABEL_PADY		0
+
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define DEF_ICON_WIDTH		16
+#define DEF_ICON_HEIGHT		16
+
+static Blt_TreeApplyProc DeleteApplyProc;
+static Blt_TreeApplyProc CreateApplyProc;
+
+extern Blt_CustomOption bltTreeViewDataOption;
+
+static Blt_OptionParseProc ObjToTree;
+static Blt_OptionPrintProc TreeToObj;
+static Blt_OptionFreeProc FreeTree;
+Blt_CustomOption bltTreeViewTreeOption =
+{
+    ObjToTree, TreeToObj, FreeTree, NULL,
+};
+
+static Blt_OptionParseProc ObjToIcons;
+static Blt_OptionPrintProc IconsToObj;
+static Blt_OptionFreeProc FreeIcons;
+Blt_CustomOption bltTreeViewIconsOption =
+{
+    /* Contains a pointer to the widget that's currently being
+     * configured.  This is used in the custom configuration parse
+     * routine for icons.  */
+    ObjToIcons, IconsToObj, FreeIcons, NULL,
+};
+
+static Blt_OptionParseProc ObjToButton;
+static Blt_OptionPrintProc ButtonToObj;
+static Blt_CustomOption buttonOption = {
+    ObjToButton, ButtonToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToUid;
+static Blt_OptionPrintProc UidToObj;
+static Blt_OptionFreeProc FreeUid;
+Blt_CustomOption bltTreeViewUidOption = {
+    ObjToUid, UidToObj, FreeUid, NULL,
+};
+
+static Blt_OptionParseProc ObjToScrollmode;
+static Blt_OptionPrintProc ScrollmodeToObj;
+static Blt_CustomOption scrollmodeOption = {
+    ObjToScrollmode, ScrollmodeToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToSelectmode;
+static Blt_OptionPrintProc SelectmodeToObj;
+static Blt_CustomOption selectmodeOption = {
+    ObjToSelectmode, SelectmodeToObj, NULL, NULL,
+};
+
+static Blt_OptionParseProc ObjToSeparator;
+static Blt_OptionPrintProc SeparatorToObj;
+static Blt_OptionFreeProc FreeSeparator;
+static Blt_CustomOption separatorOption = {
+    ObjToSeparator, SeparatorToObj, FreeSeparator, NULL,
+};
+
+static Blt_OptionParseProc ObjToLabel;
+static Blt_OptionPrintProc LabelToObj;
+static Blt_OptionFreeProc FreeLabel;
+static Blt_CustomOption labelOption =
+{
+    ObjToLabel, LabelToObj, FreeLabel, NULL,
+};
+
+
+#define DEF_BUTTON_ACTIVE_BACKGROUND	RGB_WHITE
+#define DEF_BUTTON_ACTIVE_BG_MONO	STD_ACTIVE_BG_MONO
+#define DEF_BUTTON_ACTIVE_FOREGROUND	STD_ACTIVE_FOREGROUND
+#define DEF_BUTTON_ACTIVE_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_BUTTON_BORDERWIDTH		"1"
+#if (TK_MAJOR_VERSION == 4) 
+#define DEF_BUTTON_CLOSE_RELIEF		"flat"
+#define DEF_BUTTON_OPEN_RELIEF		"flat"
+#else
+#define DEF_BUTTON_CLOSE_RELIEF		"solid"
+#define DEF_BUTTON_OPEN_RELIEF		"solid"
+#endif
+#define DEF_BUTTON_NORMAL_BACKGROUND	RGB_WHITE
+#define DEF_BUTTON_NORMAL_BG_MONO	STD_NORMAL_BG_MONO
+#define DEF_BUTTON_NORMAL_FOREGROUND	STD_NORMAL_FOREGROUND
+#define DEF_BUTTON_NORMAL_FG_MONO	STD_NORMAL_FG_MONO
+#define DEF_BUTTON_SIZE			"7"
+
+/* RGB_LIGHTBLUE1 */
+
+#define DEF_TV_ACTIVE_FOREGROUND	"black"
+#define DEF_TV_ACTIVE_ICONS \
+	"blt::tv::activeOpenFolder blt::tv::activeCloseFolder"
+#define DEF_TV_ACTIVE_RELIEF	"flat"
+#define DEF_TV_ACTIVE_STIPPLE	"gray25"
+#define DEF_TV_ALLOW_DUPLICATES	"yes"
+#define DEF_TV_BACKGROUND	"white"
+#define DEF_TV_BORDERWIDTH	STD_BORDERWIDTH
+#define DEF_TV_BUTTON		"auto"
+#define DEF_TV_DASHES		"dot"
+#define DEF_TV_EXPORT_SELECTION	"no"
+#define DEF_TV_FOREGROUND		STD_NORMAL_FOREGROUND
+#define DEF_TV_FG_MONO		STD_NORMAL_FG_MONO
+#define DEF_TV_FLAT		"no"
+#define DEF_TV_FOCUS_DASHES	"dot"
+#define DEF_TV_FOCUS_EDIT	"no"
+#define DEF_TV_FOCUS_FOREGROUND	STD_ACTIVE_FOREGROUND
+#define DEF_TV_FOCUS_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_TV_FONT		"Courier 12"
+#define DEF_TV_HEIGHT		"400"
+#define DEF_TV_HIDE_LEAVES	"no"
+#define DEF_TV_HIDE_ROOT	"yes"
+#define DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_TV_FOCUS_HIGHLIGHT_COLOR		"black"
+#define DEF_TV_FOCUS_HIGHLIGHT_WIDTH		"2"
+#define DEF_TV_ICONS "blt::tv::normalOpenFolder blt::tv::normalCloseFolder"
+#define DEF_TV_VLINE_COLOR	RGB_GREY50
+#define DEF_TV_VLINE_MONO	STD_NORMAL_FG_MONO
+#define DEF_TV_LINESPACING	"0"
+#define DEF_TV_LINEWIDTH	"1"
+#define DEF_TV_MAKE_PATH	"no"
+#define DEF_TV_NEW_TAGS		"no"
+#define DEF_TV_NORMAL_BACKGROUND 	STD_NORMAL_BACKGROUND
+#define DEF_TV_NORMAL_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_TV_RELIEF		"sunken"
+#define DEF_TV_RESIZE_CURSOR	"arrow"
+#define DEF_TV_SCROLL_INCREMENT "20"
+#define DEF_TV_SCROLL_MODE	"hierbox"
+#define DEF_TV_SELECT_BACKGROUND 	STD_SELECT_BACKGROUND /* RGB_LIGHTBLUE1 */
+#define DEF_TV_SELECT_BG_MONO  	STD_SELECT_BG_MONO
+#define DEF_TV_SELECT_BORDERWIDTH "1"
+#define DEF_TV_SELECT_FOREGROUND 	STD_SELECT_FOREGROUND
+#define DEF_TV_SELECT_FG_MONO  	STD_SELECT_FG_MONO
+#define DEF_TV_SELECT_MODE	"single"
+#define DEF_TV_SELECT_RELIEF	"flat"
+#define DEF_TV_SHOW_ROOT	"yes"
+#define DEF_TV_SHOW_TITLES	"yes"
+#define DEF_TV_SORT_SELECTION	"no"
+#define DEF_TV_TAKE_FOCUS	"1"
+#define DEF_TV_TEXT_COLOR	STD_NORMAL_FOREGROUND
+#define DEF_TV_TEXT_MONO	STD_NORMAL_FG_MONO
+#define DEF_TV_TRIMLEFT		""
+#define DEF_TV_WIDTH		"200"
+
+Blt_ConfigSpec bltTreeViewButtonSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
+	DEF_BUTTON_ACTIVE_BACKGROUND, Blt_Offset(TreeView, button.activeBorder),
+	0},
+    {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", (char *)NULL, 
+	(char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-activefg", "activeForeground", (char *)NULL, 
+	(char *)NULL, 0, 0},
+    {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
+	DEF_BUTTON_ACTIVE_FOREGROUND, 
+	Blt_Offset(TreeView, button.activeFgColor), 0},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_BUTTON_NORMAL_BACKGROUND, Blt_Offset(TreeView, button.border), 0},
+    {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 
+	0},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_BUTTON_BORDERWIDTH, Blt_Offset(TreeView, button.borderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_RELIEF, "-closerelief", "closeRelief", "Relief",
+	DEF_BUTTON_CLOSE_RELIEF, Blt_Offset(TreeView, button.closeRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_BUTTON_NORMAL_FOREGROUND, Blt_Offset(TreeView, button.fgColor), 0},
+    {BLT_CONFIG_CUSTOM, "-images", "images", "Icons",
+	(char *)NULL, Blt_Offset(TreeView, button.icons), BLT_CONFIG_NULL_OK, 
+	&bltTreeViewIconsOption},
+    {BLT_CONFIG_RELIEF, "-openrelief", "openRelief", "Relief",
+	DEF_BUTTON_OPEN_RELIEF, Blt_Offset(TreeView, button.openRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-size", "size", "Size", 
+	DEF_BUTTON_SIZE, Blt_Offset(TreeView, button.reqSize), 0},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+Blt_ConfigSpec bltTreeViewEntrySpecs[] =
+{
+    {BLT_CONFIG_CUSTOM, "-activeicons", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, activeIcons),
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
+    {BLT_CONFIG_CUSTOM, "-bindtags", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, tagsUid),
+	BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+    {BLT_CONFIG_CUSTOM, "-button", (char *)NULL, (char *)NULL,
+	DEF_TV_BUTTON, Blt_Offset(TreeViewEntry, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
+    {BLT_CONFIG_CUSTOM, "-closecommand", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, closeCmd),
+	BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+    {BLT_CONFIG_CUSTOM, "-data", (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, BLT_CONFIG_NULL_OK, &bltTreeViewDataOption},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_FONT, "-font", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, font), 0},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, color), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DISTANCE, "-height", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, reqHeight),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-icons", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, icons),
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
+    {BLT_CONFIG_CUSTOM, "-label", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, labelUid), 0, 
+	&labelOption},
+    {BLT_CONFIG_CUSTOM, "-opencommand", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, openCmd),
+	BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+    {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, shadow),
+	BLT_CONFIG_NULL_OK | BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
+	(char *)NULL, Blt_Offset(TreeViewEntry, shadow),
+	BLT_CONFIG_NULL_OK | BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+Blt_ConfigSpec bltTreeViewSpecs[] =
+{
+    {BLT_CONFIG_CUSTOM, "-activeicons", "activeIcons", "Icons",
+	DEF_TV_ACTIVE_ICONS, Blt_Offset(TreeView, activeIcons),
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
+    {BLT_CONFIG_BITFLAG, 
+	"-allowduplicates", "allowDuplicates", "AllowDuplicates",
+	DEF_TV_ALLOW_DUPLICATES, Blt_Offset(TreeView, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_ALLOW_DUPLICATES},
+    {BLT_CONFIG_BITFLAG, "-autocreate", "autoCreate", "AutoCreate",
+	DEF_TV_MAKE_PATH, Blt_Offset(TreeView, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_FILL_ANCESTORS},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_TV_BACKGROUND, Blt_Offset(TreeView, border), 0},
+    {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_TV_BORDERWIDTH, Blt_Offset(TreeView, borderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-button", "button", "Button",
+	DEF_TV_BUTTON, Blt_Offset(TreeView, buttonFlags),
+	BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
+    {BLT_CONFIG_STRING, "-closecommand", "closeCommand", "CloseCommand",
+	(char *)NULL, Blt_Offset(TreeView, closeCmd), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+	(char *)NULL, Blt_Offset(TreeView, cursor), BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes",
+	DEF_TV_DASHES, Blt_Offset(TreeView, dashes),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BITFLAG, "-exportselection", "exportSelection",
+	"ExportSelection", DEF_TV_EXPORT_SELECTION, 
+	Blt_Offset(TreeView, flags), BLT_CONFIG_DONT_SET_DEFAULT, 
+	(Blt_CustomOption *)TV_SELECT_EXPORT},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_BOOLEAN, "-flat", "flat", "Flat",
+	DEF_TV_FLAT, Blt_Offset(TreeView, flatView),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
+	DEF_TV_FOCUS_DASHES, Blt_Offset(TreeView, focusDashes),
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, 
+	"-focusforeground", "focusForeground", "FocusForeground",
+	DEF_TV_FOCUS_FOREGROUND, Blt_Offset(TreeView, focusColor),
+	BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, 
+	"-focusforeground", "focusForeground", "FocusForeground",
+	DEF_TV_FOCUS_FG_MONO, Blt_Offset(TreeView, focusColor),
+	BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_FONT, "-font", "font", "Font",
+	DEF_TV_FONT, Blt_Offset(TreeView, font), 0},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_TV_TEXT_COLOR, Blt_Offset(TreeView, fgColor),
+	BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	DEF_TV_TEXT_MONO, Blt_Offset(TreeView, fgColor), 
+	BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_DISTANCE, "-height", "height", "Height",
+	DEF_TV_HEIGHT, Blt_Offset(TreeView, reqHeight),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BITFLAG, "-hideleaves", "hideLeaves", "HideLeaves",
+	DEF_TV_HIDE_LEAVES, Blt_Offset(TreeView, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_LEAVES},
+    {BLT_CONFIG_BITFLAG, "-hideroot", "hideRoot", "HideRoot",
+	DEF_TV_HIDE_ROOT, Blt_Offset(TreeView, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_ROOT},
+    {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+	"HighlightBackground", DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND, 
+        Blt_Offset(TreeView, highlightBgColor), 0},
+    {BLT_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+	DEF_TV_FOCUS_HIGHLIGHT_COLOR, Blt_Offset(TreeView, highlightColor), 0},
+    {BLT_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+	"HighlightThickness", DEF_TV_FOCUS_HIGHLIGHT_WIDTH, 
+	Blt_Offset(TreeView, highlightWidth), BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-icons", "icons", "Icons",
+	DEF_TV_ICONS, Blt_Offset(TreeView, icons), 
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
+    {BLT_CONFIG_BORDER, "-nofocusselectbackground", "noFocusSelectBackground",
+	"NoFocusSelectBackground", DEF_TV_SELECT_BACKGROUND, 
+	Blt_Offset(TreeView, selOutFocusBorder), TK_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-nofocusselectforeground", "noFocusSelectForeground", 
+	"NoFocusSelectForeground", DEF_TV_SELECT_FOREGROUND, 
+	Blt_Offset(TreeView, selOutFocusFgColor), TK_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+	DEF_TV_VLINE_COLOR, Blt_Offset(TreeView, lineColor),
+	BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
+	DEF_TV_VLINE_MONO, Blt_Offset(TreeView, lineColor),
+	BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_DISTANCE, "-linespacing", "lineSpacing", "LineSpacing",
+	DEF_TV_LINESPACING, Blt_Offset(TreeView, leader),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-linewidth", "lineWidth", "LineWidth",
+	DEF_TV_LINEWIDTH, Blt_Offset(TreeView, lineWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BITFLAG, "-newtags", "newTags", "NewTags",
+	DEF_TV_NEW_TAGS, Blt_Offset(TreeView, flags),
+	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_NEW_TAGS},
+    {BLT_CONFIG_STRING, "-opencommand", "openCommand", "OpenCommand",
+	(char *)NULL, Blt_Offset(TreeView, openCmd), BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_TV_RELIEF, Blt_Offset(TreeView, relief), 0},
+    {BLT_CONFIG_CURSOR, "-resizecursor", "resizeCursor", "ResizeCursor",
+	DEF_TV_RESIZE_CURSOR, Blt_Offset(TreeView, resizeCursor), 0},
+    {BLT_CONFIG_CUSTOM, "-scrollmode", "scrollMode", "ScrollMode",
+	DEF_TV_SCROLL_MODE, Blt_Offset(TreeView, scrollMode),
+	BLT_CONFIG_DONT_SET_DEFAULT, &scrollmodeOption},
+    {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
+	DEF_TV_SELECT_BACKGROUND, Blt_Offset(TreeView, selInFocusBorder), 0},
+    {BLT_CONFIG_DISTANCE, 
+	"-selectborderwidth", "selectBorderWidth", "BorderWidth",
+	DEF_TV_SELECT_BORDERWIDTH, Blt_Offset(TreeView, selBorderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+	(char *)NULL, Blt_Offset(TreeView, selectCmd), BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+	DEF_TV_SELECT_FOREGROUND, Blt_Offset(TreeView, selInFocusFgColor), 0},
+    {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
+	DEF_TV_SELECT_MODE, Blt_Offset(TreeView, selectMode),
+	BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
+    {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+	DEF_TV_SELECT_RELIEF, Blt_Offset(TreeView, selRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-separator", "separator", "Separator",
+	(char *)NULL, Blt_Offset(TreeView, pathSep), BLT_CONFIG_NULL_OK, 
+	&separatorOption},
+    {BLT_CONFIG_BITFLAG, "-showtitles", "showTitles", "ShowTitles",
+	DEF_TV_SHOW_TITLES, Blt_Offset(TreeView, flags), 0,
+        (Blt_CustomOption *)TV_SHOW_COLUMN_TITLES},
+    {BLT_CONFIG_BITFLAG, "-sortselection", "sortSelection", "SortSelection",
+	DEF_TV_SORT_SELECTION, Blt_Offset(TreeView, flags), 
+        BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_SELECT_SORTED},
+    {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+	DEF_TV_TAKE_FOCUS, Blt_Offset(TreeView, takeFocus), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_CUSTOM, "-tree", "tree", "Tree", 
+	(char *)NULL, Blt_Offset(TreeView, tree), BLT_CONFIG_NULL_OK, 
+	&bltTreeViewTreeOption},
+    {BLT_CONFIG_STRING, "-trim", "trim", "Trim",
+	DEF_TV_TRIMLEFT, Blt_Offset(TreeView, trimLeft), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
+	DEF_TV_WIDTH, Blt_Offset(TreeView, reqWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_STRING, 
+	"-xscrollcommand", "xScrollCommand", "ScrollCommand",
+	(char *)NULL, Blt_Offset(TreeView, xScrollCmdPrefix), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DISTANCE, 
+	"-xscrollincrement", "xScrollIncrement", "ScrollIncrement",
+	DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, xScrollUnits),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_STRING, 
+        "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+	(char *)NULL, Blt_Offset(TreeView, yScrollCmdPrefix), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DISTANCE, 
+	"-yscrollincrement", "yScrollIncrement", "ScrollIncrement",
+	DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, yScrollUnits),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+/* Forward Declarations */
+static Blt_TreeNotifyEventProc TreeEventProc;
+static Blt_TreeTraceProc TreeTraceProc;
+static Tcl_CmdDeleteProc WidgetInstCmdDeleteProc;
+static Tcl_FreeProc DestroyTreeView;
+static Tcl_IdleProc DisplayTreeView;
+static Tk_EventProc TreeViewEventProc;
+
+static int ComputeVisibleEntries _ANSI_ARGS_((TreeView *tvPtr));
+
+EXTERN int Blt_TreeCmdGetToken _ANSI_ARGS_((Tcl_Interp *interp, 
+	CONST char *treeName, Blt_Tree *treePtr));
+
+extern Blt_TreeApplyProc Blt_TreeViewSortApplyProc;
+static Blt_BindPickProc PickItem;
+static Blt_BindTagProc GetTags;
+static Tcl_FreeProc DestroyEntry;
+static Tcl_ObjCmdProc TreeViewObjCmd;
+static Tk_ImageChangedProc IconChangedProc;
+static Tk_SelectionProc SelectionProc;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewEventuallyRedraw --
+ *
+ *	Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Information gets redisplayed.  Right now we don't do selective
+ *	redisplays:  the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewEventuallyRedraw(TreeView *tvPtr)
+{
+    if ((tvPtr->tkwin != NULL) && ((tvPtr->flags & TV_REDRAW) == 0)) {
+	tvPtr->flags |= TV_REDRAW;
+	Tcl_DoWhenIdle(DisplayTreeView, tvPtr);
+    }
+}
+
+void
+Blt_TreeViewTraceColumn(TreeView *tvPtr, TreeViewColumn *columnPtr)
+{
+    Blt_TreeCreateTrace(tvPtr->tree, NULL /* Node */, columnPtr->key, NULL,
+	TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET, 
+	TreeTraceProc, tvPtr);
+}
+
+static void
+TraceColumns(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+
+    for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	Blt_TreeCreateTrace(
+		tvPtr->tree, 
+		NULL /* Node */, 
+		columnPtr->key /* Key pattern */, 
+		NULL /* Tag */,
+	        TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET, 
+	        TreeTraceProc /* Callback routine */, 
+		tvPtr /* Client data */);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToTree --
+ *
+ *	Convert the string representing the name of a tree object 
+ *	into a tree token.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToTree(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
+    Blt_Tree tree;
+    char *string;
+
+    tree = NULL;
+    string = Tcl_GetString(objPtr);
+    if ((string[0] != '\0') && 
+	(Blt_TreeGetToken(interp, string, &tree) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    *treePtr = tree;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeToObj --
+ *
+ * Results:
+ *	The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+TreeToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    Blt_Tree tree = *(Blt_Tree *)(widgRec + offset);
+
+    if (tree == NULL) {
+	return bltEmptyStringObjPtr;
+    } else {
+	return Tcl_NewStringObj(Blt_TreeName(tree), -1);
+    }
+}
+
+/*ARGSUSED*/
+static void
+FreeTree(
+    ClientData clientData,
+    Display *display,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
+
+    if (*treePtr != NULL) {
+	Blt_TreeNode root;
+	TreeView *tvPtr = clientData;
+
+	/* 
+	 * Release the current tree, removing any entry fields. 
+	 */
+	root = Blt_TreeRootNode(*treePtr);
+	Blt_TreeApply(root, DeleteApplyProc, tvPtr);
+	Blt_TreeViewClearSelection(tvPtr);
+	Blt_TreeReleaseToken(*treePtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToScrollmode --
+ *
+ *	Convert the string reprsenting a scroll mode, to its numeric
+ *	form.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToScrollmode(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* New legend position string */
+    char *widgRec,
+    int offset)
+{
+    char *string;
+    char c;
+    int *modePtr = (int *)(widgRec + offset);
+
+    string = Tcl_GetString(objPtr);
+    c = string[0];
+    if ((c == 'l') && (strcmp(string, "listbox") == 0)) {
+	*modePtr = BLT_SCROLL_MODE_LISTBOX;
+    } else if ((c == 't') && (strcmp(string, "treeview") == 0)) {
+	*modePtr = BLT_SCROLL_MODE_HIERBOX;
+    } else if ((c == 'h') && (strcmp(string, "hiertable") == 0)) {
+	*modePtr = BLT_SCROLL_MODE_HIERBOX;
+    } else if ((c == 'c') && (strcmp(string, "canvas") == 0)) {
+	*modePtr = BLT_SCROLL_MODE_CANVAS;
+    } else {
+	Tcl_AppendResult(interp, "bad scroll mode \"", string,
+	    "\": should be \"treeview\", \"listbox\", or \"canvas\"", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollmodeToObj --
+ *
+ * Results:
+ *	The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ScrollmodeToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    int mode = *(int *)(widgRec + offset);
+
+    switch (mode) {
+    case BLT_SCROLL_MODE_LISTBOX:
+	return Tcl_NewStringObj("listbox", -1);
+    case BLT_SCROLL_MODE_HIERBOX:
+	return Tcl_NewStringObj("hierbox", -1);
+    case BLT_SCROLL_MODE_CANVAS:
+	return Tcl_NewStringObj("canvas", -1);
+    default:
+	return Tcl_NewStringObj("unknown scroll mode", -1);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToSelectmode --
+ *
+ *	Convert the string reprsenting a scroll mode, to its numeric
+ *	form.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSelectmode(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    char *string;
+    char c;
+    int *modePtr = (int *)(widgRec + offset);
+
+    string = Tcl_GetString(objPtr);
+    c = string[0];
+    if ((c == 's') && (strcmp(string, "single") == 0)) {
+	*modePtr = SELECT_MODE_SINGLE;
+    } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
+	*modePtr = SELECT_MODE_MULTIPLE;
+    } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+	*modePtr = SELECT_MODE_SINGLE;
+    } else {
+	Tcl_AppendResult(interp, "bad select mode \"", string,
+	    "\": should be \"single\" or \"multiple\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectmodeToObj --
+ *
+ * Results:
+ *	The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SelectmodeToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    int mode = *(int *)(widgRec + offset);
+
+    switch (mode) {
+    case SELECT_MODE_SINGLE:
+	return Tcl_NewStringObj("single", -1);
+    case SELECT_MODE_MULTIPLE:
+	return Tcl_NewStringObj("multiple", -1);
+    default:
+	return Tcl_NewStringObj("unknown scroll mode", -1);
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToButton --
+ *
+ *	Convert a string to one of three values.
+ *		0 - false, no, off
+ *		1 - true, yes, on
+ *		2 - auto
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToButton(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    char *string;
+    int *flagsPtr = (int *)(widgRec + offset);
+
+    string = Tcl_GetString(objPtr);
+    if ((string[0] == 'a') && (strcmp(string, "auto") == 0)) {
+	*flagsPtr &= ~BUTTON_MASK;
+	*flagsPtr |= BUTTON_AUTO;
+    } else {
+	int bool;
+
+	if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	*flagsPtr &= ~BUTTON_MASK;
+	if (bool) {
+	    *flagsPtr |= BUTTON_SHOW;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonToObj --
+ *
+ * Results:
+ *	The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ButtonToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    int bool;
+    unsigned int flags = *(int *)(widgRec + offset);
+
+    bool = (flags & BUTTON_MASK);
+    if (bool == BUTTON_AUTO) {
+	return Tcl_NewStringObj("auto", 4);
+    } else {
+	return Tcl_NewBooleanObj(bool);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToScrollmode --
+ *
+ *	Convert the string reprsenting a scroll mode, to its numeric
+ *	form.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSeparator(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;		/* Tcl_Obj representing the new value. */
+    char *widgRec;
+    int offset;
+{
+    char **sepPtr = (char **)(widgRec + offset);
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if (*string == '\0') {
+	*sepPtr = SEPARATOR_LIST;
+    } else if (strcmp(string, "none") == 0) {
+	*sepPtr = SEPARATOR_NONE;
+    } else {
+	*sepPtr = Blt_Strdup(string);
+    } 
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SeparatorToObj --
+ *
+ * Results:
+ *	The string representation of the separator is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SeparatorToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    char *separator = *(char **)(widgRec + offset);
+
+    if (separator == SEPARATOR_NONE) {
+	return bltEmptyStringObjPtr;
+    } else if (separator == SEPARATOR_LIST) {
+	return Tcl_NewStringObj("list", -1);
+    }  else {
+	return Tcl_NewStringObj(separator, -1);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeSeparator --
+ *
+ *	Free the UID from the widget record, setting it to NULL.
+ *
+ * Results:
+ *	The UID in the widget record is set to NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeSeparator(
+    ClientData clientData,
+    Display *display,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    char *separator = *(char **)(widgRec + offset);
+
+    if ((separator != SEPARATOR_LIST) && (separator != SEPARATOR_NONE)) {
+	Blt_Free(separator);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToLabel --
+ *
+ *	Convert the string representing the label. 
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToLabel(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    UID *labelPtr = (UID *)(widgRec + offset);
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if (string[0] != '\0') {
+	TreeView *tvPtr = clientData;
+
+	*labelPtr = Blt_TreeViewGetUid(tvPtr, string);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeToObj --
+ *
+ * Results:
+ *	The string of the entry's label is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+LabelToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    UID labelUid = *(UID *)(widgRec + offset);
+    char *string;
+
+    if (labelUid == NULL) {
+	TreeViewEntry *entryPtr  = (TreeViewEntry *)widgRec;
+
+	string = Blt_TreeNodeLabel(entryPtr->node);
+    } else {
+	string = labelUid;
+    }
+    return Tcl_NewStringObj(string, -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeLabel(
+    ClientData clientData,
+    Display *display,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    UID *labelPtr = (UID *)(widgRec + offset);
+
+    if (*labelPtr != NULL) {
+	TreeView *tvPtr = clientData;
+
+	Blt_TreeViewFreeUid(tvPtr, *labelPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewGetUid --
+ *
+ *	Gets or creates a unique string identifier.  Strings are
+ *	reference counted.  The string is placed into a hashed table
+ *	local to the treeview.
+ *
+ * Results:
+ *	Returns the pointer to the hashed string.
+ *
+ *---------------------------------------------------------------------- 
+ */
+UID
+Blt_TreeViewGetUid(TreeView *tvPtr, CONST char *string)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+    int refCount;
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->uidTable, string, &isNew);
+    if (isNew) {
+	refCount = 1;
+    } else {
+	refCount = (int)Blt_GetHashValue(hPtr);
+	refCount++;
+    }
+    Blt_SetHashValue(hPtr, (ClientData)refCount);
+    return Blt_GetHashKey(&tvPtr->uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewFreeUid --
+ *
+ *	Releases the uid.  Uids are reference counted, so only when
+ *	the reference count is zero (i.e. no one else is using the
+ *	string) is the entry removed from the hash table.
+ *
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+void
+Blt_TreeViewFreeUid(TreeView *tvPtr, UID uid)
+{
+    Blt_HashEntry *hPtr;
+    int refCount;
+
+    hPtr = Blt_FindHashEntry(&tvPtr->uidTable, uid);
+    assert(hPtr != NULL);
+    refCount = (int)Blt_GetHashValue(hPtr);
+    refCount--;
+    if (refCount > 0) {
+	Blt_SetHashValue(hPtr, (ClientData)refCount);
+    } else {
+	Blt_DeleteHashEntry(&tvPtr->uidTable, hPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToUid --
+ *
+ *	Converts the string to a Uid. Uid's are hashed, reference
+ *	counted strings.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToUid(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    TreeView *tvPtr = clientData;
+    UID *uidPtr = (UID *)(widgRec + offset);
+    UID newId;
+    char *string;
+
+    newId = NULL;
+    string = Tcl_GetString(objPtr);
+    if (*string != '\0') {
+	newId = Blt_TreeViewGetUid(tvPtr, string);
+    }
+    *uidPtr = newId;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UidToObj --
+ *
+ *	Returns the uid as a string.
+ *
+ * Results:
+ *	The fill style string is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+UidToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    UID uid = *(UID *)(widgRec + offset);
+
+    if (uid == NULL) {
+	return bltEmptyStringObjPtr;
+    }
+    return Tcl_NewStringObj(uid, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeUid --
+ *
+ *	Free the UID from the widget record, setting it to NULL.
+ *
+ * Results:
+ *	The UID in the widget record is set to NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+FreeUid(
+    ClientData clientData,
+    Display *display,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    UID *uidPtr = (UID *)(widgRec + offset);
+
+    if (*uidPtr != NULL) {
+	TreeView *tvPtr = clientData;
+
+	Blt_TreeViewFreeUid(tvPtr, *uidPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconChangedProc
+ *
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+IconChangedProc(
+    ClientData clientData,
+    int x,			/* Not used. */
+    int y,			/* Not used. */
+    int width,			/* Not used. */
+    int height,			/* Not used. */
+    int imageWidth, 		/* Not used. */
+    int imageHeight)		/* Not used. */
+{
+    TreeView *tvPtr = clientData;
+
+    tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+}
+
+TreeViewIcon
+Blt_TreeViewGetIcon(TreeView *tvPtr, CONST char *iconName)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+    struct TreeViewIconStruct *iconPtr;
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->iconTable, iconName, &isNew);
+    if (isNew) {
+	Tk_Image tkImage;
+	int width, height;
+
+	tkImage = Tk_GetImage(tvPtr->interp, tvPtr->tkwin, (char *)iconName, 
+		IconChangedProc, tvPtr);
+	if (tkImage == NULL) {
+	    Blt_DeleteHashEntry(&tvPtr->iconTable, hPtr);
+	    return NULL;
+	}
+	Tk_SizeOfImage(tkImage, &width, &height);
+	iconPtr = Blt_Malloc(sizeof(struct TreeViewIconStruct));
+	iconPtr->tkImage = tkImage;
+	iconPtr->hashPtr = hPtr;
+	iconPtr->refCount = 1;
+	iconPtr->width = width;
+	iconPtr->height = height;
+	Blt_SetHashValue(hPtr, iconPtr);
+    } else {
+	iconPtr = Blt_GetHashValue(hPtr);
+	iconPtr->refCount++;
+    }
+    return iconPtr;
+}
+
+void
+Blt_TreeViewFreeIcon(
+    TreeView *tvPtr,
+    struct TreeViewIconStruct *iconPtr)
+{
+    iconPtr->refCount--;
+    if (iconPtr->refCount == 0) {
+	Blt_DeleteHashEntry(&tvPtr->iconTable, iconPtr->hashPtr);
+	Tk_FreeImage(iconPtr->tkImage);
+	Blt_Free(iconPtr);
+    }
+}
+
+static void
+DumpIconTable(TreeView *tvPtr)
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    struct TreeViewIconStruct *iconPtr;
+
+    for (hPtr = Blt_FirstHashEntry(&tvPtr->iconTable, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	iconPtr = Blt_GetHashValue(hPtr);
+	Tk_FreeImage(iconPtr->tkImage);
+	Blt_Free(iconPtr);
+    }
+    Blt_DeleteHashTable(&tvPtr->iconTable);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToIcons --
+ *
+ *	Convert a list of image names into Tk images.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToIcons(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    Tk_Window tkwin,		/* Not used. */
+    Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
+    char *widgRec,
+    int offset)
+{
+    Tcl_Obj **objv;
+    TreeView *tvPtr = clientData;
+    TreeViewIcon **iconPtrPtr = (TreeViewIcon **)(widgRec + offset);
+    TreeViewIcon *icons;
+    int objc;
+    int result;
+
+    result = TCL_OK;
+    icons = NULL;
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc > 0) {
+	register int i;
+	
+	icons = Blt_Malloc(sizeof(TreeViewIcon *) * (objc + 1));
+	assert(icons);
+	for (i = 0; i < objc; i++) {
+	    icons[i] = Blt_TreeViewGetIcon(tvPtr, Tcl_GetString(objv[i]));
+	    if (icons[i] == NULL) {
+		result = TCL_ERROR;
+		break;
+	    }
+	}
+	icons[i] = NULL;
+    }
+    *iconPtrPtr = icons;
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconsToObj --
+ *
+ *	Converts the icon into its string representation (its name).
+ *
+ * Results:
+ *	The name of the icon is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+IconsToObj(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    Tk_Window tkwin,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    TreeViewIcon *icons = *(TreeViewIcon **)(widgRec + offset);
+    Tcl_Obj *listObjPtr;
+    
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (icons != NULL) {
+	register TreeViewIcon *iconPtr;
+	Tcl_Obj *objPtr;
+
+	for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
+	    objPtr = Tcl_NewStringObj(Blt_NameOfImage((*iconPtr)->tkImage), -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+
+	}
+    }
+    return listObjPtr;
+}
+
+/*ARGSUSED*/
+static void
+FreeIcons(
+    ClientData clientData,
+    Display *display,		/* Not used. */
+    char *widgRec,
+    int offset)
+{
+    TreeViewIcon *icons = *(TreeViewIcon **)(widgRec + offset);
+
+    if (icons != NULL) {
+	register TreeViewIcon *iconPtr;
+	TreeView *tvPtr = clientData;
+
+	for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
+	    Blt_TreeViewFreeIcon(tvPtr, *iconPtr);
+	}
+	Blt_Free(icons);
+    }
+}
+
+TreeViewEntry *
+Blt_NodeToEntry(TreeView *tvPtr, Blt_TreeNode node)
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
+    if (hPtr == NULL) {
+	abort();
+	return NULL;
+    }
+    return Blt_GetHashValue(hPtr);
+}
+
+int
+Blt_TreeViewApply(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,	/* Root entry of subtree. */
+    TreeViewApplyProc *proc,	/* Procedure to call for each entry. */
+    unsigned int flags)
+{
+    if ((flags & ENTRY_HIDDEN) && 
+	(Blt_TreeViewEntryIsHidden(entryPtr))) {
+	return TCL_OK;		/* Hidden node. */
+    }
+    if ((flags & ENTRY_HIDDEN) && (entryPtr->flags & ENTRY_HIDDEN)) {
+	return TCL_OK;		/* Hidden node. */
+    }
+    if (((flags & ENTRY_CLOSED) == 0) || 
+	((entryPtr->flags & ENTRY_CLOSED) == 0)) {
+	TreeViewEntry *childPtr;
+	Blt_TreeNode node, next;
+
+	for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL; 
+	     node = next) {
+	    next = Blt_TreeNextSibling(node);
+	    /* 
+	     * Get the next child before calling Blt_TreeViewApply
+	     * recursively.  This is because the apply callback may
+	     * delete the node and its link.
+	     */
+	    childPtr = Blt_NodeToEntry(tvPtr, node);
+	    if (Blt_TreeViewApply(tvPtr, childPtr, proc, flags) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_TreeViewEntryIsHidden(TreeViewEntry *entryPtr)
+{
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    if ((tvPtr->flags & TV_HIDE_LEAVES) && (Blt_TreeIsLeaf(entryPtr->node))) {
+	return TRUE;
+    }
+    return (entryPtr->flags & ENTRY_HIDDEN) ? TRUE : FALSE;
+}
+
+#ifdef notdef
+int
+Blt_TreeViewEntryIsMapped(TreeViewEntry *entryPtr)
+{
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    /* Don't check if the entry itself is open, only that its
+     * ancestors are. */
+    if (Blt_TreeViewEntryIsHidden(entryPtr)) {
+	return FALSE;
+    }
+    if (entryPtr == tvPtr->rootPtr) {
+	return TRUE;
+    }
+    entryPtr = Blt_TreeViewParentEntry(entryPtr);
+    while (entryPtr != tvPtr->rootPtr) {
+	if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
+	    return FALSE;
+	}
+	entryPtr = Blt_TreeViewParentEntry(entryPtr);
+    }
+    return TRUE;
+}
+#endif
+
+TreeViewEntry *
+Blt_TreeViewFirstChild(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    Blt_TreeNode node;
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL; 
+	 node = Blt_TreeNextSibling(node)) {
+	entryPtr = Blt_NodeToEntry(tvPtr, node);
+	if (((mask & ENTRY_HIDDEN) == 0) || 
+	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
+	    return entryPtr;
+	}
+    }
+    return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewLastChild(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    Blt_TreeNode node;
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    for (node = Blt_TreeLastChild(entryPtr->node); node != NULL; 
+	 node = Blt_TreePrevSibling(node)) {
+	entryPtr = Blt_NodeToEntry(tvPtr, node);
+	if (((mask & ENTRY_HIDDEN) == 0) ||
+	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
+	    return entryPtr;
+	}
+    }
+    return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewNextSibling(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    Blt_TreeNode node;
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    for (node = Blt_TreeNextSibling(entryPtr->node); node != NULL; 
+	 node = Blt_TreeNextSibling(node)) {
+	entryPtr = Blt_NodeToEntry(tvPtr, node);
+	if (((mask & ENTRY_HIDDEN) == 0) ||
+	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
+	    return entryPtr;
+	}
+    }
+    return NULL;
+}
+
+TreeViewEntry *
+Blt_TreeViewPrevSibling(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    Blt_TreeNode node;
+    TreeView *tvPtr = entryPtr->tvPtr; 
+
+    for (node = Blt_TreePrevSibling(entryPtr->node); node != NULL; 
+	 node = Blt_TreePrevSibling(node)) {
+	entryPtr = Blt_NodeToEntry(tvPtr, node);
+	if (((mask & ENTRY_HIDDEN) == 0) ||
+	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
+	    return entryPtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewPrevEntry --
+ *
+ *	Returns the "previous" node in the tree.  This node (in 
+ *	depth-first order) is its parent if the node has no siblings
+ *	that are previous to it.  Otherwise it is the last descendant 
+ *	of the last sibling.  In this case, descend the sibling's
+ *	hierarchy, using the last child at any ancestor, until we
+ *	we find a leaf.
+ *
+ *----------------------------------------------------------------------
+ */
+TreeViewEntry *
+Blt_TreeViewPrevEntry(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    TreeView *tvPtr = entryPtr->tvPtr; 
+    TreeViewEntry *prevPtr;
+
+    if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
+	return NULL;		/* The root is the first node. */
+    }
+    prevPtr = Blt_TreeViewPrevSibling(entryPtr, mask);
+    if (prevPtr == NULL) {
+	/* There are no siblings previous to this one, so pick the parent. */
+	prevPtr = Blt_TreeViewParentEntry(entryPtr);
+    } else {
+	/*
+	 * Traverse down the right-most thread in order to select the
+	 * last entry.  Stop if we find a "closed" entry or reach a leaf.
+	 */
+	entryPtr = prevPtr;
+	while ((entryPtr->flags & mask) == 0) {
+	    entryPtr = Blt_TreeViewLastChild(entryPtr, mask);
+	    if (entryPtr == NULL) {
+		break;		/* Found a leaf. */
+	    }
+	    prevPtr = entryPtr;
+	}
+    }
+    if (prevPtr == NULL) {
+	return NULL;
+    }
+    return prevPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewNextNode --
+ *
+ *	Returns the "next" node in relation to the given node.  
+ *	The next node (in depth-first order) is either the first 
+ *	child of the given node the next sibling if the node has
+ *	no children (the node is a leaf).  If the given node is the 
+ *	last sibling, then try it's parent next sibling.  Continue
+ *	until we either find a next sibling for some ancestor or 
+ *	we reach the root node.  In this case the current node is 
+ *	the last node in the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+TreeViewEntry *
+Blt_TreeViewNextEntry(TreeViewEntry *entryPtr, unsigned int mask)
+{
+    TreeView *tvPtr = entryPtr->tvPtr; 
+    TreeViewEntry *nextPtr;
+    int ignoreLeaf;
+
+    ignoreLeaf = ((tvPtr->flags & TV_HIDE_LEAVES) && 
+		  (Blt_TreeIsLeaf(entryPtr->node)));
+
+    if ((!ignoreLeaf) && ((entryPtr->flags & mask) == 0)) {
+	nextPtr = Blt_TreeViewFirstChild(entryPtr, mask); 
+	if (nextPtr != NULL) {
+	    return nextPtr;	/* Pick the first sub-node. */
+	}
+    }
+
+
+    /* 
+     * Back up until to a level where we can pick a "next sibling".  
+     * For the last entry we'll thread our way back to the root.
+     */
+
+    while (entryPtr != tvPtr->rootPtr) {
+	nextPtr = Blt_TreeViewNextSibling(entryPtr, mask);
+	if (nextPtr != NULL) {
+	    return nextPtr;
+	}
+	entryPtr = Blt_TreeViewParentEntry(entryPtr);
+    }
+    return NULL;		/* At root, no next node. */
+}
+
+void
+Blt_TreeViewConfigureButtons(TreeView *tvPtr)
+{
+    GC newGC;
+    TreeViewButton *buttonPtr = &tvPtr->button;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    gcMask = GCForeground;
+    gcValues.foreground = buttonPtr->fgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (buttonPtr->normalGC != NULL) {
+	Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
+    }
+    buttonPtr->normalGC = newGC;
+
+    gcMask = GCForeground;
+    gcValues.foreground = buttonPtr->activeFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (buttonPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
+    }
+    buttonPtr->activeGC = newGC;
+
+    buttonPtr->width = buttonPtr->height = ODD(buttonPtr->reqSize);
+    if (buttonPtr->icons != NULL) {
+	register int i;
+	int width, height;
+
+	for (i = 0; i < 2; i++) {
+	    if (buttonPtr->icons[i] == NULL) {
+		break;
+	    }
+	    width = TreeViewIconWidth(buttonPtr->icons[i]);
+	    height = TreeViewIconWidth(buttonPtr->icons[i]);
+	    if (buttonPtr->width < width) {
+		buttonPtr->width = width;
+	    }
+	    if (buttonPtr->height < height) {
+		buttonPtr->height = height;
+	    }
+	}
+    }
+    buttonPtr->width += 2 * buttonPtr->borderWidth;
+    buttonPtr->height += 2 * buttonPtr->borderWidth;
+}
+
+int
+Blt_TreeViewConfigureEntry(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,
+    int objc,
+    Tcl_Obj *CONST *objv,
+    int flags)
+{
+    GC newGC;
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+
+    bltTreeViewIconsOption.clientData = tvPtr;
+    bltTreeViewUidOption.clientData = tvPtr;
+    labelOption.clientData = tvPtr;
+    if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin, 
+	bltTreeViewEntrySpecs, objc, objv, (char *)entryPtr, flags) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* 
+     * Check if there are values that need to be added 
+     */
+    for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	Blt_TreeViewAddValue(entryPtr, columnPtr);
+    }
+
+    newGC = NULL;
+    if ((entryPtr->font != NULL) || (entryPtr->color != NULL)) {
+	Tk_Font font;
+	XColor *colorPtr;
+	XGCValues gcValues;
+	unsigned long gcMask;
+
+	font = entryPtr->font;
+	if (font == NULL) {
+	    font = Blt_TreeViewGetStyleFont(tvPtr, tvPtr->treeColumn.stylePtr);
+	}
+	colorPtr = CHOOSE(tvPtr->fgColor, entryPtr->color);
+	gcMask = GCForeground | GCFont;
+	gcValues.foreground = colorPtr->pixel;
+	gcValues.font = Tk_FontId(font);
+	newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    }
+    if (entryPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, entryPtr->gc);
+    }
+    /* Assume all changes require a new layout. */
+    entryPtr->gc = newGC;
+    entryPtr->flags |= ENTRY_LAYOUT_PENDING;
+    if (Blt_ObjConfigModified(bltTreeViewEntrySpecs, "-font", (char *)NULL)) {
+	tvPtr->flags |= TV_UPDATE;
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    return TCL_OK;
+}
+
+void
+Blt_TreeViewDestroyValue(TreeView *tvPtr, TreeViewValue *valuePtr)
+{
+    if (valuePtr->stylePtr != NULL) {
+	Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
+    }
+    if (valuePtr->textPtr != NULL) {
+	Blt_Free(valuePtr->textPtr);
+    }
+}
+
+
+static void
+DestroyEntry(DestroyData data)
+{
+    TreeViewEntry *entryPtr = (TreeViewEntry *)data;
+    TreeView *tvPtr;
+    
+    tvPtr = entryPtr->tvPtr;
+    bltTreeViewIconsOption.clientData = tvPtr;
+    bltTreeViewUidOption.clientData = tvPtr;
+    labelOption.clientData = tvPtr;
+    Blt_FreeObjOptions(bltTreeViewEntrySpecs, (char *)entryPtr, tvPtr->display,
+	0);
+    if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
+	/* Don't clear tags unless this client is the only one using
+	 * the tag table.*/
+	Blt_TreeClearTags(tvPtr->tree, entryPtr->node);
+    }
+    if (entryPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, entryPtr->gc);
+    }
+    if (entryPtr->shadow.color != NULL) {
+	Tk_FreeColor(entryPtr->shadow.color);
+    }
+    /* Delete the chain of data values from the entry. */
+    if (entryPtr->values != NULL) {
+	TreeViewValue *valuePtr, *nextPtr;
+	
+	for (valuePtr = entryPtr->values; valuePtr != NULL; 
+	     valuePtr = nextPtr) {
+	    nextPtr = valuePtr->nextPtr;
+	    Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+	}
+	entryPtr->values = NULL;
+    }
+    if (entryPtr->fullName != NULL) {
+	Blt_Free(entryPtr->fullName);
+    }
+    if (entryPtr->textPtr != NULL) {
+	Blt_Free(entryPtr->textPtr);
+    }
+    Blt_PoolFreeItem(tvPtr->entryPool, entryPtr);
+}
+
+TreeViewEntry *
+Blt_TreeViewParentEntry(TreeViewEntry *entryPtr)
+{
+    TreeView *tvPtr = entryPtr->tvPtr; 
+    Blt_TreeNode node;
+
+    if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
+	return NULL;
+    }
+    node = Blt_TreeNodeParent(entryPtr->node);
+    if (node == NULL) {
+	return NULL;
+    }
+    return Blt_NodeToEntry(tvPtr, node);
+}
+
+static void
+FreeEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    Blt_HashEntry *hPtr;
+
+    if (entryPtr == tvPtr->activePtr) {
+	tvPtr->activePtr = Blt_TreeViewParentEntry(entryPtr);
+    }
+    if (entryPtr == tvPtr->activeButtonPtr) {
+	tvPtr->activeButtonPtr = NULL;
+    }
+    if (entryPtr == tvPtr->focusPtr) {
+	tvPtr->focusPtr = Blt_TreeViewParentEntry(entryPtr);
+	Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
+    }
+    if (entryPtr == tvPtr->selAnchorPtr) {
+	tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+    }
+    Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+    Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+    Blt_DeleteBindings(tvPtr->bindTable, entryPtr);
+    hPtr = Blt_FindHashEntry(&tvPtr->entryTable, entryPtr->node);
+    if (hPtr != NULL) {
+	Blt_DeleteHashEntry(&tvPtr->entryTable, hPtr);
+    }
+    entryPtr->node = NULL;
+
+    Tcl_EventuallyFree(entryPtr, DestroyEntry);
+    /*
+     * Indicate that the screen layout of the hierarchy may have changed
+     * because this node was deleted.  The screen positions of the nodes
+     * in tvPtr->visibleArr are invalidated.
+     */
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+}
+
+int
+Blt_TreeViewEntryIsSelected(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+    return (hPtr != NULL);
+}
+
+void
+Blt_TreeViewSelectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    int isNew;
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->selectTable, (char *)entryPtr, &isNew);
+    if (isNew) {
+	Blt_ChainLink *linkPtr;
+
+	linkPtr = Blt_ChainAppend(tvPtr->selChainPtr, entryPtr);
+	Blt_SetHashValue(hPtr, linkPtr);
+    }
+}
+
+void
+Blt_TreeViewDeselectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+    if (hPtr != NULL) {
+	Blt_ChainLink *linkPtr;
+
+	linkPtr = Blt_GetHashValue(hPtr);
+	Blt_ChainDeleteLink(tvPtr->selChainPtr, linkPtr);
+	Blt_DeleteHashEntry(&tvPtr->selectTable, hPtr);
+    }
+}
+
+char *
+Blt_TreeViewGetFullName(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,
+    int checkEntryLabel,
+    Tcl_DString *resultPtr)
+{
+    Blt_TreeNode node;
+    char **names;		/* Used the stack the component names. */
+    char *staticSpace[64];
+    int level;
+    register int i;
+
+    level = Blt_TreeNodeDepth(tvPtr->tree, entryPtr->node);
+    if (tvPtr->rootPtr->labelUid == NULL) {
+	level--;
+    }
+    if (level > 64) {
+	names = Blt_Malloc((level + 2) * sizeof(char *));
+	assert(names);
+    } else {
+	names = staticSpace;
+    }
+    for (i = level; i >= 0; i--) {
+	/* Save the name of each ancestor in the name array. */
+	if (checkEntryLabel) {
+	    names[i] = GETLABEL(entryPtr);
+	} else {
+	    names[i] = Blt_TreeNodeLabel(entryPtr->node);
+	}
+	node = Blt_TreeNodeParent(entryPtr->node);
+	if (node != NULL) {
+	    entryPtr = Blt_NodeToEntry(tvPtr, node);
+	}
+    }
+    Tcl_DStringInit(resultPtr);
+    if (level >= 0) {
+	if ((tvPtr->pathSep == SEPARATOR_LIST) || 
+	    (tvPtr->pathSep == SEPARATOR_NONE)) {
+	    for (i = 0; i <= level; i++) {
+		Tcl_DStringAppendElement(resultPtr, names[i]);
+	    }
+	} else {
+	    Tcl_DStringAppend(resultPtr, names[0], -1);
+	    for (i = 1; i <= level; i++) {
+		Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
+		Tcl_DStringAppend(resultPtr, names[i], -1);
+	    }
+	}
+    } else {
+	if ((tvPtr->pathSep != SEPARATOR_LIST) &&
+	    (tvPtr->pathSep != SEPARATOR_NONE)) {
+	    Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
+	}
+    }
+    if (names != staticSpace) {
+	Blt_Free(names);
+    }
+    return Tcl_DStringValue(resultPtr);
+}
+
+
+int
+Blt_TreeViewCloseEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    char *cmd;
+
+    if (entryPtr->flags & ENTRY_CLOSED) {
+	return TCL_OK;		/* Entry is already closed. */
+    }
+    entryPtr->flags |= ENTRY_CLOSED;
+
+    /*
+     * Invoke the entry's "close" command, if there is one. Otherwise
+     * try the treeview's global "close" command.
+     */
+    cmd = CHOOSE(tvPtr->closeCmd, entryPtr->closeCmd);
+    if (cmd != NULL) {
+	Tcl_DString dString;
+	int result;
+
+	Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
+	Tcl_Preserve(entryPtr);
+	result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
+	Tcl_Release(entryPtr);
+	Tcl_DStringFree(&dString);
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    tvPtr->flags |= TV_LAYOUT;
+    return TCL_OK;
+}
+
+
+int
+Blt_TreeViewOpenEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    char *cmd;
+
+    if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
+	return TCL_OK;		/* Entry is already open. */
+    }
+    entryPtr->flags &= ~ENTRY_CLOSED;
+    /*
+     * If there's a "open" command proc specified for the entry, use
+     * that instead of the more general "open" proc for the entire 
+     * treeview.
+     */
+    cmd = CHOOSE(tvPtr->openCmd, entryPtr->openCmd);
+    if (cmd != NULL) {
+	Tcl_DString dString;
+	int result;
+
+	Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
+	Tcl_Preserve(entryPtr);
+	result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
+	Tcl_Release(entryPtr);
+	Tcl_DStringFree(&dString);
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    tvPtr->flags |= TV_LAYOUT;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewCreateEntry --
+ *
+ *	This procedure is called by the Tree object when a node is 
+ *	created and inserted into the tree.  It adds a new treeview 
+ *	entry field to the node.
+ *
+ * Results:
+ *	Returns the entry.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewCreateEntry(
+    TreeView *tvPtr,
+    Blt_TreeNode node,		/* Node that has just been created. */
+    int objc,
+    Tcl_Obj *CONST *objv,
+    int flags)
+{
+    TreeViewEntry *entryPtr;
+    int isNew;
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->entryTable, (char *)node, &isNew);
+    if (isNew) {
+	/* Create the entry structure */
+	entryPtr = Blt_PoolAllocItem(tvPtr->entryPool, sizeof(TreeViewEntry));
+	memset(entryPtr, 0, sizeof(TreeViewEntry));
+	entryPtr->flags = tvPtr->buttonFlags | ENTRY_CLOSED;
+	entryPtr->tvPtr = tvPtr;
+	entryPtr->labelUid = NULL;
+	entryPtr->node = node;
+	Blt_SetHashValue(hPtr, entryPtr);
+
+    } else {
+	entryPtr = Blt_GetHashValue(hPtr);
+    }
+    if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, objc, objv, flags) 
+	!= TCL_OK) {
+	FreeEntry(tvPtr, entryPtr);
+	return TCL_ERROR;	/* Error configuring the entry. */
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+CreateApplyProc(
+    Blt_TreeNode node,		/* Node that has just been created. */
+    ClientData clientData,
+    int order)			/* Not used. */
+{
+    TreeView *tvPtr = clientData; 
+    return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
+}
+
+/*ARGSUSED*/
+static int
+DeleteApplyProc(
+    Blt_TreeNode node,
+    ClientData clientData,
+    int order)			/* Not used. */
+{
+    TreeView *tvPtr = clientData;
+    /* 
+     * Unsetting the tree value triggers a call back to destroy the entry
+     * and also releases the Tcl_Obj that contains it. 
+     */
+    return Blt_TreeUnsetValueByKey(tvPtr->interp, tvPtr->tree, node, 
+	tvPtr->treeColumn.key);
+}
+
+static int
+TreeEventProc(ClientData clientData, Blt_TreeNotifyEvent *eventPtr)
+{
+    Blt_TreeNode node;
+    TreeView *tvPtr = clientData; 
+
+    node = Blt_TreeGetNode(eventPtr->tree, eventPtr->inode);
+    switch (eventPtr->type) {
+    case TREE_NOTIFY_CREATE:
+	return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
+    case TREE_NOTIFY_DELETE:
+	/*  
+	 * Deleting the tree node triggers a call back to free the
+	 * treeview entry that is associated with it.
+	 */
+	if (node != NULL) {
+	    FreeEntry(tvPtr, Blt_NodeToEntry(tvPtr, node));
+	}
+	break;
+    case TREE_NOTIFY_RELABEL:
+	if (node != NULL) {
+	    TreeViewEntry *entryPtr;
+
+	    entryPtr = Blt_NodeToEntry(tvPtr, node);
+	    entryPtr->flags |= ENTRY_DIRTY;
+	}
+	/*FALLTHRU*/
+    case TREE_NOTIFY_MOVE:
+    case TREE_NOTIFY_SORT:
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+	tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+	break;
+    default:
+	/* empty */
+	break;
+    }	
+    return TCL_OK;
+}
+
+TreeViewValue *
+Blt_TreeViewFindValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
+{
+    register TreeViewValue *valuePtr;
+
+    for (valuePtr = entryPtr->values; valuePtr != NULL; 
+	 valuePtr = valuePtr->nextPtr) {
+	if (valuePtr->columnPtr == columnPtr) {
+	    return valuePtr;
+	}
+    }
+    return NULL;
+}
+
+void
+Blt_TreeViewAddValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
+{
+    if (Blt_TreeViewFindValue(entryPtr, columnPtr) == NULL) {
+	Tcl_Obj *objPtr;
+
+	if (Blt_TreeViewGetData(entryPtr, columnPtr->key, &objPtr) == TCL_OK) {
+	    TreeViewValue *valuePtr;
+
+	    /* Add a new value only if a data entry exists. */
+	    valuePtr = Blt_PoolAllocItem(entryPtr->tvPtr->valuePool, 
+			 sizeof(TreeViewValue));
+	    valuePtr->columnPtr = columnPtr;
+	    valuePtr->nextPtr = entryPtr->values;
+	    valuePtr->textPtr = NULL;
+	    valuePtr->width = valuePtr->height = 0;
+	    valuePtr->stylePtr = NULL;
+	    valuePtr->string = NULL;
+	    entryPtr->values = valuePtr;
+	}
+    }
+    entryPtr->tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    entryPtr->flags |= ENTRY_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeTraceProc --
+ *
+ *	Mirrors the individual values of the tree object (they must
+ *	also be listed in the widget's columns chain). This is because
+ *	it must track and save the sizes of each individual data
+ *	entry, rather than re-computing all the sizes each time the
+ *	widget is redrawn.
+ *
+ *	This procedure is called by the Tree object when a node data
+ *	value is set unset.
+ *
+ * Results:
+ *	Returns TCL_OK.
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TreeTraceProc(
+    ClientData clientData,
+    Tcl_Interp *interp,
+    Blt_TreeNode node,		/* Node that has just been updated. */
+    Blt_TreeKey key,		/* Key of value that's been updated. */
+    unsigned int flags)
+{
+    Blt_HashEntry *hPtr;
+    TreeView *tvPtr = clientData; 
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr, *nextPtr, *lastPtr;
+    
+    hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
+    if (hPtr == NULL) {
+	return TCL_OK;		/* Not a node that we're interested in. */
+    }
+    entryPtr = Blt_GetHashValue(hPtr);
+    flags &= TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_UNSET;
+    switch (flags) {
+    case TREE_TRACE_WRITE:
+	hPtr = Blt_FindHashEntry(&tvPtr->columnTable, key);
+	if (hPtr == NULL) {
+	    return TCL_OK;	/* Data value isn't used by widget. */
+	}
+	columnPtr = Blt_GetHashValue(hPtr);
+	if (columnPtr != &tvPtr->treeColumn) {
+	    Blt_TreeViewAddValue(entryPtr, columnPtr);
+	}
+	entryPtr->flags |= ENTRY_DIRTY;
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+	tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+	break;
+
+    case TREE_TRACE_UNSET:
+	lastPtr = NULL;
+	for(valuePtr = entryPtr->values; valuePtr != NULL; 
+	    valuePtr = nextPtr) {
+	    nextPtr = valuePtr->nextPtr;
+	    if (valuePtr->columnPtr->key == key) { 
+		Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+		if (lastPtr == NULL) {
+		    entryPtr->values = nextPtr;
+		} else {
+		    lastPtr->nextPtr = nextPtr;
+		}
+		entryPtr->flags |= ENTRY_DIRTY;
+		Blt_TreeViewEventuallyRedraw(tvPtr);
+		tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+		break;
+	    }
+	    lastPtr = valuePtr;
+	}		
+	break;
+
+    default:
+	break;
+    }
+    return TCL_OK;
+}
+
+
+static void
+GetValueSize(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,
+    TreeViewValue *valuePtr,
+    TreeViewStyle *stylePtr)
+{
+    TreeViewColumn *columnPtr;
+
+    columnPtr = valuePtr->columnPtr;
+    valuePtr->width = valuePtr->height = 0;
+    if (entryPtr->flags & ENTRY_DIRTY) { /* Reparse the data. */
+	char *string;
+	TreeViewIcon icon;
+
+	Tcl_Obj *valueObjPtr;
+	TreeViewStyle *newStylePtr;
+    
+	icon = NULL;
+	newStylePtr = NULL;
+	if (Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key, 
+		&valueObjPtr) != TCL_OK) {
+	    return;			/* No data ??? */
+	}
+	string = Tcl_GetString(valueObjPtr);
+	valuePtr->string = string;
+	if (string[0] == '@') {	/* Name of style or Tk image. */
+	    int objc;
+	    Tcl_Obj **objv;
+	    
+	    if ((Tcl_ListObjGetElements(tvPtr->interp, valueObjPtr, &objc, 
+		&objv) != TCL_OK) || (objc < 1) || (objc > 2)) {
+		goto handleString;
+	    }
+	    if (objc > 0) {
+		char *name;
+		
+		name = Tcl_GetString(objv[0]) + 1;
+		if (Blt_TreeViewGetStyle((Tcl_Interp *)NULL, tvPtr, name, 
+					 &newStylePtr) != TCL_OK) {
+		    icon = Blt_TreeViewGetIcon(tvPtr, name);
+		    if (icon == NULL) {
+			goto handleString;
+		    }
+		    /* Create a new style by the name of the image. */
+		    newStylePtr = Blt_TreeViewCreateStyle((Tcl_Interp *)NULL, 
+			tvPtr, STYLE_TEXTBOX, name);
+		    assert(newStylePtr);
+		    Blt_TreeViewUpdateStyleGCs(tvPtr, newStylePtr);
+		}
+		
+	    }
+	    if (valuePtr->stylePtr != NULL) {
+		Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
+	    }
+	    if (icon != NULL) {
+		Blt_TreeViewSetStyleIcon(tvPtr, newStylePtr, icon);
+	    }
+	    valuePtr->stylePtr = newStylePtr;
+	    valuePtr->string = (objc > 1) ? Tcl_GetString(objv[1]) : NULL;
+	}
+    }
+ handleString:
+    stylePtr = CHOOSE(columnPtr->stylePtr, valuePtr->stylePtr);
+    /* Measure the text string. */
+    (*stylePtr->classPtr->measProc)(tvPtr, stylePtr, valuePtr);
+}
+
+static void
+GetRowExtents(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,
+    int *widthPtr, 
+    int *heightPtr)
+{
+    TreeViewValue *valuePtr;
+    int valueWidth;		/* Width of individual value.  */
+    int width, height;		/* Compute dimensions of row. */
+    TreeViewStyle *stylePtr;
+
+    width = height = 0;
+    for (valuePtr = entryPtr->values; valuePtr != NULL; 
+	valuePtr = valuePtr->nextPtr) {
+	stylePtr = valuePtr->stylePtr;
+	if (stylePtr == NULL) {
+	    stylePtr = valuePtr->columnPtr->stylePtr;
+	}
+	if ((entryPtr->flags & ENTRY_DIRTY) || 
+	    (stylePtr->flags & STYLE_DIRTY)) {
+	    GetValueSize(tvPtr, entryPtr, valuePtr, stylePtr);
+	}
+	if (valuePtr->height > height) {
+	    height = valuePtr->height;
+	}
+	valueWidth = valuePtr->width;
+	width += valueWidth;
+    }	    
+    *widthPtr = width;
+    *heightPtr = height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewNearestEntry --
+ *
+ *	Finds the entry closest to the given screen X-Y coordinates
+ *	in the viewport.
+ *
+ * Results:
+ *	Returns the pointer to the closest node.  If no node is
+ *	visible (nodes may be hidden), NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+TreeViewEntry *
+Blt_TreeViewNearestEntry(TreeView *tvPtr, int x, int y, int selectOne)
+{
+    TreeViewEntry *lastPtr, *entryPtr;
+    register TreeViewEntry **p;
+
+    /*
+     * We implicitly can pick only visible entries.  So make sure that
+     * the tree exists.
+     */
+    if (tvPtr->nVisible == 0) {
+	return NULL;
+    }
+    if (y < tvPtr->titleHeight) {
+	return (selectOne) ? tvPtr->visibleArr[0] : NULL;
+    }
+    /*
+     * Since the entry positions were previously computed in world
+     * coordinates, convert Y-coordinate from screen to world
+     * coordinates too.
+     */
+    y = WORLDY(tvPtr, y);
+    lastPtr = tvPtr->visibleArr[0];
+    for (p = tvPtr->visibleArr; *p != NULL; p++) {
+	entryPtr = *p;
+	/*
+	 * If the start of the next entry starts beyond the point,
+	 * use the last entry.
+	 */
+	if (entryPtr->worldY > y) {
+	    return (selectOne) ? entryPtr : NULL;
+	}
+	if (y < (entryPtr->worldY + entryPtr->height)) {
+	    return entryPtr;	/* Found it. */
+	}
+	lastPtr = entryPtr;
+    }
+    return (selectOne) ? lastPtr : NULL;
+}
+
+
+ClientData
+Blt_TreeViewEntryTag(TreeView *tvPtr, CONST char *string)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;			/* Not used. */
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->entryTagTable, string, &isNew);
+    return Blt_GetHashKey(&tvPtr->entryTagTable, hPtr);
+}
+
+ClientData
+Blt_TreeViewButtonTag(TreeView *tvPtr, CONST char *string)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;			/* Not used. */
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->buttonTagTable, string, &isNew);
+    return Blt_GetHashKey(&tvPtr->buttonTagTable, hPtr);
+}
+
+ClientData
+Blt_TreeViewColumnTag(TreeView *tvPtr, CONST char *string)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;			/* Not used. */
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->columnTagTable, string, &isNew);
+    return Blt_GetHashKey(&tvPtr->columnTagTable, hPtr);
+}
+
+ClientData
+Blt_TreeViewStyleTag(TreeView *tvPtr, CONST char *string)
+{
+    Blt_HashEntry *hPtr;
+    int isNew;			/* Not used. */
+
+    hPtr = Blt_CreateHashEntry(&tvPtr->styleTagTable, string, &isNew);
+    return Blt_GetHashKey(&tvPtr->styleTagTable, hPtr);
+}
+
+static void
+GetTags(
+    Blt_BindTable table,
+    ClientData object,		/* Object picked. */
+    ClientData context,		/* Context of object. */
+    Blt_List ids)		/* (out) List of binding ids to be
+				 * applied for this object. */
+{
+    TreeView *tvPtr;
+    int nNames;
+    char **names;
+    register char **p;
+
+    tvPtr = Blt_GetBindingData(table);
+    if (context == (ClientData)ITEM_ENTRY_BUTTON) {
+	TreeViewEntry *entryPtr = object;
+
+	Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Button"), 0);
+	if (entryPtr->tagsUid != NULL) {
+	    if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
+			      &names) == TCL_OK) {
+		for (p = names; *p != NULL; p++) {
+		    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, *p), 0);
+		}
+		Blt_Free(names);
+	    }
+	} else {
+	    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Entry"), 0);
+	    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "all"), 0);
+	}
+    } else if (context == (ClientData)ITEM_COLUMN_TITLE) {
+	TreeViewColumn *columnPtr = object;
+
+	Blt_ListAppend(ids, (char *)columnPtr, 0);
+	if (columnPtr->tagsUid != NULL) {
+	    if (Tcl_SplitList((Tcl_Interp *)NULL, columnPtr->tagsUid, &nNames,
+		      &names) == TCL_OK) {
+		for (p = names; *p != NULL; p++) {
+		    Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, *p), 0);
+		}
+		Blt_Free(names);
+	    }
+	}
+    } else if (context == ITEM_COLUMN_RULE) {
+	Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, "Rule"), 0);
+    } else {
+	TreeViewEntry *entryPtr = object;
+
+	Blt_ListAppend(ids, (char *)entryPtr, 0);
+	if (entryPtr->tagsUid != NULL) {
+	    if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
+		      &names) == TCL_OK) {
+		for (p = names; *p != NULL; p++) {
+		    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, *p), 0);
+		}
+		Blt_Free(names);
+	    }
+	} else if (context == ITEM_ENTRY){
+	    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
+	    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
+	} else {
+	    TreeViewValue *valuePtr = context;
+
+	    if (valuePtr != NULL) {
+		TreeViewStyle *stylePtr = valuePtr->stylePtr;
+
+		if (stylePtr == NULL) {
+		    stylePtr = valuePtr->columnPtr->stylePtr;
+		}
+		Blt_ListAppend(ids, 
+	            Blt_TreeViewEntryTag(tvPtr, stylePtr->name), 0);
+		Blt_ListAppend(ids, 
+		    Blt_TreeViewEntryTag(tvPtr, valuePtr->columnPtr->key), 0);
+		Blt_ListAppend(ids, 
+		    Blt_TreeViewEntryTag(tvPtr, stylePtr->classPtr->className),
+		    0);
+#ifndef notdef
+		Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
+		Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
+#endif
+	    }
+	}
+    }
+}
+
+/*ARGSUSED*/
+static ClientData
+PickItem(
+    ClientData clientData,
+    int x, 
+    int y,			/* Screen coordinates of the test point. */
+    ClientData *contextPtr)	/* (out) Context of item selected: should
+				 * be ITEM_ENTRY, ITEM_ENTRY_BUTTON,
+				 * ITEM_COLUMN_TITLE,
+				 * ITEM_COLUMN_RULE, or
+				 * ITEM_STYLE. */
+{
+    TreeView *tvPtr = clientData;
+    TreeViewEntry *entryPtr;
+    TreeViewColumn *columnPtr;
+
+    if (contextPtr != NULL) {
+	*contextPtr = NULL;
+    }
+    if (tvPtr->flags & TV_DIRTY) {
+	/* Can't trust the selected entry if nodes have been added or
+	 * deleted. So recompute the layout. */
+	if (tvPtr->flags & TV_LAYOUT) {
+	    Blt_TreeViewComputeLayout(tvPtr);
+	} 
+	ComputeVisibleEntries(tvPtr);
+    }
+    columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, contextPtr);
+    if ((*contextPtr != NULL) && (tvPtr->flags & TV_SHOW_COLUMN_TITLES)) {
+	return columnPtr;
+    }
+    if (tvPtr->nVisible == 0) {
+	return NULL;
+    }
+    entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
+    if (entryPtr == NULL) {
+	return NULL;
+    }
+    x = WORLDX(tvPtr, x);
+    y = WORLDY(tvPtr, y);
+    if (contextPtr != NULL) {
+	*contextPtr = ITEM_ENTRY;
+	if (columnPtr != NULL) {
+	    TreeViewValue *valuePtr;
+
+	    valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
+	    if (valuePtr != NULL) {
+		TreeViewStyle *stylePtr;
+		
+		stylePtr = valuePtr->stylePtr;
+		if (stylePtr == NULL) {
+		    stylePtr = valuePtr->columnPtr->stylePtr;
+		}
+		if ((stylePtr->classPtr->pickProc == NULL) ||
+		    ((*stylePtr->classPtr->pickProc)(entryPtr, valuePtr, 
+			stylePtr, x, y))) {
+		    *contextPtr = valuePtr;
+		} 
+	    }
+	}
+	if (entryPtr->flags & ENTRY_HAS_BUTTON) {
+	    TreeViewButton *buttonPtr = &tvPtr->button;
+	    int left, right, top, bottom;
+	    
+	    left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
+	    right = left + buttonPtr->width + 2 * BUTTON_PAD;
+	    top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
+	    bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
+	    if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
+		*contextPtr = (ClientData)ITEM_ENTRY_BUTTON;
+	    }
+	}
+    }
+    return entryPtr;
+}
+
+static void
+GetEntryExtents(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    Tk_Font font;
+    TreeViewIcon *icons;
+    char *label;
+    int entryWidth, entryHeight;
+    int width, height;
+
+    /*
+     * FIXME: Use of DIRTY flag inconsistent.  When does it
+     *	      mean "dirty entry"? When does it mean "dirty column"?
+     *	      Does it matter? probably
+     */
+    if ((entryPtr->flags & ENTRY_DIRTY) || (tvPtr->flags & TV_UPDATE)) {
+	Tk_FontMetrics fontMetrics;
+
+	entryPtr->iconWidth = entryPtr->iconHeight = 0;
+	icons = CHOOSE(tvPtr->icons, entryPtr->icons);
+	if (icons != NULL) {
+	    register int i;
+	    
+	    for (i = 0; i < 2; i++) {
+		if (icons[i] == NULL) {
+		    break;
+		}
+		if (entryPtr->iconWidth < TreeViewIconWidth(icons[i])) {
+		    entryPtr->iconWidth = TreeViewIconWidth(icons[i]);
+		}
+		if (entryPtr->iconHeight < TreeViewIconHeight(icons[i])) {
+		    entryPtr->iconHeight = TreeViewIconHeight(icons[i]);
+		}
+	    }
+	}
+	if ((icons == NULL) || (icons[0] == NULL)) {
+	    entryPtr->iconWidth = DEF_ICON_WIDTH;
+	    entryPtr->iconHeight = DEF_ICON_HEIGHT;
+	}
+	entryPtr->iconWidth += 2 * ICON_PADX;
+	entryPtr->iconHeight += 2 * ICON_PADY;
+	entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+	font = entryPtr->font;
+	if (font == NULL) {
+	    font = Blt_TreeViewGetStyleFont(tvPtr, tvPtr->treeColumn.stylePtr);
+	}
+	if (entryPtr->fullName != NULL) {
+	    Blt_Free(entryPtr->fullName);
+	    entryPtr->fullName = NULL;
+	}
+	if (entryPtr->textPtr != NULL) {
+	    Blt_Free(entryPtr->textPtr);
+	    entryPtr->textPtr = NULL;
+	}
+	
+	Tk_GetFontMetrics(font, &fontMetrics);
+	entryPtr->lineHeight = fontMetrics.linespace;
+	entryPtr->lineHeight += 2 * (FOCUS_WIDTH + LABEL_PADY + 
+				    tvPtr->selBorderWidth) + tvPtr->leader;
+	label = GETLABEL(entryPtr);
+	if (label[0] == '\0') {
+	    width = height = entryPtr->lineHeight;
+	} else {
+	    TextStyle ts;
+
+	    Blt_InitTextStyle(&ts);
+	    ts.shadow.offset = entryPtr->shadow.offset;
+	    ts.font = font;
+	    
+	    if (tvPtr->flatView) {
+		Tcl_DString dString;
+
+		Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+		entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+		Tcl_DStringFree(&dString);
+		entryPtr->textPtr = Blt_GetTextLayout(entryPtr->fullName, &ts);
+	    } else {
+		entryPtr->textPtr = Blt_GetTextLayout(label, &ts);
+	    }
+	    width = entryPtr->textPtr->width;
+	    height = entryPtr->textPtr->height;
+	}
+	width += 2 * (FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth);
+	height += 2 * (FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth);
+	width = ODD(width);
+	if (entryPtr->reqHeight > height) {
+	    height = entryPtr->reqHeight;
+	} 
+	height = ODD(height);
+	entryWidth = width;
+	if (entryHeight < height) {
+	    entryHeight = height;
+	}
+	entryPtr->labelWidth = width;
+	entryPtr->labelHeight = height;
+    } else {
+	entryHeight = entryPtr->labelHeight;
+	entryWidth = entryPtr->labelWidth;
+    }
+    /*  
+     * Find the maximum height of the data value entries. This also has
+     * the side effect of contributing the maximum width of the column. 
+     */
+    GetRowExtents(tvPtr, entryPtr, &width, &height);
+    if (entryHeight < height) {
+	entryHeight = height;
+    }
+    entryPtr->width = entryWidth + COLUMN_PAD;
+    entryPtr->height = entryHeight + tvPtr->leader;
+    /*
+     * Force the height of the entry to an even number. This is to
+     * make the dots or the vertical line segments coincide with the
+     * start of the horizontal lines.
+     */
+    if (entryPtr->height & 0x01) {
+	entryPtr->height++;
+    }
+    entryPtr->flags &= ~ENTRY_DIRTY;
+}
+
+/*
+ * TreeView Procedures
+ */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * CreateTreeView --
+ *
+ * ----------------------------------------------------------------------
+ */
+static TreeView *
+CreateTreeView(
+    Tcl_Interp *interp,
+    Tcl_Obj *objPtr,		/* Name of the new widget. */
+    CONST char *className)
+{
+    Tk_Window tkwin;
+    TreeView *tvPtr;
+    char *name;
+    Tcl_DString dString;
+    int result;
+
+    name = Tcl_GetString(objPtr);
+    tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), name,
+	(char *)NULL);
+    if (tkwin == NULL) {
+	return NULL;
+    }
+    Tk_SetClass(tkwin, (char *)className);
+
+    tvPtr = Blt_Calloc(1, sizeof(TreeView));
+    assert(tvPtr);
+    tvPtr->tkwin = tkwin;
+    tvPtr->display = Tk_Display(tkwin);
+    tvPtr->interp = interp;
+    tvPtr->flags = (TV_HIDE_ROOT | TV_SHOW_COLUMN_TITLES | 
+		    TV_DIRTY | TV_LAYOUT | TV_RESORT);
+    tvPtr->leader = 0;
+    tvPtr->dashes = 1;
+    tvPtr->highlightWidth = 2;
+    tvPtr->selBorderWidth = 1;
+    tvPtr->borderWidth = 2;
+    tvPtr->relief = TK_RELIEF_SUNKEN;
+    tvPtr->selRelief = TK_RELIEF_FLAT;
+    tvPtr->scrollMode = BLT_SCROLL_MODE_HIERBOX;
+    tvPtr->selectMode = SELECT_MODE_SINGLE;
+    tvPtr->button.closeRelief = tvPtr->button.openRelief = TK_RELIEF_SOLID;
+    tvPtr->reqWidth = 200;
+    tvPtr->reqHeight = 400;
+    tvPtr->xScrollUnits = tvPtr->yScrollUnits = 20;
+    tvPtr->lineWidth = 1;
+    tvPtr->button.borderWidth = 1;
+    tvPtr->colChainPtr = Blt_ChainCreate();
+    tvPtr->buttonFlags = BUTTON_AUTO;
+    tvPtr->selChainPtr = Blt_ChainCreate();
+    Blt_InitHashTableWithPool(&tvPtr->entryTable, BLT_ONE_WORD_KEYS);
+    Blt_InitHashTable(&tvPtr->columnTable, BLT_ONE_WORD_KEYS);
+    Blt_InitHashTable(&tvPtr->iconTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
+    Blt_InitHashTable(&tvPtr->uidTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&tvPtr->styleTable, BLT_STRING_KEYS);
+    tvPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr, PickItem, 
+	GetTags);
+    Blt_InitHashTable(&tvPtr->entryTagTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&tvPtr->columnTagTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&tvPtr->buttonTagTable, BLT_STRING_KEYS);
+    Blt_InitHashTable(&tvPtr->styleTagTable, BLT_STRING_KEYS);
+
+    tvPtr->entryPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+    tvPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+#if (TK_MAJOR_VERSION > 4)
+    Blt_SetWindowInstanceData(tkwin, tvPtr);
+#endif
+    tvPtr->cmdToken = Tcl_CreateObjCommand(interp, Tk_PathName(tvPtr->tkwin), 
+	Blt_TreeViewWidgetInstCmd, tvPtr, WidgetInstCmdDeleteProc);
+
+#ifdef ITCL_NAMESPACES
+    Itk_SetWidgetCommand(tvPtr->tkwin, tvPtr->cmdToken);
+#endif
+    Tk_CreateSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING, SelectionProc,
+	tvPtr, XA_STRING);
+    Tk_CreateEventHandler(tvPtr->tkwin, ExposureMask | StructureNotifyMask |
+	FocusChangeMask, TreeViewEventProc, tvPtr);
+    /* 
+     * Create a default style. This must exist before we can create
+     * the treeview column. 
+     */  
+    tvPtr->stylePtr = Blt_TreeViewCreateStyle(interp, tvPtr, STYLE_TEXTBOX,
+	"text");
+    if (tvPtr->stylePtr == NULL) {
+	return NULL;
+    }
+    /* Create a default column to display the view of the tree. */
+    Tcl_DStringInit(&dString);
+    Tcl_DStringAppend(&dString, "BLT TreeView ", -1);
+    Tcl_DStringAppend(&dString, Tk_PathName(tvPtr->tkwin), -1);
+    result = Blt_TreeViewCreateColumn(tvPtr, &tvPtr->treeColumn, 
+				      Tcl_DStringValue(&dString), "");
+    Tcl_DStringFree(&dString);
+    if (result != TCL_OK) {
+	return NULL;
+    }
+    Blt_ChainAppend(tvPtr->colChainPtr, &tvPtr->treeColumn);
+    return tvPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DestroyTreeView --
+ *
+ * 	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ *	to clean up the internal structure of a TreeView at a safe time
+ *	(when no-one is using it anymore).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Everything associated with the widget is freed up.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DestroyTreeView(DestroyData dataPtr)	/* Pointer to the widget record. */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    TreeView *tvPtr = (TreeView *)dataPtr;
+    TreeViewButton *buttonPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewStyle *stylePtr;
+
+    Blt_TreeDeleteEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc, 
+	   tvPtr);
+    for (hPtr = Blt_FirstHashEntry(&tvPtr->entryTable, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	entryPtr = Blt_GetHashValue(hPtr);
+	DestroyEntry((ClientData)entryPtr);
+    }
+    bltTreeViewTreeOption.clientData = tvPtr;
+    bltTreeViewIconsOption.clientData = tvPtr;
+    Blt_FreeObjOptions(bltTreeViewSpecs, (char *)tvPtr, tvPtr->display, 0);
+    if (tvPtr->tkwin != NULL) {
+	Tk_DeleteSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING);
+    }
+    if (tvPtr->lineGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
+    }
+    if (tvPtr->focusGC != NULL) {
+	Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
+    }
+    if (tvPtr->visibleArr != NULL) {
+	Blt_Free(tvPtr->visibleArr);
+    }
+    if (tvPtr->flatArr != NULL) {
+	Blt_Free(tvPtr->flatArr);
+    }
+    if (tvPtr->levelInfo != NULL) {
+	Blt_Free(tvPtr->levelInfo);
+    }
+    buttonPtr = &tvPtr->button;
+    if (buttonPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
+    }
+    if (buttonPtr->normalGC != NULL) {
+	Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
+    }
+    if (tvPtr->stylePtr != NULL) {
+	Blt_TreeViewFreeStyle(tvPtr, tvPtr->stylePtr);
+    }
+
+    Blt_TreeViewDestroyColumns(tvPtr);
+    Blt_DestroyBindingTable(tvPtr->bindTable);
+    Blt_ChainDestroy(tvPtr->selChainPtr);
+    Blt_DeleteHashTable(&tvPtr->entryTagTable);
+    Blt_DeleteHashTable(&tvPtr->columnTagTable);
+    Blt_DeleteHashTable(&tvPtr->buttonTagTable);
+    Blt_DeleteHashTable(&tvPtr->styleTagTable);
+
+    for (hPtr = Blt_FirstHashEntry(&tvPtr->styleTable, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	stylePtr = Blt_GetHashValue(hPtr);
+	/* stylePtr->refCount = 0; */
+	stylePtr->flags &= ~STYLE_USER;
+	Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+    }
+    if (tvPtr->comboWin != NULL) {
+	Tk_DestroyWindow(tvPtr->comboWin);
+    }
+    Blt_DeleteHashTable(&tvPtr->styleTable);
+    Blt_DeleteHashTable(&tvPtr->selectTable);
+    Blt_DeleteHashTable(&tvPtr->uidTable);
+    Blt_DeleteHashTable(&tvPtr->entryTable);
+
+    Blt_PoolDestroy(tvPtr->entryPool);
+    Blt_PoolDestroy(tvPtr->valuePool);
+    DumpIconTable(tvPtr);
+    Blt_Free(tvPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeViewEventProc --
+ *
+ * 	This procedure is invoked by the Tk dispatcher for various
+ * 	events on treeview widgets.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the window gets deleted, internal structures get
+ *	cleaned up.  When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TreeViewEventProc(
+    ClientData clientData,	/* Information about window. */
+    XEvent *eventPtr)		/* Information about event. */
+{
+    TreeView *tvPtr = clientData;
+
+    if (eventPtr->type == Expose) {
+	if (eventPtr->xexpose.count == 0) {
+	    Blt_TreeViewEventuallyRedraw(tvPtr);
+	    Blt_PickCurrentItem(tvPtr->bindTable);
+	}
+    } else if (eventPtr->type == ConfigureNotify) {
+	tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+    } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+	if (eventPtr->xfocus.detail != NotifyInferior) {
+	    if (eventPtr->type == FocusIn) {
+		tvPtr->flags |= TV_FOCUS;
+	    } else {
+		tvPtr->flags &= ~TV_FOCUS;
+	    }
+	    Blt_TreeViewEventuallyRedraw(tvPtr);
+	}
+    } else if (eventPtr->type == DestroyNotify) {
+	if (tvPtr->tkwin != NULL) {
+	    tvPtr->tkwin = NULL;
+	    Tcl_DeleteCommandFromToken(tvPtr->interp, tvPtr->cmdToken);
+	}
+	if (tvPtr->flags & TV_REDRAW) {
+	    Tcl_CancelIdleCall(DisplayTreeView, tvPtr);
+	}
+	if (tvPtr->flags & TV_SELECT_PENDING) {
+	    Tcl_CancelIdleCall(Blt_TreeViewSelectCmdProc, tvPtr);
+	}
+	Tcl_EventuallyFree(tvPtr, DestroyTreeView);
+    }
+}
+
+/* Selection Procedures */
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionProc --
+ *
+ *	This procedure is called back by Tk when the selection is
+ *	requested by someone.  It returns part or all of the selection
+ *	in a buffer provided by the caller.
+ *
+ * Results:
+ *	The return value is the number of non-NULL bytes stored at
+ *	buffer.  Buffer is filled (or partially filled) with a
+ *	NUL-terminated string containing part or all of the
+ *	selection, as given by offset and maxBytes.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectionProc(
+    ClientData clientData,	/* Information about the widget. */
+    int offset,			/* Offset within selection of first
+				 * character to be returned. */
+    char *buffer,		/* Location in which to place
+				 * selection. */
+    int maxBytes)		/* Maximum number of bytes to place
+				 * at buffer, not including terminating
+				 * NULL character. */
+{
+    Tcl_DString dString;
+    TreeView *tvPtr = clientData;
+    TreeViewEntry *entryPtr;
+    int size;
+
+    if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
+	return -1;
+    }
+    /*
+     * Retrieve the names of the selected entries.
+     */
+    Tcl_DStringInit(&dString);
+    if (tvPtr->flags & TV_SELECT_SORTED) {
+	Blt_ChainLink *linkPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
+	    Tcl_DStringAppend(&dString, "\n", -1);
+	}
+    } else {
+	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+	    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+		Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
+		Tcl_DStringAppend(&dString, "\n", -1);
+	    }
+	}
+    }
+    size = Tcl_DStringLength(&dString) - offset;
+    strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
+    Tcl_DStringFree(&dString);
+    buffer[maxBytes] = '\0';
+    return (size > maxBytes) ? maxBytes : size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WidgetInstCmdDeleteProc --
+ *
+ *	This procedure is invoked when a widget command is deleted.  If
+ *	the widget isn't already in the process of being destroyed,
+ *	this command destroys it.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+WidgetInstCmdDeleteProc(ClientData clientData)
+{
+    TreeView *tvPtr = clientData;
+
+    /*
+     * This procedure could be invoked either because the window was
+     * destroyed and the command was then deleted (in which case tkwin
+     * is NULL) or because the command was deleted, and then this
+     * procedure destroys the widget.
+     */
+    if (tvPtr->tkwin != NULL) {
+	Tk_Window tkwin;
+
+	tkwin = tvPtr->tkwin;
+	tvPtr->tkwin = NULL;
+	Tk_DestroyWindow(tkwin);
+#ifdef ITCL_NAMESPACES
+	Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
+#endif /* ITCL_NAMESPACES */
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_TreeViewUpdateWidget --
+ *
+ *	Updates the GCs and other information associated with the
+ *	treeview widget.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.  If TCL_ERROR is
+ * 	returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewUpdateWidget(Tcl_Interp *interp, TreeView *tvPtr)	
+{
+    GC newGC;
+    XGCValues gcValues;
+    int setupTree;
+    unsigned long gcMask;
+
+    /*
+     * GC for dotted vertical line.
+     */
+    gcMask = (GCForeground | GCLineWidth);
+    gcValues.foreground = tvPtr->lineColor->pixel;
+    gcValues.line_width = tvPtr->lineWidth;
+    if (tvPtr->dashes > 0) {
+	gcMask |= (GCLineStyle | GCDashList);
+	gcValues.line_style = LineOnOffDash;
+	gcValues.dashes = tvPtr->dashes;
+    }
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (tvPtr->lineGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
+    }
+    tvPtr->lineGC = newGC;
+
+    /*
+     * GC for active label. Dashed outline.
+     */
+    gcMask = GCForeground | GCLineStyle;
+    gcValues.foreground = tvPtr->focusColor->pixel;
+    gcValues.line_style = (LineIsDashed(tvPtr->focusDashes))
+	? LineOnOffDash : LineSolid;
+    newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (LineIsDashed(tvPtr->focusDashes)) {
+	tvPtr->focusDashes.offset = 2;
+	Blt_SetDashes(tvPtr->display, newGC, &tvPtr->focusDashes);
+    }
+    if (tvPtr->focusGC != NULL) {
+	Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
+    }
+    tvPtr->focusGC = newGC;
+
+    Blt_TreeViewConfigureButtons(tvPtr);
+    tvPtr->inset = tvPtr->highlightWidth + tvPtr->borderWidth + INSET_PAD;
+
+    setupTree = FALSE;
+
+    /*
+     * If no tree object was named, allocate a new one.  The name will
+     * be the same as the widget pathname.
+     */
+    if (tvPtr->tree == NULL) {
+	Blt_Tree token;
+	char *string;
+
+	string = Tk_PathName(tvPtr->tkwin);
+	if (Blt_TreeCreate(interp, string, &token) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	tvPtr->tree = token;
+	setupTree = TRUE;
+    } 
+
+    /*
+     * If the tree object was changed, we need to setup the new one.
+     */
+    if (Blt_ObjConfigModified(bltTreeViewSpecs, "-tree", (char *)NULL)) {
+	setupTree = TRUE;
+    }
+
+    /*
+     * These options change the layout of the box.  Mark the widget for update.
+     */
+    if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font", 
+	"-linespacing", "-*width", "-height", "-hide*", "-tree", "-flat", 
+	(char *)NULL)) {
+	tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+    }
+    /*
+     * If the tree view was changed, mark all the nodes dirty (we'll
+     * be switching back to either the full path name or the label)
+     * and free the array representing the flattened view of the tree.
+     */
+    if (Blt_ObjConfigModified(bltTreeViewSpecs, "-hideleaves", "-flat",
+			      (char *)NULL)) {
+	TreeViewEntry *entryPtr;
+	
+	tvPtr->flags |= (TV_DIRTY | TV_RESORT);
+	/* Mark all entries dirty. */
+	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
+	    entryPtr->flags |= ENTRY_DIRTY;
+	}
+	if ((!tvPtr->flatView) && (tvPtr->flatArr != NULL)) {
+	    Blt_Free(tvPtr->flatArr);
+	    tvPtr->flatArr = NULL;
+	}
+    }
+    if ((tvPtr->reqHeight != Tk_ReqHeight(tvPtr->tkwin)) ||
+	(tvPtr->reqWidth != Tk_ReqWidth(tvPtr->tkwin))) {
+	Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
+    }
+
+    if (setupTree) {
+	Blt_TreeNode root;
+
+	Blt_TreeCreateEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc, 
+		   tvPtr);
+	TraceColumns(tvPtr);
+	root = Blt_TreeRootNode(tvPtr->tree);
+
+	/* Automatically add view-entry values to the new tree. */
+	Blt_TreeApply(root, CreateApplyProc, tvPtr);
+	tvPtr->focusPtr = tvPtr->rootPtr = Blt_NodeToEntry(tvPtr, root);
+	tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+	Blt_SetFocusItem(tvPtr->bindTable, tvPtr->rootPtr, ITEM_ENTRY);
+
+	/* Automatically open the root node. */
+	if (Blt_TreeViewOpenEntry(tvPtr, tvPtr->rootPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (!(tvPtr->flags & TV_NEW_TAGS)) {
+	    Blt_Tree tree;
+
+	    if (Blt_TreeCmdGetToken(interp, Blt_TreeName(tvPtr->tree), 
+			&tree) == TCL_OK) {
+		Blt_TreeShareTagTable(tree, tvPtr->tree);
+	    }
+	}
+    }
+
+    if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font", "-color", 
+	(char *)NULL)) {
+	Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetCoordinates --
+ *
+ *	Determines the maximum height of all visible entries.
+ *
+ *	1. Sets the worldY coordinate for all mapped/open entries.
+ *	2. Determines if entry needs a button.
+ *	3. Collects the minimum height of open/mapped entries. (Do for all
+ *	   entries upon insert).
+ *	4. Figures out horizontal extent of each entry (will be width of 
+ *	   tree view column).
+ *	5. Collects maximum icon size for each level.
+ *	6. The height of its vertical line
+ *
+ * Results:
+ *	Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ *	The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetCoordinates(
+    TreeView *tvPtr,
+    TreeViewEntry *entryPtr,
+    int *yPtr)
+{
+    int depth;
+
+    entryPtr->worldY = -1;
+    entryPtr->vertLineLength = -1;
+    if ((entryPtr != tvPtr->rootPtr) && 
+	(Blt_TreeViewEntryIsHidden(entryPtr))) {
+	return;     /* If the entry is hidden, then do nothing. */
+    }
+    entryPtr->worldY = *yPtr;
+    entryPtr->vertLineLength = -(*yPtr);
+    *yPtr += entryPtr->height;
+
+    depth = DEPTH(tvPtr, entryPtr->node) + 1;
+    if (tvPtr->levelInfo[depth].labelWidth < entryPtr->labelWidth) {
+	tvPtr->levelInfo[depth].labelWidth = entryPtr->labelWidth;
+    }
+    if (tvPtr->levelInfo[depth].iconWidth < entryPtr->iconWidth) {
+	tvPtr->levelInfo[depth].iconWidth = entryPtr->iconWidth;
+    }
+    tvPtr->levelInfo[depth].iconWidth |= 0x01;
+
+    if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
+	TreeViewEntry *bottomPtr, *childPtr;
+
+	bottomPtr = entryPtr;
+	for (childPtr = Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN); 
+	     childPtr != NULL; 
+	     childPtr = Blt_TreeViewNextSibling(childPtr, ENTRY_HIDDEN)){
+	    ResetCoordinates(tvPtr, childPtr, yPtr);
+	    bottomPtr = childPtr;
+	}
+	entryPtr->vertLineLength += bottomPtr->worldY;
+    }
+}
+
+static void
+AdjustColumns(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    double weight;
+    int nOpen;
+    int size, avail, ration, growth;
+
+    growth = VPORTWIDTH(tvPtr) - tvPtr->worldWidth;
+    nOpen = 0;
+    weight = 0.0;
+    /* Find out how many columns still have space available */
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	if ((columnPtr->hidden) || 
+	    (columnPtr->weight == 0.0) || 
+	    (columnPtr->width >= columnPtr->max) || 
+	    (columnPtr->reqWidth > 0)) {
+	    continue;
+	}
+	nOpen++;
+	weight += columnPtr->weight;
+    }
+
+    while ((nOpen > 0) && (weight > 0.0) && (growth > 0)) {
+	ration = (int)(growth / weight);
+	if (ration == 0) {
+	    ration = 1;
+	}
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    if ((columnPtr->hidden) || 
+		(columnPtr->weight == 0.0) ||
+		(columnPtr->width >= columnPtr->max) || 
+		(columnPtr->reqWidth > 0)) {
+		continue;
+	    }
+	    size = (int)(ration * columnPtr->weight);
+	    if (size > growth) {
+		size = growth; 
+	    }
+	    avail = columnPtr->max - columnPtr->width;
+	    if (size > avail) {
+		size = avail;
+		nOpen--;
+		weight -= columnPtr->weight;
+	    }
+	    growth -= size;
+	    columnPtr->width += size;
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeFlatLayout --
+ *
+ *	Recompute the layout when entries are opened/closed,
+ *	inserted/deleted, or when text attributes change (such as
+ *	font, linespacing).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeFlatLayout(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    TreeViewEntry **p;
+    TreeViewEntry *entryPtr;
+    int count;
+    int maxX;
+    int y;
+
+    /* 
+     * Pass 1:	Reinitialize column sizes and loop through all nodes. 
+     *
+     *		1. Recalculate the size of each entry as needed. 
+     *		2. The maximum depth of the tree. 
+     *		3. Minimum height of an entry.  Dividing this by the
+     *		   height of the widget gives a rough estimate of the 
+     *		   maximum number of visible entries.
+     *		4. Build an array to hold level information to be filled
+     *		   in on pass 2.
+     */
+    if (tvPtr->flags & (TV_DIRTY | TV_UPDATE)) {
+	int position;
+
+	/* Reset the positions of all the columns and initialize the
+	 * column used to track the widest value. */
+	position = 1;
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    columnPtr->maxWidth = 0;
+	    columnPtr->max = SHRT_MAX;
+	    if (columnPtr->reqMax > 0) {
+		columnPtr->max = columnPtr->reqMax;
+	    }
+	    columnPtr->position = position;
+	    position++;
+	}
+
+	/* If the view needs to be resorted, free the old view. */
+	if ((tvPtr->flags & TV_RESORT) && (tvPtr->flatArr != NULL)) {
+	    Blt_Free(tvPtr->flatArr);
+	    tvPtr->flatArr = NULL;
+	}
+
+	/* Recreate the flat view of all the open and not-hidden entries. */
+	if (tvPtr->flatArr == NULL) {
+	    count = 0;
+	    /* Count the number of open entries to allocate for the array. */
+	    for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+		if ((tvPtr->flags & TV_HIDE_ROOT) && 
+		    (entryPtr == tvPtr->rootPtr)) {
+		    continue;
+		}
+		count++;
+	    }
+	    tvPtr->nEntries = count;
+
+	    /* Allocate an array for the flat view. */
+	    tvPtr->flatArr = Blt_Calloc((count + 1), sizeof(TreeViewEntry *));
+	    assert(tvPtr->flatArr);
+
+	    /* Fill the array with open and not-hidden entries */
+	    p = tvPtr->flatArr;
+	    for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+		if ((tvPtr->flags & TV_HIDE_ROOT) && 
+		    (entryPtr == tvPtr->rootPtr)) {
+		    continue;
+		}
+		*p++ = entryPtr;
+	    }
+	    *p = NULL;
+	    tvPtr->flags &= ~TV_SORTED;	/* Indicate the view isn't sorted. */
+	}
+
+	/* Collect the extents of the entries in the flat view. */
+	tvPtr->depth = 0;
+	tvPtr->minHeight = SHRT_MAX;
+	for (p = tvPtr->flatArr; *p != NULL; p++) {
+	    entryPtr = *p;
+	    GetEntryExtents(tvPtr, entryPtr);
+	    if (tvPtr->minHeight > entryPtr->height) {
+		tvPtr->minHeight = entryPtr->height;
+	    }
+	    entryPtr->flags &= ~ENTRY_HAS_BUTTON;
+	}
+	if (tvPtr->levelInfo != NULL) {
+	    Blt_Free(tvPtr->levelInfo);
+	}
+	tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
+	assert(tvPtr->levelInfo);
+	tvPtr->flags &= ~(TV_DIRTY | TV_UPDATE | TV_RESORT);
+	if (tvPtr->flags & TV_SORT_AUTO) {
+	    /* If we're auto-sorting, schedule the view to be resorted. */
+	    tvPtr->flags |= TV_SORT_PENDING;
+	}
+    } 
+
+    if (tvPtr->flags & TV_SORT_PENDING) {
+	Blt_TreeViewSortFlatView(tvPtr);
+    }
+
+    tvPtr->levelInfo[0].labelWidth = tvPtr->levelInfo[0].x = 
+	    tvPtr->levelInfo[0].iconWidth = 0;
+    /* 
+     * Pass 2:	Loop through all open/mapped nodes. 
+     *
+     *		1. Set world y-coordinates for entries. We must defer
+     *		   setting the x-coordinates until we know the maximum 
+     *		   icon sizes at each level.
+     *		2. Compute the maximum depth of the tree. 
+     *		3. Build an array to hold level information.
+     */
+    y = 0;			
+    count = 0;
+    for(p = tvPtr->flatArr; *p != NULL; p++) {
+	entryPtr = *p;
+	entryPtr->flatIndex = count++;
+	entryPtr->worldY = y;
+	entryPtr->vertLineLength = 0;
+	y += entryPtr->height;
+	if (tvPtr->levelInfo[0].labelWidth < entryPtr->labelWidth) {
+	    tvPtr->levelInfo[0].labelWidth = entryPtr->labelWidth;
+	}
+	if (tvPtr->levelInfo[0].iconWidth < entryPtr->iconWidth) {
+	    tvPtr->levelInfo[0].iconWidth = entryPtr->iconWidth;
+	}
+    }
+    tvPtr->levelInfo[0].iconWidth |= 0x01;
+    tvPtr->worldHeight = y;	/* Set the scroll height of the hierarchy. */
+    if (tvPtr->worldHeight < 1) {
+	tvPtr->worldHeight = 1;
+    }
+    maxX = tvPtr->levelInfo[0].iconWidth + tvPtr->levelInfo[0].labelWidth;
+    tvPtr->treeColumn.maxWidth = maxX;
+    tvPtr->treeWidth = maxX;
+    tvPtr->flags |= TV_VIEWPORT;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeTreeLayout --
+ *
+ *	Recompute the layout when entries are opened/closed,
+ *	inserted/deleted, or when text attributes change (such as
+ *	font, linespacing).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ComputeTreeLayout(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr;
+    int maxX, x, y;
+    int sum;
+    register int i;
+
+    /* 
+     * Pass 1:	Reinitialize column sizes and loop through all nodes. 
+     *
+     *		1. Recalculate the size of each entry as needed. 
+     *		2. The maximum depth of the tree. 
+     *		3. Minimum height of an entry.  Dividing this by the
+     *		   height of the widget gives a rough estimate of the 
+     *		   maximum number of visible entries.
+     *		4. Build an array to hold level information to be filled
+     *		   in on pass 2.
+     */
+    if (tvPtr->flags & TV_DIRTY) {
+	int position;
+
+	position = 1;
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    columnPtr->maxWidth = 0;
+	    columnPtr->max = SHRT_MAX;
+	    if (columnPtr->reqMax > 0) {
+		columnPtr->max = columnPtr->reqMax;
+	    }
+	    columnPtr->position = position;
+	    position++;
+	}
+	tvPtr->minHeight = SHRT_MAX;
+	tvPtr->depth = 0;
+	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
+	    GetEntryExtents(tvPtr, entryPtr);
+	    if (tvPtr->minHeight > entryPtr->height) {
+		tvPtr->minHeight = entryPtr->height;
+	    }
+	    /* 
+	     * Determine if the entry should display a button
+	     * (indicating that it has children) and mark the
+	     * entry accordingly. 
+	     */
+	    entryPtr->flags &= ~ENTRY_HAS_BUTTON;
+	    if (entryPtr->flags & BUTTON_SHOW) {
+		entryPtr->flags |= ENTRY_HAS_BUTTON;
+	    } else if (entryPtr->flags & BUTTON_AUTO) {
+		if (tvPtr->flags & TV_HIDE_LEAVES) {
+		    /* Check that a non-leaf child exists */
+		    if (Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN) 
+			!= NULL) {
+			entryPtr->flags |= ENTRY_HAS_BUTTON;
+		    }
+		} else if (!Blt_TreeIsLeaf(entryPtr->node)) {
+		    entryPtr->flags |= ENTRY_HAS_BUTTON;
+		}
+	    }
+	    /* Determine the depth of the tree. */
+	    if (tvPtr->depth < DEPTH(tvPtr, entryPtr->node)) {
+		tvPtr->depth = DEPTH(tvPtr, entryPtr->node);
+	    }
+	}
+	if (tvPtr->flags & TV_SORT_PENDING) {
+	    Blt_TreeViewSortTreeView(tvPtr);
+	}
+	if (tvPtr->levelInfo != NULL) {
+	    Blt_Free(tvPtr->levelInfo);
+	}
+	tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
+	assert(tvPtr->levelInfo);
+	tvPtr->flags &= ~(TV_DIRTY | TV_RESORT);
+    }
+    for (i = 0; i <= (tvPtr->depth + 1); i++) {
+	tvPtr->levelInfo[i].labelWidth = tvPtr->levelInfo[i].x = 
+	    tvPtr->levelInfo[i].iconWidth = 0;
+    }
+    /* 
+     * Pass 2:	Loop through all open/mapped nodes. 
+     *
+     *		1. Set world y-coordinates for entries. We must defer
+     *		   setting the x-coordinates until we know the maximum 
+     *		   icon sizes at each level.
+     *		2. Compute the maximum depth of the tree. 
+     *		3. Build an array to hold level information.
+     */
+    y = 0;
+    if (tvPtr->flags & TV_HIDE_ROOT) {
+	/* If the root entry is to be hidden, cheat by offsetting
+	 * the y-coordinates by the height of the entry. */
+	y = -(tvPtr->rootPtr->height);
+    } 
+    ResetCoordinates(tvPtr, tvPtr->rootPtr, &y);
+    tvPtr->worldHeight = y;	/* Set the scroll height of the hierarchy. */
+    if (tvPtr->worldHeight < 1) {
+	tvPtr->worldHeight = 1;
+    }
+    sum = maxX = 0;
+    for (i = 0; i <= (tvPtr->depth + 1); i++) {
+	sum += tvPtr->levelInfo[i].iconWidth;
+	if (i <= tvPtr->depth) {
+	    tvPtr->levelInfo[i + 1].x = sum;
+	}
+	x = sum + tvPtr->levelInfo[i].labelWidth;
+	if (x > maxX) {
+	    maxX = x;
+	}
+    }
+    tvPtr->treeColumn.maxWidth = maxX;
+    tvPtr->treeWidth = maxX;
+}
+
+
+static void
+LayoutColumns(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    int sum;
+
+    /* The width of the widget (in world coordinates) is the sum 
+     * of the column widths. */
+
+    tvPtr->worldWidth = tvPtr->titleHeight = 0;
+    sum = 0;
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	columnPtr->width = 0;
+	if (!columnPtr->hidden) {
+	    if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) &&
+		(tvPtr->titleHeight < columnPtr->titleHeight)) {
+		tvPtr->titleHeight = columnPtr->titleHeight;
+	    }
+	    if (columnPtr->reqWidth > 0) {
+		columnPtr->width = columnPtr->reqWidth;
+	    } else {
+		/* The computed width of a column is the maximum of
+		 * the title width and the widest entry. */
+		columnPtr->width = MAX(columnPtr->titleWidth, 
+				       columnPtr->maxWidth);
+		/* Check that the width stays within any constraints that
+		 * have been set. */
+		if ((columnPtr->reqMin > 0) && 
+		    (columnPtr->reqMin > columnPtr->width)) {
+		    columnPtr->width = columnPtr->reqMin;
+		}
+		if ((columnPtr->reqMax > 0) && 
+		    (columnPtr->reqMax < columnPtr->width)) {
+		    columnPtr->width = columnPtr->reqMax;
+		}
+	    }
+	    columnPtr->width += 
+		PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth;
+	}
+	columnPtr->worldX = sum;
+	sum += columnPtr->width;
+    }
+    tvPtr->worldWidth = sum;
+    if (VPORTWIDTH(tvPtr) > sum) {
+	AdjustColumns(tvPtr);
+    }
+    sum = 0;
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	columnPtr->worldX = sum;
+	sum += columnPtr->width;
+    }
+    if (tvPtr->titleHeight > 0) {
+	/* If any headings are displayed, add some extra padding to
+	 * the height. */
+	tvPtr->titleHeight += 4;
+    }
+    /* tvPtr->worldWidth += 10; */
+    if (tvPtr->yScrollUnits < 1) {
+	tvPtr->yScrollUnits = 1;
+    }
+    if (tvPtr->xScrollUnits < 1) {
+	tvPtr->xScrollUnits = 1;
+    }
+    if (tvPtr->worldWidth < 1) {
+	tvPtr->worldWidth = 1;
+    }
+    tvPtr->flags &= ~TV_LAYOUT;
+    tvPtr->flags |= TV_SCROLL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_TreeViewComputeLayout --
+ *
+ *	Recompute the layout when entries are opened/closed,
+ *	inserted/deleted, or when text attributes change (such as
+ *	font, linespacing).
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The world coordinates are set for all the opened entries.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewComputeLayout(TreeView *tvPtr)
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+
+    if (tvPtr->flatView) {
+	ComputeFlatLayout(tvPtr);
+    } else {
+	ComputeTreeLayout(tvPtr);
+    }
+    /*
+     * Determine the width of each column based upon the entries
+     * that as open (not hidden).  The widest entry in a column
+     * determines the width of that column.
+     */
+
+    /* Initialize the columns. */
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	columnPtr->maxWidth = 0;
+	columnPtr->max = SHRT_MAX;
+	if (columnPtr->reqMax > 0) {
+	    columnPtr->max = columnPtr->reqMax;
+	}
+    }
+    /* The treeview column width was computed earlier. */
+    tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
+
+    /* 
+     * Look at all open entries and their values.  Determine the column
+     * widths by tracking the maximum width value in each column.
+     */
+    for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+	for (valuePtr = entryPtr->values; valuePtr != NULL; 
+	     valuePtr = valuePtr->nextPtr) {
+	    if (valuePtr->columnPtr->maxWidth < valuePtr->width) {
+		valuePtr->columnPtr->maxWidth = valuePtr->width;
+	    }
+	}	    
+    }
+    /* Now layout the columns with the proper sizes. */
+    LayoutColumns(tvPtr);
+}
+    
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ComputeVisibleEntries --
+ *
+ *	The entries visible in the viewport (the widget's window) are
+ *	inserted into the array of visible nodes.
+ *
+ * Results:
+ *	Returns 1 if beyond the last visible entry, 0 otherwise.
+ *
+ * Side effects:
+ *	The array of visible nodes is filled.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+ComputeVisibleEntries(TreeView *tvPtr)
+{
+    int height;
+    int level;
+    int nSlots;
+    int x, maxX;
+    int xOffset, yOffset;
+
+    xOffset = Blt_AdjustViewport(tvPtr->xOffset, tvPtr->worldWidth,
+	VPORTWIDTH(tvPtr), tvPtr->xScrollUnits, tvPtr->scrollMode);
+    yOffset = Blt_AdjustViewport(tvPtr->yOffset, 
+	tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits, 
+	tvPtr->scrollMode);
+
+    if ((xOffset != tvPtr->xOffset) || (yOffset != tvPtr->yOffset)) {
+	tvPtr->yOffset = yOffset;
+	tvPtr->xOffset = xOffset;
+	tvPtr->flags |= TV_VIEWPORT;
+    }
+    height = VPORTHEIGHT(tvPtr);
+
+    /* Allocate worst case number of slots for entry array. */
+    nSlots = (height / tvPtr->minHeight) + 3;
+    if (nSlots != tvPtr->nVisible) {
+	if (tvPtr->visibleArr != NULL) {
+	    Blt_Free(tvPtr->visibleArr);
+	}
+	tvPtr->visibleArr = Blt_Calloc(nSlots, sizeof(TreeViewEntry *));
+	assert(tvPtr->visibleArr);
+    }
+    tvPtr->nVisible = 0;
+
+    if (tvPtr->rootPtr->flags & ENTRY_HIDDEN) {
+	return TCL_OK;		/* Root node is hidden. */
+    }
+    /* Find the node where the view port starts. */
+    if (tvPtr->flatView) {
+	register TreeViewEntry **p, *entryPtr;
+
+	/* Find the starting entry visible in the viewport. It can't
+	 * be hidden or any of it's ancestors closed. */
+    again:
+	for (p = tvPtr->flatArr; *p != NULL; p++) {
+	    entryPtr = *p;
+	    if ((entryPtr->worldY + entryPtr->height) > tvPtr->yOffset) {
+		break;
+	    }
+	}	    
+	/*
+	 * If we can't find the starting node, then the view must be
+	 * scrolled down, but some nodes were deleted.  Reset the view
+	 * back to the top and try again.
+	 */
+	if (*p == NULL) {
+	    if (tvPtr->yOffset == 0) {
+		return TCL_OK;	/* All entries are hidden. */
+	    }
+	    tvPtr->yOffset = 0;
+	    goto again;
+	}
+
+	maxX = 0;
+	height += tvPtr->yOffset;
+	for (/* empty */; *p != NULL; p++) {
+	    entryPtr = *p;
+	    entryPtr->worldX = LEVELX(0) + tvPtr->treeColumn.worldX;
+	    x = entryPtr->worldX + ICONWIDTH(0) + entryPtr->width;
+	    if (x > maxX) {
+		maxX = x;
+	    }
+	    if (entryPtr->worldY >= height) {
+		break;
+	    }
+	    tvPtr->visibleArr[tvPtr->nVisible] = *p;
+	    tvPtr->nVisible++;
+	}
+	tvPtr->visibleArr[tvPtr->nVisible] = NULL;
+    } else {
+	TreeViewEntry *entryPtr;
+
+	entryPtr = tvPtr->rootPtr;
+	while ((entryPtr->worldY + entryPtr->height) <= tvPtr->yOffset) {
+	    for (entryPtr = Blt_TreeViewLastChild(entryPtr, ENTRY_HIDDEN);
+		 entryPtr != NULL; 
+		 entryPtr = Blt_TreeViewPrevSibling(entryPtr, ENTRY_HIDDEN)) {
+		if (entryPtr->worldY <= tvPtr->yOffset) {
+		    break;
+		}
+	    }
+	    /*
+	     * If we can't find the starting node, then the view must be
+	     * scrolled down, but some nodes were deleted.  Reset the view
+	     * back to the top and try again.
+	     */
+	    if (entryPtr == NULL) {
+		if (tvPtr->yOffset == 0) {
+		    return TCL_OK;	/* All entries are hidden. */
+		}
+		tvPtr->yOffset = 0;
+		continue;
+	    }
+	}
+	
+
+	height += tvPtr->yOffset;
+	maxX = 0;
+	tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
+
+	for (/* empty */; entryPtr != NULL; 
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+	    /*
+	     * Compute and save the entry's X-coordinate now that we know
+	     * the maximum level offset for the entire widget.
+	     */
+	    level = DEPTH(tvPtr, entryPtr->node);
+	    entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
+	    
+	    x = entryPtr->worldX + ICONWIDTH(level) + ICONWIDTH(level + 1) + 
+		entryPtr->width;
+	    if (x > maxX) {
+		maxX = x;
+	    }
+	    if (entryPtr->worldY >= height) {
+		break;
+	    }
+	    tvPtr->visibleArr[tvPtr->nVisible] = entryPtr;
+	    tvPtr->nVisible++;
+	}
+	tvPtr->visibleArr[tvPtr->nVisible] = NULL;
+    }
+    /*
+     * -------------------------------------------------------------------
+     *
+     * Note:	It's assumed that the view port always starts at or
+     *		over an entry.  Check that a change in the hierarchy
+     *		(e.g. closing a node) hasn't left the viewport beyond
+     *		the last entry.  If so, adjust the viewport to start
+     *		on the last entry.
+     *
+     * -------------------------------------------------------------------
+     */
+    if (tvPtr->xOffset > (tvPtr->worldWidth - tvPtr->xScrollUnits)) {
+	tvPtr->xOffset = tvPtr->worldWidth - tvPtr->xScrollUnits;
+    }
+    if (tvPtr->yOffset > (tvPtr->worldHeight - tvPtr->yScrollUnits)) {
+	tvPtr->yOffset = tvPtr->worldHeight - tvPtr->yScrollUnits;
+    }
+    tvPtr->xOffset = Blt_AdjustViewport(tvPtr->xOffset, 
+	tvPtr->worldWidth, VPORTWIDTH(tvPtr), tvPtr->xScrollUnits, 
+	tvPtr->scrollMode);
+    tvPtr->yOffset = Blt_AdjustViewport(tvPtr->yOffset,
+	tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
+	tvPtr->scrollMode);
+
+    Blt_PickCurrentItem(tvPtr->bindTable);
+    tvPtr->flags &= ~TV_DIRTY;
+    return TCL_OK;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawVerticals --
+ *
+ * 	Draws vertical lines for the ancestor nodes.  While the entry
+ *	of the ancestor may not be visible, its vertical line segment
+ *	does extent into the viewport.  So walk back up the hierarchy
+ *	drawing lines until we get to the root.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Vertical lines are drawn for the ancestor nodes.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawVerticals(
+    TreeView *tvPtr,		/* Widget record containing the attribute
+				 * information for buttons. */
+    TreeViewEntry *entryPtr,	/* Entry to be drawn. */
+    Drawable drawable)		/* Pixmap or window to draw into. */
+{
+    int height, level;
+    int x, y;
+    int x1, y1, x2, y2;
+
+    while (entryPtr != tvPtr->rootPtr) {
+	entryPtr = Blt_TreeViewParentEntry(entryPtr);
+	if (entryPtr == NULL) {
+	    break;
+	}
+	level = DEPTH(tvPtr, entryPtr->node);
+	/*
+	 * World X-coordinates aren't computed only for entries that are
+	 * outside the view port.  So for each off-screen ancestor node
+	 * compute it here too.
+	 */
+	entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
+	x = SCREENX(tvPtr, entryPtr->worldX);
+	y = SCREENY(tvPtr, entryPtr->worldY);
+	height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight, 
+		tvPtr->button.height);
+	y += (height - tvPtr->button.height) / 2;
+	x1 = x2 = x + ICONWIDTH(level) + ICONWIDTH(level + 1) / 2;
+	y1 = y + tvPtr->button.height / 2;
+	y2 = y1 + entryPtr->vertLineLength;
+	if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
+	    y1 += entryPtr->height;
+	}
+	/*
+	 * Clip the line's Y-coordinates at the viewport borders.
+	 */
+	if (y1 < 0) {
+	    y1 = (y1 & 0x1);	/* Make sure the dotted line starts on 
+				 * the same even/odd pixel. */
+	}
+	if (y2 > Tk_Height(tvPtr->tkwin)) {
+	    y2 = Tk_Height(tvPtr->tkwin);
+	}
+	if ((y1 < Tk_Height(tvPtr->tkwin)) && (y2 > 0)) {
+	    XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, 
+	      x1, y1, x2, y2);
+	}
+    }
+}
+
+void
+Blt_TreeViewDrawRule(
+    TreeView *tvPtr,		/* Widget record containing the
+				 * attribute information for rules. */
+    TreeViewColumn *columnPtr,
+    Drawable drawable)		/* Pixmap or window to draw into. */
+{
+    int x, y1, y2;
+
+    x = SCREENX(tvPtr, columnPtr->worldX) + 
+	columnPtr->width + tvPtr->ruleMark - tvPtr->ruleAnchor - 1;
+
+    y1 = tvPtr->titleHeight + tvPtr->inset;
+    y2 = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
+    XDrawLine(tvPtr->display, drawable, columnPtr->ruleGC, x, y1, x, y2);
+    tvPtr->flags = TOGGLE(tvPtr->flags, TV_RULE_ACTIVE);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Blt_TreeViewDrawButton --
+ *
+ * 	Draws a button for the given entry. The button is drawn
+ * 	centered in the region immediately to the left of the origin
+ * 	of the entry (computed in the layout routines). The height
+ * 	and width of the button were previously calculated from the
+ * 	average row height.
+ *
+ *		button height = entry height - (2 * some arbitrary padding).
+ *		button width = button height.
+ *
+ *	The button may have a border.  The symbol (either a plus or
+ *	minus) is slight smaller than the width or height minus the
+ *	border.
+ *
+ *	    x,y origin of entry
+ *
+ *              +---+
+ *              | + | icon label
+ *              +---+
+ *             closed
+ *
+ *           |----|----| horizontal offset
+ *
+ *              +---+
+ *              | - | icon label
+ *              +---+
+ *              open
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+void
+Blt_TreeViewDrawButton(
+    TreeView *tvPtr,		/* Widget record containing the
+				 * attribute information for
+				 * buttons. */
+    TreeViewEntry *entryPtr,	/* Entry. */
+    Drawable drawable,		/* Pixmap or window to draw into. */
+    int x, 
+    int y)
+{
+    Tk_3DBorder border;
+    TreeViewButton *buttonPtr = &tvPtr->button;
+    TreeViewIcon icon;
+    int relief;
+    int width, height;
+
+    if (entryPtr == tvPtr->activeButtonPtr) {
+	border = buttonPtr->activeBorder;
+    } else {
+	border = buttonPtr->border;
+    }
+    if (entryPtr->flags & ENTRY_CLOSED) {
+	relief = buttonPtr->closeRelief;
+    } else {
+	relief = buttonPtr->openRelief;
+    }
+    if (relief == TK_RELIEF_SOLID) {
+	relief = TK_RELIEF_FLAT;
+    }
+    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
+	buttonPtr->width, buttonPtr->height, buttonPtr->borderWidth, relief);
+
+    x += buttonPtr->borderWidth;
+    y += buttonPtr->borderWidth;
+    width = buttonPtr->width - (2 * buttonPtr->borderWidth);
+    height = buttonPtr->height - (2 * buttonPtr->borderWidth);
+
+    icon = NULL;
+    if (buttonPtr->icons != NULL) {  /* Open or close button icon? */
+	icon = buttonPtr->icons[0];
+	if (((entryPtr->flags & ENTRY_CLOSED) == 0) && 
+	    (buttonPtr->icons[1] != NULL)) {
+	    icon = buttonPtr->icons[1];
+	}
+    }
+    if (icon != NULL) {	/* Icon or rectangle? */
+	Tk_RedrawImage(TreeViewIconBits(icon), 0, 0, width, height, drawable,
+		       x, y);
+    } else {
+	int top, bottom, left, right;
+	XSegment segments[6];
+	int count;
+	GC gc;
+
+	gc = (entryPtr == tvPtr->activeButtonPtr)
+	    ? buttonPtr->activeGC : buttonPtr->normalGC;
+	if (relief == TK_RELIEF_FLAT) {
+	    /* Draw the box outline */
+
+	    left = x - buttonPtr->borderWidth;
+	    top = y - buttonPtr->borderWidth;
+	    right = left + buttonPtr->width - 1;
+	    bottom = top + buttonPtr->height - 1;
+
+	    segments[0].x1 = left;
+	    segments[0].x2 = right;
+	    segments[0].y2 = segments[0].y1 = top;
+	    segments[1].x2 = segments[1].x1 = right;
+	    segments[1].y1 = top;
+	    segments[1].y2 = bottom;
+	    segments[2].x2 = segments[2].x1 = left;
+	    segments[2].y1 = top;
+	    segments[2].y2 = bottom;
+#ifdef WIN32
+	    segments[2].y2++;
+#endif
+	    segments[3].x1 = left;
+	    segments[3].x2 = right;
+	    segments[3].y2 = segments[3].y1 = bottom;
+#ifdef WIN32
+	    segments[3].x2++;
+#endif
+	}
+	top = y + height / 2;
+	left = x + BUTTON_IPAD;
+	right = x + width - BUTTON_IPAD;
+
+	segments[4].y1 = segments[4].y2 = top;
+	segments[4].x1 = left;
+	segments[4].x2 = right - 1;
+#ifdef WIN32
+	segments[4].x2++;
+#endif
+
+	count = 5;
+	if (entryPtr->flags & ENTRY_CLOSED) { /* Draw the vertical
+					       * line for the plus. */
+	    top = y + BUTTON_IPAD;
+	    bottom = y + height - BUTTON_IPAD;
+	    segments[5].y1 = top;
+	    segments[5].y2 = bottom - 1;
+	    segments[5].x1 = segments[5].x2 = x + width / 2;
+#ifdef WIN32
+	    segments[5].y2++;
+#endif
+	    count = 6;
+	}
+	XDrawSegments(tvPtr->display, drawable, gc, segments, count);
+    }
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Blt_TreeViewGetEntryIcon --
+ *
+ * 	Selects the correct image for the entry's icon depending upon
+ *	the current state of the entry: active/inactive normal/selected.  
+ *
+ *		active - normal
+ *		active - selected
+ *		inactive - normal
+ *		inactive - selected
+ *
+ * Results:
+ *	Returns the image for the icon.
+ *
+ * ---------------------------------------------------------------------------
+ */
+TreeViewIcon
+Blt_TreeViewGetEntryIcon(TreeView *tvPtr, TreeViewEntry *entryPtr)
+{
+    TreeViewIcon *icons;
+    TreeViewIcon icon;
+
+    int isActive, hasFocus;
+
+    isActive = (entryPtr == tvPtr->activePtr);
+    hasFocus = (entryPtr == tvPtr->focusPtr);
+    icons = NULL;
+    if (isActive) {
+	icons = CHOOSE(tvPtr->activeIcons, entryPtr->activeIcons);
+    }
+    if (icons == NULL) {
+	icons = CHOOSE(tvPtr->icons, entryPtr->icons);
+
+    }
+    icon = NULL;
+    if (icons != NULL) {	/* Selected or normal icon? */
+	icon = icons[0];
+	if ((hasFocus) && (icons[1] != NULL)) {
+	    icon = icons[1];
+	}
+    }
+    return icon;
+}
+
+
+int
+Blt_TreeViewDrawIcon(
+    TreeView *tvPtr,		/* Widget record containing the attribute
+				 * information for buttons. */
+    TreeViewEntry *entryPtr,	/* Entry to display. */
+    Drawable drawable,		/* Pixmap or window to draw into. */
+    int x, 
+    int y)
+{
+    TreeViewIcon icon;
+
+    icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
+
+    if (icon != NULL) {	/* Icon or default icon bitmap? */
+	int entryHeight;
+	int level;
+	int maxY;
+	int top, bottom;
+	int topInset, botInset;
+	int width, height;
+
+	level = DEPTH(tvPtr, entryPtr->node);
+	entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight, 
+		tvPtr->button.height);
+	height = TreeViewIconHeight(icon);
+	width = TreeViewIconWidth(icon);
+	if (tvPtr->flatView) {
+	    x += (ICONWIDTH(0) - width) / 2;
+	} else {
+	    x += (ICONWIDTH(level + 1) - width) / 2;
+	}	    
+	y += (entryHeight - height) / 2;
+	botInset = tvPtr->inset - INSET_PAD;
+	topInset = tvPtr->titleHeight + tvPtr->inset;
+	maxY = Tk_Height(tvPtr->tkwin) - botInset;
+	top = 0;
+	bottom = y + height;
+	if (y < topInset) {
+	    height += y - topInset;
+	    top = -y + topInset;
+	    y = topInset;
+	} else if (bottom >= maxY) {
+	    height = maxY - y;
+	}
+	Tk_RedrawImage(TreeViewIconBits(icon), 0, top, width, height, 
+		drawable, x, y);
+    } 
+    return (icon != NULL);
+}
+
+static int
+DrawLabel(
+    TreeView *tvPtr,		/* Widget record. */
+    TreeViewEntry *entryPtr,	/* Entry attribute information. */
+    Drawable drawable,		/* Pixmap or window to draw into. */
+    int x, 
+    int y)			
+{
+    char *label;
+    int entryHeight;
+    int isFocused;
+    int width, height;		/* Width and height of label. */
+    int selected;
+
+    entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight, 
+       tvPtr->button.height);
+    isFocused = ((entryPtr == tvPtr->focusPtr) && 
+		 (tvPtr->flags & TV_FOCUS));
+    selected = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
+
+    /* Includes padding, selection 3-D border, and focus outline. */
+    width = entryPtr->labelWidth;
+    height = entryPtr->labelHeight;
+
+    /* Center the label, if necessary, vertically along the entry row. */
+    if (height < entryHeight) {
+	y += (entryHeight - height) / 2;
+    }
+    if (isFocused) {		/* Focus outline */
+	if (selected) {
+	    XColor *color;
+
+	    color = SELECT_FG(tvPtr);
+	    XSetForeground(tvPtr->display, tvPtr->focusGC, color->pixel);
+	}
+	XDrawRectangle(tvPtr->display, drawable, tvPtr->focusGC, x, y, 
+		       width - 1, height - 1);
+	if (selected) {
+	    XSetForeground(tvPtr->display, tvPtr->focusGC, 
+		tvPtr->focusColor->pixel);
+	}
+    }
+    x += FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth;
+    y += FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth;
+
+    label = GETLABEL(entryPtr);
+    if (label[0] != '\0') {
+	GC gc;
+	TreeViewStyle *stylePtr;
+	TextStyle ts;
+	Tk_Font font;
+	XColor *normalColor, *activeColor;
+
+	stylePtr = tvPtr->treeColumn.stylePtr;
+	font = entryPtr->font;
+	if (font == NULL) {
+	    font = Blt_TreeViewGetStyleFont(tvPtr, stylePtr);
+	}
+	normalColor = entryPtr->color;
+	if (normalColor == NULL) {
+	    normalColor = Blt_TreeViewGetStyleFg(tvPtr, stylePtr);
+	}
+	activeColor = (selected) ? SELECT_FG(tvPtr) : normalColor;
+	gc = entryPtr->gc;
+	if (gc == NULL) {
+	    gc = Blt_TreeViewGetStyleGC(stylePtr);
+	}
+	Blt_SetDrawTextStyle(&ts, font, gc, normalColor, activeColor, 
+		entryPtr->shadow.color, 0.0, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, 
+		entryPtr->shadow.offset);
+	ts.state = (selected || (entryPtr->gc == NULL)) ? STATE_ACTIVE : 0;
+	Blt_DrawTextLayout(tvPtr->tkwin, drawable, entryPtr->textPtr, 
+		&ts, x, y);
+    }
+    return entryHeight;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawFlatEntry --
+ *
+ * 	Draws a button for the given entry.  Note that buttons should only
+ *	be drawn if the entry has sub-entries to be opened or closed.  It's
+ *	the responsibility of the calling routine to ensure this.
+ *
+ *	The button is drawn centered in the region immediately to the left
+ *	of the origin of the entry (computed in the layout routines). The
+ *	height and width of the button were previously calculated from the
+ *	average row height.
+ *
+ *		button height = entry height - (2 * some arbitrary padding).
+ *		button width = button height.
+ *
+ *	The button has a border.  The symbol (either a plus or minus) is
+ *	slight smaller than the width or height minus the border.
+ *
+ *	    x,y origin of entry
+ *
+ *              +---+
+ *              | + | icon label
+ *              +---+
+ *             closed
+ *
+ *           |----|----| horizontal offset
+ *
+ *              +---+
+ *              | - | icon label
+ *              +---+
+ *              open
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawFlatEntry(
+    TreeView *tvPtr,		/* Widget record containing the attribute
+				 * information for buttons. */
+    TreeViewEntry *entryPtr,	/* Entry to be drawn. */
+    Drawable drawable)		/* Pixmap or window to draw into. */
+{
+    int level;
+    int x, y;
+
+    entryPtr->flags &= ~ENTRY_REDRAW;
+
+    x = SCREENX(tvPtr, entryPtr->worldX);
+    y = SCREENY(tvPtr, entryPtr->worldY);
+    if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y)) {
+	x -= (DEF_ICON_WIDTH * 2) / 3;
+    }
+    level = 0;
+    x += ICONWIDTH(level);
+    /* Entry label. */
+    DrawLabel(tvPtr, entryPtr, drawable, x, y);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * DrawTreeEntry --
+ *
+ * 	Draws a button for the given entry.  Note that buttons should only
+ *	be drawn if the entry has sub-entries to be opened or closed.  It's
+ *	the responsibility of the calling routine to ensure this.
+ *
+ *	The button is drawn centered in the region immediately to the left
+ *	of the origin of the entry (computed in the layout routines). The
+ *	height and width of the button were previously calculated from the
+ *	average row height.
+ *
+ *		button height = entry height - (2 * some arbitrary padding).
+ *		button width = button height.
+ *
+ *	The button has a border.  The symbol (either a plus or minus) is
+ *	slight smaller than the width or height minus the border.
+ *
+ *	    x,y origin of entry
+ *
+ *              +---+
+ *              | + | icon label
+ *              +---+
+ *             closed
+ *
+ *           |----|----| horizontal offset
+ *
+ *              +---+
+ *              | - | icon label
+ *              +---+
+ *              open
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void
+DrawTreeEntry(
+    TreeView *tvPtr,		/* Widget record. */
+    TreeViewEntry *entryPtr,	/* Entry to be drawn. */
+    Drawable drawable)		/* Pixmap or window to draw into. */
+{
+    TreeViewButton *buttonPtr = &tvPtr->button;
+    int buttonY;
+    int level;
+    int width, height;
+    int x, y;
+    int x1, y1, x2, y2;
+
+    entryPtr->flags &= ~ENTRY_REDRAW;
+
+    x = SCREENX(tvPtr, entryPtr->worldX);
+    y = SCREENY(tvPtr, entryPtr->worldY);
+
+    level = DEPTH(tvPtr, entryPtr->node);
+    width = ICONWIDTH(level);
+    height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight, 
+	buttonPtr->height);
+
+    entryPtr->buttonX = (width - buttonPtr->width) / 2;
+    entryPtr->buttonY = (height - buttonPtr->height) / 2;
+
+    buttonY = y + entryPtr->buttonY;
+
+    x1 = x + (width / 2);
+    y1 = y2 = buttonY + (buttonPtr->height / 2);
+    x2 = x1 + (ICONWIDTH(level) + ICONWIDTH(level + 1)) / 2;
+
+    if ((Blt_TreeNodeParent(entryPtr->node) != NULL) && 
+	(tvPtr->lineWidth > 0)) {
+	/*
+	 * For every node except root, draw a horizontal line from
+	 * the vertical bar to the middle of the icon.
+	 */
+	XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x1, y1, x2, y2);
+    }
+    if (((entryPtr->flags & ENTRY_CLOSED) == 0) && (tvPtr->lineWidth > 0)) {
+	/*
+	 * Entry is open, draw vertical line.
+	 */
+	y2 = y1 + entryPtr->vertLineLength;
+	if (y2 > Tk_Height(tvPtr->tkwin)) {
+	    y2 = Tk_Height(tvPtr->tkwin); /* Clip line at window border. */
+	}
+	XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x2, y1, x2, y2);
+    }
+    if ((entryPtr->flags & ENTRY_HAS_BUTTON) && (entryPtr != tvPtr->rootPtr)) {
+	/*
+	 * Except for the root, draw a button for every entry that
+	 * needs one.  The displayed button can be either an icon (Tk
+	 * image) or a line drawing (rectangle with plus or minus
+	 * sign).
+	 */
+	Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, x + entryPtr->buttonX,
+		y + entryPtr->buttonY);
+    }
+    x += ICONWIDTH(level);
+
+    if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y)) {
+	x -= (DEF_ICON_WIDTH * 2) / 3;
+    }
+    x += ICONWIDTH(level + 1) + 4;
+
+    /* Entry label. */
+    DrawLabel(tvPtr, entryPtr, drawable, x, y);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Blt_TreeViewDrawValue --
+ *
+ * 	Draws a column value for the given entry.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	A button is drawn for the entry.
+ *
+ * ---------------------------------------------------------------------------
+ */
+void
+Blt_TreeViewDrawValue(
+    TreeView *tvPtr,		/* Widget record. */
+    TreeViewEntry *entryPtr,	/* Node of entry to be drawn. */
+    TreeViewValue *valuePtr,
+    Drawable drawable,		/* Pixmap or window to draw into. */
+    int x, 
+    int y)
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = CHOOSE(valuePtr->columnPtr->stylePtr, valuePtr->stylePtr);
+    (*stylePtr->classPtr->drawProc)(tvPtr, drawable, entryPtr, valuePtr, 
+		stylePtr, x, y);
+}
+
+static void
+DrawTitle(
+    TreeView *tvPtr,
+    TreeViewColumn *columnPtr,
+    Drawable drawable,
+    int x)
+{
+    GC gc;
+    Tk_3DBorder border;
+    XColor *fgColor;
+    int columnWidth;
+    int width;
+    int x0, cx, xOffset;
+
+    columnWidth = columnPtr->width;
+    cx = x;
+    if (columnPtr->position == Blt_ChainGetLength(tvPtr->colChainPtr)) {
+	/* If there's any room left over, let the last column take it. */
+	columnWidth = Tk_Width(tvPtr->tkwin) - x;
+    } else if (columnPtr->position == 1) {
+	columnWidth += x;
+	cx = 0;
+    }
+    x0 = x + columnPtr->borderWidth;
+
+    if (columnPtr == tvPtr->activeTitleColumnPtr) {
+	border = columnPtr->activeTitleBorder;
+	gc = columnPtr->activeTitleGC;
+	fgColor = columnPtr->activeTitleFgColor;
+    } else {
+	border = columnPtr->titleBorder;
+	gc = columnPtr->titleGC;
+	fgColor = columnPtr->titleFgColor;
+    }
+    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, cx + 1, 
+	tvPtr->inset + 1, columnWidth - 2, tvPtr->titleHeight - 2, 0, 
+	TK_RELIEF_FLAT);
+    width = columnPtr->width;
+    xOffset = x0 + columnPtr->pad.side1 + 1;
+    
+    if (width > columnPtr->titleWidth) {
+	x += (width - columnPtr->titleWidth) / 2;
+    }
+    if (columnPtr == tvPtr->sortColumnPtr) {
+	/* Make sure there's room for the sorting-direction triangle. */
+	if ((x - xOffset) <= (STD_ARROW_WIDTH + 4)) {
+	    x = xOffset + STD_ARROW_WIDTH + 4;
+	}
+    }
+    if (columnPtr->titleIcon != NULL) {
+	int iconX, iconY, iconWidth, iconHeight;
+
+	iconHeight = TreeViewIconHeight(columnPtr->titleIcon);
+	iconWidth = TreeViewIconWidth(columnPtr->titleIcon);
+	iconX = x;
+	if (columnPtr->titleTextPtr != NULL) {
+	    iconX += 2;
+	}
+	iconY = tvPtr->inset + (tvPtr->titleHeight - iconHeight) / 2;
+	Tk_RedrawImage(TreeViewIconBits(columnPtr->titleIcon), 0, 0, 
+	   iconWidth, iconHeight, drawable, iconX, iconY);
+	x += iconWidth + 6;
+    }
+    if (columnPtr->titleTextPtr != NULL) {
+	TextStyle ts;
+
+	Blt_SetDrawTextStyle(&ts, columnPtr->titleFont, gc, fgColor,
+	    SELECT_FG(tvPtr), columnPtr->titleShadow.color, 0.0, TK_ANCHOR_NW,
+		TK_JUSTIFY_LEFT, 0, columnPtr->titleShadow.offset);
+	Blt_DrawTextLayout(tvPtr->tkwin, drawable, columnPtr->titleTextPtr, &ts,
+		x, tvPtr->inset + 1);
+    }
+    if ((columnPtr == tvPtr->sortColumnPtr) && (tvPtr->flatView)) {
+	Blt_DrawArrow(tvPtr->display, drawable, gc, 
+		xOffset + ARROW_OFFSET, 
+		tvPtr->inset + tvPtr->titleHeight / 2, STD_ARROW_HEIGHT, 
+		(tvPtr->sortDecreasing) ? ARROW_UP : ARROW_DOWN);
+    }
+    Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, cx, tvPtr->inset, 
+	columnWidth, tvPtr->titleHeight, columnPtr->titleBorderWidth, 
+	columnPtr->titleRelief);
+}
+
+void
+Blt_TreeViewDrawHeadings(tvPtr, drawable)
+    TreeView *tvPtr;
+    Drawable drawable;
+{
+    Blt_ChainLink *linkPtr;
+    TreeViewColumn *columnPtr;
+    int x;
+
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	 linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	if (columnPtr->hidden) {
+	    continue;
+	}
+	x = SCREENX(tvPtr, columnPtr->worldX);
+	if ((x + columnPtr->width) < 0) {
+	    continue;	/* Don't draw columns before the left edge. */
+	}
+	if (x > Tk_Width(tvPtr->tkwin)) {
+	    break;		/* Discontinue when a column starts beyond
+				 * the right edge. */
+	}
+	DrawTitle(tvPtr, columnPtr, drawable, x);
+    }
+}
+
+static void
+DrawTreeView(tvPtr, drawable, x)
+    TreeView *tvPtr;
+    Drawable drawable;
+    int x;
+{
+    register TreeViewEntry **p;
+    Tk_3DBorder selBorder;
+
+    /* 
+     * Draw the backgrounds of selected entries first.  The vertical
+     * lines connecting child entries will be draw on top.
+     */
+    selBorder = SELECT_BORDER(tvPtr);
+    for (p = tvPtr->visibleArr; *p != NULL; p++) {
+	if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
+	    int y;
+
+	    y = SCREENY(tvPtr, (*p)->worldY) - 1;
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, x, y, 
+		tvPtr->treeColumn.width, (*p)->height + 1, 
+		tvPtr->selBorderWidth, tvPtr->selRelief);
+	}
+    }
+    if ((tvPtr->lineWidth > 0) && (tvPtr->nVisible > 0)) { 
+	/* Draw all the vertical lines from topmost node. */
+	DrawVerticals(tvPtr, tvPtr->visibleArr[0], drawable);
+    }
+
+    for (p = tvPtr->visibleArr; *p != NULL; p++) {
+	DrawTreeEntry(tvPtr, *p, drawable);
+    }
+}
+
+static void
+DrawFlatView(
+    TreeView *tvPtr,
+    Drawable drawable,
+    int x)
+{
+    register TreeViewEntry **p;
+    Tk_3DBorder selBorder;
+    /* 
+     * Draw the backgrounds of selected entries first.  The vertical
+     * lines connecting child entries will be draw on top. 
+     */
+    selBorder = SELECT_BORDER(tvPtr);
+    for (p = tvPtr->visibleArr; *p != NULL; p++) {
+	if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
+	    int y;
+
+	    y = SCREENY(tvPtr, (*p)->worldY) - 1;
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, x, y, 
+		tvPtr->treeColumn.width, (*p)->height + 1, 
+		tvPtr->selBorderWidth, tvPtr->selRelief);
+	}
+    }
+    for (p = tvPtr->visibleArr; *p != NULL; p++) {
+	DrawFlatEntry(tvPtr, *p, drawable);
+    }
+}
+
+void
+Blt_TreeViewDrawOuterBorders(tvPtr, drawable)
+    TreeView *tvPtr;
+    Drawable drawable;
+{
+    /* Draw 3D border just inside of the focus highlight ring. */
+    if ((tvPtr->borderWidth > 0) && (tvPtr->relief != TK_RELIEF_FLAT)) {
+	Blt_Draw3DRectangle(tvPtr->tkwin, drawable, tvPtr->border,
+	    tvPtr->highlightWidth, tvPtr->highlightWidth,
+	    Tk_Width(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
+	    Tk_Height(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
+	    tvPtr->borderWidth, tvPtr->relief);
+    }
+    /* Draw focus highlight ring. */
+    if (tvPtr->highlightWidth > 0) {
+	XColor *color;
+	GC gc;
+
+	color = (tvPtr->flags & TV_FOCUS)
+	    ? tvPtr->highlightColor : tvPtr->highlightBgColor;
+	gc = Tk_GCForColor(color, drawable);
+	Tk_DrawFocusHighlight(tvPtr->tkwin, gc, tvPtr->highlightWidth,
+	    drawable);
+    }
+    tvPtr->flags &= ~TV_BORDERS;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DisplayTreeView --
+ *
+ * 	This procedure is invoked to display the widget.
+ *
+ *      Recompute the layout of the text if necessary. This is
+ *	necessary if the world coordinate system has changed.
+ *	Specifically, the following may have occurred:
+ *
+ *	  1.  a text attribute has changed (font, linespacing, etc.).
+ *	  2.  an entry's option changed, possibly resizing the entry.
+ *
+ *      This is deferred to the display routine since potentially
+ *      many of these may occur.
+ *
+ *	Set the vertical and horizontal scrollbars.  This is done
+ *	here since the window width and height are needed for the
+ *	scrollbar calculations.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ * 	The widget is redisplayed.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DisplayTreeView(ClientData clientData)	/* Information about widget. */
+{
+    Blt_ChainLink *linkPtr;
+    TreeView *tvPtr = clientData;
+    TreeViewColumn *columnPtr;
+    Pixmap drawable; 
+    int width, height;
+    int x;
+
+    tvPtr->flags &= ~TV_REDRAW;
+    if (tvPtr->tkwin == NULL) {
+	return;			/* Window has been destroyed. */
+    }
+    if (tvPtr->flags & TV_LAYOUT) {
+	/*
+	 * Recompute the layout when entries are opened/closed,
+	 * inserted/deleted, or when text attributes change (such as
+	 * font, linespacing).
+	 */
+	Blt_TreeViewComputeLayout(tvPtr);
+    }
+    if (tvPtr->flags & TV_SCROLL) {
+	/* 
+	 * Scrolling means that the view port has changed and that the
+	 * visible entries need to be recomputed.
+	 */
+	ComputeVisibleEntries(tvPtr);
+
+	width = VPORTWIDTH(tvPtr);
+	height = VPORTHEIGHT(tvPtr);
+	if (tvPtr->flags & TV_XSCROLL) {
+	    if (tvPtr->xScrollCmdPrefix != NULL) {
+		Blt_UpdateScrollbar(tvPtr->interp, tvPtr->xScrollCmdPrefix,
+		    (double)tvPtr->xOffset / tvPtr->worldWidth,
+		    (double)(tvPtr->xOffset + width) / tvPtr->worldWidth);
+	    }
+	}
+	if (tvPtr->flags & TV_YSCROLL) {
+	    if (tvPtr->yScrollCmdPrefix != NULL) {
+		Blt_UpdateScrollbar(tvPtr->interp, tvPtr->yScrollCmdPrefix,
+		    (double)tvPtr->yOffset / tvPtr->worldHeight,
+		    (double)(tvPtr->yOffset + height) / tvPtr->worldHeight);
+	    }
+	}
+	tvPtr->flags &= ~TV_SCROLL;
+    }
+    if (tvPtr->reqWidth == 0) {
+
+	/* 
+	 * The first time through this routine, set the requested
+	 * width to the computed width.  All we want is to
+	 * automatically set the width of the widget, not dynamically
+	 * grow/shrink it as attributes change.
+	 */
+
+	tvPtr->reqWidth = tvPtr->worldWidth + 2 * tvPtr->inset;
+	Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
+    }
+    if (!Tk_IsMapped(tvPtr->tkwin)) {
+	return;
+    }
+
+    drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin), 
+	Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin), 
+	Tk_Depth(tvPtr->tkwin));
+    tvPtr->flags |= TV_VIEWPORT;
+
+    if ((tvPtr->flags & TV_RULE_ACTIVE) &&
+	(tvPtr->resizeColumnPtr != NULL)) {
+	Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
+    }
+    {
+	register TreeViewEntry **p;
+	Tk_3DBorder border, selBorder;
+	int y;
+
+	selBorder = SELECT_BORDER(tvPtr);
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    columnPtr->flags &= ~COLUMN_DIRTY;
+	    if (columnPtr->hidden) {
+		continue;
+	    }
+	    x = SCREENX(tvPtr, columnPtr->worldX);
+	    if ((x + columnPtr->width) < 0) {
+		continue;	/* Don't draw columns before the left edge. */
+	    }
+	    if (x > Tk_Width(tvPtr->tkwin)) {
+		break;		/* Discontinue when a column starts beyond
+				 * the right edge. */
+	    }
+	    /* Clear the column background. */
+	    border = Blt_TreeViewGetStyleBorder(tvPtr, tvPtr->stylePtr);
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
+	       columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
+
+	    if (columnPtr != &tvPtr->treeColumn) {
+		TreeViewValue *valuePtr;
+		TreeViewEntry *entryPtr;
+
+		for (p = tvPtr->visibleArr; *p != NULL; p++) {
+		    entryPtr = *p;
+		    y = SCREENY(tvPtr, entryPtr->worldY);
+
+		    /* Draw the background of the value. */
+		    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+			Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, 
+				x, y - 1, columnPtr->width, 
+				entryPtr->height + 1, tvPtr->selBorderWidth, 
+				tvPtr->selRelief);
+		    }
+		    /* Check if there's a corresponding value in the entry. */
+		    valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
+		    if (valuePtr != NULL) {
+			Blt_TreeViewDrawValue(tvPtr, entryPtr, valuePtr, 
+				drawable, x + columnPtr->pad.side1, y);
+		    }
+		}
+	    } else {
+		if (tvPtr->flatView) {
+		    DrawFlatView(tvPtr, drawable, x);
+		} else {
+		    DrawTreeView(tvPtr, drawable, x);
+		}
+	    }
+	    if (columnPtr->relief != TK_RELIEF_FLAT) {
+		Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, 0, 
+			columnPtr->width, Tk_Height(tvPtr->tkwin), 
+		   columnPtr->borderWidth, columnPtr->relief);
+	    }
+	}
+    }
+    if (tvPtr->flags & TV_SHOW_COLUMN_TITLES) {
+	Blt_TreeViewDrawHeadings(tvPtr, drawable);
+    }
+    Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
+    if ((tvPtr->flags & TV_RULE_NEEDED) &&
+	(tvPtr->resizeColumnPtr != NULL)) {
+	Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
+    }
+    /* Now copy the new view to the window. */
+    XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin), 
+	tvPtr->lineGC, 0, 0, Tk_Width(tvPtr->tkwin), 
+	Tk_Height(tvPtr->tkwin), 0, 0);
+    Tk_FreePixmap(tvPtr->display, drawable);
+    tvPtr->flags &= ~TV_VIEWPORT;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSelectCmdProc --
+ *
+ *      Invoked at the next idle point whenever the current
+ *      selection changes.  Executes some application-specific code
+ *      in the -selectcommand option.  This provides a way for
+ *      applications to handle selection changes.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSelectCmdProc(ClientData clientData) 
+{
+    TreeView *tvPtr = clientData;
+
+    Tcl_Preserve(tvPtr);
+    if (tvPtr->selectCmd != NULL) {
+	tvPtr->flags &= ~TV_SELECT_PENDING;
+	if (Tcl_GlobalEval(tvPtr->interp, tvPtr->selectCmd) != TCL_OK) {
+	    Tcl_BackgroundError(tvPtr->interp);
+	}
+    }
+    Tcl_Release(tvPtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeViewObjCmd --
+ *
+ * 	This procedure is invoked to process the Tcl command that
+ * 	corresponds to a widget managed by this module. See the user
+ * 	documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+TreeViewObjCmd(clientData, interp, objc, objv)
+    ClientData clientData;	/* Main window associated with interpreter. */
+    Tcl_Interp *interp;		/* Current interpreter. */
+    int objc;			/* Number of arguments. */
+    Tcl_Obj *CONST *objv;	/* Argument strings. */
+{
+    Tcl_CmdInfo cmdInfo;
+    Tcl_Obj *initObjv[2];
+    TreeView *tvPtr;
+    char *className;
+    char *string;
+
+    string = Tcl_GetString(objv[0]);
+    if (objc < 2) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", string, 
+		" pathName ?option value?...\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    className = (string[0] == 'h') ? "Hiertable" : "TreeView";
+    tvPtr = CreateTreeView(interp, objv[1], className);
+    if (tvPtr == NULL) {
+	goto error;
+    }
+    /*
+     * Invoke a procedure to initialize various bindings on treeview
+     * entries.  If the procedure doesn't already exist, source it
+     * from "$blt_library/treeview.tcl".  We deferred sourcing the
+     * file until now so that the variable $blt_library could be set
+     * within a script.
+     */
+    if (!Tcl_GetCommandInfo(interp, "blt::tv::Initialize", &cmdInfo)) {
+	char cmd[200];
+	sprintf(cmd, "set className %s\n\
+source [file join $blt_library treeview.tcl]\n\
+unset className\n", className);
+	if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
+	    char info[200];
+
+	    sprintf(info, "\n    (while loading bindings for %.50s)", 
+		    Tcl_GetString(objv[0]));
+	    Tcl_AddErrorInfo(interp, info);
+	    goto error;
+	}
+    }
+    /* 
+     * Initialize the widget's configuration options here. The options
+     * need to be set first, so that entry, column, and style
+     * components can use them for their own GCs.
+     */
+    bltTreeViewIconsOption.clientData = tvPtr;
+    bltTreeViewTreeOption.clientData = tvPtr;
+    if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs, 
+	objc - 2, objv + 2, (char *)tvPtr, 0) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, "button", "Button",
+	 bltTreeViewButtonSpecs, 0, (Tcl_Obj **)NULL, (char *)tvPtr, 0) 
+	!= TCL_OK) {
+	goto error;
+    }
+
+    /* 
+     * Rebuild the widget's GC and other resources that are predicated
+     * by the widget's configuration options.  Do the same for the
+     * default column.
+     */
+    if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
+	goto error;
+    }
+    Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
+    Blt_TreeViewUpdateStyleGCs(tvPtr, tvPtr->stylePtr);
+
+    /*
+     * Invoke a procedure to initialize various bindings on treeview
+     * entries.  If the procedure doesn't already exist, source it
+     * from "$blt_library/treeview.tcl".  We deferred sourcing the
+     * file until now so that the variable $blt_library could be set
+     * within a script.
+     */
+    initObjv[0] = Tcl_NewStringObj("blt::tv::Initialize", -1);
+    initObjv[1] = objv[1];
+    if (Tcl_EvalObjv(interp, 2, initObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
+	goto error;
+    }
+    Tcl_DecrRefCount(initObjv[0]);
+    Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1));
+    return TCL_OK;
+  error:
+    Tk_DestroyWindow(tvPtr->tkwin);
+    return TCL_ERROR;
+}
+
+int
+Blt_TreeViewInit(Tcl_Interp *interp)
+{
+    static Blt_ObjCmdSpec cmdSpec[] = { 
+	{ "treeview", TreeViewObjCmd, },
+	{ "hiertable", TreeViewObjCmd, }
+    };
+
+    if (Blt_InitObjCmd(interp, "blt", cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    if (Blt_InitObjCmd(interp, "blt", cmdSpec + 1) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+#endif /* NO_TREEVIEW */
Index: trunk/kitgen/8.x/blt/generic/bltTreeView.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeView.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeView.h	(revision 175)
@@ -0,0 +1,1114 @@
+
+/*
+ * bltTreeView.h --
+ *
+ *	This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ *   1.  "open" operation should change scroll offset so that as many
+ *	 new entries (up to half a screen) can be seen.
+ *   2.  "open" needs to adjust the scrolloffset so that the same entry
+ *	 is seen at the same place.
+ */
+
+#ifndef BLT_TREEVIEW_H
+#define BLT_TREEVIEW_H
+
+#include "bltImage.h"
+#include "bltHash.h"
+#include "bltChain.h"
+#include "bltTree.h"
+#include "bltTile.h"
+#include "bltBind.h"
+#include "bltObjConfig.h"
+
+#define ITEM_ENTRY		(ClientData)0
+#define ITEM_ENTRY_BUTTON	(ClientData)1
+#define ITEM_COLUMN_TITLE	(ClientData)2
+#define ITEM_COLUMN_RULE	(ClientData)3
+#define ITEM_STYLE		(ClientData)0x10004
+
+#if HAVE_UTF
+#else
+#define Tcl_NumUtfChars(s,n)	(((n) == -1) ? strlen((s)) : (n))
+#define Tcl_UtfAtIndex(s,i)	((s) + (i))
+#endif
+
+#define ODD(x)			((x) | 0x01)
+
+#define END			(-1)
+#define SEPARATOR_LIST		((char *)NULL)
+#define SEPARATOR_NONE		((char *)-1)
+
+#define SEARCH_Y		1
+
+typedef char *UID;
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+#define UCHAR(c)	((unsigned char) (c))
+
+#define TOGGLE(x, mask) (((x) & (mask)) ? ((x) & ~(mask)) : ((x) | (mask)))
+
+
+#define SCREENX(h, wx)	((wx) - (h)->xOffset + (h)->inset)
+#define SCREENY(h, wy)	((wy) - (h)->yOffset + (h)->inset + (h)->titleHeight)
+
+#define WORLDX(h, sx)	((sx) - (h)->inset + (h)->xOffset)
+#define WORLDY(h, sy)	((sy) - ((h)->inset + (h)->titleHeight) + (h)->yOffset)
+
+#define VPORTWIDTH(h)	(Tk_Width((h)->tkwin) - 2 * (h)->inset)
+#define VPORTHEIGHT(h) \
+	(Tk_Height((h)->tkwin) - (h)->titleHeight - 2 * (h)->inset)
+
+#define ICONWIDTH(d)	(tvPtr->levelInfo[(d)].iconWidth)
+#define LEVELX(d)	(tvPtr->levelInfo[(d)].x)
+
+#define DEPTH(h, n)	\
+		(((h)->flatView) ? 0 : Blt_TreeNodeDepth((h)->tree, (n)))
+
+#define SELECT_FG(t)	\
+   (((((t)->flags & TV_FOCUS)) || ((t)->selOutFocusFgColor == NULL)) \
+	? (t)->selInFocusFgColor : (t)->selOutFocusFgColor)
+#define SELECT_BORDER(t)	\
+   (((((t)->flags & TV_FOCUS)) || ((t)->selOutFocusBorder == NULL)) \
+	? (t)->selInFocusBorder : (t)->selOutFocusBorder)
+
+#define SELECT_MODE_SINGLE	(1<<0)
+#define SELECT_MODE_MULTIPLE	(1<<1)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ *  Internal treeview widget flags:
+ *
+ *	TV_LAYOUT	The layout of the hierarchy needs to be recomputed.
+ *
+ *	TV_REDRAW	A redraw request is pending for the widget.
+ *
+ *	TV_XSCROLL	X-scroll request is pending.
+ *
+ *	TV_YSCROLL	Y-scroll request is pending.
+ *
+ *	TV_SCROLL	Both X-scroll and  Y-scroll requests are pending.
+ *
+ *	TV_FOCUS	The widget is receiving keyboard events.
+ *			Draw the focus highlight border around the widget.
+ *
+ *	TV_DIRTY	The hierarchy has changed. It may invalidate
+ *			the locations and pointers to entries.  The widget 
+ *			will need to recompute its layout.
+ *
+ *	TV_RESORT	The tree has changed such that the view needs to
+ *			be resorted.  This can happen when an entry is
+ *			open or closed, it's label changes, a column value
+ *			changes, etc.
+ *
+ *	TV_BORDERS      The borders of the widget (highlight ring and
+ *			3-D border) need to be redrawn.
+ *
+ *	TV_VIEWPORT     Indicates that the viewport has changed in some
+ *			way: the size of the viewport, the location of 
+ *			the viewport, or the contents of the viewport.
+ *
+ */
+
+#define TV_LAYOUT	(1<<0)
+#define TV_REDRAW	(1<<1)
+#define TV_XSCROLL	(1<<2)
+#define TV_YSCROLL	(1<<3)
+#define TV_SCROLL	(TV_XSCROLL | TV_YSCROLL)
+#define TV_FOCUS	(1<<4)
+#define TV_DIRTY	(1<<5)
+#define TV_UPDATE	(1<<6)
+#define TV_RESORT	(1<<7)
+#define TV_SORTED	(1<<8)
+#define TV_SORT_PENDING (1<<9)
+#define TV_BORDERS	(1<<10)
+#define TV_VIEWPORT	(1<<11)
+
+/*
+ *  Rule related flags: Rules are XOR-ed lines. We need to track whether
+ *			they have been drawn or not. 
+ *
+ *	TV_RULE_ACTIVE  Indicates that a rule is currently being drawn
+ *			for a column.
+ *			
+ *
+ *	TV_RULE_NEEDED  Indicates that a rule is needed (but not yet
+ *			drawn) for a column.
+ */
+
+#define TV_RULE_ACTIVE	(1<<15)
+#define TV_RULE_NEEDED	(1<<16)
+
+/*
+ *  Selection related flags:
+ *
+ *	TV_SELECT_EXPORT	Export the selection to X11.
+ *
+ *	TV_SELECT_PENDING	A "selection" command idle task is pending.
+ *
+ *	TV_SELECT_CLEAR		Clear selection flag of entry.
+ *
+ *	TV_SELECT_SET		Set selection flag of entry.
+ *
+ *	TV_SELECT_TOGGLE	Toggle selection flag of entry.
+ *
+ *	TV_SELECT_MASK		Mask of selection set/clear/toggle flags.
+ *
+ *	TV_SELECT_SORTED	Indicates if the entries in the selection 
+ *				should be sorted or displayed in the order 
+ *				they were selected.
+ *
+ */
+#define TV_SELECT_CLEAR		(1<<16)
+#define TV_SELECT_EXPORT	(1<<17) 
+#define TV_SELECT_PENDING	(1<<18)
+#define TV_SELECT_SET		(1<<19)
+#define TV_SELECT_TOGGLE	(TV_SELECT_SET | TV_SELECT_CLEAR)
+#define TV_SELECT_MASK		(TV_SELECT_SET | TV_SELECT_CLEAR)
+#define TV_SELECT_SORTED	(1<<20)
+
+/*
+ *  Miscellaneous flags:
+ *
+ *	TV_ALLOW_DUPLICATES	When inserting new entries, create 
+ *			        duplicate entries.
+ *
+ *	TV_FILL_ANCESTORS	Automatically create ancestor entries 
+ *				as needed when inserting a new entry.
+ *
+ *	TV_HIDE_ROOT		Don't display the root entry.
+ *
+ *	TV_HIDE_LEAVES		Don't display entries that are leaves.
+ *
+ *	TV_SHOW_COLUMN_TITLES	Indicates whether to draw titles over each
+ *				column.
+ *
+ */
+#define TV_ALLOW_DUPLICATES	(1<<21)
+#define TV_FILL_ANCESTORS	(1<<22)
+#define TV_HIDE_ROOT		(1<<23) 
+#define TV_HIDE_LEAVES		(1<<24) 
+#define TV_SHOW_COLUMN_TITLES	(1<<25)
+#define TV_SORT_AUTO		(1<<26)
+#define TV_NEW_TAGS		(1<<27)
+#define TV_HIGHLIGHT_CELLS	(1<<28)
+
+#define TV_ITEM_COLUMN	1
+#define TV_ITEM_RULE	2
+
+/*
+ * -------------------------------------------------------------------------
+ *
+ *  Internal entry flags:
+ *
+ *	ENTRY_HAS_BUTTON	Indicates that a button needs to be
+ *				drawn for this entry.
+ *
+ *	ENTRY_CLOSED		Indicates that the entry is closed and
+ *				its subentries are not displayed.
+ *
+ *	ENTRY_HIDDEN		Indicates that the entry is hidden (i.e.
+ *				can not be viewed by opening or scrolling).
+ *
+ *	BUTTON_AUTO
+ *	BUTTON_SHOW
+ *	BUTTON_MASK
+ *
+ * -------------------------------------------------------------------------
+ */
+#define ENTRY_CLOSED		(1<<0)
+#define ENTRY_HIDDEN		(1<<1)
+#define ENTRY_NOT_LEAF		(1<<2)
+#define ENTRY_MASK		(ENTRY_CLOSED | ENTRY_HIDDEN)
+
+#define ENTRY_HAS_BUTTON	(1<<3)
+#define ENTRY_ICON		(1<<4)
+#define ENTRY_REDRAW		(1<<5)
+#define ENTRY_LAYOUT_PENDING	(1<<6)
+#define ENTRY_DATA_CHANGED	(1<<7)
+#define ENTRY_DIRTY		(ENTRY_DATA_CHANGED | ENTRY_LAYOUT_PENDING)
+
+#define BUTTON_AUTO		(1<<8)
+#define BUTTON_SHOW		(1<<9)
+#define BUTTON_MASK		(BUTTON_AUTO | BUTTON_SHOW)
+
+#define COLUMN_RULE_PICKED	(1<<1)
+#define COLUMN_DIRTY		(1<<2)
+
+#define STYLE_TEXTBOX		(0)
+#define STYLE_COMBOBOX		(1)
+#define STYLE_CHECKBOX		(2)
+#define STYLE_TYPE		0x3
+
+#define STYLE_LAYOUT		(1<<3)
+#define STYLE_DIRTY		(1<<4)
+#define STYLE_HIGHLIGHT		(1<<5)
+#define STYLE_USER		(1<<6)
+
+typedef struct TreeViewColumnStruct TreeViewColumn;
+typedef struct TreeViewComboboxStruct TreeViewCombobox;
+typedef struct TreeViewEntryStruct TreeViewEntry;
+typedef struct TreeViewStruct TreeView;
+typedef struct TreeViewStyleClassStruct TreeViewStyleClass;
+typedef struct TreeViewStyleStruct TreeViewStyle;
+
+typedef int (TreeViewCompareProc) _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+	char *pattern));
+
+typedef TreeViewEntry *(TreeViewIterProc) _ANSI_ARGS_((TreeViewEntry *entryPtr,
+	unsigned int mask));
+
+typedef struct {
+    int tagType;
+    TreeView *tvPtr;
+    Blt_HashSearch cursor;
+    TreeViewEntry *entryPtr;
+} TreeViewTagInfo;
+
+/*
+ * TreeViewIcon --
+ *
+ *	Since instances of the same Tk image can be displayed in
+ *	different windows with possibly different color palettes, Tk
+ *	internally stores each instance in a linked list.  But if
+ *	the instances are used in the same widget and therefore use
+ *	the same color palette, this adds a lot of overhead,
+ *	especially when deleting instances from the linked list.
+ *
+ *	For the treeview widget, we never need more than a single
+ *	instance of an image, regardless of how many times it's used.
+ *	Cache the image, maintaining a reference count for each
+ *	image used in the widget.  It's likely that the treeview
+ *	widget will use many instances of the same image (for example
+ *	the open/close icons).
+ */
+
+typedef struct TreeViewIconStruct {
+    Tk_Image tkImage;		/* The Tk image being cached. */
+
+    int refCount;		/* Reference count for this image. */
+
+    short int width, height;	/* Dimensions of the cached image. */
+
+    Blt_HashEntry *hashPtr;	/* Hash table pointer to the image. */
+
+} *TreeViewIcon;
+
+#define TreeViewIconHeight(icon)	((icon)->height)
+#define TreeViewIconWidth(icon)	((icon)->width)
+#define TreeViewIconBits(icon)	((icon)->tkImage)
+
+/*
+ * TreeViewColumn --
+ *
+ *	A column describes how to display a field of data in the tree.
+ *	It may display a title that you can bind to. It may display a
+ *	rule for resizing the column.  Columns may be hidden, and have
+ *	attributes (foreground color, background color, font, etc)
+ *	that override those designated globally for the treeview
+ *	widget.
+ */
+struct TreeViewColumnStruct {
+    int type;			/* Always TV_COLUMN */
+    Blt_TreeKey key;		/* Data cell identifier for current tree. */
+    int position;		/* Position of column in list.  Used
+				 * to indicate the first and last
+				 * columns. */
+    UID tagsUid;		/* List of binding tags for this
+				 * entry.  UID, not a string, because
+				 * in the typical case most columns
+				 * will have the same bindtags. */
+
+    TreeView *tvPtr;
+    unsigned int flags;
+
+    /* Title-related information */
+    char *title;		/* Text displayed in column heading as its
+				 * title. By default, this is the same as 
+				 * the data cell name. */
+    Tk_Font titleFont;		/* Font to draw title in. */
+    Shadow titleShadow;
+
+    XColor *titleFgColor;	/* Foreground color of text displayed in 
+				 * the heading */
+    Tk_3DBorder titleBorder;	/* Background color of the column's heading. */
+
+    GC titleGC;
+
+    XColor *activeTitleFgColor;	/* Foreground color of text heading when 
+				 * the column is activated.*/
+    Tk_3DBorder activeTitleBorder;	
+
+    int titleBorderWidth;
+    int titleRelief;
+
+    GC activeTitleGC;
+
+    TextLayout *titleTextPtr;
+    short int titleWidth, titleHeight;
+
+    TreeViewIcon titleIcon;	/* Icon displayed in column heading */
+    char *titleCmd;		/* Tcl script to be executed by the 
+				 * column's "invoke" operation. */
+
+    char *sortCmd;		/* Tcl script used to compare two
+				 * columns. */
+
+    /* General information. */
+    int hidden;			/* Indicates if the column is
+				 * displayed */
+    int state;			/* Indicates if column title can
+				 * invoked. */
+    int editable;		/* Indicates if column can be
+				 * edited. */
+
+    int max;			/* Maximum space allowed for column. */
+    int reqMin, reqMax;		/* Requested bounds on the width of
+				 * column.  Does not include any
+				 * padding or the borderwidth of
+				 * column.  If non-zero, overrides the
+				 * computed width of the column. */
+
+    int reqWidth;		/* User-requested width of
+				 * column. Does not include any
+				 * padding or the borderwidth of
+				 * column.  If non-zero, overrides the
+				 * computed column width. */
+
+    int maxWidth;		/* Width of the widest entry in the
+				 * column. */
+
+    int worldX;			/* Starting world x-coordinate of the
+				 * column. */
+
+    double weight;		/* Growth factor for column.  Zero
+				 * indicates that the column can not
+				 * be resized. */
+
+    int width;			/* Computed width of column. */
+
+    TreeViewStyle *stylePtr;	/* Default style for column. */
+
+    Tk_3DBorder border;		/* Background color of column. */
+    int borderWidth;		/* Border width of the column. */
+    int relief;			/* Relief of the column. */
+    Blt_Pad pad;		/* Horizontal padding on either side
+				 * of the column. */
+
+    Tk_Justify justify;		/* Indicates how the text or icon is
+				 * justified within the column. */
+
+    Blt_ChainLink *linkPtr;
+    
+    int ruleLineWidth;
+    Blt_Dashes ruleDashes;
+    GC ruleGC;
+};
+
+
+struct TreeViewStyleStruct {
+    int refCount;		/* Usage reference count.  A reference 
+				 * count of zero indicates that the 
+				 * style may be freed. */
+    unsigned int flags;		/* Bit field containing both the style
+				 * type and various flags. */
+    char *name;			/* Instance name. */
+    TreeViewStyleClass *classPtr; 
+				/* Contains class-specific information such
+				 * as configuration specifications and 
+				 * configure, draw, etc. routines. */
+    Blt_HashEntry *hashPtr;	/* If non-NULL, points to the hash
+				 * table entry for the style.  A style
+				 * that's been deleted, but still in
+				 * use (non-zero reference count) will
+				 * have no hash table entry.
+				 */
+    /* General style fields. */
+    Tk_Cursor cursor;		/* X Cursor */
+
+    TreeViewIcon icon;		/* If non-NULL, is a Tk_Image to be drawn
+				 * in the cell. */
+    int gap;			/* # pixels gap between icon and text. */
+    Tk_Font font;
+    XColor *fgColor;		/* Normal foreground color of cell. */
+    Tk_3DBorder border;		/* Normal background color of cell. */
+    XColor *highlightFgColor;	/* Foreground color of cell when
+				 * highlighted. */
+    Tk_3DBorder highlightBorder;/* Background color of cell when
+				 * highlighted. */
+    XColor *activeFgColor;	/* Foreground color of cell when active. */
+    Tk_3DBorder activeBorder;	/* Background color of cell when active. */
+
+};
+
+typedef struct TreeViewValueStruct {
+    TreeViewColumn *columnPtr;	/* Column in which the value is located. */
+    short int width, height;	/* Dimensions of value. */
+    TreeViewStyle *stylePtr;	/* Style information for cell
+				 * displaying value. */
+    char *string;		/* Raw text string. */
+    TextLayout *textPtr;	/* Processes string to be displayed .*/
+    struct TreeViewValueStruct *nextPtr;
+} TreeViewValue;
+    
+typedef void (StyleConfigProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+typedef void (StyleDrawProc) _ANSI_ARGS_((TreeView *tvPtr, Drawable drawable, 
+	TreeViewEntry *entryPtr, TreeViewValue *valuePtr, 
+	TreeViewStyle *stylePtr, int x, int y));
+typedef int (StyleEditProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, TreeViewValue *valuePtr, 
+	TreeViewStyle *stylePtr));
+typedef void (StyleFreeProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+typedef void (StyleMeasureProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr, TreeViewValue *valuePtr));
+typedef int (StylePickProc) _ANSI_ARGS_((TreeViewEntry *entryPtr, 
+	TreeViewValue *valuePtr, TreeViewStyle *stylePtr, int worldX, 
+	int worldY));
+
+struct TreeViewStyleClassStruct {
+    char *className;		/* Class name of the style */
+    Blt_ConfigSpec *specsPtr;	/* Style configuration specifications */
+    StyleConfigProc *configProc;/* Sets the GCs for style. */
+    StyleMeasureProc *measProc;	/* Measures the area needed for the value
+				 * with this style. */
+    StyleDrawProc *drawProc;	/* Draw the value in it's style. */
+    StylePickProc *pickProc;	/* Routine to pick the style's button. 
+				 * Indicates if the mouse pointer is over
+				 * the style's button (if it has one). */
+    StyleEditProc *editProc;	/* Routine to edit the style's value. */
+    StyleFreeProc *freeProc;	/* Routine to free the style's resources. */
+};
+
+/*
+ * TreeViewEntry --
+ *
+ *	Contains data-specific information how to represent the data
+ *	of a node of the hierarchy.
+ *
+ */
+struct TreeViewEntryStruct {
+    Blt_TreeNode node;		/* Node containing entry */
+    int worldX, worldY;		/* X-Y position in world coordinates
+				 * where the entry is positioned. */
+
+    short int width, height;	/* Dimensions of the entry. This
+				 * includes the size of its
+				 * columns. */
+
+    int reqHeight;		/* Requested height of the entry. 
+				 * Overrides computed height. */
+
+    int vertLineLength;		/* Length of the vertical line
+				 * segment. */
+
+    int lineHeight;		/* Height of first line of text. */
+    unsigned int flags;		/* Flags for this entry. For the
+				 * definitions of the various bit
+				 * fields see below. */
+
+    UID tagsUid;		/* List of binding tags for this
+				 * entry.  UID, not a string, because
+				 * in the typical case most entries
+				 * will have the same bindtags. */
+    TreeView *tvPtr;
+
+    UID openCmd, closeCmd;	/* Tcl commands to invoke when entries
+				 * are opened or closed. They override
+				 * those specified globally. */
+    /*
+     * Button information:
+     */
+    short int buttonX, buttonY; /* X-Y coordinate offsets from to
+				 * upper left corner of the entry to
+				 * the upper-left corner of the
+				 * button.  Used to pick the
+				 * button quickly */
+
+    TreeViewIcon *icons;	/* Tk images displayed for the entry.
+				 * The first image is the icon
+				 * displayed to the left of the
+				 * entry's label. The second is icon
+				 * displayed when entry is "open". */
+
+    TreeViewIcon *activeIcons;	/* Tk images displayed for the entry.
+				 * The first image is the icon
+				 * displayed to the left of the
+				 * entry's label. The second is icon
+				 * displayed when entry is "open". */
+
+    short int iconWidth;
+    short int iconHeight;	/* Maximum dimensions for icons and
+				 * buttons for this entry. This is
+				 * used to align the button, icon, and
+				 * text. */
+    /*
+     * Label information:
+     */
+    TextLayout *textPtr;
+
+    short int labelWidth;
+    short int labelHeight;
+
+    UID labelUid;		/* Text displayed right of the icon. */
+
+    Tk_Font font;		/* Font of label. Overrides global
+				 * font specification. */
+    char *fullName;
+
+    int flatIndex;
+
+    Tcl_Obj *dataObjPtr;	/* pre-fetched data for sorting */
+
+    XColor *color;		/* Color of label. Overrides default
+				 * text color specification. */
+    GC gc;
+
+    Shadow shadow;
+
+    TreeViewValue *values;	/* List of column-related information
+				 * for each data value in the node.
+				 * Non-NULL only if there are value
+				 * entries. */
+};
+
+/*
+ * TreeViewButton --
+ *
+ *	A button is the open/close indicator at the far left of the
+ *	entry.  It is displayed as a plus or minus in a solid
+ *	colored box with optionally an border. It has both "active"
+ *	and "normal" colors.
+ */
+typedef struct {
+    XColor *fgColor;		/* Foreground color. */
+
+    Tk_3DBorder border;		/* Background color. */
+
+    XColor *activeFgColor;	/* Active foreground color. */
+
+    Tk_3DBorder activeBorder;	/* Active background color. */
+
+    GC normalGC;
+    GC activeGC;
+
+    int reqSize;
+
+    int borderWidth;
+
+    int openRelief, closeRelief;
+
+    int width, height;
+
+    TreeViewIcon *icons;
+
+} TreeViewButton;
+
+/*
+ * LevelInfo --
+ *
+ */
+typedef struct {
+    int x;
+    int iconWidth;
+    int labelWidth;
+} LevelInfo;
+
+/*
+ * TreeView --
+ *
+ *	A TreeView is a widget that displays an hierarchical table 
+ *	of one or more entries.
+ *
+ *	Entries are positioned in "world" coordinates, referring to
+ *	the virtual treeview.  Coordinate 0,0 is the upper-left corner
+ *	of the root entry and the bottom is the end of the last entry.
+ *	The widget's Tk window acts as view port into this virtual
+ *	space. The treeview's xOffset and yOffset fields specify the
+ *	location of the view port in the virtual world.  Scrolling the
+ *	viewport is therefore simply changing the xOffset and/or
+ *	yOffset fields and redrawing.
+ *
+ *	Note that world coordinates are integers, not signed short
+ *	integers like X11 screen coordinates.  It's very easy to
+ *	create a hierarchy taller than 0x7FFF pixels.
+ */
+struct TreeViewStruct {
+    Tcl_Interp *interp;
+
+    Tcl_Command cmdToken;	/* Token for widget's Tcl command. */
+
+    Blt_Tree tree;		/* Token holding internal tree. */
+
+    Blt_HashEntry *hashPtr;
+
+    /* TreeView specific fields. */ 
+
+    Tk_Window tkwin;		/* Window that embodies the widget.
+                                 * NULL means that the window has been
+                                 * destroyed but the data structures
+                                 * haven't yet been cleaned up.*/
+
+    Display *display;		/* Display containing widget; needed,
+                                 * among other things, to release
+                                 * resources after tkwin has already
+                                 * gone away. */
+
+    Blt_HashTable entryTable;	/* Table of entry information, keyed by
+				 * the node pointer. */
+
+    Blt_HashTable columnTable;	/* Table of column information. */
+    Blt_Chain *colChainPtr;	/* Chain of columns. Same as the hash
+				 * table above but maintains the order
+				 * in which columns are displayed. */
+
+    unsigned int flags;		/* For bitfield definitions, see below */
+
+    int inset;			/* Total width of all borders,
+				 * including traversal highlight and
+				 * 3-D border.  Indicates how much
+				 * interior stuff must be offset from
+				 * outside edges to leave room for
+				 * borders. */
+
+    Tk_Font font;
+    XColor *fgColor;
+
+    Tk_3DBorder border;		/* 3D border surrounding the window
+				 * (viewport). */
+
+    int borderWidth;		/* Width of 3D border. */
+
+    int relief;			/* 3D border relief. */
+
+
+    int highlightWidth;		/* Width in pixels of highlight to
+				 * draw around widget when it has the
+				 * focus.  <= 0 means don't draw a
+				 * highlight. */
+
+    XColor *highlightBgColor;	/* Color for drawing traversal
+				 * highlight area when highlight is
+				 * off. */
+
+    XColor *highlightColor;	/* Color for drawing traversal highlight. */
+
+    char *pathSep;		/* Pathname separators */
+
+    char *trimLeft;		/* Leading characters to trim from
+				 * pathnames */
+
+    /*
+     * Entries are connected by horizontal and vertical lines. They
+     * may be drawn dashed or solid.
+     */
+    int lineWidth;		/* Width of lines connecting entries */
+
+    int dashes;			/* Dash on-off value. */
+
+    XColor *lineColor;		/* Color of connecting lines. */
+
+    /*
+     * Button Information:
+     *
+     * The button is the open/close indicator at the far left of the
+     * entry.  It is usually displayed as a plus or minus in a solid
+     * colored box with optionally an border. It has both "active" and
+     * "normal" colors.
+     */
+    TreeViewButton button;
+
+    /*
+     * Selection Information:
+     *
+     * The selection is the rectangle that contains a selected entry.
+     * There may be many selected entries.  It is displayed as a solid
+     * colored box with optionally a 3D border.
+     */
+    int selRelief;		/* Relief of selected items. Currently
+				 * is always raised. */
+
+    int selBorderWidth;		/* Border width of a selected entry.*/
+
+    XColor *selInFocusFgColor;	/* Text color of a selected entry. */
+    XColor *selOutFocusFgColor;
+
+    Tk_3DBorder selInFocusBorder;
+    Tk_3DBorder selOutFocusBorder;
+
+
+    TreeViewEntry *selAnchorPtr; /* Fixed end of selection (i.e. entry
+				  * at which selection was started.) */
+    TreeViewEntry *selMarkPtr;
+    
+    int	selectMode;		/* Selection style: "single" or
+				 * "multiple".  */
+
+    char *selectCmd;		/* Tcl script that's invoked whenever
+				 * the selection changes. */
+
+    Blt_HashTable selectTable;	/* Hash table of currently selected
+				 * entries. */
+
+    Blt_Chain *selChainPtr;	/* Chain of currently selected
+				 * entries.  Contains the same
+				 * information as the above hash
+				 * table, but maintains the order in
+				 * which entries are selected.
+				 */
+
+    int leader;			/* Number of pixels padding between
+				 * entries. */
+
+    Tk_Cursor cursor;		/* X Cursor */
+
+    Tk_Cursor resizeCursor;	/* Resize Cursor */
+
+    int reqWidth, reqHeight;	/* Requested dimensions of the
+				 * treeview widget's window. */
+
+    GC lineGC;			/* GC for drawing dotted line between
+				 * entries. */
+
+    XColor *focusColor;
+
+    Blt_Dashes focusDashes;	/* Dash on-off value. */
+
+    GC focusGC;			/* Graphics context for the active
+				 * label. */
+
+    Tk_Window comboWin;		
+
+    TreeViewEntry *activePtr;	/* Last active entry. */ 
+
+    TreeViewEntry *focusPtr;	/* Entry that currently has focus. */
+
+    TreeViewEntry *activeButtonPtr; /* Pointer to last active button */
+
+    TreeViewEntry *fromPtr;
+
+    TreeViewValue *activeValuePtr;/* Last active value. */ 
+
+    int xScrollUnits, yScrollUnits; /* # of pixels per scroll unit. */
+
+    /* Command strings to control horizontal and vertical
+     * scrollbars. */
+    char *xScrollCmdPrefix, *yScrollCmdPrefix;
+
+    int scrollMode;		/* Selects mode of scrolling: either
+				 * BLT_SCROLL_MODE_HIERBOX, 
+				 * BLT_SCROLL_MODE_LISTBOX, 
+				 * or BLT_SCROLL_MODE_CANVAS. */
+    /*
+     * Total size of all "open" entries. This represents the range of
+     * world coordinates.
+     */
+    int worldWidth, worldHeight;
+
+    int xOffset, yOffset;	/* Translation between view port and
+				 * world origin. */
+
+    short int minHeight;	/* Minimum entry height. Used to to
+				 * compute what the y-scroll unit
+				 * should be. */
+    short int titleHeight;	/* Height of column titles. */
+
+    LevelInfo *levelInfo;
+
+    /*
+     * Scanning information:
+     */
+    int scanAnchorX, scanAnchorY;
+    /* Scan anchor in screen coordinates. */
+    int scanX, scanY;		/* X-Y world coordinate where the scan
+				 * started. */
+
+    Blt_HashTable iconTable;	/* Table of Tk images */
+
+    Blt_HashTable uidTable;	/* Table of strings. */
+
+    Blt_HashTable styleTable;	/* Table of cell styles. */
+
+    TreeViewEntry *rootPtr;	/* Root entry of tree. */
+
+    TreeViewEntry **visibleArr;	/* Array of visible entries */
+
+    int nVisible;		/* Number of entries in the above array */
+
+    int nEntries;		/* Number of entries in tree. */
+    int treeWidth;		/* Computed width of the tree. */
+
+    int buttonFlags;		/* Global button indicator for all
+				 * entries.  This may be overridden by
+				 * the entry's -button option. */
+
+    char *openCmd, *closeCmd;	/* Tcl commands to invoke when entries
+				 * are opened or closed. */
+
+    TreeViewIcon *icons;	/* Tk images displayed for the entry.
+				 * The first image is the icon
+				 * displayed to the left of the
+				 * entry's label. The second is icon
+				 * displayed when entry is "open". */
+    TreeViewIcon *activeIcons;	/* Tk images displayed for the entry.
+				 * The first image is the icon
+				 * displayed to the left of the
+				 * entry's label. The second is icon
+				 * displayed when entry is "open". */
+    char *takeFocus;
+
+    ClientData clientData;
+
+    Blt_BindTable bindTable;	/* Binding information for entries. */
+
+    Blt_HashTable entryTagTable;
+    Blt_HashTable buttonTagTable;
+    Blt_HashTable columnTagTable;
+    Blt_HashTable styleTagTable;
+
+    TreeViewStyle *stylePtr;	/* Default style for text cells */
+
+    TreeViewColumn treeColumn;
+    
+    TreeViewColumn *activeColumnPtr; 
+    TreeViewColumn *activeTitleColumnPtr; 
+				/* Column title currently active. */
+
+    TreeViewColumn *resizeColumnPtr; 
+				/* Column that is being resized. */
+
+    int depth;
+
+    int flatView;		/* Indicates if the view of the tree
+				 * has been flattened. */
+
+    TreeViewEntry **flatArr;	/* Flattened array of entries. */
+
+    char *sortField;		/* Field to be sorted. */
+
+    int sortType;		/* Type of sorting to be performed. See
+				 * below for valid values. */
+
+    char *sortCmd;		/* Sort command. */
+
+    int sortDecreasing;		/* Indicates entries should be sorted
+				 * in decreasing order. */
+
+    int viewIsDecreasing;	/* Current sorting direction */
+
+    TreeViewColumn *sortColumnPtr;/* Column to use for sorting criteria. */
+
+#ifdef notdef
+    Pixmap drawable;		/* Pixmap used to cache the entries
+				 * displayed.  The pixmap is saved so
+				 * that only selected elements can be
+				 * drawn quicky. */
+
+    short int drawWidth, drawHeight;
+#endif
+    short int ruleAnchor, ruleMark;
+
+    Blt_Pool entryPool;
+    Blt_Pool valuePool;
+};
+
+
+extern UID Blt_TreeViewGetUid _ANSI_ARGS_((TreeView *tvPtr, 
+	CONST char *string));
+extern void Blt_TreeViewFreeUid _ANSI_ARGS_((TreeView *tvPtr, UID uid));
+
+extern void Blt_TreeViewEventuallyRedraw _ANSI_ARGS_((TreeView *tvPtr));
+extern Tcl_ObjCmdProc Blt_TreeViewWidgetInstCmd;
+extern TreeViewEntry *Blt_TreeViewNearestEntry _ANSI_ARGS_((TreeView *tvPtr,
+	int x, int y, int flags));
+extern char *Blt_TreeViewGetFullName _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, int checkEntryLabel, Tcl_DString *dsPtr));
+extern void Blt_TreeViewSelectCmdProc _ANSI_ARGS_((ClientData clientData));
+extern void Blt_TreeViewInsertText _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, char *string, int extra, int insertPos));
+extern void Blt_TreeViewComputeLayout _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewPercentSubst _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, char *command, Tcl_DString *resultPtr));
+extern void Blt_TreeViewDrawButton _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, Drawable drawable, int x, int y));
+extern void Blt_TreeViewDrawValue _ANSI_ARGS_((TreeView *tvPtr,
+    TreeViewEntry *entryPtr, TreeViewValue *valuePtr, Drawable drawable,
+    int x, int y));
+extern void Blt_TreeViewDrawOuterBorders _ANSI_ARGS_((TreeView *tvPtr, 
+	Drawable drawable));
+extern int Blt_TreeViewDrawIcon _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, Drawable drawable, int x, int y));
+extern void Blt_TreeViewDrawHeadings _ANSI_ARGS_((TreeView *tvPtr, 
+	Drawable drawable));
+extern void Blt_TreeViewDrawRule _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewColumn *columnPtr, Drawable drawable));
+
+extern void Blt_TreeViewConfigureButtons _ANSI_ARGS_((TreeView *tvPtr));
+extern int Blt_TreeViewUpdateWidget _ANSI_ARGS_((Tcl_Interp *interp, 
+	TreeView *tvPtr));
+extern int Blt_TreeViewScreenToIndex _ANSI_ARGS_((TreeView *tvPtr, 
+	int x, int y));
+
+extern void Blt_TreeViewFreeIcon _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewIcon icon));
+extern TreeViewIcon Blt_TreeViewGetIcon _ANSI_ARGS_((TreeView *tvPtr,
+	CONST char *iconName));
+extern void Blt_TreeViewAddValue _ANSI_ARGS_((TreeViewEntry *entryPtr, 
+	TreeViewColumn *columnPtr));
+extern int Blt_TreeViewCreateColumn _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewColumn *columnPtr, char *name, char *defaultLabel));
+extern void Blt_TreeViewDestroyValue _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewValue *valuePtr));
+extern TreeViewValue *Blt_TreeViewFindValue _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, TreeViewColumn *columnPtr));
+extern void Blt_TreeViewDestroyColumns _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewAllocateColumnUids _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewFreeColumnUids _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewUpdateColumnGCs _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewColumn *columnPtr));
+extern TreeViewColumn *Blt_TreeViewNearestColumn _ANSI_ARGS_((TreeView *tvPtr,
+	int x, int y, ClientData *contextPtr));
+
+extern void Blt_TreeViewDrawRule _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewColumn *columnPtr, Drawable drawable));
+extern int Blt_TreeViewTextOp _ANSI_ARGS_((TreeView *tvPtr, Tcl_Interp *interp,
+	int objc, Tcl_Obj *CONST *objv));
+extern int Blt_TreeViewCombobox _ANSI_ARGS_((TreeView *tvPtr,
+	TreeViewEntry *entryPtr, TreeViewColumn *columnPtr));
+extern int Blt_TreeViewCreateEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv, int flags));
+extern int Blt_TreeViewConfigureEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, int objc, Tcl_Obj *CONST *objv, int flags));
+extern int Blt_TreeViewOpenEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern int Blt_TreeViewCloseEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewNextEntry _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewPrevEntry _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, unsigned int mask));
+extern int Blt_TreeViewGetEntry _ANSI_ARGS_((TreeView *tvPtr, Tcl_Obj *objPtr, 
+	TreeViewEntry **entryPtrPtr));
+extern int Blt_TreeViewEntryIsHidden _ANSI_ARGS_((TreeViewEntry *entryPtr));
+extern TreeViewEntry *Blt_TreeViewNextSibling _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewPrevSibling _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewFirstChild _ANSI_ARGS_((
+	TreeViewEntry *parentPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewLastChild _ANSI_ARGS_((
+	TreeViewEntry *entryPtr, unsigned int mask));
+extern TreeViewEntry *Blt_TreeViewParentEntry _ANSI_ARGS_((
+	TreeViewEntry *entryPtr));
+
+typedef int (TreeViewApplyProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+
+extern int Blt_TreeViewApply _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr, TreeViewApplyProc *proc, unsigned int mask));
+
+extern int Blt_TreeViewColumnOp _ANSI_ARGS_((TreeView *tvPtr, 
+	Tcl_Interp *interp, int objc, Tcl_Obj *CONST *objv));
+extern int Blt_TreeViewSortOp _ANSI_ARGS_((TreeView *tvPtr, Tcl_Interp *interp,
+	int objc, Tcl_Obj *CONST *objv));
+extern int Blt_TreeViewGetColumn _ANSI_ARGS_((Tcl_Interp *interp, 
+	TreeView *tvPtr, Tcl_Obj *objPtr, TreeViewColumn **columnPtrPtr));
+
+extern void Blt_TreeViewSortFlatView _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewSortTreeView _ANSI_ARGS_((TreeView *tvPtr));
+
+extern int Blt_TreeViewEntryIsSelected _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern void Blt_TreeViewSelectEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern void Blt_TreeViewDeselectEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern void Blt_TreeViewPruneSelection _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern void Blt_TreeViewClearSelection _ANSI_ARGS_((TreeView *tvPtr));
+extern void Blt_TreeViewClearTags _ANSI_ARGS_((TreeView *tvPtr,
+	TreeViewEntry *entryPtr));
+extern int Blt_TreeViewFindTaggedEntries _ANSI_ARGS_((TreeView *tvPtr, 
+	Tcl_Obj *objPtr, TreeViewTagInfo *infoPtr));
+extern TreeViewEntry *Blt_TreeViewFirstTaggedEntry _ANSI_ARGS_((
+	TreeViewTagInfo *infoPtr)); 
+extern TreeViewEntry *Blt_TreeViewNextTaggedEntry _ANSI_ARGS_((
+	TreeViewTagInfo *infoPtr)); 
+extern ClientData Blt_TreeViewButtonTag _ANSI_ARGS_((TreeView *tvPtr, 
+    CONST char *string));
+extern ClientData Blt_TreeViewEntryTag _ANSI_ARGS_((TreeView *tvPtr, 
+    CONST char *string));
+extern ClientData Blt_TreeViewColumnTag _ANSI_ARGS_((TreeView *tvPtr, 
+    CONST char *string));
+extern void Blt_TreeViewGetTags _ANSI_ARGS_((Tcl_Interp *interp, 
+	TreeView *tvPtr, TreeViewEntry *entryPtr, Blt_List list));
+extern void Blt_TreeViewTraceColumn _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewColumn *columnPtr));
+extern TreeViewIcon Blt_TreeViewGetEntryIcon _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewEntry *entryPtr));
+extern void Blt_TreeViewSetStyleIcon _ANSI_ARGS_((TreeView *tvPtr,
+	TreeViewStyle *stylePtr, TreeViewIcon icon));
+extern int Blt_TreeViewGetStyle _ANSI_ARGS_((Tcl_Interp *interp, 
+	TreeView *tvPtr, char *styleName, TreeViewStyle **stylePtrPtr));
+extern void Blt_TreeViewFreeStyle _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+extern TreeViewStyle *Blt_TreeViewCreateStyle _ANSI_ARGS_((Tcl_Interp *interp,
+	TreeView *tvPtr, int type, char *styleName));
+extern void Blt_TreeViewUpdateStyleGCs _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+extern Tk_3DBorder Blt_TreeViewGetStyleBorder _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+extern GC Blt_TreeViewGetStyleGC _ANSI_ARGS_((TreeViewStyle *stylePtr));
+extern Tk_Font Blt_TreeViewGetStyleFont _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+extern XColor *Blt_TreeViewGetStyleFg _ANSI_ARGS_((TreeView *tvPtr, 
+	TreeViewStyle *stylePtr));
+extern TreeViewEntry *Blt_NodeToEntry _ANSI_ARGS_((TreeView *tvPtr, 
+	Blt_TreeNode node));
+extern int Blt_TreeViewStyleOp _ANSI_ARGS_((TreeView *tvPtr, Tcl_Interp *interp,
+	int objc, Tcl_Obj *CONST *objv));
+
+#define CHOOSE(default, override)	\
+	(((override) == NULL) ? (default) : (override))
+
+#define GETLABEL(e)		\
+	(((e)->labelUid != NULL) ? (e)->labelUid : Blt_TreeNodeLabel((e)->node))
+
+#define Blt_TreeViewGetData(entryPtr, key, objPtrPtr) \
+	Blt_TreeGetValueByKey((Tcl_Interp *)NULL, (entryPtr)->tvPtr->tree, \
+	      (entryPtr)->node, key, objPtrPtr)
+
+#endif /* BLT_TREEVIEW_H */
Index: trunk/kitgen/8.x/blt/generic/bltTreeViewCmd.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeViewCmd.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeViewCmd.c	(revision 175)
@@ -0,0 +1,4923 @@
+
+/*
+ * bltTreeViewCmd.c --
+ *
+ *	This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ *   1.  "open" operation should change scroll offset so that as many
+ *	 new entries (up to half a screen) can be seen.
+ *   2.  "open" needs to adjust the scrolloffset so that the same entry
+ *	 is seen at the same place.
+ */
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include "bltList.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define CLAMP(val,low,hi)	\
+	(((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
+
+static TreeViewCompareProc ExactCompare, GlobCompare, RegexpCompare;
+static TreeViewApplyProc ShowEntryApplyProc, HideEntryApplyProc, 
+	MapAncestorsApplyProc, FixSelectionsApplyProc;
+static Tk_LostSelProc LostSelection;
+static TreeViewApplyProc SelectEntryApplyProc;
+
+extern Blt_CustomOption bltTreeViewIconsOption;
+extern Blt_CustomOption bltTreeViewUidOption;
+extern Blt_CustomOption bltTreeViewTreeOption;
+
+extern Blt_ConfigSpec bltTreeViewButtonSpecs[];
+extern Blt_ConfigSpec bltTreeViewSpecs[];
+extern Blt_ConfigSpec bltTreeViewEntrySpecs[];
+
+#define TAG_UNKNOWN	 (1<<0)
+#define TAG_RESERVED	 (1<<1)
+#define TAG_USER_DEFINED (1<<2)
+
+#define TAG_SINGLE	(1<<3)
+#define TAG_MULTIPLE	(1<<4)
+#define TAG_ALL		(1<<5)
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SkipSeparators --
+ *
+ *	Moves the character pointer past one of more separators.
+ *
+ * Results:
+ *	Returns the updates character pointer.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+SkipSeparators(path, separator, length)
+    char *path, *separator;
+    int length;
+{
+    while ((path[0] == separator[0]) && 
+	   (strncmp(path, separator, length) == 0)) {
+	path += length;
+    }
+    return path;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteNode --
+ *
+ *	Delete the node and its descendants.  Don't remove the root
+ *	node, though.  If the root node is specified, simply remove
+ *	all its children.
+ *
+ *---------------------------------------------------------------------- 
+ */
+static void
+DeleteNode(tvPtr, node)
+    TreeView *tvPtr;
+    Blt_TreeNode node;
+{
+    Blt_TreeNode root;
+
+    if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
+	Blt_TreeClearTags(tvPtr->tree, node);
+    }
+    root = Blt_TreeRootNode(tvPtr->tree);
+    if (node == root) {
+	Blt_TreeNode next;
+	/* Don't delete the root node. Simply clean out the tree. */
+	for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
+	    next = Blt_TreeNextSibling(node);
+	    Blt_TreeDeleteNode(tvPtr->tree, node);
+	}	    
+    } else if (Blt_TreeIsAncestor(root, node)) {
+	Blt_TreeDeleteNode(tvPtr->tree, node);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SplitPath --
+ *
+ *	Returns the trailing component of the given path.  Trailing
+ *	separators are ignored.
+ *
+ * Results:
+ *	Returns the string of the tail component.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SplitPath(tvPtr, path, depthPtr, compPtrPtr)
+    TreeView *tvPtr;
+    char *path;
+    int *depthPtr;
+    char ***compPtrPtr;
+{
+    int skipLen, pathLen;
+    int depth, listSize;
+    char **components;
+    register char *p;
+    char *sep;
+
+    if (tvPtr->pathSep == SEPARATOR_LIST) {
+	if (Tcl_SplitList(tvPtr->interp, path, depthPtr, compPtrPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    }
+    pathLen = strlen(path);
+
+    skipLen = strlen(tvPtr->pathSep);
+    path = SkipSeparators(path, tvPtr->pathSep, skipLen);
+    depth = pathLen / skipLen;
+
+    listSize = (depth + 1) * sizeof(char *);
+    components = Blt_Malloc(listSize + (pathLen + 1));
+    assert(components);
+    p = (char *)components + listSize;
+    strcpy(p, path);
+
+    sep = strstr(p, tvPtr->pathSep);
+    depth = 0;
+    while ((*p != '\0') && (sep != NULL)) {
+	*sep = '\0';
+	components[depth++] = p;
+	p = SkipSeparators(sep + skipLen, tvPtr->pathSep, skipLen);
+	sep = strstr(p, tvPtr->pathSep);
+    }
+    if (*p != '\0') {
+	components[depth++] = p;
+    }
+    components[depth] = NULL;
+    *depthPtr = depth;
+    *compPtrPtr = components;
+    return TCL_OK;
+}
+
+
+static TreeViewEntry *
+LastEntry(tvPtr, entryPtr, mask)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    unsigned int mask;
+{
+    Blt_TreeNode next;
+    TreeViewEntry *nextPtr;
+
+    next = Blt_TreeLastChild(entryPtr->node);
+    while (next != NULL) {
+	nextPtr = Blt_NodeToEntry(tvPtr, next);
+	if ((nextPtr->flags & mask) != mask) {
+	    break;
+	}
+	entryPtr = nextPtr;
+	next = Blt_TreeLastChild(next);
+    }
+    return entryPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowEntryApplyProc --
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ShowEntryApplyProc(tvPtr, entryPtr)
+    TreeView *tvPtr;		/* Not used. */
+    TreeViewEntry *entryPtr;
+{
+    entryPtr->flags &= ~ENTRY_HIDDEN;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideEntryApplyProc --
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+HideEntryApplyProc(tvPtr, entryPtr)
+    TreeView *tvPtr;		/* Not used. */
+    TreeViewEntry *entryPtr;
+{
+    entryPtr->flags |= ENTRY_HIDDEN;
+    return TCL_OK;
+}
+
+static void
+MapAncestors(tvPtr, entryPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+{
+    while (entryPtr != tvPtr->rootPtr) {
+	entryPtr = Blt_TreeViewParentEntry(entryPtr);
+	if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
+	    tvPtr->flags |= TV_LAYOUT;
+	    entryPtr->flags &= ~(ENTRY_CLOSED | ENTRY_HIDDEN);
+	} 
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapAncestorsApplyProc --
+ *
+ *	If a node in mapped, then all its ancestors must be mapped also.
+ *	This routine traverses upwards and maps each unmapped ancestor.
+ *	It's assumed that for any mapped ancestor, all it's ancestors
+ *	will already be mapped too.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+MapAncestorsApplyProc(tvPtr, entryPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+{
+    /*
+     * Make sure that all the ancestors of this entry are mapped too.
+     */
+    while (entryPtr != tvPtr->rootPtr) {
+	entryPtr = Blt_TreeViewParentEntry(entryPtr);
+	if ((entryPtr->flags & (ENTRY_HIDDEN | ENTRY_CLOSED)) == 0) {
+	    break;		/* Assume ancestors are also mapped. */
+	}
+	entryPtr->flags &= ~(ENTRY_HIDDEN | ENTRY_CLOSED);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindPath --
+ *
+ *	Finds the node designated by the given path.  Each path
+ *	component is searched for as the tree is traversed.
+ *
+ *	A leading character string is trimmed off the path if it
+ *	matches the one designated (see the -trimleft option).
+ *
+ *	If no separator is designated (see the -separator
+ *	configuration option), the path is considered a Tcl list.
+ *	Otherwise the each component of the path is separated by a
+ *	character string.  Leading and trailing separators are
+ *	ignored.  Multiple separators are treated as one.
+ *
+ * Results:
+ *	Returns the pointer to the designated node.  If any component
+ *	can't be found, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeViewEntry *
+FindPath(tvPtr, rootPtr, path)
+    TreeView *tvPtr;
+    TreeViewEntry *rootPtr;
+    char *path;
+{
+    Blt_TreeNode child;
+    char **compArr;
+    char *name;
+    int nComp;
+    register char **p;
+    TreeViewEntry *entryPtr;
+
+    /* Trim off characters that we don't want */
+    if (tvPtr->trimLeft != NULL) {
+	register char *s1, *s2;
+	
+	/* Trim off leading character string if one exists. */
+	for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+	    if (*s1 != *s2) {
+		break;
+	    }
+	}
+	if (*s2 == '\0') {
+	    path = s1;
+	}
+    }
+    if (*path == '\0') {
+	return rootPtr;
+    }
+    name = path;
+    entryPtr = rootPtr;
+    if (tvPtr->pathSep == SEPARATOR_NONE) {
+	child = Blt_TreeFindChild(entryPtr->node, name);
+	if (child == NULL) {
+	    goto error;
+	}
+	return Blt_NodeToEntry(tvPtr, child);
+    }
+
+    if (SplitPath(tvPtr, path, &nComp, &compArr) != TCL_OK) {
+	return NULL;
+    }
+    for (p = compArr; *p != NULL; p++) {
+	name = *p;
+	child = Blt_TreeFindChild(entryPtr->node, name);
+	if (child == NULL) {
+	    Blt_Free(compArr);
+	    goto error;
+	}
+	entryPtr = Blt_NodeToEntry(tvPtr, child);
+    }
+    Blt_Free(compArr);
+    return entryPtr;
+ error:
+    {
+	Tcl_DString dString;
+
+	Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
+	Tcl_AppendResult(tvPtr->interp, "can't find node \"", name,
+		 "\" in parent node \"", Tcl_DStringValue(&dString), "\"", 
+		(char *)NULL);
+	Tcl_DStringFree(&dString);
+    }
+    return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NodeToObj --
+ *
+ *	Converts a node pointer to a string representation.
+ *	The string contains the node's index which is unique.
+ *
+ * Results:
+ *	The string representation of the node is returned.  Note that
+ *	the string is stored statically, so that callers must save the
+ *	string before the next call to this routine overwrites the
+ *	static array again.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tcl_Obj *
+NodeToObj(node)
+    Blt_TreeNode node;
+{
+    char string[200];
+
+    sprintf(string, "%d", Blt_TreeNodeId(node));
+    return Tcl_NewStringObj(string, -1);
+}
+
+
+static int
+GetEntryFromSpecialId(tvPtr, string, entryPtrPtr)
+    TreeView *tvPtr;
+    char *string;
+    TreeViewEntry **entryPtrPtr;
+{
+    Blt_TreeNode node;
+    TreeViewEntry *fromPtr, *entryPtr;
+    char c;
+
+    entryPtr = NULL;
+    fromPtr = tvPtr->fromPtr;
+    if (fromPtr == NULL) {
+	fromPtr = tvPtr->focusPtr;
+    } 
+    if (fromPtr == NULL) {
+	fromPtr = tvPtr->rootPtr;
+    }
+    c = string[0];
+    if (c == '@') {
+	int x, y;
+
+	if (Blt_GetXY(tvPtr->interp, tvPtr->tkwin, string, &x, &y) == TCL_OK) {
+	    *entryPtrPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
+	}
+    } else if ((c == 'b') && (strcmp(string, "bottom") == 0)) {
+	if (tvPtr->flatView) {
+	    entryPtr = tvPtr->flatArr[tvPtr->nEntries - 1];
+	} else {
+	    entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+	}
+    } else if ((c == 't') && (strcmp(string, "top") == 0)) {
+	if (tvPtr->flatView) {
+	    entryPtr = tvPtr->flatArr[0];
+	} else {
+	    entryPtr = tvPtr->rootPtr;
+	    if (tvPtr->flags & TV_HIDE_ROOT) {
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
+	    }
+	}
+    } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+	entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+    } else if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+	entryPtr = tvPtr->selAnchorPtr;
+    } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+	entryPtr = tvPtr->focusPtr;
+	if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
+	    entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr, ENTRY_MASK);
+	}
+    } else if ((c == 'r') && (strcmp(string, "root") == 0)) {
+	entryPtr = tvPtr->rootPtr;
+    } else if ((c == 'p') && (strcmp(string, "parent") == 0)) {
+	if (fromPtr != tvPtr->rootPtr) {
+	    entryPtr = Blt_TreeViewParentEntry(fromPtr);
+	}
+    } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+	/* Can't trust picked item, if entries have been 
+	 * added or deleted. */
+	if (!(tvPtr->flags & TV_DIRTY)) {
+	    ClientData context;
+
+	    context = Blt_GetCurrentContext(tvPtr->bindTable);
+	    if ((context == ITEM_ENTRY) || 
+		(context == ITEM_ENTRY_BUTTON) ||
+		(context >= ITEM_STYLE)) {
+		entryPtr = Blt_GetCurrentItem(tvPtr->bindTable);
+	    }
+	}
+    } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
+	entryPtr = fromPtr;
+	if (tvPtr->flatView) {
+	    int i;
+
+	    i = entryPtr->flatIndex - 1;
+	    if (i >= 0) {
+		entryPtr = tvPtr->flatArr[i];
+	    }
+	} else {
+	    entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
+	    if (entryPtr == NULL) {
+		entryPtr = fromPtr;
+	    }
+	    if ((entryPtr == tvPtr->rootPtr) && 
+		(tvPtr->flags & TV_HIDE_ROOT)) {
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
+	    }
+	}
+    } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
+	entryPtr = fromPtr;
+	if (tvPtr->flatView) {
+	    int i;
+
+	    i = entryPtr->flatIndex + 1;
+	    if (i < tvPtr->nEntries) {
+		entryPtr = tvPtr->flatArr[i];
+	    }
+	} else {
+	    entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
+	    if (entryPtr == NULL) {
+		entryPtr = fromPtr;
+	    }
+	    if ((entryPtr == tvPtr->rootPtr) && 
+		(tvPtr->flags & TV_HIDE_ROOT)) {
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
+	    }
+	}
+    } else if (((c == 'l') && (strcmp(string, "last") == 0)) ||
+	       ((c == 'p') && (strcmp(string, "prev") == 0))) {
+	entryPtr = fromPtr;
+	if (tvPtr->flatView) {
+	    int i;
+
+	    i = entryPtr->flatIndex - 1;
+	    if (i < 0) {
+		i = tvPtr->nEntries - 1;
+	    }
+	    entryPtr = tvPtr->flatArr[i];
+	} else {
+	    entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
+	    if (entryPtr == NULL) {
+		entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
+	    }
+	    if ((entryPtr == tvPtr->rootPtr) && 
+		(tvPtr->flags & TV_HIDE_ROOT)) {
+		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
+	    }
+	}
+    } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
+	entryPtr = fromPtr;
+	if (tvPtr->flatView) {
+	    int i;
+
+	    i = entryPtr->flatIndex + 1; 
+	    if (i >= tvPtr->nEntries) {
+		i = 0;
+	    }
+	    entryPtr = tvPtr->flatArr[i];
+	} else {
+	    entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
+	    if (entryPtr == NULL) {
+		if (tvPtr->flags & TV_HIDE_ROOT) {
+		    entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr,ENTRY_MASK);
+		} else {
+		    entryPtr = tvPtr->rootPtr;
+		}
+	    }
+	}
+    } else if ((c == 'n') && (strcmp(string, "nextsibling") == 0)) {
+	node = Blt_TreeNextSibling(fromPtr->node);
+	if (node != NULL) {
+	    entryPtr = Blt_NodeToEntry(tvPtr, node);
+	}
+    } else if ((c == 'p') && (strcmp(string, "prevsibling") == 0)) {
+	node = Blt_TreePrevSibling(fromPtr->node);
+	if (node != NULL) {
+	    entryPtr = Blt_NodeToEntry(tvPtr, node);
+	}
+    } else if ((c == 'v') && (strcmp(string, "view.top") == 0)) {
+	if (tvPtr->nVisible > 0) {
+	    entryPtr = tvPtr->visibleArr[0];
+	}
+    } else if ((c == 'v') && (strcmp(string, "view.bottom") == 0)) {
+	if (tvPtr->nVisible > 0) {
+	    entryPtr = tvPtr->visibleArr[tvPtr->nVisible - 1];
+	} 
+    } else {
+	return TCL_ERROR;
+    }
+    *entryPtrPtr = entryPtr;
+    return TCL_OK;
+}
+
+static int
+GetTagInfo(tvPtr, tagName, infoPtr)
+    TreeView *tvPtr;
+    char *tagName;
+    TreeViewTagInfo *infoPtr;
+{
+    
+    infoPtr->tagType = TAG_RESERVED | TAG_SINGLE;
+    infoPtr->entryPtr = NULL;
+
+    if (strcmp(tagName, "all") == 0) {
+	infoPtr->entryPtr = tvPtr->rootPtr;
+	infoPtr->tagType |= TAG_ALL;
+    } else {
+	Blt_HashTable *tablePtr;
+
+	tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
+	if (tablePtr != NULL) {
+	    Blt_HashEntry *hPtr;
+	    
+	    infoPtr->tagType = TAG_USER_DEFINED; /* Empty tags are not
+						  * an error. */
+	    hPtr = Blt_FirstHashEntry(tablePtr, &infoPtr->cursor); 
+	    if (hPtr != NULL) {
+		Blt_TreeNode node;
+
+		node = Blt_GetHashValue(hPtr);
+		infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
+		if (tablePtr->numEntries > 1) {
+		    infoPtr->tagType |= TAG_MULTIPLE;
+		}
+	    }
+	}  else {
+	    infoPtr->tagType = TAG_UNKNOWN;
+	    Tcl_AppendResult(tvPtr->interp, "can't find tag or id \"", tagName, 
+		"\" in \"", Tk_PathName(tvPtr->tkwin), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+void
+Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list)
+    Tcl_Interp *interp;		/* Not used. */
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    Blt_List list;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_TreeTagEntry *tPtr;
+
+    for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL; 
+	hPtr = Blt_NextHashEntry(&cursor)) {
+	tPtr = Blt_GetHashValue(hPtr);
+	hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)entryPtr->node);
+	if (hPtr != NULL) {
+	    Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, tPtr->tagName),0);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddTag --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+AddTag(tvPtr, node, tagName)
+    TreeView *tvPtr;
+    Blt_TreeNode node;
+    char *tagName;
+{
+    TreeViewEntry *entryPtr;
+
+    if (strcmp(tagName, "root") == 0) {
+	Tcl_AppendResult(tvPtr->interp, "can't add reserved tag \"",
+			 tagName, "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (isdigit(UCHAR(tagName[0]))) {
+	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName, 
+		"\": can't start with digit", (char *)NULL);
+	return TCL_ERROR;
+    } 
+    if (isdigit(UCHAR(tagName[0]))) {
+	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName, 
+		"\": can't start with digit", (char *)NULL);
+	return TCL_ERROR;
+    } 
+    if (tagName[0] == '@') {
+	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName, 
+		"\": can't start with \"@\"", (char *)NULL);
+	return TCL_ERROR;
+    } 
+    tvPtr->fromPtr = NULL;
+    if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName, 
+		"\": is a special id", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Add the tag to the node. */
+    Blt_TreeAddTag(tvPtr->tree, node, tagName);
+    return TCL_OK;
+}
+    
+TreeViewEntry *
+Blt_TreeViewFirstTaggedEntry(infoPtr)	
+    TreeViewTagInfo *infoPtr;
+{
+    return infoPtr->entryPtr;
+}
+
+int
+Blt_TreeViewFindTaggedEntries(tvPtr, objPtr, infoPtr)	
+    TreeView *tvPtr;
+    Tcl_Obj *objPtr;
+    TreeViewTagInfo *infoPtr;
+{
+    char *tagName;
+    TreeViewEntry *entryPtr;
+
+    tagName = Tcl_GetString(objPtr); 
+    tvPtr->fromPtr = NULL;
+    if (isdigit(UCHAR(tagName[0]))) {
+	int inode;
+	Blt_TreeNode node;
+
+	if (Tcl_GetIntFromObj(tvPtr->interp, objPtr, &inode) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	node = Blt_TreeGetNode(tvPtr->tree, inode);
+	infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
+	infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
+    } else if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+	infoPtr->entryPtr = entryPtr;
+	infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
+    } else {
+	if (GetTagInfo(tvPtr, tagName, infoPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
+
+TreeViewEntry *
+Blt_TreeViewNextTaggedEntry(infoPtr) 
+    TreeViewTagInfo *infoPtr;
+{
+    TreeViewEntry *entryPtr;
+
+    entryPtr = NULL;
+    if (infoPtr->entryPtr != NULL) {
+	TreeView *tvPtr = infoPtr->entryPtr->tvPtr;
+
+	if (infoPtr->tagType & TAG_ALL) {
+	    entryPtr = Blt_TreeViewNextEntry(infoPtr->entryPtr, 0);
+	} else if (infoPtr->tagType & TAG_MULTIPLE) {
+	    Blt_HashEntry *hPtr;
+	    
+	    hPtr = Blt_NextHashEntry(&infoPtr->cursor);
+	    if (hPtr != NULL) {
+		Blt_TreeNode node;
+
+		node = Blt_GetHashValue(hPtr);
+		entryPtr = Blt_NodeToEntry(tvPtr, node);
+	    }
+	} 
+	infoPtr->entryPtr = entryPtr;
+    }
+    return entryPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEntryFromObj2 --
+ *
+ *	Converts a string into node pointer.  The string may be in one
+ *	of the following forms:
+ *
+ *	    NNN			- inode.
+ *	    "active"		- Currently active node.
+ *	    "anchor"		- anchor of selected region.
+ *	    "current"		- Currently picked node in bindtable.
+ *	    "focus"		- The node currently with focus.
+ *	    "root"		- Root node.
+ *	    "end"		- Last open node in the entire hierarchy.
+ *	    "next"		- Next open node from the currently active
+ *				  node. Wraps around back to top.
+ *	    "last"		- Previous open node from the currently active
+ *				  node. Wraps around back to bottom.
+ *	    "up"		- Next open node from the currently active
+ *				  node. Does not wrap around.
+ *	    "down"		- Previous open node from the currently active
+ *				  node. Does not wrap around.
+ *	    "nextsibling"	- Next sibling of the current node.
+ *	    "prevsibling"	- Previous sibling of the current node.
+ *	    "parent"		- Parent of the current node.
+ *	    "view.top"		- Top of viewport.
+ *	    "view.bottom"	- Bottom of viewport.
+ *	    @x,y		- Closest node to the specified X-Y position.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	The pointer to the node is returned via nodePtr.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr)
+    TreeView *tvPtr;
+    Tcl_Obj *objPtr;
+    TreeViewEntry **entryPtrPtr;
+{
+    Tcl_Interp *interp;
+    char *string;
+    TreeViewTagInfo info;
+
+    interp = tvPtr->interp;
+
+    string = Tcl_GetString(objPtr);
+    *entryPtrPtr = NULL;
+    if (isdigit(UCHAR(string[0]))) {    
+	Blt_TreeNode node;
+	int inode;
+
+	if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	node = Blt_TreeGetNode(tvPtr->tree, inode);
+	if (node != NULL) {
+	    *entryPtrPtr = Blt_NodeToEntry(tvPtr, node);
+	}
+	return TCL_OK;		/* Node Id. */
+    }
+    if (GetEntryFromSpecialId(tvPtr, string, entryPtrPtr) == TCL_OK) {
+	return TCL_OK;		/* Special Id. */
+    }
+    if (GetTagInfo(tvPtr, string, &info) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (info.tagType & TAG_MULTIPLE) {
+	Tcl_AppendResult(interp, "more than one entry tagged as \"", string, 
+		"\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *entryPtrPtr = info.entryPtr;
+    return TCL_OK;		/* Singleton tag. */
+}
+
+static int
+GetEntryFromObj(tvPtr, objPtr, entryPtrPtr)
+    TreeView *tvPtr;
+    Tcl_Obj *objPtr;
+    TreeViewEntry **entryPtrPtr;
+{
+    tvPtr->fromPtr = NULL;
+    return GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewGetEntry --
+ *
+ *	Returns an entry based upon its index.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	The pointer to the node is returned via nodePtr.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewGetEntry(tvPtr, objPtr, entryPtrPtr)
+    TreeView *tvPtr;
+    Tcl_Obj *objPtr;
+    TreeViewEntry **entryPtrPtr;
+{
+    TreeViewEntry *entryPtr;
+
+    if (GetEntryFromObj(tvPtr, objPtr, &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (entryPtr == NULL) {
+	Tcl_ResetResult(tvPtr->interp);
+	Tcl_AppendResult(tvPtr->interp, "can't find entry \"", 
+		Tcl_GetString(objPtr), "\" in \"", Tk_PathName(tvPtr->tkwin), 
+		"\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *entryPtrPtr = entryPtr;
+    return TCL_OK;
+}
+
+static Blt_TreeNode 
+GetNthNode(parent, position)
+    Blt_TreeNode parent;
+    int position;
+{
+    Blt_TreeNode node;
+    int count;
+
+    count = 0;
+    for(node = Blt_TreeFirstChild(parent); node != NULL; 
+	node = Blt_TreeNextSibling(node)) {
+	if (count == position) {
+	    return node;
+	}
+    }
+    return Blt_TreeLastChild(parent);
+}
+
+static TreeViewEntry *
+GetNthEntry(
+    TreeViewEntry *parentPtr,
+    int position,
+    unsigned int mask)
+{
+    TreeViewEntry *entryPtr;
+    int count;
+
+    count = 0;
+    for(entryPtr = Blt_TreeViewFirstChild(parentPtr, mask); entryPtr != NULL; 
+	entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
+	if (count == position) {
+	    return entryPtr;
+	}
+    }
+    return Blt_TreeViewLastChild(parentPtr, mask);
+}
+
+/*
+ * Preprocess the command string for percent substitution.
+ */
+void
+Blt_TreeViewPercentSubst(tvPtr, entryPtr, command, resultPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    char *command;
+    Tcl_DString *resultPtr;
+{
+    register char *last, *p;
+    char *fullName;
+    Tcl_DString dString;
+
+    /*
+     * Get the full path name of the node, in case we need to
+     * substitute for it.
+     */
+    fullName = Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+    Tcl_DStringInit(resultPtr);
+    /* Append the widget name and the node .t 0 */
+    for (last = p = command; *p != '\0'; p++) {
+	if (*p == '%') {
+	    char *string;
+	    char buf[3];
+
+	    if (p > last) {
+		*p = '\0';
+		Tcl_DStringAppend(resultPtr, last, -1);
+		*p = '%';
+	    }
+	    switch (*(p + 1)) {
+	    case '%':		/* Percent sign */
+		string = "%";
+		break;
+	    case 'W':		/* Widget name */
+		string = Tk_PathName(tvPtr->tkwin);
+		break;
+	    case 'P':		/* Full pathname */
+		string = fullName;
+		break;
+	    case 'p':		/* Name of the node */
+		string = GETLABEL(entryPtr);
+		break;
+	    case '#':		/* Node identifier */
+		string = Blt_Itoa(Blt_TreeNodeId(entryPtr->node));
+		break;
+	    default:
+		if (*(p + 1) == '\0') {
+		    p--;
+		}
+		buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
+		string = buf;
+		break;
+	    }
+	    Tcl_DStringAppend(resultPtr, string, -1);
+	    p++;
+	    last = p + 1;
+	}
+    }
+    if (p > last) {
+	*p = '\0';
+	Tcl_DStringAppend(resultPtr, last, -1);
+    }
+    Tcl_DStringFree(&dString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectEntryApplyProc --
+ *
+ *	Sets the selection flag for a node.  The selection flag is
+ *	set/cleared/toggled based upon the flag set in the treeview
+ *	widget.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectEntryApplyProc(tvPtr, entryPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+{
+    Blt_HashEntry *hPtr;
+
+    switch (tvPtr->flags & TV_SELECT_MASK) {
+    case TV_SELECT_CLEAR:
+	Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+	break;
+
+    case TV_SELECT_SET:
+	Blt_TreeViewSelectEntry(tvPtr, entryPtr);
+	break;
+
+    case TV_SELECT_TOGGLE:
+	hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
+	if (hPtr != NULL) {
+	    Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+	} else {
+	    Blt_TreeViewSelectEntry(tvPtr, entryPtr);
+	}
+	break;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyInvokeSelectCmd --
+ *
+ *      Queues a request to execute the -selectcommand code associated
+ *      with the widget at the next idle point.  Invoked whenever the
+ *      selection changes.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      Tcl code gets executed for some application-specific task.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyInvokeSelectCmd(tvPtr)
+    TreeView *tvPtr;
+{
+    if (!(tvPtr->flags & TV_SELECT_PENDING)) {
+	tvPtr->flags |= TV_SELECT_PENDING;
+	Tcl_DoWhenIdle(Blt_TreeViewSelectCmdProc, tvPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewPruneSelection --
+ *
+ *	The root entry being deleted or closed.  Deselect any of its
+ *	descendants that are currently selected. 
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      If any of the entry's descendants are deselected the widget
+ *	is redrawn and the a selection command callback is invoked
+ *	(if there's one configured).
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewPruneSelection(tvPtr, rootPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *rootPtr;
+{
+    Blt_ChainLink *linkPtr, *nextPtr;
+    TreeViewEntry *entryPtr;
+    int selectionChanged;
+
+    /* 
+     * Check if any of the currently selected entries are a descendant
+     * of of the current root entry.  Deselect the entry and indicate
+     * that the treeview widget needs to be redrawn.
+     */
+    selectionChanged = FALSE;
+    for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL; 
+	 linkPtr = nextPtr) {
+	nextPtr = Blt_ChainNextLink(linkPtr);
+	entryPtr = Blt_ChainGetValue(linkPtr);
+	if (Blt_TreeIsAncestor(rootPtr->node, entryPtr->node)) {
+	    Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+	    selectionChanged = TRUE;
+	}
+    }
+    if (selectionChanged) {
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+	if (tvPtr->selectCmd != NULL) {
+	    EventuallyInvokeSelectCmd(tvPtr);
+	}
+    }
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TreeView operations
+ *
+ * --------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+FocusOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 3) {
+	TreeViewEntry *entryPtr;
+
+	if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((entryPtr != NULL) && (entryPtr != tvPtr->focusPtr)) {
+	    if (entryPtr->flags & ENTRY_HIDDEN) {
+		/* Doesn't make sense to set focus to a node you can't see. */
+		MapAncestors(tvPtr, entryPtr);
+	    }
+	    /* Changing focus can only affect the visible entries.  The
+	     * entry layout stays the same. */
+	    if (tvPtr->focusPtr != NULL) {
+		tvPtr->focusPtr->flags |= ENTRY_REDRAW;
+	    } 
+	    entryPtr->flags |= ENTRY_REDRAW;
+	    tvPtr->flags |= TV_SCROLL;
+	    tvPtr->focusPtr = entryPtr;
+	}
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+    }
+    Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
+    if (tvPtr->focusPtr != NULL) {
+	Tcl_SetObjResult(interp, NodeToObj(tvPtr->focusPtr->node));
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BboxOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BboxOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+    TreeViewEntry *entryPtr;
+    int width, height, yBot;
+    int left, top, right, bottom;
+    int screen;
+    int lWidth;
+    char *string;
+
+    if (tvPtr->flags & TV_LAYOUT) {
+	/*
+	 * The layout is dirty.  Recompute it now, before we use the
+	 * world dimensions.  But remember, the "bbox" operation isn't
+	 * valid for hidden entries (since they're not visible, they
+	 * don't have world coordinates).
+	 */
+	Blt_TreeViewComputeLayout(tvPtr);
+    }
+    left = tvPtr->worldWidth;
+    top = tvPtr->worldHeight;
+    right = bottom = 0;
+
+    screen = FALSE;
+    string = Tcl_GetString(objv[2]);
+    if ((string[0] == '-') && (strcmp(string, "-screen") == 0)) {
+	screen = TRUE;
+	objc--, objv++;
+    }
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if ((string[0] == 'a') && (strcmp(string, "all") == 0)) {
+	    left = top = 0;
+	    right = tvPtr->worldWidth;
+	    bottom = tvPtr->worldHeight;
+	    break;
+	}
+	if (GetEntryFromObj(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (entryPtr == NULL) {
+	    continue;
+	}
+	if (entryPtr->flags & ENTRY_HIDDEN) {
+	    continue;
+	}
+	yBot = entryPtr->worldY + entryPtr->height;
+	height = VPORTHEIGHT(tvPtr);
+	if ((yBot <= tvPtr->yOffset) &&
+	    (entryPtr->worldY >= (tvPtr->yOffset + height))) {
+	    continue;
+	}
+	if (bottom < yBot) {
+	    bottom = yBot;
+	}
+	if (top > entryPtr->worldY) {
+	    top = entryPtr->worldY;
+	}
+	lWidth = ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
+	if (right < (entryPtr->worldX + entryPtr->width + lWidth)) {
+	    right = (entryPtr->worldX + entryPtr->width + lWidth);
+	}
+	if (left > entryPtr->worldX) {
+	    left = entryPtr->worldX;
+	}
+    }
+
+    if (screen) {
+	width = VPORTWIDTH(tvPtr);
+	height = VPORTHEIGHT(tvPtr);
+	/*
+	 * Do a min-max text for the intersection of the viewport and
+	 * the computed bounding box.  If there is no intersection, return
+	 * the empty string.
+	 */
+	if ((right < tvPtr->xOffset) || (bottom < tvPtr->yOffset) ||
+	    (left >= (tvPtr->xOffset + width)) ||
+	    (top >= (tvPtr->yOffset + height))) {
+	    return TCL_OK;
+	}
+	/* Otherwise clip the coordinates at the view port boundaries. */
+	if (left < tvPtr->xOffset) {
+	    left = tvPtr->xOffset;
+	} else if (right > (tvPtr->xOffset + width)) {
+	    right = tvPtr->xOffset + width;
+	}
+	if (top < tvPtr->yOffset) {
+	    top = tvPtr->yOffset;
+	} else if (bottom > (tvPtr->yOffset + height)) {
+	    bottom = tvPtr->yOffset + height;
+	}
+	left = SCREENX(tvPtr, left), top = SCREENY(tvPtr, top);
+	right = SCREENX(tvPtr, right), bottom = SCREENY(tvPtr, bottom);
+    }
+    if ((left < right) && (top < bottom)) {
+	Tcl_Obj *listObjPtr;
+
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(left));
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(top));
+	Tcl_ListObjAppendElement(interp, listObjPtr, 
+				 Tcl_NewIntObj(right - left));
+	Tcl_ListObjAppendElement(interp, listObjPtr, 
+				 Tcl_NewIntObj(bottom - top));
+	Tcl_SetObjResult(interp, listObjPtr);
+    }
+    return TCL_OK;
+}
+
+static void
+DrawButton(tvPtr, entryPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+{
+    Drawable drawable;
+    int sx, sy, dx, dy;
+    int width, height;
+    int left, right, top, bottom;
+
+    dx = SCREENX(tvPtr, entryPtr->worldX) + entryPtr->buttonX;
+    dy = SCREENY(tvPtr, entryPtr->worldY) + entryPtr->buttonY;
+    width = tvPtr->button.width;
+    height = tvPtr->button.height;
+
+    top = tvPtr->titleHeight + tvPtr->inset;
+    bottom = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
+    left = tvPtr->inset;
+    right = Tk_Width(tvPtr->tkwin) - tvPtr->inset;
+
+    if (((dx + width) < left) || (dx > right) ||
+	((dy + height) < top) || (dy > bottom)) {
+	return;			/* Value is clipped. */
+    }
+    drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin), 
+	width, height, Tk_Depth(tvPtr->tkwin));
+    /* Draw the background of the value. */
+    Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, 0, 0);
+
+    /* Clip the drawable if necessary */
+    sx = sy = 0;
+    if (dx < left) {
+	width -= left - dx;
+	sx += left - dx;
+	dx = left;
+    }
+    if ((dx + width) >= right) {
+	width -= (dx + width) - right;
+    }
+    if (dy < top) {
+	height -= top - dy;
+	sy += top - dy;
+	dy = top;
+    }
+    if ((dy + height) >= bottom) {
+	height -= (dy + height) - bottom;
+    }
+    XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin), 
+      tvPtr->lineGC, sx, sy, width,  height, dx, dy);
+    Tk_FreePixmap(tvPtr->display, drawable);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonActivateOp --
+ *
+ *	Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonActivateOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *oldPtr, *newPtr;
+    char *string;
+
+    string = Tcl_GetString(objv[3]);
+    if (string[0] == '\0') {
+	newPtr = NULL;
+    } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tvPtr->treeColumn.hidden) {
+	return TCL_OK;
+    }
+    if ((newPtr != NULL) && !(newPtr->flags & ENTRY_HAS_BUTTON)) {
+	newPtr = NULL;
+    }
+    oldPtr = tvPtr->activeButtonPtr;
+    tvPtr->activeButtonPtr = newPtr;
+    if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
+	if ((oldPtr != NULL) && (oldPtr != tvPtr->rootPtr)) {
+	    DrawButton(tvPtr, oldPtr);
+	}
+	if ((newPtr != NULL) && (newPtr != tvPtr->rootPtr)) {
+	    DrawButton(tvPtr, newPtr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonBindOp --
+ *
+ *	  .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonBindOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    ClientData object;
+    char *string;
+
+    string = Tcl_GetString(objv[3]);
+    /* Assume that this is a binding tag. */
+    object = Blt_TreeViewButtonTag(tvPtr, string);
+    return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object, 
+	objc - 4, objv + 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ButtonCgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+	bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the one of more
+ *	entries in the widget.
+ *
+ *	  .h button configure option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ButtonConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 3) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+	    bltTreeViewButtonSpecs, (char *)tvPtr, (Tcl_Obj *)NULL, 0);
+    } else if (objc == 4) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+		bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
+    }
+    bltTreeViewIconsOption.clientData = tvPtr;
+    if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin, 
+		bltTreeViewButtonSpecs, objc - 3, objv + 3, (char *)tvPtr, 
+		BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_TreeViewConfigureButtons(tvPtr);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonOp --
+ *
+ *	This procedure handles button operations.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec buttonOps[] =
+{
+    {"activate", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
+    {"bind", 1, (Blt_Op)ButtonBindOp, 4, 6, "tagName ?sequence command?",},
+    {"cget", 2, (Blt_Op)ButtonCgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)ButtonConfigureOp, 3, 0, "?option value?...",},
+    {"highlight", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
+};
+
+static int nButtonOps = sizeof(buttonOps) / sizeof(Blt_OpSpec);
+
+static int
+ButtonOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nButtonOps, buttonOps, BLT_OP_ARG2, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
+	(char *)tvPtr, objv[2], 0);
+}
+
+/*ARGSUSED*/
+static int
+CloseOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    TreeViewTagInfo info;
+    int recurse, result;
+    register int i;
+
+    recurse = FALSE;
+
+    if (objc > 2) {
+	char *string;
+	int length;
+
+	string = Tcl_GetStringFromObj(objv[2], &length);
+	if ((string[0] == '-') && (length > 1) && 
+	    (strncmp(string, "-recurse", length) == 0)) {
+	    objv++, objc--;
+	    recurse = TRUE;
+	}
+    }
+    for (i = 2; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    /* 
+	     * Clear the selections for any entries that may have become
+	     * hidden by closing the node.  
+	     */
+	    Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+	    
+	    /*
+	     * -----------------------------------------------------------
+	     *
+	     *  Check if either the "focus" entry or selection anchor
+	     *  is in this hierarchy.  Must move it or disable it before
+	     *  we close the node.  Otherwise it may be deleted by a Tcl
+	     *  "close" script, and we'll be left pointing to a bogus
+	     *  memory location.
+	     *
+	     * -----------------------------------------------------------
+	     */
+	    if ((tvPtr->focusPtr != NULL) && 
+		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+		tvPtr->focusPtr = entryPtr;
+		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
+	    }
+	    if ((tvPtr->selAnchorPtr != NULL) && 
+		(Blt_TreeIsAncestor(entryPtr->node, 
+				    tvPtr->selAnchorPtr->node))) {
+		tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+	    }
+	    if ((tvPtr->activePtr != NULL) && 
+		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
+		tvPtr->activePtr = entryPtr;
+	    }
+	    if (recurse) {
+		result = Blt_TreeViewApply(tvPtr, entryPtr, 
+					   Blt_TreeViewCloseEntry, 0);
+	    } else {
+		result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
+	    }
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }	
+	}
+    }
+    /* Closing a node may affect the visible entries and the 
+     * the world layout of the entries. */
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * 	This procedure is called to process an objv/objc list, plus
+ *	the Tk option database, in order to configure (or reconfigure)
+ *	the widget.
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The widget is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 2) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
+		(char *)tvPtr, (Tcl_Obj *)NULL, 0);
+    } else if (objc == 3) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+		bltTreeViewSpecs, (char *)tvPtr, objv[2], 0);
+    } 
+    bltTreeViewIconsOption.clientData = tvPtr;
+    bltTreeViewTreeOption.clientData = tvPtr;
+    if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs, 
+	objc - 2, objv + 2, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CurselectionOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    TreeViewEntry *entryPtr;
+    Tcl_Obj *listObjPtr, *objPtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (tvPtr->flags & TV_SELECT_SORTED) {
+	Blt_ChainLink *linkPtr;
+
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    entryPtr = Blt_ChainGetValue(linkPtr);
+	    objPtr = NodeToObj(entryPtr->node);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+			
+	}
+    } else {
+	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
+	    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+		objPtr = NodeToObj(entryPtr->node);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    }
+	}
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ *	  .t bind tagOrId sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    ClientData object;
+    TreeViewEntry *entryPtr;
+    char *string;
+
+    /*
+     * Entries are selected by id only.  All other strings are
+     * interpreted as a binding tag.
+     */
+    string = Tcl_GetString(objv[2]);
+    if (isdigit(UCHAR(string[0]))) {
+	Blt_TreeNode node;
+	int inode;
+
+	if (Tcl_GetIntFromObj(tvPtr->interp, objv[2], &inode) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	node = Blt_TreeGetNode(tvPtr->tree, inode);
+	object = Blt_NodeToEntry(tvPtr, node);
+    } else if (GetEntryFromSpecialId(tvPtr, string, &entryPtr) == TCL_OK) {
+	if (entryPtr != NULL) {
+	    return TCL_OK;	/* Special id doesn't currently exist. */
+	}
+	object = entryPtr;
+    } else {
+	/* Assume that this is a binding tag. */
+	object = Blt_TreeViewEntryTag(tvPtr, string);
+    } 
+    return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object, 
+	 objc - 3, objv + 3);
+}
+
+
+/*ARGSUSED*/
+static int
+EditOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    char *string;
+    int isRoot, isTest;
+    int x, y;
+
+    isRoot = isTest = FALSE;
+    string = Tcl_GetString(objv[2]);
+    if (strcmp("-root", string) == 0) {
+	isRoot = TRUE;
+	objv++, objc--;
+    }
+    string = Tcl_GetString(objv[2]);
+    if (strcmp("-test", string) == 0) {
+	isTest = TRUE;
+	objv++, objc--;
+    }
+    if (objc != 4) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", 
+		Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]), 
+			" ?-root? x y\"", (char *)NULL);
+	return TCL_ERROR;
+			 
+    }
+    if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) ||
+	(Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (isRoot) {
+	int rootX, rootY;
+
+	Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
+	x -= rootX;
+	y -= rootY;
+    }
+    entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
+    if (entryPtr != NULL) {
+	Blt_ChainLink *linkPtr;
+	TreeViewColumn *columnPtr;
+	int worldX;
+
+	worldX = WORLDX(tvPtr, x);
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); 
+	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    if (!columnPtr->editable) {
+		continue;		/* Column isn't editable. */
+	    }
+	    if ((worldX >= columnPtr->worldX) && 
+		(worldX < (columnPtr->worldX + columnPtr->width))) {
+		TreeViewValue *valuePtr;
+		
+		valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
+		if (valuePtr != NULL) {
+		    TreeViewStyle *stylePtr;
+		    
+		    stylePtr = valuePtr->stylePtr;
+		    if (stylePtr == NULL) {
+			stylePtr = columnPtr->stylePtr;
+		    }
+		    if ((stylePtr->classPtr->editProc != NULL) && (!isTest)) {
+			if ((*stylePtr->classPtr->editProc)(tvPtr, entryPtr, 
+				    valuePtr, stylePtr) != TCL_OK) {
+			    return TCL_ERROR;
+			}
+			Blt_TreeViewEventuallyRedraw(tvPtr);
+		    }
+		    Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
+		    return TCL_OK;
+		}
+	    }
+	}
+    }
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryActivateOp --
+ *
+ *	Selects the entry to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryActivateOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *newPtr, *oldPtr;
+    char *string;
+
+    string = Tcl_GetString(objv[3]);
+    if (string[0] == '\0') {
+	newPtr = NULL;
+    } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tvPtr->treeColumn.hidden) {
+	return TCL_OK;
+    }
+    oldPtr = tvPtr->activePtr;
+    tvPtr->activePtr = newPtr;
+    if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
+	Drawable drawable;
+	int x, y;
+	
+	drawable = Tk_WindowId(tvPtr->tkwin);
+	if (oldPtr != NULL) {
+	    x = SCREENX(tvPtr, oldPtr->worldX);
+	    if (!tvPtr->flatView) {
+		x += ICONWIDTH(DEPTH(tvPtr, oldPtr->node));
+	    }
+	    y = SCREENY(tvPtr, oldPtr->worldY);
+	    oldPtr->flags |= ENTRY_ICON;
+	    Blt_TreeViewDrawIcon(tvPtr, oldPtr, drawable, x, y);
+	}
+	if (newPtr != NULL) {
+	    x = SCREENX(tvPtr, newPtr->worldX);
+	    if (!tvPtr->flatView) {
+		x += ICONWIDTH(DEPTH(tvPtr, newPtr->node));
+	    }
+	    y = SCREENY(tvPtr, newPtr->worldY);
+	    newPtr->flags |= ENTRY_ICON;
+	    Blt_TreeViewDrawIcon(tvPtr, newPtr, drawable, x, y);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryCgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+		bltTreeViewEntrySpecs, (char *)entryPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the one of more
+ *	entries in the widget.
+ *
+ *	  .h entryconfigure node node node node option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EntryConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int nIds, configObjc;
+    Tcl_Obj *CONST *configObjv;
+    register int i;
+    TreeViewEntry *entryPtr;
+    TreeViewTagInfo info;
+    char *string;
+
+    /* Figure out where the option value pairs begin */
+    objc -= 3, objv += 3;
+    for (i = 0; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (string[0] == '-') {
+	    break;
+	}
+    }
+    nIds = i;			/* # of tags or ids specified */
+    configObjc = objc - i;	/* # of options specified */
+    configObjv = objv + i;	/* Start of options in objv  */
+
+    bltTreeViewIconsOption.clientData = tvPtr;
+    bltTreeViewUidOption.clientData = tvPtr;
+
+    for (i = 0; i < nIds; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    if (configObjc == 0) {
+		return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+			bltTreeViewEntrySpecs, (char *)entryPtr, 
+			(Tcl_Obj *)NULL, 0);
+	    } else if (configObjc == 1) {
+		return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+			bltTreeViewEntrySpecs, (char *)entryPtr, 
+			configObjv[0], 0);
+	    }
+	    if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, configObjc, 
+		configObjv, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsOpenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsBeforeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *e1Ptr, *e2Ptr;
+    int bool;
+
+    if ((Blt_TreeViewGetEntry(tvPtr, objv[3], &e1Ptr) != TCL_OK) ||
+	(Blt_TreeViewGetEntry(tvPtr, objv[4], &e2Ptr) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    bool = Blt_TreeIsBefore(e1Ptr->node, e2Ptr->node);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsHiddenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsHiddenOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int bool;
+
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    bool = (entryPtr->flags & ENTRY_HIDDEN);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryIsOpenOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryIsOpenOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int bool;
+
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    bool = ((entryPtr->flags & ENTRY_CLOSED) == 0);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+EntryChildrenOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *parentPtr;
+    Tcl_Obj *listObjPtr, *objPtr;
+    unsigned int mask;
+
+    mask = 0;
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &parentPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (objc == 4) {
+	TreeViewEntry *entryPtr;
+
+	for (entryPtr = Blt_TreeViewFirstChild(parentPtr, mask); 
+	     entryPtr != NULL;
+	     entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
+	    objPtr = NodeToObj(entryPtr->node);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+    } else if (objc == 6) {
+	TreeViewEntry *entryPtr, *lastPtr, *firstPtr;
+	int firstPos, lastPos;
+	int nNodes;
+
+	if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
+	    (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
+	    return TCL_ERROR;
+	}
+	nNodes = Blt_TreeNodeDegree(parentPtr->node);
+	if (nNodes == 0) {
+	    return TCL_OK;
+	}
+	if ((lastPos == END) || (lastPos >= nNodes)) {
+	    lastPtr = Blt_TreeViewLastChild(parentPtr, mask);
+	} else {
+	    lastPtr = GetNthEntry(parentPtr, lastPos, mask);
+	}
+	if ((firstPos == END) || (firstPos >= nNodes)) {
+	    firstPtr = Blt_TreeViewLastChild(parentPtr, mask);
+	} else {
+	    firstPtr = GetNthEntry(parentPtr, firstPos, mask);
+	}
+	if ((lastPos != END) && (firstPos > lastPos)) {
+	    for (entryPtr = lastPtr; entryPtr != NULL; 
+		entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
+		objPtr = NodeToObj(entryPtr->node);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+		if (entryPtr == firstPtr) {
+		    break;
+		}
+	    }
+	} else {
+	    for (entryPtr = firstPtr; entryPtr != NULL; 
+		 entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
+		objPtr = NodeToObj(entryPtr->node);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+		if (entryPtr == lastPtr) {
+		    break;
+		}
+	    }
+	}
+    } else {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", 
+			 Tcl_GetString(objv[0]), " ",
+			 Tcl_GetString(objv[1]), " ", 
+			 Tcl_GetString(objv[2]), " tagOrId ?first last?", 
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EntryDeleteOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 5) {
+	int entryPos;
+	Blt_TreeNode node;
+	/*
+	 * Delete a single child node from a hierarchy specified 
+	 * by its numeric position.
+	 */
+	if (Blt_GetPositionFromObj(interp, objv[3], &entryPos) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (entryPos >= (int)Blt_TreeNodeDegree(entryPtr->node)) {
+	    return TCL_OK;	/* Bad first index */
+	}
+	if (entryPos == END) {
+	    node = Blt_TreeLastChild(entryPtr->node);
+	} else {
+	    node = GetNthNode(entryPtr->node, entryPos);
+	}
+	DeleteNode(tvPtr, node);
+    } else {
+	int firstPos, lastPos;
+	Blt_TreeNode node, first, last, next;
+	int nEntries;
+	/*
+	 * Delete range of nodes in hierarchy specified by first/last
+	 * positions.
+	 */
+	if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
+	    (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
+	    return TCL_ERROR;
+	}
+	nEntries = Blt_TreeNodeDegree(entryPtr->node);
+	if (nEntries == 0) {
+	    return TCL_OK;
+	}
+	if (firstPos == END) {
+	    firstPos = nEntries - 1;
+	}
+	if (firstPos >= nEntries) {
+	    Tcl_AppendResult(interp, "first position \"", 
+		Tcl_GetString(objv[4]), " is out of range", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if ((lastPos == END) || (lastPos >= nEntries)) {
+	    lastPos = nEntries - 1;
+	}
+	if (firstPos > lastPos) {
+	    Tcl_AppendResult(interp, "bad range: \"", Tcl_GetString(objv[4]), 
+		" > ", Tcl_GetString(objv[5]), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	first = GetNthNode(entryPtr->node, firstPos);
+	last = GetNthNode(entryPtr->node, lastPos);
+	for (node = first; node != NULL; node = next) {
+	    next = Blt_TreeNextSibling(node);
+	    DeleteNode(tvPtr, node);
+	    if (node == last) {
+		break;
+	    }
+	}
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntrySizeOp --
+ *
+ *	Counts the number of entries at this node.
+ *
+ * Results:
+ *	A standard Tcl result.  If an error occurred TCL_ERROR is
+ *	returned and interp->result will contain an error message.
+ *	Otherwise, TCL_OK is returned and interp->result contains
+ *	the number of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EntrySizeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int length, sum, recurse;
+    char *string;
+
+    recurse = FALSE;
+    string = Tcl_GetStringFromObj(objv[3], &length);
+    if ((string[0] == '-') && (length > 1) &&
+	(strncmp(string, "-recurse", length) == 0)) {
+	objv++, objc--;
+	recurse = TRUE;
+    }
+    if (objc == 3) {
+	Tcl_AppendResult(interp, "missing node argument: should be \"",
+	    Tcl_GetString(objv[0]), " entry open node\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (recurse) {
+	sum = Blt_TreeSize(entryPtr->node);
+    } else {
+	sum = Blt_TreeNodeDegree(entryPtr->node);
+    }
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(sum));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryOp --
+ *
+ *	This procedure handles entry operations.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_OpSpec entryOps[] =
+{
+    {"activate", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
+    /*bbox*/
+    /*bind*/
+    {"cget", 2, (Blt_Op)EntryCgetOp, 5, 5, "tagOrId option",},
+    {"children", 2, (Blt_Op)EntryChildrenOp, 4, 6, 
+	"tagOrId firstPos lastPos",},
+    /*close*/
+    {"configure", 2, (Blt_Op)EntryConfigureOp, 4, 0,
+	"tagOrId ?tagOrId...? ?option value?...",},
+    {"delete", 2, (Blt_Op)EntryDeleteOp, 5, 6, "tagOrId firstPos ?lastPos?",},
+    /*focus*/
+    /*hide*/
+    {"highlight", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
+    /*index*/
+    {"isbefore", 3, (Blt_Op)EntryIsBeforeOp, 5, 5, "tagOrId tagOrId",},
+    {"ishidden", 3, (Blt_Op)EntryIsHiddenOp, 4, 4, "tagOrId",},
+    {"isopen", 3, (Blt_Op)EntryIsOpenOp, 4, 4, "tagOrId",},
+    /*move*/
+    /*nearest*/
+    /*open*/
+    /*see*/
+    /*show*/
+    {"size", 1, (Blt_Op)EntrySizeOp, 4, 5, "?-recurse? tagOrId",},
+    /*toggle*/
+};
+static int nEntryOps = sizeof(entryOps) / sizeof(Blt_OpSpec);
+
+static int
+EntryOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nEntryOps, entryOps, BLT_OP_ARG2, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+/*ARGSUSED*/
+static int
+ExactCompare(interp, name, pattern)
+    Tcl_Interp *interp;		/* Not used. */
+    char *name;
+    char *pattern;
+{
+    return (strcmp(name, pattern) == 0);
+}
+
+/*ARGSUSED*/
+static int
+GlobCompare(interp, name, pattern)
+    Tcl_Interp *interp;		/* Not used. */
+    char *name;
+    char *pattern;
+{
+    return Tcl_StringMatch(name, pattern);
+}
+
+static int
+RegexpCompare(interp, name, pattern)
+    Tcl_Interp *interp;
+    char *name;
+    char *pattern;
+{
+    return Tcl_RegExpMatch(interp, name, pattern);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ *	Find one or more nodes based upon the pattern provided.
+ *
+ * Results:
+ *	A standard Tcl result.  The interpreter result will contain a
+ *	list of the node serial identifiers.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+FindOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *firstPtr, *lastPtr;
+    int nMatches, maxMatches;
+    char c;
+    int length;
+    TreeViewCompareProc *compareProc;
+    TreeViewIterProc *nextProc;
+    int invertMatch;		/* normal search mode (matching entries) */
+    char *namePattern, *fullPattern;
+    char *execCmd;
+    register int i;
+    int result;
+    char *pattern, *option;
+    Tcl_DString dString;
+    Blt_List options;
+    Blt_ListNode node;
+    char *addTag, *withTag;
+    register TreeViewEntry *entryPtr;
+    char *string;
+    Tcl_Obj *listObjPtr, *objPtr;
+
+    invertMatch = FALSE;
+    maxMatches = 0;
+    execCmd = namePattern = fullPattern = NULL;
+    compareProc = ExactCompare;
+    nextProc = Blt_TreeViewNextEntry;
+    options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+    withTag = addTag = NULL;
+
+    entryPtr = tvPtr->rootPtr;
+    /*
+     * Step 1:  Process flags for find operation.
+     */
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetStringFromObj(objv[i], &length);
+	if (string[0] != '-') {
+	    break;
+	}
+	option = string + 1;
+	length--;
+	c = option[0];
+	if ((c == 'e') && (length > 2) &&
+	    (strncmp(option, "exact", length) == 0)) {
+	    compareProc = ExactCompare;
+	} else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+	    compareProc = GlobCompare;
+	} else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+	    compareProc = RegexpCompare;
+	} else if ((c == 'n') && (length > 1) &&
+	    (strncmp(option, "nonmatching", length) == 0)) {
+	    invertMatch = TRUE;
+	} else if ((c == 'n') && (length > 1) &&
+	    (strncmp(option, "name", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    namePattern = Tcl_GetString(objv[i]);
+	} else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    fullPattern = Tcl_GetString(objv[i]);
+	} else if ((c == 'e') && (length > 2) &&
+	    (strncmp(option, "exec", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    execCmd = Tcl_GetString(objv[i]);
+	} else if ((c == 'a') && (length > 1) &&
+		   (strncmp(option, "addtag", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    addTag = Tcl_GetString(objv[i]);
+	} else if ((c == 't') && (length > 1) && 
+		   (strncmp(option, "tag", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    withTag = Tcl_GetString(objv[i]);
+	} else if ((c == 'c') && (strncmp(option, "count", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    if (Tcl_GetIntFromObj(interp, objv[i], &maxMatches) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (maxMatches < 0) {
+		Tcl_AppendResult(interp, "bad match count \"", objv[i],
+		    "\": should be a positive number", (char *)NULL);
+		Blt_ListDestroy(options);
+		return TCL_ERROR;
+	    }
+	} else if ((option[0] == '-') && (option[1] == '\0')) {
+	    break;
+	} else {
+	    /*
+	     * Verify that the switch is actually an entry configuration
+	     * option.
+	     */
+	    if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+		  bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0) 
+		!= TCL_OK) {
+		Tcl_ResetResult(interp);
+		Tcl_AppendResult(interp, "bad find switch \"", string, "\"",
+		    (char *)NULL);
+		Blt_ListDestroy(options);
+		return TCL_ERROR;
+	    }
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    /* Save the option in the list of configuration options */
+	    node = Blt_ListGetNode(options, (char *)objv[i]);
+	    if (node == NULL) {
+		node = Blt_ListCreateNode(options, (char *)objv[i]);
+		Blt_ListAppendNode(options, node);
+	    }
+	    i++;
+	    Blt_ListSetValue(node, Tcl_GetString(objv[i]));
+	}
+    }
+
+    if ((objc - i) > 2) {
+	Blt_ListDestroy(options);
+	Tcl_AppendResult(interp, "too many args", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /*
+     * Step 2:  Find the range of the search.  Check the order of two
+     *		nodes and arrange the search accordingly.
+     *
+     *	Note:	Be careful to treat "end" as the end of all nodes, instead
+     *		of the end of visible nodes.  That way, we can search the
+     *		entire tree, even if the last folder is closed.
+     */
+    firstPtr = tvPtr->rootPtr;	/* Default to root node */
+    lastPtr = LastEntry(tvPtr, firstPtr, 0);
+
+    if (i < objc) {
+	string = Tcl_GetString(objv[i]);
+	if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+	    firstPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
+	} else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &firstPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	i++;
+    }
+    if (i < objc) {
+	string = Tcl_GetString(objv[i]);
+	if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+	    lastPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
+	} else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &lastPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
+	nextProc = Blt_TreeViewPrevEntry;
+    }
+    nMatches = 0;
+
+    /*
+     * Step 3:	Search through the tree and look for nodes that match the
+     *		current pattern specifications.  Save the name of each of
+     *		the matching nodes.
+     */
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for (entryPtr = firstPtr; entryPtr != NULL; 
+	 entryPtr = (*nextProc) (entryPtr, 0)) {
+	if (namePattern != NULL) {
+	    result = (*compareProc)(interp, Blt_TreeNodeLabel(entryPtr->node),
+		     namePattern);
+	    if (result == invertMatch) {
+		goto nextEntry;	/* Failed to match */
+	    }
+	}
+	if (fullPattern != NULL) {
+	    Tcl_DString fullName;
+
+	    Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &fullName);
+	    result = (*compareProc) (interp, Tcl_DStringValue(&fullName), 
+		fullPattern);
+	    Tcl_DStringFree(&fullName);
+	    if (result == invertMatch) {
+		goto nextEntry;	/* Failed to match */
+	    }
+	}
+	if (withTag != NULL) {
+	    result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
+	    if (result == invertMatch) {
+		goto nextEntry;	/* Failed to match */
+	    }
+	}
+	for (node = Blt_ListFirstNode(options); node != NULL;
+	    node = Blt_ListNextNode(node)) {
+	    objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
+	    Tcl_ResetResult(interp);
+	    Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+		bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0);
+	    pattern = Blt_ListGetValue(node);
+	    objPtr = Tcl_GetObjResult(interp);
+	    result = (*compareProc) (interp, Tcl_GetString(objPtr), pattern);
+	    if (result == invertMatch) {
+		goto nextEntry;	/* Failed to match */
+	    }
+	}
+	/* 
+	 * Someone may actually delete the current node in the "exec"
+	 * callback.  Preserve the entry.
+	 */
+	Tcl_Preserve(entryPtr);
+	if (execCmd != NULL) {
+	    Tcl_DString cmdString;
+
+	    Blt_TreeViewPercentSubst(tvPtr, entryPtr, execCmd, &cmdString);
+	    result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
+	    Tcl_DStringFree(&cmdString);
+	    if (result != TCL_OK) {
+		Tcl_Release(entryPtr);
+		goto error;
+	    }
+	}
+	/* A NULL node reference in an entry indicates that the entry
+	 * was deleted, but its memory not released yet. */
+	if (entryPtr->node != NULL) {
+	    /* Finally, save the matching node name. */
+	    objPtr = NodeToObj(entryPtr->node);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    if (addTag != NULL) {
+		if (AddTag(tvPtr, entryPtr->node, addTag) != TCL_OK) {
+		    goto error;
+		}
+	    }
+	}
+	    
+	Tcl_Release(entryPtr);
+	nMatches++;
+	if ((nMatches == maxMatches) && (maxMatches > 0)) {
+	    break;
+	}
+      nextEntry:
+	if (entryPtr == lastPtr) {
+	    break;
+	}
+    }
+    Tcl_ResetResult(interp);
+    Blt_ListDestroy(options);
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+
+  missingArg:
+    Tcl_AppendResult(interp, "missing argument for find option \"", objv[i],
+	"\"", (char *)NULL);
+  error:
+    Tcl_DStringFree(&dString);
+    Blt_ListDestroy(options);
+    return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ *	Converts one or more node identifiers to its path component.
+ *	The path may be either the single entry name or the full path
+ *	of the entry.
+ *
+ * Results:
+ *	A standard Tcl result.  The interpreter result will contain a
+ *	list of the convert names.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewTagInfo info;
+    TreeViewEntry *entryPtr;
+    int useFullName;
+    register int i;
+    Tcl_DString dString1, dString2;
+    int count;
+
+    useFullName = FALSE;
+    if (objc > 2) {
+	char *string;
+
+	string = Tcl_GetString(objv[2]);
+	if ((string[0] == '-') && (strcmp(string, "-full") == 0)) {
+	    useFullName = TRUE;
+	    objv++, objc--;
+	}
+    }
+    Tcl_DStringInit(&dString1);
+    Tcl_DStringInit(&dString2);
+    count = 0;
+    for (i = 2; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    Tcl_DStringSetLength(&dString2, 0);
+	    count++;
+	    if (entryPtr->node == NULL) {
+		Tcl_DStringAppendElement(&dString1, "");
+		continue;
+	    }
+	    if (useFullName) {
+		Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString2);
+		Tcl_DStringAppendElement(&dString1, 
+			 Tcl_DStringValue(&dString2));
+	    } else {
+		Tcl_DStringAppendElement(&dString1, 
+			 Blt_TreeNodeLabel(entryPtr->node));
+	    }
+	}
+    }
+    /* This handles the single element list problem. */
+    if (count == 1) {
+	Tcl_DStringResult(interp, &dString2);
+	Tcl_DStringFree(&dString1);
+    } else {
+	Tcl_DStringResult(interp, &dString1);
+	Tcl_DStringFree(&dString2);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SearchAndApplyToTree --
+ *
+ *	Searches through the current tree and applies a procedure
+ *	to matching nodes.  The search specification is taken from
+ *	the following command-line arguments:
+ *
+ *      ?-exact? ?-glob? ?-regexp? ?-nonmatching?
+ *      ?-data string?
+ *      ?-name string?
+ *      ?-full string?
+ *      ?--?
+ *      ?inode...?
+ *
+ * Results:
+ *	A standard Tcl result.  If the result is valid, and if the
+ *      nonmatchPtr is specified, it returns a boolean value
+ *      indicating whether or not the search was inverted.  This
+ *      is needed to fix things properly for the "hide nonmatching"
+ *      case.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SearchAndApplyToTree(tvPtr, interp, objc, objv, proc, nonMatchPtr)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+    TreeViewApplyProc *proc;
+    int *nonMatchPtr;		/* returns: inverted search indicator */
+{
+    TreeViewCompareProc *compareProc;
+    int invertMatch;		/* normal search mode (matching entries) */
+    char *namePattern, *fullPattern;
+    register int i;
+    int length;
+    int result;
+    char *option, *pattern;
+    char c;
+    Blt_List options;
+    TreeViewEntry *entryPtr;
+    register Blt_ListNode node;
+    char *string;
+    char *withTag;
+    Tcl_Obj *objPtr;
+    TreeViewTagInfo info;
+
+    options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+    invertMatch = FALSE;
+    namePattern = fullPattern = NULL;
+    compareProc = ExactCompare;
+    withTag = NULL;
+
+    entryPtr = tvPtr->rootPtr;
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetStringFromObj(objv[i], &length);
+	if (string[0] != '-') {
+	    break;
+	}
+	option = string + 1;
+	length--;
+	c = option[0];
+	if ((c == 'e') && (strncmp(option, "exact", length) == 0)) {
+	    compareProc = ExactCompare;
+	} else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
+	    compareProc = GlobCompare;
+	} else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
+	    compareProc = RegexpCompare;
+	} else if ((c == 'n') && (length > 1) &&
+	    (strncmp(option, "nonmatching", length) == 0)) {
+	    invertMatch = TRUE;
+	} else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    fullPattern = Tcl_GetString(objv[i]);
+	} else if ((c == 'n') && (length > 1) &&
+	    (strncmp(option, "name", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    namePattern = Tcl_GetString(objv[i]);
+	} else if ((c == 't') && (length > 1) && 
+		   (strncmp(option, "tag", length) == 0)) {
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    i++;
+	    withTag = Tcl_GetString(objv[i]);
+	} else if ((option[0] == '-') && (option[1] == '\0')) {
+	    break;
+	} else {
+	    /*
+	     * Verify that the switch is actually an entry configuration option.
+	     */
+	    if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+		bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0) 
+		!= TCL_OK) {
+		Tcl_ResetResult(interp);
+		Tcl_AppendResult(interp, "bad switch \"", string,
+	    "\": must be -exact, -glob, -regexp, -name, -full, or -nonmatching",
+		    (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    if ((i + 1) == objc) {
+		goto missingArg;
+	    }
+	    /* Save the option in the list of configuration options */
+	    node = Blt_ListGetNode(options, (char *)objv[i]);
+	    if (node == NULL) {
+		node = Blt_ListCreateNode(options, (char *)objv[i]);
+		Blt_ListAppendNode(options, node);
+	    }
+	    i++;
+	    Blt_ListSetValue(node, Tcl_GetString(objv[i]));
+	}
+    }
+
+    if ((namePattern != NULL) || (fullPattern != NULL) ||
+	(Blt_ListGetLength(options) > 0)) {
+	/*
+	 * Search through the tree and look for nodes that match the
+	 * current spec.  Apply the input procedure to each of the
+	 * matching nodes.
+	 */
+	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
+	    if (namePattern != NULL) {
+		result = (*compareProc) (interp, 
+			Blt_TreeNodeLabel(entryPtr->node), namePattern);
+		if (result == invertMatch) {
+		    continue;	/* Failed to match */
+		}
+	    }
+	    if (fullPattern != NULL) {
+		Tcl_DString dString;
+
+		Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
+		result = (*compareProc) (interp, Tcl_DStringValue(&dString), 
+			fullPattern);
+		Tcl_DStringFree(&dString);
+		if (result == invertMatch) {
+		    continue;	/* Failed to match */
+		}
+	    }
+	    if (withTag != NULL) {
+		result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
+		if (result == invertMatch) {
+		    continue;	/* Failed to match */
+		}
+	    }
+	    for (node = Blt_ListFirstNode(options); node != NULL;
+		node = Blt_ListNextNode(node)) {
+		objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
+		Tcl_ResetResult(interp);
+		if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+			bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0) 
+		    != TCL_OK) {
+		    return TCL_ERROR;	/* This shouldn't happen. */
+		}
+		pattern = Blt_ListGetValue(node);
+		objPtr = Tcl_GetObjResult(interp);
+		result = (*compareProc)(interp, Tcl_GetString(objPtr), pattern);
+		if (result == invertMatch) {
+		    continue;	/* Failed to match */
+		}
+	    }
+	    /* Finally, apply the procedure to the node */
+	    (*proc) (tvPtr, entryPtr);
+	}
+	Tcl_ResetResult(interp);
+	Blt_ListDestroy(options);
+    }
+    /*
+     * Apply the procedure to nodes that have been specified
+     * individually.
+     */
+    for ( /*empty*/ ; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    if (nonMatchPtr != NULL) {
+	*nonMatchPtr = invertMatch;	/* return "inverted search" status */
+    }
+    return TCL_OK;
+
+  missingArg:
+    Blt_ListDestroy(options);
+    Tcl_AppendResult(interp, "missing pattern for search option \"", objv[i],
+	"\"", (char *)NULL);
+    return TCL_ERROR;
+
+}
+
+static int
+FixSelectionsApplyProc(tvPtr, entryPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+{
+    if (entryPtr->flags & ENTRY_HIDDEN) {
+	Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
+	if ((tvPtr->focusPtr != NULL) &&
+	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+	    if (entryPtr != tvPtr->rootPtr) {
+		entryPtr = Blt_TreeViewParentEntry(entryPtr);
+		tvPtr->focusPtr = (entryPtr == NULL) 
+		    ? tvPtr->focusPtr : entryPtr;
+		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
+	    }
+	}
+	if ((tvPtr->selAnchorPtr != NULL) &&
+	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->selAnchorPtr->node))) {
+	    tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
+	}
+	if ((tvPtr->activePtr != NULL) &&
+	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
+	    tvPtr->activePtr = NULL;
+	}
+	Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HideOp --
+ *
+ *	Hides one or more nodes.  Nodes can be specified by their
+ *      inode, or by matching a name or data value pattern.  By
+ *      default, the patterns are matched exactly.  They can also
+ *      be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+HideOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int status, nonmatching;
+
+    status = SearchAndApplyToTree(tvPtr, interp, objc, objv, 
+	HideEntryApplyProc, &nonmatching);
+
+    if (status != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /*
+     * If this was an inverted search, scan back through the
+     * tree and make sure that the parents for all visible
+     * nodes are also visible.  After all, if a node is supposed
+     * to be visible, its parent can't be hidden.
+     */
+    if (nonmatching) {
+	Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, MapAncestorsApplyProc, 0);
+    }
+    /*
+     * Make sure that selections are cleared from any hidden
+     * nodes.  This wasn't done earlier--we had to delay it until
+     * we fixed the visibility status for the parents.
+     */
+    Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, FixSelectionsApplyProc, 0);
+
+    /* Hiding an entry only effects the visible nodes. */
+    tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ *	Mark one or more nodes to be exposed.  Nodes can be specified
+ *	by their inode, or by matching a name or data value pattern.  By
+ *      default, the patterns are matched exactly.  They can also
+ *      be matched using glob-style and regular expression rules.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShowOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (SearchAndApplyToTree(tvPtr, interp, objc, objv, ShowEntryApplyProc,
+	    (int *)NULL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *	Converts one of more words representing indices of the entries
+ *	in the treeview widget to their respective serial identifiers.
+ *
+ * Results:
+ *	A standard Tcl result.  Interp->result will contain the
+ *	identifier of each inode found. If an inode could not be found,
+ *	then the serial identifier will be the empty string.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    char *string;
+    TreeViewEntry *fromPtr;
+    int usePath;
+
+    usePath = FALSE;
+    fromPtr = NULL;
+    string = Tcl_GetString(objv[2]);
+    if ((string[0] == '-') && (strcmp(string, "-path") == 0)) {
+	usePath = TRUE;
+	objv++, objc--;
+    }
+    if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
+	if (Blt_TreeViewGetEntry(tvPtr, objv[3], &fromPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	objv += 2, objc -= 2;
+    }
+    if (objc != 3) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", 
+		Tcl_GetString(objv[0]), 
+		" index ?-at tagOrId? ?-path? tagOrId\"", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    tvPtr->fromPtr = fromPtr;
+    if (tvPtr->fromPtr == NULL) {
+	tvPtr->fromPtr = tvPtr->focusPtr;
+    }
+    if (tvPtr->fromPtr == NULL) {
+	tvPtr->fromPtr = tvPtr->rootPtr;
+    }
+    if (usePath) {
+	if (fromPtr == NULL) {
+	    fromPtr = tvPtr->rootPtr;
+	}
+	string = Tcl_GetString(objv[2]);
+	entryPtr = FindPath(tvPtr, fromPtr, string);
+	if (entryPtr != NULL) {
+	    Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+	}
+    } else {
+	if ((GetEntryFromObj2(tvPtr, objv[2], &entryPtr) == TCL_OK) && 
+	    (entryPtr != NULL)) {
+	    Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *	Add new entries into a hierarchy.  If no node is specified,
+ *	new entries will be added to the root of the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+InsertOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode node, parent;
+    int insertPos;
+    int depth, count;
+    char *path;
+    Tcl_Obj *CONST *options;
+    Tcl_Obj *listObjPtr;
+    char **compArr;
+    register char **p;
+    register int n;
+    TreeViewEntry *rootPtr;
+    char *string;
+
+    rootPtr = tvPtr->rootPtr;
+    string = Tcl_GetString(objv[2]);
+    if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
+	if (objc > 2) {
+	    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &rootPtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    objv += 2, objc -= 2;
+	} else {
+	    Tcl_AppendResult(interp, "missing argument for \"-at\" flag",
+		     (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    if (objc == 2) {
+	Tcl_AppendResult(interp, "missing position argument", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Blt_GetPositionFromObj(interp, objv[2], &insertPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    node = NULL;
+    objc -= 3, objv += 3;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    while (objc > 0) {
+	path = Tcl_GetString(objv[0]);
+	objv++, objc--;
+
+	/*
+	 * Count the option-value pairs that follow.  Count until we
+	 * spot one that looks like an entry name (i.e. doesn't start
+	 * with a minus "-").
+	 */
+	for (count = 0; count < objc; count += 2) {
+	    string = Tcl_GetString(objv[count]);
+	    if (string[0] != '-') {
+		break;
+	    }
+	}
+	if (count > objc) {
+	    count = objc;
+	}
+	options = objv;
+	objc -= count, objv += count;
+
+	if (tvPtr->trimLeft != NULL) {
+	    register char *s1, *s2;
+
+	    /* Trim off leading character string if one exists. */
+	    for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+		if (*s1 != *s2) {
+		    break;
+		}
+	    }
+	    if (*s2 == '\0') {
+		path = s1;
+	    }
+	}
+	/*
+	 * Split the path and find the parent node of the path.
+	 */
+	compArr = &path;
+	depth = 1;
+	if (tvPtr->pathSep != SEPARATOR_NONE) {
+	    if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
+		goto error;
+	    }
+	    if (depth == 0) {
+		Blt_Free(compArr);
+		continue;		/* Root already exists. */
+	    }
+	}
+	parent = rootPtr->node;
+	depth--;		
+
+	/* Verify each component in the path preceding the tail.  */
+	for (n = 0, p = compArr; n < depth; n++, p++) {
+	    node = Blt_TreeFindChild(parent, *p);
+	    if (node == NULL) {
+		if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
+		    Tcl_AppendResult(interp, "can't find path component \"",
+		         *p, "\" in \"", path, "\"", (char *)NULL);
+		    goto error;
+		}
+		node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
+		if (node == NULL) {
+		    goto error;
+		}
+	    }
+	    parent = node;
+	}
+	node = NULL;
+	if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) && 
+	    (Blt_TreeFindChild(parent, *p) != NULL)) {
+	    Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
+		 path, "\"", (char *)NULL);
+	    goto error;
+	}
+	node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
+	if (node == NULL) {
+	    goto error;
+	}
+	if (Blt_TreeViewCreateEntry(tvPtr, node, count, options, 0) != TCL_OK) {
+	    goto error;
+	}
+	if (compArr != &path) {
+	    Blt_Free(compArr);
+	}
+	Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+
+  error:
+    if (compArr != &path) {
+	Blt_Free(compArr);
+    }
+    Tcl_DecrRefCount(listObjPtr);
+    if (node != NULL) {
+	DeleteNode(tvPtr, node);
+    }
+    return TCL_ERROR;
+}
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddOp --
+ *
+ *	Add new entries into a hierarchy.  If no node is specified,
+ *	new entries will be added to the root of the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Blt_SwitchParseProc StringToChild;
+#define INSERT_BEFORE	(ClientData)0
+#define INSERT_AFTER	(ClientData)1
+static Blt_SwitchCustom beforeSwitch =
+{
+    StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
+};
+static Blt_SwitchCustom afterSwitch =
+{
+    StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
+};
+
+typedef struct {
+    int insertPos;
+    Blt_TreeNode parent;
+} InsertData;
+
+static Blt_SwitchSpec insertSwitches[] = 
+{
+    {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0, 
+	&afterSwitch},
+    {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
+    {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
+	&beforeSwitch},
+    {BLT_SWITCH_END, NULL, 0, 0}
+};
+
+static int
+AddOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode node, parent;
+    int insertPos;
+    int depth, count;
+    char *path;
+    Tcl_Obj *CONST *options;
+    Tcl_Obj *listObjPtr;
+    char **compArr;
+    register char **p;
+    register int n;
+    TreeViewEntry *rootPtr;
+    char *string;
+
+    memset(&data, 0, sizeof(data));
+    data.maxDepth = -1;
+    data.cmdPtr = cmdPtr;
+
+    /* Process any leading switches  */
+    i = Blt_ProcessObjSwitches(interp, addSwitches, objc - 2, objv + 2, 
+	     (char *)&data, BLT_CONFIG_OBJV_PARTIAL);
+    if (i < 0) {
+	return TCL_ERROR;
+    }
+    i += 2;
+    /* Should have at the starting node */
+    if (i >= objc) {
+	Tcl_AppendResult(interp, "starting node argument is missing", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Blt_TreeViewGetEntry(tvPtr, objv[i], &rootPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    objv += i, objc -= i;
+    node = NULL;
+
+    /* Process sections of path ?options? */
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    while (objc > 0) {
+	path = Tcl_GetString(objv[0]);
+	objv++, objc--;
+	/*
+	 * Count the option-value pairs that follow.  Count until we
+	 * spot one that looks like an entry name (i.e. doesn't start
+	 * with a minus "-").
+	 */
+	for (count = 0; count < objc; count += 2) {
+	    if (!Blt_ObjIsOption(bltTreeViewEntrySpecs, objv[count], 0)) {
+		break;
+	    }
+	}
+	if (count > objc) {
+	    count = objc;
+	}
+	options = objv;
+	objc -= count, objv += count;
+
+	if (tvPtr->trimLeft != NULL) {
+	    register char *s1, *s2;
+
+	    /* Trim off leading character string if one exists. */
+	    for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
+		if (*s1 != *s2) {
+		    break;
+		}
+	    }
+	    if (*s2 == '\0') {
+		path = s1;
+	    }
+	}
+	/*
+	 * Split the path and find the parent node of the path.
+	 */
+	compArr = &path;
+	depth = 1;
+	if (tvPtr->pathSep != SEPARATOR_NONE) {
+	    if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
+		goto error;
+	    }
+	    if (depth == 0) {
+		Blt_Free(compArr);
+		continue;		/* Root already exists. */
+	    }
+	}
+	parent = rootPtr->node;
+	depth--;		
+
+	/* Verify each component in the path preceding the tail.  */
+	for (n = 0, p = compArr; n < depth; n++, p++) {
+	    node = Blt_TreeFindChild(parent, *p);
+	    if (node == NULL) {
+		if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
+		    Tcl_AppendResult(interp, "can't find path component \"",
+		         *p, "\" in \"", path, "\"", (char *)NULL);
+		    goto error;
+		}
+		node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
+		if (node == NULL) {
+		    goto error;
+		}
+	    }
+	    parent = node;
+	}
+	node = NULL;
+	if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) && 
+	    (Blt_TreeFindChild(parent, *p) != NULL)) {
+	    Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
+		 path, "\"", (char *)NULL);
+	    goto error;
+	}
+	node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
+	if (node == NULL) {
+	    goto error;
+	}
+	if (Blt_TreeViewCreateEntry(tvPtr, node, count, options, 0) != TCL_OK) {
+	    goto error;
+	}
+	if (compArr != &path) {
+	    Blt_Free(compArr);
+	}
+	Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+
+  error:
+    if (compArr != &path) {
+	Blt_Free(compArr);
+    }
+    Tcl_DecrRefCount(listObjPtr);
+    if (node != NULL) {
+	DeleteNode(tvPtr, node);
+    }
+    return TCL_ERROR;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes nodes from the hierarchy. Deletes one or more entries 
+ *	(except root). In all cases, nodes are removed recursively.
+ *
+ *	Note: There's no need to explicitly clean up Entry structures 
+ *	      or request a redraw of the widget. When a node is 
+ *	      deleted in the tree, all of the Tcl_Objs representing
+ *	      the various data fields are also removed.  The treeview 
+ *	      widget store the Entry structure in a data field. So it's
+ *	      automatically cleaned up when FreeEntryInternalRep is
+ *	      called.
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewTagInfo info;
+    TreeViewEntry *entryPtr;
+    register int i;
+
+    for (i = 2; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    if (entryPtr == tvPtr->rootPtr) {
+		Blt_TreeNode next, node;
+
+		/* 
+		 *   Don't delete the root node.  We implicitly assume
+		 *   that even an empty tree has at a root.  Instead
+		 *   delete all the children regardless if they're closed
+		 *   or hidden.
+		 */
+		for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL; 
+		     node = next) {
+		    next = Blt_TreeNextSibling(node);
+		    DeleteNode(tvPtr, node);
+		}
+	    } else {
+		DeleteNode(tvPtr, entryPtr->node);
+	    }
+	}
+    } 
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MoveOp --
+ *
+ *	Move an entry into a new location in the hierarchy.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MoveOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeNode parent;
+    TreeViewEntry *srcPtr, *destPtr;
+    char c;
+    int action;
+    char *string;
+    TreeViewTagInfo info;
+
+#define MOVE_INTO	(1<<0)
+#define MOVE_BEFORE	(1<<1)
+#define MOVE_AFTER	(1<<2)
+    if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    string = Tcl_GetString(objv[3]);
+    c = string[0];
+    if ((c == 'i') && (strcmp(string, "into") == 0)) {
+	action = MOVE_INTO;
+    } else if ((c == 'b') && (strcmp(string, "before") == 0)) {
+	action = MOVE_BEFORE;
+    } else if ((c == 'a') && (strcmp(string, "after") == 0)) {
+	action = MOVE_AFTER;
+    } else {
+	Tcl_AppendResult(interp, "bad position \"", string,
+	    "\": should be into, before, or after", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (Blt_TreeViewGetEntry(tvPtr, objv[4], &destPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (srcPtr = Blt_TreeViewFirstTaggedEntry(&info); srcPtr != NULL; 
+	 srcPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	/* Verify they aren't ancestors. */
+	if (Blt_TreeIsAncestor(srcPtr->node, destPtr->node)) {
+	    Tcl_DString dString;
+	    char *path;
+
+	    path = Blt_TreeViewGetFullName(tvPtr, srcPtr, 1, &dString);
+	    Tcl_AppendResult(interp, "can't move node: \"", path, 
+			"\" is an ancestor of \"", Tcl_GetString(objv[4]), 
+			"\"", (char *)NULL);
+	    Tcl_DStringFree(&dString);
+	    return TCL_ERROR;
+	}
+	parent = Blt_TreeNodeParent(destPtr->node);
+	if (parent == NULL) {
+	    action = MOVE_INTO;
+	}
+	switch (action) {
+	case MOVE_INTO:
+	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, destPtr->node, 
+			     (Blt_TreeNode)NULL);
+	    break;
+	    
+	case MOVE_BEFORE:
+	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent, destPtr->node);
+	    break;
+	    
+	case MOVE_AFTER:
+	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent, 
+			     Blt_TreeNextSibling(destPtr->node));
+	    break;
+	}
+    }
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NearestOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewButton *buttonPtr = &tvPtr->button;
+    int x, y;			/* Screen coordinates of the test point. */
+    register TreeViewEntry *entryPtr;
+    int isRoot;
+    char *string;
+
+    isRoot = FALSE;
+    string = Tcl_GetString(objv[2]);
+    if (strcmp("-root", string) == 0) {
+	isRoot = TRUE;
+	objv++, objc--;
+    } 
+    if (objc < 4) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", 
+		Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]), 
+		" ?-root? x y\"", (char *)NULL);
+	return TCL_ERROR;
+			 
+    }
+    if ((Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[2], &x) != TCL_OK) ||
+	(Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (tvPtr->nVisible == 0) {
+	return TCL_OK;
+    }
+    if (isRoot) {
+	int rootX, rootY;
+
+	Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
+	x -= rootX;
+	y -= rootY;
+    }
+    entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
+    if (entryPtr == NULL) {
+	return TCL_OK;
+    }
+    x = WORLDX(tvPtr, x);
+    y = WORLDY(tvPtr, y);
+    if (objc > 4) {
+	char *where;
+	int labelX, labelY, depth;
+	TreeViewIcon icon;
+
+	where = "";
+	if (entryPtr->flags & ENTRY_HAS_BUTTON) {
+	    int buttonX, buttonY;
+
+	    buttonX = entryPtr->worldX + entryPtr->buttonX;
+	    buttonY = entryPtr->worldY + entryPtr->buttonY;
+	    if ((x >= buttonX) && (x < (buttonX + buttonPtr->width)) &&
+		(y >= buttonY) && (y < (buttonY + buttonPtr->height))) {
+		where = "button";
+		goto done;
+	    }
+	} 
+	depth = DEPTH(tvPtr, entryPtr->node);
+
+	icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
+	if (icon != NULL) {
+	    int iconWidth, iconHeight, entryHeight;
+	    int iconX, iconY;
+	    
+	    entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
+	    iconHeight = TreeViewIconHeight(icon);
+	    iconWidth = TreeViewIconWidth(icon);
+	    iconX = entryPtr->worldX + ICONWIDTH(depth);
+	    iconY = entryPtr->worldY;
+	    if (tvPtr->flatView) {
+		iconX += (ICONWIDTH(0) - iconWidth) / 2;
+	    } else {
+		iconX += (ICONWIDTH(depth + 1) - iconWidth) / 2;
+	    }	    
+	    iconY += (entryHeight - iconHeight) / 2;
+	    if ((x >= iconX) && (x <= (iconX + iconWidth)) &&
+		(y >= iconY) && (y < (iconY + iconHeight))) {
+		where = "icon";
+		goto done;
+	    }
+	}
+	labelX = entryPtr->worldX + ICONWIDTH(depth);
+	labelY = entryPtr->worldY;
+	if (!tvPtr->flatView) {
+	    labelX += ICONWIDTH(depth + 1) + 4;
+	}	    
+	if ((x >= labelX) && (x < (labelX + entryPtr->labelWidth)) &&
+	    (y >= labelY) && (y < (labelY + entryPtr->labelHeight))) {
+	    where = "label";
+	}
+    done:
+	if (Tcl_SetVar(interp, Tcl_GetString(objv[4]), where, 
+		TCL_LEAVE_ERR_MSG) == NULL) {
+	    return TCL_ERROR;
+	}
+    }
+    Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+    return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+OpenOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    TreeViewTagInfo info;
+    int recurse, result;
+    register int i;
+
+    recurse = FALSE;
+    if (objc > 2) {
+	int length;
+	char *string;
+
+	string = Tcl_GetStringFromObj(objv[2], &length);
+	if ((string[0] == '-') && (length > 1) && 
+	    (strncmp(string, "-recurse", length) == 0)) {
+	    objv++, objc--;
+	    recurse = TRUE;
+	}
+    }
+    for (i = 2; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    if (recurse) {
+		result = Blt_TreeViewApply(tvPtr, entryPtr, 
+					   Blt_TreeViewOpenEntry, 0);
+	    } else {
+		result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
+	    }
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    /* Make sure ancestors of this node aren't hidden. */
+	    MapAncestors(tvPtr, entryPtr);
+	}
+    }
+    /*FIXME: This is only for flattened entries.  */
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ *	Returns the node identifiers in a given range.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+RangeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr, *firstPtr, *lastPtr;
+    unsigned int mask;
+    int length;
+    Tcl_Obj *listObjPtr, *objPtr;
+    char *string;
+
+    mask = 0;
+    string = Tcl_GetStringFromObj(objv[2], &length);
+    if ((string[0] == '-') && (length > 1) && 
+	(strncmp(string, "-open", length) == 0)) {
+	objv++, objc--;
+	mask |= ENTRY_CLOSED;
+    }
+    if (Blt_TreeViewGetEntry(tvPtr, objv[2], &firstPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc > 3) {
+	if (Blt_TreeViewGetEntry(tvPtr, objv[3], &lastPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    } else {
+	lastPtr = LastEntry(tvPtr, firstPtr, mask);
+    }    
+    if (mask & ENTRY_CLOSED) {
+	if (firstPtr->flags & ENTRY_HIDDEN) {
+	    Tcl_AppendResult(interp, "first node \"", Tcl_GetString(objv[2]), 
+		"\" is hidden.", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (lastPtr->flags & ENTRY_HIDDEN) {
+	    Tcl_AppendResult(interp, "last node \"", Tcl_GetString(objv[3]), 
+		"\" is hidden.", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+
+    /*
+     * The relative order of the first/last markers determines the
+     * direction.
+     */
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
+	for (entryPtr = lastPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
+	    objPtr = NodeToObj(entryPtr->node);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    if (entryPtr == firstPtr) {
+		break;
+	    }
+	}
+    } else {
+	for (entryPtr = firstPtr; entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
+	    objPtr = NodeToObj(entryPtr->node);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    if (entryPtr == lastPtr) {
+		break;
+	    }
+	}
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScanOp --
+ *
+ *	Implements the quick scan.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ScanOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int x, y;
+    char c;
+    int length;
+    int oper;
+    char *string;
+    Tk_Window tkwin;
+
+#define SCAN_MARK	1
+#define SCAN_DRAGTO	2
+    string = Tcl_GetStringFromObj(objv[2], &length);
+    c = string[0];
+    tkwin = tvPtr->tkwin;
+    if ((c == 'm') && (strncmp(string, "mark", length) == 0)) {
+	oper = SCAN_MARK;
+    } else if ((c == 'd') && (strncmp(string, "dragto", length) == 0)) {
+	oper = SCAN_DRAGTO;
+    } else {
+	Tcl_AppendResult(interp, "bad scan operation \"", string,
+	    "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((Blt_GetPixelsFromObj(interp, tkwin, objv[3], 0, &x) != TCL_OK) ||
+	(Blt_GetPixelsFromObj(interp, tkwin, objv[4], 0, &y) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (oper == SCAN_MARK) {
+	tvPtr->scanAnchorX = x;
+	tvPtr->scanAnchorY = y;
+	tvPtr->scanX = tvPtr->xOffset;
+	tvPtr->scanY = tvPtr->yOffset;
+    } else {
+	int worldX, worldY;
+	int dx, dy;
+
+	dx = tvPtr->scanAnchorX - x;
+	dy = tvPtr->scanAnchorY - y;
+	worldX = tvPtr->scanX + (10 * dx);
+	worldY = tvPtr->scanY + (10 * dy);
+
+	if (worldX < 0) {
+	    worldX = 0;
+	} else if (worldX >= tvPtr->worldWidth) {
+	    worldX = tvPtr->worldWidth - tvPtr->xScrollUnits;
+	}
+	if (worldY < 0) {
+	    worldY = 0;
+	} else if (worldY >= tvPtr->worldHeight) {
+	    worldY = tvPtr->worldHeight - tvPtr->yScrollUnits;
+	}
+	tvPtr->xOffset = worldX;
+	tvPtr->yOffset = worldY;
+	tvPtr->flags |= TV_SCROLL;
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SeeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int width, height;
+    int x, y;
+    Tk_Anchor anchor;
+    int left, right, top, bottom;
+    char *string;
+
+    string = Tcl_GetString(objv[2]);
+    anchor = TK_ANCHOR_W;	/* Default anchor is West */
+    if ((string[0] == '-') && (strcmp(string, "-anchor") == 0)) {
+	if (objc == 3) {
+	    Tcl_AppendResult(interp, "missing \"-anchor\" argument",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Tk_GetAnchorFromObj(interp, objv[3], &anchor) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	objc -= 2, objv += 2;
+    }
+    if (objc == 2) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", objv[0],
+	    "see ?-anchor anchor? tagOrId\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (entryPtr == NULL) {
+	return TCL_OK;
+    }
+    if (entryPtr->flags & ENTRY_HIDDEN) {
+	MapAncestors(tvPtr, entryPtr);
+	tvPtr->flags |= TV_SCROLL;
+	/*
+	 * If the entry wasn't previously exposed, its world coordinates
+	 * aren't likely to be valid.  So re-compute the layout before
+	 * we try to see the viewport to the entry's location.
+	 */
+	Blt_TreeViewComputeLayout(tvPtr);
+    }
+    width = VPORTWIDTH(tvPtr);
+    height = VPORTHEIGHT(tvPtr);
+
+    /*
+     * XVIEW:	If the entry is left or right of the current view, adjust
+     *		the offset.  If the entry is nearby, adjust the view just
+     *		a bit.  Otherwise, center the entry.
+     */
+    left = tvPtr->xOffset;
+    right = tvPtr->xOffset + width;
+
+    switch (anchor) {
+    case TK_ANCHOR_W:
+    case TK_ANCHOR_NW:
+    case TK_ANCHOR_SW:
+	x = 0;
+	break;
+    case TK_ANCHOR_E:
+    case TK_ANCHOR_NE:
+    case TK_ANCHOR_SE:
+	x = entryPtr->worldX + entryPtr->width + 
+	    ICONWIDTH(DEPTH(tvPtr, entryPtr->node)) - width;
+	break;
+    default:
+	if (entryPtr->worldX < left) {
+	    x = entryPtr->worldX;
+	} else if ((entryPtr->worldX + entryPtr->width) > right) {
+	    x = entryPtr->worldX + entryPtr->width - width;
+	} else {
+	    x = tvPtr->xOffset;
+	}
+	break;
+    }
+    /*
+     * YVIEW:	If the entry is above or below the current view, adjust
+     *		the offset.  If the entry is nearby, adjust the view just
+     *		a bit.  Otherwise, center the entry.
+     */
+    top = tvPtr->yOffset;
+    bottom = tvPtr->yOffset + height;
+
+    switch (anchor) {
+    case TK_ANCHOR_N:
+	y = tvPtr->yOffset;
+	break;
+    case TK_ANCHOR_NE:
+    case TK_ANCHOR_NW:
+	y = entryPtr->worldY - (height / 2);
+	break;
+    case TK_ANCHOR_S:
+    case TK_ANCHOR_SE:
+    case TK_ANCHOR_SW:
+	y = entryPtr->worldY + entryPtr->height - height;
+	break;
+    default:
+	if (entryPtr->worldY < top) {
+	    y = entryPtr->worldY;
+	} else if ((entryPtr->worldY + entryPtr->height) > bottom) {
+	    y = entryPtr->worldY + entryPtr->height - height;
+	} else {
+	    y = tvPtr->yOffset;
+	}
+	break;
+    }
+    if ((y != tvPtr->yOffset) || (x != tvPtr->xOffset)) {
+	/* tvPtr->xOffset = x; */
+	tvPtr->yOffset = y;
+	tvPtr->flags |= TV_SCROLL;
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+void
+Blt_TreeViewClearSelection(tvPtr)
+    TreeView *tvPtr;
+{
+    Blt_DeleteHashTable(&tvPtr->selectTable);
+    Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
+    Blt_ChainReset(tvPtr->selChainPtr);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    if (tvPtr->selectCmd != NULL) {
+	EventuallyInvokeSelectCmd(tvPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LostSelection --
+ *
+ *	This procedure is called back by Tk when the selection is grabbed
+ *	away.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The existing selection is unhighlighted, and the window is
+ *	marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LostSelection(clientData)
+    ClientData clientData;	/* Information about the widget. */
+{
+    TreeView *tvPtr = clientData;
+
+    if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
+	return;
+    }
+    Blt_TreeViewClearSelection(tvPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectRange --
+ *
+ *	Sets the selection flag for a range of nodes.  The range is
+ *	determined by two pointers which designate the first/last
+ *	nodes of the range.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectRange(tvPtr, fromPtr, toPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *fromPtr, *toPtr;
+{
+    if (tvPtr->flatView) {
+	register int i;
+
+	if (fromPtr->flatIndex > toPtr->flatIndex) {
+	    for (i = fromPtr->flatIndex; i >= toPtr->flatIndex; i--) {
+		SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
+	    }
+	} else {
+	    for (i = fromPtr->flatIndex; i <= toPtr->flatIndex; i++) {
+		SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
+	    }
+	}	    
+    } else {
+	TreeViewEntry *entryPtr;
+	TreeViewIterProc *proc;
+	/* From the range determine the direction to select entries. */
+
+	proc = (Blt_TreeIsBefore(toPtr->node, fromPtr->node)) 
+	    ? Blt_TreeViewPrevEntry : Blt_TreeViewNextEntry;
+	for (entryPtr = fromPtr; entryPtr != NULL;
+	     entryPtr = (*proc)(entryPtr, ENTRY_MASK)) {
+	    SelectEntryApplyProc(tvPtr, entryPtr);
+	    if (entryPtr == toPtr) {
+		break;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionAnchorOp --
+ *
+ *	Sets the selection anchor to the element given by a index.
+ *	The selection anchor is the end of the selection that is fixed
+ *	while dragging out a selection with the mouse.  The index
+ *	"anchor" may be used to refer to the anchor element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionAnchorOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+
+    if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* Set both the anchor and the mark. Indicates that a single entry
+     * is selected. */
+    tvPtr->selAnchorPtr = entryPtr;
+    tvPtr->selMarkPtr = NULL;
+    if (entryPtr != NULL) {
+	Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionClearallOp
+ *
+ *	Clears the entire selection.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionClearallOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    Blt_TreeViewClearSelection(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionIncludesOp
+ *
+ *	Returns 1 if the element indicated by index is currently
+ *	selected, 0 if it isn't.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionIncludesOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int bool;
+
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    bool = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionMarkOp --
+ *
+ *	Sets the selection mark to the element given by a index.
+ *	The selection anchor is the end of the selection that is movable
+ *	while dragging out a selection with the mouse.  The index
+ *	"mark" may be used to refer to the anchor element.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionMarkOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+
+    if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tvPtr->selAnchorPtr == NULL) {
+	Tcl_AppendResult(interp, "selection anchor must be set first", 
+		 (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (tvPtr->selMarkPtr != entryPtr) {
+	Blt_ChainLink *linkPtr, *nextPtr;
+	TreeViewEntry *selectPtr;
+
+	/* Deselect entry from the list all the way back to the anchor. */
+	for (linkPtr = Blt_ChainLastLink(tvPtr->selChainPtr); linkPtr != NULL; 
+	     linkPtr = nextPtr) {
+	    nextPtr = Blt_ChainPrevLink(linkPtr);
+	    selectPtr = Blt_ChainGetValue(linkPtr);
+	    if (selectPtr == tvPtr->selAnchorPtr) {
+		break;
+	    }
+	    Blt_TreeViewDeselectEntry(tvPtr, selectPtr);
+	}
+	tvPtr->flags &= ~TV_SELECT_MASK;
+	tvPtr->flags |= TV_SELECT_SET;
+	SelectRange(tvPtr, tvPtr->selAnchorPtr, entryPtr);
+	Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
+	tvPtr->selMarkPtr = entryPtr;
+
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+	if (tvPtr->selectCmd != NULL) {
+	    EventuallyInvokeSelectCmd(tvPtr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionPresentOp
+ *
+ *	Returns 1 if there is a selection and 0 if it isn't.
+ *
+ * Results:
+ *	A standard Tcl result.  interp->result will contain a
+ *	boolean string indicating if there is a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionPresentOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int bool;
+
+    bool = (Blt_ChainGetLength(tvPtr->selChainPtr) > 0);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionSetOp
+ *
+ *	Selects, deselects, or toggles all of the elements in the
+ *	range between first and last, inclusive, without affecting the
+ *	selection state of elements outside that range.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionSetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *firstPtr, *lastPtr;
+    char *string;
+
+    tvPtr->flags &= ~TV_SELECT_MASK;
+    string = Tcl_GetString(objv[2]);
+    switch (string[0]) {
+    case 's':
+	tvPtr->flags |= TV_SELECT_SET;
+	break;
+    case 'c':
+	tvPtr->flags |= TV_SELECT_CLEAR;
+	break;
+    case 't':
+	tvPtr->flags |= TV_SELECT_TOGGLE;
+	break;
+    }
+    if (Blt_TreeViewGetEntry(tvPtr, objv[3], &firstPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((firstPtr->flags & ENTRY_HIDDEN) && 
+	(!(tvPtr->flags & TV_SELECT_CLEAR))) {
+	Tcl_AppendResult(interp, "can't select hidden node \"", 
+		Tcl_GetString(objv[3]), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    lastPtr = firstPtr;
+    if (objc > 4) {
+	if (Blt_TreeViewGetEntry(tvPtr, objv[4], &lastPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((lastPtr->flags & ENTRY_HIDDEN) && 
+		(!(tvPtr->flags & TV_SELECT_CLEAR))) {
+	    Tcl_AppendResult(interp, "can't select hidden node \"", 
+		     Tcl_GetString(objv[4]), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+    }
+    if (firstPtr == lastPtr) {
+	SelectEntryApplyProc(tvPtr, firstPtr);
+    } else {
+	SelectRange(tvPtr, firstPtr, lastPtr);
+    }
+    /* Set both the anchor and the mark. Indicates that a single entry
+     * is selected. */
+    if (tvPtr->selAnchorPtr == NULL) {
+	tvPtr->selAnchorPtr = firstPtr;
+    }
+    if (tvPtr->flags & TV_SELECT_EXPORT) {
+	Tk_OwnSelection(tvPtr->tkwin, XA_PRIMARY, LostSelection, tvPtr);
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    if (tvPtr->selectCmd != NULL) {
+	EventuallyInvokeSelectCmd(tvPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ *	This procedure handles the individual options for text
+ *	selections.  The selected text is designated by start and end
+ *	indices into the text pool.  The selected segment has both a
+ *	anchored and unanchored ends.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec selectionOps[] =
+{
+    {"anchor", 1, (Blt_Op)SelectionAnchorOp, 4, 4, "tagOrId",},
+    {"clear", 5, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+    {"clearall", 6, (Blt_Op)SelectionClearallOp, 3, 3, "",},
+    {"includes", 1, (Blt_Op)SelectionIncludesOp, 4, 4, "tagOrId",},
+    {"mark", 1, (Blt_Op)SelectionMarkOp, 4, 4, "tagOrId",},
+    {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, "",},
+    {"set", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+    {"toggle", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
+};
+static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
+
+static int
+SelectionOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2, 
+	objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagForgetOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TagForgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+
+    for (i = 3; i < objc; i++) {
+	Blt_TreeForgetTag(tvPtr->tree, Tcl_GetString(objv[i]));
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNamesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagNamesOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Tcl_Obj *listObjPtr, *objPtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    objPtr = Tcl_NewStringObj("all", -1);
+    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    if (objc == 3) {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	Blt_TreeTagEntry *tPtr;
+
+	objPtr = Tcl_NewStringObj("root", -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL;
+	     hPtr = Blt_NextHashEntry(&cursor)) {
+	    tPtr = Blt_GetHashValue(hPtr);
+	    objPtr = Tcl_NewStringObj(tPtr->tagName, -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	}
+    } else {
+	register int i;
+	TreeViewEntry *entryPtr;
+	Blt_List list;
+	Blt_ListNode listNode;
+
+	for (i = 3; i < objc; i++) {
+	    if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
+	    Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list);
+	    for (listNode = Blt_ListFirstNode(list); listNode != NULL; 
+		 listNode = Blt_ListNextNode(listNode)) {
+		objPtr = Tcl_NewStringObj(Blt_ListGetKey(listNode), -1);
+		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    }
+	    Blt_ListDestroy(list);
+	}
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagNodesOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagNodesOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Blt_HashTable nodeTable;
+    Blt_TreeNode node;
+    TreeViewTagInfo info;
+    Tcl_Obj *listObjPtr;
+    Tcl_Obj *objPtr;
+    TreeViewEntry *entryPtr;
+    int isNew;
+    register int i;
+
+    Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
+    for (i = 3; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    Blt_CreateHashEntry(&nodeTable, (char *)entryPtr->node, &isNew);
+	}
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL; 
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
+	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    Blt_DeleteHashTable(&nodeTable);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagAddOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static int
+TagAddOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    register int i;
+    char *tagName;
+    TreeViewTagInfo info;
+
+    tagName = Tcl_GetString(objv[3]);
+    tvPtr->fromPtr = NULL;
+    if (strcmp(tagName, "root") == 0) {
+	Tcl_AppendResult(interp, "can't add reserved tag \"", tagName, "\"", 
+		(char *)NULL);
+	return TCL_ERROR;
+    }
+    if (isdigit(UCHAR(tagName[0]))) {
+	Tcl_AppendResult(interp, "invalid tag \"", tagName, 
+		 "\": can't start with digit", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (tagName[0] == '@') {
+	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName, 
+		"\": can't start with \"@\"", (char *)NULL);
+	return TCL_ERROR;
+    } 
+    if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
+	Tcl_AppendResult(interp, "invalid tag \"", tagName, 
+		 "\": is a special id", (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (i = 4; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    if (AddTag(tvPtr, entryPtr->node, tagName) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagDeleteOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+/*ARGSUSED*/
+static int
+TagDeleteOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    char *tagName;
+    Blt_HashTable *tablePtr;
+    TreeViewTagInfo info;
+
+    tagName = Tcl_GetString(objv[3]);
+    tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
+    if (tablePtr != NULL) {
+        register int i;
+        Blt_HashEntry *hPtr;
+	TreeViewEntry *entryPtr;
+
+        for (i = 4; i < objc; i++) {
+	    if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info)!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); 
+		entryPtr != NULL; 
+		entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	        hPtr = Blt_FindHashEntry(tablePtr, (char *)entryPtr->node);
+	        if (hPtr != NULL) {
+		    Blt_DeleteHashEntry(tablePtr, hPtr);
+	        }
+	   }
+       }
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagOp --
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec tagOps[] = {
+    {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag id...",},
+    {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag id...",},
+    {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
+    {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?id...?",}, 
+    {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
+};
+
+static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
+
+static int
+TagOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv, 
+	0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc)(tvPtr, interp, objc, objv);
+    return result;
+}
+
+/*ARGSUSED*/
+static int
+ToggleOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    TreeViewTagInfo info;
+
+    if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	if (entryPtr == NULL) {
+	    return TCL_OK;
+	}
+	if (entryPtr->flags & ENTRY_CLOSED) {
+	    Blt_TreeViewOpenEntry(tvPtr, entryPtr);
+	} else {
+	    Blt_TreeViewPruneSelection(tvPtr, entryPtr);
+	    if ((tvPtr->focusPtr != NULL) && 
+		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
+		tvPtr->focusPtr = entryPtr;
+		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
+	    }
+	    if ((tvPtr->selAnchorPtr != NULL) &&
+		(Blt_TreeIsAncestor(entryPtr->node, 
+				    tvPtr->selAnchorPtr->node))) {
+		tvPtr->selAnchorPtr = NULL;
+	    }
+	    Blt_TreeViewCloseEntry(tvPtr, entryPtr);
+	}
+    }
+    tvPtr->flags |= TV_SCROLL;
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+static int
+XViewOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int width, worldWidth;
+
+    width = VPORTWIDTH(tvPtr);
+    worldWidth = tvPtr->worldWidth;
+    if (objc == 2) {
+	double fract;
+	Tcl_Obj *listObjPtr;
+
+	/*
+	 * Note that we are bounding the fractions between 0.0 and 1.0
+	 * to support the "canvas"-style of scrolling.
+	 */
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	fract = (double)tvPtr->xOffset / worldWidth;
+	fract = CLAMP(fract, 0.0, 1.0);
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+	fract = (double)(tvPtr->xOffset + width) / worldWidth;
+	fract = CLAMP(fract, 0.0, 1.0);
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+	Tcl_SetObjResult(interp, listObjPtr);
+	return TCL_OK;
+    }
+    if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->xOffset,
+	    worldWidth, width, tvPtr->xScrollUnits, tvPtr->scrollMode) 
+	    != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tvPtr->flags |= TV_XSCROLL;
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+static int
+YViewOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int height, worldHeight;
+
+    height = VPORTHEIGHT(tvPtr);
+    worldHeight = tvPtr->worldHeight;
+    if (objc == 2) {
+	double fract;
+	Tcl_Obj *listObjPtr;
+
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	/* Report first and last fractions */
+	fract = (double)tvPtr->yOffset / worldHeight;
+	fract = CLAMP(fract, 0.0, 1.0);
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+	fract = (double)(tvPtr->yOffset + height) / worldHeight;
+	fract = CLAMP(fract, 0.0, 1.0);
+	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
+	Tcl_SetObjResult(interp, listObjPtr);
+	return TCL_OK;
+    }
+    if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->yOffset,
+	    worldHeight, height, tvPtr->yScrollUnits, tvPtr->scrollMode)
+	!= TCL_OK) {
+	return TCL_ERROR;
+    }
+    tvPtr->flags |= TV_SCROLL;
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Blt_TreeViewWidgetInstCmd --
+ *
+ * 	This procedure is invoked to process commands on behalf of
+ *	the treeview widget.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * --------------------------------------------------------------
+ */
+static Blt_OpSpec treeViewOps[] =
+{
+    {"bbox", 2, (Blt_Op)BboxOp, 3, 0, "tagOrId...",}, 
+    {"bind", 2, (Blt_Op)BindOp, 3, 5, "tagName ?sequence command?",}, 
+    {"button", 2, (Blt_Op)ButtonOp, 2, 0, "args",},
+    {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",}, 
+    {"close", 2, (Blt_Op)CloseOp, 2, 0, "?-recurse? tagOrId...",}, 
+    {"column", 3, (Blt_Op)Blt_TreeViewColumnOp, 2, 0, "oper args",}, 
+    {"configure", 3, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
+    {"curselection", 2, (Blt_Op)CurselectionOp, 2, 2, "",},
+    {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "tagOrId ?tagOrId...?",}, 
+    {"edit", 2, (Blt_Op)EditOp, 4, 6, "?-root|-test? x y",},
+    {"entry", 2, (Blt_Op)EntryOp, 2, 0, "oper args",},
+    {"find", 2, (Blt_Op)FindOp, 2, 0, "?flags...? ?first last?",}, 
+    {"focus", 2, (Blt_Op)FocusOp, 3, 3, "tagOrId",}, 
+    {"get", 1, (Blt_Op)GetOp, 2, 0, "?-full? tagOrId ?tagOrId...?",}, 
+    {"hide", 1, (Blt_Op)HideOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
+    {"index", 3, (Blt_Op)IndexOp, 3, 6, "?-at tagOrId? ?-path? string",},
+    {"insert", 3, (Blt_Op)InsertOp, 3, 0, "?-at tagOrId? position label ?label...? ?option value?",},
+    {"move", 1, (Blt_Op)MoveOp, 5, 5, "tagOrId into|before|after tagOrId",},
+    {"nearest", 1, (Blt_Op)NearestOp, 4, 5, "x y ?varName?",}, 
+    {"open", 1, (Blt_Op)OpenOp, 2, 0, "?-recurse? tagOrId...",}, 
+    {"range", 1, (Blt_Op)RangeOp, 4, 5, "?-open? tagOrId tagOrId",},
+    {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
+    {"see", 3, (Blt_Op)SeeOp, 3, 0, "?-anchor anchor? tagOrId",},
+    {"selection", 3, (Blt_Op)SelectionOp, 2, 0, "oper args",},
+    {"show", 2, (Blt_Op)ShowOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
+    {"sort", 2, (Blt_Op)Blt_TreeViewSortOp, 2, 0, "args",},
+    {"style", 2, (Blt_Op)Blt_TreeViewStyleOp, 2, 0, "args",},
+    {"tag", 2, (Blt_Op)TagOp, 2, 0, "oper args",},
+    {"toggle", 2, (Blt_Op)ToggleOp, 3, 3, "tagOrId",},
+    {"xview", 1, (Blt_Op)XViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+    {"yview", 1, (Blt_Op)YViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
+};
+
+static int nTreeViewOps = sizeof(treeViewOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_TreeViewWidgetInstCmd(clientData, interp, objc, objv)
+    ClientData clientData;	/* Information about the widget. */
+    Tcl_Interp *interp;		/* Interpreter to report errors back to. */
+    int objc;			/* Number of arguments. */
+    Tcl_Obj *CONST *objv;	/* Vector of argument strings. */
+{
+    Blt_Op proc;
+    TreeView *tvPtr = clientData;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nTreeViewOps, treeViewOps, BLT_OP_ARG1, 
+	objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    Tcl_Preserve(tvPtr);
+    result = (*proc) (tvPtr, interp, objc, objv);
+    Tcl_Release(tvPtr);
+    return result;
+}
+
+#endif /* NO_TREEVIEW */
Index: trunk/kitgen/8.x/blt/generic/bltTreeViewColumn.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeViewColumn.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeViewColumn.c	(revision 175)
@@ -0,0 +1,2047 @@
+
+/*
+ * bltTreeViewColumn.c --
+ *
+ *	This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+/*
+ * TODO:
+ *
+ * BUGS:
+ *   1.  "open" operation should change scroll offset so that as many
+ *	 new entries (up to half a screen) can be seen.
+ *   2.  "open" needs to adjust the scrolloffset so that the same entry
+ *	 is seen at the same place.
+ */
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include <X11/Xutil.h>
+
+#define RULE_AREA		(8)
+
+static Blt_OptionParseProc ObjToColumn;
+static Blt_OptionPrintProc ColumnToObj;
+static Blt_OptionParseProc ObjToData;
+static Blt_OptionPrintProc DataToObj;
+
+static char *sortTypeStrings[] = {
+    "dictionary", "ascii", "integer", "real", "command", "none", NULL
+};
+
+enum SortTypeValues { 
+    SORT_TYPE_DICTIONARY, SORT_TYPE_ASCII, SORT_TYPE_INTEGER, 
+    SORT_TYPE_REAL, SORT_TYPE_COMMAND, SORT_TYPE_NONE
+};
+
+#define DEF_SORT_COLUMN		(char *)NULL
+#define DEF_SORT_COMMAND	(char *)NULL
+#define DEF_SORT_DECREASING	"no"
+#define DEF_SORT_TYPE		"dictionary"
+
+#ifdef WIN32
+#define DEF_COLUMN_ACTIVE_TITLE_BG	RGB_GREY85
+#else
+#define DEF_COLUMN_ACTIVE_TITLE_BG	RGB_GREY90
+#endif
+#define DEF_COLUMN_ACTIVE_TITLE_FG	STD_ACTIVE_FOREGROUND
+#define DEF_COLUMN_BACKGROUND		(char *)NULL
+#define DEF_COLUMN_BIND_TAGS		"all"
+#define DEF_COLUMN_BORDERWIDTH		STD_BORDERWIDTH
+#define DEF_COLUMN_COLOR		RGB_BLACK
+#define DEF_COLUMN_EDIT			"yes"
+#define DEF_COLUMN_FONT			STD_FONT
+#define DEF_COLUMN_COMMAND		(char *)NULL
+#define DEF_COLUMN_FORMAT_COMMAND	(char *)NULL
+#define DEF_COLUMN_HIDE			"no"
+#define DEF_COLUMN_JUSTIFY		"center"
+#define DEF_COLUMN_MAX			"0"
+#define DEF_COLUMN_MIN			"0"
+#define DEF_COLUMN_PAD			"2"
+#define DEF_COLUMN_RELIEF		"flat"
+#define DEF_COLUMN_STATE		"normal"
+#define DEF_COLUMN_STYLE		"text"
+#define DEF_COLUMN_TITLE_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_COLUMN_TITLE_BORDERWIDTH	STD_BORDERWIDTH
+#define DEF_COLUMN_TITLE_FONT		STD_FONT
+#define DEF_COLUMN_TITLE_FOREGROUND	STD_NORMAL_FOREGROUND
+#define DEF_COLUMN_TITLE_RELIEF		"raised"
+#define DEF_COLUMN_WEIGHT		"1.0"
+#define DEF_COLUMN_WIDTH		"0"
+#define DEF_COLUMN_RULE_DASHES		"dot"
+
+extern Blt_OptionParseProc Blt_ObjToEnum;
+extern Blt_OptionPrintProc Blt_EnumToObj;
+
+static Blt_CustomOption typeOption =
+{
+    Blt_ObjToEnum, Blt_EnumToObj, NULL, (ClientData)sortTypeStrings
+};
+
+static Blt_CustomOption columnOption =
+{
+    ObjToColumn, ColumnToObj, NULL, (ClientData)0
+};
+
+Blt_CustomOption bltTreeViewDataOption =
+{
+    ObjToData, DataToObj, NULL, (ClientData)0,
+};
+
+static Blt_OptionParseProc ObjToStyle;
+static Blt_OptionPrintProc StyleToObj;
+static Blt_OptionFreeProc FreeStyle;
+static Blt_CustomOption styleOption =
+{
+    /* Contains a pointer to the widget that's currently being
+     * configured.  This is used in the custom configuration parse
+     * routine for icons.  */
+    ObjToStyle, StyleToObj, FreeStyle, NULL,
+};
+
+extern Blt_CustomOption bltTreeViewUidOption;
+extern Blt_CustomOption bltTreeViewIconOption;
+static Blt_TreeApplyProc SortApplyProc;
+
+static Blt_ConfigSpec columnSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-activetitlebackground", "activeTitleBackground", 
+	"Background", DEF_COLUMN_ACTIVE_TITLE_BG, 
+	Blt_Offset(TreeViewColumn, activeTitleBorder), 0},
+    {BLT_CONFIG_COLOR, "-activetitleforeground", "activeTitleForeground", 
+	"Foreground", DEF_COLUMN_ACTIVE_TITLE_FG, 
+	Blt_Offset(TreeViewColumn, activeTitleFgColor), 0},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_COLUMN_BACKGROUND, Blt_Offset(TreeViewColumn, border), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+	DEF_COLUMN_BIND_TAGS, Blt_Offset(TreeViewColumn, tagsUid),
+	BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
+    {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_COLUMN_BORDERWIDTH, Blt_Offset(TreeViewColumn, borderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_STRING, "-command", "command", "Command",
+	DEF_COLUMN_COMMAND, Blt_Offset(TreeViewColumn, titleCmd),
+	BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK}, 
+    {BLT_CONFIG_BOOLEAN, "-edit", "edit", "Edit",
+	DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, editable), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+	DEF_COLUMN_HIDE, Blt_Offset(TreeViewColumn, hidden),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-icon", "icon", "icon",
+	(char *)NULL, Blt_Offset(TreeViewColumn, titleIcon),
+	BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewIconOption},
+    {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+	DEF_COLUMN_JUSTIFY, Blt_Offset(TreeViewColumn, justify), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-max", "max", "Max",
+	DEF_COLUMN_MAX, Blt_Offset(TreeViewColumn, reqMax), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-min", "min", "Min",
+	DEF_COLUMN_MIN, Blt_Offset(TreeViewColumn, reqMin), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_PAD, "-pad", "pad", "Pad",
+	DEF_COLUMN_PAD, Blt_Offset(TreeViewColumn, pad), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_COLUMN_RELIEF, Blt_Offset(TreeViewColumn, relief), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DASHES, "-ruledashes", "ruleDashes", "RuleDashes",
+	DEF_COLUMN_RULE_DASHES, Blt_Offset(TreeViewColumn, ruleDashes),
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_STRING, "-sortcommand", "sortCommand", "SortCommand",
+	DEF_SORT_COMMAND, Blt_Offset(TreeViewColumn, sortCmd), 
+	BLT_CONFIG_NULL_OK}, 
+    {BLT_CONFIG_STATE, "-state", "state", "State",
+	DEF_COLUMN_STATE, Blt_Offset(TreeViewColumn, state), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CUSTOM, "-style", "style", "Style",
+	DEF_COLUMN_STYLE, Blt_Offset(TreeViewColumn, stylePtr), 
+        0, &styleOption},
+    {BLT_CONFIG_STRING, "-text", "text", "Text",
+	(char *)NULL, Blt_Offset(TreeViewColumn, title), 0},
+    {BLT_CONFIG_STRING, "-title", "title", "Title",
+	(char *)NULL, Blt_Offset(TreeViewColumn, title), 0},
+    {BLT_CONFIG_BORDER, "-titlebackground", "titleBackground", 
+	"TitleBackground", DEF_COLUMN_TITLE_BACKGROUND, 
+	Blt_Offset(TreeViewColumn, titleBorder),0},
+    {BLT_CONFIG_DISTANCE, "-titleborderwidth", "BorderWidth", 
+	"TitleBorderWidth", DEF_COLUMN_TITLE_BORDERWIDTH, 
+	Blt_Offset(TreeViewColumn, titleBorderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+	DEF_COLUMN_TITLE_FONT, Blt_Offset(TreeViewColumn, titleFont), 0},
+    {BLT_CONFIG_COLOR, "-titleforeground", "titleForeground", "TitleForeground",
+	DEF_COLUMN_TITLE_FOREGROUND, 
+	Blt_Offset(TreeViewColumn, titleFgColor), 0},
+    {BLT_CONFIG_RELIEF, "-titlerelief", "titleRelief", "TitleRelief",
+	DEF_COLUMN_TITLE_RELIEF, Blt_Offset(TreeViewColumn, titleRelief), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_SHADOW, "-titleshadow", "titleShadow", "TitleShadow",
+	(char *)NULL, Blt_Offset(TreeViewColumn, titleShadow), 0},
+    {BLT_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
+	DEF_COLUMN_WEIGHT, Blt_Offset(TreeViewColumn, weight), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
+	DEF_COLUMN_WIDTH, Blt_Offset(TreeViewColumn, reqWidth), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+static Blt_ConfigSpec sortSpecs[] =
+{
+    {BLT_CONFIG_STRING, "-command", "command", "Command",
+	DEF_SORT_COMMAND, Blt_Offset(TreeView, sortCmd),
+	BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_CUSTOM, "-column", "column", "Column",
+	DEF_SORT_COLUMN, Blt_Offset(TreeView, sortColumnPtr),
+	BLT_CONFIG_DONT_SET_DEFAULT, &columnOption},
+    {BLT_CONFIG_BOOLEAN, "-decreasing", "decreasing", "Decreasing",
+	DEF_SORT_DECREASING, Blt_Offset(TreeView, sortDecreasing),
+        BLT_CONFIG_DONT_SET_DEFAULT}, 
+    {BLT_CONFIG_CUSTOM, "-mode", "mode", "Mode",
+	DEF_SORT_TYPE, Blt_Offset(TreeView, sortType), 0, &typeOption},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+static Blt_TreeCompareNodesProc CompareNodes;
+static Blt_TreeApplyProc SortApplyProc;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToColumn --
+ *
+ *	Convert the string reprsenting a scroll mode, to its numeric
+ *	form.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToColumn(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;		/* New legend position string */
+    char *widgRec;
+    int offset;
+{
+    TreeViewColumn **columnPtrPtr = (TreeViewColumn **)(widgRec + offset);
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if (*string == '\0') {
+	*columnPtrPtr = NULL;
+    } else {
+	TreeView *tvPtr = (TreeView *)widgRec;
+
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objPtr, columnPtrPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnToString --
+ *
+ * Results:
+ *	The string representation of the button boolean is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ColumnToObj(clientData, interp, tkwin, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    TreeViewColumn *columnPtr = *(TreeViewColumn **)(widgRec + offset);
+
+    if (columnPtr == NULL) {
+	return bltEmptyStringObjPtr;
+    }
+    return Tcl_NewStringObj(columnPtr->key, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToData --
+ *
+ *	Convert the string reprsenting a scroll mode, to its numeric
+ *	form.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left
+ *	in interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToData(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Node of entry. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;		/* Tcl_Obj representing new data. */
+    char *widgRec;
+    int offset;
+{
+    Tcl_Obj **objv;
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+    char *string;
+    int objc;
+    register int i;
+
+    string = Tcl_GetString(objPtr);
+    if (*string == '\0') {
+	return TCL_OK;
+    } 
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 0) {
+	return TCL_OK;
+    }
+    if (objc & 0x1) {
+	Tcl_AppendResult(interp, "data \"", string, 
+		 "\" must be in even name-value pairs", (char *)NULL);
+	return TCL_ERROR;
+    }
+    for (i = 0; i < objc; i += 2) {
+	TreeView *tvPtr = entryPtr->tvPtr;
+
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objv[i], &columnPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node, 
+		columnPtr->key, objv[i + 1]) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Blt_TreeViewAddValue(entryPtr, columnPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DataToObj --
+ *
+ * Results:
+ *	The string representation of the data is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+DataToObj(clientData, interp, tkwin, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    Tcl_Obj *listObjPtr, *objPtr;
+    TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
+    TreeViewValue *valuePtr;
+
+    /* Add the key-value pairs to a new Tcl_Obj */
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for (valuePtr = entryPtr->values; valuePtr != NULL; 
+	valuePtr = valuePtr->nextPtr) {
+	objPtr = Tcl_NewStringObj(valuePtr->columnPtr->key, -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	if (Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key, &objPtr)
+	    != TCL_OK) {
+	    objPtr = bltEmptyStringObjPtr;
+	} 
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    return listObjPtr;
+}
+
+int
+Blt_TreeViewGetColumn(interp, tvPtr, objPtr, columnPtrPtr)
+    Tcl_Interp *interp;
+    TreeView *tvPtr;
+    Tcl_Obj *objPtr;
+    TreeViewColumn **columnPtrPtr;
+{
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if (strcmp(string, "treeView") == 0) {
+	*columnPtrPtr = &tvPtr->treeColumn;
+    } else {
+	Blt_HashEntry *hPtr;
+    
+	hPtr = Blt_FindHashEntry(&tvPtr->columnTable, Blt_TreeGetKey(string));
+	if (hPtr == NULL) {
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "can't find column \"", string, 
+			"\" in \"", Tk_PathName(tvPtr->tkwin), "\"", 
+			(char *)NULL);
+	    }
+	    return TCL_ERROR;
+	} 
+	*columnPtrPtr = Blt_GetHashValue(hPtr);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToStyle --
+ *
+ *	Convert the name of an icon into a treeview style.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToStyle(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;		/* Tcl_Obj representing the new value. */
+    char *widgRec;
+    int offset;
+{
+    TreeView *tvPtr = clientData;
+    TreeViewStyle **stylePtrPtr = (TreeViewStyle **)(widgRec + offset);
+    TreeViewStyle *stylePtr;
+
+    if (Blt_TreeViewGetStyle(interp, tvPtr, Tcl_GetString(objPtr), 
+	     &stylePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    stylePtr->flags |= STYLE_DIRTY;
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+    *stylePtrPtr = stylePtr;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconToObj --
+ *
+ *	Converts the icon into its string representation (its name).
+ *
+ * Results:
+ *	The name of the icon is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+StyleToObj(clientData, interp, tkwin, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    TreeViewStyle *stylePtr = *(TreeViewStyle **)(widgRec + offset);
+
+    if (stylePtr == NULL) {
+	return bltEmptyStringObjPtr;
+    }
+    return Tcl_NewStringObj(stylePtr->name, -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeStyle(clientData, display, widgRec, offset)
+    ClientData clientData;
+    Display *display;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    TreeView *tvPtr = clientData;
+    TreeViewStyle *stylePtr = *(TreeViewStyle **)(widgRec + offset);
+
+    Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+}
+
+void
+Blt_TreeViewUpdateColumnGCs(tvPtr, columnPtr)
+    TreeView *tvPtr;
+    TreeViewColumn *columnPtr;
+{
+    Drawable drawable;
+    GC newGC;
+    Tk_3DBorder border;
+    XGCValues gcValues;
+    int ruleDrawn;
+    unsigned long gcMask;
+    int iconWidth, iconHeight;
+    int textWidth, textHeight;
+
+    gcMask = GCForeground | GCFont;
+    gcValues.font = Tk_FontId(columnPtr->titleFont);
+
+    /* Normal title text */
+    gcValues.foreground = columnPtr->titleFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (columnPtr->titleGC != NULL) {
+	Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+    }
+    columnPtr->titleGC = newGC;
+
+    /* Active title text */
+    gcValues.foreground = columnPtr->activeTitleFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (columnPtr->activeTitleGC != NULL) {
+	Tk_FreeGC(tvPtr->display, columnPtr->activeTitleGC);
+    }
+    columnPtr->activeTitleGC = newGC;
+
+    columnPtr->titleWidth = 0;
+    iconWidth = iconHeight = 0;
+    if (columnPtr->titleIcon != NULL) {
+	iconWidth = TreeViewIconWidth(columnPtr->titleIcon);
+	iconHeight = TreeViewIconHeight(columnPtr->titleIcon);
+	columnPtr->titleWidth += iconWidth;
+    }
+    if (columnPtr->titleTextPtr != NULL) {
+	Blt_Free(columnPtr->titleTextPtr);
+	columnPtr->titleTextPtr = NULL;
+    }
+    textWidth = textHeight = 0;
+    if (columnPtr->title != NULL) {
+	TextStyle ts;
+
+	memset(&ts, 0, sizeof(TextStyle));
+	ts.font = columnPtr->titleFont;
+	ts.justify = TK_JUSTIFY_LEFT;
+	ts.shadow.offset = columnPtr->titleShadow.offset;
+	columnPtr->titleTextPtr = Blt_GetTextLayout(columnPtr->title, &ts);
+	textHeight = columnPtr->titleTextPtr->height;
+	textWidth = columnPtr->titleTextPtr->width;
+	columnPtr->titleWidth += textWidth;
+    }
+    if ((iconWidth > 0) && (textWidth > 0)) {
+	columnPtr->titleWidth += 8;
+    }
+    columnPtr->titleWidth += STD_ARROW_HEIGHT;
+    columnPtr->titleHeight = MAX(iconHeight, textHeight);
+
+    gcMask = (GCFunction | GCLineWidth | GCLineStyle | GCForeground);
+
+    /* 
+     * If the rule is active, turn it off (i.e. draw again to erase
+     * it) before changing the GC.  If the color changes, we won't be
+     * able to erase the old line, since it will no longer be
+     * correctly XOR-ed with the background.
+     */
+    drawable = Tk_WindowId(tvPtr->tkwin);
+    ruleDrawn = ((tvPtr->flags & TV_RULE_ACTIVE) &&
+		 (tvPtr->activeTitleColumnPtr == columnPtr) && 
+		 (drawable != None));
+    if (ruleDrawn) {
+	Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+    }
+    /* XOR-ed rule column divider */ 
+    gcValues.line_width = LineWidth(columnPtr->ruleLineWidth);
+    gcValues.foreground = 
+	Blt_TreeViewGetStyleFg(tvPtr, columnPtr->stylePtr)->pixel;
+    if (LineIsDashed(columnPtr->ruleDashes)) {
+	gcValues.line_style = LineOnOffDash;
+    } else {
+	gcValues.line_style = LineSolid;
+    }
+    gcValues.function = GXxor;
+
+    border = CHOOSE(tvPtr->border, columnPtr->border);
+    gcValues.foreground ^= Tk_3DBorderColor(border)->pixel; 
+    newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (columnPtr->ruleGC != NULL) {
+	Blt_FreePrivateGC(tvPtr->display, columnPtr->ruleGC);
+    }
+    if (LineIsDashed(columnPtr->ruleDashes)) {
+	Blt_SetDashes(tvPtr->display, newGC, &columnPtr->ruleDashes);
+    }
+    columnPtr->ruleGC = newGC;
+    if (ruleDrawn) {
+	Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+    }
+    columnPtr->flags |= COLUMN_DIRTY;
+    tvPtr->flags |= TV_UPDATE;
+}
+
+static void
+DestroyColumn(tvPtr, columnPtr)
+    TreeView *tvPtr;
+    TreeViewColumn *columnPtr;
+{
+    Blt_HashEntry *hPtr;
+
+    bltTreeViewUidOption.clientData = tvPtr;
+    bltTreeViewIconOption.clientData = tvPtr;
+    styleOption.clientData = tvPtr;
+    Blt_FreeObjOptions(columnSpecs, (char *)columnPtr, tvPtr->display, 0);
+
+    if (columnPtr->titleGC != NULL) {
+	Tk_FreeGC(tvPtr->display, columnPtr->titleGC);
+    }
+    if (columnPtr->ruleGC != NULL) {
+	Blt_FreePrivateGC(tvPtr->display, columnPtr->ruleGC);
+    }
+    hPtr = Blt_FindHashEntry(&tvPtr->columnTable, columnPtr->key);
+    if (hPtr != NULL) {
+	Blt_DeleteHashEntry(&tvPtr->columnTable, hPtr);
+    }
+    if (columnPtr->linkPtr != NULL) {
+	Blt_ChainDeleteLink(tvPtr->colChainPtr, columnPtr->linkPtr);
+    }
+    if (columnPtr->title != NULL) {
+	Blt_Free(columnPtr->title);
+    }
+    if (columnPtr->titleTextPtr != NULL) {
+	Blt_Free(columnPtr->titleTextPtr);
+    }
+    if (columnPtr->stylePtr != NULL) {
+	Blt_TreeViewFreeStyle(tvPtr, columnPtr->stylePtr);
+    }
+    if (columnPtr != &tvPtr->treeColumn) {
+	Blt_Free(columnPtr);
+    }
+}
+
+void
+Blt_TreeViewDestroyColumns(tvPtr)
+    TreeView *tvPtr;
+{
+    if (tvPtr->colChainPtr != NULL) {
+	Blt_ChainLink *linkPtr;
+	TreeViewColumn *columnPtr;
+	
+	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	     linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    columnPtr->linkPtr = NULL;
+	    DestroyColumn(tvPtr, columnPtr);
+	}
+	Blt_ChainDestroy(tvPtr->colChainPtr);
+	tvPtr->colChainPtr = NULL;
+    }
+    Blt_DeleteHashTable(&tvPtr->columnTable);
+}
+
+int
+Blt_TreeViewCreateColumn(tvPtr, columnPtr, name, defTitle)
+    TreeView *tvPtr;
+    TreeViewColumn *columnPtr;
+    char *name, *defTitle;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    columnPtr->key = Blt_TreeGetKey(name);
+    columnPtr->title = Blt_Strdup(defTitle);
+    columnPtr->justify = TK_JUSTIFY_CENTER;
+    columnPtr->relief = TK_RELIEF_FLAT;
+    columnPtr->borderWidth = 1;
+    columnPtr->pad.side1 = columnPtr->pad.side2 = 2;
+    columnPtr->state = STATE_NORMAL;
+    columnPtr->weight = 1.0;
+    columnPtr->editable = FALSE;
+    columnPtr->ruleLineWidth = 1;
+    columnPtr->titleBorderWidth = 2;
+    columnPtr->titleRelief = TK_RELIEF_RAISED;
+    columnPtr->titleIcon = NULL;
+    hPtr = Blt_CreateHashEntry(&tvPtr->columnTable, columnPtr->key, &isNew);
+    Blt_SetHashValue(hPtr, columnPtr);
+
+    bltTreeViewUidOption.clientData = tvPtr;
+    bltTreeViewIconOption.clientData = tvPtr;
+    styleOption.clientData = tvPtr;
+    if (Blt_ConfigureComponentFromObj(tvPtr->interp, tvPtr->tkwin, name, 
+	"Column", columnSpecs, 0, (Tcl_Obj **)NULL, (char *)columnPtr, 0) 
+	!= TCL_OK) {
+	DestroyColumn(tvPtr, columnPtr);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static TreeViewColumn *
+CreateColumn(tvPtr, nameObjPtr, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Obj *nameObjPtr;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+
+    columnPtr = Blt_Calloc(1, sizeof(TreeViewColumn));
+    assert(columnPtr);
+    if (Blt_TreeViewCreateColumn(tvPtr, columnPtr, Tcl_GetString(nameObjPtr), 
+	Tcl_GetString(nameObjPtr)) != TCL_OK) {
+	return NULL;
+    }
+    bltTreeViewUidOption.clientData = tvPtr;
+    bltTreeViewIconOption.clientData = tvPtr;
+    styleOption.clientData = tvPtr;
+    if (Blt_ConfigureComponentFromObj(tvPtr->interp, tvPtr->tkwin, 
+	columnPtr->key, "Column", columnSpecs, objc, objv, (char *)columnPtr, 
+	BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	DestroyColumn(tvPtr, columnPtr);
+	return NULL;
+    }
+    Blt_TreeViewUpdateColumnGCs(tvPtr, columnPtr);
+    return columnPtr;
+}
+
+TreeViewColumn *
+Blt_TreeViewNearestColumn(tvPtr, x, y, contextPtr)
+    TreeView *tvPtr;
+    int x, y;
+    ClientData *contextPtr;
+{
+    if (tvPtr->nVisible > 0) {
+	Blt_ChainLink *linkPtr;
+	TreeViewColumn *columnPtr;
+	int right;
+
+	/*
+	 * Determine if the pointer is over the rightmost portion of the
+	 * column.  This activates the rule.
+	 */
+	x = WORLDX(tvPtr, x);
+	for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    columnPtr = Blt_ChainGetValue(linkPtr);
+	    right = columnPtr->worldX + columnPtr->width;
+	    if ((x >= columnPtr->worldX) && (x <= right)) {
+		if (contextPtr != NULL) {
+		    *contextPtr = NULL;
+		    if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) && 
+			(y >= tvPtr->inset) &&
+			(y < (tvPtr->titleHeight + tvPtr->inset))) {
+			*contextPtr = (x >= (right - RULE_AREA)) 
+			    ? ITEM_COLUMN_RULE : ITEM_COLUMN_TITLE;
+		    } 
+		}
+		return columnPtr;
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnActivateOp --
+ *
+ *	Selects the button to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnActivateOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 4) {
+	Drawable drawable;
+	TreeViewColumn *columnPtr;
+	char *string;
+
+	string = Tcl_GetString(objv[3]);
+	if (string[0] == '\0') {
+	    columnPtr = NULL;
+	} else {
+	    if (Blt_TreeViewGetColumn(interp, tvPtr, objv[3], &columnPtr) 
+		!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (((tvPtr->flags & TV_SHOW_COLUMN_TITLES) == 0) || 
+		(columnPtr->hidden) || (columnPtr->state == STATE_DISABLED)) {
+		columnPtr = NULL;
+	    }
+	}
+	tvPtr->activeTitleColumnPtr = tvPtr->activeColumnPtr = columnPtr;
+	drawable = Tk_WindowId(tvPtr->tkwin);
+	if (drawable != None) {
+	    Blt_TreeViewDrawHeadings(tvPtr, drawable);
+	    Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
+	}
+    }
+    if (tvPtr->activeTitleColumnPtr != NULL) {
+	Tcl_SetResult(interp, tvPtr->activeTitleColumnPtr->key, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnBindOp --
+ *
+ *	  .t bind tag sequence command
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnBindOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    ClientData object;
+    TreeViewColumn *columnPtr;
+
+    if (Blt_TreeViewGetColumn(NULL, tvPtr, objv[3], &columnPtr) == TCL_OK) {
+	object = Blt_TreeViewColumnTag(tvPtr, columnPtr->key);
+    } else {
+	object = Blt_TreeViewColumnTag(tvPtr, Tcl_GetString(objv[3]));
+    }
+    return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
+	objc - 4, objv + 4);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+
+    if (Blt_TreeViewGetColumn(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, columnSpecs, 
+	(char *)columnPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the one of more
+ *	entries in the widget.
+ *
+ *	  .h entryconfigure node node node node option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+    int nOptions, start;
+    register int i;
+
+    /* Figure out where the option value pairs begin */
+    for(i = 3; i < objc; i++) {
+	if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+	    break;
+	}
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objv[i], &columnPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    start = i;
+    nOptions = objc - start;
+    
+    bltTreeViewUidOption.clientData = tvPtr;
+    bltTreeViewIconOption.clientData = tvPtr;
+    styleOption.clientData = tvPtr;
+    for (i = 3; i < start; i++) {
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objv[i], &columnPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (nOptions == 0) {
+	    return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs, 
+		(char *)columnPtr, (Tcl_Obj *)NULL, 0);
+	} else if (nOptions == 1) {
+	    return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, columnSpecs, 
+		(char *)columnPtr, objv[start], 0);
+	}
+	if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin, 
+	       columnSpecs, nOptions, objv + start, (char *)columnPtr, 
+		BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Blt_TreeViewUpdateColumnGCs(tvPtr, columnPtr);
+    }
+    /*FIXME: Makes every change redo everything. */
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnDeleteOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnDeleteOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr;
+    register int i;
+
+    for(i = 3; i < objc; i++) {
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objv[i], &columnPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	/* Traverse the tree deleting values associated with the column.  */
+	for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+	    entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
+	    if (entryPtr != NULL) {
+		TreeViewValue *valuePtr, *lastPtr, *nextPtr;
+		
+		lastPtr = NULL;
+		for (valuePtr = entryPtr->values; valuePtr != NULL; 
+		     valuePtr = nextPtr) {
+		    nextPtr = valuePtr->nextPtr;
+		    if (valuePtr->columnPtr == columnPtr) {
+			Blt_TreeViewDestroyValue(tvPtr, valuePtr);
+			if (lastPtr == NULL) {
+			    entryPtr->values = nextPtr;
+			} else {
+			    lastPtr->nextPtr = nextPtr;
+			}
+			break;
+		    }
+		    lastPtr = valuePtr;
+		}
+	    }
+	}
+	DestroyColumn(tvPtr, columnPtr);
+    }
+    /* Deleting a column may affect the height of an entry. */
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInsertOp --
+ *
+ *	Add new columns to the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInsertOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_ChainLink *beforePtr;
+    Tcl_Obj *CONST *options;
+    TreeViewColumn *columnPtr;
+    TreeViewEntry *entryPtr;
+    int insertPos;
+    int nOptions;
+    int start;
+    register int i;
+
+    if (Blt_GetPositionFromObj(tvPtr->interp, objv[3], &insertPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((insertPos == -1) || 
+	(insertPos >= Blt_ChainGetLength(tvPtr->colChainPtr))) {
+	beforePtr = NULL;
+    } else {
+	beforePtr =  Blt_ChainGetNthLink(tvPtr->colChainPtr, insertPos);
+    }
+    /*
+     * Count the column names that follow.  Count the arguments until we
+     * spot one that looks like a configuration option (i.e. starts
+     * with a minus ("-")).
+     */
+    for (i = 4; i < objc; i++) {
+	if (Blt_ObjIsOption(columnSpecs, objv[i], 0)) {
+	    break;
+	}
+    }
+    start = i;
+    nOptions = objc - i;
+    options = objv + start;
+
+    for (i = 4; i < start; i++) {
+	if (Blt_TreeViewGetColumn(NULL, tvPtr, objv[i], &columnPtr) == TCL_OK) {
+	    Tcl_AppendResult(interp, "column \"", Tcl_GetString(objv[i]), 
+		"\" already exists", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	columnPtr = CreateColumn(tvPtr, objv[i], nOptions, options);
+	if (columnPtr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (beforePtr == NULL) {
+	    columnPtr->linkPtr = Blt_ChainAppend(tvPtr->colChainPtr, columnPtr);
+	} else {
+	    columnPtr->linkPtr = Blt_ChainNewLink();
+	    Blt_ChainSetValue(columnPtr->linkPtr, columnPtr);
+	    Blt_ChainLinkBefore(tvPtr->colChainPtr, columnPtr->linkPtr, 
+		beforePtr);
+	}
+	/* 
+	 * Traverse the tree adding column entries where needed.
+	 */
+	for(entryPtr = tvPtr->rootPtr; entryPtr != NULL;
+	    entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
+	    Blt_TreeViewAddValue(entryPtr, columnPtr);
+	}
+	Blt_TreeViewTraceColumn(tvPtr, columnPtr);
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnCurrentOp --
+ *
+ *	Make the rule to appear active.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnCurrentOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    ClientData context;
+    TreeViewColumn *columnPtr;
+
+    columnPtr = NULL;
+    context = Blt_GetCurrentContext(tvPtr->bindTable);
+    if ((context == ITEM_COLUMN_TITLE) || (context == ITEM_COLUMN_RULE)) {
+	columnPtr = Blt_GetCurrentItem(tvPtr->bindTable);
+    }
+    if (context >= ITEM_STYLE) {
+	TreeViewValue *valuePtr = context;
+	
+	columnPtr = valuePtr->columnPtr;
+    }
+    if (columnPtr != NULL) {
+	Tcl_SetResult(interp, columnPtr->key, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnInvokeOp --
+ *
+ * 	This procedure is called to invoke a column command.
+ *
+ *	  .h column invoke columnName
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnInvokeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+    char *string;
+
+    string = Tcl_GetString(objv[3]);
+    if (string[0] == '\0') {
+	return TCL_OK;
+    }
+    if (Blt_TreeViewGetColumn(interp, tvPtr, objv[3], &columnPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((columnPtr->state == STATE_NORMAL) && (columnPtr->titleCmd != NULL)) {
+	int result;
+
+	Tcl_Preserve(tvPtr);
+	Tcl_Preserve(columnPtr);
+	result = Tcl_GlobalEval(interp, columnPtr->titleCmd);
+	Tcl_Release(columnPtr);
+	Tcl_Release(tvPtr);
+	return result;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnMoveOp --
+ *
+ *	Move a column.
+ *
+ * .h column move field1 position
+ *----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnNamesOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColumnNamesOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;		/* Not used. */
+{
+    Blt_ChainLink *linkPtr;
+    Tcl_Obj *listObjPtr, *objPtr;
+    TreeViewColumn *columnPtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	columnPtr = Blt_ChainGetValue(linkPtr);
+	objPtr = Tcl_NewStringObj(columnPtr->key, -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+ColumnNearestOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int x, y;			/* Screen coordinates of the test point. */
+    TreeViewColumn *columnPtr;
+    ClientData context;
+    int checkTitle;
+#ifdef notdef
+    int isRoot;
+
+    isRoot = FALSE;
+    string = Tcl_GetString(objv[3]);
+
+    if (strcmp("-root", string) == 0) {
+	isRoot = TRUE;
+	objv++, objc--;
+    }
+    if (objc != 5) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", 
+		Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]), 
+		Tcl_GetString(objv[2]), " ?-root? x y\"", (char *)NULL);
+	return TCL_ERROR;
+			 
+    }
+#endif
+    if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &x) != TCL_OK) {
+	return TCL_ERROR;
+    } 
+    y = 0;
+    checkTitle = FALSE;
+    if (objc == 5) {
+	if (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[4], &y) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	checkTitle = TRUE;
+    }
+    columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, &context);
+    if ((checkTitle) && (context == NULL)) {
+	columnPtr = NULL;
+    }
+    if (columnPtr != NULL) {
+	Tcl_SetResult(interp, columnPtr->key, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+static void
+UpdateMark(tvPtr, newMark)
+    TreeView *tvPtr;
+    int newMark;
+{
+    Drawable drawable;
+    TreeViewColumn *columnPtr;
+    int dx;
+    int width;
+
+    columnPtr = tvPtr->resizeColumnPtr;
+    if (columnPtr == NULL) {
+	return;
+    }
+    drawable = Tk_WindowId(tvPtr->tkwin);
+    if (drawable == None) {
+	return;
+    }
+
+    /* Erase any existing rule. */
+    if (tvPtr->flags & TV_RULE_ACTIVE) { 
+	Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+    }
+    
+    dx = newMark - tvPtr->ruleAnchor; 
+    width = columnPtr->width - 
+	(PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth);
+    if ((columnPtr->reqMin > 0) && ((width + dx) < columnPtr->reqMin)) {
+	dx = columnPtr->reqMin - width;
+    }
+    if ((columnPtr->reqMax > 0) && ((width + dx) > columnPtr->reqMax)) {
+	dx = columnPtr->reqMax - width;
+    }
+    if ((width + dx) < 4) {
+	dx = 4 - width;
+    }
+    tvPtr->ruleMark = tvPtr->ruleAnchor + dx;
+
+    /* Redraw the rule if required. */
+    if (tvPtr->flags & TV_RULE_NEEDED) {
+	Blt_TreeViewDrawRule(tvPtr, columnPtr, drawable);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeActivateOp --
+ *
+ *	Turns on/off the resize cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeActivateOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewColumn *columnPtr;
+    char *string;
+
+    string = Tcl_GetString(objv[4]);
+    if (string[0] == '\0') {
+	if (tvPtr->cursor != None) {
+	    Tk_DefineCursor(tvPtr->tkwin, tvPtr->cursor);
+	} else {
+	    Tk_UndefineCursor(tvPtr->tkwin);
+	}
+	tvPtr->resizeColumnPtr = NULL;
+    } else if (Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr) 
+	       == TCL_OK) {
+	if (tvPtr->resizeCursor != None) {
+	    Tk_DefineCursor(tvPtr->tkwin, tvPtr->resizeCursor);
+	} 
+	tvPtr->resizeColumnPtr = columnPtr;
+    } else {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeAnchorOp --
+ *
+ *	Set the anchor for the resize.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeAnchorOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int x;
+
+    if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+	return TCL_ERROR;
+    } 
+    tvPtr->ruleAnchor = x;
+    tvPtr->flags |= TV_RULE_NEEDED;
+    UpdateMark(tvPtr, x);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeMarkOp --
+ *
+ *	Sets the resize mark.  The distance between the mark and the anchor
+ *	is the delta to change the width of the active column.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeMarkOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int x;
+
+    if (Tcl_GetIntFromObj(NULL, objv[4], &x) != TCL_OK) {
+	return TCL_ERROR;
+    } 
+    tvPtr->flags |= TV_RULE_NEEDED;
+    UpdateMark(tvPtr, x);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResizeSetOp --
+ *
+ *	Returns the new width of the column including the resize delta.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ResizeSetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    tvPtr->flags &= ~TV_RULE_NEEDED;
+    UpdateMark(tvPtr, tvPtr->ruleMark);
+    if (tvPtr->resizeColumnPtr != NULL) {
+	int width, delta;
+	TreeViewColumn *columnPtr;
+
+	columnPtr = tvPtr->resizeColumnPtr;
+	delta = (tvPtr->ruleMark - tvPtr->ruleAnchor);
+	width = tvPtr->resizeColumnPtr->width + delta - 
+	    (PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth) - 1;
+	Tcl_SetObjResult(interp, Tcl_NewIntObj(width));
+    }
+    return TCL_OK;
+}
+
+static Blt_OpSpec resizeOps[] =
+{ 
+    {"activate", 2, (Blt_Op)ResizeActivateOp, 5, 5, "column"},
+    {"anchor", 2, (Blt_Op)ResizeAnchorOp, 5, 5, "x"},
+    {"mark", 1, (Blt_Op)ResizeMarkOp, 5, 5, "x"},
+    {"set", 1, (Blt_Op)ResizeSetOp, 4, 4, "",},
+};
+
+static int nResizeOps = sizeof(resizeOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColumnResizeOp --
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ColumnResizeOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nResizeOps, resizeOps, BLT_OP_ARG3, 
+	objc, objv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+
+static Blt_OpSpec columnOps[] =
+{
+    {"activate", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+    {"bind", 1, (Blt_Op)ColumnBindOp, 4, 6, "tagName ?sequence command?",},
+    {"cget", 2, (Blt_Op)ColumnCgetOp, 5, 5, "field option",},
+    {"configure", 2, (Blt_Op)ColumnConfigureOp, 4, 0, 
+	"field ?option value?...",},
+    {"current", 2, (Blt_Op)ColumnCurrentOp, 3, 3, "",},
+    {"delete", 1, (Blt_Op)ColumnDeleteOp, 3, 0, "?field...?",},
+    {"highlight", 1, (Blt_Op)ColumnActivateOp, 3, 4, "?field?",},
+    {"insert", 3, (Blt_Op)ColumnInsertOp, 5, 0, 
+	"position field ?field...? ?option value?...",},
+    {"invoke", 3, (Blt_Op)ColumnInvokeOp, 4, 4, "field",},
+    {"names", 2, (Blt_Op)ColumnNamesOp, 3, 3, "",},
+    {"nearest", 2, (Blt_Op)ColumnNearestOp, 4, 5, "x ?y?",},
+    {"resize", 1, (Blt_Op)ColumnResizeOp, 3, 0, "arg",},
+};
+static int nColumnOps = sizeof(columnOps) / sizeof(Blt_OpSpec);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewColumnOp --
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeViewColumnOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nColumnOps, columnOps, BLT_OP_ARG2, 
+	objc, objv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+
+static int
+InvokeCompare(tvPtr, e1Ptr, e2Ptr, command)
+    TreeView *tvPtr;
+    TreeViewEntry *e1Ptr, *e2Ptr;
+    char *command;
+{
+    int result;
+    Tcl_Obj *objv[8];
+    int i;
+
+    objv[0] = Tcl_NewStringObj(command, -1);
+    objv[1] = Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1);
+    objv[2] = Tcl_NewIntObj(Blt_TreeNodeId(e1Ptr->node));
+    objv[3] = Tcl_NewIntObj(Blt_TreeNodeId(e2Ptr->node));
+    objv[4] = Tcl_NewStringObj(tvPtr->sortColumnPtr->key, -1);
+	     
+    if (tvPtr->flatView) {
+	objv[5] = Tcl_NewStringObj(e1Ptr->fullName, -1);
+	objv[6] = Tcl_NewStringObj(e2Ptr->fullName, -1);
+    } else {
+	objv[5] = Tcl_NewStringObj(GETLABEL(e1Ptr), -1);
+	objv[6] = Tcl_NewStringObj(GETLABEL(e2Ptr), -1);
+    }
+    for(i = 0; i < 7; i++) {
+	Tcl_IncrRefCount(objv[i]);
+    }
+    objv[7] = NULL;
+    result = Tcl_EvalObjv(tvPtr->interp, 7, objv, TCL_EVAL_GLOBAL);
+    if ((result != TCL_OK) ||
+	(Tcl_GetIntFromObj(tvPtr->interp, Tcl_GetObjResult(tvPtr->interp), 
+			   &result) != TCL_OK)) {
+	Tcl_BackgroundError(tvPtr->interp);
+    }
+    for(i = 0; i < 7; i++) {
+	Tcl_DecrRefCount(objv[i]);
+    }
+    Tcl_ResetResult(tvPtr->interp);
+    return result;
+}
+
+static TreeView *treeViewInstance;
+
+static int
+CompareEntries(a, b)
+    CONST void *a, *b;
+{
+    TreeView *tvPtr;
+    TreeViewEntry **e1PtrPtr = (TreeViewEntry **)a;
+    TreeViewEntry **e2PtrPtr = (TreeViewEntry **)b;
+    Tcl_Obj *obj1, *obj2;
+    char *s1, *s2;
+    int result;
+
+    tvPtr = (*e1PtrPtr)->tvPtr;
+    obj1 = (*e1PtrPtr)->dataObjPtr;
+    obj2 = (*e2PtrPtr)->dataObjPtr;
+    s1 = Tcl_GetString(obj1);
+    s2 = Tcl_GetString(obj2);
+    result = 0;
+    switch (tvPtr->sortType) {
+    case SORT_TYPE_ASCII:
+	result = strcmp(s1, s2);
+	break;
+
+    case SORT_TYPE_COMMAND:
+	{
+	    char *cmd;
+
+	    cmd = tvPtr->sortColumnPtr->sortCmd;
+	    if (cmd == NULL) {
+		cmd = tvPtr->sortCmd;
+	    }
+	    if (cmd == NULL) {
+		result = Blt_DictionaryCompare(s1, s2);
+	    } else {
+		result = InvokeCompare(tvPtr, *e1PtrPtr, *e2PtrPtr, cmd);
+	    }
+	}
+	break;
+
+    case SORT_TYPE_DICTIONARY:
+	result = Blt_DictionaryCompare(s1, s2);
+	break;
+
+    case SORT_TYPE_INTEGER:
+	{
+	    int i1, i2;
+
+	    if (Tcl_GetIntFromObj(NULL, obj1, &i1)==TCL_OK) {
+		if (Tcl_GetIntFromObj(NULL, obj2, &i2) == TCL_OK) {
+		    result = i1 - i2;
+		} else {
+		    result = -1;
+		} 
+	    } else if (Tcl_GetIntFromObj(NULL, obj2, &i2) == TCL_OK) {
+		result = 1;
+	    } else {
+		result = Blt_DictionaryCompare(s1, s2);
+	    }
+	}
+	break;
+
+    case SORT_TYPE_REAL:
+	{
+	    double r1, r2;
+
+	    if (Tcl_GetDoubleFromObj(NULL, obj1, &r1) == TCL_OK) {
+		if (Tcl_GetDoubleFromObj(NULL, obj2, &r2) == TCL_OK) {
+		    result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
+		} else {
+		    result = -1;
+		} 
+	    } else if (Tcl_GetDoubleFromObj(NULL, obj2, &r2) == TCL_OK) {
+		result = 1;
+	    } else {
+		result = Blt_DictionaryCompare(s1, s2);
+	    }
+	}
+	break;
+    }
+    if (tvPtr->sortDecreasing) {
+	return -result;
+    } 
+    return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareNodes --
+ *
+ *	Comparison routine (used by qsort) to sort a chain of subnodes.
+ *
+ * Results:
+ *	1 is the first is greater, -1 is the second is greater, 0
+ *	if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CompareNodes(n1Ptr, n2Ptr)
+    Blt_TreeNode *n1Ptr, *n2Ptr;
+{
+    TreeView *tvPtr = treeViewInstance;
+    TreeViewEntry *e1Ptr, *e2Ptr;
+
+    e1Ptr = Blt_NodeToEntry(tvPtr, *n1Ptr);
+    e2Ptr = Blt_NodeToEntry(tvPtr, *n2Ptr);
+
+    /* Fetch the data for sorting. */
+    if (tvPtr->sortType == SORT_TYPE_COMMAND) {
+	e1Ptr->dataObjPtr = Tcl_NewIntObj(Blt_TreeNodeId(*n1Ptr));
+	e2Ptr->dataObjPtr = Tcl_NewIntObj(Blt_TreeNodeId(*n2Ptr));
+    } else if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+	Tcl_DString dString;
+
+	Tcl_DStringInit(&dString);
+	if (e1Ptr->fullName == NULL) {
+	    Blt_TreeViewGetFullName(tvPtr, e1Ptr, TRUE, &dString);
+	    e1Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+	}
+	e1Ptr->dataObjPtr = Tcl_NewStringObj(e1Ptr->fullName, -1);
+	if (e2Ptr->fullName == NULL) {
+	    Blt_TreeViewGetFullName(tvPtr, e2Ptr, TRUE, &dString);
+	    e2Ptr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+	}
+	e2Ptr->dataObjPtr = Tcl_NewStringObj(e2Ptr->fullName, -1);
+	Tcl_DStringFree(&dString);
+    } else {
+	Blt_TreeKey key;
+	Tcl_Obj *objPtr;
+
+	key = tvPtr->sortColumnPtr->key;
+	if (Blt_TreeViewGetData(e1Ptr, key, &objPtr) != TCL_OK) {
+	    e1Ptr->dataObjPtr = bltEmptyStringObjPtr;
+	} else {
+	    e1Ptr->dataObjPtr = objPtr;
+	}
+	if (Blt_TreeViewGetData(e2Ptr, key, &objPtr) != TCL_OK) {
+	    e2Ptr->dataObjPtr = bltEmptyStringObjPtr;
+	} else {
+	    e2Ptr->dataObjPtr = objPtr;
+	}
+    }
+    return CompareEntries(&e1Ptr, &e2Ptr);
+}
+
+static int
+SortAutoOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+
+    if (objc == 4) {
+	int bool;
+	int isAuto;
+
+	isAuto = ((tvPtr->flags & TV_SORT_AUTO) != 0);
+	if (Tcl_GetBooleanFromObj(interp, objv[3], &bool) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (isAuto != bool) {
+	    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+	    Blt_TreeViewEventuallyRedraw(tvPtr);
+	}
+	if (bool) {
+	    tvPtr->flags |= TV_SORT_AUTO;
+	} else {
+	    tvPtr->flags &= ~TV_SORT_AUTO;
+	}
+    }
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(tvPtr->flags & TV_SORT_AUTO));
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortCgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortCgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, sortSpecs, 
+	(char *)tvPtr, objv[3], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the one of more
+ *	entries in the widget.
+ *
+ *	  .h sort configure option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SortConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int oldType;
+    char *oldCommand;
+    TreeViewColumn *oldColumn;
+
+    if (objc == 3) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs, 
+		(char *)tvPtr, (Tcl_Obj *)NULL, 0);
+    } else if (objc == 4) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, sortSpecs, 
+		(char *)tvPtr, objv[3], 0);
+    }
+    oldColumn = tvPtr->sortColumnPtr;
+    oldType = tvPtr->sortType;
+    oldCommand = tvPtr->sortCmd;
+    if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, sortSpecs, 
+	objc - 3, objv + 3, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((oldColumn != tvPtr->sortColumnPtr) ||
+	(oldType != tvPtr->sortType) ||
+	(oldCommand != tvPtr->sortCmd)) {
+	tvPtr->flags &= ~TV_SORTED;
+	tvPtr->flags |= (TV_DIRTY | TV_RESORT);
+    } 
+    if (tvPtr->flags & TV_SORT_AUTO) {
+	tvPtr->flags |= TV_SORT_PENDING;
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SortOnceOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    int recurse, result;
+    register int i;
+
+    recurse = FALSE;
+    if (objc > 3) {
+	char *string;
+	int length;
+
+	string = Tcl_GetStringFromObj(objv[3], &length);
+	if ((string[0] == '-') && (length > 1) &&
+	    (strncmp(string, "-recurse", length) == 0)) {
+	    objv++, objc--;
+	    recurse = TRUE;
+	}
+    }
+    for (i = 3; i < objc; i++) {
+	if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (recurse) {
+	    result = Blt_TreeApply(entryPtr->node, SortApplyProc, tvPtr);
+	} else {
+	    result = SortApplyProc(entryPtr->node, tvPtr, TREE_PREORDER);
+	}
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    tvPtr->flags |= TV_LAYOUT;
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortOp --
+ *
+ *	Comparison routine (used by qsort) to sort a chain of subnodes.
+ *	A simple string comparison is performed on each node name.
+ *
+ *	.h sort auto
+ *	.h sort once -recurse root
+ *
+ * Results:
+ *	1 is the first is greater, -1 is the second is greater, 0
+ *	if equal.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec sortOps[] =
+{
+    {"auto", 1, (Blt_Op)SortAutoOp, 3, 4, "?boolean?",},
+    {"cget", 2, (Blt_Op)SortCgetOp, 4, 4, "option",},
+    {"configure", 2, (Blt_Op)SortConfigureOp, 3, 0, "?option value?...",},
+    {"once", 1, (Blt_Op)SortOnceOp, 3, 0, "?-recurse? node...",},
+};
+static int nSortOps = sizeof(sortOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_TreeViewSortOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nSortOps, sortOps, BLT_OP_ARG2, objc, 
+	    objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tvPtr, interp, objc, objv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortApplyProc --
+ *
+ *	Sorts the subnodes at a given node.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SortApplyProc(node, clientData, order)
+    Blt_TreeNode node;
+    ClientData clientData;
+    int order;			/* Not used. */
+{
+    TreeView *tvPtr = clientData;
+
+    if (!Blt_TreeIsLeaf(node)) {
+	Blt_TreeSortNode(tvPtr->tree, node, CompareNodes);
+    }
+    return TCL_OK;
+}
+ 
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortFlatView --
+ *
+ *	Sorts the flatten array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortFlatView(tvPtr)
+    TreeView *tvPtr;
+{
+    TreeViewEntry *entryPtr, **p;
+
+    tvPtr->flags &= ~TV_SORT_PENDING;
+    if ((tvPtr->sortType == SORT_TYPE_NONE) || (tvPtr->sortColumnPtr == NULL) ||
+	(tvPtr->nEntries == 1)) {
+	return;
+    }
+    if (tvPtr->flags & TV_SORTED) {
+	int first, last;
+	TreeViewEntry *hold;
+
+	if (tvPtr->sortDecreasing == tvPtr->viewIsDecreasing) {
+	    return;
+	}
+
+	/* 
+	 * The view is already sorted but in the wrong direction. 
+	 * Reverse the entries in the array.
+	 */
+ 	for (first = 0, last = tvPtr->nEntries - 1; last > first; 
+	     first++, last--) {
+	    hold = tvPtr->flatArr[first];
+	    tvPtr->flatArr[first] = tvPtr->flatArr[last];
+	    tvPtr->flatArr[last] = hold;
+	}
+	tvPtr->viewIsDecreasing = tvPtr->sortDecreasing;
+	tvPtr->flags |= TV_SORTED | TV_LAYOUT;
+	return;
+    }
+    /* Fetch each entry's data as Tcl_Objs for sorting. */
+    if (tvPtr->sortColumnPtr == &tvPtr->treeColumn) {
+	for(p = tvPtr->flatArr; *p != NULL; p++) {
+	    entryPtr = *p;
+	    if (entryPtr->fullName == NULL) {
+		Tcl_DString dString;
+
+		Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
+		entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
+		Tcl_DStringFree(&dString);
+	    }
+	    entryPtr->dataObjPtr = Tcl_NewStringObj(entryPtr->fullName, -1);
+	    Tcl_IncrRefCount(entryPtr->dataObjPtr);
+	}
+    } else {
+	Blt_TreeKey key;
+	Tcl_Obj *objPtr;
+
+	key = tvPtr->sortColumnPtr->key;
+	for(p = tvPtr->flatArr; *p != NULL; p++) {
+	    entryPtr = *p;
+	    if (Blt_TreeViewGetData(entryPtr, key, &objPtr) != TCL_OK) {
+		objPtr = bltEmptyStringObjPtr;
+	    }
+	    entryPtr->dataObjPtr = objPtr;
+	    Tcl_IncrRefCount(entryPtr->dataObjPtr);
+	}
+    }
+    qsort((char *)tvPtr->flatArr, tvPtr->nEntries, sizeof(TreeViewEntry *),
+	  (QSortCompareProc *)CompareEntries);
+
+    /* Free all the Tcl_Objs used for comparison data. */
+    for(p = tvPtr->flatArr; *p != NULL; p++) {
+	Tcl_DecrRefCount((*p)->dataObjPtr);
+    }
+    tvPtr->viewIsDecreasing = tvPtr->sortDecreasing;
+    tvPtr->flags |= TV_SORTED;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeViewSortTreeView --
+ *
+ *	Sorts the tree array of entries.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_TreeViewSortTreeView(tvPtr)
+    TreeView *tvPtr;
+{
+    tvPtr->flags &= ~TV_SORT_PENDING;
+    if ((tvPtr->sortType != SORT_TYPE_NONE) && (tvPtr->sortColumnPtr != NULL)) {
+	treeViewInstance = tvPtr;
+	Blt_TreeApply(tvPtr->rootPtr->node, SortApplyProc, tvPtr);
+    }
+    tvPtr->viewIsDecreasing = tvPtr->sortDecreasing;
+}
+
+
+#endif /* NO_TREEVIEW */
Index: trunk/kitgen/8.x/blt/generic/bltTreeViewEdit.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeViewEdit.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeViewEdit.c	(revision 175)
@@ -0,0 +1,1769 @@
+/*
+  Remember row,column where string was acquired.
+  postcombobox x y 
+	icon?, text, row, column position, fg, bg button?
+	based upon style.
+  grab set
+  SetIcon
+  SetText
+  SetBg
+  SetFg
+  SetFont
+  SetButton  
+ */
+
+/*
+ * bltTreeViewEdit.c --
+ *
+ *	This module implements an hierarchy widget for the BLT toolkit.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define TEXTBOX_FOCUS	(1<<0)
+#define TEXTBOX_REDRAW	(1<<1)
+
+static Tcl_IdleProc DisplayTextbox;
+static Tcl_FreeProc DestroyTextbox;
+static Tcl_TimerProc BlinkCursorProc;
+static Tcl_ObjCmdProc TextboxCmd;
+
+/*
+ * Textbox --
+ *
+ *	This structure is shared by entries when their labels are
+ *	edited via the keyboard.  It maintains the location of the
+ *	insertion cursor and the text selection for the editted entry.
+ *	The structure is shared since we need only one.  The "focus"
+ *	entry should be the only entry receiving KeyPress/KeyRelease
+ *	events at any time.  Information from the previously editted
+ *	entry is overwritten.
+ *
+ *	Note that all the indices internally are in terms of bytes,
+ *	not characters.  This is because UTF-8 strings may encode a
+ *	single character into a multi-byte sequence.  To find the
+ *	respective character position
+ *
+ *		n = Tcl_NumUtfChars(string, index);
+ *
+ *	where n is the resulting character number.
+ */
+typedef struct {
+
+    /*
+     * This is a SNAFU in the Tk API.  It assumes that only an official
+     * Tk "toplevel" widget will ever become a toplevel window (i.e. a
+     * window whose parent is the root window).  Because under Win32,
+     * Tk tries to use the widget record associated with the TopLevel
+     * as a Tk frame widget, to read its menu name.  What this means
+     * is that any widget that's going to be a toplevel, must also look
+     * like a frame. Therefore we've copied the frame widget structure
+     * fields into the token.
+     */
+
+    Tk_Window tkwin;		/* Window that embodies the frame.  NULL
+				 * means that the window has been destroyed
+				 * but the data structures haven't yet been
+				 * cleaned up. */
+    Display *display;		/* Display containing widget.  Used, among
+				 * other things, so that resources can be
+				 * freed even after tkwin has gone away. */
+    Tcl_Interp *interp;		/* Interpreter associated with widget.  Used
+				 * to delete widget command. */
+    Tcl_Command widgetCmd;	/* Token for frame's widget command. */
+    char *className;		/* Class name for widget (from configuration
+				 * option).  Malloc-ed. */
+    int mask;			/* Either FRAME or TOPLEVEL;  used to select
+				 * which configuration options are valid for
+				 * widget. */
+    char *screenName;		/* Screen on which widget is created.  Non-null
+				 * only for top-levels.  Malloc-ed, may be
+				 * NULL. */
+    char *visualName;		/* Textual description of visual for window,
+				 * from -visual option.  Malloc-ed, may be
+				 * NULL. */
+    char *colormapName;		/* Textual description of colormap for window,
+				 * from -colormap option.  Malloc-ed, may be
+				 * NULL. */
+    char *menuName;		/* Textual description of menu to use for
+				 * menubar. Malloc-ed, may be NULL. */
+    Colormap colormap;		/* If not None, identifies a colormap
+				 * allocated for this window, which must be
+				 * freed when the window is deleted. */
+    Tk_3DBorder border;		/* Structure used to draw 3-D border and
+				 * background.  NULL means no background
+				 * or border. */
+    int borderWidth;		/* Width of 3-D border (if any). */
+    int relief;			/* 3-d effect: TK_RELIEF_RAISED etc. */
+    int highlightWidth;		/* Width in pixels of highlight to draw
+				 * around widget when it has the focus.
+				 * 0 means don't draw a highlight. */
+    XColor *highlightBgColorPtr;
+				/* Color for drawing traversal highlight
+				 * area when highlight is off. */
+    XColor *highlightColorPtr;	/* Color for drawing traversal highlight. */
+    int width;			/* Width to request for window.  <= 0 means
+				 * don't request any size. */
+    int height;			/* Height to request for window.  <= 0 means
+				 * don't request any size. */
+    Tk_Cursor cursor;		/* Current cursor for window, or None. */
+    char *takeFocus;		/* Value of -takefocus option;  not used in
+				 * the C code, but used by keyboard traversal
+				 * scripts.  Malloc'ed, but may be NULL. */
+    int isContainer;		/* 1 means this window is a container, 0 means
+				 * that it isn't. */
+    char *useThis;		/* If the window is embedded, this points to
+				 * the name of the window in which it is
+				 * embedded (malloc'ed).  For non-embedded
+				 * windows this is NULL. */
+    int flags;			/* Various flags;  see below for
+				 * definitions. */
+
+    /* Textbox-specific fields */
+    TreeView *tvPtr;
+    int x, y;			/* Position of window. */
+
+    int active;			/* Indicates that the frame is active. */
+    int exportSelection;
+
+    int insertPos;		/* Position of the cursor within the
+				 * array of bytes of the entry's label. */
+
+    int cursorX, cursorY;	/* Position of the insertion cursor in the
+				 * textbox window. */
+    short int cursorWidth;	/* Size of the insertion cursor symbol. */
+    short int cursorHeight;
+
+    int selAnchor;		/* Fixed end of selection. Used to extend
+				 * the selection while maintaining the
+				 * other end of the selection. */
+    int selFirst;		/* Position of the first character in the
+				 * selection. */
+    int selLast;		/* Position of the last character in the
+				 * selection. */
+
+    int cursorOn;		/* Indicates if the cursor is displayed. */
+    int onTime, offTime;	/* Time in milliseconds to wait before
+				 * changing the cursor from off-to-on
+				 * and on-to-off. Setting offTime to 0 makes
+				 * the cursor steady. */
+    Tcl_TimerToken timerToken;	/* Handle for a timer event called periodically
+				 * to blink the cursor. */
+    /* Data-specific fields. */
+    TreeViewEntry *entryPtr;	/* Selected entry */
+    TreeViewColumn *columnPtr;	/* Column of entry to be edited */
+    TreeViewStyle *stylePtr;
+    TreeViewIcon icon;
+    int gap;
+    char *string;
+    TextLayout *textPtr;
+    Tk_Font font;
+    GC gc;
+
+    Tk_3DBorder selBorder;
+    int selRelief;
+    int selBorderWidth;
+    XColor *selFgColor;		/* Text color of a selected entry. */
+    int buttonBorderWidth;
+    Tk_3DBorder buttonBorder;
+    int buttonRelief;
+} Textbox;
+
+#define DEF_TEXTBOX_BACKGROUND		RGB_WHITE
+#define DEF_TEXTBOX_BORDERWIDTH	STD_BORDERWIDTH
+#ifdef WIN32
+#define DEF_TEXTBOX_BUTTON_BACKGROUND  RGB_GREY85
+#else
+#define DEF_TEXTBOX_BUTTON_BACKGROUND  RGB_GREY90
+#endif
+#define DEF_TEXTBOX_BUTTON_BORDERWIDTH	"2"
+#define DEF_TEXTBOX_BUTTON_RELIEF	"raised"
+#define DEF_TEXTBOX_CURSOR		(char *)NULL
+#define DEF_TEXTBOX_EXPORT_SELECTION	"no"
+#define DEF_TEXTBOX_NORMAL_BACKGROUND 	STD_NORMAL_BACKGROUND
+#define DEF_TEXTBOX_NORMAL_FG_MONO	STD_ACTIVE_FG_MONO
+#define DEF_TEXTBOX_RELIEF		"sunken"
+#define DEF_TEXTBOX_SELECT_BACKGROUND 	RGB_LIGHTBLUE0
+#define DEF_TEXTBOX_SELECT_BG_MONO  	STD_SELECT_BG_MONO
+#define DEF_TEXTBOX_SELECT_BORDERWIDTH "1"
+#define DEF_TEXTBOX_SELECT_FOREGROUND 	STD_SELECT_FOREGROUND
+#define DEF_TEXTBOX_SELECT_FG_MONO  	STD_SELECT_FG_MONO
+#define DEF_TEXTBOX_SELECT_RELIEF	"flat"
+
+/* Textbox Procedures */
+static Blt_ConfigSpec textboxConfigSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	DEF_TEXTBOX_BACKGROUND, Blt_Offset(Textbox, border), 0},
+    {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,0},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0,0},
+    {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_TEXTBOX_CURSOR, Blt_Offset(Textbox, cursor), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_TEXTBOX_BORDERWIDTH, Blt_Offset(Textbox, borderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BORDER, "-buttonbackground", "buttonBackground", 
+	"ButtonBackground", DEF_TEXTBOX_BUTTON_BACKGROUND,
+	Blt_Offset(Textbox, buttonBorder), 0},
+    {BLT_CONFIG_RELIEF, "-buttonrelief", "buttonRelief", "ButtonRelief",
+	DEF_TEXTBOX_BUTTON_RELIEF, Blt_Offset(Textbox, buttonRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-buttonborderwidth", "buttonBorderWidth", 
+	"ButtonBorderWidth", DEF_TEXTBOX_BUTTON_BORDERWIDTH, 
+	Blt_Offset(Textbox, buttonBorderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
+	"ExportSelection", DEF_TEXTBOX_EXPORT_SELECTION, 
+	Blt_Offset(Textbox, exportSelection), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_TEXTBOX_RELIEF, Blt_Offset(Textbox, relief), 0},
+    {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+	DEF_TEXTBOX_SELECT_BG_MONO, Blt_Offset(Textbox, selBorder),
+	BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
+	DEF_TEXTBOX_SELECT_BACKGROUND, Blt_Offset(Textbox, selBorder),
+	BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_DISTANCE, "-selectborderwidth", "selectBorderWidth", 
+        "BorderWidth", DEF_TEXTBOX_SELECT_BORDERWIDTH, 
+	Blt_Offset(Textbox, selBorderWidth), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+
+	DEF_TEXTBOX_SELECT_FG_MONO, Blt_Offset(Textbox, selFgColor),
+	BLT_CONFIG_MONO_ONLY},
+    {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+	DEF_TEXTBOX_SELECT_FOREGROUND, Blt_Offset(Textbox, selFgColor),
+	BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+	DEF_TEXTBOX_SELECT_RELIEF, Blt_Offset(Textbox, selRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 
+	0, 0}
+};
+
+static Tk_LostSelProc TextboxLostSelectionProc;
+static Tk_SelectionProc TextboxSelectionProc;
+static Tk_EventProc TextboxEventProc;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ *	Queues a request to redraw the widget at the next idle point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Information gets redisplayed.  Right now we don't do selective
+ *	redisplays:  the whole window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+EventuallyRedraw(tbPtr)
+    Textbox *tbPtr;
+{
+    if ((tbPtr->tkwin != NULL) && 
+	((tbPtr->flags & TEXTBOX_REDRAW) == 0)) {
+	tbPtr->flags |= TEXTBOX_REDRAW;
+	Tcl_DoWhenIdle(DisplayTextbox, tbPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BlinkCursorProc --
+ *
+ *	This procedure is called as a timer handler to blink the
+ *	insertion cursor off and on.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The cursor gets turned on or off, redisplay gets invoked,
+ *	and this procedure reschedules itself.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+BlinkCursorProc(clientData)
+    ClientData clientData;	/* Pointer to record describing entry. */
+{
+    Textbox *tbPtr = clientData;
+    int interval;
+
+    if (!(tbPtr->flags & TEXTBOX_FOCUS) || (tbPtr->offTime == 0)) {
+	return;
+    }
+    if (tbPtr->active) {
+	tbPtr->cursorOn ^= 1;
+	interval = (tbPtr->cursorOn) ? tbPtr->onTime : tbPtr->offTime;
+	tbPtr->timerToken = 
+	    Tcl_CreateTimerHandler(interval, BlinkCursorProc, tbPtr);
+	EventuallyRedraw(tbPtr);
+    }
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * TextboxEventProc --
+ *
+ * 	This procedure is invoked by the Tk dispatcher for various
+ * 	events on treeview widgets.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the window gets deleted, internal structures get
+ *	cleaned up.  When it gets exposed, it is redisplayed.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+TextboxEventProc(clientData, eventPtr)
+    ClientData clientData;	/* Information about window. */
+    XEvent *eventPtr;		/* Information about event. */
+{
+    Textbox *tbPtr = clientData;
+
+    if (eventPtr->type == Expose) {
+	if (eventPtr->xexpose.count == 0) {
+	    EventuallyRedraw(tbPtr);
+	}
+    } else if (eventPtr->type == ConfigureNotify) {
+	EventuallyRedraw(tbPtr);
+    } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+	if (eventPtr->xfocus.detail == NotifyInferior) {
+	    return;
+	}
+	if (eventPtr->type == FocusIn) {
+	    tbPtr->flags |= TEXTBOX_FOCUS;
+	} else {
+	    tbPtr->flags &= ~TEXTBOX_FOCUS;
+	}
+	Tcl_DeleteTimerHandler(tbPtr->timerToken);
+	if ((tbPtr->active) && (tbPtr->flags & TEXTBOX_FOCUS)) {
+	    tbPtr->cursorOn = TRUE;
+	    if (tbPtr->offTime != 0) {
+		tbPtr->timerToken = Tcl_CreateTimerHandler(tbPtr->onTime, 
+		   BlinkCursorProc, tbPtr);
+	    }
+	} else {
+	    tbPtr->cursorOn = FALSE;
+	    tbPtr->timerToken = (Tcl_TimerToken) NULL;
+	}
+	EventuallyRedraw(tbPtr);
+    } else if (eventPtr->type == DestroyNotify) {
+	if (tbPtr->tkwin != NULL) {
+	    tbPtr->tkwin = NULL;
+	}
+	if (tbPtr->flags & TEXTBOX_REDRAW) {
+	    Tcl_CancelIdleCall(DisplayTextbox, tbPtr);
+	}
+	if (tbPtr->timerToken != NULL) {
+	    Tcl_DeleteTimerHandler(tbPtr->timerToken);
+	}
+	tbPtr->tvPtr->comboWin = NULL;
+	Tcl_EventuallyFree(tbPtr, DestroyTextbox);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextboxLostSelectionProc --
+ *
+ *	This procedure is called back by Tk when the selection is
+ *	grabbed away from a Text widget.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The existing selection is unhighlighted, and the window is
+ *	marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TextboxLostSelectionProc(clientData)
+    ClientData clientData;	/* Information about Text widget. */
+{
+    Textbox *tbPtr = clientData;
+
+    if ((tbPtr->selFirst >= 0) && (tbPtr->exportSelection)) {
+	tbPtr->selFirst = tbPtr->selLast = -1;
+	EventuallyRedraw(tbPtr);
+    }
+}
+
+static int
+PointerToIndex(tbPtr, x, y)
+    Textbox *tbPtr;
+    int x, y;
+{
+    TextLayout *textPtr;
+    Tk_FontMetrics fontMetrics;
+    TextFragment *fragPtr;
+    int nBytes;
+    register int i;
+    int total;
+
+    if ((tbPtr->string == NULL) || (tbPtr->string[0] == '\0')) {
+	return 0;
+    }
+    x -= tbPtr->selBorderWidth;
+    y -= tbPtr->selBorderWidth;
+
+    textPtr = tbPtr->textPtr;
+
+    /* Bound the y-coordinate within the window. */
+    if (y < 0) {
+	y = 0;
+    } else if (y >= textPtr->height) {
+	y = textPtr->height - 1;
+    }
+    /* 
+     * Compute the line that contains the y-coordinate. 
+     *
+     * FIXME: This assumes that segments are distributed 
+     *	     line-by-line.  This may change in the future.
+     */
+    Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
+    fragPtr = textPtr->fragArr;
+    total = 0;
+    for (i = (y / fontMetrics.linespace); i > 0; i--) {
+	total += fragPtr->count;
+	fragPtr++;
+    }
+    if (x < 0) {
+	nBytes = 0;
+    } else if (x >= textPtr->width) {
+	nBytes = fragPtr->count;
+    } else {
+	int newX;
+
+	/* Find the character underneath the pointer. */
+	nBytes = Tk_MeasureChars(tbPtr->font, fragPtr->text, fragPtr->count, 
+		 x, 0, &newX);
+	if ((newX < x) && (nBytes < fragPtr->count)) {
+	    double fract;
+	    int length, charSize;
+	    char *next;
+
+	    next = fragPtr->text + nBytes;
+#if HAVE_UTF
+	    {
+		Tcl_UniChar dummy;
+
+		length = Tcl_UtfToUniChar(next, &dummy);
+	    }
+#else
+	    length = 1;
+#endif
+	    charSize = Tk_TextWidth(tbPtr->font, next, length);
+	    fract = ((double)(x - newX) / (double)charSize);
+	    if (ROUND(fract)) {
+		nBytes += length;
+	    }
+	}
+    }
+    return nBytes + total;
+}
+
+static int
+IndexToPointer(tbPtr)
+    Textbox *tbPtr;
+{
+    int x, y;
+    int maxLines;
+    TextLayout *textPtr;
+    Tk_FontMetrics fontMetrics;
+    int nBytes;
+    int sum;
+    TextFragment *fragPtr;
+    register int i;
+
+    textPtr = tbPtr->textPtr;
+    Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
+    maxLines = (textPtr->height / fontMetrics.linespace) - 1;
+
+    sum = 0;
+    x = y = tbPtr->borderWidth;
+    if (tbPtr->icon != NULL) {
+	x += TreeViewIconWidth(tbPtr->icon) + 2 * tbPtr->gap;
+    }
+    fragPtr = textPtr->fragArr;
+    for (i = 0; i <= maxLines; i++) {
+	/* Total the number of bytes on each line.  Include newlines. */
+	nBytes = fragPtr->count + 1;
+	if ((sum + nBytes) > tbPtr->insertPos) {
+	    x += Tk_TextWidth(tbPtr->font, fragPtr->text, 
+		tbPtr->insertPos - sum);
+	    break;
+	}
+	y += fontMetrics.linespace;
+	sum += nBytes;
+	fragPtr++;
+    }
+    tbPtr->cursorX = x;
+    tbPtr->cursorY = y;
+    tbPtr->cursorHeight = fontMetrics.linespace;
+    tbPtr->cursorWidth = 3;
+    return TCL_OK;
+}
+
+static void
+UpdateLayout(tbPtr)
+    Textbox *tbPtr;
+{
+    TextStyle ts;
+    int width, height;
+    TextLayout *textPtr;
+    int gap;
+    int iconWidth, iconHeight;
+
+    gap = iconWidth = iconHeight = 0;
+    if (tbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(tbPtr->icon) + 4;
+	iconHeight = TreeViewIconHeight(tbPtr->icon);
+	gap = tbPtr->gap;
+    }
+
+    /* The layout is based upon the current font. */
+    Blt_InitTextStyle(&ts);
+    ts.anchor = TK_ANCHOR_NW;
+    ts.justify = TK_JUSTIFY_LEFT;
+    ts.font = tbPtr->font;
+    textPtr = Blt_GetTextLayout(tbPtr->string, &ts);
+    if (tbPtr->textPtr != NULL) {
+	Blt_Free(tbPtr->textPtr);
+    }
+    tbPtr->textPtr = textPtr;
+
+    width = iconWidth + textPtr->width + gap * 2;
+    height = MAX(iconHeight, textPtr->height);
+
+    if (width <= tbPtr->columnPtr->width) {
+	width = tbPtr->columnPtr->width;
+    }
+    if (height < tbPtr->entryPtr->height) {
+	height = tbPtr->entryPtr->height;
+    }
+    tbPtr->width = width + (2 * tbPtr->borderWidth);
+    tbPtr->height = height + (2 * tbPtr->borderWidth);
+    IndexToPointer(tbPtr);
+    Tk_MoveResizeWindow(tbPtr->tkwin, tbPtr->x, tbPtr->y, 
+	      tbPtr->width, tbPtr->height);
+    Tk_MapWindow(tbPtr->tkwin);
+    XRaiseWindow(tbPtr->display, Tk_WindowId(tbPtr->tkwin));
+}
+
+static void
+InsertText(tbPtr, insertText, insertPos, nBytes)
+    Textbox *tbPtr;
+    char *insertText;
+    int insertPos;
+    int nBytes;
+{
+    int oldSize, newSize;
+    char *oldText, *newText;
+
+    oldText = tbPtr->string;
+    oldSize = strlen(oldText);
+    newSize = oldSize + nBytes;
+    newText = Blt_Malloc(sizeof(char) * (newSize + 1));
+    if (insertPos == oldSize) {	/* Append */
+	strcpy(newText, oldText);
+	strcat(newText, insertText);
+    } else if (insertPos == 0) {/* Prepend */
+	strcpy(newText, insertText);
+	strcat(newText, oldText);
+    } else {			/* Insert into existing. */
+	char *p;
+	
+	p = newText;
+	strncpy(p, oldText, insertPos);
+	p += insertPos;
+	strcpy(p, insertText);
+	p += nBytes;
+	strcpy(p, oldText + insertPos);
+    }
+
+    /* 
+     * All indices from the start of the insertion to the end of the
+     * string need to be updated.  Simply move the indices down by the
+     * number of characters added.  
+     */
+    if (tbPtr->selFirst >= insertPos) {
+	tbPtr->selFirst += nBytes;
+    }
+    if (tbPtr->selLast > insertPos) {
+	tbPtr->selLast += nBytes;
+    }
+    if ((tbPtr->selAnchor > insertPos) || (tbPtr->selFirst >= insertPos)) {
+	tbPtr->selAnchor += nBytes;
+    }
+    if (tbPtr->string != NULL) {
+	Blt_Free(tbPtr->string);
+    }
+    tbPtr->string = newText;
+    tbPtr->insertPos = insertPos + nBytes;
+    UpdateLayout(tbPtr);
+}
+
+static int
+DeleteText(tbPtr, firstPos, lastPos)
+    Textbox *tbPtr;
+    int firstPos, lastPos;
+{
+    char *oldText, *newText;
+    int oldSize, newSize;
+    int nBytes;
+    char *p;
+
+    oldText = tbPtr->string;
+    if (firstPos > lastPos) {
+	return TCL_OK;
+    }
+    lastPos++;			/* Now is the position after the last
+				 * character. */
+
+    nBytes = lastPos - firstPos;
+
+    oldSize = strlen(oldText) + 1;
+    newSize = oldSize - nBytes + 1;
+    newText = Blt_Malloc(sizeof(char) * newSize);
+    p = newText;
+    if (firstPos > 0) {
+	strncpy(p, oldText, firstPos);
+	p += firstPos;
+    }
+    *p = '\0';
+    if (lastPos < oldSize) {
+	strcpy(p, oldText + lastPos);
+    }
+    Blt_Free(oldText);
+
+    /*
+     * Since deleting characters compacts the character array, we need to
+     * update the various character indices according.  It depends where
+     * the index occurs in relation to range of deleted characters:
+     *
+     *	 before		Ignore.
+     *   within		Move the index back to the start of the deletion.
+     *	 after		Subtract off the deleted number of characters,
+     *			since the array has been compressed by that
+     *			many characters.
+     *
+     */
+    if (tbPtr->selFirst >= firstPos) {
+	if (tbPtr->selFirst >= lastPos) {
+	    tbPtr->selFirst -= nBytes;
+	} else {
+	    tbPtr->selFirst = firstPos;
+	}
+    }
+    if (tbPtr->selLast >= firstPos) {
+	if (tbPtr->selLast >= lastPos) {
+	    tbPtr->selLast -= nBytes;
+	} else {
+	    tbPtr->selLast = firstPos;
+	}
+    }
+    if (tbPtr->selLast <= tbPtr->selFirst) {
+	tbPtr->selFirst = tbPtr->selLast = -1; /* Cut away the entire
+						    * selection. */ 
+    }
+    if (tbPtr->selAnchor >= firstPos) {
+	if (tbPtr->selAnchor >= lastPos) {
+	    tbPtr->selAnchor -= nBytes;
+	} else {
+	    tbPtr->selAnchor = firstPos;
+	}
+    }
+    if (tbPtr->insertPos >= firstPos) {
+	if (tbPtr->insertPos >= lastPos) {
+	    tbPtr->insertPos -= nBytes;
+	} else {
+	    tbPtr->insertPos = firstPos;
+	}
+    }
+    tbPtr->string = newText;
+    UpdateLayout(tbPtr);
+    EventuallyRedraw(tbPtr);
+    return TCL_OK;
+}
+
+static int
+AcquireText(tvPtr, tbPtr, entryPtr, columnPtr)
+    TreeView *tvPtr;
+    Textbox *tbPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewColumn *columnPtr;
+{
+    TreeViewStyle *stylePtr;
+    int x, y;
+    char *string;
+    TreeViewIcon icon;
+
+    if (columnPtr == &tvPtr->treeColumn) {
+	int level;
+
+	level = DEPTH(tvPtr, entryPtr->node);
+	x = SCREENX(tvPtr, entryPtr->worldX);
+	y = SCREENY(tvPtr, entryPtr->worldY);
+	x += ICONWIDTH(level) + ICONWIDTH(level + 1) + 4;
+	string = GETLABEL(entryPtr);
+	stylePtr = columnPtr->stylePtr;
+	icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
+    } else {
+	TreeViewValue *valuePtr;
+
+	x = SCREENX(tvPtr, columnPtr->worldX);
+	y = SCREENY(tvPtr, entryPtr->worldY);
+	stylePtr = columnPtr->stylePtr;
+	valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
+	string = valuePtr->string;
+	if (valuePtr->stylePtr != NULL) {
+	    stylePtr = valuePtr->stylePtr;
+	}
+	icon = stylePtr->icon;
+    }
+    if (tbPtr->textPtr != NULL) {
+	Blt_Free(tbPtr->textPtr);
+	tbPtr->textPtr = NULL;
+    }
+    if (tbPtr->string != NULL) {
+	Blt_Free(tbPtr->string);
+    }
+    if (string == NULL) {
+	string = "";
+    }
+    tbPtr->icon = icon;
+    tbPtr->entryPtr = entryPtr;
+    tbPtr->columnPtr = columnPtr;
+    tbPtr->x = x - tbPtr->borderWidth;
+    tbPtr->y = y - tbPtr->borderWidth;
+    
+    tbPtr->gap = stylePtr->gap;
+    tbPtr->string = Blt_Strdup(string);
+    tbPtr->gc = Blt_TreeViewGetStyleGC(stylePtr);
+    tbPtr->font = Blt_TreeViewGetStyleFont(tvPtr, stylePtr);
+    tbPtr->selFirst = tbPtr->selLast = -1;
+    UpdateLayout(tbPtr);
+    Tk_MapWindow(tbPtr->tkwin);
+    EventuallyRedraw(tbPtr);
+    return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetIndexFromObj --
+ *
+ *	Parse an index into an entry and return either its value
+ *	or an error.
+ *
+ * Results:
+ *	A standard Tcl result.  If all went well, then *indexPtr is
+ *	filled in with the character index (into entryPtr) corresponding to
+ *	string.  The index value is guaranteed to lie between 0 and
+ *	the number of characters in the string, inclusive.  If an
+ *	error occurs then an error message is left in the interp's result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetIndexFromObj(interp, tbPtr, objPtr, indexPtr)
+    Tcl_Interp *interp;
+    Textbox *tbPtr;
+    Tcl_Obj *objPtr;
+    int *indexPtr;
+{
+    int textPos;
+    char c;
+    char *string;
+
+    string = Tcl_GetString(objPtr);
+    if ((tbPtr->string == NULL) || (tbPtr->string[0] == '\0')) {
+	*indexPtr = 0;
+	return TCL_OK;
+    }
+    c = string[0];
+    if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+	textPos = tbPtr->selAnchor;
+    } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+	textPos = strlen(tbPtr->string);
+    } else if ((c == 'i') && (strcmp(string, "insert") == 0)) {
+	textPos = tbPtr->insertPos;
+    } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
+	textPos = tbPtr->insertPos;
+	if (textPos < (int)strlen(tbPtr->string)) {
+	    textPos++;
+	}
+    } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
+	textPos = tbPtr->insertPos;
+	if (textPos > 0) {
+	    textPos--;
+	}
+    } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
+	if (tbPtr->selFirst < 0) {
+	    textPos = -1;
+	} else {
+	    textPos = tbPtr->selFirst;
+	}
+    } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
+	if (tbPtr->selLast < 0) {
+	    textPos = -1;
+	} else {
+	    textPos = tbPtr->selLast;
+	}
+    } else if (c == '@') {
+	int x, y;
+
+	if (Blt_GetXY(interp, tbPtr->tkwin, string, &x, &y) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	textPos = PointerToIndex(tbPtr, x, y);
+    } else if (isdigit((int)c)) {
+	int number;
+	int maxChars;
+
+	if (Tcl_GetIntFromObj(interp, objPtr, &number) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	/* Don't allow the index to point outside the label. */
+	maxChars = Tcl_NumUtfChars(tbPtr->string, -1);
+	if (number < 0) {
+	    textPos = 0;
+	} else if (number > maxChars) {
+	    textPos = strlen(tbPtr->string);
+	} else {
+	    textPos = Tcl_UtfAtIndex(tbPtr->string, number) - 
+		tbPtr->string;
+	}
+    } else {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "bad label index \"", string, "\"",
+			     (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *indexPtr = textPos;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectText --
+ *
+ *	Modify the selection by moving its un-anchored end.  This
+ *	could make the selection either larger or smaller.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+SelectText(tbPtr, textPos)
+    Textbox *tbPtr; 	/* Information about textbox. */
+    int textPos;		/* Index of element that is to
+				 * become the "other" end of the
+				 * selection. */
+{
+    int selFirst, selLast;
+
+    /*
+     * Grab the selection if we don't own it already.
+     */
+    if ((tbPtr->exportSelection) && (tbPtr->selFirst == -1)) {
+	Tk_OwnSelection(tbPtr->tkwin, XA_PRIMARY, 
+			TextboxLostSelectionProc, tbPtr);
+    }
+    /*  If the anchor hasn't been set yet, assume the beginning of the text*/
+    if (tbPtr->selAnchor < 0) {
+	tbPtr->selAnchor = 0;
+    }
+    if (tbPtr->selAnchor <= textPos) {
+	selFirst = tbPtr->selAnchor;
+	selLast = textPos;
+    } else {
+	selFirst = textPos;
+	selLast = tbPtr->selAnchor;
+    }
+    if ((tbPtr->selFirst != selFirst) || (tbPtr->selLast != selLast)) {
+	tbPtr->selFirst = selFirst;
+	tbPtr->selLast = selLast;
+	EventuallyRedraw(tbPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextboxSelectionProc --
+ *
+ *	This procedure is called back by Tk when the selection is
+ *	requested by someone.  It returns part or all of the selection
+ *	in a buffer provided by the caller.
+ *
+ * Results:
+ *	The return value is the number of non-NULL bytes stored at
+ *	buffer.  Buffer is filled (or partially filled) with a
+ *	NUL-terminated string containing part or all of the
+ *	selection, as given by offset and maxBytes.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TextboxSelectionProc(clientData, offset, buffer, maxBytes)
+    ClientData clientData;	/* Information about the widget. */
+    int offset;			/* Offset within selection of first
+				 * character to be returned. */
+    char *buffer;		/* Location in which to place
+				 * selection. */
+    int maxBytes;		/* Maximum number of bytes to place
+				 * at buffer, not including terminating
+				 * NULL character. */
+{
+    Textbox *tbPtr = clientData;
+    int size;
+
+    size = strlen(tbPtr->string + offset);
+    /*
+     * Return the string currently in the textbox.
+     */
+    strncpy(buffer, tbPtr->string + offset, maxBytes);
+    buffer[maxBytes] = '\0';
+    return (size > maxBytes) ? maxBytes : size;
+}
+
+
+static void
+DestroyTextbox(data)
+    DestroyData data;
+{
+    Textbox *tbPtr = (Textbox *)data;
+
+    Blt_FreeObjOptions(textboxConfigSpecs, (char *)tbPtr, 
+	tbPtr->display, 0);
+
+    if (tbPtr->string != NULL) {
+	Blt_Free(tbPtr->string);
+    }
+    if (tbPtr->textPtr != NULL) {
+	Blt_Free(tbPtr->textPtr);
+    }
+    if (tbPtr->timerToken != NULL) {
+	Tcl_DeleteTimerHandler(tbPtr->timerToken);
+    }
+    if (tbPtr->tkwin != NULL) {
+	Tk_DeleteSelHandler(tbPtr->tkwin, XA_PRIMARY, XA_STRING);
+    }
+    Blt_Free(tbPtr);
+}
+
+static void
+ConfigureTextbox(tbPtr)
+    Textbox *tbPtr;
+{
+#ifdef notdef
+    GC newGC;
+    Textbox *tbPtr = tvPtr->tbPtr;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    /*
+     * GC for edit window.
+     */
+    gcMask = 0;
+    newGC = Tk_GetGC(tbPtr->tkwin, gcMask, &gcValues);
+    if (tbPtr->gc != NULL) {
+	Tk_FreeGC(tbPtr->display, tbPtr->gc);
+    }
+    tbPtr->gc = newGC;
+    tbPtr->width = tbPtr->textPtr->width + 
+	2 * (tbPtr->borderWidth + tbPtr->highlightWidth);
+    tbPtr->height = tbPtr->textPtr->height + 
+	2 * (tbPtr->borderWidth + tbPtr->highlightWidth);
+    
+    if (Tk_IsMapped(tbPtr->tkwin)) {
+	if ((tbPtr->height != Tk_Height(tbPtr->tkwin)) ||
+	    (tbPtr->width != Tk_Width(tbPtr->tkwin))) {
+	    Tk_ResizeWindow(tbPtr->tkwin, tbPtr->width, tbPtr->height);
+	}
+    }
+#endif
+}
+
+int
+Blt_TreeViewTextbox(tvPtr, entryPtr, columnPtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewColumn *columnPtr;
+{
+    Tk_Window tkwin;
+    Textbox *tbPtr;
+    char editClass[20];
+
+    if (tvPtr->comboWin != NULL) {
+	Tk_DestroyWindow(tvPtr->comboWin);
+    }
+    tkwin = Tk_CreateWindow(tvPtr->interp, tvPtr->tkwin, "edit", (char *)NULL);
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+
+    Tk_MakeWindowExist(tkwin);
+
+    sprintf(editClass, "%sEditor", Tk_Class(tvPtr->tkwin));
+    Tk_SetClass(tkwin, editClass); 
+
+    tbPtr = Blt_Calloc(1, sizeof(Textbox));
+    assert(tbPtr);
+
+    tbPtr->interp = tvPtr->interp;
+    tbPtr->display = Tk_Display(tkwin);
+    tbPtr->tkwin = tkwin;
+    tbPtr->borderWidth = 1;
+    tbPtr->relief = TK_RELIEF_SOLID;
+    tbPtr->selRelief = TK_RELIEF_FLAT;
+    tbPtr->selBorderWidth = 1;
+    tbPtr->selAnchor = -1;
+    tbPtr->selFirst = tbPtr->selLast = -1;
+    tbPtr->onTime = 600;
+    tbPtr->active = TRUE;
+    tbPtr->offTime = 300;
+    tbPtr->tvPtr = tvPtr;
+    tbPtr->buttonRelief = TK_RELIEF_SUNKEN;
+    tbPtr->buttonBorderWidth = 1;
+    tvPtr->comboWin = tkwin;
+#if (TK_MAJOR_VERSION > 4)
+    Blt_SetWindowInstanceData(tkwin, tbPtr);
+#endif
+    Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING, 
+	TextboxSelectionProc, tbPtr, XA_STRING);
+    Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask |
+	FocusChangeMask, TextboxEventProc, tbPtr);
+    Tcl_CreateObjCommand(tvPtr->interp, Tk_PathName(tkwin), 
+	TextboxCmd, tbPtr, NULL);
+    if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tkwin, textboxConfigSpecs, 0, 
+	(Tcl_Obj **)NULL, (char *)tbPtr, 0) != TCL_OK) {
+	Tk_DestroyWindow(tkwin);
+	return TCL_ERROR;
+    }
+    AcquireText(tvPtr, tbPtr, entryPtr, columnPtr);
+    tbPtr->insertPos = strlen(tbPtr->string);
+    
+    Tk_MoveResizeWindow(tkwin, tbPtr->x, tbPtr->y, tbPtr->width, 
+	tbPtr->height);
+    Tk_MapWindow(tkwin);
+    Tk_MakeWindowExist(tkwin);
+    XRaiseWindow(tbPtr->display, Tk_WindowId(tkwin));
+    EventuallyRedraw(tbPtr);
+    return TCL_OK;
+}
+
+static void
+DisplayTextbox(clientData)
+    ClientData clientData;
+{
+    Textbox *tbPtr = clientData;
+    Pixmap drawable;
+    register int i;
+    int x1, x2;
+    int count, nChars;
+    int leftPos, rightPos;
+    int selStart, selEnd, selLength;
+    int x, y;
+    TextFragment *fragPtr;
+    Tk_FontMetrics fontMetrics;
+#ifdef notdef
+    int buttonX, buttonY, buttonWidth, buttonHeight;
+#endif
+    tbPtr->flags &= ~TEXTBOX_REDRAW;
+    if (!Tk_IsMapped(tbPtr->tkwin)) {
+	return;
+    }
+    if (tbPtr->columnPtr == NULL) {
+	return;
+    }
+    drawable = Tk_GetPixmap(tbPtr->display, Tk_WindowId(tbPtr->tkwin), 
+	Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin), 
+	Tk_Depth(tbPtr->tkwin));
+
+    Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->border, 0, 0,
+	Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin), 
+	tbPtr->borderWidth, tbPtr->relief);
+
+    x = tbPtr->borderWidth + tbPtr->gap;
+    y = tbPtr->borderWidth;
+
+#ifdef notdef
+    buttonX = Tk_Width(tbPtr->tkwin) - 
+	(tbPtr->borderWidth + tbPtr->gap + 1);
+    buttonY = tbPtr->borderWidth + 1;
+#endif
+
+    if (tbPtr->icon != NULL) {
+	y += (tbPtr->height - TreeViewIconHeight(tbPtr->icon)) / 2;
+	Tk_RedrawImage(TreeViewIconBits(tbPtr->icon), 0, 0, 
+		       TreeViewIconWidth(tbPtr->icon), 
+		       TreeViewIconHeight(tbPtr->icon), 
+		       drawable, x, y);
+	x += TreeViewIconWidth(tbPtr->icon) + tbPtr->gap;
+    }
+    
+    Tk_GetFontMetrics(tbPtr->font, &fontMetrics);
+    fragPtr = tbPtr->textPtr->fragArr;
+    count = 0;
+    y = tbPtr->borderWidth;
+    if (tbPtr->height > fontMetrics.linespace) {
+	y += (tbPtr->height - fontMetrics.linespace) / 2;
+    }
+    for (i = 0; i < tbPtr->textPtr->nFrags; i++, fragPtr++) {
+	leftPos = count;
+	count += fragPtr->count;
+	rightPos = count;
+	if ((rightPos < tbPtr->selFirst) || (leftPos > tbPtr->selLast)) {
+	    /* No part of the text fragment is selected. */
+	    Tk_DrawChars(tbPtr->display, drawable, tbPtr->gc, 
+			 tbPtr->font, fragPtr->text, fragPtr->count, 
+			 x + fragPtr->x, y + fragPtr->y);
+	    continue;
+	}
+
+	/*
+	 *  A text fragment can have up to 3 regions:
+	 *
+	 *	1. Text before the start the selection
+	 *	2. Selected text itself (drawn in a raised border)
+	 *	3. Text following the selection.
+	 */
+
+	selStart = leftPos;
+	selEnd = rightPos;
+	/* First adjust selected region for current line. */
+	if (tbPtr->selFirst > leftPos) {
+	    selStart = tbPtr->selFirst;
+	}
+	if (tbPtr->selLast < rightPos) {
+	    selEnd = tbPtr->selLast;
+	}
+	selLength = (selEnd - selStart);
+	x1 = x;
+
+	if (selStart > leftPos) { /* Normal text preceding the selection */
+	    nChars = (selStart - leftPos);
+	    Tk_MeasureChars(tbPtr->font, tbPtr->string + leftPos,
+		    nChars, 10000, DEF_TEXT_FLAGS, &x1);
+	    x1 += x;
+	}
+	if (selLength > 0) {	/* The selection itself */
+	    int width;
+
+	    Tk_MeasureChars(tbPtr->font, fragPtr->text + selStart, selLength,
+		10000, DEF_TEXT_FLAGS, &x2);
+	    x2 += x;
+	    width = (x2 - x1) + 1;
+	    Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->selBorder,
+		x1, y + fragPtr->y - fontMetrics.ascent, 
+	        width, fontMetrics.linespace,
+		tbPtr->selBorderWidth, tbPtr->selRelief);
+	}
+	Tk_DrawChars(Tk_Display(tbPtr->tkwin), drawable, tbPtr->gc, 
+	     tbPtr->font, fragPtr->text, fragPtr->count, 
+	     fragPtr->x + x, fragPtr->y + y);
+    }
+    if ((tbPtr->flags & TEXTBOX_FOCUS) && (tbPtr->cursorOn)) {
+	int left, top, right, bottom;
+
+	IndexToPointer(tbPtr);
+	left = tbPtr->cursorX + 1;
+	right = left + 1;
+	top = tbPtr->cursorY;
+	if (tbPtr->height > fontMetrics.linespace) {
+	    top += (tbPtr->height - fontMetrics.linespace) / 2;
+	}
+	bottom = top + tbPtr->cursorHeight - 1;
+	XDrawLine(tbPtr->display, drawable, tbPtr->gc, left, top, left,
+		bottom);
+	XDrawLine(tbPtr->display, drawable, tbPtr->gc, left - 1, top, right,
+		top);
+	XDrawLine(tbPtr->display, drawable, tbPtr->gc, left - 1, bottom, 
+		right, bottom);
+    }
+#ifdef notdef
+    buttonWidth = STD_ARROW_WIDTH + 6 + 2 * tbPtr->buttonBorderWidth;
+    buttonX -= buttonWidth;
+    buttonHeight = Tk_Height(tbPtr->tkwin) - 4;
+    Blt_Fill3DRectangle(tbPtr->tkwin, drawable, tbPtr->buttonBorder, 
+	       buttonX, buttonY, buttonWidth, buttonHeight,
+	       tbPtr->buttonBorderWidth, tbPtr->buttonRelief); 
+    
+    buttonX += buttonWidth / 2;
+    buttonY = tbPtr->height / 2;
+    Blt_DrawArrow(tbPtr->display, drawable, tbPtr->gc, buttonX,
+		  buttonY, STD_ARROW_HEIGHT, ARROW_DOWN);
+#endif
+    Blt_Draw3DRectangle(tbPtr->tkwin, drawable, tbPtr->border, 0, 0,
+	Tk_Width(tbPtr->tkwin), Tk_Height(tbPtr->tkwin), 
+	tbPtr->borderWidth, tbPtr->relief);
+    XCopyArea(tbPtr->display, drawable, Tk_WindowId(tbPtr->tkwin),
+	tbPtr->gc, 0, 0, Tk_Width(tbPtr->tkwin), 
+	Tk_Height(tbPtr->tkwin), 0, 0);
+    Tk_FreePixmap(tbPtr->display, drawable);
+}
+
+/*ARGSUSED*/
+static int
+ApplyOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    TreeViewEntry *entryPtr;
+    TreeView *tvPtr = tbPtr->tvPtr;
+
+    entryPtr = tbPtr->entryPtr;
+    if (tbPtr->columnPtr == &tvPtr->treeColumn) {
+	if (entryPtr->labelUid != NULL) {
+	    Blt_TreeViewFreeUid(tvPtr, entryPtr->labelUid);
+	}
+	if (tbPtr->string == NULL) {
+	    entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, "");
+	} else {
+	    entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, tbPtr->string);
+	}
+    } else {
+	TreeViewColumn *columnPtr;
+	Tcl_Obj *objPtr;
+
+	columnPtr = tbPtr->columnPtr;
+	objPtr = Tcl_NewStringObj(tbPtr->string, -1);
+	if (Blt_TreeSetValueByKey(interp, tvPtr->tree, entryPtr->node, 
+		columnPtr->key, objPtr) != TCL_OK) {
+	    Tcl_DecrRefCount(objPtr);
+	    return TCL_ERROR;
+	}
+	entryPtr->flags |= ENTRY_DIRTY;
+    }	
+    if (tvPtr != NULL) {
+	Blt_TreeViewConfigureEntry(tvPtr, entryPtr, 0, NULL, 
+		BLT_CONFIG_OBJV_ONLY);
+	tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+    }
+    Tk_DestroyWindow(tbPtr->tkwin);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CancelOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    Tk_DestroyWindow(tbPtr->tkwin);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    return Blt_ConfigureValueFromObj(interp, tbPtr->tkwin, 
+	textboxConfigSpecs, (char *)tbPtr, objv[2], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure the one of more
+ *	entries in the widget.
+ *
+ *	  .c configure option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for tvPtr; old resources get freed, if there
+ *	were any.  The hypertext is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ConfigureOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 2) {
+	return Blt_ConfigureInfoFromObj(interp, tbPtr->tkwin, 
+		textboxConfigSpecs, (char *)tbPtr, (Tcl_Obj *)NULL, 0);
+    } else if (objc == 3) {
+	return Blt_ConfigureInfoFromObj(interp, tbPtr->tkwin, 
+		textboxConfigSpecs, (char *)tbPtr, objv[3], 0);
+    }
+    if (Blt_ConfigureWidgetFromObj(interp, tbPtr->tkwin, 
+	textboxConfigSpecs, objc - 2, objv + 2, (char *)tbPtr, 
+	BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    ConfigureTextbox(tbPtr);
+    EventuallyRedraw(tbPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Remove one or more characters from the label of an entry.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Memory gets freed, the entry gets modified and (eventually)
+ *	redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int firstPos, lastPos;
+
+    if (tbPtr->entryPtr == NULL) {
+	return TCL_OK;
+    }
+    if (GetIndexFromObj(interp, tbPtr, objv[2], &firstPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    lastPos = firstPos;
+    if ((objc == 4) && 
+	(GetIndexFromObj(interp, tbPtr, objv[3], &lastPos) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (firstPos > lastPos) {
+	return TCL_OK;
+    }
+    return DeleteText(tbPtr, firstPos, lastPos);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IcursorOp --
+ *
+ *	Returns the numeric index of the given string. Indices can be
+ *	one of the following:
+ *
+ *	"anchor"	Selection anchor.
+ *	"end"		End of the label.
+ *	"insert"	Insertion cursor.
+ *	"sel.first"	First character selected.
+ *	"sel.last"	Last character selected.
+ *	@x,y		Index at X-Y screen coordinate.
+ *	number		Returns the same number.
+ *
+ * Results:
+ *	A standard Tcl result.  If the argument does not represent a
+ *	valid label index, then TCL_ERROR is returned and the interpreter
+ *	result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IcursorOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int textPos;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[2], &textPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (tbPtr->columnPtr != NULL) {
+	tbPtr->insertPos = textPos;
+	IndexToPointer(tbPtr);
+	EventuallyRedraw(tbPtr);
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *	Returns the numeric index of the given string. Indices can be
+ *	one of the following:
+ *
+ *	"anchor"	Selection anchor.
+ *	"end"		End of the label.
+ *	"insert"	Insertion cursor.
+ *	"sel.first"	First character selected.
+ *	"sel.last"	Last character selected.
+ *	@x,y		Index at X-Y screen coordinate.
+ *	number		Returns the same number.
+ *
+ * Results:
+ *	A standard Tcl result.  If the argument does not represent a
+ *	valid label index, then TCL_ERROR is returned and the interpreter
+ *	result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IndexOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int textPos;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[2], &textPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((tbPtr->columnPtr != NULL) && (tbPtr->string != NULL)) {
+	int nChars;
+
+	nChars = Tcl_NumUtfChars(tbPtr->string, textPos);
+	Tcl_SetObjResult(interp, Tcl_NewIntObj(nChars));
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertOp --
+ *
+ *	Add new characters to the label of an entry.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	New information gets added to tbPtr;  it will be redisplayed
+ *	soon, but not necessarily immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InsertOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int extra;
+    int insertPos;
+    char *string;
+
+    if (tbPtr->entryPtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (GetIndexFromObj(interp, tbPtr, objv[2], &insertPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    string = Tcl_GetStringFromObj(objv[3], &extra);
+    if (extra == 0) {	/* Nothing to insert. Move the cursor anyways. */
+	tbPtr->insertPos = insertPos;
+    } else {
+	InsertText(tbPtr, string, insertPos, extra);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionAdjustOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int textPos;
+    int half1, half2;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    half1 = (tbPtr->selFirst + tbPtr->selLast) / 2;
+    half2 = (tbPtr->selFirst + tbPtr->selLast + 1) / 2;
+    if (textPos < half1) {
+	tbPtr->selAnchor = tbPtr->selLast;
+    } else if (textPos > half2) {
+	tbPtr->selAnchor = tbPtr->selFirst;
+    }
+    return SelectText(tbPtr, textPos);
+}
+
+/*ARGSUSED*/
+static int
+SelectionClearOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    if (tbPtr->selFirst != -1) {
+	tbPtr->selFirst = tbPtr->selLast = -1;
+	EventuallyRedraw(tbPtr);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionFromOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int textPos;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tbPtr->selAnchor = textPos;
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionPresentOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    int bool;
+
+    bool = (tbPtr->selFirst != -1);
+    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+SelectionRangeOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int selFirst, selLast;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[3], &selFirst) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (GetIndexFromObj(interp, tbPtr, objv[4], &selLast) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    tbPtr->selAnchor = selFirst;
+    return SelectText(tbPtr, selLast);
+}
+
+/*ARGSUSED*/
+static int
+SelectionToOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int textPos;
+
+    if (GetIndexFromObj(interp, tbPtr, objv[3], &textPos) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return SelectText(tbPtr, textPos);
+}
+
+
+static Blt_OpSpec selectionOps[] =
+{
+    {"adjust", 1, (Blt_Op)SelectionAdjustOp, 4, 4, "index",},
+    {"clear", 1, (Blt_Op)SelectionClearOp, 3, 3, "",},
+    {"from", 1, (Blt_Op)SelectionFromOp, 4, 4, "index"},
+    {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, ""},
+    {"range", 1, (Blt_Op)SelectionRangeOp, 5, 5, "start end",},
+    {"to", 1, (Blt_Op)SelectionToOp, 4, 4, "index"},
+};
+
+static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
+
+/*
+ *	This procedure handles the individual options for text
+ *	selections.  The selected text is designated by start and end
+ *	indices into the text pool.  The selected segment has both a
+ *	anchored and unanchored ends.  The following selection
+ *	operations are implemented:
+ *
+ *	  "adjust"	- resets either the first or last index
+ *			  of the selection.
+ *	  "clear"	- clears the selection. Sets first/last
+ *			  indices to -1.
+ *	  "from"	- sets the index of the selection anchor.
+ *	  "present"	- return "1" if a selection is available,
+ *			  "0" otherwise.
+ *	  "range"	- sets the first and last indices.
+ *	  "to"		- sets the index of the un-anchored end.
+ */
+static int
+SelectionOp(tbPtr, interp, objc, objv)
+    Textbox *tbPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2, 
+	objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tbPtr, interp, objc, objv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextboxCmd --
+ *
+ *	This procedure handles entry operations.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec comboOps[] =
+{
+    {"apply", 1, (Blt_Op)ApplyOp, 2, 2, "",},
+    {"cancel", 2, (Blt_Op)CancelOp, 2, 2, "",},
+    {"cget", 2, (Blt_Op)CgetOp, 3, 3, "value",},
+    {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value...?",},
+    {"delete", 1, (Blt_Op)DeleteOp, 3, 4, "first ?last?"},
+    {"icursor", 2, (Blt_Op)IcursorOp, 3, 3, "index"},
+    {"index", 3, (Blt_Op)IndexOp, 3, 3, "index"},
+    {"insert", 3, (Blt_Op)InsertOp, 4, 4, "index string"},
+    {"selection", 1, (Blt_Op)SelectionOp, 2, 0, "args"},
+};
+static int nComboOps = sizeof(comboOps) / sizeof(Blt_OpSpec);
+
+static int
+TextboxCmd(clientData, interp, objc, objv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Textbox *tbPtr = clientData;
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nComboOps, comboOps, BLT_OP_ARG1, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (tbPtr, interp, objc, objv);
+    return result;
+}
+
+#endif
+
Index: trunk/kitgen/8.x/blt/generic/bltTreeViewStyle.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltTreeViewStyle.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltTreeViewStyle.c	(revision 175)
@@ -0,0 +1,2674 @@
+
+/*
+ * bltTreeViewStyle.c --
+ *
+ *	This module implements styles for treeview widget cells.
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ *	The "treeview" widget was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREEVIEW
+
+#include "bltTreeView.h"
+#include "bltList.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#define STYLE_GAP		2
+
+static Blt_OptionParseProc ObjToIcon;
+static Blt_OptionPrintProc IconToObj;
+static Blt_OptionFreeProc FreeIcon;
+Blt_CustomOption bltTreeViewIconOption =
+{
+    /* Contains a pointer to the widget that's currently being
+     * configured.  This is used in the custom configuration parse
+     * routine for icons.  */
+    ObjToIcon, IconToObj, FreeIcon, NULL,
+};
+
+#define DEF_STYLE_HIGHLIGHT_BACKGROUND	STD_NORMAL_BACKGROUND
+#define DEF_STYLE_HIGHLIGHT_FOREGROUND	STD_NORMAL_FOREGROUND
+#ifdef WIN32
+#define DEF_STYLE_ACTIVE_BACKGROUND	RGB_GREY85
+#else
+#define DEF_STYLE_ACTIVE_BACKGROUND	RGB_GREY95
+#endif
+#define DEF_STYLE_ACTIVE_FOREGROUND 	STD_ACTIVE_FOREGROUND
+#define DEF_STYLE_GAP			"2"
+
+typedef struct {
+    int refCount;		/* Usage reference count. */
+    unsigned int flags;
+    char *name;			
+    TreeViewStyleClass *classPtr; /* Class-specific routines to manage style. */
+    Blt_HashEntry *hashPtr;
+
+    /* General style fields. */
+    Tk_Cursor cursor;		/* X Cursor */
+    TreeViewIcon icon;		/* If non-NULL, is a Tk_Image to be drawn
+				 * in the cell. */
+    int gap;			/* # pixels gap between icon and text. */
+    Tk_Font font;
+    XColor *fgColor;		/* Normal foreground color of cell. */
+    Tk_3DBorder border;		/* Normal background color of cell. */
+    XColor *highlightFgColor;	/* Foreground color of cell when
+				 * highlighted. */
+    Tk_3DBorder highlightBorder;/* Background color of cell when
+				 * highlighted. */
+    XColor *activeFgColor;	/* Foreground color of cell when active. */
+    Tk_3DBorder activeBorder;	/* Background color of cell when active. */
+
+    /* TextBox-specific fields */
+    GC gc;
+    GC highlightGC;
+    GC activeGC;
+
+    int side;			/* Position of the text in relation to
+				 * the icon.  */
+    Blt_TreeKey key;		/* Actual data resides in this tree
+				   value. */
+
+} TreeViewTextBox;
+
+#ifdef WIN32
+#define DEF_TEXTBOX_CURSOR		"arrow"
+#else
+#define DEF_TEXTBOX_CURSOR		"hand2"
+#endif /*WIN32*/
+#define DEF_TEXTBOX_SIDE		"left"
+
+static Blt_ConfigSpec textBoxSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", 
+	"ActiveBackground", DEF_STYLE_ACTIVE_BACKGROUND, 
+	Blt_Offset(TreeViewTextBox, activeBorder), 0},
+    {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-activefg", "activeFackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", 
+	"ActiveForeground", DEF_STYLE_ACTIVE_FOREGROUND, 
+	Blt_Offset(TreeViewTextBox, activeFgColor), 0},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	(char *)NULL, Blt_Offset(TreeViewTextBox, border), BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_TEXTBOX_CURSOR, Blt_Offset(TreeViewTextBox, cursor), 0},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_FONT, "-font", "font", "Font",
+	(char *)NULL, Blt_Offset(TreeViewTextBox, font), 
+        BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	(char *)NULL, Blt_Offset(TreeViewTextBox, fgColor),BLT_CONFIG_NULL_OK },
+    {BLT_CONFIG_DISTANCE, "-gap", "gap", "Gap",
+	DEF_STYLE_GAP, Blt_Offset(TreeViewTextBox, gap), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
+	"HighlightBackground", DEF_STYLE_HIGHLIGHT_BACKGROUND, 
+        Blt_Offset(TreeViewTextBox, highlightBorder), BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, "-highlightforeground", "highlightForeground", 
+	"HighlightForeground", DEF_STYLE_HIGHLIGHT_FOREGROUND, 
+	 Blt_Offset(TreeViewTextBox, highlightFgColor), 0},
+    {BLT_CONFIG_SYNONYM, "-highlightbg", "highlightBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-highlightfg", "highlightForeground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_CUSTOM, "-icon", "icon", "Icon",
+	(char *)NULL, Blt_Offset(TreeViewTextBox, icon), 
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconOption},
+    {BLT_CONFIG_STRING, "-key", "key", "key",
+	(char *)NULL, Blt_Offset(TreeViewTextBox, key), 
+	BLT_CONFIG_NULL_OK, 0},
+    {BLT_CONFIG_SIDE, "-side", "side", "side",
+	DEF_TEXTBOX_SIDE, Tk_Offset(TreeViewTextBox, side),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+typedef struct {
+    int refCount;		/* Usage reference count. */
+    unsigned int flags;		/* Contains style type and update flags. */
+    char *name;			/* Instance name. */
+    TreeViewStyleClass *classPtr; /* Class-specific routines to manage style. */
+    Blt_HashEntry *hashPtr;	
+
+    /* General style fields. */
+    Tk_Cursor cursor;		/* X Cursor */
+    TreeViewIcon icon;		/* If non-NULL, is a Tk_Image to be drawn
+				 * in the cell. */
+    int gap;			/* # pixels gap between icon and text. */
+    Tk_Font font;
+    XColor *fgColor;		/* Normal foreground color of cell. */
+    Tk_3DBorder border;		/* Normal background color of cell. */
+    XColor *highlightFgColor;	/* Foreground color of cell when
+				 * highlighted. */
+    Tk_3DBorder highlightBorder;/* Background color of cell when
+				 * highlighted. */
+    XColor *activeFgColor;	/* Foreground color of cell when active. */
+    Tk_3DBorder activeBorder;	/* Background color of cell when active. */
+
+    /* Checkbox specific fields. */
+    GC gc;
+    GC highlightGC;
+    GC activeGC;
+
+    Blt_TreeKey key;		/* Actual data resides in this tree
+				   value. */
+    int size;			/* Size of the checkbox. */
+    int showValue;		/* If non-zero, display the on/off value.  */
+    char *onValue;
+    char *offValue;
+    int lineWidth;		/* Linewidth of the surrounding box. */
+
+    XColor *boxColor;		/* Rectangle (box) color (grey). */
+    XColor *fillColor;		/* Fill color (white) */
+    XColor *checkColor;		/* Check color (red). */
+
+    GC boxGC;
+    GC fillGC;			/* Box fill GC */
+    GC checkGC;
+
+    TextLayout *onPtr, *offPtr;
+    
+} TreeViewCheckBox;
+
+#define DEF_CHECKBOX_BOX_COLOR		"black"
+#define DEF_CHECKBOX_CHECK_COLOR	"red"
+#define DEF_CHECKBOX_FILL_COLOR		"white"
+#define DEF_CHECKBOX_OFFVALUE		"0"
+#define DEF_CHECKBOX_ONVALUE		"1"
+#define DEF_CHECKBOX_SHOWVALUE		"yes"
+#define DEF_CHECKBOX_SIZE		"11"
+#define DEF_CHECKBOX_LINEWIDTH		"2"
+#define DEF_CHECKBOX_GAP		"4"
+#ifdef WIN32
+#define DEF_CHECKBOX_CURSOR		"arrow"
+#else
+#define DEF_CHECKBOX_CURSOR		"hand2"
+#endif /*WIN32*/
+
+static Blt_ConfigSpec checkBoxSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", 
+	"ActiveBackground", DEF_STYLE_ACTIVE_BACKGROUND, 
+	Blt_Offset(TreeViewCheckBox, activeBorder), 0},
+    {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-activefg", "activeFackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", 
+	"ActiveForeground", DEF_STYLE_ACTIVE_FOREGROUND, 
+	Blt_Offset(TreeViewCheckBox, activeFgColor), 0},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, border), BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_POS_DISTANCE, "-boxsize", "boxSize", "BoxSize",
+	DEF_CHECKBOX_SIZE, Blt_Offset(TreeViewCheckBox, size), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_CHECKBOX_CURSOR, Blt_Offset(TreeViewTextBox, cursor), 0},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_FONT, "-font", "font", "Font",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, font), 
+        BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, fgColor), 
+        BLT_CONFIG_NULL_OK },
+    {BLT_CONFIG_DISTANCE, "-gap", "gap", "Gap",
+	DEF_CHECKBOX_GAP, Blt_Offset(TreeViewCheckBox, gap), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
+	"HighlightBackground", DEF_STYLE_HIGHLIGHT_BACKGROUND, 
+        Blt_Offset(TreeViewCheckBox, highlightBorder), BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, "-highlightforeground", "highlightForeground", 
+	"HighlightForeground", DEF_STYLE_HIGHLIGHT_FOREGROUND, 
+	 Blt_Offset(TreeViewCheckBox, highlightFgColor), 0},
+    {BLT_CONFIG_SYNONYM, "-highlightbg", "highlightBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-highlightfg", "highlightForeground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_CUSTOM, "-icon", "icon", "Icon",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, icon), 
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconOption},
+    {BLT_CONFIG_STRING, "-key", "key", "key",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, key), 
+	BLT_CONFIG_NULL_OK, 0},
+    {BLT_CONFIG_DISTANCE, "-linewidth", "lineWidth", "LineWidth",
+	DEF_CHECKBOX_LINEWIDTH, 
+	Blt_Offset(TreeViewCheckBox, lineWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_COLOR, "-checkcolor", "checkColor", "CheckColor", 
+	DEF_CHECKBOX_CHECK_COLOR, Blt_Offset(TreeViewCheckBox, checkColor), 0},
+    {BLT_CONFIG_COLOR, "-boxcolor", "boxColor", "BoxColor", 
+	DEF_CHECKBOX_BOX_COLOR, Blt_Offset(TreeViewCheckBox, boxColor), 0},
+    {BLT_CONFIG_COLOR, "-fillcolor", "fillColor", "FillColor", 
+	DEF_CHECKBOX_FILL_COLOR, Blt_Offset(TreeViewCheckBox, fillColor), 0},
+    {BLT_CONFIG_STRING, "-offvalue", "offValue", "OffValue",
+	DEF_CHECKBOX_OFFVALUE, Blt_Offset(TreeViewCheckBox, offValue), 
+        BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_STRING, "-onvalue", "onValue", "OnValue",
+	DEF_CHECKBOX_ONVALUE, Blt_Offset(TreeViewCheckBox, onValue), 
+        BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_STRING, "-key", "key", "key",
+	(char *)NULL, Blt_Offset(TreeViewCheckBox, key), BLT_CONFIG_NULL_OK, 0},
+    {BLT_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
+	DEF_CHECKBOX_SHOWVALUE, Blt_Offset(TreeViewCheckBox, showValue), 
+        BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+typedef struct {
+    int refCount;		/* Usage reference count. */
+    unsigned int flags;
+    char *name;			
+    TreeViewStyleClass *classPtr;/* Class-specific style routines. */
+    Blt_HashEntry *hashPtr;
+
+    /* General style fields. */
+    Tk_Cursor cursor;		/* X Cursor */
+    TreeViewIcon icon;		/* If non-NULL, is a Tk_Image to be drawn
+				 * in the cell. */
+    int gap;			/* # pixels gap between icon and text. */
+    Tk_Font font;
+    XColor *fgColor;		/* Normal foreground color of cell. */
+    Tk_3DBorder border;		/* Normal background color of cell. */
+    XColor *highlightFgColor;	/* Foreground color of cell when
+				 * highlighted. */
+    Tk_3DBorder highlightBorder;/* Background color of cell when
+				 * highlighted. */
+    XColor *activeFgColor;	/* Foreground color of cell when active. */
+    Tk_3DBorder activeBorder;	/* Background color of cell when active. */
+
+    /* ComboBox-specific fields */
+    GC gc;
+    GC highlightGC;
+    GC activeGC;
+
+    int borderWidth;		/* Width of outer border surrounding
+				 * the entire box. */
+    int relief;			/* Relief of outer border. */
+
+    Blt_TreeKey key;		/* Actual data resides in this tree
+				   value. */
+
+    char *choices;		/* List of available choices. */
+    char *choiceIcons;		/* List of icons associated with choices. */
+    int scrollWidth;
+    int button;
+    int buttonWidth;
+    int buttonBorderWidth;	/* Border width of button. */
+    int buttonRelief;		/* Normal relief of button. */
+
+} TreeViewComboBox;
+
+#define DEF_COMBOBOX_BORDERWIDTH	"1"
+#define DEF_COMBOBOX_BUTTON_BORDERWIDTH	"1"
+#define DEF_COMBOBOX_BUTTON_RELIEF	"raised"
+#define DEF_COMBOBOX_RELIEF		"flat"
+#ifdef WIN32
+#define DEF_COMBOBOX_CURSOR		"arrow"
+#else
+#define DEF_COMBOBOX_CURSOR		"hand2"
+#endif /*WIN32*/
+
+
+static Blt_ConfigSpec comboBoxSpecs[] =
+{
+    {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", 
+	"ActiveBackground", DEF_STYLE_ACTIVE_BACKGROUND, 
+	Blt_Offset(TreeViewComboBox, activeBorder), 0},
+    {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-activefg", "activeFackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", 
+	"ActiveForeground", DEF_STYLE_ACTIVE_FOREGROUND, 
+	Blt_Offset(TreeViewComboBox, activeFgColor), 0},
+    {BLT_CONFIG_BORDER, "-background", "background", "Background",
+	(char *)NULL, Blt_Offset(TreeViewComboBox, border), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 
+	0},
+    {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
+	DEF_COMBOBOX_BORDERWIDTH, Blt_Offset(TreeViewComboBox, borderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_RELIEF, "-buttonrelief", "buttonRelief", "ButtonRelief",
+	DEF_COMBOBOX_BUTTON_RELIEF, Blt_Offset(TreeViewComboBox, buttonRelief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_DISTANCE, "-buttonborderwidth", "buttonBorderWidth", 
+	"ButtonBorderWidth", DEF_COMBOBOX_BUTTON_BORDERWIDTH, 
+	Blt_Offset(TreeViewComboBox, buttonBorderWidth),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_CURSOR, "-cursor", "cursor", "Cursor",
+	DEF_COMBOBOX_CURSOR, Blt_Offset(TreeViewTextBox, cursor), 0},
+    {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 
+	0, 0},
+    {BLT_CONFIG_FONT, "-font", "font", "Font",
+	(char *)NULL, Blt_Offset(TreeViewComboBox, font), 
+	BLT_CONFIG_NULL_OK},
+    {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+	(char *)NULL, Blt_Offset(TreeViewComboBox, fgColor), 
+	BLT_CONFIG_NULL_OK },
+    {BLT_CONFIG_DISTANCE, "-gap", "gap", "Gap",
+	DEF_STYLE_GAP, Blt_Offset(TreeViewComboBox, gap), 
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
+	"HighlightBackground", DEF_STYLE_HIGHLIGHT_BACKGROUND, 
+        Blt_Offset(TreeViewComboBox, highlightBorder), BLT_CONFIG_COLOR_ONLY},
+    {BLT_CONFIG_COLOR, "-highlightforeground", "highlightForeground", 
+	"HighlightForeground", DEF_STYLE_HIGHLIGHT_FOREGROUND, 
+	 Blt_Offset(TreeViewComboBox, highlightFgColor), 0},
+    {BLT_CONFIG_SYNONYM, "-highlightbg", "highlightBackground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_SYNONYM, "-highlightfg", "highlightForeground", 
+	(char *)NULL, (char *)NULL, 0, 0},
+    {BLT_CONFIG_CUSTOM, "-icon", "icon", "Icon",
+	(char *)NULL, Blt_Offset(TreeViewComboBox, icon), 
+	BLT_CONFIG_NULL_OK, &bltTreeViewIconOption},
+    {BLT_CONFIG_STRING, "-key", "key", "key",
+        (char *)NULL, Blt_Offset(TreeViewComboBox, key), 
+	BLT_CONFIG_NULL_OK, 0},
+    {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+	DEF_COMBOBOX_RELIEF, Blt_Offset(TreeViewComboBox, relief),
+	BLT_CONFIG_DONT_SET_DEFAULT},
+    {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+	(char *)NULL, 0, 0}
+};
+
+typedef TreeViewStyle *(StyleCreateProc) _ANSI_ARGS_((TreeView *tvPtr, 
+	Blt_HashEntry *hPtr));
+
+static StyleConfigProc ConfigureTextBox, ConfigureCheckBox, ConfigureComboBox;
+static StyleCreateProc CreateTextBox, CreateCheckBox, CreateComboBox;
+static StyleDrawProc DrawTextBox, DrawCheckBox, DrawComboBox;
+static StyleEditProc EditTextBox, EditCheckBox, EditComboBox;
+static StyleFreeProc FreeTextBox, FreeCheckBox, FreeComboBox;
+static StyleMeasureProc MeasureTextBox, MeasureCheckBox, MeasureComboBox;
+static StylePickProc PickCheckBox, PickComboBox;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ObjToIcon --
+ *
+ *	Convert the name of an icon into a Tk image.
+ *
+ * Results:
+ *	If the string is successfully converted, TCL_OK is returned.
+ *	Otherwise, TCL_ERROR is returned and an error message is left in
+ *	interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToIcon(clientData, interp, tkwin, objPtr, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Interpreter to send results back to */
+    Tk_Window tkwin;		/* Not used. */
+    Tcl_Obj *objPtr;		/* Tcl_Obj representing the new value. */
+    char *widgRec;
+    int offset;
+{
+    TreeView *tvPtr = clientData;
+    TreeViewIcon *iconPtr = (TreeViewIcon *)(widgRec + offset);
+    TreeViewIcon icon;
+
+    icon = Blt_TreeViewGetIcon(tvPtr, Tcl_GetString(objPtr));
+    if (icon == NULL) {
+	return TCL_ERROR;
+    }
+    *iconPtr = icon;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconToObj --
+ *
+ *	Converts the icon into its string representation (its name).
+ *
+ * Results:
+ *	The name of the icon is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+IconToObj(clientData, interp, tkwin, widgRec, offset)
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;
+    Tk_Window tkwin;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    TreeViewIcon icon = *(TreeViewIcon *)(widgRec + offset);
+
+    if (icon == NULL) {
+	return bltEmptyStringObjPtr;
+    }
+    return Tcl_NewStringObj(Blt_NameOfImage((icon)->tkImage), -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeIcon(clientData, display, widgRec, offset)
+    ClientData clientData;
+    Display *display;		/* Not used. */
+    char *widgRec;
+    int offset;
+{
+    TreeViewIcon icon = *(TreeViewIcon *)(widgRec + offset);
+    TreeView *tvPtr = clientData;
+
+    Blt_TreeViewFreeIcon(tvPtr, icon);
+}
+
+static TreeViewStyleClass textBoxClass = {
+    "TextBoxStyle",
+    textBoxSpecs,
+    ConfigureTextBox,
+    MeasureTextBox,
+    DrawTextBox,
+    NULL,
+    EditTextBox,
+    FreeTextBox,
+};
+
+static TreeViewStyleClass checkBoxClass = {
+    "CheckBoxStyle",
+    checkBoxSpecs,
+    ConfigureCheckBox,
+    MeasureCheckBox,
+    DrawCheckBox,
+    NULL,
+    EditCheckBox,
+    FreeCheckBox,
+};
+
+static TreeViewStyleClass comboBoxClass = {
+    "ComboBoxStyle", 
+    comboBoxSpecs,
+    ConfigureComboBox,
+    MeasureComboBox,
+    DrawComboBox,
+    PickComboBox,
+    EditComboBox,
+    FreeComboBox,
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateTextBox --
+ *
+ *	Creates a "textbox" style.
+ *
+ * Results:
+ *	A pointer to the new style structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeViewStyle *
+CreateTextBox(tvPtr, hPtr)
+    TreeView *tvPtr;
+    Blt_HashEntry *hPtr;
+{
+    TreeViewTextBox *tbPtr;
+
+    tbPtr = Blt_Calloc(1, sizeof(TreeViewTextBox));
+    assert(tbPtr);
+    tbPtr->classPtr = &textBoxClass;
+    tbPtr->side = SIDE_LEFT;
+    tbPtr->gap = STYLE_GAP;
+    tbPtr->name = Blt_Strdup(Blt_GetHashKey(&tvPtr->styleTable, hPtr));
+    tbPtr->hashPtr = hPtr;
+    tbPtr->flags = STYLE_TEXTBOX;
+    tbPtr->refCount = 1;
+    Blt_SetHashValue(hPtr, tbPtr);
+    return (TreeViewStyle *)tbPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureTextBox --
+ *
+ *	Configures a "textbox" style.  This routine performs 
+ *	generates the GCs required for a textbox style.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs are created for the style.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureTextBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    GC newGC;
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+    XColor *bgColor;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    gcMask = GCForeground | GCBackground | GCFont;
+    gcValues.font = Tk_FontId(CHOOSE(tvPtr->font, tbPtr->font));
+    bgColor = Tk_3DBorderColor(CHOOSE(tvPtr->border, tbPtr->border));
+
+    gcValues.background = bgColor->pixel;
+    gcValues.foreground = CHOOSE(tvPtr->fgColor, tbPtr->fgColor)->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (tbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->gc);
+    }
+    tbPtr->gc = newGC;
+    gcValues.background = Tk_3DBorderColor(tbPtr->highlightBorder)->pixel;
+    gcValues.foreground = tbPtr->highlightFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (tbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->highlightGC);
+    }
+    tbPtr->highlightGC = newGC;
+
+    gcValues.background = Tk_3DBorderColor(tbPtr->activeBorder)->pixel;
+    gcValues.foreground = tbPtr->activeFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (tbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->activeGC);
+    }
+    tbPtr->activeGC = newGC;
+    tbPtr->flags |= STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MeasureTextBox --
+ *
+ *	Determines the space requirements for the "textbox" given
+ *	the value to be displayed.  Depending upon whether an icon
+ *	or text is displayed and their relative placements, this
+ *	routine computes the space needed for the text entry.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The width and height fields of *valuePtr* are set with the
+ *	computed dimensions.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MeasureTextBox(tvPtr, stylePtr, valuePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+    TreeViewValue *valuePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+    int iconWidth, iconHeight;
+    int textWidth, textHeight;
+    int gap;
+
+    textWidth = textHeight = 0;
+    iconWidth = iconHeight = 0;
+    valuePtr->width = valuePtr->height = 0;
+
+    if (tbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(tbPtr->icon);
+	iconHeight = TreeViewIconHeight(tbPtr->icon);
+    } 
+    if (valuePtr->textPtr != NULL) {
+	Blt_Free(valuePtr->textPtr);
+	valuePtr->textPtr = NULL;
+    }
+    if (valuePtr->string != NULL) {	/* New string defined. */
+	TextStyle ts;
+
+	Blt_InitTextStyle(&ts);
+	ts.font = CHOOSE(tvPtr->font, tbPtr->font);
+	ts.anchor = TK_ANCHOR_NW;
+	ts.justify = TK_JUSTIFY_LEFT;
+	valuePtr->textPtr = Blt_GetTextLayout(valuePtr->string, &ts);
+    } 
+    gap = 0;
+    if (valuePtr->textPtr != NULL) {
+	textWidth = valuePtr->textPtr->width;
+	textHeight = valuePtr->textPtr->height;
+	if (tbPtr->icon != NULL) {
+	    gap = tbPtr->gap;
+	}
+    }
+    if (SIDE_VERTICAL(tbPtr->side)) {
+	valuePtr->height = iconHeight + gap + textHeight;
+	valuePtr->width = MAX(textWidth, iconWidth);
+    } else {
+	valuePtr->width = iconWidth + gap + textWidth;
+	valuePtr->height = MAX(textHeight, iconHeight);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawTextBox --
+ *
+ *	Draws the "textbox" given the screen coordinates and the
+ *	value to be displayed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The textbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawTextBox(tvPtr, drawable, entryPtr, valuePtr, stylePtr, x, y)
+    TreeView *tvPtr;
+    Drawable drawable;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+    int x, y;
+{
+    GC gc;
+    TreeViewColumn *columnPtr;
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+    int iconX, iconY, iconWidth, iconHeight;
+    int textX, textY, textWidth, textHeight;
+    int gap, columnWidth;
+    Tk_3DBorder border;
+    XColor *fgColor;
+
+    columnPtr = valuePtr->columnPtr;
+
+    if (stylePtr->flags & STYLE_HIGHLIGHT) {
+	gc = tbPtr->highlightGC;
+	border = tbPtr->highlightBorder;
+	fgColor = tbPtr->highlightFgColor;
+    } else {
+	gc = tbPtr->gc;
+	border = CHOOSE(tvPtr->border, tbPtr->border);
+	fgColor = CHOOSE(tvPtr->fgColor, tbPtr->fgColor);
+    }
+    if (!Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	/*
+	 * Draw the active or normal background color over the entire
+	 * label area.  This includes both the tab's text and image.
+	 * The rectangle should be 2 pixels wider/taller than this
+	 * area. So if the label consists of just an image, we get an
+	 * halo around the image when the tab is active.
+	 */
+	if (border != NULL) {
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y, 
+	       columnPtr->width, entryPtr->height, 0, TK_RELIEF_FLAT);
+	}
+    }    
+    columnWidth = columnPtr->width - 
+	(2 * columnPtr->borderWidth + PADDING(columnPtr->pad));
+    if (columnWidth > valuePtr->width) {
+	switch(columnPtr->justify) {
+	case TK_JUSTIFY_RIGHT:
+	    x += (columnWidth - valuePtr->width);
+	    break;
+	case TK_JUSTIFY_CENTER:
+	    x += (columnWidth - valuePtr->width) / 2;
+	    break;
+	case TK_JUSTIFY_LEFT:
+	    break;
+	}
+    }
+
+    textX = textY = iconX = iconY = 0;	/* Suppress compiler warning. */
+    
+    iconWidth = iconHeight = 0;
+    if (tbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(tbPtr->icon);
+	iconHeight = TreeViewIconHeight(tbPtr->icon);
+    }
+    textWidth = textHeight = 0;
+    if (valuePtr->textPtr != NULL) {
+	textWidth = valuePtr->textPtr->width;
+	textHeight = valuePtr->textPtr->height;
+    }
+    gap = 0;
+    if ((tbPtr->icon != NULL) && (valuePtr->textPtr != NULL)) {
+	gap = tbPtr->gap;
+    }
+    switch (tbPtr->side) {
+    case SIDE_RIGHT:
+	textX = x;
+	textY = y + (entryPtr->height - textHeight) / 2;
+	iconX = textX + textWidth + gap;
+	iconY = y + (entryPtr->height - iconHeight) / 2;
+	break;
+    case SIDE_LEFT:
+	iconX = x;
+	iconY = y + (entryPtr->height - iconHeight) / 2;
+	textX = iconX + iconWidth + gap;
+	textY = y + (entryPtr->height - textHeight) / 2;
+	break;
+    case SIDE_TOP:
+	iconY = y;
+	iconX = x + (columnWidth - iconWidth) / 2;
+	textY = iconY + iconHeight + gap;
+	textX = x + (columnWidth - textWidth) / 2;
+	break;
+    case SIDE_BOTTOM:
+	textY = y;
+	textX = x + (columnWidth - textWidth) / 2;
+	iconY = textY + textHeight + gap;
+	iconX = x + (columnWidth - iconWidth) / 2;
+	break;
+    }
+    if (tbPtr->icon != NULL) {
+	Tk_RedrawImage(TreeViewIconBits(tbPtr->icon), 0, 0, iconWidth, 
+		       iconHeight, drawable, iconX, iconY);
+    }
+    if (valuePtr->textPtr != NULL) {
+	TextStyle ts;
+	XColor *color;
+	Tk_Font font;
+	
+	font = CHOOSE(tvPtr->font, tbPtr->font);
+	if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	    color = SELECT_FG(tvPtr);
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else if (entryPtr->color != NULL) {
+	    color = entryPtr->color;
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else {
+	    color = fgColor;
+	}
+	Blt_SetDrawTextStyle(&ts, font, gc, color, fgColor, NULL, 0.0, 
+		TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, 0);
+	Blt_DrawTextLayout(tvPtr->tkwin, drawable, valuePtr->textPtr, 
+		&ts, textX, textY);
+	if (color != fgColor) {
+	    XSetForeground(tvPtr->display, gc, fgColor->pixel);
+	}
+    }
+    stylePtr->flags &= ~STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EditCombobox --
+ *
+ *	Edits the "combobox".
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EditTextBox(tvPtr, entryPtr, valuePtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;	/* Not used. */
+{
+    return Blt_TreeViewTextbox(tvPtr, entryPtr, valuePtr->columnPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeTextBox --
+ *
+ *	Releases resources allocated for the textbox. The resources
+ *	freed by this routine are specific only to the "textbox".   
+ *	Other resources (common to all styles) are freed in the 
+ *	Blt_TreeViewFreeStyle routine.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs allocated for the textbox are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeTextBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+
+    if (tbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->highlightGC);
+    }
+    if (tbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->activeGC);
+    }
+    if (tbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, tbPtr->gc);
+    }
+    if (tbPtr->icon != NULL) {
+	Blt_TreeViewFreeIcon(tvPtr, tbPtr->icon);
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateCheckbox --
+ *
+ *	Creates a "checkbox" style.
+ *
+ * Results:
+ *	A pointer to the new style structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeViewStyle *
+CreateCheckBox(tvPtr, hPtr)
+    TreeView *tvPtr;
+    Blt_HashEntry *hPtr;
+{
+    TreeViewCheckBox *cbPtr;
+
+    cbPtr = Blt_Calloc(1, sizeof(TreeViewCheckBox));
+    assert(cbPtr);
+    cbPtr->classPtr = &checkBoxClass;
+    cbPtr->gap = 4;
+    cbPtr->size = 11;
+    cbPtr->lineWidth = 2;
+    cbPtr->showValue = TRUE;
+    cbPtr->name = Blt_Strdup(Blt_GetHashKey(&tvPtr->styleTable, hPtr));
+    cbPtr->hashPtr = hPtr;
+    cbPtr->flags = STYLE_CHECKBOX;
+    cbPtr->refCount = 1;
+    Blt_SetHashValue(hPtr, cbPtr);
+    return (TreeViewStyle *)cbPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureCheckbox --
+ *
+ *	Configures a "checkbox" style.  This routine performs 
+ *	generates the GCs required for a checkbox style.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs are created for the style.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureCheckBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    GC newGC;
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+    XColor *bgColor;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    gcMask = GCForeground | GCBackground | GCFont;
+    gcValues.font = Tk_FontId(CHOOSE(tvPtr->font, cbPtr->font));
+    bgColor = Tk_3DBorderColor(CHOOSE(tvPtr->border, cbPtr->border));
+
+    gcValues.background = bgColor->pixel;
+    gcValues.foreground = CHOOSE(tvPtr->fgColor, cbPtr->fgColor)->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->gc);
+    }
+    cbPtr->gc = newGC;
+    gcValues.background = Tk_3DBorderColor(cbPtr->highlightBorder)->pixel;
+    gcValues.foreground = cbPtr->highlightFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->highlightGC);
+    }
+    cbPtr->highlightGC = newGC;
+
+    gcValues.background = Tk_3DBorderColor(cbPtr->activeBorder)->pixel;
+    gcValues.foreground = cbPtr->activeFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->activeGC);
+    }
+    cbPtr->activeGC = newGC;
+
+    gcMask = GCForeground;
+    gcValues.foreground = cbPtr->fillColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->fillGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->fillGC);
+    }
+    cbPtr->fillGC = newGC;
+
+    gcMask = GCForeground | GCLineWidth;
+    gcValues.line_width = cbPtr->lineWidth;
+    gcValues.foreground = cbPtr->boxColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->boxGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->boxGC);
+    }
+    cbPtr->boxGC = newGC;
+
+    gcMask = GCForeground | GCLineWidth;
+    gcValues.line_width = 1;
+    gcValues.foreground = cbPtr->checkColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->checkGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->checkGC);
+    }
+    cbPtr->checkGC = newGC;
+    cbPtr->flags |= STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MeasureCheckbox --
+ *
+ *	Determines the space requirements for the "checkbox" given
+ *	the value to be displayed.  Depending upon whether an icon
+ *	or text is displayed and their relative placements, this
+ *	routine computes the space needed for the text entry.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The width and height fields of *valuePtr* are set with the
+ *	computed dimensions.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MeasureCheckBox(tvPtr, stylePtr, valuePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+    TreeViewValue *valuePtr;
+{
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+    int iconWidth, iconHeight;
+    int textWidth, textHeight;
+    int gap;
+    int boxWidth, boxHeight;
+
+    boxWidth = boxHeight = ODD(cbPtr->size);
+
+    textWidth = textHeight = iconWidth = iconHeight = 0;
+    valuePtr->width = valuePtr->height = 0;
+    if (cbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(cbPtr->icon);
+	iconHeight = TreeViewIconHeight(cbPtr->icon);
+    } 
+    if (cbPtr->onPtr != NULL) {
+	Blt_Free(cbPtr->onPtr);
+	cbPtr->onPtr = NULL;
+    }
+    if (cbPtr->offPtr != NULL) {
+	Blt_Free(cbPtr->offPtr);
+	cbPtr->offPtr = NULL;
+    }
+    gap = 0;
+    if (cbPtr->showValue) {
+	TextStyle ts;
+	char *string;
+
+	Blt_InitTextStyle(&ts);
+	ts.font = CHOOSE(tvPtr->font, cbPtr->font);
+	ts.anchor = TK_ANCHOR_NW;
+	ts.justify = TK_JUSTIFY_LEFT;
+	string = (cbPtr->onValue != NULL) ? cbPtr->onValue : valuePtr->string;
+	cbPtr->onPtr = Blt_GetTextLayout(string, &ts);
+	string = (cbPtr->offValue != NULL) ? cbPtr->offValue : valuePtr->string;
+	cbPtr->offPtr = Blt_GetTextLayout(string, &ts);
+	textWidth = MAX(cbPtr->offPtr->width, cbPtr->onPtr->width);
+	textHeight = MAX(cbPtr->offPtr->height, cbPtr->onPtr->height);
+	if (cbPtr->icon != NULL) {
+	    gap = cbPtr->gap;
+	}
+    }
+    valuePtr->width = cbPtr->gap * 2 + boxWidth + iconWidth + gap + textWidth;
+    valuePtr->height = MAX3(boxHeight, textHeight, iconHeight);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawCheckbox --
+ *
+ *	Draws the "checkbox" given the screen coordinates and the
+ *	value to be displayed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawCheckBox(tvPtr, drawable, entryPtr, valuePtr, stylePtr, x, y)
+    TreeView *tvPtr;
+    Drawable drawable;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+    int x, y;
+{
+    GC gc;
+    TreeViewColumn *columnPtr;
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+    int iconX, iconY, iconWidth, iconHeight;
+    int textX, textY, textHeight;
+    int gap, columnWidth;
+    Tk_3DBorder border;
+    XColor *fgColor;
+    Tk_Font font;
+    int bool;
+    int borderWidth, relief;
+    TextLayout *textPtr;
+    int boxX, boxY, boxWidth, boxHeight;
+
+    font = CHOOSE(tvPtr->font, cbPtr->font);
+    columnPtr = valuePtr->columnPtr;
+    borderWidth = 0;
+    relief = TK_RELIEF_FLAT;
+    if (valuePtr == tvPtr->activeValuePtr) {
+	gc = cbPtr->activeGC;
+	border = cbPtr->activeBorder;
+	fgColor = cbPtr->activeFgColor;
+	borderWidth = 1;
+	relief = TK_RELIEF_RAISED;
+    } else if (stylePtr->flags & STYLE_HIGHLIGHT) {
+	gc = cbPtr->highlightGC;
+	border = cbPtr->highlightBorder;
+	fgColor = cbPtr->highlightFgColor;
+    } else {
+	gc = cbPtr->gc;
+	border = CHOOSE(tvPtr->border, cbPtr->border);
+	fgColor = CHOOSE(tvPtr->fgColor, cbPtr->fgColor);
+    }
+    columnWidth = columnPtr->width - PADDING(columnPtr->pad);
+    if (valuePtr == tvPtr->activeValuePtr) {
+	/*
+	 * Draw the active or normal background color over the entire
+	 * label area.  This includes both the tab's text and image.
+	 * The rectangle should be 2 pixels wider/taller than this
+	 * area. So if the label consists of just an image, we get an
+	 * halo around the image when the tab is active.
+	 */
+	if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, SELECT_BORDER(tvPtr),
+		x, y, columnWidth, entryPtr->height - 1, borderWidth, relief);
+	} else {
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y, 
+		columnWidth, entryPtr->height - 1, borderWidth, relief);
+	}
+    }    
+
+    if (columnWidth > valuePtr->width) {
+	switch(columnPtr->justify) {
+	case TK_JUSTIFY_RIGHT:
+	    x += (columnWidth - valuePtr->width);
+	    break;
+	case TK_JUSTIFY_CENTER:
+	    x += (columnWidth - valuePtr->width) / 2;
+	    break;
+	case TK_JUSTIFY_LEFT:
+	    break;
+	}
+    }
+
+    bool = (strcmp(valuePtr->string, cbPtr->onValue) == 0);
+    textPtr = (bool) ? cbPtr->onPtr : cbPtr->offPtr;
+
+    /*
+     * Draw the box and check. 
+     *
+     *		+-----------+
+     *		|           |
+     *		|         * |
+     *          |        *  |
+     *          | *     *   |
+     *          |  *   *    |
+     *          |   * *     |
+     *		|    *      |
+     *		+-----------+
+     */
+    boxWidth = boxHeight = ODD(cbPtr->size);
+    boxX = x + cbPtr->gap;
+    boxY = y + (entryPtr->height - boxHeight) / 2;
+    XFillRectangle(tvPtr->display, drawable, cbPtr->fillGC, boxX, boxY, 
+		       boxWidth, boxHeight);
+    XDrawRectangle(tvPtr->display, drawable, cbPtr->boxGC, boxX, boxY, 
+	boxWidth, boxHeight);
+
+    if (bool) {
+	int midX, midY;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+	    midX = boxX + 2 * boxWidth / 5;
+	    midY = boxY + boxHeight - 5 + i;
+	    XDrawLine(tvPtr->display, drawable, cbPtr->checkGC, 
+		      boxX + 2, boxY + boxHeight / 3 + 1 + i, midX, midY);
+	    XDrawLine(tvPtr->display, drawable, cbPtr->checkGC, 
+		      midX, midY, boxX + boxWidth - 2, boxY + i + 1);
+	}
+    }
+#ifdef notdef
+    textX = textY = iconX = iconY = 0;	/* Suppress compiler warning. */
+#endif
+    iconWidth = iconHeight = 0;
+    if (cbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(cbPtr->icon);
+	iconHeight = TreeViewIconHeight(cbPtr->icon);
+    }
+    textHeight = 0;
+    gap = 0;
+    if (cbPtr->showValue) {
+	textHeight = textPtr->height;
+	if (cbPtr->icon != NULL) {
+	    gap = cbPtr->gap;
+	}
+    }
+    x = boxX + boxWidth + cbPtr->gap;
+
+    /* The icon sits to the left of the text. */
+    iconX = x;
+    iconY = y + (entryPtr->height - iconHeight) / 2;
+    textX = iconX + iconWidth + gap;
+    textY = y + (entryPtr->height - textHeight) / 2;
+
+    if (cbPtr->icon != NULL) {
+	Tk_RedrawImage(TreeViewIconBits(cbPtr->icon), 0, 0, iconWidth, 
+		       iconHeight, drawable, iconX, iconY);
+    }
+    if ((cbPtr->showValue) && (textPtr != NULL)) {
+	TextStyle ts;
+	XColor *color;
+	
+	if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	    color = SELECT_FG(tvPtr);
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else if (entryPtr->color != NULL) {
+	    color = entryPtr->color;
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else {
+	    color = fgColor;
+	}
+	Blt_SetDrawTextStyle(&ts, font, gc, color, fgColor, NULL, 0.0, 
+		TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, 0);
+	Blt_DrawTextLayout(tvPtr->tkwin, drawable, textPtr, &ts, textX, textY);
+	if (color != fgColor) {
+	    XSetForeground(tvPtr->display, gc, fgColor->pixel);
+	}
+    }
+    stylePtr->flags &= ~STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PickCheckbox --
+ *
+ *	Draws the "checkbox" given the screen coordinates and the
+ *	value to be displayed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+PickCheckBox(entryPtr, valuePtr, stylePtr, worldX, worldY)
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+    int worldX, worldY;
+{
+    TreeViewColumn *columnPtr;
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+    int columnWidth;
+    int x, y, width, height;
+
+    columnPtr = valuePtr->columnPtr;
+    columnWidth = columnPtr->width - 
+	(2 * columnPtr->borderWidth + PADDING(columnPtr->pad));
+    if (columnWidth > valuePtr->width) {
+	switch(columnPtr->justify) {
+	case TK_JUSTIFY_RIGHT:
+	    worldX += (columnWidth - valuePtr->width);
+	    break;
+	case TK_JUSTIFY_CENTER:
+	    worldX += (columnWidth - valuePtr->width) / 2;
+	    break;
+	case TK_JUSTIFY_LEFT:
+	    break;
+	}
+    }
+    width = height = ODD(cbPtr->size) + 2 * cbPtr->lineWidth;
+    x = columnPtr->worldX + columnPtr->pad.side1 + cbPtr->gap - 
+	cbPtr->lineWidth;
+    y = entryPtr->worldY + (entryPtr->height - height) / 2;
+    if ((worldX >= x) && (worldX < (x + width)) && 
+	(worldY >= y) && (worldY < (y + height))) {
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EditCheckbox --
+ *
+ *	Edits the "checkbox".
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+EditCheckBox(tvPtr, entryPtr, valuePtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewColumn *columnPtr;
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+    Tcl_Obj *objPtr;
+
+    columnPtr = valuePtr->columnPtr;
+    if (Blt_TreeGetValueByKey(tvPtr->interp, tvPtr->tree, 
+	      entryPtr->node, columnPtr->key, &objPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (strcmp(Tcl_GetString(objPtr), cbPtr->onValue) == 0) {
+	objPtr = Tcl_NewStringObj(cbPtr->offValue, -1);
+    } else {
+	objPtr = Tcl_NewStringObj(cbPtr->onValue, -1);
+    }
+    entryPtr->flags |= ENTRY_DIRTY;
+    tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL | TV_RESORT);
+    if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, 
+	      entryPtr->node, columnPtr->key, objPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeCheckbox --
+ *
+ *	Releases resources allocated for the checkbox. The resources
+ *	freed by this routine are specific only to the "checkbox".   
+ *	Other resources (common to all styles) are freed in the 
+ *	Blt_TreeViewFreeStyle routine.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs allocated for the checkbox are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeCheckBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewCheckBox *cbPtr = (TreeViewCheckBox *)stylePtr;
+
+    if (cbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->highlightGC);
+    }
+    if (cbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->activeGC);
+    }
+    if (cbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->gc);
+    }
+    if (cbPtr->fillGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->fillGC);
+    }
+    if (cbPtr->boxGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->boxGC);
+    }
+    if (cbPtr->checkGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->checkGC);
+    }
+    if (cbPtr->icon != NULL) {
+	Blt_TreeViewFreeIcon(tvPtr, cbPtr->icon);
+    }
+    if (cbPtr->offPtr != NULL) {
+	Blt_Free(cbPtr->offPtr);
+    }
+    if (cbPtr->onPtr != NULL) {
+	Blt_Free(cbPtr->onPtr);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateComboBox --
+ *
+ *	Creates a "combobox" style.
+ *
+ * Results:
+ *	A pointer to the new style structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static TreeViewStyle *
+CreateComboBox(tvPtr, hPtr)
+    TreeView *tvPtr;
+    Blt_HashEntry *hPtr;
+{
+    TreeViewComboBox *cbPtr;
+
+    cbPtr = Blt_Calloc(1, sizeof(TreeViewComboBox));
+    assert(cbPtr);
+    cbPtr->classPtr = &comboBoxClass;
+    cbPtr->gap = STYLE_GAP;
+    cbPtr->buttonRelief = TK_RELIEF_RAISED;
+    cbPtr->buttonBorderWidth = 1;
+    cbPtr->borderWidth = 1;
+    cbPtr->relief = TK_RELIEF_FLAT;
+    cbPtr->name = Blt_Strdup(Blt_GetHashKey(&tvPtr->styleTable, hPtr));
+    cbPtr->hashPtr = hPtr;
+    cbPtr->flags = STYLE_COMBOBOX;
+    cbPtr->refCount = 1;
+    Blt_SetHashValue(hPtr, cbPtr);
+    return (TreeViewStyle *)cbPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureComboBox --
+ *
+ *	Configures a "combobox" style.  This routine performs 
+ *	generates the GCs required for a combobox style.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs are created for the style.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ConfigureComboBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    GC newGC;
+    TreeViewComboBox *cbPtr = (TreeViewComboBox *)stylePtr;
+    XColor *bgColor;
+    XGCValues gcValues;
+    unsigned long gcMask;
+
+    gcValues.font = Tk_FontId(CHOOSE(tvPtr->font, cbPtr->font));
+    bgColor = Tk_3DBorderColor(CHOOSE(tvPtr->border, cbPtr->border));
+    gcMask = GCForeground | GCBackground | GCFont;
+
+    /* Normal foreground */
+    gcValues.background = bgColor->pixel;
+    gcValues.foreground = CHOOSE(tvPtr->fgColor, cbPtr->fgColor)->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->gc);
+    }
+    cbPtr->gc = newGC;
+
+    /* Highlight foreground */
+    gcValues.background = Tk_3DBorderColor(cbPtr->highlightBorder)->pixel;
+    gcValues.foreground = cbPtr->highlightFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->highlightGC);
+    }
+    cbPtr->highlightGC = newGC;
+
+    /* Active foreground */
+    gcValues.background = Tk_3DBorderColor(cbPtr->activeBorder)->pixel;
+    gcValues.foreground = cbPtr->activeFgColor->pixel;
+    newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
+    if (cbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->activeGC);
+    }
+    cbPtr->activeGC = newGC;
+    cbPtr->flags |= STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MeasureComboBox --
+ *
+ *	Determines the space requirements for the "combobox" given
+ *	the value to be displayed.  Depending upon whether an icon
+ *	or text is displayed and their relative placements, this
+ *	routine computes the space needed for the text entry.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The width and height fields of *valuePtr* are set with the
+ *	computed dimensions.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MeasureComboBox(tvPtr, stylePtr, valuePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+    TreeViewValue *valuePtr;
+{
+    TreeViewComboBox *cbPtr = (TreeViewComboBox *)stylePtr;
+    int iconWidth, iconHeight;
+    int textWidth, textHeight;
+    int gap;
+    Tk_Font font;
+
+    textWidth = textHeight = 0;
+    iconWidth = iconHeight = 0;
+    valuePtr->width = valuePtr->height = 0;
+
+    if (cbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(cbPtr->icon);
+	iconHeight = TreeViewIconHeight(cbPtr->icon);
+    } 
+    if (valuePtr->textPtr != NULL) {
+	Blt_Free(valuePtr->textPtr);
+	valuePtr->textPtr = NULL;
+    }
+    font = CHOOSE(tvPtr->font, cbPtr->font);
+    if (valuePtr->string != NULL) {	/* New string defined. */
+	TextStyle ts;
+
+	Blt_InitTextStyle(&ts);
+	ts.font = font;
+	ts.anchor = TK_ANCHOR_NW;
+	ts.justify = TK_JUSTIFY_LEFT;
+	valuePtr->textPtr = Blt_GetTextLayout(valuePtr->string, &ts);
+    } 
+    gap = 0;
+    if (valuePtr->textPtr != NULL) {
+	textWidth = valuePtr->textPtr->width;
+	textHeight = valuePtr->textPtr->height;
+	if (cbPtr->icon != NULL) {
+	    gap = cbPtr->gap;
+	}
+    }
+    cbPtr->buttonWidth = STD_ARROW_WIDTH + 6 + 2 * cbPtr->buttonBorderWidth;
+    valuePtr->width = 2 * cbPtr->borderWidth + iconWidth + 4 * gap + 
+	cbPtr->buttonWidth + textWidth;
+    valuePtr->height = MAX(textHeight, iconHeight) + 2 * cbPtr->borderWidth;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawComboBox --
+ *
+ *	Draws the "combobox" given the screen coordinates and the
+ *	value to be displayed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The combobox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawComboBox(tvPtr, drawable, entryPtr, valuePtr, stylePtr, x, y)
+    TreeView *tvPtr;
+    Drawable drawable;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+    int x, y;
+{
+    GC gc;
+    TreeViewColumn *columnPtr;
+    TreeViewComboBox *cbPtr = (TreeViewComboBox *)stylePtr;
+    int iconX, iconY, iconWidth, iconHeight;
+    int textX, textY, textHeight;
+    int buttonX, buttonY;
+    int gap, columnWidth;
+    Tk_3DBorder border;
+    XColor *fgColor;
+
+    columnPtr = valuePtr->columnPtr;
+    if (stylePtr->flags & STYLE_HIGHLIGHT) {
+	gc = cbPtr->highlightGC;
+	border = cbPtr->highlightBorder;
+	fgColor = cbPtr->highlightFgColor;
+    } else {
+	gc = cbPtr->gc;
+	border = CHOOSE(tvPtr->border, cbPtr->border);
+	fgColor = CHOOSE(tvPtr->fgColor, cbPtr->fgColor);
+    }
+    if (!Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	/*
+	 * Draw the active or normal background color over the entire
+	 * label area.  This includes both the tab's text and image.
+	 * The rectangle should be 2 pixels wider/taller than this
+	 * area. So if the label consists of just an image, we get an
+	 * halo around the image when the tab is active.
+	 */
+	if (border != NULL) {
+	    Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y, 
+		columnPtr->width, entryPtr->height, cbPtr->borderWidth, 
+		cbPtr->relief);
+	}
+    }    
+    buttonX = x + columnPtr->width;
+    buttonX -= columnPtr->pad.side2 + cbPtr->borderWidth  +
+	cbPtr->buttonWidth + cbPtr->gap;
+    buttonY = y;
+
+    columnWidth = columnPtr->width - 
+	(2 * columnPtr->borderWidth + PADDING(columnPtr->pad));
+    if (columnWidth > valuePtr->width) {
+	switch(columnPtr->justify) {
+	case TK_JUSTIFY_RIGHT:
+	    x += (columnWidth - valuePtr->width);
+	    break;
+	case TK_JUSTIFY_CENTER:
+	    x += (columnWidth - valuePtr->width) / 2;
+	    break;
+	case TK_JUSTIFY_LEFT:
+	    break;
+	}
+    }
+
+#ifdef notdef
+    textX = textY = iconX = iconY = 0;	/* Suppress compiler warning. */
+#endif
+    
+    iconWidth = iconHeight = 0;
+    if (cbPtr->icon != NULL) {
+	iconWidth = TreeViewIconWidth(cbPtr->icon);
+	iconHeight = TreeViewIconHeight(cbPtr->icon);
+    }
+    textHeight = 0;
+    if (valuePtr->textPtr != NULL) {
+	textHeight = valuePtr->textPtr->height;
+    }
+    gap = 0;
+    if ((cbPtr->icon != NULL) && (valuePtr->textPtr != NULL)) {
+	gap = cbPtr->gap;
+    }
+
+    iconX = x + gap;
+    iconY = y + (entryPtr->height - iconHeight) / 2;
+    textX = iconX + iconWidth + gap;
+    textY = y + (entryPtr->height - textHeight) / 2;
+
+    if (cbPtr->icon != NULL) {
+	Tk_RedrawImage(TreeViewIconBits(cbPtr->icon), 0, 0, iconWidth, 
+	       iconHeight, drawable, iconX, iconY);
+    }
+    if (valuePtr->textPtr != NULL) {
+	TextStyle ts;
+	XColor *color;
+	Tk_Font font;
+	
+	font = CHOOSE(tvPtr->font, cbPtr->font);
+	if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
+	    color = SELECT_FG(tvPtr);
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else if (entryPtr->color != NULL) {
+	    color = entryPtr->color;
+	    XSetForeground(tvPtr->display, gc, color->pixel);
+	} else {
+	    color = fgColor;
+	}
+	Blt_SetDrawTextStyle(&ts, font, gc, color, fgColor, NULL, 0.0, 
+		TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, 0);
+	Blt_DrawTextLayout(tvPtr->tkwin, drawable, valuePtr->textPtr, 
+		&ts, textX, textY);
+	if (color != fgColor) {
+	    XSetForeground(tvPtr->display, gc, fgColor->pixel);
+	}
+    }
+    if (valuePtr == tvPtr->activeValuePtr) {
+	Blt_Fill3DRectangle(tvPtr->tkwin, drawable, stylePtr->activeBorder, 
+	   buttonX, buttonY + cbPtr->borderWidth, cbPtr->buttonWidth, 
+	   entryPtr->height - 2 * cbPtr->borderWidth, 
+	cbPtr->buttonBorderWidth, cbPtr->buttonRelief); 
+    } else {
+	Blt_Fill3DRectangle(tvPtr->tkwin, drawable, columnPtr->titleBorder, 
+		buttonX, buttonY + cbPtr->borderWidth, cbPtr->buttonWidth, 
+		entryPtr->height - 2 * cbPtr->borderWidth, 
+		cbPtr->buttonBorderWidth, cbPtr->buttonRelief); 
+    }
+    buttonX += cbPtr->buttonWidth / 2;
+    buttonY += entryPtr->height / 2;
+    Blt_DrawArrow(tvPtr->display, drawable, gc, buttonX, buttonY, 
+		  STD_ARROW_HEIGHT, ARROW_DOWN);
+    stylePtr->flags &= ~STYLE_DIRTY;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PickCombobox --
+ *
+ *	Draws the "checkbox" given the screen coordinates and the
+ *	value to be displayed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+PickComboBox(entryPtr, valuePtr, stylePtr, worldX, worldY)
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;
+    int worldX, worldY;
+{
+    TreeViewColumn *columnPtr;
+    TreeViewComboBox *cbPtr = (TreeViewComboBox *)stylePtr;
+    int x, y, width, height;
+
+    columnPtr = valuePtr->columnPtr;
+    width = cbPtr->buttonWidth;
+    height = entryPtr->height - 4;
+    x = columnPtr->worldX + columnPtr->width - columnPtr->pad.side2 - 
+	cbPtr->borderWidth - columnPtr->borderWidth - width;
+    y = entryPtr->worldY + cbPtr->borderWidth;
+    if ((worldX >= x) && (worldX < (x + width)) && 
+	(worldY >= y) && (worldY < (y + height))) {
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EditCombobox --
+ *
+ *	Edits the "combobox".
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The checkbox value is drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+EditComboBox(tvPtr, entryPtr, valuePtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+    TreeViewStyle *stylePtr;	/* Not used. */
+{
+    return Blt_TreeViewTextbox(tvPtr, entryPtr, valuePtr->columnPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeComboBox --
+ *
+ *	Releases resources allocated for the combobox. The resources
+ *	freed by this routine are specific only to the "combobox".   
+ *	Other resources (common to all styles) are freed in the 
+ *	Blt_TreeViewFreeStyle routine.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	GCs allocated for the combobox are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeComboBox(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewComboBox *cbPtr = (TreeViewComboBox *)stylePtr;
+
+    if (cbPtr->highlightGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->highlightGC);
+    }
+    if (cbPtr->activeGC != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->activeGC);
+    }
+    if (cbPtr->gc != NULL) {
+	Tk_FreeGC(tvPtr->display, cbPtr->gc);
+    }
+    if (cbPtr->icon != NULL) {
+	Blt_TreeViewFreeIcon(tvPtr, cbPtr->icon);
+    }
+}
+
+static TreeViewStyle *
+GetStyle(interp, tvPtr, styleName)
+    Tcl_Interp *interp;
+    TreeView *tvPtr;
+    char *styleName;
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&tvPtr->styleTable, styleName);
+    if (hPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find cell style \"", styleName, 
+		"\"", (char *)NULL);
+	}
+	return NULL;
+    }
+    return Blt_GetHashValue(hPtr);
+}
+
+int
+Blt_TreeViewGetStyle(interp, tvPtr, styleName, stylePtrPtr)
+    Tcl_Interp *interp;
+    TreeView *tvPtr;
+    char *styleName;
+    TreeViewStyle **stylePtrPtr;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = GetStyle(interp, tvPtr, styleName);
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    stylePtr->refCount++;
+    *stylePtrPtr = stylePtr;
+    return TCL_OK;
+}
+
+static TreeViewStyle *
+CreateStyle(interp, tvPtr, type, styleName, objc, objv)
+     Tcl_Interp *interp;
+     TreeView *tvPtr;		/* TreeView widget. */
+     int type;			/* Type of style: either
+				 * STYLE_TEXTBOX,
+				 * STYLE_COMBOBOX, or
+				 * STYLE_CHECKBOX */
+    char *styleName;		/* Name of the new style. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{    
+    Blt_HashEntry *hPtr;
+    int isNew;
+    TreeViewStyle *stylePtr;
+    
+    hPtr = Blt_CreateHashEntry(&tvPtr->styleTable, styleName, &isNew);
+    if (!isNew) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "cell style \"", styleName, 
+			     "\" already exists", (char *)NULL);
+	}
+	return NULL;
+    }
+    /* Create the new marker based upon the given type */
+    switch (type) {
+    case STYLE_TEXTBOX:
+	stylePtr = CreateTextBox(tvPtr, hPtr);
+	break;
+    case STYLE_COMBOBOX:
+	stylePtr = CreateComboBox(tvPtr, hPtr);
+	break;
+    case STYLE_CHECKBOX:
+	stylePtr = CreateCheckBox(tvPtr, hPtr);
+	break;
+    default:
+	return NULL;
+    }
+    bltTreeViewIconOption.clientData = tvPtr;
+    if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, styleName, 
+	stylePtr->classPtr->className, stylePtr->classPtr->specsPtr, 
+	objc, objv, (char *)stylePtr, 0) != TCL_OK) {
+	Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+	return NULL;
+    }
+    return stylePtr;
+}
+
+void
+Blt_TreeViewUpdateStyleGCs(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    (*stylePtr->classPtr->configProc)(tvPtr, stylePtr);
+    stylePtr->flags |= STYLE_DIRTY;
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+}
+
+TreeViewStyle *
+Blt_TreeViewCreateStyle(interp, tvPtr, type, styleName)
+     Tcl_Interp *interp;
+     TreeView *tvPtr;		/* TreeView widget. */
+     int type;			/* Type of style: either
+				 * STYLE_TEXTBOX,
+				 * STYLE_COMBOBOX, or
+				 * STYLE_CHECKBOX */
+    char *styleName;		/* Name of the new style. */
+{    
+    return CreateStyle(interp, tvPtr, type, styleName, 0, (Tcl_Obj **)NULL);
+}
+
+void
+Blt_TreeViewFreeStyle(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    stylePtr->refCount--;
+#ifdef notdef
+    fprint(f(stderr, "Blt_TreeViewFreeStyle %s count=%d\n", stylePtr->name,
+	    stylePtr->refCount);
+#endif
+    /* Remove the style from the hash table so that it's name can be used.*/
+    /* If no cell is using the style, remove it.*/
+    if ((stylePtr->refCount <= 0) && !(stylePtr->flags & STYLE_USER)){
+#ifdef notdef
+	fprintf(stderr, "freeing %s\n", stylePtr->name);
+#endif
+	bltTreeViewIconOption.clientData = tvPtr;
+	Blt_FreeObjOptions(stylePtr->classPtr->specsPtr, (char *)stylePtr, 
+		   tvPtr->display, 0);
+	(*stylePtr->classPtr->freeProc)(tvPtr, stylePtr); 
+	if (stylePtr->hashPtr != NULL) {
+	    Blt_DeleteHashEntry(&tvPtr->styleTable, stylePtr->hashPtr);
+	} 
+	if (stylePtr->name != NULL) {
+	    Blt_Free(stylePtr->name);
+	}
+	Blt_Free(stylePtr);
+    } 
+}
+
+void
+Blt_TreeViewSetStyleIcon(tvPtr, stylePtr, icon)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+    TreeViewIcon icon;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+
+    if (tbPtr->icon != NULL) {
+	Blt_TreeViewFreeIcon(tvPtr, tbPtr->icon);
+    }
+    tbPtr->icon = icon;
+}
+
+GC
+Blt_TreeViewGetStyleGC(stylePtr)
+    TreeViewStyle *stylePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+    return tbPtr->gc;
+}
+
+Tk_3DBorder
+Blt_TreeViewGetStyleBorder(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+    Tk_3DBorder border;
+
+    border = (tbPtr->flags & STYLE_HIGHLIGHT) 
+	? tbPtr->highlightBorder : tbPtr->border;
+    return (border != NULL) ? border : tvPtr->border;
+}
+
+Tk_Font
+Blt_TreeViewGetStyleFont(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+
+    if (tbPtr->font != NULL) {
+	return tbPtr->font;
+    }
+    return tvPtr->font;
+}
+
+XColor *
+Blt_TreeViewGetStyleFg(tvPtr, stylePtr)
+    TreeView *tvPtr;
+    TreeViewStyle *stylePtr;
+{
+    TreeViewTextBox *tbPtr = (TreeViewTextBox *)stylePtr;
+
+    if (tbPtr->fgColor != NULL) {
+	return tbPtr->fgColor;
+    }
+    return tvPtr->fgColor;
+}
+
+static void
+DrawValue(tvPtr, entryPtr, valuePtr)
+    TreeView *tvPtr;
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr;
+{
+    Drawable drawable;
+    int sx, sy, dx, dy;
+    int width, height;
+    int left, right, top, bottom;
+    TreeViewColumn *columnPtr;
+    TreeViewStyle *stylePtr;
+
+    stylePtr = valuePtr->stylePtr;
+    if (stylePtr == NULL) {
+	stylePtr = valuePtr->columnPtr->stylePtr;
+    }
+    if (stylePtr->cursor != None) {
+	if (valuePtr == tvPtr->activeValuePtr) {
+	    Tk_DefineCursor(tvPtr->tkwin, stylePtr->cursor);
+	} else {
+	    if (tvPtr->cursor != None) {
+		Tk_DefineCursor(tvPtr->tkwin, tvPtr->cursor);
+	    } else {
+		Tk_UndefineCursor(tvPtr->tkwin);
+	    }
+	}
+    }
+    columnPtr = valuePtr->columnPtr;
+    dx = SCREENX(tvPtr, columnPtr->worldX) + columnPtr->pad.side1;
+    dy = SCREENY(tvPtr, entryPtr->worldY);
+    height = entryPtr->height - 1;
+    width = valuePtr->columnPtr->width - PADDING(columnPtr->pad);
+
+    top = tvPtr->titleHeight + tvPtr->inset;
+    bottom = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
+    left = tvPtr->inset;
+    right = Tk_Width(tvPtr->tkwin) - tvPtr->inset;
+
+    if (((dx + width) < left) || (dx > right) ||
+	((dy + height) < top) || (dy > bottom)) {
+	return;			/* Value is clipped. */
+    }
+
+    drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin), 
+	width, height, Tk_Depth(tvPtr->tkwin));
+    /* Draw the background of the value. */
+    if ((valuePtr == tvPtr->activeValuePtr) ||
+	(!Blt_TreeViewEntryIsSelected(tvPtr, entryPtr))) {
+	Tk_3DBorder border;
+
+	border = Blt_TreeViewGetStyleBorder(tvPtr, tvPtr->stylePtr);
+	Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, 0, 0, width, height,
+		0, TK_RELIEF_FLAT);
+    } else {
+	Blt_Fill3DRectangle(tvPtr->tkwin, drawable, SELECT_BORDER(tvPtr), 0, 0, 
+		width, height, tvPtr->selBorderWidth, tvPtr->selRelief);
+    }
+    Blt_TreeViewDrawValue(tvPtr, entryPtr, valuePtr, drawable, 0, 0);
+    
+    /* Clip the drawable if necessary */
+    sx = sy = 0;
+    if (dx < left) {
+	width -= left - dx;
+	sx += left - dx;
+	dx = left;
+    }
+    if ((dx + width) >= right) {
+	width -= (dx + width) - right;
+    }
+    if (dy < top) {
+	height -= top - dy;
+	sy += top - dy;
+	dy = top;
+    }
+    if ((dy + height) >= bottom) {
+	height -= (dy + height) - bottom;
+    }
+    XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin), 
+      tvPtr->lineGC, sx, sy, width,  height, dx, dy);
+    Tk_FreePixmap(tvPtr->display, drawable);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleActivateOp --
+ *
+ * 	Turns on/off highlighting for a particular style.
+ *
+ *	  .t style activate entry column
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleActivateOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewEntry *entryPtr;
+    TreeViewValue *valuePtr, *oldPtr;
+
+    oldPtr = tvPtr->activeValuePtr;
+    if (objc == 3) {
+	Tcl_Obj *listObjPtr; 
+
+	valuePtr = tvPtr->activeValuePtr;
+	entryPtr = tvPtr->activePtr;
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	if ((entryPtr != NULL) && (valuePtr != NULL)) {
+	    Tcl_Obj *objPtr; 
+	    objPtr = Tcl_NewIntObj(Blt_TreeNodeId(entryPtr->node));
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	    objPtr = Tcl_NewStringObj(valuePtr->columnPtr->key, -1);
+	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+	} 
+	Tcl_SetObjResult(interp, listObjPtr);
+	return TCL_OK;
+    } else if (objc == 4) {
+	tvPtr->activeValuePtr = NULL;
+	if ((oldPtr != NULL)  && (tvPtr->activePtr != NULL)) {
+	    DrawValue(tvPtr, tvPtr->activePtr, oldPtr);
+	}
+    } else {
+	TreeViewColumn *columnPtr;
+
+	if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+
+	if (Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
+	if (valuePtr == NULL) {
+	    return TCL_OK;
+	}
+	tvPtr->activePtr = entryPtr;
+	tvPtr->activeColumnPtr = columnPtr;
+	oldPtr = tvPtr->activeValuePtr;
+	tvPtr->activeValuePtr = valuePtr;
+	if (valuePtr != oldPtr) {
+	    if (oldPtr != NULL) {
+		DrawValue(tvPtr, entryPtr, oldPtr);
+	    }
+	    if (valuePtr != NULL) {
+		DrawValue(tvPtr, entryPtr, valuePtr);
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleCgetOp --
+ *
+ *	  .t style cget "styleName" -background
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleCgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[3]));
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, 
+	stylePtr->classPtr->specsPtr, (char *)tvPtr, objv[4], 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleCheckBoxOp --
+ *
+ *	  .t style checkbox "styleName" -background blue
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleCheckBoxOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = CreateStyle(interp, tvPtr, STYLE_CHECKBOX, 
+	Tcl_GetString(objv[3]), objc - 4, objv + 4);
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    stylePtr->refCount = 0;
+    stylePtr->flags |= STYLE_USER;
+    Blt_TreeViewUpdateStyleGCs(tvPtr, stylePtr);
+    Tcl_SetObjResult(interp, objv[3]);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleComboBoxOp --
+ *
+ *	  .t style combobox "styleName" -background blue
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleComboBoxOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = CreateStyle(interp, tvPtr, STYLE_COMBOBOX, 
+	Tcl_GetString(objv[3]), objc - 4, objv + 4);
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    stylePtr->refCount = 0;
+    stylePtr->flags |= STYLE_USER;
+    Blt_TreeViewUpdateStyleGCs(tvPtr, stylePtr);
+    Tcl_SetObjResult(interp, objv[3]);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleConfigureOp --
+ *
+ * 	This procedure is called to process a list of configuration
+ *	options database, in order to reconfigure a style.
+ *
+ *	  .t style configure "styleName" option value
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ * Side effects:
+ *	Configuration information, such as text string, colors, font,
+ *	etc. get set for stylePtr; old resources get freed, if there
+ *	were any.  
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleConfigureOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[3]));
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (objc == 4) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+	    stylePtr->classPtr->specsPtr, (char *)stylePtr, (Tcl_Obj *)NULL, 0);
+    } else if (objc == 5) {
+	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, 
+		stylePtr->classPtr->specsPtr, (char *)stylePtr, objv[5], 0);
+    }
+    bltTreeViewIconOption.clientData = tvPtr;
+    if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, 
+	stylePtr->classPtr->specsPtr, objc - 4, objv + 4, (char *)stylePtr, 
+	BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    (*stylePtr->classPtr->configProc)(tvPtr, stylePtr);
+    stylePtr->flags |= STYLE_DIRTY;
+    tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleForgetOp --
+ *
+ * 	Eliminates one or more style names.  A style still may be in
+ * 	use after its name has been officially removed.  Only its hash
+ * 	table entry is removed.  The style itself remains until its
+ * 	reference count returns to zero (i.e. no one else is using it).
+ *
+ *	  .t style forget "styleName"...
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleForgetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+    int i;
+
+    for (i = 3; i < objc; i++) {
+	stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[i]));
+	if (stylePtr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (stylePtr->hashPtr != NULL) {
+	    Blt_DeleteHashEntry(&tvPtr->styleTable, stylePtr->hashPtr);
+	    stylePtr->hashPtr = NULL;
+	} 
+	stylePtr->flags &= ~STYLE_USER;
+	if (stylePtr->refCount <= 0) {
+	    Blt_TreeViewFreeStyle(tvPtr, stylePtr);
+	}
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleHighlightOp --
+ *
+ * 	Turns on/off highlighting for a particular style.
+ *
+ *	  .t style highlight styleName on|off
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleHighlightOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+    int bool, oldBool;
+
+    stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[3]));
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (Tcl_GetBooleanFromObj(interp, objv[4], &bool) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    oldBool = ((stylePtr->flags & STYLE_HIGHLIGHT) != 0);
+    if (oldBool != bool) {
+	if (bool) {
+	    stylePtr->flags |= STYLE_HIGHLIGHT;
+	} else {
+	    stylePtr->flags &= ~STYLE_HIGHLIGHT;
+	}
+	Blt_TreeViewEventuallyRedraw(tvPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleNamesOp --
+ *
+ * 	Lists the names of all the current styles in the treeview widget.
+ *
+ *	  .t style names
+ *
+ * Results:
+ *	Always TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleNamesOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    Tcl_Obj *listObjPtr, *objPtr;
+    TreeViewStyle *stylePtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for (hPtr = Blt_FirstHashEntry(&tvPtr->styleTable, &cursor); hPtr != NULL;
+	 hPtr = Blt_NextHashEntry(&cursor)) {
+	stylePtr = Blt_GetHashValue(hPtr);
+	objPtr = Tcl_NewStringObj(stylePtr->name, -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleSetOp --
+ *
+ * 	Sets a style for a given key for all the ids given.
+ *
+ *	  .t style set styleName key node...
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleSetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeKey key;
+    TreeViewEntry *entryPtr;
+    TreeViewStyle *stylePtr, *oldStylePtr;
+    TreeViewTagInfo info;
+    int i;
+
+    stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[3]));
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    key = Blt_TreeGetKey(Tcl_GetString(objv[4]));
+    stylePtr->flags |= STYLE_LAYOUT;
+    for (i = 5; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    register TreeViewValue *valuePtr;
+
+	    for (valuePtr = entryPtr->values; valuePtr != NULL; 
+		 valuePtr = valuePtr->nextPtr) {
+		if (valuePtr->columnPtr->key == key) {
+		    stylePtr->refCount++;
+		    oldStylePtr = valuePtr->stylePtr;
+		    valuePtr->stylePtr = stylePtr;
+		    if (oldStylePtr != NULL) {
+			Blt_TreeViewFreeStyle(tvPtr, oldStylePtr);
+		    }
+		    break;
+		}
+	    }
+	}
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleTextBoxOp --
+ *
+ *	  .t style text "styleName" -background blue
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StyleTextBoxOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    TreeViewStyle *stylePtr;
+
+    stylePtr = CreateStyle(interp, tvPtr, STYLE_TEXTBOX, 
+	Tcl_GetString(objv[3]), objc - 4, objv + 4);
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    stylePtr->refCount = 0;
+    stylePtr->flags |= STYLE_USER;
+    Blt_TreeViewUpdateStyleGCs(tvPtr, stylePtr);
+    Tcl_SetObjResult(interp, objv[3]);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleUnsetOp --
+ *
+ * 	Removes a style for a given key for all the ids given.
+ *	The cell's style is returned to its default state.
+ *
+ *	  .t style unset styleName key node...
+ *
+ * Results:
+ *	A standard Tcl result.  If TCL_ERROR is returned, then
+ *	interp->result contains an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+StyleUnsetOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_TreeKey key;
+    TreeViewEntry *entryPtr;
+    TreeViewStyle *stylePtr;
+    TreeViewTagInfo info;
+    int i;
+
+    stylePtr = GetStyle(interp, tvPtr, Tcl_GetString(objv[3]));
+    if (stylePtr == NULL) {
+	return TCL_ERROR;
+    }
+    key = Blt_TreeGetKey(Tcl_GetString(objv[4]));
+    stylePtr->flags |= STYLE_LAYOUT;
+    for (i = 5; i < objc; i++) {
+	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL; 
+	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
+	    register TreeViewValue *valuePtr;
+
+	    for (valuePtr = entryPtr->values; valuePtr != NULL; 
+		 valuePtr = valuePtr->nextPtr) {
+		if (valuePtr->columnPtr->key == key) {
+		    if (valuePtr->stylePtr != NULL) {
+			Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
+			valuePtr->stylePtr = NULL;
+		    }
+		    break;
+		}
+	    }
+	}
+    }
+    Blt_TreeViewEventuallyRedraw(tvPtr);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StyleOp --
+ *
+ *	.t style activate $node $column
+ *	.t style activate 
+ *	.t style cget "highlight" -foreground
+ *	.t style configure "highlight" -fg blue -bg green
+ *	.t style checkbox "highlight"
+ *	.t style highlight "highlight" on|off
+ *	.t style combobox "highlight"
+ *	.t style text "highlight"
+ *	.t style forget "highlight"
+ *	.t style get "mtime" $node
+ *	.t style names
+ *	.t style set "mtime" "highlight" all
+ *	.t style unset "mtime" all
+ *
+ *---------------------------------------------------------------------- 
+ */
+static Blt_OpSpec styleOps[] = {
+    {"activate", 1, (Blt_Op)StyleActivateOp, 3, 5,"entry column",},
+    {"cget", 2, (Blt_Op)StyleCgetOp, 5, 5, "styleName option",},
+    {"checkbox", 2, (Blt_Op)StyleCheckBoxOp, 4, 0, "styleName options...",},
+    {"combobox", 3, (Blt_Op)StyleComboBoxOp, 4, 0, "styleName options...",},
+    {"configure", 3, (Blt_Op)StyleConfigureOp, 4, 0, "styleName options...",},
+    {"forget", 1, (Blt_Op)StyleForgetOp, 3, 0, "styleName...",},
+    {"highlight", 1, (Blt_Op)StyleHighlightOp, 5, 5, "styleName boolean",},
+    {"names", 1, (Blt_Op)StyleNamesOp, 3, 3, "",}, 
+    {"set", 1, (Blt_Op)StyleSetOp, 6, 6, "key styleName tagOrId...",},
+    {"textbox", 1, (Blt_Op)StyleTextBoxOp, 4, 0, "styleName options...",},
+    {"unset", 1, (Blt_Op)StyleUnsetOp, 5, 5, "key tagOrId",},
+};
+
+static int nStyleOps = sizeof(styleOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_TreeViewStyleOp(tvPtr, interp, objc, objv)
+    TreeView *tvPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nStyleOps, styleOps, BLT_OP_ARG2, objc, 
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc)(tvPtr, interp, objc, objv);
+    return result;
+}
+#endif /* NO_TREEVIEW */
Index: trunk/kitgen/8.x/blt/generic/bltUtil.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltUtil.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltUtil.c	(revision 175)
@@ -0,0 +1,1336 @@
+/*
+ * bltUtil.c --
+ *
+ *	This module implements utility procedures for the BLT
+ *	toolkit.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "bltHash.h"
+
+#ifndef HAVE_STRTOLOWER
+void
+strtolower(s) 
+    register char *s;
+{
+    while (*s != '\0') {
+	*s = tolower(UCHAR(*s));
+	s++;
+    }
+}
+#endif /* !HAVE_STRTOLOWER */
+
+#ifndef HAVE_STRCASECMP
+
+static unsigned char caseTable[] =
+{
+    (unsigned char)'\000', (unsigned char)'\001', 
+    (unsigned char)'\002', (unsigned char)'\003', 
+    (unsigned char)'\004', (unsigned char)'\005', 
+    (unsigned char)'\006', (unsigned char)'\007',
+    (unsigned char)'\010', (unsigned char)'\011', 
+    (unsigned char)'\012', (unsigned char)'\013', 
+    (unsigned char)'\014', (unsigned char)'\015', 
+    (unsigned char)'\016', (unsigned char)'\017',
+    (unsigned char)'\020', (unsigned char)'\021', 
+    (unsigned char)'\022', (unsigned char)'\023', 
+    (unsigned char)'\024', (unsigned char)'\025', 
+    (unsigned char)'\026', (unsigned char)'\027',
+    (unsigned char)'\030', (unsigned char)'\031', 
+    (unsigned char)'\032', (unsigned char)'\033', 
+    (unsigned char)'\034', (unsigned char)'\035', 
+    (unsigned char)'\036', (unsigned char)'\037',
+    (unsigned char)'\040', (unsigned char)'\041', 
+    (unsigned char)'\042', (unsigned char)'\043', 
+    (unsigned char)'\044', (unsigned char)'\045', 
+    (unsigned char)'\046', (unsigned char)'\047',
+    (unsigned char)'\050', (unsigned char)'\051', 
+    (unsigned char)'\052', (unsigned char)'\053', 
+    (unsigned char)'\054', (unsigned char)'\055', 
+    (unsigned char)'\056', (unsigned char)'\057',
+    (unsigned char)'\060', (unsigned char)'\061', 
+    (unsigned char)'\062', (unsigned char)'\063', 
+    (unsigned char)'\064', (unsigned char)'\065', 
+    (unsigned char)'\066', (unsigned char)'\067',
+    (unsigned char)'\070', (unsigned char)'\071', 
+    (unsigned char)'\072', (unsigned char)'\073', 
+    (unsigned char)'\074', (unsigned char)'\075', 
+    (unsigned char)'\076', (unsigned char)'\077',
+    (unsigned char)'\100', (unsigned char)'\141', 
+    (unsigned char)'\142', (unsigned char)'\143', 
+    (unsigned char)'\144', (unsigned char)'\145', 
+    (unsigned char)'\146', (unsigned char)'\147',
+    (unsigned char)'\150', (unsigned char)'\151', 
+    (unsigned char)'\152', (unsigned char)'\153', 
+    (unsigned char)'\154', (unsigned char)'\155', 
+    (unsigned char)'\156', (unsigned char)'\157',
+    (unsigned char)'\160', (unsigned char)'\161', 
+    (unsigned char)'\162', (unsigned char)'\163', 
+    (unsigned char)'\164', (unsigned char)'\165', 
+    (unsigned char)'\166', (unsigned char)'\167',
+    (unsigned char)'\170', (unsigned char)'\171', 
+    (unsigned char)'\172', (unsigned char)'\133', 
+    (unsigned char)'\134', (unsigned char)'\135', 
+    (unsigned char)'\136', (unsigned char)'\137',
+    (unsigned char)'\140', (unsigned char)'\141', 
+    (unsigned char)'\142', (unsigned char)'\143', 
+    (unsigned char)'\144', (unsigned char)'\145', 
+    (unsigned char)'\146', (unsigned char)'\147',
+    (unsigned char)'\150', (unsigned char)'\151', 
+    (unsigned char)'\152', (unsigned char)'\153', 
+    (unsigned char)'\154', (unsigned char)'\155', 
+    (unsigned char)'\156', (unsigned char)'\157',
+    (unsigned char)'\160', (unsigned char)'\161',
+    (unsigned char)'\162', (unsigned char)'\163', 
+    (unsigned char)'\164', (unsigned char)'\165', 
+    (unsigned char)'\166', (unsigned char)'\167',
+    (unsigned char)'\170', (unsigned char)'\171', 
+    (unsigned char)'\172', (unsigned char)'\173', 
+    (unsigned char)'\174', (unsigned char)'\175', 
+    (unsigned char)'\176', (unsigned char)'\177',
+    (unsigned char)'\200', (unsigned char)'\201', 
+    (unsigned char)'\202', (unsigned char)'\203', 
+    (unsigned char)'\204', (unsigned char)'\205', 
+    (unsigned char)'\206', (unsigned char)'\207',
+    (unsigned char)'\210', (unsigned char)'\211', 
+    (unsigned char)'\212', (unsigned char)'\213', 
+    (unsigned char)'\214', (unsigned char)'\215', 
+    (unsigned char)'\216', (unsigned char)'\217',
+    (unsigned char)'\220', (unsigned char)'\221', 
+    (unsigned char)'\222', (unsigned char)'\223', 
+    (unsigned char)'\224', (unsigned char)'\225', 
+    (unsigned char)'\226', (unsigned char)'\227',
+    (unsigned char)'\230', (unsigned char)'\231', 
+    (unsigned char)'\232', (unsigned char)'\233', 
+    (unsigned char)'\234', (unsigned char)'\235', 
+    (unsigned char)'\236', (unsigned char)'\237',
+    (unsigned char)'\240', (unsigned char)'\241', 
+    (unsigned char)'\242', (unsigned char)'\243', 
+    (unsigned char)'\244', (unsigned char)'\245', 
+    (unsigned char)'\246', (unsigned char)'\247',
+    (unsigned char)'\250', (unsigned char)'\251', 
+    (unsigned char)'\252', (unsigned char)'\253', 
+    (unsigned char)'\254', (unsigned char)'\255', 
+    (unsigned char)'\256', (unsigned char)'\257',
+    (unsigned char)'\260', (unsigned char)'\261', 
+    (unsigned char)'\262', (unsigned char)'\263', 
+    (unsigned char)'\264', (unsigned char)'\265', 
+    (unsigned char)'\266', (unsigned char)'\267',
+    (unsigned char)'\270', (unsigned char)'\271', 
+    (unsigned char)'\272', (unsigned char)'\273', 
+    (unsigned char)'\274', (unsigned char)'\275', 
+    (unsigned char)'\276', (unsigned char)'\277',
+    (unsigned char)'\300', (unsigned char)'\341', 
+    (unsigned char)'\342', (unsigned char)'\343', 
+    (unsigned char)'\344', (unsigned char)'\345', 
+    (unsigned char)'\346', (unsigned char)'\347',
+    (unsigned char)'\350', (unsigned char)'\351', 
+    (unsigned char)'\352', (unsigned char)'\353', 
+    (unsigned char)'\354', (unsigned char)'\355', 
+    (unsigned char)'\356', (unsigned char)'\357',
+    (unsigned char)'\360', (unsigned char)'\361', 
+    (unsigned char)'\362', (unsigned char)'\363', 
+    (unsigned char)'\364', (unsigned char)'\365', 
+    (unsigned char)'\366', (unsigned char)'\367',
+    (unsigned char)'\370', (unsigned char)'\371', 
+    (unsigned char)'\372', (unsigned char)'\333', 
+    (unsigned char)'\334', (unsigned char)'\335', 
+    (unsigned char)'\336', (unsigned char)'\337',
+    (unsigned char)'\340', (unsigned char)'\341', 
+    (unsigned char)'\342', (unsigned char)'\343', 
+    (unsigned char)'\344', (unsigned char)'\345', 
+    (unsigned char)'\346', (unsigned char)'\347',
+    (unsigned char)'\350', (unsigned char)'\351', 
+    (unsigned char)'\352', (unsigned char)'\353', 
+    (unsigned char)'\354', (unsigned char)'\355', 
+    (unsigned char)'\356', (unsigned char)'\357',
+    (unsigned char)'\360', (unsigned char)'\361', 
+    (unsigned char)'\362', (unsigned char)'\363', 
+    (unsigned char)'\364', (unsigned char)'\365', 
+    (unsigned char)'\366', (unsigned char)'\367',
+    (unsigned char)'\370', (unsigned char)'\371', 
+    (unsigned char)'\372', (unsigned char)'\373', 
+    (unsigned char)'\374', (unsigned char)'\375', 
+    (unsigned char)'\376', (unsigned char)'\377',
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strcasecmp --
+ *
+ *      Compare two strings, disregarding case.
+ *
+ * Results:
+ *      Returns a signed integer representing the following:
+ *
+ *	zero      - two strings are equal
+ *	negative  - first string is less than second
+ *	positive  - first string is greater than second
+ *
+ *----------------------------------------------------------------------
+ */
+int
+strcasecmp(s1, s2)
+    CONST char *s1;
+    CONST char *s2;
+{
+    unsigned char *s = (unsigned char *)s1;
+    unsigned char *t = (unsigned char *)s2;
+
+    for ( /* empty */ ; (caseTable[*s] == caseTable[*t]); s++, t++) {
+	if (*s == '\0') {
+	    return 0;
+	}
+    }
+    return (caseTable[*s] - caseTable[*t]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strncasecmp --
+ *
+ *      Compare two strings, disregarding case, up to a given length.
+ *
+ * Results:
+ *      Returns a signed integer representing the following:
+ *
+ *	zero      - two strings are equal
+ *	negative  - first string is less than second
+ *	positive  - first string is greater than second
+ *
+ *----------------------------------------------------------------------
+ */
+int
+strncasecmp(s1, s2, length)
+    CONST char *s1;
+    CONST char *s2;
+    size_t length;
+{
+    register unsigned char *s = (unsigned char *)s1;
+    register unsigned char *t = (unsigned char *)s2;
+
+    for ( /* empty */ ; (length > 0); s++, t++, length--) {
+	if (caseTable[*s] != caseTable[*t]) {
+	    return (caseTable[*s] - caseTable[*t]);
+	}
+	if (*s == '\0') {
+	    return 0;
+	}
+    }
+    return 0;
+}
+
+#endif /* !HAVE_STRCASECMP */
+
+
+#if (TCL_VERSION_NUMBER < _VERSION(8,1,0)) && (TCL_MAJOR_VERSION > 7)
+
+char *
+Tcl_GetString(Tcl_Obj *objPtr)
+{
+    unsigned int dummy;
+
+    return Tcl_GetStringFromObj(objPtr, &dummy);
+}
+
+int 
+Tcl_EvalObjv(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int flags)
+{
+    Tcl_DString dString;
+    register int i;
+    int result;
+
+    Tcl_DStringInit(&dString);
+    for (i = 0; i < objc; i++) {
+	Tcl_DStringAppendElement(&dString, Tcl_GetString(objv[i]));
+    }
+    result = Tcl_Eval(interp, Tcl_DStringValue(&dString)); 
+    Tcl_DStringFree(&dString);
+    return result;
+}
+
+int 
+Tcl_WriteObj(Tcl_Channel channel, Tcl_Obj *objPtr)
+{
+    char *data;
+    int nBytes;
+
+    data = Tcl_GetStringFromObj(objPtr, &nBytes);
+    return Tcl_Write(channel, data, nBytes);
+}
+
+char *
+Tcl_SetVar2Ex(
+    Tcl_Interp *interp, 
+    char *part1, 
+    char *part2, 
+    Tcl_Obj *objPtr, 
+    int flags)
+{
+    return Tcl_SetVar2(interp, part1, part2, Tcl_GetString(objPtr), flags);
+}
+
+Tcl_Obj *
+Tcl_GetVar2Ex(
+    Tcl_Interp *interp,
+    char *part1, 
+    char *part2,
+    int flags)
+{
+    char *result;
+    
+    result = Tcl_GetVar2(interp, part1, part2, flags);
+    if (result == NULL) {
+	return NULL;
+    }
+    return Tcl_NewStringObj(result, -1);
+}
+
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompareByDictionary
+ *
+ *	This function compares two strings as if they were being used in
+ *	an index or card catalog.  The case of alphabetic characters is
+ *	ignored, except to break ties.  Thus "B" comes before "b" but
+ *	after "a".  Also, integers embedded in the strings compare in
+ *	numerical order.  In other words, "x10y" comes after "x9y", not
+ *      before it as it would when using strcmp().
+ *
+ * Results:
+ *      A negative result means that the first element comes before the
+ *      second, and a positive result means that the second element
+ *      should come first.  A result of zero means the two elements
+ *      are equal and it doesn't matter which comes first.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if HAVE_UTF
+int
+Blt_DictionaryCompare(left, right)
+    char *left, *right;
+{
+    Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
+    int diff, zeros;
+    int secondaryDiff = 0;
+
+    for(;;) {
+	if ((isdigit(UCHAR(*right))) && (isdigit(UCHAR(*left)))) { 
+	    /*
+	     * There are decimal numbers embedded in the two
+	     * strings.  Compare them as numbers, rather than
+	     * strings.  If one number has more leading zeros than
+	     * the other, the number with more leading zeros sorts
+	     * later, but only as a secondary choice.
+	     */
+
+	    zeros = 0;
+	    while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
+		right++;
+		zeros--;
+	    }
+	    while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
+		left++;
+		zeros++;
+	    }
+	    if (secondaryDiff == 0) {
+		secondaryDiff = zeros;
+	    }
+
+	    /*
+	     * The code below compares the numbers in the two
+	     * strings without ever converting them to integers.  It
+	     * does this by first comparing the lengths of the
+	     * numbers and then comparing the digit values.
+	     */
+
+	    diff = 0;
+	    for (;;) {
+		if (diff == 0) {
+		    diff = UCHAR(*left) - UCHAR(*right);
+		}
+		right++;
+		left++;
+
+		/* Ignore commas in numbers. */
+		if (*left == ',') {
+		    left++;
+		}
+		if (*right == ',') {
+		    right++;
+		}
+
+		if (!isdigit(UCHAR(*right))) { /* INTL: digit */
+		    if (isdigit(UCHAR(*left))) { /* INTL: digit */
+			return 1;
+		    } else {
+			/*
+			 * The two numbers have the same length. See
+			 * if their values are different.
+			 */
+
+			if (diff != 0) {
+			    return diff;
+			}
+			break;
+		    }
+		} else if (!isdigit(UCHAR(*left))) { /* INTL: digit */
+		    return -1;
+		}
+	    }
+	    continue;
+	}
+
+	/*
+	 * Convert character to Unicode for comparison purposes.  If either
+	 * string is at the terminating null, do a byte-wise comparison and
+	 * bail out immediately.
+	 */
+	if ((*left != '\0') && (*right != '\0')) {
+	    left += Tcl_UtfToUniChar(left, &uniLeft);
+	    right += Tcl_UtfToUniChar(right, &uniRight);
+	    /*
+	     * Convert both chars to lower for the comparison, because
+	     * dictionary sorts are case insensitve.  Convert to lower, not
+	     * upper, so chars between Z and a will sort before A (where most
+	     * other interesting punctuations occur)
+	     */
+	    uniLeftLower = Tcl_UniCharToLower(uniLeft);
+	    uniRightLower = Tcl_UniCharToLower(uniRight);
+	} else {
+	    diff = UCHAR(*left) - UCHAR(*right);
+	    break;
+	}
+
+        diff = uniLeftLower - uniRightLower;
+        if (diff) {
+	    return diff;
+	} else if (secondaryDiff == 0) {
+	    if (Tcl_UniCharIsUpper(uniLeft) &&
+		    Tcl_UniCharIsLower(uniRight)) {
+		secondaryDiff = -1;
+	    } else if (Tcl_UniCharIsUpper(uniRight)
+		    && Tcl_UniCharIsLower(uniLeft)) {
+		secondaryDiff = 1;
+	    }
+        }
+    }
+    if (diff == 0) {
+	diff = secondaryDiff;
+    }
+    return diff;
+}
+
+#else 
+
+int
+Blt_DictionaryCompare(left, right)
+    char *left, *right;          /* The strings to compare */
+{
+    int diff, zeros;
+    int secondaryDiff = 0;
+
+    while (1) {
+	if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
+	    /*
+	     * There are decimal numbers embedded in the two
+	     * strings.  Compare them as numbers, rather than
+	     * strings.  If one number has more leading zeros than
+	     * the other, the number with more leading zeros sorts
+	     * later, but only as a secondary choice.
+	     */
+
+	    zeros = 0;
+	    while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
+		right++;
+		zeros--;
+	    }
+	    while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
+		left++;
+		zeros++;
+	    }
+	    if (secondaryDiff == 0) {
+		secondaryDiff = zeros;
+	    }
+
+	    /*
+	     * The code below compares the numbers in the two
+	     * strings without ever converting them to integers.  It
+	     * does this by first comparing the lengths of the
+	     * numbers and then comparing the digit values.
+	     */
+
+	    diff = 0;
+	    while (1) {
+		if (diff == 0) {
+		    diff = UCHAR(*left) - UCHAR(*right);
+		}
+		right++;
+		left++;
+		/* Ignore commas in numbers. */
+		if (*left == ',') {
+		    left++;
+		}
+		if (*right == ',') {
+		    right++;
+		}
+		if (!isdigit(UCHAR(*right))) {
+		    if (isdigit(UCHAR(*left))) {
+			return 1;
+		    } else {
+			/*
+			 * The two numbers have the same length. See
+			 * if their values are different.
+			 */
+
+			if (diff != 0) {
+			    return diff;
+			}
+			break;
+		    }
+		} else if (!isdigit(UCHAR(*left))) {
+		    return -1;
+		}
+	    }
+	    continue;
+	}
+        diff = UCHAR(*left) - UCHAR(*right);
+        if (diff) {
+            if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
+                diff = UCHAR(tolower(*left)) - UCHAR(*right);
+                if (diff) {
+		    return diff;
+                } else if (secondaryDiff == 0) {
+		    secondaryDiff = -1;
+                }
+            } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
+                diff = UCHAR(*left) - UCHAR(tolower(UCHAR(*right)));
+                if (diff) {
+		    return diff;
+                } else if (secondaryDiff == 0) {
+		    secondaryDiff = 1;
+                }
+            } else {
+                return diff;
+            }
+        }
+        if (*left == 0) {
+	    break;
+	}
+        left++;
+        right++;
+    }
+    if (diff == 0) {
+	diff = secondaryDiff;
+    }
+    return diff;
+}
+#endif
+
+#ifndef NDEBUG
+void
+Blt_Assert(testExpr, fileName, lineNumber)
+    char *testExpr;
+    char *fileName;
+    int lineNumber;
+{
+#ifdef WINDEBUG
+    PurifyPrintf("line %d of %s: Assert \"%s\" failed\n", lineNumber,
+	fileName, testExpr);
+#endif
+    fprintf(stderr, "line %d of %s: Assert \"%s\" failed\n",
+	lineNumber, fileName, testExpr);
+    fflush(stderr);
+    abort();
+}
+#endif
+
+/*ARGSUSED*/
+void
+Blt_Panic TCL_VARARGS_DEF(char *, arg1)
+{
+    va_list argList;
+    char *format;
+
+    format = TCL_VARARGS_START(char *, arg1, argList);
+    vfprintf(stderr, format, argList);
+    fprintf(stderr, "\n");
+    fflush(stderr);
+    abort();
+}
+
+void 
+Blt_DStringAppendElements
+TCL_VARARGS_DEF(Tcl_DString *, arg1)
+{
+    va_list argList;
+    Tcl_DString *dsPtr;
+    register char *elem;
+
+    dsPtr = TCL_VARARGS_START(Tcl_DString *, arg1, argList);
+    while ((elem = va_arg(argList, char *)) != NULL) {
+	Tcl_DStringAppendElement(dsPtr, elem);
+    }
+    va_end(argList);
+}
+
+static char stringRep[200];
+
+char *
+Blt_Itoa(value)
+    int value;
+{
+    sprintf(stringRep, "%d", value);
+    return stringRep;
+}
+
+char *
+Blt_Utoa(value)
+    unsigned int value;
+{
+    sprintf(stringRep, "%u", value);
+    return stringRep;
+}
+
+char *
+Blt_Dtoa(interp, value)
+    Tcl_Interp *interp;
+    double value;
+{
+    Tcl_PrintDouble(interp, value, stringRep);
+    return stringRep;
+}
+
+#if HAVE_UTF
+
+#undef fopen
+FILE *
+Blt_OpenUtfFile(fileName, mode)
+    char *fileName, *mode;
+{
+    Tcl_DString dString;
+    FILE *f;
+
+    fileName = Tcl_UtfToExternalDString(NULL, fileName, -1, &dString);
+    f = fopen(fileName, mode);
+    Tcl_DStringFree(&dString);
+    return f;
+}
+
+#endif /* HAVE_UTF */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_InitHexTable --
+ *
+ *	Table index for the hex values. Initialized once, first time.
+ *	Used for translation value or delimiter significance lookup.
+ *
+ *	We build the table at run time for several reasons:
+ *
+ *     	  1.  portable to non-ASCII machines.
+ *	  2.  still reentrant since we set the init flag after setting
+ *            table.
+ *        3.  easier to extend.
+ *        4.  less prone to bugs.
+ *
+ * Results:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_InitHexTable(hexTable)
+    char hexTable[];
+{
+    hexTable['0'] = 0;
+    hexTable['1'] = 1;
+    hexTable['2'] = 2;
+    hexTable['3'] = 3;
+    hexTable['4'] = 4;
+    hexTable['5'] = 5;
+    hexTable['6'] = 6;
+    hexTable['7'] = 7;
+    hexTable['8'] = 8;
+    hexTable['9'] = 9;
+    hexTable['a'] = hexTable['A'] = 10;
+    hexTable['b'] = hexTable['B'] = 11;
+    hexTable['c'] = hexTable['C'] = 12;
+    hexTable['d'] = hexTable['D'] = 13;
+    hexTable['e'] = hexTable['E'] = 14;
+    hexTable['f'] = hexTable['F'] = 15;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_GetPosition --
+ *
+ *	Convert a string representing a numeric position.
+ *	A position can be in one of the following forms.
+ *
+ * 	  number	- number of the item in the hierarchy, indexed
+ *			  from zero.
+ *	  "end"		- last position in the hierarchy.
+ *
+ * Results:
+ *	A standard Tcl result.  If "string" is a valid index, then
+ *	*indexPtr is filled with the corresponding numeric index.
+ *	If "end" was selected then *indexPtr is set to -1.
+ *	Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ *	None.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_GetPosition(interp, string, indexPtr)
+    Tcl_Interp *interp;		/* Interpreter to report results back
+				 * to. */
+    char *string;		/* String representation of the index.
+				 * Can be an integer or "end" to refer
+				 * to the last index. */
+    int *indexPtr;		/* Holds the converted index. */
+{
+    if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+	*indexPtr = -1;		/* Indicates last position in hierarchy. */
+    } else {
+	int position;
+
+	if (Tcl_GetInt(interp, string, &position) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (position < 0) {
+	    Tcl_AppendResult(interp, "bad position \"", string, "\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	*indexPtr = position;
+    }
+    return TCL_OK;
+}
+
+/*
+ * The hash table below is used to keep track of all the Blt_Uids created
+ * so far.
+ */
+static Blt_HashTable uidTable;
+static int uidInitialized = 0;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetUid --
+ *
+ *	Given a string, returns a unique identifier for the string.
+ *	A reference count is maintained, so that the identifier
+ *	can be freed when it is not needed any more. This can be used
+ *	in many places to replace Tcl_GetUid.
+ *
+ * Results:
+ *	This procedure returns a Blt_Uid corresponding to the "string"
+ *	argument.  The Blt_Uid has a string value identical to string
+ *	(strcmp will return 0), but it's guaranteed that any other
+ *	calls to this procedure with a string equal to "string" will
+ *	return exactly the same result (i.e. can compare Blt_Uid
+ *	*values* directly, without having to call strcmp on what they
+ *	point to).
+ *
+ * Side effects:
+ *	New information may be entered into the identifier table.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Uid
+Blt_GetUid(string)
+    char *string;		/* String to convert. */
+{
+    int isNew;
+    Blt_HashEntry *hPtr;
+    int refCount;
+    
+    if (!uidInitialized) {
+	Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+	uidInitialized = 1;
+    }
+    hPtr = Blt_CreateHashEntry(&uidTable, string, &isNew);
+    if (isNew) {
+	refCount = 0;
+    } else {
+	refCount = (int)Blt_GetHashValue(hPtr);
+    }
+    refCount++;
+    Blt_SetHashValue(hPtr, (ClientData)refCount);
+    return (Blt_Uid)Blt_GetHashKey(&uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FreeUid --
+ *
+ *	Frees the Blt_Uid if there are no more clients using this
+ *	identifier.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The identifier may be deleted from the identifier table.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_FreeUid(uid)
+    Blt_Uid uid;			/* Identifier to release. */
+{
+    Blt_HashEntry *hPtr;
+
+    if (!uidInitialized) {
+	Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+	uidInitialized = 1;
+    }
+    hPtr = Blt_FindHashEntry(&uidTable, uid);
+    if (hPtr) {
+	int refCount;
+
+	refCount = (int)Blt_GetHashValue(hPtr);
+	refCount--;
+	if (refCount == 0) {
+	    Blt_DeleteHashEntry(&uidTable, hPtr);
+	} else {
+	    Blt_SetHashValue(hPtr, (ClientData)refCount);
+	}
+    } else {
+	fprintf(stderr, "tried to release unknown identifier \"%s\"\n", uid);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindUid --
+ *
+ *	Returns a Blt_Uid associated with a given string, if one exists.
+ *
+ * Results:
+ *	A Blt_Uid for the string if one exists. Otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Uid
+Blt_FindUid(string)
+    char *string;		/* String to find. */
+{
+    Blt_HashEntry *hPtr;
+
+    if (!uidInitialized) {
+	Blt_InitHashTable(&uidTable, BLT_STRING_KEYS);
+	uidInitialized = 1;
+    }
+    hPtr = Blt_FindHashEntry(&uidTable, string);
+    if (hPtr == NULL) {
+	return NULL;
+    }
+    return (Blt_Uid) Blt_GetHashKey(&uidTable, hPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BinaryOpSearch --
+ *
+ *      Performs a binary search on the array of command operation
+ *      specifications to find a partial, anchored match for the
+ *      given operation string.
+ *
+ * Results:
+ *	If the string matches unambiguously the index of the specification
+ *	in the array is returned.  If the string does not match, even
+ *	as an abbreviation, any operation, -1 is returned.  If the string
+ *	matches, but ambiguously -2 is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+BinaryOpSearch(specArr, nSpecs, string)
+    Blt_OpSpec specArr[];
+    int nSpecs;
+    char *string;		/* Name of minor operation to search for */
+{
+    Blt_OpSpec *specPtr;
+    char c;
+    register int high, low, median;
+    register int compare, length;
+
+    low = 0;
+    high = nSpecs - 1;
+    c = string[0];
+    length = strlen(string);
+    while (low <= high) {
+	median = (low + high) >> 1;
+	specPtr = specArr + median;
+
+	/* Test the first character */
+	compare = c - specPtr->name[0];
+	if (compare == 0) {
+	    /* Now test the entire string */
+	    compare = strncmp(string, specPtr->name, length);
+	    if (compare == 0) {
+		if (length < specPtr->minChars) {
+		    return -2;	/* Ambiguous operation name */
+		}
+	    }
+	}
+	if (compare < 0) {
+	    high = median - 1;
+	} else if (compare > 0) {
+	    low = median + 1;
+	} else {
+	    return median;	/* Op found. */
+	}
+    }
+    return -1;			/* Can't find operation */
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinearOpSearch --
+ *
+ *      Performs a binary search on the array of command operation
+ *      specifications to find a partial, anchored match for the
+ *      given operation string.
+ *
+ * Results:
+ *	If the string matches unambiguously the index of the specification
+ *	in the array is returned.  If the string does not match, even
+ *	as an abbreviation, any operation, -1 is returned.  If the string
+ *	matches, but ambiguously -2 is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+LinearOpSearch(specArr, nSpecs, string)
+    Blt_OpSpec specArr[];
+    int nSpecs;
+    char *string;		/* Name of minor operation to search for */
+{
+    Blt_OpSpec *specPtr;
+    char c;
+    int length, nMatches, last;
+    register int i;
+
+    c = string[0];
+    length = strlen(string);
+    nMatches = 0;
+    last = -1;
+    for (specPtr = specArr, i = 0; i < nSpecs; i++, specPtr++) {
+	if ((c == specPtr->name[0]) && 
+	    (strncmp(string, specPtr->name, length) == 0)) {
+	    last = i;
+	    nMatches++;
+	    if (length == specPtr->minChars) {
+		break;
+	    }
+	}
+    }
+    if (nMatches > 1) {
+	return -2;		/* Ambiguous operation name */
+    } 
+    if (nMatches == 0) {
+	return -1;		/* Can't find operation */
+    } 
+    return last;		/* Op found. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetOp --
+ *
+ *      Find the command operation given a string name.  This is useful
+ *      where a group of command operations have the same argument
+ *      signature.
+ *
+ * Results:
+ *      If found, a pointer to the procedure (function pointer) is
+ *      returned.  Otherwise NULL is returned and an error message
+ *      containing a list of the possible commands is returned in
+ *      interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Op
+Blt_GetOp(interp, nSpecs, specArr, operPos, argc, argv, flags)
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int nSpecs;			/* Number of specifications in array */
+    Blt_OpSpec specArr[];	/* Op specification array */
+    int operPos;		/* Index of the operation name argument */
+    int argc;			/* Number of arguments in the argument vector.
+				 * This includes any prefixed arguments */
+    char *argv[];		/* Argument vector */
+    int flags;			/*  */
+{
+    Blt_OpSpec *specPtr;
+    char *string;
+    register int i;
+    register int n;
+
+    if (argc <= operPos) {	/* No operation argument */
+	Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
+      usage:
+	Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
+	for (n = 0; n < nSpecs; n++) {
+	    Tcl_AppendResult(interp, "\n  ", (char *)NULL);
+	    for (i = 0; i < operPos; i++) {
+		Tcl_AppendResult(interp, argv[i], " ", (char *)NULL);
+	    }
+	    specPtr = specArr + n;
+	    Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
+		(char *)NULL);
+	}
+	return NULL;
+    }
+    string = argv[operPos];
+    if (flags & BLT_OP_LINEAR_SEARCH) {
+	n = LinearOpSearch(specArr, nSpecs, string);
+    } else {
+	n = BinaryOpSearch(specArr, nSpecs, string);
+    }
+    if (n == -2) {
+	char c;
+	int length;
+
+	Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
+	if (operPos > 2) {
+	    Tcl_AppendResult(interp, " ", argv[operPos - 1], (char *)NULL);
+	}
+	Tcl_AppendResult(interp, " operation \"", string, "\" matches:",
+	    (char *)NULL);
+
+	c = string[0];
+	length = strlen(string);
+	for (n = 0; n < nSpecs; n++) {
+	    specPtr = specArr + n;
+	    if ((c == specPtr->name[0]) &&
+		(strncmp(string, specPtr->name, length) == 0)) {
+		Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
+	    }
+	}
+	return NULL;
+
+    } else if (n == -1) {	/* Can't find operation, display help */
+	Tcl_AppendResult(interp, "bad", (char *)NULL);
+	if (operPos > 2) {
+	    Tcl_AppendResult(interp, " ", argv[operPos - 1], (char *)NULL);
+	}
+	Tcl_AppendResult(interp, " operation \"", string, "\": ", 
+			 (char *)NULL);
+	goto usage;
+    }
+    specPtr = specArr + n;
+    if ((argc < specPtr->minArgs) || ((specPtr->maxArgs > 0) &&
+	    (argc > specPtr->maxArgs))) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
+	for (i = 0; i < operPos; i++) {
+	    Tcl_AppendResult(interp, argv[i], " ", (char *)NULL);
+	}
+	Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
+	    (char *)NULL);
+	return NULL;
+    }
+    return specPtr->proc;
+}
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,0,0)) 
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetOpFromObj --
+ *
+ *      Find the command operation given a string name.  This is useful
+ *      where a group of command operations have the same argument
+ *      signature.
+ *
+ * Results:
+ *      If found, a pointer to the procedure (function pointer) is
+ *      returned.  Otherwise NULL is returned and an error message
+ *      containing a list of the possible commands is returned in
+ *      interp->result.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_Op
+Blt_GetOpFromObj(interp, nSpecs, specArr, operPos, objc, objv, flags)
+    Tcl_Interp *interp;		/* Interpreter to report errors to */
+    int nSpecs;			/* Number of specifications in array */
+    Blt_OpSpec specArr[];	/* Op specification array */
+    int operPos;		/* Position of operation in argument list. */
+    int objc;			/* Number of arguments in the argument vector.
+				 * This includes any prefixed arguments */
+    Tcl_Obj *CONST objv[];	/* Argument vector */
+    int flags;
+{
+    Blt_OpSpec *specPtr;
+    char *string;
+    register int i;
+    register int n;
+
+    if (objc <= operPos) {	/* No operation argument */
+	Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
+      usage:
+	Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
+	for (n = 0; n < nSpecs; n++) {
+	    Tcl_AppendResult(interp, "\n  ", (char *)NULL);
+	    for (i = 0; i < operPos; i++) {
+		Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", 
+			 (char *)NULL);
+	    }
+	    specPtr = specArr + n;
+	    Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
+		(char *)NULL);
+	}
+	return NULL;
+    }
+    string = Tcl_GetString(objv[operPos]);
+    if (flags & BLT_OP_LINEAR_SEARCH) {
+	n = LinearOpSearch(specArr, nSpecs, string);
+    } else {
+	n = BinaryOpSearch(specArr, nSpecs, string);
+    }
+    if (n == -2) {
+	char c;
+	int length;
+
+	Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
+	if (operPos > 2) {
+	    Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), 
+		(char *)NULL);
+	}
+	Tcl_AppendResult(interp, " operation \"", string, "\" matches:",
+	    (char *)NULL);
+
+	c = string[0];
+	length = strlen(string);
+	for (n = 0; n < nSpecs; n++) {
+	    specPtr = specArr + n;
+	    if ((c == specPtr->name[0]) &&
+		(strncmp(string, specPtr->name, length) == 0)) {
+		Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
+	    }
+	}
+	return NULL;
+
+    } else if (n == -1) {	/* Can't find operation, display help */
+	Tcl_AppendResult(interp, "bad", (char *)NULL);
+	if (operPos > 2) {
+	    Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), 
+		(char *)NULL);
+	}
+	Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL);
+	goto usage;
+    }
+    specPtr = specArr + n;
+    if ((objc < specPtr->minArgs) || 
+	((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) {
+	Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
+	for (i = 0; i < operPos; i++) {
+	    Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", 
+		(char *)NULL);
+	}
+	Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
+	    (char *)NULL);
+	return NULL;
+    }
+    return specPtr->proc;
+}
+
+#endif
+
+#include <stdio.h>
+
+/* open a file
+ * calculate the CRC32 of the entire contents
+ * return the CRC
+ * if there is an error rdet the global variable Crcerror
+ */
+
+/* ---------------------------------------------------------------- */
+
+/* this is the CRC32 lookup table
+ * thanks Gary S. Brown 
+ * 64 lines of 4 values for a 256 dword table (1024 bytes)
+ */
+static unsigned long crcTab[256] =
+{				/* CRC polynomial 0xedb88320 */
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL,
+    0x076dc419UL, 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL,
+    0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL,
+    0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL,
+    0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL,
+    0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL,
+    0x14015c4fUL, 0x63066cd9UL, 0xfa0f3d63UL, 0x8d080df5UL,
+    0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL,
+    0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL,
+    0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL,
+    0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL,
+    0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, 0xb8bda50fUL,
+    0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL,
+    0x76dc4190UL, 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL,
+    0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL,
+    0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL,
+    0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL,
+    0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL,
+    0x65b0d9c6UL, 0x12b7e950UL, 0x8bbeb8eaUL, 0xfcb9887cUL,
+    0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL,
+    0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL,
+    0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL,
+    0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL,
+    0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, 0xc90c2086UL,
+    0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL,
+    0x59b33d17UL, 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL,
+    0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL,
+    0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL,
+    0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL,
+    0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL,
+    0xf762575dUL, 0x806567cbUL, 0x196c3671UL, 0x6e6b06e7UL,
+    0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL,
+    0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL,
+    0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL,
+    0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL,
+    0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, 0x4669be79UL,
+    0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL,
+    0xc5ba3bbeUL, 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL,
+    0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL,
+    0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL,
+    0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL,
+    0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL,
+    0x86d3d2d4UL, 0xf1d4e242UL, 0x68ddb3f8UL, 0x1fda836eUL,
+    0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL,
+    0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL,
+    0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL,
+    0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL,
+    0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, 0x37d83bf0UL,
+    0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL,
+    0xbad03605UL, 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL,
+    0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL,
+    0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL
+}; 
+
+#define CRC32(c, b) (crcTab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
+#define DO1(buf)  crc = CRC32(crc, *buf++)
+#define DO2(buf)  DO1(buf); DO1(buf)
+#define DO4(buf)  DO2(buf); DO2(buf)
+#define DO8(buf)  DO4(buf); DO4(buf)
+
+static int
+Crc32Cmd(
+   ClientData clientData,
+   Tcl_Interp *interp, 
+   int argc, char **argv)
+{
+    register unsigned int crc;
+    char buf[200];
+    
+    crc = 0L;
+    crc = crc ^ 0xffffffffL;
+    if (strcmp(argv[1], "-data") == 0) {
+	register char *p;
+
+	if (argc != 3) {
+	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+		     " ?fileName? ?-data dataString?", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	for (p = argv[2]; *p != '\0'; p++) {
+	    crc = CRC32(crc, *p);
+	}
+    } else {
+	register int c;
+	FILE *f;
+	
+	if (argc != 2) {
+	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+		     " ?fileName? ?-data dataString?", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	f = fopen(argv[1], "rb");
+	if (f == NULL) {
+	    Tcl_AppendResult(interp, "can't open file \"", argv[1], "\": ",
+			     Tcl_PosixError(interp), (char *)NULL);
+	    return TCL_ERROR;
+	}
+	while((c = getc(f)) != EOF) {
+	    crc = CRC32(crc, c);
+	}
+	fclose(f);
+    }
+    crc = crc ^ 0xffffffffL;
+    sprintf(buf, "%x", crc);
+    Tcl_SetResult(interp, buf, TCL_VOLATILE);
+    return TCL_OK;
+}
+
+int
+Blt_Crc32Init(interp)
+    Tcl_Interp *interp;
+{
+    static Blt_CmdSpec cmdSpec = {"crc32", Crc32Cmd,};
+
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
Index: trunk/kitgen/8.x/blt/generic/bltVecCmd.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltVecCmd.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltVecCmd.c	(revision 175)
@@ -0,0 +1,1979 @@
+/*
+ * bltVecCmd.c --
+ *
+ *	This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ *	o Add H. Kirsch's vector binary read operation
+ *		x binread file0
+ *		x binread -file file0
+ *
+ *	o Add ASCII/binary file reader
+ *		x read fileName
+ *
+ *	o Allow Tcl-based client notifications.
+ *		vector x
+ *		x notify call Display
+ *		x notify delete Display
+ *		x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+
+#if (TCL_MAJOR_VERSION == 7)
+
+static void
+GetValues(vPtr, first, last, resultPtr) 
+    VectorObject *vPtr;
+    int first, last;
+    Tcl_DString *resultPtr;
+{ 
+    register int i; 
+    char valueString[TCL_DOUBLE_SPACE + 1]; 
+
+    for (i = first; i <= last; i++) { 
+	Tcl_PrintDouble(vPtr->interp, vPtr->valueArr[i], valueString); 
+	Tcl_DStringAppendElement(resultPtr, valueString); 
+    } 
+}
+
+static void
+ReplicateValue(vPtr, first, last, value) 
+    VectorObject *vPtr;
+    int first, last;
+    double value;
+{ 
+    register int i; 
+    for (i = first; i <= last; i++) { 
+	vPtr->valueArr[i] = value; 
+    } 
+    vPtr->notifyFlags |= UPDATE_RANGE; 
+}
+
+static int
+CopyList(vPtr, nElem, elemArr)
+    VectorObject *vPtr;
+    int nElem;
+    char **elemArr;
+{
+    register int i;
+    double value;
+
+    if (Blt_VectorChangeLength(vPtr, nElem) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (i = 0; i < nElem; i++) {
+	if (Tcl_GetDouble(vPtr->interp, elemArr[i], &value)!= TCL_OK) {
+	    vPtr->length = i;
+	    return TCL_ERROR;
+	}
+	vPtr->valueArr[i] = value;
+    }
+    return TCL_OK;
+}
+
+static int
+AppendVector(destPtr, srcPtr)
+    VectorObject *destPtr, *srcPtr;
+{
+    int nBytes;
+    int oldSize, newSize;
+
+    oldSize = destPtr->length;
+    newSize = oldSize + srcPtr->last - srcPtr->first + 1;
+    if (Blt_VectorChangeLength(destPtr, newSize) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    nBytes = (newSize - oldSize) * sizeof(double);
+    memcpy((char *)(destPtr->valueArr + oldSize),
+	(srcPtr->valueArr + srcPtr->first), nBytes);
+    destPtr->notifyFlags |= UPDATE_RANGE;
+    return TCL_OK;
+}
+
+static int
+AppendList(vPtr, nElem, elemArr)
+    VectorObject *vPtr;
+    int nElem;
+    char **elemArr;
+{
+    int count;
+    register int i;
+    double value;
+    int oldSize;
+
+    oldSize = vPtr->length;
+    if (Blt_VectorChangeLength(vPtr, vPtr->length + nElem) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    count = oldSize;
+    for (i = 0; i < nElem; i++) {
+	if (Tcl_ExprDouble(vPtr->interp, elemArr[i], &value) 
+	    != TCL_OK) {
+	    vPtr->length = count;
+	    return TCL_ERROR;
+	}
+	vPtr->valueArr[count++] = value;
+    }
+    vPtr->notifyFlags |= UPDATE_RANGE;
+    return TCL_OK;
+}
+
+/* Vector instance option commands */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ *	Appends one of more Tcl lists of values, or vector objects
+ *	onto the end of the current vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If a current vector can't be created,
+ *      resized, any of the named vectors can't be found, or one of
+ *	lists of values is invalid, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	Clients of current vector will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AppendOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    register int i;
+    int result;
+    VectorObject *v2Ptr;
+
+    for (i = 2; i < argc; i++) {
+	v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, 
+		argv[i], (char **)NULL, NS_SEARCH_BOTH);
+	if (v2Ptr != NULL) {
+	    result = AppendVector(vPtr, v2Ptr);
+	} else {
+	    int nElem;
+	    char **elemArr;
+
+	    if (Tcl_SplitList(interp, argv[i], &nElem, &elemArr) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    result = AppendList(vPtr, nElem, elemArr);
+	    Blt_Free(elemArr);
+	}
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (argc > 2) {
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ClearOp --
+ *
+ *	Deletes all the accumulated array indices for the Tcl array
+ *	associated will the vector.  This routine can be used to
+ *	free excess memory from a large vector.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ * Side Effects:
+ *	Memory used for the entries of the Tcl array variable is freed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_VectorFlushCache(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes the given indices from the vector.  If no indices are
+ *	provided the entire vector is deleted.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given indices is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	The clients of the vector will be notified of the vector
+ *	deletions.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    unsigned char *unsetArr;
+    register int i, j;
+    register int count;
+
+    if (argc == 2) {
+	Blt_VectorFree(vPtr);
+	return TCL_OK;
+    }
+    /*
+     * Allocate an "unset" bitmap the size of the vector.  We should
+     * try to use bit fields instead of a character array, since
+     * memory may be an issue if the vector is large.
+     */
+    unsetArr = Blt_Calloc(sizeof(unsigned char), vPtr->length);
+    assert(unsetArr);
+    for (i = 2; i < argc; i++) {
+	if (Blt_VectorGetIndexRange(interp, vPtr, argv[i], 
+		(INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL) 
+		!= TCL_OK) {
+	    Blt_Free(unsetArr);
+	    return TCL_ERROR;
+	}
+	for (j = vPtr->first; j <= vPtr->last; j++) {
+	    unsetArr[j] = TRUE;
+	}
+    }
+    count = 0;
+    for (i = 0; i < vPtr->length; i++) {
+	if (unsetArr[i]) {
+	    continue;
+	}
+	if (count < i) {
+	    vPtr->valueArr[count] = vPtr->valueArr[i];
+	}
+	count++;
+    }
+    Blt_Free(unsetArr);
+    vPtr->length = count;
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DupOp --
+ *
+ *	Creates one or more duplicates of the vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If a new vector can't be created,
+ *      or and existing vector resized, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	Clients of existing vectors will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DupOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int argc;
+    char **argv;
+{
+    VectorObject *v2Ptr;
+    int isNew;
+    register int i;
+
+    for (i = 2; i < argc; i++) {
+	v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[i], argv[i], argv[i], 
+		&isNew);
+	if (v2Ptr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (v2Ptr == vPtr) {
+	    continue;
+	}
+	if (Blt_VectorDuplicate(v2Ptr, vPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (!isNew) {
+	    if (v2Ptr->flush) {
+		Blt_VectorFlushCache(v2Ptr);
+	    }
+	    Blt_VectorUpdateClients(v2Ptr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *	Sets or reads the value of the index.  This simulates what the
+ *	vector's variable does.
+ *
+ * Results:
+ *	A standard Tcl result.  If the index is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *	Otherwise interp->result will contain the values.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+IndexOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int first, last;
+
+    if (Blt_VectorGetIndexRange(interp, vPtr, argv[2], INDEX_ALL_FLAGS, 
+		(Blt_VectorIndexProc **) NULL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    first = vPtr->first, last = vPtr->last;
+    if (argc == 3) {
+	Tcl_DString dString;
+
+	if (first == vPtr->length) {
+	    Tcl_AppendResult(interp, "can't get index \"", argv[2], "\"",
+		(char *)NULL);
+	    return TCL_ERROR;	/* Can't read from index "++end" */
+	}
+	Tcl_DStringInit(&dString);
+	GetValues(vPtr, first, last, &dString);
+	Tcl_DStringResult(interp, &dString);
+	Tcl_DStringFree(&dString);
+    } else {
+	char string[TCL_DOUBLE_SPACE + 1];
+	double value;
+
+	if (first == SPECIAL_INDEX) {
+	    Tcl_AppendResult(interp, "can't set index \"", argv[2], "\"",
+		(char *)NULL);
+	    return TCL_ERROR;	/* Tried to set "min" or "max" */
+	}
+	if (Tcl_ExprDouble(interp, argv[3], &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (first == vPtr->length) {
+	    if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	ReplicateValue(vPtr, first, last, value);
+
+	Tcl_PrintDouble(interp, value, string);
+	Tcl_SetResult(interp, string, TCL_VOLATILE);
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * LengthOp --
+ *
+ *	Returns the length of the vector.  If a new size is given, the
+ *	vector is resized to the new vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If the new length is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *	Otherwise interp->result will contain the length of the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+LengthOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    if (argc == 3) {
+	int size;
+
+	if (Tcl_GetInt(interp, argv[2], &size) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (size < 0) {
+	    Tcl_AppendResult(interp, "bad vector size \"", argv[3], "\"",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    Tcl_SetResult(interp, Blt_Itoa(vPtr->length), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ *	Queries or sets the offset of the array index from the base
+ *	address of the data array of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    if (argc > 2) {
+	if (Blt_VectorMapVariable(interp, vPtr, argv[2]) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (vPtr->arrayName != NULL) {
+	Tcl_SetResult(interp, vPtr->arrayName, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MergeOp --
+ *
+ *	Merges the values from the given vectors to the current vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given vectors differ in size,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	vector data will contain merged values of the given vectors.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MergeOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorObject *v2Ptr;
+    VectorObject **vecArr;
+    register VectorObject **vPtrPtr;
+    int refSize, length, nElem;
+    register int i;
+    double *valuePtr, *valueArr;
+
+    /* Allocate an array of vector pointers of each vector to be
+     * merged in the current vector.  */
+    vecArr = Blt_Malloc(sizeof(VectorObject *) * argc);
+    assert(vecArr);
+    vPtrPtr = vecArr;
+
+    refSize = -1;
+    nElem = 0;
+    for (i = 2; i < argc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+	    Blt_Free(vecArr);
+	    return TCL_ERROR;
+	}
+	/* Check that all the vectors are the same length */
+	length = v2Ptr->last - v2Ptr->first + 1;
+	if (refSize < 0) {
+	    refSize = length;
+	} else if (length != refSize) {
+	    Tcl_AppendResult(vPtr->interp, "vector \"", v2Ptr->name,
+		"\" has inconsistent length", (char *)NULL);
+	    Blt_Free(vecArr);
+	    return TCL_ERROR;
+	}
+	*vPtrPtr++ = v2Ptr;
+	nElem += refSize;
+    }
+    *vPtrPtr = NULL;
+    valueArr = Blt_Malloc(sizeof(double) * nElem);
+    if (valueArr == NULL) {
+	Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ", 
+		 Blt_Itoa(nElem), " vector elements", (char *)NULL);
+	Blt_Free(vecArr);
+	return TCL_ERROR;
+    }
+    /* Merge the values from each of the vectors into the current vector */
+    valuePtr = valueArr;
+    for (i = 0; i < refSize; i++) {
+	for (vPtrPtr = vecArr; *vPtrPtr != NULL; vPtrPtr++) {
+	    *valuePtr++ = (*vPtrPtr)->valueArr[i + (*vPtrPtr)->first];
+	}
+    }
+    Blt_Free(vecArr);
+    Blt_VectorReset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NormalizeOp --
+ *
+ *	Normalizes the vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If the density is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NormalizeOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    register int i;
+    double range;
+
+    Blt_VectorUpdateRange(vPtr);
+    range = vPtr->max - vPtr->min;
+    if (argc > 2) {
+	VectorObject *v2Ptr;
+	int isNew;
+
+	v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2], 
+		&isNew);
+	if (v2Ptr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (Blt_VectorChangeLength(v2Ptr, vPtr->length) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < vPtr->length; i++) {
+	    v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range;
+	}
+	Blt_VectorUpdateRange(v2Ptr);
+	if (!isNew) {
+	    if (v2Ptr->flush) {
+		Blt_VectorFlushCache(v2Ptr);
+	    }
+	    Blt_VectorUpdateClients(v2Ptr);
+	}
+    } else {
+	double norm;
+
+	for (i = 0; i < vPtr->length; i++) {
+	    norm = (vPtr->valueArr[i] - vPtr->min) / range;
+	    Tcl_AppendElement(interp, Blt_Dtoa(interp, norm));
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ *	Notify clients of vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given vectors differ in size,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	vector data will contain merged values of the given vectors.
+ *
+ *  x vector notify now
+ *  x vector notify always
+ *  x vector notify whenidle
+ *  x vector notify update {}
+ *  x vector notify delete {}
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    char c;
+    int length;
+
+    c = argv[2][0];
+    length = strlen(argv[2]);
+    if ((c == 'a') && (length > 1)
+	&& (strncmp(argv[2], "always", length) == 0)) {
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_ALWAYS;
+    } else if ((c == 'n') && (length > 2)
+	&& (strncmp(argv[2], "never", length) == 0)) {
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_NEVER;
+    } else if ((c == 'w') && (length > 1)
+	&& (strncmp(argv[2], "whenidle", length) == 0)) {
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_WHENIDLE;
+    } else if ((c == 'n') && (length > 2)
+	&& (strncmp(argv[2], "now", length) == 0)) {
+	/* How does this play when an update is pending? */
+	Blt_VectorNotifyClients(vPtr);
+    } else if ((c == 'c') && (length > 1)
+	&& (strncmp(argv[2], "cancel", length) == 0)) {
+	if (vPtr->notifyFlags & NOTIFY_PENDING) {
+	    vPtr->notifyFlags &= ~NOTIFY_PENDING;
+	    Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr);
+	}
+    } else if ((c == 'p') && (length > 1)
+	&& (strncmp(argv[2], "pending", length) == 0)) {
+	Blt_SetBooleanResult(interp, (vPtr->notifyFlags & NOTIFY_PENDING));
+    } else {
+	Tcl_AppendResult(interp, "bad qualifier \"", argv[2], "\": should be \
+\"always\", \"never\", \"whenidle\", \"now\", \"cancel\", or \"pending\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PopulateOp --
+ *
+ *	Creates or resizes a new vector based upon the density specified.
+ *
+ * Results:
+ *	A standard Tcl result.  If the density is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PopulateOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorObject *v2Ptr;
+    int size, density;
+    int isNew;
+    register int i, j;
+    double slice, range;
+    register double *valuePtr;
+    int count;
+
+    v2Ptr = Blt_VectorCreate(vPtr->dataPtr, argv[2], argv[2], argv[2], 
+		     &isNew);
+    if (v2Ptr == NULL) {
+	return TCL_ERROR;
+    }
+    if (vPtr->length == 0) {
+	return TCL_OK;		/* Source vector is empty. */
+    }
+    if (Tcl_GetInt(interp, argv[3], &density) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (density < 1) {
+	Tcl_AppendResult(interp, "bad density \"", argv[3], "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    size = (vPtr->length - 1) * (density + 1) + 1;
+    if (Blt_VectorChangeLength(v2Ptr, size) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    count = 0;
+    valuePtr = v2Ptr->valueArr;
+    for (i = 0; i < (vPtr->length - 1); i++) {
+	range = vPtr->valueArr[i + 1] - vPtr->valueArr[i];
+	slice = range / (double)(density + 1);
+	for (j = 0; j <= density; j++) {
+	    *valuePtr = vPtr->valueArr[i] + (slice * (double)j);
+	    valuePtr++;
+	    count++;
+	}
+    }
+    count++;
+    *valuePtr = vPtr->valueArr[i];
+    assert(count == v2Ptr->length);
+    if (!isNew) {
+	if (v2Ptr->flush) {
+	    Blt_VectorFlushCache(v2Ptr);
+	}
+	Blt_VectorUpdateClients(v2Ptr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ *	Returns a Tcl list of the range of vector values specified.
+ *
+ * Results:
+ *	A standard Tcl result.  If the given range is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned and interp->result
+ *	will contain the list of values.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RangeOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int first, last;
+    register int i;
+
+    if ((Blt_VectorGetIndex(interp, vPtr, argv[2], &first, INDEX_CHECK,
+		(Blt_VectorIndexProc **) NULL) != TCL_OK) ||
+	(Blt_VectorGetIndex(interp, vPtr, argv[3], &last, INDEX_CHECK,
+		(Blt_VectorIndexProc **) NULL) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (first > last) {
+	/* Return the list reversed */
+	for (i = last; i <= first; i++) {
+	    Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+	}
+    } else {
+	for (i = first; i <= last; i++) {
+	    Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ *	Determines if a value lies within a given range.
+ *
+ *	The value is normalized and compared against the interval
+ *	[0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ *	DBL_EPSILON is the smallest number that can be represented
+ *	on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ *	Please note, min can't be greater than max.
+ *
+ * Results:
+ *	If the value is within of the interval [min..max], 1 is 
+ *	returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(value, min, max)
+    register double value, min, max;
+{
+    double range;
+
+    range = max - min;
+    if (range < DBL_EPSILON) {
+	return (FABS(max - value) < DBL_EPSILON);
+    } else {
+	double norm;
+
+	norm = (value - min) / range;
+	return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+    }
+}
+
+enum NativeFormats {
+    FMT_UNKNOWN = -1,
+    FMT_UCHAR, FMT_CHAR,
+    FMT_USHORT, FMT_SHORT,
+    FMT_UINT, FMT_INT,
+    FMT_ULONG, FMT_LONG,
+    FMT_FLOAT, FMT_DOUBLE
+};
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetBinaryFormat
+ *
+ *      Translates a format string into a native type.  Formats may be
+ *	as follows.
+ *
+ *		signed		i1, i2, i4, i8
+ *		unsigned 	u1, u2, u4, u8
+ *		real		r4, r8, r16
+ *
+ *	But there must be a corresponding native type.  For example,
+ *	this for reading 2-byte binary integers from an instrument and
+ *	converting them to unsigned shorts or ints.
+ *
+ * -----------------------------------------------------------------------
+ */
+static enum NativeFormats
+GetBinaryFormat(interp, string, sizePtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *sizePtr;
+{
+    char c;
+
+    c = tolower(string[0]);
+    if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) {
+	Tcl_AppendResult(interp, "unknown binary format \"", string,
+	    "\": incorrect byte size", (char *)NULL);
+	return TCL_ERROR;
+    }
+    switch (c) {
+    case 'r':
+	if (*sizePtr == sizeof(double)) {
+	    return FMT_DOUBLE;
+	} else if (*sizePtr == sizeof(float)) {
+	    return FMT_FLOAT;
+	}
+	break;
+
+    case 'i':
+	if (*sizePtr == sizeof(char)) {
+	    return FMT_CHAR;
+	} else if (*sizePtr == sizeof(int)) {
+	    return FMT_INT;
+	} else if (*sizePtr == sizeof(long)) {
+	    return FMT_LONG;
+	} else if (*sizePtr == sizeof(short)) {
+	    return FMT_SHORT;
+	}
+	break;
+
+    case 'u':
+	if (*sizePtr == sizeof(unsigned char)) {
+	    return FMT_UCHAR;
+	} else if (*sizePtr == sizeof(unsigned int)) {
+	    return FMT_UINT;
+	} else if (*sizePtr == sizeof(unsigned long)) {
+	    return FMT_ULONG;
+	} else if (*sizePtr == sizeof(unsigned short)) {
+	    return FMT_USHORT;
+	}
+	break;
+
+    default:
+	Tcl_AppendResult(interp, "unknown binary format \"", string,
+	    "\": should be either i#, r#, u# (where # is size in bytes)",
+	    (char *)NULL);
+	return FMT_UNKNOWN;
+    }
+    Tcl_AppendResult(interp, "can't handle format \"", string, "\"", 
+		     (char *)NULL);
+    return FMT_UNKNOWN;
+}
+
+static int
+CopyValues(vPtr, byteArr, fmt, size, length, swap, indexPtr)
+    VectorObject *vPtr;
+    char *byteArr;
+    enum NativeFormats fmt;
+    int size;
+    int length;
+    int swap;
+    int *indexPtr;
+{
+    register int i, n;
+    int newSize;
+
+    if ((swap) && (size > 1)) {
+	int nBytes = size * length;
+	register unsigned char *p;
+	register int left, right;
+
+	for (i = 0; i < nBytes; i += size) {
+	    p = (unsigned char *)(byteArr + i);
+	    for (left = 0, right = size - 1; left < right; left++, right--) {
+		p[left] ^= p[right];
+		p[right] ^= p[left];
+		p[left] ^= p[right];
+	    }
+
+	}
+    }
+    newSize = *indexPtr + length;
+    if (newSize > vPtr->length) {
+	if (Blt_VectorChangeLength(vPtr, newSize) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+#define CopyArrayToVector(vPtr, arr) \
+    for (i = 0, n = *indexPtr; i < length; i++, n++) { \
+	(vPtr)->valueArr[n] = (double)(arr)[i]; \
+    }
+
+    switch (fmt) {
+    case FMT_CHAR:
+	CopyArrayToVector(vPtr, (char *)byteArr);
+	break;
+
+    case FMT_UCHAR:
+	CopyArrayToVector(vPtr, (unsigned char *)byteArr);
+	break;
+
+    case FMT_INT:
+	CopyArrayToVector(vPtr, (int *)byteArr);
+	break;
+
+    case FMT_UINT:
+	CopyArrayToVector(vPtr, (unsigned int *)byteArr);
+	break;
+
+    case FMT_LONG:
+	CopyArrayToVector(vPtr, (long *)byteArr);
+	break;
+
+    case FMT_ULONG:
+	CopyArrayToVector(vPtr, (unsigned long *)byteArr);
+	break;
+
+    case FMT_SHORT:
+	CopyArrayToVector(vPtr, (short int *)byteArr);
+	break;
+
+    case FMT_USHORT:
+	CopyArrayToVector(vPtr, (unsigned short int *)byteArr);
+	break;
+
+    case FMT_FLOAT:
+	CopyArrayToVector(vPtr, (float *)byteArr);
+	break;
+
+    case FMT_DOUBLE:
+	CopyArrayToVector(vPtr, (double *)byteArr);
+	break;
+
+    case FMT_UNKNOWN:
+	break;
+    }
+    *indexPtr += length;
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BinreadOp --
+ *
+ *	Reads binary values from a Tcl channel. Values are either appended
+ *	to the end of the vector or placed at a given index (using the
+ *	"-at" option), overwriting existing values.  Data is read until EOF
+ *	is found on the channel or a specified number of values are read.
+ *	(note that this is not necessarily the same as the number of bytes).
+ *
+ *	The following flags are supported:
+ *		-swap		Swap bytes
+ *		-at index	Start writing data at the index.
+ *		-format fmt	Specifies the format of the data.
+ *
+ *	This binary reader was created by Harald Kirsch (kir@iitb.fhg.de).
+ *
+ * Results:
+ *	Returns a standard Tcl result. The interpreter result will contain
+ *	the number of values (not the number of bytes) read.
+ *
+ * Caveats:
+ *	Channel reads must end on an element boundary.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BinreadOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    char *byteArr;
+    enum NativeFormats fmt;
+    int size, length, mode;
+    Tcl_Channel channel;
+    int arraySize, bytesRead;
+    int count, total;
+    int first;
+    int swap;
+    register int i;
+
+    channel = Tcl_GetChannel(interp, argv[2], &mode);
+    if (channel == NULL) {
+	return TCL_ERROR;
+    }
+    if ((mode & TCL_READABLE) == 0) {
+	Tcl_AppendResult(interp, "channel \"", argv[2],
+	    "\" wasn't opened for reading", (char *)NULL);
+	return TCL_ERROR;
+    }
+    first = vPtr->length;
+    fmt = FMT_DOUBLE;
+    size = sizeof(double);
+    swap = FALSE;
+    count = 0;
+
+    if ((argc > 3) && (argv[3][0] != '-')) {
+	long int value;
+	/* Get the number of values to read.  */
+	if (Tcl_ExprLong(interp, argv[3], &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (value < 0) {
+	    Tcl_AppendResult(interp, "count can't be negative", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	count = (int)value;
+	argc--, argv++;
+    }
+    /* Process any option-value pairs that remain.  */
+    for (i = 3; i < argc; i++) {
+	if (strcmp(argv[i], "-swap") == 0) {
+	    swap = TRUE;
+	} else if (strcmp(argv[i], "-format") == 0) {
+	    i += 1;
+	    if (i >= argc) {
+		Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1],
+		    "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    fmt = GetBinaryFormat(interp, argv[i], &size);
+	    if (fmt == FMT_UNKNOWN) {
+		return TCL_ERROR;
+	    }
+	} else if (strcmp(argv[i], "-at") == 0) {
+	    i += 1;
+	    if (i >= argc) {
+		Tcl_AppendResult(interp, "missing arg after \"", argv[i - 1],
+		    "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    if (Blt_VectorGetIndex(interp, vPtr, argv[i], &first, 0, 
+			 (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (first > vPtr->length) {
+		Tcl_AppendResult(interp, "index \"", argv[i],
+		    "\" is out of range", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	}
+    }
+
+#define BUFFER_SIZE 1024
+    if (count == 0) {
+	arraySize = BUFFER_SIZE * size;
+    } else {
+	arraySize = count * size;
+    }
+
+    byteArr = Blt_Malloc(arraySize);
+    assert(byteArr);
+
+    /* FIXME: restore old channel translation later? */
+    if (Tcl_SetChannelOption(interp, channel, "-translation",
+	    "binary") != TCL_OK) {
+	return TCL_ERROR;
+    }
+    total = 0;
+    while (!Tcl_Eof(channel)) {
+	bytesRead = Tcl_Read(channel, byteArr, arraySize);
+	if (bytesRead < 0) {
+	    Tcl_AppendResult(interp, "error reading channel: ",
+		Tcl_PosixError(interp), (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if ((bytesRead % size) != 0) {
+	    Tcl_AppendResult(interp, "error reading channel: short read",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	length = bytesRead / size;
+	if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first)
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	total += length;
+	if (count > 0) {
+	    break;
+	}
+    }
+    Blt_Free(byteArr);
+
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+
+    /* Set the result as the number of values read.  */
+    Tcl_SetResult(interp, Blt_Itoa(total), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ *	Searchs for a value in the vector. Returns the indices of all
+ *	vector elements matching a particular value.
+ *
+ * Results:
+ *	Always returns TCL_OK.  interp->result will contain a list of
+ *	the indices of array elements matching value. If no elements
+ *	match, interp->result will contain the empty string.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    double min, max;
+    register int i;
+    int wantValue;
+
+    wantValue = FALSE;
+    if ((argv[2][0] == '-') && (strcmp(argv[2], "-value") == 0)) {
+	wantValue = TRUE;
+	argv++, argc--;
+    }
+    if (Tcl_ExprDouble(interp, argv[2], &min) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    max = min;
+    if ((argc > 3) && (Tcl_ExprDouble(interp, argv[3], &max) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if ((min - max) >= DBL_EPSILON) {
+	return TCL_OK;		/* Bogus range. Don't bother looking. */
+    }
+    if (wantValue) {
+	for (i = 0; i < vPtr->length; i++) {
+	    if (InRange(vPtr->valueArr[i], min, max)) {
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, vPtr->valueArr[i]));
+	    }
+	}
+    } else {
+	for (i = 0; i < vPtr->length; i++) {
+	    if (InRange(vPtr->valueArr[i], min, max)) {
+		Tcl_AppendElement(interp, Blt_Itoa(i + vPtr->offset));
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * OffsetOp --
+ *
+ *	Queries or sets the offset of the array index from the base
+ *	address of the data array of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffsetOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    if (argc == 3) {
+	int newOffset;
+
+	if (Tcl_GetInt(interp, argv[2], &newOffset) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	vPtr->offset = newOffset;
+    }
+    Tcl_SetResult(interp, Blt_Itoa(vPtr->offset), TCL_VOLATILE);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RandomOp --
+ *
+ *	Generates random values for the length of the vector.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RandomOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+#ifdef HAVE_DRAND48
+    register int i;
+
+    for (i = 0; i < vPtr->length; i++) {
+	vPtr->valueArr[i] = drand48();
+    }
+#endif /* HAVE_DRAND48 */
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SeqOp --
+ *
+ *	Generates a sequence of values in the vector.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SeqOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    register int i;
+    double start, finish, step;
+    int fillVector;
+    int nSteps;
+
+    if (Tcl_ExprDouble(interp, argv[2], &start) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    fillVector = FALSE;
+    if ((argv[3][0] == 'e') && (strcmp(argv[3], "end") == 0)) {
+	fillVector = TRUE;
+    } else if (Tcl_ExprDouble(interp, argv[3], &finish) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    step = 1.0;
+    if ((argc > 4) && (Tcl_ExprDouble(interp, argv[4], &step) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (fillVector) {
+	nSteps = vPtr->length;
+    } else {
+	nSteps = (int)((finish - start) / step) + 1;
+    }
+    if (nSteps > 0) {
+	if (Blt_VectorChangeLength(vPtr, nSteps) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < nSteps; i++) {
+	    vPtr->valueArr[i] = start + (step * (double)i);
+	}
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ *	Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vector data is reset.  Clients of the vector are notified.
+ *	Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SetOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    int result;
+    VectorObject *v2Ptr;
+    int nElem;
+    char **elemArr;
+
+    /* The source can be either a list of expressions of another
+     * vector.  */
+    if (Tcl_SplitList(interp, argv[2], &nElem, &elemArr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    /* If there's only one element, see whether it's the name of a
+     * vector.  Otherwise, treat it as a single numeric expression. */
+
+    if ((nElem == 1) && ((v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL,
+	vPtr->dataPtr, argv[2], (char **)NULL, NS_SEARCH_BOTH)) != NULL)) {
+	if (vPtr == v2Ptr) {
+	    VectorObject *tmpPtr;
+
+	    /* 
+	     * Source and destination vectors are the same.  Copy the
+	     * source first into a temporary vector to avoid memory
+	     * overlaps. 
+	     */
+	    tmpPtr = Blt_VectorNew(vPtr->dataPtr);
+	    result = Blt_VectorDuplicate(tmpPtr, v2Ptr);
+	    if (result == TCL_OK) {
+		result = Blt_VectorDuplicate(vPtr, tmpPtr);
+	    }
+	    Blt_VectorFree(tmpPtr);
+	} else {
+	    result = Blt_VectorDuplicate(vPtr, v2Ptr);
+	}
+    } else {
+	result = CopyList(vPtr, nElem, elemArr);
+    }
+    Blt_Free(elemArr);
+
+    if (result == TCL_OK) {
+	/*
+	 * The vector has changed; so flush the array indices (they're
+	 * wrong now), find the new range of the data, and notify
+	 * the vector's clients that it's been modified.
+	 */
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return result;
+}
+
+static VectorObject **sortVectorArr;	/* Pointer to the array of values currently
+				 * being sorted. */
+static int nSortVectors;
+static int reverse;		/* Indicates the ordering of the sort. If
+				 * non-zero, the vectors are sorted in
+				 * decreasing order */
+
+static int
+CompareVectors(a, b)
+    void *a;
+    void *b;
+{
+    double delta;
+    int i;
+    int sign;
+    register VectorObject *vPtr;
+
+    sign = (reverse) ? -1 : 1;
+    for (i = 0; i < nSortVectors; i++) {
+	vPtr = sortVectorArr[i];
+	delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b];
+	if (delta < 0.0) {
+	    return (-1 * sign);
+	} else if (delta > 0.0) {
+	    return (1 * sign);
+	}
+    }
+    return 0;
+}
+
+int *
+Blt_VectorSortIndex(vPtrPtr, nVectors)
+    VectorObject **vPtrPtr;
+    int nVectors;
+{
+    int *indexArr;
+    register int i;
+    VectorObject *vPtr = *vPtrPtr;
+
+    indexArr = Blt_Malloc(sizeof(int) * vPtr->length);
+    assert(indexArr);
+    for (i = 0; i < vPtr->length; i++) {
+	indexArr[i] = i;
+    }
+    sortVectorArr = vPtrPtr;
+    nSortVectors = nVectors;
+    qsort((char *)indexArr, vPtr->length, sizeof(int),
+	    (QSortCompareProc *)CompareVectors);
+    return indexArr;
+}
+
+static int *
+SortVectors(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorObject **vPtrArray, *v2Ptr;
+    int *iArr;
+    register int i;
+
+    vPtrArray = Blt_Malloc(sizeof(VectorObject *) * (argc + 1));
+    assert(vPtrArray);
+    vPtrArray[0] = vPtr;
+    iArr = NULL;
+    for (i = 0; i < argc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+	    goto error;
+	}
+	if (v2Ptr->length != vPtr->length) {
+	    Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+		"\" is not the same size as \"", vPtr->name, "\"",
+		(char *)NULL);
+	    goto error;
+	}
+	vPtrArray[i + 1] = v2Ptr;
+    }
+    iArr = Blt_VectorSortIndex(vPtrArray, argc + 1);
+  error:
+    Blt_Free(vPtrArray);
+    return iArr;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ *	Sorts the vector object and any other vectors according to
+ *	sorting order of the vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the auxiliary vectors are
+ *	a different size than the sorted vector object, TCL_ERROR is
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vectors are sorted.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+static int
+SortOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    int *iArr;
+    double *mergeArr;
+    VectorObject *v2Ptr;
+    int refSize, nBytes;
+    int result;
+    register int i, n;
+
+    reverse = FALSE;
+    if ((argc > 2) && (argv[2][0] == '-')) {
+	int length;
+
+	length = strlen(argv[2]);
+	if ((length > 1) && (strncmp(argv[2], "-reverse", length) == 0)) {
+	    reverse = TRUE;
+	} else {
+	    Tcl_AppendResult(interp, "unknown flag \"", argv[2],
+		"\": should be \"-reverse\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	argc--, argv++;
+    }
+    if (argc > 2) {
+	iArr = SortVectors(vPtr, interp, argc - 2, argv + 2);
+    } else {
+	iArr = Blt_VectorSortIndex(&vPtr, 1);
+    }
+    if (iArr == NULL) {
+	return TCL_ERROR;
+    }
+    refSize = vPtr->length;
+
+    /*
+     * Create an array to store a copy of the current values of the
+     * vector. We'll merge the values back into the vector based upon
+     * the indices found in the index array.
+     */
+    nBytes = sizeof(double) * refSize;
+    mergeArr = Blt_Malloc(nBytes);
+    assert(mergeArr);
+    memcpy((char *)mergeArr, (char *)vPtr->valueArr, nBytes);
+    for (n = 0; n < refSize; n++) {
+	vPtr->valueArr[n] = mergeArr[iArr[n]];
+    }
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+
+    /* Now sort any other vectors in the same fashion.  The vectors
+     * must be the same size as the iArr though.  */
+    result = TCL_ERROR;
+    for (i = 2; i < argc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, argv[i], &v2Ptr) != TCL_OK) {
+	    goto error;
+	}
+	if (v2Ptr->length != refSize) {
+	    Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+		"\" is not the same size as \"", vPtr->name, "\"",
+		(char *)NULL);
+	    goto error;
+	}
+	memcpy((char *)mergeArr, (char *)v2Ptr->valueArr, nBytes);
+	for (n = 0; n < refSize; n++) {
+	    v2Ptr->valueArr[n] = mergeArr[iArr[n]];
+	}
+	Blt_VectorUpdateClients(v2Ptr);
+	if (v2Ptr->flush) {
+	    Blt_VectorFlushCache(v2Ptr);
+	}
+    }
+    result = TCL_OK;
+  error:
+    Blt_Free(mergeArr);
+    Blt_Free(iArr);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InstExprOp --
+ *
+ *	Computes the result of the expression which may be
+ *	either a scalar (single value) or vector (list of values).
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InstExprOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    if (Blt_ExprVector(interp, argv[2], (Blt_Vector *) vPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ArithOp --
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vector data is reset.  Clients of the vector are notified.
+ *	Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArithOp(vPtr, interp, argc, argv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int argc;			/* Not used. */
+    char **argv;
+{
+    register double value;
+    register int i;
+    VectorObject *v2Ptr;
+
+    v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, argv[2], 
+		(char **)NULL, NS_SEARCH_BOTH);
+    if (v2Ptr != NULL) {
+	register int j;
+	int length;
+
+	length = v2Ptr->last - v2Ptr->first + 1;
+	if (length != vPtr->length) {
+	    Tcl_AppendResult(interp, "vectors \"", argv[0], "\" and \"",
+		argv[2], "\" are not the same length", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	switch (argv[1][0]) {
+	case '*':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] * v2Ptr->valueArr[j];
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '/':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] / v2Ptr->valueArr[j];
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '-':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] - v2Ptr->valueArr[j];
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '+':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] + v2Ptr->valueArr[j];
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+	}
+    } else {
+	double scalar;
+
+	if (Tcl_ExprDouble(interp, argv[2], &scalar) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	switch (argv[1][0]) {
+	case '*':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] * scalar;
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '/':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] / scalar;
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '-':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] - scalar;
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+
+	case '+':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] + scalar;
+		Tcl_AppendElement(interp, Blt_Dtoa(interp, value));
+	    }
+	    break;
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorInstCmd --
+ *
+ *	Parses and invokes the appropriate vector instance command
+ *	option.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec vectorInstOps[] =
+{
+    {"*", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"+", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"-", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"/", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"append", 1, (Blt_Op)AppendOp, 3, 0, "item ?item...?",},
+    {"binread", 1, (Blt_Op)BinreadOp, 3, 0, "channel ?numValues? ?flags?",},
+    {"clear", 1, (Blt_Op)ClearOp, 2, 2, "",},
+    {"delete", 2, (Blt_Op)DeleteOp, 2, 0, "index ?index...?",},
+    {"dup", 2, (Blt_Op)DupOp, 3, 0, "vecName",},
+    {"expr", 1, (Blt_Op)InstExprOp, 3, 3, "expression",},
+    {"index", 1, (Blt_Op)IndexOp, 3, 4, "index ?value?",},
+    {"length", 1, (Blt_Op)LengthOp, 2, 3, "?newSize?",},
+    {"merge", 1, (Blt_Op)MergeOp, 3, 0, "vecName ?vecName...?",},
+    {"normalize", 3, (Blt_Op)NormalizeOp, 2, 3, "?vecName?",},	/*Deprecated*/
+    {"notify", 3, (Blt_Op)NotifyOp, 3, 3, "keyword",},
+    {"offset", 2, (Blt_Op)OffsetOp, 2, 3, "?offset?",},
+    {"populate", 1, (Blt_Op)PopulateOp, 4, 4, "vecName density",},
+    {"random", 4, (Blt_Op)RandomOp, 2, 2, "",},	/*Deprecated*/
+    {"range", 4, (Blt_Op)RangeOp, 4, 4, "first last",},
+    {"search", 3, (Blt_Op)SearchOp, 3, 4, "?-value? value ?value?",},
+    {"seq", 3, (Blt_Op)SeqOp, 4, 5, "start end ?step?",},
+    {"set", 3, (Blt_Op)SetOp, 3, 3, "list",},
+    {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-reverse? ?vecName...?",},
+    {"variable", 1, (Blt_Op)MapOp, 2, 3, "?varName?",},
+};
+
+static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_VectorInstCmd(clientData, interp, argc, argv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+    VectorObject *vPtr = clientData;
+
+    vPtr->first = 0;
+    vPtr->last = vPtr->length - 1;
+    proc = Blt_GetOp(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, argc, argv,
+	0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (vPtr, interp, argc, argv);
+}
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorVarTrace --
+ *
+ * Results:
+ *	Returns NULL on success.  Only called from a variable trace.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+char *
+Blt_VectorVarTrace(clientData, interp, part1, part2, flags)
+    ClientData clientData;	/* File output information. */
+    Tcl_Interp *interp;
+    char *part1, *part2;
+    int flags;
+{
+    VectorObject *vPtr = clientData;
+    char string[TCL_DOUBLE_SPACE + 1];
+#define MAX_ERR_MSG	1023
+    static char message[MAX_ERR_MSG + 1];
+    Blt_VectorIndexProc *indexProc;
+    int varFlags;
+    int first, last;
+
+    if (part2 == NULL) {
+	if (flags & TCL_TRACE_UNSETS) {
+	    Blt_Free(vPtr->arrayName);
+	    vPtr->arrayName = NULL;
+	    vPtr->varNsPtr = NULL;
+	    if (vPtr->freeOnUnset) {
+		Blt_VectorFree(vPtr);
+	    }
+	}
+	return NULL;
+    }
+    if (Blt_VectorGetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, 
+		&indexProc) != TCL_OK) {
+	goto error;
+    }
+    first = vPtr->first, last = vPtr->last;
+    varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags);
+    if (flags & TCL_TRACE_WRITES) {
+	double value;
+	char *newValue;
+
+	if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */
+	    return "read-only index";
+	}
+	newValue = Tcl_GetVar2(interp, part1, part2, varFlags);
+	if (newValue == NULL) {
+	    goto error;
+	}
+	if (Tcl_ExprDouble(interp, newValue, &value) != TCL_OK) {
+	    if ((last == first) && (first >= 0)) {
+		/* Single numeric index. Reset the array element to
+		 * its old value on errors */
+		Tcl_PrintDouble(interp, vPtr->valueArr[first], string);
+		Tcl_SetVar2(interp, part1, part2, string, varFlags);
+	    }
+	    goto error;
+	}
+	if (first == vPtr->length) {
+	    if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+		return "error resizing vector";
+	    }
+	}
+	/* Set possibly an entire range of values */
+	ReplicateValue(vPtr, first, last, value);
+    } else if (flags & TCL_TRACE_READS) {
+	double value;
+
+	if (vPtr->length == 0) {
+	    if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) {
+		goto error;
+	    }
+	    return NULL;
+	}
+	if  (first == vPtr->length) {
+	    return "write-only index";
+	}
+	if (first == last) {
+	    if (first >= 0) {
+		value = vPtr->valueArr[first];
+	    } else {
+		vPtr->first = 0, vPtr->last = vPtr->length - 1;
+		value = (*indexProc) ((Blt_Vector *) vPtr);
+	    }
+	    Tcl_PrintDouble(interp, value, string);
+	    if (Tcl_SetVar2(interp, part1, part2, string, varFlags) 
+		== NULL) {
+		goto error;
+	    }
+	} else {
+	    Tcl_DString dString;
+	    char *result;
+
+	    Tcl_DStringInit(&dString);
+	    GetValues(vPtr, first, last, &dString);
+	    result = Tcl_SetVar2(interp, part1, part2, 
+		Tcl_DStringValue(&dString), varFlags);
+	    Tcl_DStringFree(&dString);
+	    if (result == NULL) {
+		goto error;
+	    }
+	}
+    } else if (flags & TCL_TRACE_UNSETS) {
+	register int i, j;
+
+	if ((first == vPtr->length) || (first == SPECIAL_INDEX)) {
+	    return "special vector index";
+	}
+	/*
+	 * Collapse the vector from the point of the first unset element.
+	 * Also flush any array variable entries so that the shift is
+	 * reflected when the array variable is read.
+	 */
+	for (i = first, j = last + 1; j < vPtr->length; i++, j++) {
+	    vPtr->valueArr[i] = vPtr->valueArr[j];
+	}
+	vPtr->length -= ((last - first) + 1);
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+    } else {
+	return "unknown variable trace flag";
+    }
+    if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) {
+	Blt_VectorUpdateClients(vPtr);
+    }
+    Tcl_ResetResult(interp);
+    return NULL;
+
+ error: 
+    strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG);
+    message[MAX_ERR_MSG] = '\0';
+    return message;
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 */
Index: trunk/kitgen/8.x/blt/generic/bltVecInt.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltVecInt.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltVecInt.h	(revision 175)
@@ -0,0 +1,234 @@
+/*
+ * bltVecInt.h --
+ *
+ *	This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+
+#include "bltInt.h"
+#include "bltHash.h"
+#include "bltChain.h"
+
+#define VECTOR_THREAD_KEY	"BLT Vector Data"
+#define VECTOR_MAGIC		((unsigned int) 0x46170277)
+
+/* These defines allow parsing of different types of indices */
+
+#define INDEX_SPECIAL	(1<<0)	/* Recognize "min", "max", and "++end" as
+				 * valid indices */
+#define INDEX_COLON	(1<<1)	/* Also recognize a range of indices 
+				 * separated by a colon */
+#define INDEX_CHECK	(1<<2)	/* Verify that the specified index or 
+				 * range of indices are within limits */
+#define INDEX_ALL_FLAGS    (INDEX_SPECIAL | INDEX_COLON | INDEX_CHECK)
+
+#define SPECIAL_INDEX		-2
+
+
+typedef struct {
+    Blt_HashTable vectorTable;	/* Table of vectors */
+    Blt_HashTable mathProcTable; /* Table of vector math functions */
+    Blt_HashTable indexProcTable;
+    Tcl_Interp *interp;
+    unsigned int nextId;
+} VectorInterpData;
+
+/*
+ * VectorObject --
+ *
+ *	A vector is an array of double precision values.  It can be
+ *	accessed through a Tcl command, a Tcl array variable, or C
+ *	API. The storage for the array points initially to a
+ *	statically allocated buffer, but to malloc-ed memory if more
+ *	is necessary.
+ *
+ *	Vectors can be shared by several clients (for example, two
+ *	different graph widgets).  The data is shared. When a client
+ *	wants to use a vector, it allocates a vector identifier, which
+ *	identifies the client.  Clients use this ID to specify a
+ *	callback routine to be invoked whenever the vector is modified
+ *	or destroyed.  Whenever the vector is updated or destroyed,
+ *	each client is notified of the change by their callback
+ *	routine.
+ */
+
+typedef struct {
+
+    /*
+     * If you change these fields, make sure you change the definition
+     * of Blt_Vector in bltInt.h and blt.h too.
+     */
+
+    double *valueArr;		/* Array of values (malloc-ed) */
+
+    int length;			/* Current number of values in the array. */
+
+    int size;			/* Maximum number of values that can be stored
+				 * in the value array. */
+
+    double min, max;		/* Minimum and maximum values in the vector */
+
+    int dirty;			/* Indicates if the vector has been updated */
+
+    int reserved;
+
+    /* The following fields are local to this module  */
+
+    char *name;			/* The namespace-qualified name of the vector.
+				 * It points to the hash key allocated for the
+				 * entry in the vector hash table. */
+
+    VectorInterpData *dataPtr;
+    Tcl_Interp *interp;		/* Interpreter associated with the
+				 * vector */
+
+    Blt_HashEntry *hashPtr;	/* If non-NULL, pointer in a hash table to
+				 * track the vectors in use. */
+
+    Tcl_FreeProc *freeProc;	/* Address of procedure to call to
+				 * release storage for the value
+				 * array, Optionally can be one of the
+				 * following: TCL_STATIC, TCL_DYNAMIC,
+				 * or TCL_VOLATILE. */
+
+    char *arrayName;		/* The name of the Tcl array variable
+				 * mapped to the vector
+				 * (malloc'ed). If NULL, indicates
+				 * that the vector isn't mapped to any
+				 * variable */
+
+    Tcl_Namespace *varNsPtr;	/* Namespace context of the Tcl variable
+				 * associated with the vector. This is
+				 * needed to reset the indices of the array
+				 * variable. */
+
+    Tcl_Namespace *nsPtr;	/* Namespace context of the vector itself. */
+
+    int offset;			/* Offset from zero of the vector's
+				 * starting index */
+
+    Tcl_Command cmdToken;	/* Token for vector's Tcl command. */
+
+    Blt_Chain *chainPtr;	/* List of clients using this vector */
+
+    int notifyFlags;		/* Notification flags. See definitions
+				 * below */
+
+    int varFlags;		/* Indicate if the variable is global,
+				 * namespace, or local */
+
+    int freeOnUnset;		/* For backward compatibility only: If
+				 * non-zero, free the vector when its
+				 * variable is unset. */
+    int flush;
+
+    int first, last;		/* Selected region of vector. This is used
+				 * mostly for the math routines */
+} VectorObject;
+
+#define NOTIFY_UPDATED		((int)BLT_VECTOR_NOTIFY_UPDATE)
+#define NOTIFY_DESTROYED	((int)BLT_VECTOR_NOTIFY_DESTROY)
+
+#define NOTIFY_NEVER		(1<<3)	/* Never notify clients of updates to
+					 * the vector */
+#define NOTIFY_ALWAYS		(1<<4)	/* Notify clients after each update
+					 * of the vector is made */
+#define NOTIFY_WHENIDLE		(1<<5)	/* Notify clients at the next idle point
+					 * that the vector has been updated. */
+
+#define NOTIFY_PENDING		(1<<6)	/* A do-when-idle notification of the
+					 * vector's clients is pending. */
+#define NOTIFY_NOW		(1<<7)	/* Notify clients of changes once
+					 * immediately */
+
+#define NOTIFY_WHEN_MASK	(NOTIFY_NEVER|NOTIFY_ALWAYS|NOTIFY_WHENIDLE)
+
+#define UPDATE_RANGE		(1<<9)	/* The data of the vector has changed.
+					 * Update the min and max limits when
+					 * they are needed */
+
+extern void Blt_VectorInstallSpecialIndices
+	_ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern void Blt_VectorInstallMathFunctions 
+	_ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern void Blt_VectorUninstallMathFunctions 
+	_ANSI_ARGS_((Blt_HashTable *tablePtr));
+
+extern VectorInterpData *Blt_VectorGetInterpData 
+	_ANSI_ARGS_((Tcl_Interp *interp));
+
+extern VectorObject *Blt_VectorNew _ANSI_ARGS_((VectorInterpData *dataPtr));
+
+extern int Blt_VectorDuplicate _ANSI_ARGS_((VectorObject *destPtr,
+	VectorObject *srcPtr));
+
+extern int Blt_VectorChangeLength _ANSI_ARGS_((VectorObject *vPtr, 
+	int length));
+
+extern VectorObject *Blt_VectorParseElement _ANSI_ARGS_((Tcl_Interp *interp, 
+	VectorInterpData *dataPtr, CONST char *start, char **endPtr, 
+	int flags));
+
+extern void Blt_VectorFree _ANSI_ARGS_((VectorObject *vPtr));
+
+extern int *Blt_VectorSortIndex _ANSI_ARGS_((VectorObject **vPtrPtr, 
+	int nVectors));
+
+extern int Blt_VectorLookupName _ANSI_ARGS_((VectorInterpData *dataPtr, 
+	char *vecName, VectorObject **vPtrPtr));
+
+extern VectorObject *Blt_VectorCreate _ANSI_ARGS_((VectorInterpData *dataPtr, 
+	CONST char *name, CONST char *cmdName, CONST char *varName, 
+	int *newPtr));
+
+extern void Blt_VectorUpdateRange _ANSI_ARGS_((VectorObject *vPtr));
+
+extern void Blt_VectorUpdateClients _ANSI_ARGS_((VectorObject *vPtr));
+
+extern void Blt_VectorFlushCache _ANSI_ARGS_((VectorObject *vPtr));
+
+extern int Blt_VectorReset _ANSI_ARGS_((VectorObject *vPtr, double *dataArr,
+	int nValues, int arraySize, Tcl_FreeProc *freeProc));
+
+extern int  Blt_VectorGetIndex _ANSI_ARGS_((Tcl_Interp *interp, 
+	VectorObject *vPtr, CONST char *string, int *indexPtr, int flags,
+	Blt_VectorIndexProc **procPtrPtr));
+
+extern int  Blt_VectorGetIndexRange _ANSI_ARGS_((Tcl_Interp *interp, 
+	VectorObject *vPtr, CONST char *string, int flags, 
+	Blt_VectorIndexProc **procPtrPtr));
+
+extern int Blt_VectorMapVariable _ANSI_ARGS_((Tcl_Interp *interp, 
+	VectorObject *vPtr, CONST char *name));
+
+#if (TCL_MAJOR_VERSION == 7) 
+extern Tcl_CmdProc Blt_VectorInstCmd;
+#else 
+extern Tcl_ObjCmdProc Blt_VectorInstCmd;
+#endif
+
+extern Tcl_VarTraceProc Blt_VectorVarTrace;
+
+extern Tcl_IdleProc Blt_VectorNotifyClients;
Index: trunk/kitgen/8.x/blt/generic/bltVecObjCmd.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltVecObjCmd.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltVecObjCmd.c	(revision 175)
@@ -0,0 +1,2084 @@
+
+/*
+ * bltVecCmd.c --
+ *
+ *	This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ *	o Add H. Kirsch's vector binary read operation
+ *		x binread file0
+ *		x binread -file file0
+ *
+ *	o Add ASCII/binary file reader
+ *		x read fileName
+ *
+ *	o Allow Tcl-based client notifications.
+ *		vector x
+ *		x notify call Display
+ *		x notify delete Display
+ *		x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+
+#if (TCL_MAJOR_VERSION > 7) 
+
+static 
+int GetDouble(interp, objPtr, valuePtr)
+    Tcl_Interp *interp;
+    Tcl_Obj *objPtr;
+    double *valuePtr;
+{
+    /* First try to extract the value as a double precision number. */
+    if (Tcl_GetDoubleFromObj(interp, objPtr, valuePtr) == TCL_OK) {
+	return TCL_OK;
+    }
+    Tcl_ResetResult(interp);
+
+    /* Then try to parse it as an expression. */
+    if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) {
+	return TCL_OK;
+    }
+    return TCL_ERROR;
+}
+
+static Tcl_Obj *
+GetValues(VectorObject *vPtr, int first, int last)
+{ 
+    register int i; 
+    Tcl_Obj *listObjPtr;
+
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    for (i = first; i <= last; i++) { 
+	Tcl_ListObjAppendElement(vPtr->interp, listObjPtr, 
+				 Tcl_NewDoubleObj(vPtr->valueArr[i]));
+    } 
+    return listObjPtr;
+}
+
+static void
+ReplicateValue(vPtr, first, last, value) 
+    VectorObject *vPtr;
+    int first, last;
+    double value;
+{ 
+    register int i;
+ 
+    for (i = first; i <= last; i++) { 
+	vPtr->valueArr[i] = value; 
+    } 
+    vPtr->notifyFlags |= UPDATE_RANGE; 
+}
+
+static int
+CopyList(vPtr, objc, objv)
+    VectorObject *vPtr;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+    double value;
+
+    if (Blt_VectorChangeLength(vPtr, objc) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (i = 0; i < objc; i++) {
+	if (GetDouble(vPtr->interp, objv[i], &value) != TCL_OK) {
+	    Blt_VectorChangeLength(vPtr, i);
+	    return TCL_ERROR;
+	}
+	vPtr->valueArr[i] = value;
+    }
+    return TCL_OK;
+}
+
+static int
+AppendVector(destPtr, srcPtr)
+    VectorObject *destPtr, *srcPtr;
+{
+    int nBytes;
+    int oldSize, newSize;
+
+    oldSize = destPtr->length;
+    newSize = oldSize + srcPtr->last - srcPtr->first + 1;
+    if (Blt_VectorChangeLength(destPtr, newSize) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    nBytes = (newSize - oldSize) * sizeof(double);
+    memcpy((char *)(destPtr->valueArr + oldSize),
+	(srcPtr->valueArr + srcPtr->first), nBytes);
+    destPtr->notifyFlags |= UPDATE_RANGE;
+    return TCL_OK;
+}
+
+static int
+AppendList(vPtr, objc, objv)
+    VectorObject *vPtr;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int count;
+    register int i;
+    double value;
+    int oldSize;
+
+    oldSize = vPtr->length;
+    if (Blt_VectorChangeLength(vPtr, vPtr->length + objc) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    count = oldSize;
+    for (i = 0; i < objc; i++) {
+	if (GetDouble(vPtr->interp, objv[i], &value) != TCL_OK) {
+	    Blt_VectorChangeLength(vPtr, count);
+	    return TCL_ERROR;
+	}
+	vPtr->valueArr[count++] = value;
+    }
+    vPtr->notifyFlags |= UPDATE_RANGE;
+    return TCL_OK;
+}
+
+/* Vector instance option commands */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ *	Appends one of more Tcl lists of values, or vector objects
+ *	onto the end of the current vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If a current vector can't be created,
+ *      resized, any of the named vectors can't be found, or one of
+ *	lists of values is invalid, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	Clients of current vector will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+AppendOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+    int result;
+    VectorObject *v2Ptr;
+
+    for (i = 2; i < objc; i++) {
+	v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, 
+	       Tcl_GetString(objv[i]), (char **)NULL, NS_SEARCH_BOTH);
+	if (v2Ptr != NULL) {
+	    result = AppendVector(vPtr, v2Ptr);
+	} else {
+	    int nElem;
+	    Tcl_Obj **elemObjArr;
+
+	    if (Tcl_ListObjGetElements(interp, objv[i], &nElem, &elemObjArr) 
+		!= TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    result = AppendList(vPtr, nElem, elemObjArr);
+	}
+	if (result != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (objc > 2) {
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ClearOp --
+ *
+ *	Deletes all the accumulated array indices for the Tcl array
+ *	associated will the vector.  This routine can be used to
+ *	free excess memory from a large vector.
+ *
+ * Results:
+ *	Always returns TCL_OK.
+ *
+ * Side Effects:
+ *	Memory used for the entries of the Tcl array variable is freed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;	/* Not used. */
+{
+    Blt_VectorFlushCache(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ *	Deletes the given indices from the vector.  If no indices are
+ *	provided the entire vector is deleted.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given indices is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	The clients of the vector will be notified of the vector
+ *	deletions.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    unsigned char *unsetArr;
+    register int i, j;
+    register int count;
+    char *string;
+
+    /* FIXME: Don't delete vector with no indices.  */
+    if (objc == 2) {
+	Blt_VectorFree(vPtr);
+	return TCL_OK;
+    }
+    /*
+     * Allocate an "unset" bitmap the size of the vector.  
+     */
+    unsetArr = Blt_Calloc(sizeof(unsigned char), (vPtr->length + 7) / 8);
+    assert(unsetArr);
+
+#define SetBit(i) \
+    unsetArr[(i) >> 3] |= (1 << ((i) & 0x07))
+#define GetBit(i) \
+    (unsetArr[(i) >> 3] & (1 << ((i) & 0x07)))
+
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (Blt_VectorGetIndexRange(interp, vPtr, string, 
+		(INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL) 
+		!= TCL_OK) {
+	    Blt_Free(unsetArr);
+	    return TCL_ERROR;
+	}
+	for (j = vPtr->first; j <= vPtr->last; j++) {
+	    SetBit(j);		/* Mark the range of elements for deletion. */
+	}
+    }
+    count = 0;
+    for (i = 0; i < vPtr->length; i++) {
+	if (GetBit(i)) {
+	    continue;		/* Skip elements marked for deletion. */
+	}
+	if (count < i) {
+	    vPtr->valueArr[count] = vPtr->valueArr[i];
+	}
+	count++;
+    }
+    Blt_Free(unsetArr);
+    vPtr->length = count;
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DupOp --
+ *
+ *	Creates one or more duplicates of the vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If a new vector can't be created,
+ *      or and existing vector resized, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ *	Clients of existing vectors will be notified of the change.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DupOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;		/* Not used. */
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    VectorObject *v2Ptr;
+    int isNew;
+    register int i;
+    char *string;
+
+    for (i = 2; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string,&isNew);
+	if (v2Ptr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (v2Ptr == vPtr) {
+	    continue;
+	}
+	if (Blt_VectorDuplicate(v2Ptr, vPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (!isNew) {
+	    if (v2Ptr->flush) {
+		Blt_VectorFlushCache(v2Ptr);
+	    }
+	    Blt_VectorUpdateClients(v2Ptr);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ *	Sets or reads the value of the index.  This simulates what the
+ *	vector's variable does.
+ *
+ * Results:
+ *	A standard Tcl result.  If the index is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *	Otherwise interp->result will contain the values.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+IndexOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int first, last;
+    char *string;
+
+    string = Tcl_GetString(objv[2]);
+    if (Blt_VectorGetIndexRange(interp, vPtr, string, INDEX_ALL_FLAGS, 
+		(Blt_VectorIndexProc **) NULL) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    first = vPtr->first, last = vPtr->last;
+    if (objc == 3) {
+	Tcl_Obj *listObjPtr;
+
+	if (first == vPtr->length) {
+	    Tcl_AppendResult(interp, "can't get index \"", string, "\"",
+		(char *)NULL);
+	    return TCL_ERROR;	/* Can't read from index "++end" */
+	}
+	listObjPtr = GetValues(vPtr, first, last);
+	Tcl_SetObjResult(interp, listObjPtr);
+    } else {
+	double value;
+
+	/* FIXME: huh? Why set values here?.  */
+	if (first == SPECIAL_INDEX) {
+	    Tcl_AppendResult(interp, "can't set index \"", string, "\"",
+		(char *)NULL);
+	    return TCL_ERROR;	/* Tried to set "min" or "max" */
+	}
+	if (GetDouble(vPtr->interp, objv[3], &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (first == vPtr->length) {
+	    if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	}
+	ReplicateValue(vPtr, first, last, value);
+	Tcl_SetObjResult(interp, objv[3]);
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * LengthOp --
+ *
+ *	Returns the length of the vector.  If a new size is given, the
+ *	vector is resized to the new vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If the new length is invalid,
+ *	interp->result will an error message and TCL_ERROR is returned.
+ *	Otherwise interp->result will contain the length of the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+static int
+LengthOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 3) {
+	int size;
+
+	if (Tcl_GetIntFromObj(interp, objv[2], &size) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (size < 0) {
+	    Tcl_AppendResult(interp, "bad vector size \"", 
+		Tcl_GetString(objv[2]), "\"", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(vPtr->length));
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ *	Queries or sets the offset of the array index from the base
+ *	address of the data array of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    if (objc > 2) {
+	if (Blt_VectorMapVariable(interp, vPtr, Tcl_GetString(objv[2])) 
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (vPtr->arrayName != NULL) {
+	Tcl_SetResult(interp, vPtr->arrayName, TCL_VOLATILE);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * MergeOp --
+ *
+ *	Merges the values from the given vectors to the current vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given vectors differ in size,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	vector data will contain merged values of the given vectors.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MergeOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    VectorObject *v2Ptr;
+    VectorObject **vecArr;
+    register VectorObject **vPtrPtr;
+    int refSize, length, nElem;
+    register int i;
+    double *valuePtr, *valueArr;
+    
+    /* Allocate an array of vector pointers of each vector to be
+     * merged in the current vector.  */
+    vecArr = Blt_Malloc(sizeof(VectorObject *) * objc);
+    assert(vecArr);
+    vPtrPtr = vecArr;
+
+    refSize = -1;
+    nElem = 0;
+    for (i = 2; i < objc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+		!= TCL_OK) {
+	    Blt_Free(vecArr);
+	    return TCL_ERROR;
+	}
+	/* Check that all the vectors are the same length */
+	length = v2Ptr->last - v2Ptr->first + 1;
+	if (refSize < 0) {
+	    refSize = length;
+	} else if (length != refSize) {
+	    Tcl_AppendResult(vPtr->interp, "vectors \"", vPtr->name,
+		"\" and \"", v2Ptr->name, "\" differ in length",
+		(char *)NULL);
+	    Blt_Free(vecArr);
+	    return TCL_ERROR;
+	}
+	*vPtrPtr++ = v2Ptr;
+	nElem += refSize;
+    }
+    *vPtrPtr = NULL;
+
+    valueArr = Blt_Malloc(sizeof(double) * nElem);
+    if (valueArr == NULL) {
+	Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ",
+		 Blt_Itoa(nElem), " vector elements", (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Merge the values from each of the vectors into the current vector */
+    valuePtr = valueArr;
+    for (i = 0; i < refSize; i++) {
+	for (vPtrPtr = vecArr; *vPtrPtr != NULL; vPtrPtr++) {
+	    *valuePtr++ = (*vPtrPtr)->valueArr[i + (*vPtrPtr)->first];
+	}
+    }
+    Blt_Free(vecArr);
+    Blt_VectorReset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NormalizeOp --
+ *
+ *	Normalizes the vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If the density is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NormalizeOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+    double range;
+
+    Blt_VectorUpdateRange(vPtr);
+    range = vPtr->max - vPtr->min;
+    if (objc > 2) {
+	VectorObject *v2Ptr;
+	int isNew;
+	char *string;
+
+	string = Tcl_GetString(objv[2]);
+	v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string, 
+				 &isNew);
+	if (v2Ptr == NULL) {
+	    return TCL_ERROR;
+	}
+	if (Blt_VectorChangeLength(v2Ptr, vPtr->length) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < vPtr->length; i++) {
+	    v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range;
+	}
+	Blt_VectorUpdateRange(v2Ptr);
+	if (!isNew) {
+	    if (v2Ptr->flush) {
+		Blt_VectorFlushCache(v2Ptr);
+	    }
+	    Blt_VectorUpdateClients(v2Ptr);
+	}
+    } else {
+	double norm;
+	Tcl_Obj *listObjPtr;
+
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	for (i = 0; i < vPtr->length; i++) {
+	    norm = (vPtr->valueArr[i] - vPtr->min) / range;
+	    Tcl_ListObjAppendElement(interp, listObjPtr, 
+		Tcl_NewDoubleObj(norm));
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ *	Notify clients of vector.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the given vectors differ in size,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	vector data will contain merged values of the given vectors.
+ *
+ *  x vector notify now
+ *  x vector notify always
+ *  x vector notify whenidle
+ *  x vector notify update {}
+ *  x vector notify delete {}
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int option;
+    int bool;
+    enum optionIndices {
+	OPTION_ALWAYS, OPTION_NEVER, OPTION_WHENIDLE, 
+	OPTION_NOW, OPTION_CANCEL, OPTION_PENDING
+    };
+    static char *optionArr[] = {
+	"always", "never", "whenidle", "now", "cancel", "pending", NULL
+    };
+
+    if (Tcl_GetIndexFromObj(interp, objv[2], optionArr, "qualifier", TCL_EXACT,
+	    &option) != TCL_OK) {
+	return TCL_OK;
+    }
+    switch (option) {
+    case OPTION_ALWAYS:
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_ALWAYS;
+	break;
+    case OPTION_NEVER:
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_NEVER;
+	break;
+    case OPTION_WHENIDLE:
+	vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+	vPtr->notifyFlags |= NOTIFY_WHENIDLE;
+	break;
+    case OPTION_NOW:
+	/* FIXME: How does this play when an update is pending? */
+	Blt_VectorNotifyClients(vPtr);
+	break;
+    case OPTION_CANCEL:
+	if (vPtr->notifyFlags & NOTIFY_PENDING) {
+	    vPtr->notifyFlags &= ~NOTIFY_PENDING;
+	    Tcl_CancelIdleCall(Blt_VectorNotifyClients, (ClientData)vPtr);
+	}
+	break;
+    case OPTION_PENDING:
+	bool = (vPtr->notifyFlags & NOTIFY_PENDING);
+	Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
+	break;
+    }	
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PopulateOp --
+ *
+ *	Creates or resizes a new vector based upon the density specified.
+ *
+ * Results:
+ *	A standard Tcl result.  If the density is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PopulateOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    VectorObject *v2Ptr;
+    int size, density;
+    int isNew;
+    register int i, j;
+    double slice, range;
+    register double *valuePtr;
+    int count;
+    char *string;
+
+    string = Tcl_GetString(objv[2]);
+    v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string, &isNew);
+    if (v2Ptr == NULL) {
+	return TCL_ERROR;
+    }
+    if (vPtr->length == 0) {
+	return TCL_OK;		/* Source vector is empty. */
+    }
+    if (Tcl_GetIntFromObj(interp, objv[3], &density) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (density < 1) {
+	Tcl_AppendResult(interp, "bad density \"", Tcl_GetString(objv[3]), 
+		"\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    size = (vPtr->length - 1) * (density + 1) + 1;
+    if (Blt_VectorChangeLength(v2Ptr, size) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    count = 0;
+    valuePtr = v2Ptr->valueArr;
+    for (i = 0; i < (vPtr->length - 1); i++) {
+	range = vPtr->valueArr[i + 1] - vPtr->valueArr[i];
+	slice = range / (double)(density + 1);
+	for (j = 0; j <= density; j++) {
+	    *valuePtr = vPtr->valueArr[i] + (slice * (double)j);
+	    valuePtr++;
+	    count++;
+	}
+    }
+    count++;
+    *valuePtr = vPtr->valueArr[i];
+    assert(count == v2Ptr->length);
+    if (!isNew) {
+	if (v2Ptr->flush) {
+	    Blt_VectorFlushCache(v2Ptr);
+	}
+	Blt_VectorUpdateClients(v2Ptr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ *	Returns a Tcl list of the range of vector values specified.
+ *
+ * Results:
+ *	A standard Tcl result.  If the given range is invalid, TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned and interp->result
+ *	will contain the list of values.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RangeOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    Tcl_Obj *listObjPtr;
+    int first, last;
+    register int i;
+
+    if ((Blt_VectorGetIndex(interp, vPtr, Tcl_GetString(objv[2]), &first, 
+		INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK) ||
+	(Blt_VectorGetIndex(interp, vPtr, Tcl_GetString(objv[3]), &last, 
+		INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (first > last) {
+	/* Return the list reversed */
+	for (i = last; i <= first; i++) {
+	    Tcl_ListObjAppendElement(interp, listObjPtr, 
+		Tcl_NewDoubleObj(vPtr->valueArr[i]));
+	}
+    } else {
+	for (i = first; i <= last; i++) {
+	    Tcl_ListObjAppendElement(interp, listObjPtr, 
+		Tcl_NewDoubleObj(vPtr->valueArr[i]));
+	}
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ *	Determines if a value lies within a given range.
+ *
+ *	The value is normalized and compared against the interval
+ *	[0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ *	DBL_EPSILON is the smallest number that can be represented
+ *	on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ *	Please note, min cannot be greater than max.
+ *
+ * Results:
+ *	If the value is within of the interval [min..max], 1 is 
+ *	returned; 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+INLINE static int
+InRange(value, min, max)
+    double value, min, max;
+{
+    double range;
+
+    range = max - min;
+    if (range < DBL_EPSILON) {
+	return (FABS(max - value) < DBL_EPSILON);
+    } else {
+	double norm;
+
+	norm = (value - min) / range;
+	return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+    }
+}
+
+enum NativeFormats {
+    FMT_UNKNOWN = -1,
+    FMT_UCHAR, FMT_CHAR,
+    FMT_USHORT, FMT_SHORT,
+    FMT_UINT, FMT_INT,
+    FMT_ULONG, FMT_LONG,
+    FMT_FLOAT, FMT_DOUBLE
+};
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * GetBinaryFormat
+ *
+ *      Translates a format string into a native type.  Formats may be
+ *	as follows.
+ *
+ *		signed		i1, i2, i4, i8
+ *		unsigned 	u1, u2, u4, u8
+ *		real		r4, r8, r16
+ *
+ *	But there must be a corresponding native type.  For example,
+ *	this for reading 2-byte binary integers from an instrument and
+ *	converting them to unsigned shorts or ints.
+ *
+ * -----------------------------------------------------------------------
+ */
+static enum NativeFormats
+GetBinaryFormat(interp, string, sizePtr)
+    Tcl_Interp *interp;
+    char *string;
+    int *sizePtr;
+{
+    char c;
+
+    c = tolower(string[0]);
+    if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) {
+	Tcl_AppendResult(interp, "unknown binary format \"", string,
+	    "\": incorrect byte size", (char *)NULL);
+	return FMT_UNKNOWN;
+    }
+    switch (c) {
+    case 'r':
+	if (*sizePtr == sizeof(double)) {
+	    return FMT_DOUBLE;
+	} else if (*sizePtr == sizeof(float)) {
+	    return FMT_FLOAT;
+	}
+	break;
+
+    case 'i':
+	if (*sizePtr == sizeof(char)) {
+	    return FMT_CHAR;
+	} else if (*sizePtr == sizeof(int)) {
+	    return FMT_INT;
+	} else if (*sizePtr == sizeof(long)) {
+	    return FMT_LONG;
+	} else if (*sizePtr == sizeof(short)) {
+	    return FMT_SHORT;
+	}
+	break;
+
+    case 'u':
+	if (*sizePtr == sizeof(unsigned char)) {
+	    return FMT_UCHAR;
+	} else if (*sizePtr == sizeof(unsigned int)) {
+	    return FMT_UINT;
+	} else if (*sizePtr == sizeof(unsigned long)) {
+	    return FMT_ULONG;
+	} else if (*sizePtr == sizeof(unsigned short)) {
+	    return FMT_USHORT;
+	}
+	break;
+
+    default:
+	Tcl_AppendResult(interp, "unknown binary format \"", string,
+	    "\": should be either i#, r#, u# (where # is size in bytes)",
+	    (char *)NULL);
+	return FMT_UNKNOWN;
+    }
+    Tcl_AppendResult(interp, "can't handle format \"", string, "\"", 
+		     (char *)NULL);
+    return FMT_UNKNOWN;
+}
+
+static int
+CopyValues(vPtr, byteArr, fmt, size, length, swap, indexPtr)
+    VectorObject *vPtr;
+    char *byteArr;
+    enum NativeFormats fmt;
+    int size;
+    int length;
+    int swap;
+    int *indexPtr;
+{
+    register int i, n;
+    int newSize;
+
+    if ((swap) && (size > 1)) {
+	int nBytes = size * length;
+	register unsigned char *p;
+	register int left, right;
+
+	for (i = 0; i < nBytes; i += size) {
+	    p = (unsigned char *)(byteArr + i);
+	    for (left = 0, right = size - 1; left < right; left++, right--) {
+		p[left] ^= p[right];
+		p[right] ^= p[left];
+		p[left] ^= p[right];
+	    }
+
+	}
+    }
+    newSize = *indexPtr + length;
+    if (newSize > vPtr->length) {
+	if (Blt_VectorChangeLength(vPtr, newSize) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+#define CopyArrayToVector(vPtr, arr) \
+    for (i = 0, n = *indexPtr; i < length; i++, n++) { \
+	(vPtr)->valueArr[n] = (double)(arr)[i]; \
+    }
+
+    switch (fmt) {
+    case FMT_CHAR:
+	CopyArrayToVector(vPtr, (char *)byteArr);
+	break;
+
+    case FMT_UCHAR:
+	CopyArrayToVector(vPtr, (unsigned char *)byteArr);
+	break;
+
+    case FMT_INT:
+	CopyArrayToVector(vPtr, (int *)byteArr);
+	break;
+
+    case FMT_UINT:
+	CopyArrayToVector(vPtr, (unsigned int *)byteArr);
+	break;
+
+    case FMT_LONG:
+	CopyArrayToVector(vPtr, (long *)byteArr);
+	break;
+
+    case FMT_ULONG:
+	CopyArrayToVector(vPtr, (unsigned long *)byteArr);
+	break;
+
+    case FMT_SHORT:
+	CopyArrayToVector(vPtr, (short int *)byteArr);
+	break;
+
+    case FMT_USHORT:
+	CopyArrayToVector(vPtr, (unsigned short int *)byteArr);
+	break;
+
+    case FMT_FLOAT:
+	CopyArrayToVector(vPtr, (float *)byteArr);
+	break;
+
+    case FMT_DOUBLE:
+	CopyArrayToVector(vPtr, (double *)byteArr);
+	break;
+
+    case FMT_UNKNOWN:
+	break;
+    }
+    *indexPtr += length;
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * BinreadOp --
+ *
+ *	Reads binary values from a Tcl channel. Values are either appended
+ *	to the end of the vector or placed at a given index (using the
+ *	"-at" option), overwriting existing values.  Data is read until EOF
+ *	is found on the channel or a specified number of values are read.
+ *	(note that this is not necessarily the same as the number of bytes).
+ *
+ *	The following flags are supported:
+ *		-swap		Swap bytes
+ *		-at index	Start writing data at the index.
+ *		-format fmt	Specifies the format of the data.
+ *
+ *	This binary reader was created by Harald Kirsch (kir@iitb.fhg.de).
+ *	Anything that's wrong is due to my munging of his code.
+ *
+ * Results:
+ *	Returns a standard Tcl result. The interpreter result will contain
+ *	the number of values (not the number of bytes) read.
+ *
+ * Caveats:
+ *	Channel reads must end on an element boundary.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BinreadOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Tcl_Channel channel;
+    char *byteArr;
+    char *string;
+    enum NativeFormats fmt;
+    int arraySize, bytesRead;
+    int count, total;
+    int first;
+    int size, length, mode;
+    int swap;
+    register int i;
+
+    string = Tcl_GetString(objv[2]);
+    channel = Tcl_GetChannel(interp, string, &mode);
+    if (channel == NULL) {
+	return TCL_ERROR;
+    }
+    if ((mode & TCL_READABLE) == 0) {
+	Tcl_AppendResult(interp, "channel \"", string,
+	    "\" wasn't opened for reading", (char *)NULL);
+	return TCL_ERROR;
+    }
+    first = vPtr->length;
+    fmt = FMT_DOUBLE;
+    size = sizeof(double);
+    swap = FALSE;
+    count = 0;
+
+    if (objc > 3) {
+	string = Tcl_GetString(objv[3]);
+	if (string[0] != '-') {
+	    long int value;
+	    /* Get the number of values to read.  */
+	    if (Tcl_GetLongFromObj(interp, objv[3], &value) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (value < 0) {
+		Tcl_AppendResult(interp, "count can't be negative", 
+				 (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    count = (int)value;
+	    objc--, objv++;
+	}
+    }
+    /* Process any option-value pairs that remain.  */
+    for (i = 3; i < objc; i++) {
+	string = Tcl_GetString(objv[i]);
+	if (strcmp(string, "-swap") == 0) {
+	    swap = TRUE;
+	} else if (strcmp(string, "-format") == 0) {
+	    i++;
+	    if (i >= objc) {
+		Tcl_AppendResult(interp, "missing arg after \"", string,
+		    "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    string = Tcl_GetString(objv[i]);
+	    fmt = GetBinaryFormat(interp, string, &size);
+	    if (fmt == FMT_UNKNOWN) {
+		return TCL_ERROR;
+	    }
+	} else if (strcmp(string, "-at") == 0) {
+	    i++;
+	    if (i >= objc) {
+		Tcl_AppendResult(interp, "missing arg after \"", string,
+		    "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    string = Tcl_GetString(objv[i]);
+	    if (Blt_VectorGetIndex(interp, vPtr, string, &first, 0, 
+			 (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    if (first > vPtr->length) {
+		Tcl_AppendResult(interp, "index \"", string,
+		    "\" is out of range", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	}
+    }
+
+#define BUFFER_SIZE 1024
+    if (count == 0) {
+	arraySize = BUFFER_SIZE * size;
+    } else {
+	arraySize = count * size;
+    }
+
+    byteArr = Blt_Malloc(arraySize);
+    assert(byteArr);
+
+    /* FIXME: restore old channel translation later? */
+    if (Tcl_SetChannelOption(interp, channel, "-translation",
+	    "binary") != TCL_OK) {
+	return TCL_ERROR;
+    }
+    total = 0;
+    while (!Tcl_Eof(channel)) {
+	bytesRead = Tcl_Read(channel, byteArr, arraySize);
+	if (bytesRead < 0) {
+	    Tcl_AppendResult(interp, "error reading channel: ",
+		Tcl_PosixError(interp), (char *)NULL);
+	    return TCL_ERROR;
+	}
+	if ((bytesRead % size) != 0) {
+	    Tcl_AppendResult(interp, "error reading channel: short read",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	length = bytesRead / size;
+	if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first)
+	    != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	total += length;
+	if (count > 0) {
+	    break;
+	}
+    }
+    Blt_Free(byteArr);
+
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+
+    /* Set the result as the number of values read.  */
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(total));
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ *	Searchs for a value in the vector. Returns the indices of all
+ *	vector elements matching a particular value.
+ *
+ * Results:
+ *	Always returns TCL_OK.  interp->result will contain a list of
+ *	the indices of array elements matching value. If no elements
+ *	match, interp->result will contain the empty string.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    double min, max;
+    register int i;
+    int wantValue;
+    char *string;
+    Tcl_Obj *listObjPtr;
+
+    wantValue = FALSE;
+    string = Tcl_GetString(objv[2]);
+    if ((string[0] == '-') && (strcmp(string, "-value") == 0)) {
+	wantValue = TRUE;
+	objv++, objc--;
+    }
+    if (GetDouble(interp, objv[2], &min) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    max = min;
+    if ((objc > 3) && (GetDouble(interp, objv[3], &max) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if ((min - max) >= DBL_EPSILON) {
+	return TCL_OK;		/* Bogus range. Don't bother looking. */
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    if (wantValue) {
+	for (i = 0; i < vPtr->length; i++) {
+	    if (InRange(vPtr->valueArr[i], min, max)) {
+		Tcl_ListObjAppendElement(interp, listObjPtr, 
+			Tcl_NewDoubleObj(vPtr->valueArr[i]));
+	    }
+	}
+    } else {
+	for (i = 0; i < vPtr->length; i++) {
+	    if (InRange(vPtr->valueArr[i], min, max)) {
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewIntObj(i + vPtr->offset));
+	    }
+	}
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * OffsetOp --
+ *
+ *	Queries or sets the offset of the array index from the base
+ *	address of the data array of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffsetOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    if (objc == 3) {
+	int newOffset;
+
+	if (Tcl_GetIntFromObj(interp, objv[2], &newOffset) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	vPtr->offset = newOffset;
+    }
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(vPtr->offset));
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * RandomOp --
+ *
+ *	Generates random values for the length of the vector.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RandomOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;		/* Not used. */
+{
+#ifdef HAVE_DRAND48
+    register int i;
+
+    for (i = 0; i < vPtr->length; i++) {
+	vPtr->valueArr[i] = drand48();
+    }
+#endif /* HAVE_DRAND48 */
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SeqOp --
+ *
+ *	Generates a sequence of values in the vector.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SeqOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    register int i;
+    double start, finish, step;
+    int fillVector;
+    int nSteps;
+    char *string;
+
+    if (GetDouble(interp, objv[2], &start) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    fillVector = FALSE;
+    string = Tcl_GetString(objv[3]);
+    if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+	fillVector = TRUE;
+    } else if (GetDouble(interp, objv[3], &finish) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    step = 1.0;
+    if ((objc > 4) && (GetDouble(interp, objv[4], &step) != TCL_OK)) {
+	return TCL_ERROR;
+    }
+    if (fillVector) {
+	nSteps = vPtr->length;
+    } else {
+	nSteps = (int)((finish - start) / step) + 1;
+    }
+    if (nSteps > 0) {
+	if (Blt_VectorChangeLength(vPtr, nSteps) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	for (i = 0; i < nSteps; i++) {
+	    vPtr->valueArr[i] = start + (step * (double)i);
+	}
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ *	Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vector data is reset.  Clients of the vector are notified.
+ *	Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SetOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    int result;
+    VectorObject *v2Ptr;
+    int nElem;
+    Tcl_Obj **elemObjArr;
+
+    /* The source can be either a list of numbers or another vector.  */
+
+    v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, 
+	   Tcl_GetString(objv[2]), (char **)NULL, NS_SEARCH_BOTH);
+    if (v2Ptr != NULL) {
+	if (vPtr == v2Ptr) {
+	    VectorObject *tmpPtr;
+	    /* 
+	     * Source and destination vectors are the same.  Copy the
+	     * source first into a temporary vector to avoid memory
+	     * overlaps. 
+	     */
+	    tmpPtr = Blt_VectorNew(vPtr->dataPtr);
+	    result = Blt_VectorDuplicate(tmpPtr, v2Ptr);
+	    if (result == TCL_OK) {
+		result = Blt_VectorDuplicate(vPtr, tmpPtr);
+	    }
+	    Blt_VectorFree(tmpPtr);
+	} else {
+	    result = Blt_VectorDuplicate(vPtr, v2Ptr);
+	}
+    } else if (Tcl_ListObjGetElements(interp, objv[2], &nElem, &elemObjArr) 
+	       == TCL_OK) {
+	result = CopyList(vPtr, nElem, elemObjArr);
+    } else {
+	return TCL_ERROR;
+    }
+
+    if (result == TCL_OK) {
+	/*
+	 * The vector has changed; so flush the array indices (they're
+	 * wrong now), find the new range of the data, and notify
+	 * the vector's clients that it's been modified.
+	 */
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+	Blt_VectorUpdateClients(vPtr);
+    }
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SplitOp --
+ *
+ *	Copies the values from the vector evens into one of more
+ *	vectors.
+ *
+ * Results:
+ *	A standard Tcl result.  
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SplitOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int nVectors;
+
+    nVectors = objc - 2;
+    if ((vPtr->length % nVectors) != 0) {
+	Tcl_AppendResult(interp, "can't split vector \"", vPtr->name, 
+	   "\" into ", Blt_Itoa(nVectors), " even parts.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (nVectors > 0) {
+	VectorObject *v2Ptr;
+	char *string;		/* Name of vector. */
+	int i, j, k;
+	int oldSize, newSize, extra, isNew;
+
+	extra = vPtr->length / nVectors;
+	for (i = 0; i < nVectors; i++) {
+	    string = Tcl_GetString(objv[i+2]);
+	    v2Ptr = Blt_VectorCreate(vPtr->dataPtr, string, string, string,
+		&isNew);
+	    oldSize = v2Ptr->length;
+	    newSize = oldSize + extra;
+	    if (Blt_VectorChangeLength(v2Ptr, newSize) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    for (j = i, k = oldSize; j < vPtr->length; j += nVectors, k++) {
+		v2Ptr->valueArr[k] = vPtr->valueArr[j];
+	    }
+	    Blt_VectorUpdateClients(v2Ptr);
+	    if (v2Ptr->flush) {
+		Blt_VectorFlushCache(v2Ptr);
+	    }
+	}
+    }
+    return TCL_OK;
+}
+
+
+static VectorObject **sortVectorArr; /* Pointer to the array of values
+				      * currently being sorted. */
+static int nSortVectors;
+static int reverse;		/* Indicates the ordering of the sort. If
+				 * non-zero, the vectors are sorted in
+				 * decreasing order */
+
+static int
+CompareVectors(a, b)
+    void *a;
+    void *b;
+{
+    double delta;
+    int i;
+    int sign;
+    register VectorObject *vPtr;
+
+    sign = (reverse) ? -1 : 1;
+    for (i = 0; i < nSortVectors; i++) {
+	vPtr = sortVectorArr[i];
+	delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b];
+	if (delta < 0.0) {
+	    return (-1 * sign);
+	} else if (delta > 0.0) {
+	    return (1 * sign);
+	}
+    }
+    return 0;
+}
+
+int *
+Blt_VectorSortIndex(vPtrPtr, nVectors)
+    VectorObject **vPtrPtr;
+    int nVectors;
+{
+    int *indexArr;
+    register int i;
+    VectorObject *vPtr = *vPtrPtr;
+    int length;
+
+    length = vPtr->last - vPtr->first + 1;
+    indexArr = Blt_Malloc(sizeof(int) * length);
+    assert(indexArr);
+    for (i = vPtr->first; i <= vPtr->last; i++) {
+	indexArr[i] = i;
+    }
+    sortVectorArr = vPtrPtr;
+    nSortVectors = nVectors;
+    qsort((char *)indexArr, length, sizeof(int),
+	  (QSortCompareProc *)CompareVectors);
+    return indexArr;
+}
+
+static int *
+SortVectors(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    VectorObject **vPtrArray, *v2Ptr;
+    int *iArr;
+    register int i;
+
+    vPtrArray = Blt_Malloc(sizeof(VectorObject *) * (objc + 1));
+    assert(vPtrArray);
+    vPtrArray[0] = vPtr;
+    iArr = NULL;
+    for (i = 0; i < objc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+	    != TCL_OK) {
+	    goto error;
+	}
+	if (v2Ptr->length != vPtr->length) {
+	    Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+		"\" is not the same size as \"", vPtr->name, "\"",
+		(char *)NULL);
+	    goto error;
+	}
+	vPtrArray[i + 1] = v2Ptr;
+    }
+    iArr = Blt_VectorSortIndex(vPtrArray, objc + 1);
+  error:
+    Blt_Free(vPtrArray);
+    return iArr;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ *	Sorts the vector object and any other vectors according to
+ *	sorting order of the vector object.
+ *
+ * Results:
+ *	A standard Tcl result.  If any of the auxiliary vectors are
+ *	a different size than the sorted vector object, TCL_ERROR is
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vectors are sorted.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+static int
+SortOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    VectorObject *v2Ptr;
+    char *string;
+    double *mergeArr;
+    int *iArr;
+    int refSize, nBytes;
+    int result;
+    register int i, n;
+
+    reverse = FALSE;
+    if (objc > 2) {
+	int length;
+
+	string = Tcl_GetStringFromObj(objv[2], &length);
+	if (string[0] == '-') {
+	    if ((length > 1) && (strncmp(string, "-reverse", length) == 0)) {
+		reverse = TRUE;
+	    } else {
+		Tcl_AppendResult(interp, "unknown flag \"", string,
+			 "\": should be \"-reverse\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    objc--, objv++;
+	}
+    }
+    if (objc > 2) {
+	iArr = SortVectors(vPtr, interp, objc - 2, objv + 2);
+    } else {
+	iArr = Blt_VectorSortIndex(&vPtr, 1);
+    }
+    if (iArr == NULL) {
+	return TCL_ERROR;
+    }
+    refSize = vPtr->length;
+
+    /*
+     * Create an array to store a copy of the current values of the
+     * vector. We'll merge the values back into the vector based upon
+     * the indices found in the index array.
+     */
+    nBytes = sizeof(double) * refSize;
+    mergeArr = Blt_Malloc(nBytes);
+    assert(mergeArr);
+    memcpy((char *)mergeArr, (char *)vPtr->valueArr, nBytes);
+    for (n = 0; n < refSize; n++) {
+	vPtr->valueArr[n] = mergeArr[iArr[n]];
+    }
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+
+    /* Now sort any other vectors in the same fashion.  The vectors
+     * must be the same size as the iArr though.  */
+    result = TCL_ERROR;
+    for (i = 2; i < objc; i++) {
+	if (Blt_VectorLookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+	    != TCL_OK) {
+	    goto error;
+	}
+	if (v2Ptr->length != refSize) {
+	    Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+		"\" is not the same size as \"", vPtr->name, "\"",
+		(char *)NULL);
+	    goto error;
+	}
+	memcpy((char *)mergeArr, (char *)v2Ptr->valueArr, nBytes);
+	for (n = 0; n < refSize; n++) {
+	    v2Ptr->valueArr[n] = mergeArr[iArr[n]];
+	}
+	Blt_VectorUpdateClients(v2Ptr);
+	if (v2Ptr->flush) {
+	    Blt_VectorFlushCache(v2Ptr);
+	}
+    }
+    result = TCL_OK;
+  error:
+    Blt_Free(mergeArr);
+    Blt_Free(iArr);
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * ArithOp --
+ *
+ * Results:
+ *	A standard Tcl result.  If the source vector doesn't exist
+ *	or the source list is not a valid list of numbers, TCL_ERROR
+ *	returned.  Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ *	The vector data is reset.  Clients of the vector are notified.
+ *	Any cached array indices are flushed.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArithOp(vPtr, interp, objc, objv)
+    VectorObject *vPtr;
+    Tcl_Interp *interp;
+    int objc;			/* Not used. */
+    Tcl_Obj *CONST *objv;
+{
+    register double value;
+    register int i;
+    VectorObject *v2Ptr;
+    double scalar;
+    Tcl_Obj *listObjPtr;
+    char *string;
+
+    v2Ptr = Blt_VectorParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+		Tcl_GetString(objv[2]), (char **)NULL, NS_SEARCH_BOTH);
+    if (v2Ptr != NULL) {
+	register int j;
+	int length;
+
+	length = v2Ptr->last - v2Ptr->first + 1;
+	if (length != vPtr->length) {
+	    Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[0]), 
+		"\" and \"", Tcl_GetString(objv[2]), 
+		"\" are not the same length", (char *)NULL);
+	    return TCL_ERROR;
+	}
+	string = Tcl_GetString(objv[1]);
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	switch (string[0]) {
+	case '*':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] * v2Ptr->valueArr[j];
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '/':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] / v2Ptr->valueArr[j];
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '-':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] - v2Ptr->valueArr[j];
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '+':
+	    for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+		value = vPtr->valueArr[i] + v2Ptr->valueArr[j];
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+
+    } else if (GetDouble(interp, objv[2], &scalar) == TCL_OK) {
+	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+	string = Tcl_GetString(objv[1]);
+	switch (string[0]) {
+	case '*':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] * scalar;
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '/':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] / scalar;
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '-':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] - scalar;
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+
+	case '+':
+	    for (i = 0; i < vPtr->length; i++) {
+		value = vPtr->valueArr[i] + scalar;
+		Tcl_ListObjAppendElement(interp, listObjPtr,
+			 Tcl_NewDoubleObj(value));
+	    }
+	    break;
+	}
+	Tcl_SetObjResult(interp, listObjPtr);
+    } else {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorInstCmd --
+ *
+ *	Parses and invokes the appropriate vector instance command
+ *	option.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+static Blt_OpSpec vectorInstOps[] =
+{
+    {"*", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"+", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"-", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"/", 1, (Blt_Op)ArithOp, 3, 3, "item",},	/*Deprecated*/
+    {"append", 1, (Blt_Op)AppendOp, 3, 0, "item ?item...?",},
+    {"binread", 1, (Blt_Op)BinreadOp, 3, 0, "channel ?numValues? ?flags?",},
+    {"clear", 1, (Blt_Op)ClearOp, 2, 2, "",},
+    {"delete", 2, (Blt_Op)DeleteOp, 2, 0, "index ?index...?",},
+    {"dup", 2, (Blt_Op)DupOp, 3, 0, "vecName",},
+    {"index", 1, (Blt_Op)IndexOp, 3, 4, "index ?value?",},
+    {"length", 1, (Blt_Op)LengthOp, 2, 3, "?newSize?",},
+    {"merge", 1, (Blt_Op)MergeOp, 3, 0, "vecName ?vecName...?",},
+    {"normalize", 3, (Blt_Op)NormalizeOp, 2, 3, "?vecName?",},	/*Deprecated*/
+    {"notify", 3, (Blt_Op)NotifyOp, 3, 3, "keyword",},
+    {"offset", 2, (Blt_Op)OffsetOp, 2, 3, "?offset?",},
+    {"populate", 1, (Blt_Op)PopulateOp, 4, 4, "vecName density",},
+    {"random", 4, (Blt_Op)RandomOp, 2, 2, "",},	/*Deprecated*/
+    {"range", 4, (Blt_Op)RangeOp, 4, 4, "first last",},
+    {"search", 3, (Blt_Op)SearchOp, 3, 4, "?-value? value ?value?",},
+    {"seq", 3, (Blt_Op)SeqOp, 4, 5, "start end ?step?",},
+    {"set", 3, (Blt_Op)SetOp, 3, 3, "list",},
+    {"sort", 2, (Blt_Op)SortOp, 2, 0, "?-reverse? ?vecName...?",},
+    {"split", 2, (Blt_Op)SplitOp, 2, 0, "?vecName...?",},
+    {"variable", 1, (Blt_Op)MapOp, 2, 3, "?varName?",},
+};
+
+static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_VectorInstCmd(clientData, interp, objc, objv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    VectorObject *vPtr = clientData;
+
+    vPtr->first = 0;
+    vPtr->last = vPtr->length - 1;
+    proc = Blt_GetOpFromObj(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, objc,
+	objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (vPtr, interp, objc, objv);
+}
+
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorVarTrace --
+ *
+ * Results:
+ *	Returns NULL on success.  Only called from a variable trace.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+char *
+Blt_VectorVarTrace(clientData, interp, part1, part2, flags)
+    ClientData clientData;	/* Vector object. */
+    Tcl_Interp *interp;
+    char *part1, *part2;
+    int flags;
+{
+    Blt_VectorIndexProc *indexProc;
+    VectorObject *vPtr = clientData;
+    int first, last;
+    int varFlags;
+#define MAX_ERR_MSG	1023
+    static char message[MAX_ERR_MSG + 1];
+
+    if (part2 == NULL) {
+	if (flags & TCL_TRACE_UNSETS) {
+	    Blt_Free(vPtr->arrayName);
+	    vPtr->arrayName = NULL;
+	    vPtr->varNsPtr = NULL;
+	    if (vPtr->freeOnUnset) {
+		Blt_VectorFree(vPtr);
+	    }
+	}
+	return NULL;
+    }
+    if (Blt_VectorGetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, 
+		&indexProc) != TCL_OK) {
+	goto error;
+    }
+    first = vPtr->first, last = vPtr->last;
+    varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags);
+    if (flags & TCL_TRACE_WRITES) {
+	double value;
+	Tcl_Obj *objPtr;
+
+	if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */
+	    return "read-only index";
+	}
+	objPtr = Tcl_GetVar2Ex(interp, part1, part2, varFlags);
+	if (objPtr == NULL) {
+	    goto error;
+	}
+	if (GetDouble(interp, objPtr, &value) != TCL_OK) {
+	    if ((last == first) && (first >= 0)) {
+		/* Single numeric index. Reset the array element to
+		 * its old value on errors */
+		Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags);
+	    }
+	    goto error;
+	}
+	if (first == vPtr->length) {
+	    if (Blt_VectorChangeLength(vPtr, vPtr->length + 1) != TCL_OK) {
+		return "error resizing vector";
+	    }
+	}
+	/* Set possibly an entire range of values */
+	ReplicateValue(vPtr, first, last, value);
+    } else if (flags & TCL_TRACE_READS) {
+	double value;
+	Tcl_Obj *objPtr;
+
+	if (vPtr->length == 0) {
+	    if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) {
+		goto error;
+	    }
+	    return NULL;
+	}
+	if  (first == vPtr->length) {
+	    return "write-only index";
+	}
+	if (first == last) {
+	    if (first >= 0) {
+		value = vPtr->valueArr[first];
+	    } else {
+		vPtr->first = 0, vPtr->last = vPtr->length - 1;
+		value = (*indexProc) ((Blt_Vector *) vPtr);
+	    }
+	    objPtr = Tcl_NewDoubleObj(value);
+	    if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) {
+		Tcl_DecrRefCount(objPtr);
+		goto error;
+	    }
+	} else {
+	    objPtr = GetValues(vPtr, first, last);
+	    if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) {
+		Tcl_DecrRefCount(objPtr);
+		goto error;
+	    }
+	}
+    } else if (flags & TCL_TRACE_UNSETS) {
+	register int i, j;
+
+	if ((first == vPtr->length) || (first == SPECIAL_INDEX)) {
+	    return "special vector index";
+	}
+	/*
+	 * Collapse the vector from the point of the first unset element.
+	 * Also flush any array variable entries so that the shift is
+	 * reflected when the array variable is read.
+	 */
+	for (i = first, j = last + 1; j < vPtr->length; i++, j++) {
+	    vPtr->valueArr[i] = vPtr->valueArr[j];
+	}
+	vPtr->length -= ((last - first) + 1);
+	if (vPtr->flush) {
+	    Blt_VectorFlushCache(vPtr);
+	}
+    } else {
+	return "unknown variable trace flag";
+    }
+    if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) {
+	Blt_VectorUpdateClients(vPtr);
+    }
+    Tcl_ResetResult(interp);
+    return NULL;
+
+ error: 
+    strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG);
+    message[MAX_ERR_MSG] = '\0';
+    return message;
+}
+
+#endif /* TCL_MAJOR_VERSION > 7 */
Index: trunk/kitgen/8.x/blt/generic/bltVector.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltVector.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltVector.c	(revision 175)
@@ -0,0 +1,2332 @@
+/*
+ * bltVector.c --
+ *
+ *	This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ *	o Add H. Kirsch's vector binary read operation
+ *		x binread file0
+ *		x binread -file file0
+ *
+ *	o Add ASCII/binary file reader
+ *		x read fileName
+ *
+ *	o Allow Tcl-based client notifications.
+ *		vector x
+ *		x notify call Display
+ *		x notify delete Display
+ *		x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+#include "bltMath.h"
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define DEF_ARRAY_SIZE		64
+#define VECFLAGS(v)	\
+	(((v)->varNsPtr != NULL) ? (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0;
+#define TRACE_ALL  (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS)
+
+
+#define VECTOR_CHAR(c)	((isalnum(UCHAR(c))) || \
+	(c == '_') || (c == ':') || (c == '@') || (c == '.'))
+
+
+/*
+ * VectorClient --
+ *
+ *	A vector can be shared by several clients.  Each client
+ *	allocates this structure that acts as its key for using the
+ *	vector.  Clients can also designate a callback routine that is
+ *	executed whenever the vector is updated or destroyed.
+ *
+ */
+typedef struct {
+    unsigned int magic;		/* Magic value designating whether this
+				 * really is a vector token or not */
+
+    VectorObject *serverPtr;	/* Pointer to the master record of the
+				 * vector.  If NULL, indicates that the
+				 * vector has been destroyed but as of
+				 * yet, this client hasn't recognized
+				 * it. */
+
+    Blt_VectorChangedProc *proc;/* Routine to call when the contents
+				 * of the vector change or the vector
+				 * is deleted. */
+
+    ClientData clientData;	/* Data passed whenever the vector
+				 * change procedure is called. */
+
+    Blt_ChainLink *linkPtr;	/* Used to quickly remove this entry from
+				 * its server's client chain. */
+} VectorClient;
+
+static Tcl_CmdDeleteProc VectorInstDeleteProc;
+static Tcl_CmdProc VectorCmd;
+static Tcl_InterpDeleteProc VectorInterpDeleteProc;
+
+extern void srand48 _ANSI_ARGS_((long int seed));
+
+static VectorObject *
+FindVectorInNamespace(dataPtr, nsPtr, vecName)
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    Tcl_Namespace *nsPtr;
+    CONST char *vecName;
+{
+    Tcl_DString dString;
+    CONST char *name;
+    Blt_HashEntry *hPtr;
+
+    name = Blt_GetQualifiedName(nsPtr, vecName, &dString);
+    hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), name);
+    Tcl_DStringFree(&dString);
+    if (hPtr != NULL) {
+	return (VectorObject *)Blt_GetHashValue(hPtr);
+    }
+    return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetVectorObject --
+ *
+ *	Searches for the vector associated with the name given.
+ *	Allow for a range specification.
+ *
+ * Results:
+ *	Returns a pointer to the vector if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static VectorObject *
+GetVectorObject(dataPtr, name, flags)
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    CONST char *name;
+    int flags;
+{
+    CONST char *vecName;
+    Tcl_Namespace *nsPtr;
+    VectorObject *vPtr;
+
+    nsPtr = NULL;
+    vecName = name;
+    if (Blt_ParseQualifiedName(dataPtr->interp, name, &nsPtr, &vecName) 
+	!= TCL_OK) {
+	return NULL;		/* Can't find namespace. */
+    } 
+    vPtr = NULL;
+    if (nsPtr != NULL) {
+	vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+    } else {
+	if (flags & NS_SEARCH_CURRENT) {
+	    nsPtr = Tcl_GetCurrentNamespace(dataPtr->interp);
+	    vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+	}
+	if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+	    nsPtr = Tcl_GetGlobalNamespace(dataPtr->interp);
+	    vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+	}
+    }
+    return vPtr;
+}
+
+void
+Blt_VectorUpdateRange(vPtr)
+    VectorObject *vPtr;
+{
+    double min, max;
+    register int i;
+
+    min = DBL_MAX, max = -DBL_MAX;
+    for (i = 0; i < vPtr->length; i++) {
+	if (FINITE(vPtr->valueArr[i])) {
+	    min = max = vPtr->valueArr[i];
+	    break;
+	}
+    }
+    for (/* empty */; i < vPtr->length; i++) {
+	if (FINITE(vPtr->valueArr[i])) {
+	    if (min > vPtr->valueArr[i]) {
+		min = vPtr->valueArr[i]; 
+	    } else if (max < vPtr->valueArr[i]) { 
+		max = vPtr->valueArr[i]; 
+	    } 
+	} 
+    } 
+    vPtr->min = min;
+    vPtr->max = max;
+    vPtr->notifyFlags &= ~UPDATE_RANGE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndex --
+ *
+ *	Converts the string representing an index in the vector, to
+ *	its numeric value.  A valid index may be an numeric string of
+ *	the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ *	A standard Tcl result.  If the string is a valid index, TCL_OK
+ *	is returned.  Otherwise TCL_ERROR is returned and interp->result
+ *	will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndex(interp, vPtr, string, indexPtr, flags, procPtrPtr)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *string;
+    int *indexPtr;
+    int flags;
+    Blt_VectorIndexProc **procPtrPtr;
+{
+    char c;
+    int value;
+
+    c = string[0];
+
+    /* Treat the index "end" like a numeric index.  */
+
+    if ((c == 'e') && (strcmp(string, "end") == 0)) {
+	if (vPtr->length < 1) {
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "bad index \"end\": vector is empty", 
+				 (char *)NULL);
+	    }
+	    return TCL_ERROR;
+	}
+	*indexPtr = vPtr->length - 1;
+	return TCL_OK;
+    } else if ((c == '+') && (strcmp(string, "++end") == 0)) {
+	*indexPtr = vPtr->length;
+	return TCL_OK;
+    }
+    if (procPtrPtr != NULL) {
+	Blt_HashEntry *hPtr;
+
+	hPtr = Blt_FindHashEntry(&(vPtr->dataPtr->indexProcTable), string);
+	if (hPtr != NULL) {
+	    *indexPtr = SPECIAL_INDEX;
+	    *procPtrPtr = (Blt_VectorIndexProc *) Blt_GetHashValue(hPtr);
+	    return TCL_OK;
+	}
+    }
+    if (Tcl_GetInt(interp, (char *)string, &value) != TCL_OK) {
+	long int lvalue;
+	/*
+	 * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter,
+	 * but the interp passed in may be NULL.  So we have to use
+	 * vPtr->interp and then reset the result.
+	 */
+	if (Tcl_ExprLong(vPtr->interp, (char *)string, &lvalue) != TCL_OK) {
+	    Tcl_ResetResult(vPtr->interp);
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "bad index \"", string, "\"", 
+				 (char *)NULL);
+	    }
+	    return TCL_ERROR;
+	}
+	value = lvalue;
+    }
+    /*
+     * Correct the index by the current value of the offset. This makes
+     * all the numeric indices non-negative, which is how we distinguish
+     * the special non-numeric indices.
+     */
+    value -= vPtr->offset;
+
+    if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "index \"", string, "\" is out of range", 
+			 (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *indexPtr = (int)value;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndexRange --
+ *
+ *	Converts the string representing an index in the vector, to
+ *	its numeric value.  A valid index may be an numeric string of
+ *	the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ *	A standard Tcl result.  If the string is a valid index, TCL_OK
+ *	is returned.  Otherwise TCL_ERROR is returned and interp->result
+ *	will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndexRange(interp, vPtr, string, flags, procPtrPtr)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *string;
+    int flags;
+    Blt_VectorIndexProc **procPtrPtr;
+{
+    int ielem;
+    char *colon;
+
+    colon = NULL;
+    if (flags & INDEX_COLON) {
+	colon = strchr(string, ':');
+    }
+    if (colon != NULL) {
+	if (string == colon) {
+	    vPtr->first = 0;	/* Default to the first index */
+	} else {
+	    int result;
+
+	    *colon = '\0';
+	    result = Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags,
+		(Blt_VectorIndexProc **) NULL);
+	    *colon = ':';
+	    if (result != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    vPtr->first = ielem;
+	}
+	if (*(colon + 1) == '\0') {
+	    /* Default to the last index */
+	    vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0;
+	} else {
+	    if (Blt_VectorGetIndex(interp, vPtr, colon + 1, &ielem, flags,
+		    (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+		return TCL_ERROR;
+	    }
+	    vPtr->last = ielem;
+	}
+	if (vPtr->first > vPtr->last) {
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "bad range \"", string,
+			 "\" (first > last)", (char *)NULL);
+	    }
+	    return TCL_ERROR;
+	}
+    } else {
+	if (Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags, 
+		       procPtrPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	vPtr->last = vPtr->first = ielem;
+    }
+    return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorParseElement(interp, dataPtr, start, endPtr, flags)
+    Tcl_Interp *interp;
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    CONST char *start;
+    char **endPtr;
+    int flags;
+{
+    register char *p;
+    char saved;
+    VectorObject *vPtr;
+
+    p = (char *)start;
+    /* Find the end of the vector name */
+    while (VECTOR_CHAR(*p)) {
+	p++;
+    }
+    saved = *p;
+    *p = '\0';
+
+    vPtr = GetVectorObject(dataPtr, start, flags);
+    if (vPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find vector \"", start, "\"", 
+			     (char *)NULL);
+	}
+	*p = saved;
+	return NULL;
+    }
+    *p = saved;
+    vPtr->first = 0;
+    vPtr->last = vPtr->length - 1;
+    if (*p == '(') {
+	int count, result;
+
+	start = p + 1;
+	p++;
+
+	/* Find the matching right parenthesis */
+	count = 1;
+	while (*p != '\0') {
+	    if (*p == ')') {
+		count--;
+		if (count == 0) {
+		    break;
+		}
+	    } else if (*p == '(') {
+		count++;
+	    }
+	    p++;
+	}
+	if (count > 0) {
+	    if (interp != NULL) {
+		Tcl_AppendResult(interp, "unbalanced parentheses \"", start, 
+			"\"", (char *)NULL);
+	    }
+	    return NULL;
+	}
+	*p = '\0';
+	result = Blt_VectorGetIndexRange(interp, vPtr, start, 
+		(INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL);
+	*p = ')';
+	if (result != TCL_OK) {
+	    return NULL;
+	}
+	p++;
+    }
+    if (endPtr != NULL) {
+      *endPtr = p;
+    }
+    return vPtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorNotifyClients --
+ *
+ *	Notifies each client of the vector that the vector has changed
+ *	(updated or destroyed) by calling the provided function back.
+ *	The function pointer may be NULL, in that case the client is
+ *	not notified.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The results depend upon what actions the client callbacks
+ *	take.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorNotifyClients(clientData)
+    ClientData clientData;
+{
+    VectorObject *vPtr = clientData;
+    Blt_ChainLink *linkPtr;
+    VectorClient *clientPtr;
+    Blt_VectorNotify notify;
+
+    notify = (vPtr->notifyFlags & NOTIFY_DESTROYED)
+	? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE;
+    vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING);
+
+    for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	if (clientPtr->proc != NULL) {
+	    (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify);
+	}
+    }
+    /*
+     * Some clients may not handle the "destroy" callback properly
+     * (they should call Blt_FreeVectorId to release the client
+     * identifier), so mark any remaining clients to indicate that
+     * vector's server has gone away.
+     */
+    if (notify == BLT_VECTOR_NOTIFY_DESTROY) {
+	for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+	    linkPtr = Blt_ChainNextLink(linkPtr)) {
+	    clientPtr = Blt_ChainGetValue(linkPtr);
+	    clientPtr->serverPtr = NULL;
+	}
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorUpdateClients --
+ *
+ *	Notifies each client of the vector that the vector has changed
+ *	(updated or destroyed) by calling the provided function back.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The individual client callbacks are eventually invoked.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorUpdateClients(vPtr)
+    VectorObject *vPtr;
+{
+    vPtr->dirty++;
+    vPtr->max = vPtr->min = bltNaN;
+    if (vPtr->notifyFlags & NOTIFY_NEVER) {
+	return;
+    }
+    vPtr->notifyFlags |= NOTIFY_UPDATED;
+    if (vPtr->notifyFlags & NOTIFY_ALWAYS) {
+	Blt_VectorNotifyClients(vPtr);
+	return;
+    }
+    if (!(vPtr->notifyFlags & NOTIFY_PENDING)) {
+	vPtr->notifyFlags |= NOTIFY_PENDING;
+	Tcl_DoWhenIdle(Blt_VectorNotifyClients, vPtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFlushCache --
+ *
+ *	Unsets all the elements of the Tcl array variable associated
+ *	with the vector, freeing memory associated with the variable.
+ *	This includes both the hash table and the hash keys.  The down
+ *	side is that this effectively flushes the caching of vector
+ *	elements in the array.  This means that the subsequent reads
+ *	of the array will require a decimal to string conversion.
+ *
+ *	This is needed when the vector changes its values, making
+ *	the array variable out-of-sync.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	All elements of array variable (except one) are unset, freeing
+ *	the memory associated with the variable.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFlushCache(vPtr)
+    VectorObject *vPtr;
+{
+    Tcl_CallFrame *framePtr;
+    Tcl_Interp *interp = vPtr->interp;
+
+    if (vPtr->arrayName == NULL) {
+	return;			/* Doesn't use the variable API */
+    }
+    framePtr = NULL;
+    if (vPtr->varNsPtr != NULL) {
+	framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+    }
+    /* Turn off the trace temporarily so that we can unset all the
+     * elements in the array.  */
+
+    Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+	TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+    /* Clear all the element entries from the entire array */
+    Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+    /* Restore the "end" index by default and the trace on the entire array */
+    Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags);
+    Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL,
+	TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+    if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+	Blt_LeaveNamespace(interp, framePtr);	/* Go back to current */
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorLookupName --
+ *
+ *	Searches for the vector associated with the name given.  Allow
+ *	for a range specification.
+ *
+ * Results:
+ *	Returns a pointer to the vector if found, otherwise NULL.
+ *	If the name is not associated with a vector and the
+ *	TCL_LEAVE_ERR_MSG flag is set, and interp->result will contain
+ *	an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorLookupName(dataPtr, vecName, vPtrPtr)
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    char *vecName;
+    VectorObject **vPtrPtr;
+{
+    VectorObject *vPtr;
+    char *endPtr;
+
+    vPtr = Blt_VectorParseElement(dataPtr->interp, dataPtr, vecName, &endPtr, 
+	NS_SEARCH_BOTH);
+    if (vPtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (*endPtr != '\0') {
+	Tcl_AppendResult(dataPtr->interp, 
+			 "extra characters after vector name", (char *)NULL);
+	return TCL_ERROR;
+    }
+    *vPtrPtr = vPtr;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteCommand --
+ *
+ *	Deletes the Tcl command associated with the vector, without
+ *	triggering a callback to "VectorInstDeleteProc".
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DeleteCommand(vPtr)
+    VectorObject *vPtr;		/* Vector associated with the Tcl command. */
+{
+    Tcl_Interp *interp = vPtr->interp;
+    char *qualName;		/* Name of Tcl command. */
+    Tcl_CmdInfo cmdInfo;
+    Tcl_DString dString;
+
+    Tcl_DStringInit(&dString);
+    qualName = Blt_GetQualifiedName(
+	Blt_GetCommandNamespace(interp, vPtr->cmdToken), 
+	Tcl_GetCommandName(interp, vPtr->cmdToken), &dString);
+    if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) {
+	cmdInfo.deleteProc = NULL;	/* Disable the callback before
+					 * deleting the Tcl command.*/
+	Tcl_SetCommandInfo(interp, qualName, &cmdInfo);
+	Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken);
+    }
+    Tcl_DStringFree(&dString);
+    vPtr->cmdToken = 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * UnmapVariable --
+ *
+ *	Destroys the trace on the current Tcl variable designated
+ *	to access the vector.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+UnmapVariable(vPtr)
+    VectorObject *vPtr;
+{
+    Tcl_Interp *interp = vPtr->interp;
+    Tcl_CallFrame *framePtr;
+
+    framePtr = NULL;
+    if (vPtr->varNsPtr != NULL) {	/* Activate namespace */
+	framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+    }
+    /* Unset the entire array */
+    Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+	(TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+    Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+    if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+	/* Go back to current namespace */
+	Blt_LeaveNamespace(interp, framePtr);
+    }
+    if (vPtr->arrayName != NULL) {
+	Blt_Free(vPtr->arrayName);
+	vPtr->arrayName = NULL;
+    }
+    vPtr->varNsPtr = NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorMapVariable --
+ *
+ *	Sets up traces on a Tcl variable to access the vector.
+ *
+ *	If another variable is already mapped, it's first untraced and
+ *	removed.  Don't do anything else for variables named "" (even
+ *	though Tcl allows this pathology). Saves the name of the new
+ *	array variable.
+ *
+ * Results:
+ *	A standard Tcl result. If an error occurs setting the variable
+ *	TCL_ERROR is returned and an error message is left in the
+ *	interpreter.
+ *
+ * Side effects:
+ *	Traces are set for the new variable. The new variable name is
+ *	saved in a malloc'ed string in vPtr->arrayName.  If this
+ *	variable is non-NULL, it indicates that a Tcl variable has
+ *	been mapped to this vector.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorMapVariable(interp, vPtr, name)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *name;
+{
+    Tcl_Namespace *nsPtr;
+    Tcl_CallFrame *framePtr;
+    CONST char *varName;
+    CONST char *result;
+
+    if (vPtr->arrayName != NULL) {
+	UnmapVariable(vPtr);
+    }
+    if ((name == NULL) || (name[0] == '\0')) {
+	return TCL_OK;		/* If the variable name is the empty
+				 * string, simply return after
+				 * removing any existing variable. */
+    }
+    framePtr = NULL;
+
+    /* Get the variable name (without the namespace qualifier). */
+    if (Blt_ParseQualifiedName(interp, name, &nsPtr, &varName) != TCL_OK) {
+	Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (nsPtr != NULL) {
+	/* [incr Tcl] 2.x doesn't like qualifiers with variable names,
+	 * so we need to enter the namespace if one was designated. */
+	framePtr = Blt_EnterNamespace(interp, nsPtr);
+    }
+    /*
+     * To play it safe, delete the variable first.  This has
+     * side-effect of unmapping the variable from any vector that may
+     * be currently using it.
+     */
+    Tcl_UnsetVar2(interp, (char *)varName, (char *)NULL, 0);
+
+    /* Set the index "end" in the array.  This will create the
+     * variable immediately so that we can check its namespace
+     * context.  */
+    result = Tcl_SetVar2(interp, (char *)varName, "end", "", TCL_LEAVE_ERR_MSG);
+
+    /* Determine if the variable is global or not.  If there wasn't a
+     * namespace qualifier, it still may be global.  We need to look
+     * inside the Var structure to see what it's namespace field says.
+     * NULL indicates that it's local.  */
+
+    vPtr->varNsPtr = Blt_GetVariableNamespace(interp, varName);
+    vPtr->varFlags = (vPtr->varNsPtr != NULL) ?
+	(TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0;
+
+    if (result != NULL) {
+	/* Trace the array on reads, writes, and unsets */
+	Tcl_TraceVar2(interp, (char *)varName, (char *)NULL, 
+		(TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+    }
+    if ((nsPtr != NULL) && (framePtr != NULL)) {
+	Blt_LeaveNamespace(interp, framePtr);	/* Go back to current */
+    }
+    vPtr->arrayName = Blt_Strdup(varName);
+    return (result == NULL) ? TCL_ERROR : TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorChangeLength --
+ *
+ *	Resizes the vector to the new size.
+ *
+ *	The new size of the vector is computed by doubling the
+ *	size of the vector until it fits the number of slots needed
+ *	(designated by *length*).
+ *
+ *	If the new size is the same as the old, simply adjust the
+ *	length of the vector.  Otherwise we're copying the data from
+ *	one memory location to another. The trailing elements of the 
+ *	vector need to be reset to zero.
+ *
+ *	If the storage changed memory locations, free up the old
+ *	location if it was dynamically allocated.
+ *
+ * Results:
+ *	A standard Tcl result.  If the reallocation is successful,
+ *	TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ *	Memory for the array is reallocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+int
+Blt_VectorChangeLength(vPtr, length)
+    VectorObject *vPtr;
+    int length;
+{
+    int newSize;		/* Size of array in elements */
+    double *newArr;
+    Tcl_FreeProc *freeProc;
+
+    newArr = NULL;
+    newSize = 0;
+    freeProc = TCL_STATIC;
+
+    if (length > 0) {
+	int wanted, used;
+
+	wanted = length;
+	used = vPtr->length;
+
+	/* Compute the new size by doubling old size until it's big enough */
+	newSize = DEF_ARRAY_SIZE;
+	if (wanted > DEF_ARRAY_SIZE) {
+	    while (newSize < wanted) {
+		newSize += newSize;
+	    }
+	}
+	freeProc = vPtr->freeProc;
+	if (newSize == vPtr->size) {
+	    newArr = vPtr->valueArr; /* Same size, use current array. */
+	} else {
+	    /* Dynamically allocate memory for the new array. */
+	    newArr = Blt_Malloc(newSize * sizeof(double));
+	    if (newArr == NULL) {
+		Tcl_AppendResult(vPtr->interp, "can't allocate ", 
+		Blt_Itoa(newSize), " elements for vector \"", vPtr->name, 
+				 "\"", (char *)NULL); return TCL_ERROR;
+	    }
+	    if (used > wanted) {
+		used = wanted;
+	    }
+	    /* Copy any previous data */
+	    if (used > 0) {
+		memcpy(newArr, vPtr->valueArr, used * sizeof(double));
+	    }
+	    freeProc = TCL_DYNAMIC;
+	}
+	/* Clear any new slots that we're now using in the array */
+	if (wanted > used) {
+	    memset(newArr + used, 0, (wanted - used) * sizeof(double));
+	}
+    }
+    if ((newArr != vPtr->valueArr) && (vPtr->valueArr != NULL)) {
+	/* 
+	 * We're not using the old storage anymore, so free it if it's
+	 * not static.  It's static because the user previously reset
+	 * the vector with a statically allocated array (setting freeProc 
+	 * to TCL_STATIC).  
+	 */
+	if (vPtr->freeProc != TCL_STATIC) {
+	    if (vPtr->freeProc == TCL_DYNAMIC) {
+		Blt_Free(vPtr->valueArr);
+	    } else {
+		(*vPtr->freeProc) ((char *)vPtr->valueArr);
+	    }
+	}
+    }
+    vPtr->valueArr = newArr;
+    vPtr->size = newSize;
+    vPtr->length = length;
+    vPtr->first = 0;
+    vPtr->last = length - 1;
+    vPtr->freeProc = freeProc;	/* Set the type of the new storage */
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ *	Resets the vector data.  This is called by a client to
+ *	indicate that the vector data has changed.  The vector does
+ *	not need to point to different memory.  Any clients of the
+ *	vector will be notified of the change.
+ *
+ * Results:
+ *	A standard Tcl result.  If the new array size is invalid,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	new vector data is recorded.
+ *
+ * Side Effects:
+ *	Any client designated callbacks will be posted.  Memory may
+ *	be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_VectorReset(vPtr, valueArr, length, size, freeProc)
+    VectorObject *vPtr;
+    double *valueArr;		/* Array containing the elements of the
+				 * vector. If NULL, indicates to reset the 
+				 * vector.*/
+    int length;			/* The number of elements that the vector 
+				 * currently holds. */
+    int size;			/* The maximum number of elements that the
+				 * array can hold. */
+    Tcl_FreeProc *freeProc;	/* Address of memory deallocation routine
+				 * for the array of values.  Can also be
+				 * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+    if (vPtr->valueArr != valueArr) {	/* New array of values resides
+					 * in different memory than
+					 * the current vector.  */
+	if ((valueArr == NULL) || (size == 0)) {
+	    /* Empty array. Set up default values */
+	    freeProc = TCL_STATIC;
+	    valueArr = NULL;
+	    size = length = 0;
+	} else if (freeProc == TCL_VOLATILE) {
+	    double *newArr;
+	    /* Data is volatile. Make a copy of the value array.  */
+	    newArr = Blt_Malloc(size * sizeof(double));
+	    if (newArr == NULL) {
+		Tcl_AppendResult(vPtr->interp, "can't allocate ", 
+			Blt_Itoa(size), " elements for vector \"", 
+			vPtr->name, "\"", (char *)NULL);
+		return TCL_ERROR;
+	    }
+	    memcpy((char *)newArr, (char *)valueArr, 
+		   sizeof(double) * length);
+	    valueArr = newArr;
+	    freeProc = TCL_DYNAMIC;
+	} 
+
+	if (vPtr->freeProc != TCL_STATIC) {
+	    /* Old data was dynamically allocated. Free it before
+	     * attaching new data.  */
+	    if (vPtr->freeProc == TCL_DYNAMIC) {
+		Blt_Free(vPtr->valueArr);
+	    } else {
+		(*freeProc) ((char *)vPtr->valueArr);
+	    }
+	}
+	vPtr->freeProc = freeProc;
+	vPtr->valueArr = valueArr;
+	vPtr->size = size;
+    }
+
+    vPtr->length = length;
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorNew(dataPtr)
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+{
+    VectorObject *vPtr;
+
+    vPtr = Blt_Calloc(1, sizeof(VectorObject));
+    assert(vPtr);
+    vPtr->notifyFlags = NOTIFY_WHENIDLE;
+    vPtr->freeProc = TCL_STATIC;
+    vPtr->dataPtr = dataPtr;
+    vPtr->valueArr = NULL;
+    vPtr->length = vPtr->size = 0;
+    vPtr->interp = dataPtr->interp;
+    vPtr->hashPtr = NULL;
+    vPtr->chainPtr = Blt_ChainCreate();
+    vPtr->flush = FALSE;
+    vPtr->min = vPtr->max = bltNaN;
+    return vPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFree --
+ *
+ *	Removes the memory and frees resources associated with the
+ *	vector.
+ *
+ *	o Removes the trace and the Tcl array variable and unsets
+ *	  the variable.
+ *	o Notifies clients of the vector that the vector is being
+ *	  destroyed.
+ *	o Removes any clients that are left after notification.
+ *	o Frees the memory (if necessary) allocated for the array.
+ *	o Removes the entry from the hash table of vectors.
+ *	o Frees the memory allocated for the name.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFree(vPtr)
+    VectorObject *vPtr;
+{
+    Blt_ChainLink *linkPtr;
+    VectorClient *clientPtr;
+
+    if (vPtr->cmdToken != 0) {
+	DeleteCommand(vPtr);
+    }
+    if (vPtr->arrayName != NULL) {
+	UnmapVariable(vPtr);
+    }
+    vPtr->length = 0;
+
+    /* Immediately notify clients that vector is going away */
+    if (vPtr->notifyFlags & NOTIFY_PENDING) {
+	vPtr->notifyFlags &= ~NOTIFY_PENDING;
+	Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr);
+    }
+    vPtr->notifyFlags |= NOTIFY_DESTROYED;
+    Blt_VectorNotifyClients(vPtr);
+
+    for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+	linkPtr = Blt_ChainNextLink(linkPtr)) {
+	clientPtr = Blt_ChainGetValue(linkPtr);
+	Blt_Free(clientPtr);
+    }
+    Blt_ChainDestroy(vPtr->chainPtr);
+    if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) {
+	if (vPtr->freeProc == TCL_DYNAMIC) {
+	    Blt_Free(vPtr->valueArr);
+	} else {
+	    (*vPtr->freeProc) ((char *)vPtr->valueArr);
+	}
+    }
+    if (vPtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(&(vPtr->dataPtr->vectorTable), vPtr->hashPtr);
+    }
+#ifdef NAMESPACE_DELETE_NOTIFY
+    if (vPtr->nsPtr != NULL) {
+	Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr);
+    }
+#endif /* NAMESPACE_DELETE_NOTIFY */
+    Blt_Free(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VectorInstDeleteProc --
+ *
+ *	Deletes the command associated with the vector.  This is
+ *	called only when the command associated with the vector is
+ *	destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+VectorInstDeleteProc(clientData)
+    ClientData clientData;
+{
+    VectorObject *vPtr = clientData;
+
+    vPtr->cmdToken = 0;
+    Blt_VectorFree(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorCreate --
+ *
+ *	Creates a vector structure and the following items:
+ *
+ *	o Tcl command
+ *	o Tcl array variable and establishes traces on the variable
+ *	o Adds a  new entry in the vector hash table
+ *
+ * Results:
+ *	A pointer to the new vector structure.  If an error occurred
+ *	NULL is returned and an error message is left in
+ *	interp->result.
+ *
+ * Side effects:
+ *	A new Tcl command and array variable is added to the
+ *	interpreter.
+ *
+ * ---------------------------------------------------------------------- */
+VectorObject *
+Blt_VectorCreate(dataPtr, vecName, cmdName, varName, newPtr)
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    CONST char *vecName;	/* Namespace-qualified name of the vector */
+    CONST char *cmdName;	/* Name of the Tcl command mapped to
+				 * the vector */
+    CONST char *varName;	/* Name of the Tcl array mapped to the
+				 * vector */
+    int *newPtr;
+{
+    Tcl_DString dString;
+    VectorObject *vPtr;
+    int isNew;
+    CONST char *name;
+    char *qualName;
+    Tcl_Namespace *nsPtr;
+    Blt_HashEntry *hPtr;
+    Tcl_Interp *interp = dataPtr->interp;
+
+    isNew = 0;
+    nsPtr = NULL;
+    vPtr = NULL;
+
+    if (Blt_ParseQualifiedName(interp, vecName, &nsPtr, &name) != TCL_OK) {
+	Tcl_AppendResult(interp, "can't find namespace in \"", vecName, "\"",
+	    (char *)NULL);
+	return NULL;
+    }
+    if (nsPtr == NULL) {
+	nsPtr = Tcl_GetCurrentNamespace(interp);
+    }
+    Tcl_DStringInit(&dString);
+    if ((name[0] == '#') && (strcmp(name, "#auto") == 0)) {
+	char string[200];
+
+	do {	/* Generate a unique vector name. */
+	    sprintf(string, "vector%d", dataPtr->nextId++);
+	    qualName = Blt_GetQualifiedName(nsPtr, string, &dString);
+	    hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), qualName);
+	} while (hPtr != NULL);
+    } else {
+	register CONST char *p;
+
+	for (p = name; *p != '\0'; p++) {
+	    if (!VECTOR_CHAR(*p)) {
+		Tcl_AppendResult(interp, "bad vector name \"", name,
+		    "\": must contain digits, letters, underscore, or period",
+		    (char *)NULL);
+		goto error;
+	    }
+	}
+	qualName = Blt_GetQualifiedName(nsPtr, name, &dString);
+	vPtr = Blt_VectorParseElement((Tcl_Interp *)NULL, dataPtr, qualName, 
+		(char **)NULL, NS_SEARCH_CURRENT);
+    }
+    if (vPtr == NULL) {
+	hPtr = Blt_CreateHashEntry(&(dataPtr->vectorTable), qualName, &isNew);
+	vPtr = Blt_VectorNew(dataPtr);
+	vPtr->hashPtr = hPtr;
+	vPtr->nsPtr = nsPtr;
+
+	vPtr->name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+#ifdef NAMESPACE_DELETE_NOTIFY
+	Blt_CreateNsDeleteNotify(interp, nsPtr, vPtr, VectorInstDeleteProc);
+#endif /* NAMESPACE_DELETE_NOTIFY */
+	Blt_SetHashValue(hPtr, vPtr);
+    }
+    if (cmdName != NULL) {
+	Tcl_CmdInfo cmdInfo;
+
+	if ((cmdName == vecName) ||
+	    ((name[0] == '#') && (strcmp(name, "#auto") == 0))) {
+	    cmdName = qualName;
+	} 
+	if (Tcl_GetCommandInfo(interp, (char *)cmdName, &cmdInfo)) {
+#if TCL_MAJOR_VERSION > 7
+	    if (vPtr != cmdInfo.objClientData) {
+#else
+	    if (vPtr != cmdInfo.clientData) {
+#endif
+		Tcl_AppendResult(interp, "command \"", cmdName,
+			 "\" already exists", (char *)NULL);
+		goto error;
+	    }
+	    /* We get here only if the old name is the same as the new. */
+	    goto checkVariable;
+	}
+    }
+    if (vPtr->cmdToken != 0) {
+	DeleteCommand(vPtr);	/* Command already exists, delete old first */
+    }
+    if (cmdName != NULL) {
+#if (TCL_MAJOR_VERSION == 7)
+	vPtr->cmdToken = Blt_CreateCommand(interp, cmdName, Blt_VectorInstCmd, 
+		vPtr, VectorInstDeleteProc);
+#else
+	Tcl_DString dString2;
+	
+	Tcl_DStringInit(&dString2);
+	if (cmdName != qualName) {
+	    if (Blt_ParseQualifiedName(interp, cmdName, &nsPtr, &name) 
+		!= TCL_OK) {
+		Tcl_AppendResult(interp, "can't find namespace in \"", cmdName,
+				 "\"", (char *)NULL);
+		goto error;
+	    }
+	    if (nsPtr == NULL) {
+		nsPtr = Tcl_GetCurrentNamespace(interp);
+	    }
+	    cmdName = Blt_GetQualifiedName(nsPtr, name, &dString2);
+	}
+	vPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)cmdName, 
+		Blt_VectorInstCmd, vPtr, VectorInstDeleteProc);
+	Tcl_DStringFree(&dString2);
+#endif
+    }
+  checkVariable:
+    if (varName != NULL) {
+	if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) {
+	    varName = qualName;
+	}
+	if (Blt_VectorMapVariable(interp, vPtr, varName) != TCL_OK) {
+	    goto error;
+	}
+    }
+
+    Tcl_DStringFree(&dString);
+    *newPtr = isNew;
+    return vPtr;
+
+  error:
+    Tcl_DStringFree(&dString);
+    if (vPtr != NULL) {
+	Blt_VectorFree(vPtr);
+    }
+    return NULL;
+}
+
+
+int
+Blt_VectorDuplicate(destPtr, srcPtr)
+    VectorObject *destPtr, *srcPtr;
+{
+    int nBytes;
+    int length;
+
+    if (destPtr == srcPtr) {
+	/* Copying the same vector. */
+    }
+    length = srcPtr->last - srcPtr->first + 1;
+    if (Blt_VectorChangeLength(destPtr, length) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    nBytes = length * sizeof(double);
+    memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes);
+    destPtr->offset = srcPtr->offset;
+    return TCL_OK;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorNamesOp --
+ *
+ *	Reports the names of all the current vectors in the interpreter.
+ *
+ * Results:
+ *	A standard Tcl result.  interp->result will contain a list of
+ *	all the names of the vector instances.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorNamesOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    char *name;
+    Blt_HashSearch cursor;
+
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+	if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
+	    Tcl_AppendElement(interp, name);
+	}
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ *	Creates a Tcl command, and array variable representing an
+ *	instance of a vector.
+ *
+ *	vector a
+ *	vector b(20)
+ *	vector c(-5:14)
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreate2(clientData, interp, argStart, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argStart;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    VectorObject *vPtr;
+    char *leftParen, *rightParen;
+    int isNew, size, first, last;
+    char *cmdName, *varName;
+    int length;
+    int inspectFlags, freeOnUnset, flush;
+    char **nameArr;
+    int count;
+    register int i;
+
+    /*
+     * Handle switches to the vector command and collect the vector
+     * name arguments into an array.
+     */
+    varName = cmdName = NULL;
+    freeOnUnset = 0;
+    nameArr = Blt_Malloc(sizeof(char *) * argc);
+    assert(nameArr);
+
+    inspectFlags = TRUE;
+    flush = FALSE;
+    count = 0;
+    vPtr = NULL;
+    for (i = argStart; i < argc; i++) {
+	if ((inspectFlags) && (argv[i][0] == '-')) {
+	    length = strlen(argv[i]);
+	    if ((length > 1) &&
+		(strncmp(argv[i], "-variable", length) == 0)) {
+		if ((i + 1) == argc) {
+		    Tcl_AppendResult(interp,
+			"no variable name supplied with \"",
+			argv[i], "\" switch", (char *)NULL);
+		    goto error;
+		}
+		i++;
+		varName = argv[i];
+	    } else if ((length > 1) &&
+		(strncmp(argv[i], "-command", length) == 0)) {
+		if ((i + 1) == argc) {
+		    Tcl_AppendResult(interp,
+			"no command name supplied with \"",
+			argv[i], "\" switch", (char *)NULL);
+		    goto error;
+		}
+		i++;
+		cmdName = argv[i];
+	    } else if ((length > 1) &&
+		(strncmp(argv[i], "-watchunset", length) == 0)) {
+		int bool;
+
+		if ((i + 1) == argc) {
+		    Tcl_AppendResult(interp, "no value name supplied with \"",
+			argv[i], "\" switch", (char *)NULL);
+		    goto error;
+		}
+		i++;
+		if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+		    goto error;
+		}
+		freeOnUnset = bool;
+	    } else if ((length > 1) && (strncmp(argv[i], "-flush", length) == 0)) {
+		int bool;
+
+		if ((i + 1) == argc) {
+		    Tcl_AppendResult(interp, "no value name supplied with \"",
+			argv[i], "\" switch", (char *)NULL);
+		    goto error;
+		}
+		i++;
+		if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+		    goto error;
+		}
+		flush = bool;
+	    } else if ((length > 1) && (argv[i][1] == '-') &&
+		(argv[i][2] == '\0')) {
+		inspectFlags = FALSE;	/* Allow vector names to start with - */
+	    } else {
+		Tcl_AppendResult(interp, "bad vector switch \"", argv[i], "\"",
+		    (char *)NULL);
+		goto error;
+	    }
+	} else {
+	    nameArr[count++] = argv[i];
+	}
+    }
+    if (count == 0) {
+	Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL);
+	goto error;
+    }
+    if (count > 1) {
+	if ((cmdName != NULL) && (cmdName[0] != '\0')) {
+	    Tcl_AppendResult(interp,
+		"can't specify more than one vector with \"-command\" switch",
+		(char *)NULL);
+	    goto error;
+	}
+	if ((varName != NULL) && (varName[0] != '\0')) {
+	    Tcl_AppendResult(interp,
+		"can't specify more than one vector with \"-variable\" switch",
+		(char *)NULL);
+	    goto error;
+	}
+    }
+    for (i = 0; i < count; i++) {
+	size = first = last = 0;
+	leftParen = strchr(nameArr[i], '(');
+	rightParen = strchr(nameArr[i], ')');
+	if (((leftParen != NULL) && (rightParen == NULL)) ||
+	    ((leftParen == NULL) && (rightParen != NULL)) ||
+	    (leftParen > rightParen)) {
+	    Tcl_AppendResult(interp, "bad vector specification \"", nameArr[i],
+		"\"", (char *)NULL);
+	    goto error;
+	}
+	if (leftParen != NULL) {
+	    int result;
+	    char *colon;
+
+	    *rightParen = '\0';
+	    colon = strchr(leftParen + 1, ':');
+	    if (colon != NULL) {
+
+		/* Specification is in the form vecName(first:last) */
+		*colon = '\0';
+		result = Tcl_GetInt(interp, leftParen + 1, &first);
+		if ((*(colon + 1) != '\0') && (result == TCL_OK)) {
+		    result = Tcl_GetInt(interp, colon + 1, &last);
+		    if (first > last) {
+			Tcl_AppendResult(interp, "bad vector range \"",
+			    nameArr[i], "\"", (char *)NULL);
+			result = TCL_ERROR;
+		    }
+		    size = (last - first) + 1;
+		}
+		*colon = ':';
+	    } else {
+		/* Specification is in the form vecName(size) */
+		result = Tcl_GetInt(interp, leftParen + 1, &size);
+	    }
+	    *rightParen = ')';
+	    if (result != TCL_OK) {
+		goto error;
+	    }
+	    if (size < 0) {
+		Tcl_AppendResult(interp, "bad vector size \"", nameArr[i], "\"",
+		    (char *)NULL);
+		goto error;
+	    }
+	}
+	if (leftParen != NULL) {
+	    *leftParen = '\0';
+	}
+	/*
+	 * By default, we create a Tcl command by the name of the vector.
+	 */
+	vPtr = Blt_VectorCreate(dataPtr, nameArr[i],
+	    (cmdName == NULL) ? nameArr[i] : cmdName,
+	    (varName == NULL) ? nameArr[i] : varName,
+	    &isNew);
+	if (leftParen != NULL) {
+	    *leftParen = '(';
+	}
+	if (vPtr == NULL) {
+	    goto error;
+	}
+	vPtr->freeOnUnset = freeOnUnset;
+	vPtr->flush = flush;
+	vPtr->offset = first;
+	if (size > 0) {
+	    if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+		goto error;
+	    }
+	}
+	if (!isNew) {
+	    if (vPtr->flush) {
+		Blt_VectorFlushCache(vPtr);
+	    }
+	    Blt_VectorUpdateClients(vPtr);
+	}
+    }
+    Blt_Free(nameArr);
+    if (vPtr != NULL) {
+	/* Return the name of the last vector created  */
+	Tcl_SetResult(interp, vPtr->name, TCL_VOLATILE);
+    }
+    return TCL_OK;
+  error:
+    Blt_Free(nameArr);
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ *	Creates a Tcl command, and array variable representing an
+ *	instance of a vector.
+ *
+ *	vector a
+ *	vector b(20)
+ *	vector c(-5:14)
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreateOp(clientData, interp, argc, argv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return VectorCreate2(clientData, interp, 2, argc, argv);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorDestroyOp --
+ *
+ *	Destroys the vector and its related Tcl command and array
+ *	variable (if they exist).
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	Deletes the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorDestroyOp(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    VectorObject *vPtr;
+    register int i;
+
+    for (i = 2; i < argc; i++) {
+	if (Blt_VectorLookupName(dataPtr, argv[i], &vPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Blt_VectorFree(vPtr);
+    }
+    return TCL_OK;
+}
+
+static Blt_OpSpec vectorCmdOps[] =
+{
+    {"create", 1, (Blt_Op)VectorCreateOp, 3, 0,
+	"vecName ?vecName...? ?switches...?",},
+    {"destroy", 1, (Blt_Op)VectorDestroyOp, 3, 0,
+	"vecName ?vecName...?",},
+    {"names", 1, (Blt_Op)VectorNamesOp, 2, 3, "?pattern?...",},
+};
+
+static int nCmdOps = sizeof(vectorCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+VectorCmd(clientData, interp, argc, argv)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+
+    /*
+     * Try to replicate the old vector command's behavior:
+     */
+    if (argc > 1) {
+	char c;
+	register int i;
+	register Blt_OpSpec *specPtr;
+
+	c = argv[1][0];
+	for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) {
+	    if ((c == specPtr->name[0]) &&
+		(strcmp(argv[1], specPtr->name) == 0)) {
+		goto doOp;
+	    }
+	}
+	/*
+	 * The first argument is not an operation, so assume that its
+	 * actually the name of a vector to be created
+	 */
+	return VectorCreate2(clientData, interp, 1, argc, argv);
+    }
+  doOp:
+    /* Do the usual vector operation lookup now. */
+    proc = Blt_GetOp(interp, nCmdOps, vectorCmdOps, BLT_OP_ARG1, argc, argv,0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, argc, argv);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * VectorInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting the "vector" command
+ *	is deleted.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Destroys the math and index hash tables.  In addition removes
+ *	the hash table managing all vector names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+VectorInterpDeleteProc(clientData, interp)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    VectorObject *vPtr;
+    
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	vPtr = (VectorObject *)Blt_GetHashValue(hPtr);
+	vPtr->hashPtr = NULL;
+	Blt_VectorFree(vPtr);
+    }
+    Blt_DeleteHashTable(&(dataPtr->vectorTable));
+
+    /* If any user-defined math functions were installed, remove them.  */
+    Blt_DeleteHashTable(&(dataPtr->mathProcTable));
+
+    Blt_DeleteHashTable(&(dataPtr->indexProcTable));
+    Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+VectorInterpData *
+Blt_VectorGetInterpData(interp)
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (VectorInterpData *)
+	Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(VectorInterpData));
+	assert(dataPtr);
+	dataPtr->interp = interp;
+	dataPtr->nextId = 0;
+	Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc,
+		 dataPtr);
+	Blt_InitHashTable(&(dataPtr->vectorTable), BLT_STRING_KEYS);
+	Blt_InitHashTable(&(dataPtr->mathProcTable), BLT_STRING_KEYS);
+	Blt_InitHashTable(&(dataPtr->indexProcTable), BLT_STRING_KEYS);
+	srand48(time((time_t *) NULL));
+    }
+    return dataPtr;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_VectorInit --
+ *
+ *	This procedure is invoked to initialize the "vector" command.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Creates the new command and adds a new entry into a global Tcl
+ *	associative array.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+int
+Blt_VectorInit(interp)
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    static Blt_CmdSpec cmdSpec = {"vector", VectorCmd, };
+    
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /* 
+     * This routine may be run several times in the same interpreter. 
+     * For example, if someone tries to initial the BLT commands from 
+     * another namespace. Keep a reference count, so we know when it's 
+     * safe to clean up.
+     */
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+
+
+/* C Application interface to vectors */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_CreateVector --
+ *
+ *	Creates a new vector by the name and size.
+ *
+ * Results:
+ *	A standard Tcl result.  If the new array size is invalid or a
+ *	vector already exists by that name, TCL_ERROR is returned.
+ *	Otherwise TCL_OK is returned and the new vector is created.
+ *
+ * Side Effects:
+ *	Memory will be allocated for the new vector.  A new Tcl command
+ *	and Tcl array variable will be created.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+int
+Blt_CreateVector2(interp, vecName, cmdName, varName, initialSize, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *vecName;
+    char *cmdName, *varName;
+    int initialSize;
+    Blt_Vector **vecPtrPtr;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    VectorObject *vPtr;
+    int isNew;
+    char *nameCopy;
+
+    if (initialSize < 0) {
+	Tcl_AppendResult(interp, "bad vector size \"", Blt_Itoa(initialSize),
+	    "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    dataPtr = Blt_VectorGetInterpData(interp);
+
+    nameCopy = Blt_Strdup(vecName);
+    vPtr = Blt_VectorCreate(dataPtr, nameCopy, cmdName, varName, &isNew);
+    Blt_Free(nameCopy);
+
+    if (vPtr == NULL) {
+	return TCL_ERROR;
+    }
+    if (initialSize > 0) {
+	if (Blt_VectorChangeLength(vPtr, initialSize) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+    }
+    if (vecPtrPtr != NULL) {
+	*vecPtrPtr = (Blt_Vector *) vPtr;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_CreateVector(interp, name, size, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *name;
+    int size;
+    Blt_Vector **vecPtrPtr;
+{
+    return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVector --
+ *
+ *	Deletes the vector of the given name.  All clients with
+ *	designated callback routines will be notified.
+ *
+ * Results:
+ *	A standard Tcl result.  If no vector exists by that name,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *	vector is deleted.
+ *
+ * Side Effects:
+ *	Memory will be released for the new vector.  Both the Tcl
+ *	command and array variable will be deleted.  All clients which
+ *	set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVector(vecPtr)
+    Blt_Vector *vecPtr;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    Blt_VectorFree(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVectorByName --
+ *
+ *	Deletes the vector of the given name.  All clients with
+ *	designated callback routines will be notified.
+ *
+ * Results:
+ *	A standard Tcl result.  If no vector exists by that name,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *	vector is deleted.
+ *
+ * Side Effects:
+ *	Memory will be released for the new vector.  Both the Tcl
+ *	command and array variable will be deleted.  All clients which
+ *	set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVectorByName(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    VectorObject *vPtr;
+    char *nameCopy;
+    int result;
+
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseElement routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    dataPtr = Blt_VectorGetInterpData(interp);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+
+    if (result != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_VectorFree(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists2 --
+ *
+ *	Returns whether the vector associated with the client token
+ *	still exists.
+ *
+ * Results:
+ *	Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists2(interp, vecName)
+    Tcl_Interp *interp;
+    char *vecName;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) {
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists --
+ *
+ *	Returns whether the vector associated with the client token
+ *	still exists.
+ *
+ * Results:
+ *	Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists(interp, vecName)
+    Tcl_Interp *interp;
+    char *vecName;
+{
+    char *nameCopy;
+    int result;
+
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(vecName);
+    result = Blt_VectorExists2(interp, nameCopy);
+    Blt_Free(nameCopy);
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVector --
+ *
+ *	Returns a pointer to the vector associated with the given name.
+ *
+ * Results:
+ *	A standard Tcl result.  If there is no vector "name", TCL_ERROR
+ *	is returned.  Otherwise TCL_OK is returned and vecPtrPtr will
+ *	point to the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVector(interp, name, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *name;
+    Blt_Vector **vecPtrPtr;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    VectorObject *vPtr;
+    char *nameCopy;
+    int result;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+    if (result != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Blt_VectorUpdateRange(vPtr);
+    *vecPtrPtr = (Blt_Vector *) vPtr;
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ *	Resets the vector data.  This is called by a client to
+ *	indicate that the vector data has changed.  The vector does
+ *	not need to point to different memory.  Any clients of the
+ *	vector will be notified of the change.
+ *
+ * Results:
+ *	A standard Tcl result.  If the new array size is invalid,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *	new vector data is recorded.
+ *
+ * Side Effects:
+ *	Any client designated callbacks will be posted.  Memory may
+ *	be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResetVector(vecPtr, valueArr, length, size, freeProc)
+    Blt_Vector *vecPtr;
+    double *valueArr;		/* Array containing the elements of the
+				 * vector. If NULL, indicates to reset the
+				 * vector.*/
+    int length;			/* The number of elements that the vector 
+				 * currently holds. */
+    int size;			/* The maximum number of elements that the
+				 * array can hold. */
+    Tcl_FreeProc *freeProc;	/* Address of memory deallocation routine
+				 * for the array of values.  Can also be
+				 * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (size < 0) {
+	Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL);
+	return TCL_ERROR;
+    }
+    return Blt_VectorReset(vPtr, valueArr, length, size, freeProc);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResizeVector --
+ *
+ *	Changes the size of the vector.  All clients with designated
+ *	callback routines will be notified of the size change.
+ *
+ * Results:
+ *	A standard Tcl result.  If no vector exists by that name,
+ *	TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *	vector is resized.
+ *
+ * Side Effects:
+ *	Memory may be reallocated for the new vector size.  All clients
+ *	which set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResizeVector(vecPtr, length)
+    Blt_Vector *vecPtr;
+    int length;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (Blt_VectorChangeLength(vPtr, length) != TCL_OK) {
+	Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name,
+	    "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (vPtr->flush) {
+	Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_AllocVectorId --
+ *
+ *	Creates an identifier token for an existing vector.  The
+ *	identifier is used by the client routines to get call backs
+ *	when (and if) the vector changes.
+ *
+ * Results:
+ *	A standard Tcl result.  If "vecName" is not associated with
+ *	a vector, TCL_ERROR is returned and interp->result is filled
+ *	with an error message.
+ *
+ *--------------------------------------------------------------
+ */
+Blt_VectorId
+Blt_AllocVectorId(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    VectorObject *vPtr;
+    VectorClient *clientPtr;
+    Blt_VectorId clientId;
+    int result;
+    char *nameCopy;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+
+    if (result != TCL_OK) {
+	return (Blt_VectorId) 0;
+    }
+    /* Allocate a new client structure */
+    clientPtr = Blt_Calloc(1, sizeof(VectorClient));
+    assert(clientPtr);
+    clientPtr->magic = VECTOR_MAGIC;
+
+    /* Add the new client to the server's list of clients */
+    clientPtr->linkPtr = Blt_ChainAppend(vPtr->chainPtr, clientPtr);
+    clientPtr->serverPtr = vPtr;
+    clientId = (Blt_VectorId) clientPtr;
+    return clientId;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_SetVectorChangedProc --
+ *
+ *	Sets the routine to be called back when the vector is changed
+ *	or deleted.  *clientData* will be provided as an argument. If
+ *	*proc* is NULL, no callback will be made.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	The designated routine will be called when the vector is changed
+ *	or deleted.
+ *
+ * -----------------------------------------------------------------------
+ */
+void
+Blt_SetVectorChangedProc(clientId, proc, clientData)
+    Blt_VectorId clientId;	/* Client token identifying the vector */
+    Blt_VectorChangedProc *proc;/* Address of routine to call when the contents
+				 * of the vector change. If NULL, no routine
+				 * will be called */
+    ClientData clientData;	/* One word of information to pass along when
+				 * the above routine is called */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+	return;			/* Not a valid token */
+    }
+    clientPtr->clientData = clientData;
+    clientPtr->proc = proc;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_FreeVectorId --
+ *
+ *	Releases the token for an existing vector.  This indicates
+ *	that the client is no longer interested the vector.  Any
+ *	previously specified callback routine will no longer be
+ *	invoked when (and if) the vector changes.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Any previously specified callback routine will no longer be
+ *	invoked when (and if) the vector changes.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_FreeVectorId(clientId)
+    Blt_VectorId clientId;	/* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+	return;			/* Not a valid token */
+    }
+    if (clientPtr->serverPtr != NULL) {
+	/* Remove the client from the server's list */
+	Blt_ChainDeleteLink(clientPtr->serverPtr->chainPtr, clientPtr->linkPtr);
+    }
+    Blt_Free(clientPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_NameOfVectorId --
+ *
+ *	Returns the name of the vector (and array variable).
+ *
+ * Results:
+ *	The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+char *
+Blt_NameOfVectorId(clientId)
+    Blt_VectorId clientId;	/* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) {
+	return NULL;
+    }
+    return clientPtr->serverPtr->name;
+}
+
+char *
+Blt_NameOfVector(vecPtr)
+    Blt_Vector *vecPtr;		/* Vector to query. */
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+    return vPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_VectorNotifyPending --
+ *
+ *	Returns the name of the vector (and array variable).
+ *
+ * Results:
+ *	The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_VectorNotifyPending(clientId)
+    Blt_VectorId clientId;	/* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if ((clientPtr == NULL) || (clientPtr->magic != VECTOR_MAGIC) || 
+	(clientPtr->serverPtr == NULL)) {
+	return 0;
+    }
+    return (clientPtr->serverPtr->notifyFlags & NOTIFY_PENDING);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVectorById --
+ *
+ *	Returns a pointer to the vector associated with the client
+ *	token.
+ *
+ * Results:
+ *	A standard Tcl result.  If the client token is not associated
+ *	with a vector any longer, TCL_ERROR is returned. Otherwise,
+ *	TCL_OK is returned and vecPtrPtr will point to vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVectorById(interp, clientId, vecPtrPtr)
+    Tcl_Interp *interp;
+    Blt_VectorId clientId;	/* Client token identifying the vector */
+    Blt_Vector **vecPtrPtr;
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+	Tcl_AppendResult(interp, "bad vector token", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (clientPtr->serverPtr == NULL) {
+	Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Blt_VectorUpdateRange(clientPtr->serverPtr);
+    *vecPtrPtr = (Blt_Vector *) clientPtr->serverPtr;
+    return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InstallIndexProc(interp, string, procPtr)
+    Tcl_Interp *interp;
+    char *string;
+    Blt_VectorIndexProc *procPtr; /* Pointer to function to be called
+				   * when the vector finds the named index.
+				   * If NULL, this indicates to remove
+				   * the index from the table.
+				   */
+{
+    VectorInterpData *dataPtr;	/* Interpreter-specific data. */
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    hPtr = Blt_CreateHashEntry(&(dataPtr->indexProcTable), string, &isNew);
+    if (procPtr == NULL) {
+	Blt_DeleteHashEntry(&(dataPtr->indexProcTable), hPtr);
+    } else {
+	Blt_SetHashValue(hPtr, procPtr);
+    }
+}
Index: trunk/kitgen/8.x/blt/generic/bltVector.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltVector.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltVector.h	(revision 175)
@@ -0,0 +1,122 @@
+
+/*
+ * bltVector.h --
+ *
+ * Copyright 1993-2000 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_VECTOR_H
+#define _BLT_VECTOR_H
+
+typedef enum {
+    BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */
+    BLT_VECTOR_NOTIFY_DESTROY	/* The vector has been destroyed and the client
+				 * should no longer use its data (calling
+				 * Blt_FreeVectorId) */
+} Blt_VectorNotify;
+
+typedef struct Blt_VectorIdStruct *Blt_VectorId;
+
+typedef void (Blt_VectorChangedProc) _ANSI_ARGS_((Tcl_Interp *interp,
+	ClientData clientData, Blt_VectorNotify notify));
+
+typedef struct {
+    double *valueArr;		/* Array of values (possibly malloc-ed) */
+    int numValues;		/* Number of values in the array */
+    int arraySize;		/* Size of the allocated space */
+    double min, max;		/* Minimum and maximum values in the vector */
+    int dirty;			/* Indicates if the vector has been updated */
+    int reserved;		/* Reserved for future use */
+
+} Blt_Vector;
+
+typedef double (Blt_VectorIndexProc) _ANSI_ARGS_((Blt_Vector * vecPtr));
+
+typedef enum {
+    BLT_MATH_FUNC_SCALAR = 1,	/* The function returns a single double
+				 * precision value. */
+    BLT_MATH_FUNC_VECTOR	/* The function processes the entire vector. */
+} Blt_MathFuncType;
+
+/*
+ * To be safe, use the macros below, rather than the fields of the
+ * structure directly.
+ *
+ * The Blt_Vector is basically an opaque type.  But it's also the
+ * actual memory address of the vector itself.  I wanted to make the
+ * API as unobtrusive as possible.  So instead of giving you a copy of
+ * the vector, providing various functions to access and update the
+ * vector, you get your hands on the actual memory (array of doubles)
+ * shared by all the vector's clients.
+ *
+ * The trade-off for speed and convenience is safety.  You can easily
+ * break things by writing into the vector when other clients are
+ * using it.  Use Blt_ResetVector to get around this.  At least the
+ * macros are a reminder it isn't really safe to reset the data
+ * fields, except by the API routines.
+ */
+#define Blt_VecData(v)		((v)->valueArr)
+#define Blt_VecLength(v)	((v)->numValues)
+#define Blt_VecSize(v)		((v)->arraySize)
+#define Blt_VecDirty(v)		((v)->dirty)
+
+EXTERN double Blt_VecMin _ANSI_ARGS_((Blt_Vector *vPtr));
+EXTERN double Blt_VecMax _ANSI_ARGS_((Blt_Vector *vPtr));
+
+EXTERN Blt_VectorId Blt_AllocVectorId _ANSI_ARGS_((Tcl_Interp *interp,
+	char *vecName));
+
+EXTERN void Blt_SetVectorChangedProc _ANSI_ARGS_((Blt_VectorId clientId,
+	Blt_VectorChangedProc * proc, ClientData clientData));
+
+EXTERN void Blt_FreeVectorId _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN int Blt_GetVectorById _ANSI_ARGS_((Tcl_Interp *interp,
+	Blt_VectorId clientId, Blt_Vector **vecPtrPtr));
+
+EXTERN char *Blt_NameOfVectorId _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN char *Blt_NameOfVector _ANSI_ARGS_((Blt_Vector *vecPtr));
+
+EXTERN int Blt_VectorNotifyPending _ANSI_ARGS_((Blt_VectorId clientId));
+
+EXTERN int Blt_CreateVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName,
+	int size, Blt_Vector ** vecPtrPtr));
+
+EXTERN int Blt_GetVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName,
+	Blt_Vector **vecPtrPtr));
+
+EXTERN int Blt_VectorExists _ANSI_ARGS_((Tcl_Interp *interp, char *vecName));
+
+EXTERN int Blt_ResetVector _ANSI_ARGS_((Blt_Vector *vecPtr, double *dataArr,
+	int nValues, int arraySize, Tcl_FreeProc *freeProc));
+
+EXTERN int Blt_ResizeVector _ANSI_ARGS_((Blt_Vector *vecPtr, int nValues));
+
+EXTERN int Blt_DeleteVectorByName _ANSI_ARGS_((Tcl_Interp *interp,
+	char *vecName));
+
+EXTERN int Blt_DeleteVector _ANSI_ARGS_((Blt_Vector *vecPtr));
+
+EXTERN void Blt_InstallIndexProc _ANSI_ARGS_((Tcl_Interp *interp,
+	char *indexName, Blt_VectorIndexProc * procPtr));
+
+#endif /* _BLT_VECTOR_H */
Index: trunk/kitgen/8.x/blt/generic/bltWindow.c
===================================================================
--- trunk/kitgen/8.x/blt/generic/bltWindow.c	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/bltWindow.c	(revision 175)
@@ -0,0 +1,2214 @@
+/*
+ * bltWindow.c --
+ *
+ *	This module implements additional window functionality for
+ *	the BLT toolkit, such as transparent Tk windows,
+ *	and reparenting Tk windows.
+ *
+ * Copyright 1991-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#include <X11/Xlib.h>
+#ifndef WIN32
+#include <X11/Xproto.h>
+#endif
+
+typedef struct TkIdStackStruct TkIdStack;
+typedef struct TkErrorHandlerStruct TkErrorHandler;
+typedef struct TkSelectionInfoStruct TkSelectionInfo;
+typedef struct TkClipboardTargetStruct TkClipboardTarget;
+
+#ifndef WIN32
+typedef struct TkWindowStruct TkWindow;
+#endif
+typedef struct TkWindowEventStruct TkWindowEvent;
+typedef struct TkMainInfoStruct TkMainInfo;
+typedef struct TkEventHandlerStruct TkEventHandler;
+typedef struct TkSelHandlerStruct TkSelHandler;
+typedef struct TkWinInfoStruct TkWinInfo;
+typedef struct TkClassProcsStruct TkClassProcs;
+typedef struct TkWindowPrivateStruct TkWindowPrivate;
+typedef struct TkGrabEventStruct TkGrabEvent;
+typedef struct TkColormapStruct TkColormap;
+typedef struct TkStressedCmapStruct TkStressedCmap;
+typedef struct TkWmInfoStruct TkWmInfo;
+
+#ifdef XNQueryInputStyle
+#define TK_USE_INPUT_METHODS
+#endif
+
+/*
+ * This defines whether we should try to use XIM over-the-spot style
+ * input.  Allow users to override it.  It is a much more elegant use
+ * of XIM, but uses a bit more memory.
+ */
+#ifndef TK_XIM_SPOT
+#   define TK_XIM_SPOT	1
+#endif
+
+#ifndef TK_REPARENTED
+#define TK_REPARENTED 	0
+#endif
+
+#if (TK_VERSION_NUMBER >= _VERSION(8,5,0))
+
+typedef struct TkCaret {
+    struct TkWindow *winPtr;	/* The window on which we requested caret
+				 * placement. */
+    int x;			/* Relative x coord of the caret. */
+    int y;			/* Relative y coord of the caret. */
+    int height;			/* Specified height of the window. */
+} TkCaret;
+
+/*
+ * One of the following structures is maintained for each display containing a
+ * window managed by Tk. In part, the structure is used to store thread-
+ * specific data, since each thread will have its own TkDisplay structure.
+ */
+
+typedef struct TkDisplayStruct {
+    Display *display;		/* Xlib's info about display. */
+    struct TkDisplay *nextPtr;	/* Next in list of all displays. */
+    char *name;			/* Name of display (with any screen identifier
+				 * removed). Malloc-ed. */
+    Time lastEventTime;		/* Time of last event received for this
+				 * display. */
+
+    /*
+     * Information used primarily by tk3d.c:
+     */
+
+    int borderInit;		/* 0 means borderTable needs initializing. */
+    Tcl_HashTable borderTable;	/* Maps from color name to TkBorder
+				 * structure. */
+
+    /*
+     * Information used by tkAtom.c only:
+     */
+
+    int atomInit;		/* 0 means stuff below hasn't been initialized
+				 * yet. */
+    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
+    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */
+
+    /*
+     * Information used primarily by tkBind.c:
+     */
+
+    int bindInfoStale;		/* Non-zero means the variables in this part
+				 * of the structure are potentially incorrect
+				 * and should be recomputed. */
+    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to "mode shift". If no such
+				 * modifier, than this is zero. */
+    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key. If no such
+				 * modifier, then this is zero. */
+    unsigned int altModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key. If no such
+				 * modifier, then this is zero. */
+    enum {LU_IGNORE, LU_CAPS, LU_SHIFT} lockUsage;
+				/* Indicates how to interpret lock
+				 * modifier. */
+    int numModKeyCodes;		/* Number of entries in modKeyCodes array
+				 * below. */
+    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for all
+				 * of the keys that have modifiers associated
+				 * with them. Malloc'ed, but may be NULL. */
+
+    /*
+     * Information used by tkBitmap.c only:
+     */
+
+    int bitmapInit;		/* 0 means tables above need initializing. */
+    int bitmapAutoNumber;	/* Used to number bitmaps. */
+    Tcl_HashTable bitmapNameTable;
+				/* Maps from name of bitmap to the first
+				 * TkBitmap record for that name. */
+    Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap
+				 * structure for the bitmap. */
+    Tcl_HashTable bitmapDataTable;
+				/* Used by Tk_GetBitmapFromData to map from a
+				 * collection of in-core data about a bitmap
+				 * to a reference giving an automatically-
+				 * generated name for the bitmap. */
+
+    /*
+     * Information used by tkCanvas.c only:
+     */
+
+    int numIdSearches;
+    int numSlowSearches;
+
+    /*
+     * Used by tkColor.c only:
+     */
+
+    int colorInit;		/* 0 means color module needs initializing. */
+    TkStressedCmap *stressPtr;	/* First in list of colormaps that have filled
+				 * up, so we have to pick an approximate
+				 * color. */
+    Tcl_HashTable colorNameTable;
+				/* Maps from color name to TkColor structure
+				 * for that color. */
+    Tcl_HashTable colorValueTable;
+				/* Maps from integer RGB values to TkColor
+				 * structures. */
+
+    /*
+     * Used by tkCursor.c only:
+     */
+
+    int cursorInit;		/* 0 means cursor module need initializing. */
+    Tcl_HashTable cursorNameTable;
+				/* Maps from a string name to a cursor to the
+				 * TkCursor record for the cursor. */
+    Tcl_HashTable cursorDataTable;
+				/* Maps from a collection of in-core data
+				 * about a cursor to a TkCursor structure. */
+    Tcl_HashTable cursorIdTable;
+				/* Maps from a cursor id to the TkCursor
+				 * structure for the cursor. */
+    char cursorString[20];	/* Used to store a cursor id string. */
+    Font cursorFont;		/* Font to use for standard cursors. None
+				 * means font not loaded yet. */
+
+    /*
+     * Information used by tkError.c only:
+     */
+
+    struct TkErrorHandler *errorPtr;
+				/* First in list of error handlers for this
+				 * display. NULL means no handlers exist at
+				 * present. */
+    int deleteCount;		/* Counts # of handlers deleted since last
+				 * time inactive handlers were garbage-
+				 * collected. When this number gets big,
+				 * handlers get cleaned up. */
+
+    /*
+     * Used by tkEvent.c only:
+     */
+
+    struct TkWindowEvent *delayedMotionPtr;
+				/* Points to a malloc-ed motion event whose
+				 * processing has been delayed in the hopes
+				 * that another motion event will come along
+				 * right away and we can merge the two of them
+				 * together. NULL means that there is no
+				 * delayed motion event. */
+
+    /*
+     * Information used by tkFocus.c only:
+     */
+
+    int focusDebug;		/* 1 means collect focus debugging
+				 * statistics. */
+    struct TkWindow *implicitWinPtr;
+				/* If the focus arrived at a toplevel window
+				 * implicitly via an Enter event (rather than
+				 * via a FocusIn event), this points to the
+				 * toplevel window. Otherwise it is NULL. */
+    struct TkWindow *focusPtr;	/* Points to the window on this display that
+				 * should be receiving keyboard events. When
+				 * multiple applications on the display have
+				 * the focus, this will refer to the innermost
+				 * window in the innermost application. This
+				 * information isn't used on Windows, but it's
+				 * needed on the Mac, and also on X11 when XIM
+				 * processing is being done. */
+
+    /*
+     * Information used by tkGC.c only:
+     */
+
+    Tcl_HashTable gcValueTable; /* Maps from a GC's values to a TkGC structure
+				 * describing a GC with those values. */
+    Tcl_HashTable gcIdTable;    /* Maps from a GC to a TkGC. */
+    int gcInit;			/* 0 means the tables below need
+				 * initializing. */
+
+    /*
+     * Information used by tkGeometry.c only:
+     */
+
+    Tcl_HashTable maintainHashTable;
+				/* Hash table that maps from a master's
+				 * Tk_Window token to a list of slaves managed
+				 * by that master. */
+    int geomInit;
+
+    /*
+     * Information used by tkGet.c only:
+     */
+
+    Tcl_HashTable uidTable;	/* Stores all Tk_Uid used in a thread. */
+    int uidInit;		/* 0 means uidTable needs initializing. */
+
+    /*
+     * Information used by tkGrab.c only:
+     */
+
+    struct TkWindow *grabWinPtr;/* Window in which the pointer is currently
+				 * grabbed, or NULL if none. */
+    struct TkWindow *eventualGrabWinPtr;
+				/* Value that grabWinPtr will have once the
+				 * grab event queue (below) has been
+				 * completely emptied. */
+    struct TkWindow *buttonWinPtr;
+				/* Window in which first mouse button was
+				 * pressed while grab was in effect, or NULL
+				 * if no such press in effect. */
+    struct TkWindow *serverWinPtr;
+				/* If no application contains the pointer then
+				 * this is NULL. Otherwise it contains the
+				 * last window for which we've gotten an Enter
+				 * or Leave event from the server (i.e. the
+				 * last window known to have contained the
+				 * pointer). Doesn't reflect events that were
+				 * synthesized in tkGrab.c. */
+    TkGrabEvent *firstGrabEventPtr;
+				/* First in list of enter/leave events
+				 * synthesized by grab code. These events must
+				 * be processed in order before any other
+				 * events are processed. NULL means no such
+				 * events. */
+    TkGrabEvent *lastGrabEventPtr;
+				/* Last in list of synthesized events, or NULL
+				 * if list is empty. */
+    int grabFlags;		/* Miscellaneous flag values. See definitions
+				 * in tkGrab.c. */
+
+    /*
+     * Information used by tkGrid.c only:
+     */
+
+    int gridInit;		/* 0 means table below needs initializing. */
+    Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to corresponding
+				 * Grid structures. */
+
+    /*
+     * Information used by tkImage.c only:
+     */
+
+    int imageId;		/* Value used to number image ids. */
+
+    /*
+     * Information used by tkMacWinMenu.c only:
+     */
+
+    int postCommandGeneration;
+
+    /*
+     * Information used by tkOption.c only.
+     */
+
+    /*
+     * Information used by tkPack.c only.
+     */
+
+    int packInit;		/* 0 means table below needs initializing. */
+    Tcl_HashTable packerHashTable;
+				/* Maps from Tk_Window tokens to corresponding
+				 * Packer structures. */
+
+    /*
+     * Information used by tkPlace.c only.
+     */
+
+    int placeInit;		/* 0 means tables below need initializing. */
+    Tcl_HashTable masterTable;	/* Maps from Tk_Window toke to the Master
+				 * structure for the window, if it exists. */
+    Tcl_HashTable slaveTable;	/* Maps from Tk_Window toke to the Slave
+				 * structure for the window, if it exists. */
+
+    /*
+     * Information used by tkSelect.c and tkClipboard.c only:
+     */
+
+    struct TkSelectionInfo *selectionInfoPtr;
+				/* First in list of selection information
+				 * records. Each entry contains information
+				 * about the current owner of a particular
+				 * selection on this display. */
+    Atom multipleAtom;		/* Atom for MULTIPLE. None means selection
+				 * stuff isn't initialized. */
+    Atom incrAtom;		/* Atom for INCR. */
+    Atom targetsAtom;		/* Atom for TARGETS. */
+    Atom timestampAtom;		/* Atom for TIMESTAMP. */
+    Atom textAtom;		/* Atom for TEXT. */
+    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
+    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
+    Atom windowAtom;		/* Atom for TK_WINDOW. */
+    Atom clipboardAtom;		/* Atom for CLIPBOARD. */
+    Atom utf8Atom;		/* Atom for UTF8_STRING. */
+
+    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
+				 * retrieve selections between processes. NULL
+				 * means clipboard info hasn't been
+				 * initialized. */
+    int clipboardActive;	/* 1 means we currently own the clipboard
+				 * selection, 0 means we don't. */
+    struct TkMainInfo *clipboardAppPtr;
+				/* Last application that owned clipboard. */
+    struct TkClipboardTarget *clipTargetPtr;
+				/* First in list of clipboard type information
+				 * records. Each entry contains information
+				 * about the buffers for a given selection
+				 * target. */
+
+    /*
+     * Information used by tkSend.c only:
+     */
+
+    Tk_Window commTkwin;	/* Window used for communication between
+				 * interpreters during "send" commands. NULL
+				 * means send info hasn't been initialized
+				 * yet. */
+    Atom commProperty;		/* X's name for comm property. */
+    Atom registryProperty;	/* X's name for property containing registry
+				 * of interpreter names. */
+    Atom appNameProperty;	/* X's name for property used to hold the
+				 * application name on each comm window. */
+
+    /*
+     * Information used by tkXId.c only:
+     */
+
+    struct TkIdStack *idStackPtr;
+				/* First in list of chunks of free resource
+				 * identifiers, or NULL if there are no free
+				 * resources. */
+    XID (*defaultAllocProc) (Display *display);
+				/* Default resource allocator for display. */
+    struct TkIdStack *windowStackPtr;
+				/* First in list of chunks of window ids that
+				 * can't be reused right now. */
+    Tcl_TimerToken idCleanupScheduled;
+				/* If set, it means a call to WindowIdCleanup
+				 * has already been scheduled, 0 means it
+				 * hasn't. */
+
+    /*
+     * Information used by tkUnixWm.c and tkWinWm.c only:
+     */
+
+    struct TkWmInfo *firstWmPtr;/* Points to first top-level window. */
+    struct TkWmInfo *foregroundWmPtr;
+				/* Points to the foreground window. */
+
+    /*
+     * Information maintained by tkWindow.c for use later on by tkXId.c:
+     */
+
+    int destroyCount;		/* Number of Tk_DestroyWindow operations in
+				 * progress. */
+    unsigned long lastDestroyRequest;
+				/* Id of most recent XDestroyWindow request;
+				 * can re-use ids in windowStackPtr when
+				 * server has seen this request and event
+				 * queue is empty. */
+
+    /*
+     * Information used by tkVisual.c only:
+     */
+
+    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
+				 * allocated for this display. */
+
+    /*
+     * Miscellaneous information:
+     */
+
+#ifdef TK_USE_INPUT_METHODS
+    XIM inputMethod;		/* Input method for this display. */
+    XIMStyle inputStyle;	/* Input style selected for this display. */
+    XFontSet inputXfs;		/* XFontSet cached for over-the-spot XIM. */
+#endif /* TK_USE_INPUT_METHODS */
+    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */
+
+    int refCount;		/* Reference count of how many Tk applications
+				 * are using this display. Used to clean up
+				 * the display when we no longer have any Tk
+				 * applications using it. */
+
+    /*
+     * The following field were all added for Tk8.3
+     */
+
+    int mouseButtonState;	/* Current mouse button state for this
+				 * display. */
+    Window mouseButtonWindow;	/* Window the button state was set in, added
+				 * in Tk 8.4. */
+    Window warpWindow;
+    int warpX;
+    int warpY;
+
+    /*
+     * The following field(s) were all added for Tk8.4
+     */
+
+    unsigned int flags;		/* Various flag values: these are all defined
+				 * in below. */
+    TkCaret caret;		/* Information about the caret for this
+				 * display. This is not a pointer. */
+
+    int iconDataSize;		/* Size of default iconphoto image data. */
+    unsigned char *iconDataPtr;	/* Default iconphoto image data, if set. */
+} TkDisplay;
+
+#elif (TK_VERSION_NUMBER >= _VERSION(8,1,0))
+
+typedef struct TkCaret {
+    struct TkWindow *winPtr;	/* the window on which we requested caret
+				 * placement */
+    int x;			/* relative x coord of the caret */
+    int y;			/* relative y coord of the caret */
+    int height;			/* specified height of the window */
+} TkCaret;
+
+/*
+ * One of the following structures is maintained for each display
+ * containing a window managed by Tk.  In part, the structure is
+ * used to store thread-specific data, since each thread will have
+ * its own TkDisplay structure.
+ */
+
+typedef struct TkDisplayStruct {
+    Display *display;		/* Xlib's info about display. */
+    struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
+    char *name;			/* Name of display (with any screen
+				 * identifier removed).  Malloc-ed. */
+    Time lastEventTime;		/* Time of last event received for this
+				 * display. */
+
+    /*
+     * Information used primarily by tk3d.c:
+     */
+
+    int borderInit;		/* 0 means borderTable needs initializing. */
+    Tcl_HashTable borderTable;	/* Maps from color name to TkBorder
+				 * structure. */
+
+    /*
+     * Information used by tkAtom.c only:
+     */
+
+    int atomInit;		/* 0 means stuff below hasn't been
+				 * initialized yet. */
+    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
+    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */
+
+    /*
+     * Information used primarily by tkBind.c:
+     */
+
+    int bindInfoStale;		/* Non-zero means the variables in this
+				 * part of the structure are potentially
+				 * incorrect and should be recomputed. */
+    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to "mode shift".  If no
+				 * such modifier, than this is zero. */
+    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key.  If no
+				 * such modifier, then this is zero. */
+    unsigned int altModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key.  If no
+				 * such modifier, then this is zero. */
+    enum {
+	LU_IGNORE, LU_CAPS, LU_SHIFT
+    } lockUsage;		/* Indicates how to interpret lock modifier. */
+    int numModKeyCodes;		/* Number of entries in modKeyCodes array
+				 * below. */
+    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for
+				 * all of the keys that have modifiers
+				 * associated with them.  Malloc'ed, but
+				 * may be NULL. */
+
+    /*
+     * Information used by tkBitmap.c only:
+     */
+
+    int bitmapInit;		/* 0 means tables above need initializing. */
+    int bitmapAutoNumber;	/* Used to number bitmaps. */
+    Tcl_HashTable bitmapNameTable;
+				/* Maps from name of bitmap to the first
+				 * TkBitmap record for that name. */
+    Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap
+				 * structure for the bitmap. */
+    Tcl_HashTable bitmapDataTable;
+				/* Used by Tk_GetBitmapFromData to map from
+				 * a collection of in-core data about a
+				 * bitmap to a reference giving an auto-
+				 * matically-generated name for the bitmap. */
+
+    /*
+     * Information used by tkCanvas.c only:
+     */
+
+    int numIdSearches;
+    int numSlowSearches;
+
+    /*
+     * Used by tkColor.c only:
+     */
+
+    int colorInit;		/* 0 means color module needs initializing. */
+    TkStressedCmap *stressPtr;	/* First in list of colormaps that have
+				 * filled up, so we have to pick an
+				 * approximate color. */
+    Tcl_HashTable colorNameTable;
+				/* Maps from color name to TkColor structure
+				 * for that color. */
+    Tcl_HashTable colorValueTable;
+				/* Maps from integer RGB values to TkColor
+				 * structures. */
+
+    /*
+     * Used by tkCursor.c only:
+     */
+
+    int cursorInit;		/* 0 means cursor module need initializing. */
+    Tcl_HashTable cursorNameTable;
+				/* Maps from a string name to a cursor to the
+				 * TkCursor record for the cursor. */
+    Tcl_HashTable cursorDataTable;
+				/* Maps from a collection of in-core data
+				 * about a cursor to a TkCursor structure. */
+    Tcl_HashTable cursorIdTable;
+				/* Maps from a cursor id to the TkCursor
+				 * structure for the cursor. */
+    char cursorString[20];	/* Used to store a cursor id string. */
+    Font cursorFont;		/* Font to use for standard cursors.
+				 * None means font not loaded yet. */
+
+    /*
+     * Information used by tkError.c only:
+     */
+
+    struct TkErrorHandler *errorPtr;
+				/* First in list of error handlers
+				 * for this display.  NULL means
+				 * no handlers exist at present. */
+    int deleteCount;		/* Counts # of handlers deleted since
+				 * last time inactive handlers were
+				 * garbage-collected.  When this number
+				 * gets big, handlers get cleaned up. */
+
+    /*
+     * Used by tkEvent.c only:
+     */
+
+    struct TkWindowEvent *delayedMotionPtr;
+				/* Points to a malloc-ed motion event
+				 * whose processing has been delayed in
+				 * the hopes that another motion event
+				 * will come along right away and we can
+				 * merge the two of them together.  NULL
+				 * means that there is no delayed motion
+				 * event. */
+
+    /*
+     * Information used by tkFocus.c only:
+     */
+
+    int focusDebug;		/* 1 means collect focus debugging
+				 * statistics. */
+    struct TkWindow *implicitWinPtr;
+				/* If the focus arrived at a toplevel window
+				 * implicitly via an Enter event (rather
+				 * than via a FocusIn event), this points
+				 * to the toplevel window.  Otherwise it is
+				 * NULL. */
+    struct TkWindow *focusPtr;	/* Points to the window on this display that
+				 * should be receiving keyboard events.  When
+				 * multiple applications on the display have
+				 * the focus, this will refer to the
+				 * innermost window in the innermost
+				 * application.  This information isn't used
+				 * under Unix or Windows, but it's needed on
+				 * the Macintosh. */
+
+    /*
+     * Information used by tkGC.c only:
+     */
+
+    Tcl_HashTable gcValueTable;	/* Maps from a GC's values to a TkGC structure
+				 * describing a GC with those values. */
+    Tcl_HashTable gcIdTable;	/* Maps from a GC to a TkGC. */
+    int gcInit;			/* 0 means the tables below need
+				 * initializing. */
+
+    /*
+     * Information used by tkGeometry.c only:
+     */
+
+    Tcl_HashTable maintainHashTable;
+				/* Hash table that maps from a master's
+				 * Tk_Window token to a list of slaves
+				 * managed by that master. */
+    int geomInit;
+
+    /*
+     * Information used by tkGet.c only:
+     */
+
+    Tcl_HashTable uidTable;	/* Stores all Tk_Uids used in a thread. */
+    int uidInit;		/* 0 means uidTable needs initializing. */
+
+    /*
+     * Information used by tkGrab.c only:
+     */
+
+    struct TkWindow *grabWinPtr;
+				/* Window in which the pointer is currently
+				 * grabbed, or NULL if none. */
+    struct TkWindow *eventualGrabWinPtr;
+				/* Value that grabWinPtr will have once the
+				 * grab event queue (below) has been
+				 * completely emptied. */
+    struct TkWindow *buttonWinPtr;
+				/* Window in which first mouse button was
+				 * pressed while grab was in effect, or NULL
+				 * if no such press in effect. */
+    struct TkWindow *serverWinPtr;
+				/* If no application contains the pointer then
+				 * this is NULL.  Otherwise it contains the
+				 * last window for which we've gotten an
+				 * Enter or Leave event from the server (i.e.
+				 * the last window known to have contained
+				 * the pointer).  Doesn't reflect events
+				 * that were synthesized in tkGrab.c. */
+    TkGrabEvent *firstGrabEventPtr;
+				/* First in list of enter/leave events
+				 * synthesized by grab code.  These events
+				 * must be processed in order before any other
+				 * events are processed.  NULL means no such
+				 * events. */
+    TkGrabEvent *lastGrabEventPtr;
+				/* Last in list of synthesized events, or NULL
+				 * if list is empty. */
+    int grabFlags;		/* Miscellaneous flag values.  See definitions
+				 * in tkGrab.c. */
+
+    /*
+     * Information used by tkGrid.c only:
+     */
+
+    int gridInit;		/* 0 means table below needs initializing. */
+    Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to
+				 * corresponding Grid structures. */
+
+    /*
+     * Information used by tkImage.c only:
+     */
+
+    int imageId;		/* Value used to number image ids. */
+
+    /*
+     * Information used by tkMacWinMenu.c only:
+     */
+
+    int postCommandGeneration;
+
+    /*
+     * Information used by tkOption.c only.
+     */
+
+
+
+    /*
+     * Information used by tkPack.c only.
+     */
+
+    int packInit;		/* 0 means table below needs initializing. */
+    Tcl_HashTable packerHashTable;
+				/* Maps from Tk_Window tokens to
+				 * corresponding Packer structures. */
+
+
+    /*
+     * Information used by tkPlace.c only.
+     */
+
+    int placeInit;		/* 0 means tables below need initializing. */
+    Tcl_HashTable masterTable;	/* Maps from Tk_Window toke to the Master
+				 * structure for the window, if it exists. */
+    Tcl_HashTable slaveTable;	/* Maps from Tk_Window toke to the Slave
+				 * structure for the window, if it exists. */
+
+    /*
+     * Information used by tkSelect.c and tkClipboard.c only:
+     */
+
+
+    struct TkSelectionInfo *selectionInfoPtr;
+    /* First in list of selection information
+				 * records.  Each entry contains information
+				 * about the current owner of a particular
+				 * selection on this display. */
+    Atom multipleAtom;		/* Atom for MULTIPLE.  None means
+				 * selection stuff isn't initialized. */
+    Atom incrAtom;		/* Atom for INCR. */
+    Atom targetsAtom;		/* Atom for TARGETS. */
+    Atom timestampAtom;		/* Atom for TIMESTAMP. */
+    Atom textAtom;		/* Atom for TEXT. */
+    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
+    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
+    Atom windowAtom;		/* Atom for TK_WINDOW. */
+    Atom clipboardAtom;		/* Atom for CLIPBOARD. */
+#if (TK_VERSION_NUMBER >= _VERSION(8,4,0))
+    Atom utf8Atom;
+#endif
+    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
+				 * retrieve selections between processes. NULL
+				 * means clipboard info hasn't been
+				 * initialized. */
+    int clipboardActive;	/* 1 means we currently own the clipboard
+				 * selection, 0 means we don't. */
+    struct TkMainInfo *clipboardAppPtr;
+				/* Last application that owned clipboard. */
+    struct TkClipboardTarget *clipTargetPtr;
+				/* First in list of clipboard type information
+				 * records.  Each entry contains information
+				 * about the buffers for a given selection
+				 * target. */
+
+    /*
+     * Information used by tkSend.c only:
+     */
+
+    Tk_Window commTkwin;	/* Window used for communication
+				 * between interpreters during "send"
+				 * commands.  NULL means send info hasn't
+				 * been initialized yet. */
+    Atom commProperty;		/* X's name for comm property. */
+    Atom registryProperty;	/* X's name for property containing
+				 * registry of interpreter names. */
+    Atom appNameProperty;	/* X's name for property used to hold the
+				 * application name on each comm window. */
+
+    /*
+     * Information used by tkXId.c only:
+     */
+
+    struct TkIdStack *idStackPtr;
+				/* First in list of chunks of free resource
+				 * identifiers, or NULL if there are no free
+				 * resources. */
+    XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
+				/* Default resource allocator for display. */
+    struct TkIdStack *windowStackPtr;
+				/* First in list of chunks of window
+				 * identifers that can't be reused right
+				 * now. */
+#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
+    int idCleanupScheduled;	/* 1 means a call to WindowIdCleanup has
+				 * already been scheduled, 0 means it
+				 * hasn't. */
+#else
+    Tcl_TimerToken idCleanupScheduled;
+				/* If set, it means a call to WindowIdCleanup
+				 * has already been scheduled, 0 means it
+				 * hasn't. */
+#endif
+    /*
+     * Information used by tkUnixWm.c and tkWinWm.c only:
+     */
+
+#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
+    int wmTracing;		/* Used to enable or disable tracing in
+				 * this module.  If tracing is enabled,
+				 * then information is printed on
+				 * standard output about interesting
+				 * interactions with the window manager. */
+#endif
+    struct TkWmInfo *firstWmPtr; /* Points to first top-level window. */
+    struct TkWmInfo *foregroundWmPtr;
+				/* Points to the foreground window. */
+
+    /*
+     * Information maintained by tkWindow.c for use later on by tkXId.c:
+     */
+
+
+    int destroyCount;		/* Number of Tk_DestroyWindow operations
+				 * in progress. */
+    unsigned long lastDestroyRequest;
+				/* Id of most recent XDestroyWindow request;
+				 * can re-use ids in windowStackPtr when
+				 * server has seen this request and event
+				 * queue is empty. */
+
+    /*
+     * Information used by tkVisual.c only:
+     */
+
+    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
+				 * allocated for this display. */
+
+    /*
+     * Miscellaneous information:
+     */
+
+#ifdef TK_USE_INPUT_METHODS
+    XIM inputMethod;		/* Input method for this display */
+#if (TK_VERSION_NUMBER >= _VERSION(8,4,0))
+#if TK_XIM_SPOT
+    XFontSet inputXfs;		/* XFontSet cached for over-the-spot XIM. */
+#endif /* TK_XIM_SPOT */
+#endif /* TK_VERSION_NUMBER >= 8.4 */
+#endif /* TK_USE_INPUT_METHODS */
+    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */
+    int refCount;		/* Reference count of how many Tk applications
+                                 * are using this display. Used to clean up
+                                 * the display when we no longer have any
+                                 * Tk applications using it.
+                                 */
+    /*
+     * The following field were all added for Tk8.3
+     */
+    int mouseButtonState;       /* current mouse button state for this
+                                 * display */
+#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
+    int warpInProgress;
+#endif
+    Window warpWindow;
+    int warpX;
+    int warpY;
+#if (TK_VERSION_NUMBER < _VERSION(8,4,0))
+    int useInputMethods;        /* Whether to use input methods */
+#else
+    /*
+     * The following field(s) were all added for Tk8.4
+     */
+    long deletionEpoch;		/* Incremented by window deletions */
+    unsigned int flags;		/* Various flag values:  these are all
+				 * defined in below. */
+    TkCaret caret;		/* information about the caret for this
+				 * display.  This is not a pointer. */
+#endif
+} TkDisplay;
+
+#else
+
+/*
+ * One of the following structures is maintained for each display
+ * containing a window managed by Tk:
+ */
+typedef struct TkDisplayStruct {
+    Display *display;		/* Xlib's info about display. */
+    struct TkDisplayStruct *nextPtr; /* Next in list of all displays. */
+    char *name;			/* Name of display (with any screen
+				 * identifier removed).  Malloc-ed. */
+    Time lastEventTime;		/* Time of last event received for this
+				 * display. */
+
+    /*
+     * Information used primarily by tkBind.c:
+     */
+
+    int bindInfoStale;		/* Non-zero means the variables in this
+				 * part of the structure are potentially
+				 * incorrect and should be recomputed. */
+    unsigned int modeModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to "mode shift".  If no
+				 * such modifier, than this is zero. */
+    unsigned int metaModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key.  If no
+				 * such modifier, then this is zero. */
+    unsigned int altModMask;	/* Has one bit set to indicate the modifier
+				 * corresponding to the "Meta" key.  If no
+				 * such modifier, then this is zero. */
+    enum {
+	LU_IGNORE, LU_CAPS, LU_SHIFT
+    } lockUsage;
+    /* Indicates how to interpret lock modifier. */
+    int numModKeyCodes;		/* Number of entries in modKeyCodes array
+				 * below. */
+    KeyCode *modKeyCodes;	/* Pointer to an array giving keycodes for
+				 * all of the keys that have modifiers
+				 * associated with them.  Malloc'ed, but
+				 * may be NULL. */
+
+    /*
+     * Information used by tkError.c only:
+     */
+
+    TkErrorHandler *errorPtr;
+    /* First in list of error handlers
+				 * for this display.  NULL means
+				 * no handlers exist at present. */
+     int deleteCount;		/* Counts # of handlers deleted since
+				 * last time inactive handlers were
+				 * garbage-collected.  When this number
+				 * gets big, handlers get cleaned up. */
+
+    /*
+     * Information used by tkSend.c only:
+     */
+
+    Tk_Window commTkwin;	/* Window used for communication
+				 * between interpreters during "send"
+				 * commands.  NULL means send info hasn't
+				 * been initialized yet. */
+    Atom commProperty;		/* X's name for comm property. */
+    Atom registryProperty;	/* X's name for property containing
+				 * registry of interpreter names. */
+    Atom appNameProperty;	/* X's name for property used to hold the
+				 * application name on each comm window. */
+
+    /*
+     * Information used by tkSelect.c and tkClipboard.c only:
+     */
+
+     TkSelectionInfo *selectionInfoPtr;
+    /* First in list of selection information
+				 * records.  Each entry contains information
+				 * about the current owner of a particular
+				 * selection on this display. */
+    Atom multipleAtom;		/* Atom for MULTIPLE.  None means
+				 * selection stuff isn't initialized. */
+    Atom incrAtom;		/* Atom for INCR. */
+    Atom targetsAtom;		/* Atom for TARGETS. */
+    Atom timestampAtom;		/* Atom for TIMESTAMP. */
+    Atom textAtom;		/* Atom for TEXT. */
+    Atom compoundTextAtom;	/* Atom for COMPOUND_TEXT. */
+    Atom applicationAtom;	/* Atom for TK_APPLICATION. */
+    Atom windowAtom;		/* Atom for TK_WINDOW. */
+    Atom clipboardAtom;		/* Atom for CLIPBOARD. */
+
+    Tk_Window clipWindow;	/* Window used for clipboard ownership and to
+				 * retrieve selections between processes. NULL
+				 * means clipboard info hasn't been
+				 * initialized. */
+    int clipboardActive;	/* 1 means we currently own the clipboard
+				 * selection, 0 means we don't. */
+     TkMainInfo *clipboardAppPtr;
+     /* Last application that owned clipboard. */
+     TkClipboardTarget *clipTargetPtr;
+     /* First in list of clipboard type information
+				 * records.  Each entry contains information
+				 * about the buffers for a given selection
+				 * target. */
+
+    /*
+     * Information used by tkAtom.c only:
+     */
+
+    int atomInit;		/* 0 means stuff below hasn't been
+				 * initialized yet. */
+    Tcl_HashTable nameTable;	/* Maps from names to Atom's. */
+    Tcl_HashTable atomTable;	/* Maps from Atom's back to names. */
+
+    /*
+     * Information used by tkCursor.c only:
+     */
+
+    Font cursorFont;		/* Font to use for standard cursors.
+				 * None means font not loaded yet. */
+
+    /*
+     * Information used by tkGrab.c only:
+     */
+
+     TkWindow *grabWinPtr;
+    /* Window in which the pointer is currently
+				 * grabbed, or NULL if none. */
+     TkWindow *eventualGrabWinPtr;
+    /* Value that grabWinPtr will have once the
+				 * grab event queue (below) has been
+				 * completely emptied. */
+     TkWindow *buttonWinPtr;
+    /* Window in which first mouse button was
+				 * pressed while grab was in effect, or NULL
+				 * if no such press in effect. */
+     TkWindow *serverWinPtr;
+    /* If no application contains the pointer then
+				 * this is NULL.  Otherwise it contains the
+				 * last window for which we've gotten an
+				 * Enter or Leave event from the server (i.e.
+				 * the last window known to have contained
+				 * the pointer).  Doesn't reflect events
+				 * that were synthesized in tkGrab.c. */
+    TkGrabEvent *firstGrabEventPtr;
+    /* First in list of enter/leave events
+				 * synthesized by grab code.  These events
+				 * must be processed in order before any other
+				 * events are processed.  NULL means no such
+				 * events. */
+    TkGrabEvent *lastGrabEventPtr;
+    /* Last in list of synthesized events, or NULL
+				 * if list is empty. */
+    int grabFlags;		/* Miscellaneous flag values.  See definitions
+				 * in tkGrab.c. */
+
+    /*
+     * Information used by tkXId.c only:
+     */
+
+     TkIdStack *idStackPtr;
+    /* First in list of chunks of free resource
+				 * identifiers, or NULL if there are no free
+				 * resources. */
+              XID(*defaultAllocProc) _ANSI_ARGS_((Display *display));
+    /* Default resource allocator for display. */
+     TkIdStack *windowStackPtr;
+    /* First in list of chunks of window
+				 * identifers that can't be reused right
+				 * now. */
+    int idCleanupScheduled;	/* 1 means a call to WindowIdCleanup has
+				 * already been scheduled, 0 means it
+				 * hasn't. */
+
+    /*
+     * Information maintained by tkWindow.c for use later on by tkXId.c:
+     */
+
+
+    int destroyCount;		/* Number of Tk_DestroyWindow operations
+				 * in progress. */
+    unsigned long lastDestroyRequest;
+    /* Id of most recent XDestroyWindow request;
+				 * can re-use ids in windowStackPtr when
+				 * server has seen this request and event
+				 * queue is empty. */
+
+    /*
+     * Information used by tkVisual.c only:
+     */
+
+    TkColormap *cmapPtr;	/* First in list of all non-default colormaps
+				 * allocated for this display. */
+
+    /*
+     * Information used by tkFocus.c only:
+     */
+#if (TK_MAJOR_VERSION == 4)
+
+     TkWindow *focusWinPtr;
+				/* Window that currently has the focus for
+				 * this display, or NULL if none. */
+     TkWindow *implicitWinPtr;
+				/* If the focus arrived at a toplevel window
+				 * implicitly via an Enter event (rather
+				 * than via a FocusIn event), this points
+				 * to the toplevel window.  Otherwise it is
+				 * NULL. */
+     TkWindow *focusOnMapPtr;
+				/* This points to a toplevel window that is
+				 * supposed to receive the X input focus as
+				 * soon as it is mapped (needed to handle the
+				 * fact that X won't allow the focus on an
+				 * unmapped window).  NULL means no delayed
+				 * focus op in progress. */
+    int forceFocus;		/* Associated with focusOnMapPtr:  non-zero
+				 * means claim the focus even if some other
+				 * application currently has it. */
+#else
+     TkWindow *implicitWinPtr;
+				/* If the focus arrived at a toplevel window
+				 * implicitly via an Enter event (rather
+				 * than via a FocusIn event), this points
+				 * to the toplevel window.  Otherwise it is
+				 * NULL. */
+     TkWindow *focusPtr;	/* Points to the window on this display that
+				 * should be receiving keyboard events.  When
+				 * multiple applications on the display have
+				 * the focus, this will refer to the
+				 * innermost window in the innermost
+				 * application.  This information isn't used
+				 * under Unix or Windows, but it's needed on
+				 * the Macintosh. */
+#endif /* TK_MAJOR_VERSION == 4 */
+
+    /*
+     * Used by tkColor.c only:
+     */
+
+    TkStressedCmap *stressPtr;	/* First in list of colormaps that have
+				 * filled up, so we have to pick an
+				 * approximate color. */
+
+    /*
+     * Used by tkEvent.c only:
+     */
+
+     TkWindowEvent *delayedMotionPtr;
+				/* Points to a malloc-ed motion event
+				 * whose processing has been delayed in
+				 * the hopes that another motion event
+				 * will come along right away and we can
+				 * merge the two of them together.  NULL
+				 * means that there is no delayed motion
+				 * event. */
+    /*
+     * Miscellaneous information:
+     */
+
+#ifdef TK_USE_INPUT_METHODS
+    XIM inputMethod;		/* Input method for this display */
+#endif /* TK_USE_INPUT_METHODS */
+    Tcl_HashTable winTable;	/* Maps from X window ids to TkWindow ptrs. */
+#if (TK_MAJOR_VERSION > 4)
+    int refCount;		/* Reference count of how many Tk applications
+                                 * are using this display. Used to clean up
+                                 * the display when we no longer have any
+                                 * Tk applications using it.
+                                 */
+#endif /* TK_MAJOR_VERSION > 4 */
+
+} TkDisplay;
+
+#endif /* TK_VERSION_NUMBER >= _VERSION(8,1,0) */
+
+
+struct TkWindowStruct {
+    Display *display;
+    TkDisplay *dispPtr;
+    int screenNum;
+    Visual *visual;
+    int depth;
+    Window window;
+    TkWindow *childList;
+    TkWindow *lastChildPtr;
+    TkWindow *parentPtr;
+    TkWindow *nextPtr;
+    TkMainInfo *infoPtr;
+    char *pathName;
+    Tk_Uid nameUid;
+    Tk_Uid classUid;
+    XWindowChanges changes;
+    unsigned int dirtyChanges;
+    XSetWindowAttributes atts;
+    unsigned long dirtyAtts;
+    unsigned int flags;
+    TkEventHandler *handlerList;
+#ifdef TK_USE_INPUT_METHODS
+    XIC inputContext;
+#endif /* TK_USE_INPUT_METHODS */
+    ClientData *tagPtr;
+    int nTags;
+    int optionLevel;
+    TkSelHandler *selHandlerList;
+    Tk_GeomMgr *geomMgrPtr;
+    ClientData geomData;
+    int reqWidth, reqHeight;
+    int internalBorderWidth;
+    TkWinInfo *wmInfoPtr;
+#if (TK_MAJOR_VERSION > 4)
+    TkClassProcs *classProcsPtr;
+    ClientData instanceData;
+#endif
+    TkWindowPrivate *privatePtr;
+};
+
+#ifdef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowHandle --
+ *
+ *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
+ *      the toplevel widgets are wrapped by another window.
+ *      Currently there's no way to get at that window, other than
+ *      what is done here: query the X window hierarchy and grab the
+ *      parent.
+ *
+ * Results:
+ *      Returns the X Window ID of the widget.  If it's a toplevel, then
+ *	the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static HWND
+GetWindowHandle(Tk_Window tkwin)
+{
+    HWND hWnd;
+    Window window;
+    
+    window = Tk_WindowId(tkwin);
+    if (window == None) {
+	Tk_MakeWindowExist(tkwin);
+    }
+    hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+#if (TK_MAJOR_VERSION > 4)
+    if (Tk_IsTopLevel(tkwin)) {
+	hWnd = GetParent(hWnd);
+    }
+#endif /* TK_MAJOR_VERSION > 4 */
+    return hWnd;
+}
+
+#else
+
+Window
+Blt_GetParent(display, window)
+    Display *display;
+    Window window;
+{
+    Window root, parent;
+    Window *dummy;
+    unsigned int count;
+
+    if (XQueryTree(display, window, &root, &parent, &dummy, &count) > 0) {
+	XFree(dummy);
+	return parent;
+    }
+    return None;
+}
+
+static Window
+GetWindowId(tkwin)
+    Tk_Window tkwin;
+{
+    Window window;
+
+    Tk_MakeWindowExist(tkwin);
+    window = Tk_WindowId(tkwin);
+#if (TK_MAJOR_VERSION > 4)
+    if (Tk_IsTopLevel(tkwin)) {
+	Window parent;
+
+	parent = Blt_GetParent(Tk_Display(tkwin), window);
+	if (parent != None) {
+	    window = parent;
+	}
+	window = parent;
+    }
+#endif /* TK_MAJOR_VERSION > 4 */
+    return window;
+}
+
+#endif /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DoConfigureNotify --
+ *
+ *	Generate a ConfigureNotify event describing the current
+ *	configuration of a window.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	An event is generated and processed by Tk_HandleEvent.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DoConfigureNotify(winPtr)
+    Tk_FakeWin *winPtr;		/* Window whose configuration was just
+				 * changed. */
+{
+    XEvent event;
+
+    event.type = ConfigureNotify;
+    event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display);
+    event.xconfigure.send_event = False;
+    event.xconfigure.display = winPtr->display;
+    event.xconfigure.event = winPtr->window;
+    event.xconfigure.window = winPtr->window;
+    event.xconfigure.x = winPtr->changes.x;
+    event.xconfigure.y = winPtr->changes.y;
+    event.xconfigure.width = winPtr->changes.width;
+    event.xconfigure.height = winPtr->changes.height;
+    event.xconfigure.border_width = winPtr->changes.border_width;
+    if (winPtr->changes.stack_mode == Above) {
+	event.xconfigure.above = winPtr->changes.sibling;
+    } else {
+	event.xconfigure.above = None;
+    }
+    event.xconfigure.override_redirect = winPtr->atts.override_redirect;
+    Tk_HandleEvent(&event);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_MakeTransparentWindowExist --
+ *
+ *	Similar to Tk_MakeWindowExist but instead creates a
+ *	transparent window to block for user events from sibling
+ *	windows.
+ *
+ *	Differences from Tk_MakeWindowExist.
+ *
+ *	1. This is always a "busy" window. There's never a
+ *	   platform-specific class procedure to execute instead.
+ *	2. The window is transparent and never will contain children,
+ *	   so colormap information is irrelevant.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	When the procedure returns, the internal window associated
+ *	with tkwin is guaranteed to exist.  This may require the
+ *	window's ancestors to be created too.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_MakeTransparentWindowExist(tkwin, parent, isBusy)
+    Tk_Window tkwin;		/* Token for window. */
+    Window parent;		/* Parent window. */
+    int isBusy;			/*  */
+{
+    TkWindow *winPtr = (TkWindow *) tkwin;
+    TkWindow *winPtr2;
+    Tcl_HashEntry *hPtr;
+    int notUsed;
+    TkDisplay *dispPtr;
+#ifdef WIN32
+    HWND hParent;
+    int style;
+    DWORD exStyle;
+    HWND hWnd;
+#else
+    long int mask;
+#endif /* WIN32 */
+
+    if (winPtr->window != None) {
+	return;			/* Window already exists. */
+    }
+#ifdef notdef			
+    if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) {
+	parent = XRootWindow(winPtr->display, winPtr->screenNum);
+	/* TODO: Make the entire screen busy */
+    } else {
+	if (Tk_WindowId(winPtr->parentPtr) == None) {
+	    Tk_MakeWindowExist((Tk_Window)winPtr->parentPtr);
+	}
+    }
+#endif
+
+    /* Create a transparent window and put it on top.  */
+
+#ifdef WIN32
+    hParent = (HWND) parent;
+    style = (WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+    exStyle = (WS_EX_TRANSPARENT | WS_EX_TOPMOST);
+#define TK_WIN_CHILD_CLASS_NAME "TkChild"
+    hWnd = CreateWindowEx(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style,
+	Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
+	hParent, NULL, (HINSTANCE) Tk_GetHINSTANCE(), NULL);
+    winPtr->window = Tk_AttachHWND(tkwin, hWnd);
+#else
+    mask = (!isBusy) ? 0 : (CWDontPropagate | CWEventMask);
+    /* Ignore the important events while the window is mapped.  */
+#define USER_EVENTS  (EnterWindowMask | LeaveWindowMask | KeyPressMask | \
+	KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
+#define PROP_EVENTS  (KeyPressMask | KeyReleaseMask | ButtonPressMask | \
+	ButtonReleaseMask | PointerMotionMask)
+
+    winPtr->atts.do_not_propagate_mask = PROP_EVENTS;
+    winPtr->atts.event_mask = USER_EVENTS;
+    winPtr->changes.border_width = 0;
+    winPtr->depth = 0; 
+
+    winPtr->window = XCreateWindow(winPtr->display, parent,
+	winPtr->changes.x, winPtr->changes.y,
+	(unsigned)winPtr->changes.width,	/* width */
+	(unsigned)winPtr->changes.height,	/* height */
+	(unsigned)winPtr->changes.border_width,	/* border_width */
+	winPtr->depth,		/* depth */
+	InputOnly,		/* class */
+	winPtr->visual,		/* visual */
+        mask,			/* valuemask */
+	&(winPtr->atts)		/* attributes */ );
+#endif /* WIN32 */
+
+    dispPtr = winPtr->dispPtr;
+    hPtr = Tcl_CreateHashEntry(&(dispPtr->winTable), (char *)winPtr->window,
+	&notUsed);
+    Tcl_SetHashValue(hPtr, winPtr);
+    winPtr->dirtyAtts = 0;
+    winPtr->dirtyChanges = 0;
+#ifdef TK_USE_INPUT_METHODS
+    winPtr->inputContext = NULL;
+#endif /* TK_USE_INPUT_METHODS */
+    if (!(winPtr->flags & TK_TOP_LEVEL)) {
+	/*
+	 * If any siblings higher up in the stacking order have already
+	 * been created then move this window to its rightful position
+	 * in the stacking order.
+	 *
+	 * NOTE: this code ignores any changes anyone might have made
+	 * to the sibling and stack_mode field of the window's attributes,
+	 * so it really isn't safe for these to be manipulated except
+	 * by calling Tk_RestackWindow.
+	 */
+	for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL;
+	    winPtr2 = winPtr2->nextPtr) {
+	    if ((winPtr2->window != None) && !(winPtr2->flags & TK_TOP_LEVEL)) {
+		XWindowChanges changes;
+		changes.sibling = winPtr2->window;
+		changes.stack_mode = Below;
+		XConfigureWindow(winPtr->display, winPtr->window,
+		    CWSibling | CWStackMode, &changes);
+		break;
+	    }
+	}
+    }
+
+    /*
+     * Issue a ConfigureNotify event if there were deferred configuration
+     * changes (but skip it if the window is being deleted;  the
+     * ConfigureNotify event could cause problems if we're being called
+     * from Tk_DestroyWindow under some conditions).
+     */
+    if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY)
+	&& !(winPtr->flags & TK_ALREADY_DEAD)) {
+	winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY;
+	DoConfigureNotify((Tk_FakeWin *) tkwin);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindChild --
+ *
+ *      Performs a linear search for the named child window in a given
+ *	parent window.
+ *
+ *	This can be done via Tcl, but not through Tk's C API.  It's 
+ *	simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ *      The child Tk_Window. If the named child can't be found, NULL
+ *	is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Tk_Window
+Blt_FindChild(parent, name)
+    Tk_Window parent;
+    char *name;
+{
+    register TkWindow *winPtr;
+    TkWindow *parentPtr = (TkWindow *)parent;
+
+    for (winPtr = parentPtr->childList; winPtr != NULL; 
+	winPtr = winPtr->nextPtr) {
+	if (strcmp(name, winPtr->nameUid) == 0) {
+	    return (Tk_Window)winPtr;
+	}
+    }
+    return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FirstChildWindow --
+ *
+ *      Performs a linear search for the named child window in a given
+ *	parent window.
+ *
+ *	This can be done via Tcl, but not through Tk's C API.  It's 
+ *	simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ *      The child Tk_Window. If the named child can't be found, NULL
+ *	is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+Tk_Window
+Blt_FirstChild(parent)
+    Tk_Window parent;
+{
+    TkWindow *parentPtr = (TkWindow *)parent;
+    return (Tk_Window)parentPtr->childList;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_FindChild --
+ *
+ *      Performs a linear search for the named child window in a given
+ *	parent window.
+ *
+ *	This can be done via Tcl, but not through Tk's C API.  It's 
+ *	simple enough, if you peek into the Tk_Window structure.
+ *
+ * Results:
+ *      The child Tk_Window. If the named child can't be found, NULL
+ *	is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+Tk_Window
+Blt_NextChild(tkwin)
+    Tk_Window tkwin;
+{
+    TkWindow *winPtr = (TkWindow *)tkwin;
+
+    if (winPtr == NULL) {
+	return NULL;
+    }
+    return (Tk_Window)winPtr->nextPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkWindow --
+ *
+ *	This procedure removes a window from the childList of its
+ *	parent.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UnlinkWindow(winPtr)
+    TkWindow *winPtr;	/* Child window to be unlinked. */
+{
+    TkWindow *prevPtr;
+
+    prevPtr = winPtr->parentPtr->childList;
+    if (prevPtr == winPtr) {
+	winPtr->parentPtr->childList = winPtr->nextPtr;
+	if (winPtr->nextPtr == NULL) {
+	    winPtr->parentPtr->lastChildPtr = NULL;
+	}
+    } else {
+	while (prevPtr->nextPtr != winPtr) {
+	    prevPtr = prevPtr->nextPtr;
+	    if (prevPtr == NULL) {
+		panic("UnlinkWindow couldn't find child in parent");
+	    }
+	}
+	prevPtr->nextPtr = winPtr->nextPtr;
+	if (winPtr->nextPtr == NULL) {
+	    winPtr->parentPtr->lastChildPtr = prevPtr;
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RelinkWindow --
+ *
+ *	Relinks a window into a new parent.  The window is unlinked
+ *	from its original parent's child list and added onto the end
+ *	of the new parent's list.
+ *
+ *	FIXME:  If the window has focus, the focus should be moved
+ *		to an ancestor.  Otherwise, Tk becomes confused 
+ *		about which Toplevel turns on focus for the window. 
+ *		Right now this is done at the Tcl layer.  For example,
+ *		see blt::CreateTearoff in tabset.tcl.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RelinkWindow(tkwin, newParent, x, y)
+    Tk_Window tkwin;		/* Child window to be linked. */
+    Tk_Window newParent;
+    int x, y;
+{
+    TkWindow *winPtr, *parentWinPtr;
+
+    if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
+	    Tk_WindowId(newParent), x, y) != TCL_OK) {
+	return;
+    }
+    winPtr = (TkWindow *)tkwin;
+    parentWinPtr = (TkWindow *)newParent;
+
+    winPtr->flags &= ~TK_REPARENTED;
+    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */
+
+    /* Append the window onto the end of the parent's list of children */
+    winPtr->parentPtr = parentWinPtr;
+    winPtr->nextPtr = NULL;
+    if (parentWinPtr->childList == NULL) {
+	parentWinPtr->childList = winPtr;
+    } else {
+	parentWinPtr->lastChildPtr->nextPtr = winPtr;
+    }
+    parentWinPtr->lastChildPtr = winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RelinkWindow --
+ *
+ *	Relinks a window into a new parent.  The window is unlinked
+ *	from its original parent's child list and added onto the end
+ *	of the new parent's list.
+ *
+ *	FIXME:  If the window has focus, the focus should be moved
+ *		to an ancestor.  Otherwise, Tk becomes confused 
+ *		about which Toplevel turns on focus for the window. 
+ *		Right now this is done at the Tcl layer.  For example,
+ *		see blt::CreateTearoff in tabset.tcl.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RelinkWindow2(tkwin, window, newParent, x, y)
+    Tk_Window tkwin;		/* Child window to be linked. */
+    Window window;
+    Tk_Window newParent;
+    int x, y;
+{
+#ifdef notdef
+    TkWindow *winPtr, *parentWinPtr;
+#endif
+    if (Blt_ReparentWindow(Tk_Display(tkwin), window,
+	    Tk_WindowId(newParent), x, y) != TCL_OK) {
+	return;
+    }
+#ifdef notdef
+    winPtr = (TkWindow *)tkwin;
+    parentWinPtr = (TkWindow *)newParent;
+
+    winPtr->flags &= ~TK_REPARENTED;
+    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */
+
+    /* Append the window onto the end of the parent's list of children */
+    winPtr->parentPtr = parentWinPtr;
+    winPtr->nextPtr = NULL;
+    if (parentWinPtr->childList == NULL) {
+	parentWinPtr->childList = winPtr;
+    } else {
+	parentWinPtr->lastChildPtr->nextPtr = winPtr;
+    }
+    parentWinPtr->lastChildPtr = winPtr;
+#endif
+}
+
+void
+Blt_UnlinkWindow(tkwin)
+    Tk_Window tkwin;		/* Child window to be linked. */
+{
+    TkWindow *winPtr;
+    Window root;
+
+    root = XRootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+    if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin),
+	    root, 0, 0) != TCL_OK) {
+	return;
+    }
+    winPtr = (TkWindow *)tkwin;
+    winPtr->flags &= ~TK_REPARENTED;
+#ifdef notdef
+    UnlinkWindow(winPtr);	/* Remove the window from its parent's list */
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_Toplevel --
+ *
+ *      Climbs up the widget hierarchy to find the top level window of
+ *      the window given.
+ *
+ * Results:
+ *      Returns the Tk_Window of the toplevel widget.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Window
+Blt_Toplevel(tkwin)
+    register Tk_Window tkwin;
+{
+    while (!Tk_IsTopLevel(tkwin)) {
+	tkwin = Tk_Parent(tkwin);
+    }
+    return tkwin;
+}
+
+void
+Blt_RootCoordinates(tkwin, x, y, rootXPtr, rootYPtr)
+    Tk_Window tkwin;
+    int x, y;
+    int *rootXPtr, *rootYPtr;
+{
+    int vx, vy, vw, vh;
+    int rootX, rootY;
+
+    Tk_GetRootCoords(tkwin, &rootX, &rootY);
+    x += rootX;
+    y += rootY;
+    Tk_GetVRootGeometry(tkwin, &vx, &vy, &vw, &vh);
+    x += vx;
+    y += vy;
+    *rootXPtr = x;
+    *rootYPtr = y;
+}
+
+
+/* Find the toplevel then  */
+int
+Blt_RootX(tkwin)
+    Tk_Window tkwin;
+{
+    int x;
+    
+    for (x = 0; tkwin != NULL;  tkwin = Tk_Parent(tkwin)) {
+	x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
+	if (Tk_IsTopLevel(tkwin)) {
+	    break;
+	}
+    }
+    return x;
+}
+
+int
+Blt_RootY(tkwin)
+    Tk_Window tkwin;
+{
+    int y;
+    
+    for (y = 0; tkwin != NULL;  tkwin = Tk_Parent(tkwin)) {
+	y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
+	if (Tk_IsTopLevel(tkwin)) {
+	    break;
+	}
+    }
+    return y;
+}
+
+#ifdef WIN32
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetRealWindowId --
+ *
+ *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
+ *      the toplevel widgets are wrapped by another window.
+ *      Currently there's no way to get at that window, other than
+ *      what is done here: query the X window hierarchy and grab the
+ *      parent.
+ *
+ * Results:
+ *      Returns the X Window ID of the widget.  If it's a toplevel, then
+ *	the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Window
+Blt_GetRealWindowId(Tk_Window tkwin)
+{
+    return (Window) GetWindowHandle(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetToplevel --
+ *
+ *      Retrieves the toplevel window which is the nearest ancestor of
+ *      of the specified window.
+ *
+ * Results:
+ *      Returns the toplevel window or NULL if the window has no
+ *      ancestor which is a toplevel.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+Tk_Window
+Blt_GetToplevel(Tk_Window tkwin) /* Window for which the toplevel
+				  * should be deterined. */
+{
+     while (!Tk_IsTopLevel(tkwin)) {
+         tkwin = Tk_Parent(tkwin);
+	 if (tkwin == NULL) {
+             return NULL;
+         }
+     }
+     return tkwin;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RaiseToLevelWindow --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RaiseToplevel(Tk_Window tkwin)
+{
+    SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, 0, 0, 0, 0,
+	SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapToplevel(Tk_Window tkwin)
+{
+    ShowWindow(GetWindowHandle(tkwin), SW_SHOWNORMAL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_UnmapToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UnmapToplevel(Tk_Window tkwin)
+{
+    ShowWindow(GetWindowHandle(tkwin), SW_HIDE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MoveResizeToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MoveResizeToplevel(tkwin, x, y, width, height)
+    Tk_Window tkwin;
+    int x, y, width, height;
+{
+    SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, x, y, width, height, 0);
+}
+
+int
+Blt_ReparentWindow(
+    Display *display,
+    Window window, 
+    Window newParent,
+    int x, int y)
+{
+    XReparentWindow(display, window, newParent, x, y);
+    return TCL_OK;
+}
+
+#else  /* WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetRealWindowId --
+ *
+ *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
+ *      the toplevel widgets are wrapped by another window.
+ *      Currently there's no way to get at that window, other than
+ *      what is done here: query the X window hierarchy and grab the
+ *      parent.
+ *
+ * Results:
+ *      Returns the X Window ID of the widget.  If it's a toplevel, then
+ *	the XID of the wrapper is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Window
+Blt_GetRealWindowId(tkwin)
+    Tk_Window tkwin;
+{
+    return GetWindowId(tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_RaiseToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_RaiseToplevel(tkwin)
+    Tk_Window tkwin;
+{
+    XRaiseWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LowerToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_LowerToplevel(tkwin)
+    Tk_Window tkwin;
+{
+    XLowerWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizeToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_ResizeToplevel(tkwin, width, height)
+    Tk_Window tkwin;
+    int width, height;
+{
+    XResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MoveResizeToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MoveResizeToplevel(tkwin, x, y, width, height)
+    Tk_Window tkwin;
+    int x, y, width, height;
+{
+    XMoveResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), x, y, 
+	      width, height);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ResizeToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MoveToplevel(tkwin, x, y)
+    Tk_Window tkwin;
+    int x, y;
+{
+    XMoveWindow(Tk_Display(tkwin), GetWindowId(tkwin), x, y);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_MapToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_MapToplevel(tkwin)
+    Tk_Window tkwin;
+{
+    XMapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_UnmapToplevel --
+ *
+ * Results:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_UnmapToplevel(tkwin)
+    Tk_Window tkwin;
+{
+    XUnmapWindow(Tk_Display(tkwin), GetWindowId(tkwin));
+}
+
+/* ARGSUSED */
+static int
+XReparentWindowErrorProc(clientData, errEventPtr)
+    ClientData clientData;
+    XErrorEvent *errEventPtr;
+{
+    int *errorPtr = clientData;
+
+    *errorPtr = TCL_ERROR;
+    return 0;
+}
+
+int
+Blt_ReparentWindow(display, window, newParent, x, y)
+    Display *display;
+    Window window, newParent;
+    int x, y;
+{
+    Tk_ErrorHandler handler;
+    int result;
+    int any = -1;
+
+    result = TCL_OK;
+    handler = Tk_CreateErrorHandler(display, any, X_ReparentWindow, any,
+	XReparentWindowErrorProc, &result);
+    XReparentWindow(display, window, newParent, x, y);
+    Tk_DeleteErrorHandler(handler);
+    XSync(display, False);
+    return result;
+}
+
+#endif /* WIN32 */
+
+#if (TK_MAJOR_VERSION == 4)
+#include <bltHash.h>
+static int initialized = FALSE;
+static Blt_HashTable windowTable;
+
+void
+Blt_SetWindowInstanceData(tkwin, instanceData)
+    Tk_Window tkwin;
+    ClientData instanceData;
+{
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    if (!initialized) {
+	Blt_InitHashTable(&windowTable, BLT_ONE_WORD_KEYS);
+	initialized = TRUE;
+    }
+    hPtr = Blt_CreateHashEntry(&windowTable, (char *)tkwin, &isNew);
+    assert(isNew);
+    Blt_SetHashValue(hPtr, instanceData);
+}
+
+ClientData
+Blt_GetWindowInstanceData(tkwin)
+    Tk_Window tkwin;
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
+    if (hPtr == NULL) {
+	return NULL;
+    }
+    return Blt_GetHashValue(hPtr);
+}
+
+void
+Blt_DeleteWindowInstanceData(tkwin)
+    Tk_Window tkwin;
+{
+    Blt_HashEntry *hPtr;
+
+    hPtr = Blt_FindHashEntry(&windowTable, (char *)tkwin);
+    assert(hPtr);
+    Blt_DeleteHashEntry(&windowTable, hPtr);
+}
+
+#else
+
+void
+Blt_SetWindowInstanceData(tkwin, instanceData)
+    Tk_Window tkwin;
+    ClientData instanceData;
+{
+    TkWindow *winPtr = (TkWindow *)tkwin;
+
+    winPtr->instanceData = instanceData;
+}
+
+ClientData
+Blt_GetWindowInstanceData(tkwin)
+    Tk_Window tkwin;
+{
+    TkWindow *winPtr = (TkWindow *)tkwin;
+
+    return winPtr->instanceData;
+}
+
+void
+Blt_DeleteWindowInstanceData(tkwin)
+    Tk_Window tkwin;
+{
+}
+
+#endif
+
Index: trunk/kitgen/8.x/blt/generic/missing.h
===================================================================
--- trunk/kitgen/8.x/blt/generic/missing.h	(revision 175)
+++ trunk/kitgen/8.x/blt/generic/missing.h	(revision 175)
@@ -0,0 +1,114 @@
+#ifndef _MISSING_H
+#define _MISSING_H
+
+#include <winspool.h>
+
+#ifndef DeleteBitmap
+#define DeleteBitmap(hbm)       DeleteObject((HGDIOBJ)(HBITMAP)(hbm))
+#endif
+#ifndef DeleteBrush
+#define DeleteBrush(hbr)	DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
+#endif
+#ifndef DeleteFont
+#define DeleteFont(hfont)	DeleteObject((HGDIOBJ)(HFONT)(hfont))
+#endif
+#ifndef DeletePalette
+#define DeletePalette(hpal)     DeleteObject((HGDIOBJ)(HPALETTE)(hpal))
+#endif
+#ifndef DeletePen
+#define DeletePen(hpen)		DeleteObject((HGDIOBJ)(HPEN)(hpen))
+#endif
+#ifndef SelectBitmap
+#define SelectBitmap(hdc, hbm)  ((HBITMAP)SelectObject((hdc), (HGDIOBJ)(HBITMAP)(hbm)))
+#endif
+#ifndef SelectBrush
+#define SelectBrush(hdc, hbr)   ((HBRUSH)SelectObject((hdc), (HGDIOBJ)(HBRUSH)(hbr)))
+#endif
+#ifndef SelectFont
+#define SelectFont(hdc, hfont)  ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
+#endif
+#ifndef SelectPen
+#define SelectPen(hdc, hpen)    ((HPEN)SelectObject((hdc), (HGDIOBJ)(HPEN)(hpen)))
+#endif
+#ifndef GetNextWindow
+#define GetNextWindow(hWnd,wCmd) GetWindow((hWnd),(wCmd))
+#endif
+#ifndef GetStockBrush
+#define GetStockBrush(i)     ((HBRUSH)GetStockObject(i))
+#endif
+#ifndef GetStockPen
+#define GetStockPen(i)       ((HPEN)GetStockObject(i))
+#endif
+
+
+#define DM_SPECVERSION 0x0401
+
+#define DMPAPER_ISO_B4              42	/* B4 (ISO) 250 x 353 mm              */
+#define DMPAPER_JAPANESE_POSTCARD   43	/* Japanese Postcard 100 x 148 mm     */
+#define DMPAPER_9X11                44	/* 9 x 11 in                          */
+#define DMPAPER_10X11               45	/* 10 x 11 in                         */
+#define DMPAPER_15X11               46	/* 15 x 11 in                         */
+#define DMPAPER_ENV_INVITE          47	/* Envelope Invite 220 x 220 mm       */
+#define DMPAPER_RESERVED_48         48	/* RESERVED--DO NOT USE               */
+#define DMPAPER_RESERVED_49         49	/* RESERVED--DO NOT USE               */
+#define DMPAPER_LETTER_EXTRA        50	/* Letter Extra 9 \275 x 12 in        */
+#define DMPAPER_LEGAL_EXTRA         51	/* Legal Extra 9 \275 x 15 in         */
+#define DMPAPER_TABLOID_EXTRA       52	/* Tabloid Extra 11.69 x 18 in        */
+#define DMPAPER_A4_EXTRA            53	/* A4 Extra 9.27 x 12.69 in           */
+#define DMPAPER_LETTER_TRANSVERSE   54	/* Letter Transverse 8 \275 x 11 in   */
+#define DMPAPER_A4_TRANSVERSE       55	/* A4 Transverse 210 x 297 mm         */
+#define DMPAPER_LETTER_EXTRA_TRANSVERSE 56	/* Letter Extra Transverse 9\275 x 12 in */
+#define DMPAPER_A_PLUS              57	/* SuperA/SuperA/A4 227 x 356 mm      */
+#define DMPAPER_B_PLUS              58	/* SuperB/SuperB/A3 305 x 487 mm      */
+#define DMPAPER_LETTER_PLUS         59	/* Letter Plus 8.5 x 12.69 in         */
+#define DMPAPER_A4_PLUS             60	/* A4 Plus 210 x 330 mm               */
+#define DMPAPER_A5_TRANSVERSE       61	/* A5 Transverse 148 x 210 mm         */
+#define DMPAPER_B5_TRANSVERSE       62	/* B5 (JIS) Transverse 182 x 257 mm   */
+#define DMPAPER_A3_EXTRA            63	/* A3 Extra 322 x 445 mm              */
+#define DMPAPER_A5_EXTRA            64	/* A5 Extra 174 x 235 mm              */
+#define DMPAPER_B5_EXTRA            65	/* B5 (ISO) Extra 201 x 276 mm        */
+#define DMPAPER_A2                  66	/* A2 420 x 594 mm                    */
+#define DMPAPER_A3_TRANSVERSE       67	/* A3 Transverse 297 x 420 mm         */
+#define DMPAPER_A3_EXTRA_TRANSVERSE 68	/* A3 Extra Transverse 322 x 445 mm   */
+#ifndef DMPAPER_LAST
+#define DMPAPER_LAST                DMPAPER_A3_EXTRA_TRANSVERSE
+#endif /*DMPAPER_LAST */
+
+#define DMPAPER_USER                256
+
+/* bin selections */
+#ifndef DMPAPER_FIRST
+#define DMBIN_FIRST         DMBIN_UPPER
+#endif /*DMPAPER_FIRST*/
+
+#define DMBIN_UPPER         1
+#define DMBIN_ONLYONE       1
+#define DMBIN_LOWER         2
+#define DMBIN_MIDDLE        3
+#define DMBIN_MANUAL        4
+#define DMBIN_ENVELOPE      5
+#define DMBIN_ENVMANUAL     6
+#define DMBIN_AUTO          7
+#define DMBIN_TRACTOR       8
+#define DMBIN_SMALLFMT      9
+#define DMBIN_LARGEFMT      10
+#define DMBIN_LARGECAPACITY 11
+#define DMBIN_CASSETTE      14
+#define DMBIN_FORMSOURCE    15
+
+#ifndef DMBIN_LAST 
+#define DMBIN_LAST          DMBIN_FORMSOURCE
+#endif /*DMBIN_LAST*/
+
+#define DMBIN_USER          256	/* device specific bins start here */
+
+/* print qualities */
+#define DMRES_DRAFT         (-1)
+#define DMRES_LOW           (-2)
+#define DMRES_MEDIUM        (-3)
+#define DMRES_HIGH          (-4)
+
+#define DMTT_DOWNLOAD_OUTLINE 4	/* download TT fonts as outline soft fonts */
+
+
+#endif /* _MISSING_H */
Index: trunk/kitgen/8.x/blt/library/bltCanvEps.pro
===================================================================
--- trunk/kitgen/8.x/blt/library/bltCanvEps.pro	(revision 175)
+++ trunk/kitgen/8.x/blt/library/bltCanvEps.pro	(revision 175)
@@ -0,0 +1,78 @@
+%
+% PostScript encapulator prolog file of the BLT "eps" canvas item.
+%
+% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.
+%
+% 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 the
+% copyright notice and warranty disclaimer appear in supporting documentation,
+% and that the names of Lucent Technologies any of their entities not be used
+% in advertising or publicity pertaining to distribution of the software
+% without specific, written prior permission.
+%
+% Lucent Technologies disclaims all warranties with regard to this software,
+% including all implied warranties of merchantability and fitness.  In no event
+% shall Lucent Technologies be liable for any special, indirect or
+% consequential damages or any damages whatsoever resulting from loss of use,
+% data or profits, whether in an action of contract, negligence or other
+% tortuous action, arising out of or in connection with the use or performance
+% of this software.
+%
+
+%
+% The definitions of the next two macros are from Appendix H of 
+% Adobe's "PostScript Language Reference Manual" pp. 709-736.
+% 
+
+% Prepare for EPS file
+
+/BeginEPSF {				
+  /beforeInclusionState save def
+  /dictCount countdictstack def		% Save the # objects in the dictionary
+  /opCount count 1 sub def		% Count object on operator stack
+  userdict begin			% Make "userdict" the current 
+					% dictionary
+    /showpage {} def			% Redefine showpage to be null
+    0 setgray 
+    0 setlinecap
+    1 setlinewidth
+    0 setlinejoin
+    10 setmiterlimit
+    [] 0 setdash
+    newpath
+    /languagellevel where {
+      pop languagelevel 
+      1 ne {
+	false setstrokeadjust false setoverprint
+      } if
+    } if
+    % note: no "end"
+} bind def
+
+/EndEPSF { %def
+  count opCount sub {
+    pop
+  } repeat
+  countdictstack dictCount sub { 
+  end					% Clean up dictionary stack
+  } repeat
+  beforeInclusionState restore
+} bind def
+
+
+%
+% Set up a clip region based upon a bounding box (x1, y1, x2, y2).
+%
+/SetClipRegion {
+  % Stack: x1 y1 x2 y2
+  newpath
+  4 2 roll moveto
+  1 index 0 rlineto
+  0 exch rlineto
+  neg 0 rlineto
+  closepath
+  clip
+  newpath
+} def
+
Index: trunk/kitgen/8.x/blt/library/bltGraph.pro
===================================================================
--- trunk/kitgen/8.x/blt/library/bltGraph.pro	(revision 175)
+++ trunk/kitgen/8.x/blt/library/bltGraph.pro	(revision 175)
@@ -0,0 +1,471 @@
+%%BeginProlog
+%
+% PostScript prolog file of the BLT graph widget.
+%
+% Copyright 1989-1992 Regents of the University of California.
+% 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.  The University of California
+% makes no representations about the suitability of this
+% software for any purpose.  It is provided "as is" without
+% express or implied warranty.
+%
+% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.
+%
+% 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 the
+% copyright notice and warranty disclaimer appear in supporting documentation,
+% and that the names of Lucent Technologies any of their entities not be used
+% in advertising or publicity pertaining to distribution of the software
+% without specific, written prior permission.
+%
+% Lucent Technologies disclaims all warranties with regard to this software,
+% including all implied warranties of merchantability and fitness.  In no event
+% shall Lucent Technologies be liable for any special, indirect or
+% consequential damages or any damages whatsoever resulting from loss of use,
+% data or profits, whether in an action of contract, negligence or other
+% tortuous action, arising out of or in connection with the use or performance
+% of this software.
+%
+
+200 dict begin
+
+/BaseRatio 1.3467736870885982 def	% Ratio triangle base / symbol size
+/BgColorProc 0 def			% Background color routine (symbols)
+/DrawSymbolProc 0 def			% Routine to draw symbol outline/fill
+/StippleProc 0 def			% Stipple routine (bar segments)
+/DashesProc 0 def			% Dashes routine (line segments)
+  
+% Define the array ISOLatin1Encoding (which specifies how characters are 
+% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript 
+% level 2 is supposed to define it, but level 1 doesn't). 
+ 
+systemdict /ISOLatin1Encoding known not { 
+  /ISOLatin1Encoding [ 
+    /space /space /space /space /space /space /space /space 
+    /space /space /space /space /space /space /space /space 
+    /space /space /space /space /space /space /space /space 
+    /space /space /space /space /space /space /space /space 
+    /space /exclam /quotedbl /numbersign /dollar /percent /ampersand 
+    /quoteright 
+    /parenleft /parenright /asterisk /plus /comma /minus /period /slash 
+    /zero /one /two /three /four /five /six /seven 
+    /eight /nine /colon /semicolon /less /equal /greater /question 
+    /at /A /B /C /D /E /F /G 
+    /H /I /J /K /L /M /N /O 
+    /P /Q /R /S /T /U /V /W 
+    /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore 
+    /quoteleft /a /b /c /d /e /f /g 
+    /h /i /j /k /l /m /n /o 
+    /p /q /r /s /t /u /v /w 
+    /x /y /z /braceleft /bar /braceright /asciitilde /space 
+    /space /space /space /space /space /space /space /space 
+    /space /space /space /space /space /space /space /space 
+    /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent 
+    /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron 
+    /space /exclamdown /cent /sterling /currency /yen /brokenbar /section 
+    /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen 
+    /registered /macron 
+    /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph 
+    /periodcentered 
+    /cedillar /onesuperior /ordmasculine /guillemotright /onequarter 
+    /onehalf /threequarters /questiondown 
+    /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla 
+    /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex 
+    /Idieresis 
+    /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply 
+    /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn 
+    /germandbls 
+    /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla 
+    /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex 
+    /idieresis 
+    /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide 
+    /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn 
+    /ydieresis 
+  ] def 
+} if 
+
+% font ISOEncode font 
+% This procedure changes the encoding of a font from the default 
+% Postscript encoding to ISOLatin1.  It is typically invoked just 
+% before invoking "setfont".  The body of this procedure comes from 
+% Section 5.6.1 of the Postscript book. 
+
+/ISOEncode { 
+  dup length dict
+  begin 
+    {1 index /FID ne {def} {pop pop} ifelse} forall 
+    /Encoding ISOLatin1Encoding def 
+    currentdict 
+  end 
+
+  % I'm not sure why it's necessary to use "definefont" on this new 
+  % font, but it seems to be important; just use the name "Temporary" 
+  % for the font. 
+
+  /Temporary exch definefont 
+} bind def 
+
+/Stroke {
+  gsave
+    stroke
+  grestore
+} def
+
+/Fill {
+  gsave
+    fill
+  grestore
+} def
+
+/SetFont { 	
+  % Stack: pointSize fontName
+  findfont exch scalefont ISOEncode setfont
+} def
+
+/Box {
+  % Stack: x y width height
+  newpath
+  exch 4 2 roll moveto
+  dup 0 rlineto
+  exch 0 exch rlineto
+  neg 0 rlineto
+  closepath
+} def
+
+/SetFgColor {
+  % Stack: red green blue
+  CL 0 eq { 
+    pop pop pop 0 0 0 
+  } if
+  setrgbcolor
+  CL 1 eq { 
+    currentgray setgray 
+  } if
+} def
+
+/SetBgColor {
+  % Stack: red green blue
+  CL 0 eq { 
+    pop pop pop 1 1 1 
+  } if
+  setrgbcolor
+  CL 1 eq { 
+    currentgray setgray 
+  } if
+} def
+
+% The next two definitions are taken from "$tk_library/prolog.ps"
+
+% desiredSize EvenPixels closestSize
+%
+% The procedure below is used for stippling.  Given the optimal size
+% of a dot in a stipple pattern in the current user coordinate system,
+% compute the closest size that is an exact multiple of the device's
+% pixel size.  This allows stipple patterns to be displayed without
+% aliasing effects.
+
+/EvenPixels {
+  % Compute exact number of device pixels per stipple dot.
+  dup 0 matrix currentmatrix dtransform
+  dup mul exch dup mul add sqrt
+
+  % Round to an integer, make sure the number is at least 1, and compute
+  % user coord distance corresponding to this.
+  dup round dup 1 lt {pop 1} if
+  exch div mul
+} bind def
+
+% width height string filled StippleFill --
+%
+% Given a path and other graphics information already set up, this
+% procedure will fill the current path in a stippled fashion.  "String"
+% contains a proper image description of the stipple pattern and
+% "width" and "height" give its dimensions.  If "filled" is true then
+% it means that the area to be stippled is gotten by filling the
+% current path (e.g. the interior of a polygon); if it's false, the
+% area is gotten by stroking the current path (e.g. a wide line).
+% Each stipple dot is assumed to be about one unit across in the
+% current user coordinate system.
+
+% width height string StippleFill --
+%
+% Given a path already set up and a clipping region generated from
+% it, this procedure will fill the clipping region with a stipple
+% pattern.  "String" contains a proper image description of the
+% stipple pattern and "width" and "height" give its dimensions.  Each
+% stipple dot is assumed to be about one unit across in the current
+% user coordinate system.  This procedure trashes the graphics state.
+
+/StippleFill {
+    % The following code is needed to work around a NeWSprint bug.
+
+    /tmpstip 1 index def
+
+    % Change the scaling so that one user unit in user coordinates
+    % corresponds to the size of one stipple dot.
+    1 EvenPixels dup scale
+
+    % Compute the bounding box occupied by the path (which is now
+    % the clipping region), and round the lower coordinates down
+    % to the nearest starting point for the stipple pattern.  Be
+    % careful about negative numbers, since the rounding works
+    % differently on them.
+
+    pathbbox
+    4 2 roll
+    5 index div dup 0 lt {1 sub} if cvi 5 index mul 4 1 roll
+    6 index div dup 0 lt {1 sub} if cvi 6 index mul 3 2 roll
+
+    % Stack now: width height string y1 y2 x1 x2
+    % Below is a doubly-nested for loop to iterate across this area
+    % in units of the stipple pattern size, going up columns then
+    % across rows, blasting out a stipple-pattern-sized rectangle at
+    % each position
+
+    6 index exch {
+	2 index 5 index 3 index {
+	    % Stack now: width height string y1 y2 x y
+
+	    gsave
+	    1 index exch translate
+	    5 index 5 index true matrix tmpstip imagemask
+	    grestore
+	} for
+	pop
+    } for
+    pop pop pop pop pop
+} bind def
+
+
+/LS {	% Stack: x1 y1 x2 y2
+  newpath 4 2 roll moveto lineto stroke
+} def
+
+/EndText {
+  %Stack :
+  grestore
+} def
+
+/BeginText {
+  %Stack :  w h theta centerX centerY
+  gsave
+    % Translate the origin to the center of bounding box and rotate
+    translate neg rotate
+    % Translate back to the origin of the text region
+    -0.5 mul exch -0.5 mul exch translate
+} def
+
+/DrawAdjText {
+  %Stack : str strWidth x y
+  moveto				% Go to the text position
+  exch dup dup 4 2 roll
+
+  % Adjust character widths to get desired overall string width
+  % adjust X = (desired width - real width)/#chars
+
+  stringwidth pop sub exch
+  length div
+  0 3 -1 roll
+
+  % Flip back the scale so that the string is not drawn in reverse
+
+  gsave
+    1 -1 scale
+    ashow
+  grestore
+} def
+
+/DrawBitmap {
+  % Stack: ?bgColorProc? boolean centerX centerY width height theta imageStr
+  gsave
+    6 -2 roll translate			% Translate to center of bounding box
+    4 1 roll neg rotate			% Rotate by theta
+    
+    % Find upperleft corner of bounding box
+    
+    2 copy -.5 mul exch -.5 mul exch translate
+    2 copy scale			% Make pixel unit scale
+    newpath
+    0 0 moveto 0 1 lineto 1 1 lineto 1 0 lineto
+    closepath
+    
+    % Fill rectangle with background color
+    
+    4 -1 roll { 
+      gsave 
+	4 -1 roll exec fill 
+      grestore 
+    } if
+    
+    % Paint the image string into the unit rectangle
+    
+    2 copy true 3 -1 roll 0 0 5 -1 roll 0 0 6 array astore 5 -1 roll
+    imagemask
+  grestore
+} def
+
+% Symbols:
+
+% Skinny-cross
+/Sc {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate 45 rotate
+    0 0 3 -1 roll Sp
+  grestore
+} def
+
+% Skinny-plus
+/Sp {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate
+    2 idiv
+    dup 2 copy
+    newpath neg 0 moveto 0 lineto
+    DrawSymbolProc
+    newpath neg 0 exch moveto 0 exch lineto
+    DrawSymbolProc
+  grestore
+} def
+
+% Cross
+/Cr {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate 45 rotate
+    0 0 3 -1 roll Pl
+  grestore
+} def
+
+% Plus
+/Pl {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate
+    dup 2 idiv
+    exch 6 idiv
+
+    %
+    %          2   3		The plus/cross symbol is a
+    %				closed polygon of 12 points.
+    %      0   1   4    5	The diagram to the left
+    %           x,y		represents the positions of
+    %     11  10   7    6	the points which are computed
+    %				below.
+    %          9   8
+    %
+
+    newpath
+    2 copy exch neg exch neg moveto dup neg dup lineto
+    2 copy neg exch neg lineto 2 copy exch neg lineto
+    dup dup neg lineto 2 copy neg lineto 2 copy lineto
+    dup dup lineto 2 copy exch lineto 2 copy neg exch lineto
+    dup dup neg exch lineto exch neg exch lineto
+    closepath
+    DrawSymbolProc
+  grestore
+} def
+
+% Circle
+/Ci {
+  % Stack: x y symbolSize
+  gsave
+    3 copy pop
+    moveto newpath
+    2 div 0 360 arc
+    closepath DrawSymbolProc
+  grestore
+} def
+
+% Square
+/Sq {
+  % Stack: x y symbolSize
+  gsave
+    dup dup 2 div dup
+    6 -1 roll exch sub exch
+    5 -1 roll exch sub 4 -2 roll Box
+    DrawSymbolProc
+  grestore
+} def
+
+% Line
+/Li {
+  % Stack: x y symbolSize
+  gsave
+    3 1 roll exch 3 -1 roll 2 div 3 copy
+    newpath
+    sub exch moveto add exch lineto
+    stroke
+  grestore
+} def
+
+% Diamond
+/Di {
+  % Stack: x y symbolSize
+  gsave
+    3 1 roll translate 45 rotate 0 0 3 -1 roll Sq
+  grestore
+} def
+    
+% Triangle
+/Tr {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate
+    BaseRatio mul 0.5 mul		% Calculate 1/2 base
+    dup 0 exch 30 cos mul		% h1 = height above center point
+    neg					% b2 0 -h1
+    newpath moveto			% point 1;  b2
+    dup 30 sin 30 cos div mul		% h2 = height below center point
+    2 copy lineto			% point 2;  b2 h2
+    exch neg exch lineto		% 
+    closepath
+    DrawSymbolProc
+  grestore
+} def
+
+% Arrow
+/Ar {
+  % Stack: x y symbolSize
+  gsave
+    3 -2 roll translate
+    BaseRatio mul 0.5 mul		% Calculate 1/2 base
+    dup 0 exch 30 cos mul		% h1 = height above center point
+					% b2 0 h1
+    newpath moveto			% point 1;  b2
+    dup 30 sin 30 cos div mul		% h2 = height below center point
+    neg					% -h2 b2
+    2 copy lineto			% point 2;  b2 h2
+    exch neg exch lineto		% 
+    closepath
+    DrawSymbolProc
+  grestore
+} def
+
+% Bitmap
+/Bm {
+  % Stack: x y symbolSize
+  gsave
+    3 1 roll translate pop DrawSymbolProc
+  grestore
+} def
+
+%%EndProlog
+
+%%BeginSetup
+gsave					% Save the graphics state
+
+% Default line/text style parameters
+
+1 setlinewidth				% width
+1 setlinejoin				% join
+0 setlinecap				% cap
+[] 0 setdash				% dashes
+
+/CL 0 def				% Set color level mode
+0 0 0 setrgbcolor			% color
+
Index: trunk/kitgen/8.x/blt/library/graph.tcl
===================================================================
--- trunk/kitgen/8.x/blt/library/graph.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/library/graph.tcl	(revision 175)
@@ -0,0 +1,496 @@
+
+proc Blt_ActiveLegend { graph } {
+    $graph legend bind all <Enter> [list blt::ActivateLegend $graph ]
+    $graph legend bind all <Leave> [list blt::DeactivateLegend $graph]
+    $graph legend bind all <ButtonPress-1> [list blt::HighlightLegend $graph]
+}
+
+proc Blt_Crosshairs { graph } {
+    blt::Crosshairs $graph 
+}
+
+proc Blt_ResetCrosshairs { graph state } {
+    blt::Crosshairs $graph "Any-Motion" $state
+}
+
+proc Blt_ZoomStack { graph } {
+    blt::ZoomStack $graph
+}
+
+proc Blt_PrintKey { graph } {
+    blt::PrintKey $graph
+}
+
+proc Blt_ClosestPoint { graph } {
+    blt::ClosestPoint $graph
+}
+
+#
+# The following procedures that reside in the "blt" namespace are
+# supposed to be private.
+#
+
+proc blt::ActivateLegend { graph } {
+    set elem [$graph legend get current]
+    $graph legend activate $elem
+}
+proc blt::DeactivateLegend { graph } {
+    set elem [$graph legend get current]
+    $graph legend deactivate $elem
+}
+
+proc blt::HighlightLegend { graph } {
+    set elem [$graph legend get current]
+    set relief [$graph element cget $elem -labelrelief]
+    if { $relief == "flat" } {
+	$graph element configure $elem -labelrelief raised
+	$graph element activate $elem
+    } else {
+	$graph element configure $elem -labelrelief flat
+	$graph element deactivate $elem
+    }
+}
+
+proc blt::Crosshairs { graph {event "Any-Motion"} {state "on"}} {
+    $graph crosshairs $state
+    bind crosshairs-$graph <$event>   {
+	%W crosshairs configure -position @%x,%y 
+    }
+    bind crosshairs-$graph <Leave>   {
+	%W crosshairs off
+    }
+    bind crosshairs-$graph <Enter>   {
+	%W crosshairs on
+    }
+    $graph crosshairs configure -color red
+    if { $state == "on" } {
+	blt::AddBindTag $graph crosshairs-$graph
+    } elseif { $state == "off" } {
+	blt::RemoveBindTag $graph crosshairs-$graph
+    }
+}
+
+proc blt::InitStack { graph } {
+    global zoomInfo
+    set zoomInfo($graph,interval) 100
+    set zoomInfo($graph,afterId) 0
+    set zoomInfo($graph,A,x) {}
+    set zoomInfo($graph,A,y) {}
+    set zoomInfo($graph,B,x) {}
+    set zoomInfo($graph,B,y) {}
+    set zoomInfo($graph,stack) {}
+    set zoomInfo($graph,corner) A
+}
+
+proc blt::ZoomStack { graph {start "ButtonPress-1"} {reset "ButtonPress-3"} } {
+    global zoomInfo zoomMod
+    
+    blt::InitStack $graph
+    
+    if { [info exists zoomMod] } {
+	set modifier $zoomMod
+    } else {
+	set modifier ""
+    }
+    bind zoom-$graph <${modifier}${start}> { blt::SetZoomPoint %W %x %y }
+    bind zoom-$graph <${modifier}${reset}> { 
+	if { [%W inside %x %y] } { 
+	    blt::ResetZoom %W 
+	}
+    }
+    blt::AddBindTag $graph zoom-$graph
+}
+
+proc blt::PrintKey { graph {event "Shift-ButtonRelease-3"} } {
+    bind print-$graph <$event>  { Blt_PostScriptDialog %W }
+    blt::AddBindTag $graph print-$graph
+}
+
+proc blt::ClosestPoint { graph {event "Control-ButtonPress-2"} } {
+    bind closest-point-$graph <$event>  {
+	blt::FindElement %W %x %y
+    }
+    blt::AddBindTag $graph closest-point-$graph
+}
+
+proc blt::AddBindTag { widget tag } {
+    set oldTagList [bindtags $widget]
+    if { [lsearch $oldTagList $tag] < 0 } {
+	bindtags $widget [linsert $oldTagList 0  $tag]
+    }
+}
+
+proc blt::RemoveBindTag { widget tag } {
+    set oldTagList [bindtags $widget]
+    set index [lsearch $oldTagList $tag]
+    if { $index >= 0 } {
+	bindtags $widget [lreplace $oldTagList $index $index]
+    }
+}
+
+proc blt::FindElement { graph x y } {
+    if ![$graph element closest $x $y info -interpolate yes] {
+	beep
+	return
+    }
+    # --------------------------------------------------------------
+    # find(name)		- element Id
+    # find(index)		- index of closest point
+    # find(x) find(y)		- coordinates of closest point
+    #				  or closest point on line segment.
+    # find(dist)		- distance from sample coordinate
+    # --------------------------------------------------------------
+    set markerName "bltClosest_$info(name)"
+    catch { $graph marker delete $markerName }
+    $graph marker create text -coords { $info(x) $info(y) } \
+	-name $markerName \
+	-text "$info(name): $info(dist)\nindex $info(index)" \
+	-font *lucida*-r-*-10-* \
+	-anchor center -justify left \
+	-yoffset 0 -bg {} 
+
+    set coords [$graph invtransform $x $y]
+    set nx [lindex $coords 0]
+    set ny [lindex $coords 1]
+
+    $graph marker create line -coords "$nx $ny $info(x) $info(y)" \
+	-name line.$markerName 
+
+    blt::FlashPoint $graph $info(name) $info(index) 10
+    blt::FlashPoint $graph $info(name) [expr $info(index) + 1] 10
+}
+
+proc blt::FlashPoint { graph name index count } {
+    if { $count & 1 } {
+        $graph element deactivate $name 
+    } else {
+        $graph element activate $name $index
+    }
+    incr count -1
+    if { $count > 0 } {
+	after 200 blt::FlashPoint $graph $name $index $count
+	update
+    } else {
+	eval $graph marker delete [$graph marker names "bltClosest_*"]
+    }
+}
+
+proc blt::GetCoords { graph x y index } {
+    global zoomInfo
+    if { [$graph cget -invertxy] } {
+	set zoomInfo($graph,$index,x) $y
+	set zoomInfo($graph,$index,y) $x
+    } else {
+	set zoomInfo($graph,$index,x) $x
+	set zoomInfo($graph,$index,y) $y
+    }
+}
+
+proc blt::MarkPoint { graph index } {
+    global zoomInfo
+    set x [$graph xaxis invtransform $zoomInfo($graph,$index,x)]
+    set y [$graph yaxis invtransform $zoomInfo($graph,$index,y)]
+    set marker "zoomText_$index"
+    set text [format "x=%.4g\ny=%.4g" $x $y] 
+
+    if [$graph marker exists $marker] {
+     	$graph marker configure $marker -coords { $x $y } -text $text 
+    } else {
+    	$graph marker create text -coords { $x $y } -name $marker \
+   	    -font *lucida*-r-*-10-* \
+	    -text $text -anchor center -bg {} -justify left
+    }
+}
+
+proc blt::DestroyZoomTitle { graph } {
+    global zoomInfo
+
+    if { $zoomInfo($graph,corner) == "A" } {
+	catch { $graph marker delete "zoomTitle" }
+    }
+}
+
+proc blt::PopZoom { graph } {
+    global zoomInfo
+
+    set zoomStack $zoomInfo($graph,stack)
+    if { [llength $zoomStack] > 0 } {
+	set cmd [lindex $zoomStack 0]
+	set zoomInfo($graph,stack) [lrange $zoomStack 1 end]
+	eval $cmd
+	blt::ZoomTitleLast $graph
+	busy hold $graph
+	update
+	busy release $graph
+	after 2000 "blt::DestroyZoomTitle $graph"
+    } else {
+	catch { $graph marker delete "zoomTitle" }
+    }
+}
+
+# Push the old axis limits on the stack and set the new ones
+
+proc blt::PushZoom { graph } {
+    global zoomInfo
+    eval $graph marker delete [$graph marker names "zoom*"]
+    if { [info exists zoomInfo($graph,afterId)] } {
+	after cancel $zoomInfo($graph,afterId)
+    }
+    set x1 $zoomInfo($graph,A,x)
+    set y1 $zoomInfo($graph,A,y)
+    set x2 $zoomInfo($graph,B,x)
+    set y2 $zoomInfo($graph,B,y)
+
+    if { ($x1 == $x2) || ($y1 == $y2) } { 
+	# No delta, revert to start
+	return
+    }
+    set cmd {}
+    foreach margin { xaxis yaxis x2axis y2axis } {
+	foreach axis [$graph $margin use] {
+	    set min [$graph axis cget $axis -min] 
+	    set max [$graph axis cget $axis -max]
+	    set c [list $graph axis configure $axis -min $min -max $max]
+	    append cmd "$c\n"
+	}
+    }
+    set zoomInfo($graph,stack) [linsert $zoomInfo($graph,stack) 0 $cmd]
+
+
+    foreach margin { xaxis x2axis } {
+	foreach axis [$graph $margin use] {
+	    set min [$graph axis invtransform $axis $x1]
+	    set max [$graph axis invtransform $axis $x2]
+	    if { $min > $max } { 
+		$graph axis configure $axis -min $max -max $min
+	    } else {
+		$graph axis configure $axis -min $min -max $max
+	    }
+	}
+    }
+    foreach margin { yaxis y2axis } {
+	foreach axis [$graph $margin use] {
+	    set min [$graph axis invtransform $axis $y1]
+	    set max [$graph axis invtransform $axis $y2]
+	    if { $min > $max } { 
+		$graph axis configure $axis -min $max -max $min
+	    } else {
+		$graph axis configure $axis -min $min -max $max
+	    }
+	}
+    }
+    busy hold $graph 
+    update;				# This "update" redraws the graph
+    busy release $graph
+}
+
+#
+# This routine terminates either an existing zoom, or pops back to
+# the previous zoom level (if no zoom is in progress).
+#
+
+proc blt::ResetZoom { graph } {
+    global zoomInfo 
+
+    if { ![info exists zoomInfo($graph,corner)] } {
+	blt::InitStack $graph 
+    }
+    eval $graph marker delete [$graph marker names "zoom*"]
+
+    if { $zoomInfo($graph,corner) == "A" } {
+	# Reset the whole axis
+	blt::PopZoom $graph
+    } else {
+	global zoomMod
+
+	if { [info exists zoomMod] } {
+	    set modifier $zoomMod
+	} else {
+	    set modifier "Any-"
+	}
+	set zoomInfo($graph,corner) A
+	blt::RemoveBindTag $graph select-region-$graph
+    }
+}
+
+option add *zoomTitle.font	  -*-helvetica-medium-R-*-*-18-*-*-*-*-*-*-* 
+option add *zoomTitle.shadow	  yellow4
+option add *zoomTitle.foreground  yellow1
+option add *zoomTitle.coords	  "-Inf Inf"
+
+proc blt::ZoomTitleNext { graph } {
+    global zoomInfo
+    set level [expr [llength $zoomInfo($graph,stack)] + 1]
+    if { [$graph cget -invertxy] } {
+	set coords "-Inf -Inf"
+    } else {
+	set coords "-Inf Inf"
+    }
+    $graph marker create text -name "zoomTitle" -text "Zoom #$level" \
+	-coords $coords -bindtags "" -anchor nw
+}
+
+proc blt::ZoomTitleLast { graph } {
+    global zoomInfo
+
+    set level [llength $zoomInfo($graph,stack)]
+    if { $level > 0 } {
+     	$graph marker create text -name "zoomTitle" -anchor nw \
+	    -text "Zoom #$level" 
+    }
+}
+
+
+proc blt::SetZoomPoint { graph x y } {
+    global zoomInfo zoomMod
+    if { ![info exists zoomInfo($graph,corner)] } {
+	blt::InitStack $graph
+    }
+    blt::GetCoords $graph $x $y $zoomInfo($graph,corner)
+    if { [info exists zoomMod] } {
+	set modifier $zoomMod
+    } else {
+	set modifier "Any-"
+    }
+    bind select-region-$graph <${modifier}Motion> { 
+	blt::GetCoords %W %x %y B
+	#blt::MarkPoint $graph B
+	blt::Box %W
+    }
+    if { $zoomInfo($graph,corner) == "A" } {
+	if { ![$graph inside $x $y] } {
+	    return
+	}
+	# First corner selected, start watching motion events
+
+	#blt::MarkPoint $graph A
+	blt::ZoomTitleNext $graph 
+
+	blt::AddBindTag $graph select-region-$graph
+	set zoomInfo($graph,corner) B
+    } else {
+	# Delete the modal binding
+	blt::RemoveBindTag $graph select-region-$graph
+	blt::PushZoom $graph 
+	set zoomInfo($graph,corner) A
+    }
+}
+
+option add *zoomOutline.dashes		4	
+option add *zoomTitle.anchor		nw
+option add *zoomOutline.lineWidth	2
+option add *zoomOutline.xor		yes
+
+proc blt::MarchingAnts { graph offset } {
+    global zoomInfo
+
+    incr offset
+    if { [$graph marker exists zoomOutline] } {
+	$graph marker configure zoomOutline -dashoffset $offset 
+	set interval $zoomInfo($graph,interval)
+	set id [after $interval [list blt::MarchingAnts $graph $offset]]
+	set zoomInfo($graph,afterId) $id
+    }
+}
+
+proc blt::Box { graph } {
+    global zoomInfo
+
+    if { $zoomInfo($graph,A,x) > $zoomInfo($graph,B,x) } { 
+	set x1 [$graph xaxis invtransform $zoomInfo($graph,B,x)]
+	set y1 [$graph yaxis invtransform $zoomInfo($graph,B,y)]
+	set x2 [$graph xaxis invtransform $zoomInfo($graph,A,x)]
+	set y2 [$graph yaxis invtransform $zoomInfo($graph,A,y)]
+    } else {
+	set x1 [$graph xaxis invtransform $zoomInfo($graph,A,x)]
+	set y1 [$graph yaxis invtransform $zoomInfo($graph,A,y)]
+	set x2 [$graph xaxis invtransform $zoomInfo($graph,B,x)]
+	set y2 [$graph yaxis invtransform $zoomInfo($graph,B,y)]
+    }
+    set coords { $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1 }
+    if { [$graph marker exists "zoomOutline"] } {
+	$graph marker configure "zoomOutline" -coords $coords 
+    } else {
+	set X [lindex [$graph xaxis use] 0]
+	set Y [lindex [$graph yaxis use] 0]
+	$graph marker create line -coords $coords -name "zoomOutline" \
+	    -mapx $X -mapy $Y
+	set interval $zoomInfo($graph,interval)
+	set id [after $interval [list blt::MarchingAnts $graph 0]]
+	set zoomInfo($graph,afterId) $id
+    }
+}
+
+
+proc Blt_PostScriptDialog { graph } {
+    set top $graph.top
+    toplevel $top
+
+    foreach var { center landscape maxpect preview decorations padx 
+	pady paperwidth paperheight width height colormode } {
+	global $graph.$var
+	set $graph.$var [$graph postscript cget -$var]
+    }
+    set row 1
+    set col 0
+    label $top.title -text "PostScript Options"
+    table $top $top.title -cspan 7
+    foreach bool { center landscape maxpect preview decorations } {
+	set w $top.$bool-label
+	label $w -text "-$bool" -font *courier*-r-*12* 
+	table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 }
+	set w $top.$bool-yes
+	global $graph.$bool
+	radiobutton $w -text "yes" -variable $graph.$bool -value 1
+	table $top $row,$col+1 $w -anchor w
+	set w $top.$bool-no
+	radiobutton $w -text "no" -variable $graph.$bool -value 0
+	table $top $row,$col+2 $w -anchor w
+	incr row
+    }
+    label $top.modes -text "-colormode" -font *courier*-r-*12* 
+    table $top $row,0 $top.modes -anchor e  -pady { 2 0 } -padx { 0 4 }
+    set col 1
+    foreach m { color greyscale } {
+	set w $top.$m
+	radiobutton $w -text $m -variable $graph.colormode -value $m
+	table $top $row,$col $w -anchor w
+	incr col
+    }
+    set row 1
+    frame $top.sep -width 2 -bd 1 -relief sunken
+    table $top $row,3 $top.sep -fill y -rspan 6
+    set col 4
+    foreach value { padx pady paperwidth paperheight width height } {
+	set w $top.$value-label
+	label $w -text "-$value" -font *courier*-r-*12* 
+	table $top $row,$col $w -anchor e  -pady { 2 0 } -padx { 0 4 }
+	set w $top.$value-entry
+	global $graph.$value
+	entry $w -textvariable $graph.$value -width 8
+	table $top $row,$col+1 $w -cspan 2 -anchor w -padx 8
+	incr row
+    }
+    table configure $top c3 -width .125i
+    button $top.cancel -text "Cancel" -command "destroy $top"
+    table $top $row,0 $top.cancel  -width 1i -pady 2 -cspan 3
+    button $top.reset -text "Reset" -command "destroy $top"
+    #table $top $row,1 $top.reset  -width 1i
+    button $top.print -text "Print" -command "blt::ResetPostScript $graph"
+    table $top $row,4 $top.print  -width 1i -pady 2 -cspan 2
+}
+
+proc blt::ResetPostScript { graph } {
+    foreach var { center landscape maxpect preview decorations padx 
+	pady paperwidth paperheight width height colormode } {
+	global $graph.$var
+	set old [$graph postscript cget -$var]
+	if { [catch {$graph postscript configure -$var [set $graph.$var]}] != 0 } {
+	    $graph postscript configure -$var $old
+	    set $graph.$var $old
+	}
+    }
+    $graph postscript output "out.ps"
+    puts stdout "wrote file \"out.ps\"."
+    flush stdout
+}
Index: trunk/kitgen/8.x/blt/library/tabnotebook.tcl
===================================================================
--- trunk/kitgen/8.x/blt/library/tabnotebook.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/library/tabnotebook.tcl	(revision 175)
@@ -0,0 +1,318 @@
+#
+# tabnotebook.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT tabnotebook widget
+# ----------------------------------------------------------------------
+#   AUTHOR:  George Howlett
+#            Bell Labs Innovations for Lucent Technologies
+#            gah@bell-labs.com
+#            http://www.tcltk.com/blt
+# ----------------------------------------------------------------------
+# Copyright (c) 1998  Lucent Technologies, Inc.
+# ======================================================================
+#
+# 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 the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness.  In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+#
+# Indicates whether to activate (highlight) tabs when the mouse passes
+# over them.  This is turned off during scan operations.
+#
+set bltTabnotebook(activate) yes
+
+# ----------------------------------------------------------------------
+# 
+# ButtonPress assignments
+#
+#   <ButtonPress-2>	Starts scan mechanism (pushes the tabs)
+#   <B2-Motion>		Adjust scan
+#   <ButtonRelease-2>	Stops scan
+#
+# ----------------------------------------------------------------------
+bind Tabnotebook <B2-Motion> {
+    %W scan dragto %x %y
+}
+
+bind Tabnotebook <ButtonPress-2> {
+    set bltTabnotebook(cursor) [%W cget -cursor]
+    set bltTabnotebook(activate) no
+    %W configure -cursor hand1
+    %W scan mark %x %y
+}
+
+bind Tabnotebook <ButtonRelease-2> {
+    %W configure -cursor $bltTabnotebook(cursor)
+    set bltTabnotebook(activate) yes
+    %W activate @%x,%y
+}
+
+# ----------------------------------------------------------------------
+# 
+# KeyPress assignments
+#
+#   <KeyPress-Up>	Moves focus to the tab immediately above the 
+#			current.
+#   <KeyPress-Down>	Moves focus to the tab immediately below the 
+#			current.
+#   <KeyPress-Left>	Moves focus to the tab immediately left of the 
+#			currently focused tab.
+#   <KeyPress-Right>	Moves focus to the tab immediately right of the 
+#			currently focused tab.
+#   <KeyPress-space>	Invokes the commands associated with the current
+#			tab.
+#   <KeyPress-Return>	Same as above.
+#   <KeyPress>		Go to next tab starting with the ASCII character.
+#
+# ----------------------------------------------------------------------
+bind Tabnotebook <KeyPress-Up> { blt::SelectTab %W "up" }
+bind Tabnotebook <KeyPress-Down> { blt::SelectTab %W "down" }
+bind Tabnotebook <KeyPress-Right> { blt::SelectTab %W "right" }
+bind Tabnotebook <KeyPress-Left> { blt::SelectTab %W "left" }
+bind Tabnotebook <KeyPress-space> { %W invoke focus }
+bind Tabnotebook <KeyPress-Return> { %W invoke focus }
+
+bind Tabnotebook <KeyPress> {
+    if { [string match {[A-Za-z0-9]*} "%A"] } {
+	blt::FindMatchingTab %W %A
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# FirstMatchingTab --
+#
+#	Find the first tab (from the tab that currently has focus) 
+#	starting with the same first letter as the tab.  It searches
+#	in order of the tab positions and wraps around. If no tab
+#	matches, it stops back at the current tab.
+#
+# Arguments:	
+#	widget		Tabnotebook widget.
+#	key		ASCII character of key pressed
+#
+# ----------------------------------------------------------------------
+proc blt::FindMatchingTab { widget key } {
+    set key [string tolower $key]
+    set itab [$widget index focus]
+    set numTabs [$widget size]
+    for { set i 0 } { $i < $numTabs } { incr i } {
+	if { [incr itab] >= $numTabs } {
+	    set itab 0
+	}
+	set label [string tolower [$widget tab cget $itab -text]]
+	if { [string index $label 0] == $key } {
+	    break
+	}
+    }
+    $widget focus $itab
+    $widget see focus
+}
+
+# ----------------------------------------------------------------------
+#
+# SelectTab --
+#
+#	Invokes the command for the tab.  If the widget associated tab 
+#	is currently torn off, the tearoff is raised.
+#
+# Arguments:	
+#	widget		Tabnotebook widget.
+#	x y		Unused.
+#
+# ----------------------------------------------------------------------
+proc blt::SelectTab { widget tab } {
+    set index [$widget index $tab]
+    if { $index != "" } {
+	$widget select $index
+	$widget focus $index
+	$widget see $index
+	set w [$widget tab tearoff $index]
+	if { ($w != "") && ($w != "$widget") } {
+	    raise [winfo toplevel $w]
+	}
+	$widget invoke $index
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# DestroyTearoff --
+#
+#	Destroys the toplevel window and the container tearoff 
+#	window holding the embedded widget.  The widget is placed
+#	back inside the tab.
+#
+# Arguments:	
+#	widget		Tabnotebook widget.
+#	tab		Tab selected.
+#
+# ----------------------------------------------------------------------
+proc blt::DestroyTearoff { widget tab } {
+    set id [$widget id $tab]
+    set top "$widget.toplevel-$id"
+    if { [winfo exists $top] } {
+	wm withdraw $top
+	update
+	$widget tab tearoff $tab $widget
+	destroy $top
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# CreateTearoff --
+#
+#	Creates a new toplevel window and moves the embedded widget
+#	into it.  The toplevel is placed just below the tab.  The
+#	DELETE WINDOW property is set so that if the toplevel window 
+#	is requested to be deleted by the window manager, the embedded
+#	widget is placed back inside of the tab.  Note also that 
+#	if the tabnotebook container is ever destroyed, the toplevel is
+#	also destroyed.  
+#
+# Arguments:	
+#	widget		Tabnotebook widget.
+#	tab		Tab selected.
+#	x y		The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::CreateTearoff { widget tab rootX rootY } {
+
+    # ------------------------------------------------------------------
+    # When reparenting the window contained in the tab, check if the
+    # window or any window in its hierarchy currently has focus.
+    # Since we're reparenting windows behind its back, Tk can
+    # mistakenly activate the keyboard focus when the mouse enters the
+    # old toplevel.  The simplest way to deal with this problem is to
+    # take the focus off the window and set it to the tabnotebook widget
+    # itself.
+    # ------------------------------------------------------------------
+
+    set focus [focus]
+    set window [$widget tab cget $tab -window]
+    set index [$widget index $tab]
+    if { ($focus == $window) || ([string match  $window.* $focus]) } {
+	focus -force $widget
+    }
+    set id [$widget id $index]
+    set top "$widget.toplevel-$id"
+    toplevel $top
+    $widget tab tearoff $tab $top.container
+    table $top $top.container -fill both
+
+    incr rootX 10 ; incr rootY 10
+    wm geometry $top +$rootX+$rootY
+
+    set parent [winfo toplevel $widget]
+    wm title $top "[wm title $parent]: [$widget tab cget $index -text]"
+    wm transient $top $parent
+
+    # If the user tries to delete the toplevel, put the window back
+    # into the tab folder.  
+
+    wm protocol $top WM_DELETE_WINDOW [list blt::DestroyTearoff $widget $tab]
+
+    # If the container is ever destroyed, automatically destroy the
+    # toplevel too.  
+
+    bind $top.container <Destroy> [list destroy $top]
+}
+
+# ----------------------------------------------------------------------
+#
+# ToggleTearoff --
+#
+#	Toggles the tab tearoff.  If the tab contains a embedded widget, 
+#	it is placed inside of a toplevel window.  If the widget has 
+#	already been torn off, the widget is replaced back in the tab.
+#
+# Arguments:	
+#	widget		tabnotebook widget.
+#	x y		The coordinates of the mouse pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::ToggleTearoff { widget x y index } {
+    set tab [$widget index $index]
+    if { $tab == "" } {
+	return
+    }
+    $widget invoke $tab
+
+    set container [$widget tab tearoff $index]
+    if { $container == "$widget" } {
+	blt::CreateTearoff $widget $tab $x $y
+    } elseif { $container != "" } {
+	blt::DestroyTearoff $widget $tab
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# TabnotebookInit
+#
+#	Invoked from C whenever a new tabnotebook widget is created.
+#	Sets up the default bindings for the all tab entries.  
+#	These bindings are local to the widget, so they can't be 
+#	set through the usual widget class bind tags mechanism.
+#
+#	<Enter>		Activates the tab.
+#	<Leave>		Deactivates all tabs.
+#	<ButtonPress-1>	Selects the tab and invokes its command.
+#	<Control-ButtonPress-1>	
+#			Toggles the tab tearoff.  If the tab contains
+#			a embedded widget, it is placed inside of a
+#			toplevel window.  If the widget has already
+#			been torn off, the widget is replaced back
+#			in the tab.
+#
+# Arguments:	
+#	widget		tabnotebook widget
+#
+# ----------------------------------------------------------------------
+proc blt::TabnotebookInit { widget } {
+    $widget bind all <Enter> { 
+	if { $bltTabnotebook(activate) } {
+	    %W activate current
+        }
+    }
+    $widget bind all <Leave> { 
+        %W activate "" 
+    }
+    $widget bind all <ButtonPress-1> { 
+	blt::SelectTab %W "current"
+    }
+    $widget bind all <Control-ButtonPress-1> { 
+	blt::ToggleTearoff %W %X %Y active
+    }
+    $widget configure -perforationcommand {
+	blt::ToggleTearoff %W $bltTabnotebook(x) $bltTabnotebook(y) select
+    }
+    $widget bind Perforation <Enter> { 
+	%W perforation activate on
+    }
+    $widget bind Perforation <Leave> { 
+	%W perforation activate off
+    }
+    $widget bind Perforation <ButtonPress-1> { 
+	set bltTabnotebook(x) %X
+	set bltTabnotebook(y) %Y
+	%W perforation invoke
+    }
+}
Index: trunk/kitgen/8.x/blt/library/treeview.tcl
===================================================================
--- trunk/kitgen/8.x/blt/library/treeview.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/library/treeview.tcl	(revision 175)
@@ -0,0 +1,1035 @@
+
+# ======================================================================
+#
+# treeview.tcl
+#
+# ----------------------------------------------------------------------
+# Bindings for the BLT treeview widget
+# ----------------------------------------------------------------------
+#
+#   AUTHOR:  George Howlett
+#            Bell Labs Innovations for Lucent Technologies
+#            gah@lucent.com
+#            http://www.tcltk.com/blt
+#
+#      RCS:  $Id: treeview.tcl,v 1.25 2002/08/06 05:08:24 ghowlett Exp $
+#
+# ----------------------------------------------------------------------
+# Copyright (c) 1998  Lucent Technologies, Inc.
+# ----------------------------------------------------------------------
+#
+# 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 the copyright notice and warranty disclaimer appear in
+# supporting documentation, and that the names of Lucent Technologies
+# any of their entities not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Lucent Technologies disclaims all warranties with regard to this
+# software, including all implied warranties of merchantability and
+# fitness.  In no event shall Lucent be liable for any special, indirect
+# or consequential damages or any damages whatsoever resulting from loss
+# of use, data or profits, whether in an action of contract, negligence
+# or other tortuous action, arising out of or in connection with the use
+# or performance of this software.
+#
+# ======================================================================
+
+namespace eval blt::tv {
+    set afterId ""
+    set scroll 0
+    set column ""
+    set space   off
+    set x 0
+    set y 0
+}
+
+image create photo blt::tv::normalCloseFolder -format gif -data {
+    R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+    AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+    ZTKAsiCtWq0JADs=
+}
+image create photo blt::tv::normalOpenFolder -format gif -data {
+    R0lGODlhEAANAMIAAAAAAH9/f///////AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+    AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+    nQkAOw==
+}
+image create photo blt::tv::activeCloseFolder -format gif -data {
+    R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+    AAM8WBrM+rAEQWmIb5KxiWjNInCkV32AJHRlGQBgDA7vdN4vUa8tC78qlrCWmvRKsJTquHkp
+    ZTKAsiCtWq0JADs=
+}
+image create photo blt::tv::activeOpenFolder -format gif -data {
+    R0lGODlhEAANAMIAAAAAAH9/f/////+/AL+/vwAA/wAAAAAAACH5BAEAAAUALAAAAAAQAA0A
+    AAM1WBrM+rAEMigJ8c3Kb3OSII6kGABhp1JnaK1VGwjwKwtvHqNzzd263M3H4n2OH1QBwGw6
+    nQkAOw==
+}
+
+if { $tcl_platform(platform) == "windows" } {
+    if { $tk_version >= 8.3 } {
+	set cursor "@[file join $blt_library treeview.cur]"
+    } else {
+	set cursor "size_we"
+    }
+    option add *${className}.ResizeCursor [list $cursor]
+} else {
+    option add *${className}.ResizeCursor \
+	"@$blt_library/treeview.xbm $blt_library/treeview_m.xbm black white"
+}
+
+# ----------------------------------------------------------------------
+#
+# Initialize --
+#
+#	Invoked by internally by Treeview_Init routine.  Initializes
+#	the default bindings for the treeview widget entries.  These
+#	are local to the widget, so they can't be set through the
+#	widget's class bind tags.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::Initialize { w } {
+    #
+    # Active entry bindings
+    #
+    $w bind Entry <Enter> { 
+	%W entry highlight current 
+    }
+    $w bind Entry <Leave> { 
+	%W entry highlight "" 
+    }
+
+    #
+    # Button bindings
+    #
+    $w button bind all <ButtonRelease-1> {
+	%W see -anchor nw current
+	%W toggle current
+    }
+    $w button bind all <Enter> {
+	%W button highlight current
+    }
+    $w button bind all <Leave> {
+	%W button highlight ""
+    }
+
+    #
+    # ButtonPress-1
+    #
+    #	Performs the following operations:
+    #
+    #	1. Clears the previous selection.
+    #	2. Selects the current entry.
+    #	3. Sets the focus to this entry.
+    #	4. Scrolls the entry into view.
+    #	5. Sets the selection anchor to this entry, just in case
+    #	   this is "multiple" mode.
+    #
+    
+    $w bind Entry <ButtonPress-1> { 	
+	blt::tv::SetSelectionAnchor %W current
+	set blt::tv::scroll 1
+    }
+
+    $w bind Entry <Double-ButtonPress-1> {
+	%W toggle current
+    }
+
+    #
+    # B1-Motion
+    #
+    #	For "multiple" mode only.  Saves the current location of the
+    #	pointer for auto-scrolling.  Resets the selection mark.  
+    #
+    $w bind Entry <B1-Motion> { 
+	set blt::tv::x %x
+	set blt::tv::y %y
+	set index [%W nearest %x %y]
+	if { [%W cget -selectmode] == "multiple" } {
+	    %W selection mark $index
+	} else {
+	    blt::tv::SetSelectionAnchor %W $index
+	}
+    }
+
+    #
+    # ButtonRelease-1
+    #
+    #	For "multiple" mode only.  
+    #
+    $w bind Entry <ButtonRelease-1> { 
+	if { [%W cget -selectmode] == "multiple" } {
+	    %W selection anchor current
+	}
+	after cancel $blt::tv::afterId
+	set blt::tv::scroll 0
+    }
+
+    #
+    # Shift-ButtonPress-1
+    #
+    #	For "multiple" mode only.
+    #
+
+    $w bind Entry <Shift-ButtonPress-1> { 
+	if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+	    if { [%W index anchor] == "" } {
+		%W selection anchor current
+	    }
+	    set index [%W index anchor]
+	    %W selection clearall
+	    %W selection set $index current
+	} else {
+	    blt::tv::SetSelectionAnchor %W current
+	}
+    }
+    $w bind Entry <Shift-Double-ButtonPress-1> {
+	# do nothing
+    }
+    $w bind Entry <Shift-B1-Motion> { 
+	# do nothing
+    }
+    $w bind Entry <Shift-ButtonRelease-1> { 
+	after cancel $blt::tv::afterId
+	set blt::tv::scroll 0
+    }
+
+    #
+    # Control-ButtonPress-1
+    #
+    #	For "multiple" mode only.  
+    #
+    $w bind Entry <Control-ButtonPress-1> { 
+	if { [%W cget -selectmode] == "multiple" } {
+	    set index [%W index current]
+	    %W selection toggle $index
+	    %W selection anchor $index
+	} else {
+	    blt::tv::SetSelectionAnchor %W current
+	}
+    }
+    $w bind Entry <Control-Double-ButtonPress-1> {
+	# do nothing
+    }
+    $w bind Entry <Control-B1-Motion> { 
+	# do nothing
+    }
+    $w bind Entry <Control-ButtonRelease-1> { 
+	after cancel $blt::tv::afterId
+	set blt::tv::scroll 0
+    }
+
+    $w bind Entry <Control-Shift-ButtonPress-1> { 
+	if { [%W cget -selectmode] == "multiple" && [%W selection present] } {
+	    if { [%W index anchor] == "" } {
+		%W selection anchor current
+	    }
+	    if { [%W selection includes anchor] } {
+		%W selection set anchor current
+	    } else {
+		%W selection clear anchor current
+		%W selection set current
+	    }
+	} else {
+	    blt::tv::SetSelectionAnchor %W current
+	}
+    }
+    $w bind Entry <Control-Shift-Double-ButtonPress-1> {
+	# do nothing
+    }
+    $w bind Entry <Control-Shift-B1-Motion> { 
+	# do nothing
+    }
+
+    $w bind Entry <Shift-ButtonPress-3> { 
+	blt::tv::EditColumn %W %X %Y
+    }
+
+    $w column bind all <Enter> {
+	%W column highlight [%W column current]
+    }
+    $w column bind all <Leave> {
+	%W column highlight ""
+    }
+    $w column bind Rule <Enter> {
+	%W column highlight [%W column current]
+	%W column resize activate [%W column current]
+    }
+    $w column bind Rule <Leave> {
+	%W column highlight ""
+	%W column resize activate ""
+    }
+    $w column bind Rule <ButtonPress-1> {
+	%W column resize anchor %x
+    }
+    $w column bind Rule <B1-Motion> {
+	%W column resize mark %x
+    }
+    $w column bind Rule <ButtonRelease-1> {
+	%W column configure [%W column current] -width [%W column resize set]
+    }
+    $w column bind all <ButtonPress-1> {
+	set blt::tv::column [%W column current]
+	%W column configure $blt::tv::column -titlerelief sunken
+    }
+    $w column bind all <ButtonRelease-1> {
+	set column [%W column current]
+	if { $column != "" } {
+	    %W column invoke $column
+	}
+	%W column configure $blt::tv::column -titlerelief raised
+    }
+    $w bind TextBoxStyle <ButtonPress-3> { 
+	if { [%W edit -root -test %X %Y] } {
+	    break
+	}
+    }
+    $w bind TextBoxStyle <ButtonRelease-3> { 
+	if { [%W edit -root -test %X %Y] } {
+	    blt::tv::EditColumn %W %X %Y
+	    break
+	}
+    }
+    $w bind CheckBoxStyle <Enter> { 
+	set column [%W column current]
+	if { [%W column cget $column -edit] } {
+	    %W style activate current $column
+	} 
+    }
+    $w bind CheckBoxStyle <Leave> { 
+	%W style activate ""
+    }
+    $w bind CheckBoxStyle <ButtonPress-1> { 
+	set column [%W column current]
+	if { [%W column cget $column -edit] } {
+	    break
+	}
+    }
+    $w bind CheckBoxStyle <B1-Motion> { 
+	set column [%W column current]
+	if { [%W column cget $column -edit] } {
+	    break
+	}
+    }
+    $w bind CheckBoxStyle <ButtonRelease-1> { 
+	if { [%W edit -root -test %X %Y] } {
+	    %W edit -root %X %Y
+	    break
+	}
+    }
+    $w bind ComboBoxStyle <ButtonPress-1> { 
+	set column [%W column current]
+	if { [%W column cget $column -edit] } {
+	    break
+	}
+    }
+    $w bind ComboBoxStyle <ButtonRelease-1> { 
+	if { [%W edit -root -test %X %Y] } {
+	    %W edit -root %X %Y
+	    break
+	}
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# AutoScroll --
+#
+#	Invoked when the user is selecting elements in a treeview
+#	widget and drags the mouse pointer outside of the widget.
+#	Scrolls the view in the direction of the pointer.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::AutoScroll { w } {
+    if { ![winfo exists $w] } {
+	return
+    }
+    set x $blt::tv::x
+    set y $blt::tv::y
+
+    set index [$w nearest $x $y]
+
+    if {$y >= [winfo height $w]} {
+	$w yview scroll 1 units
+	set neighbor down
+    } elseif {$y < 0} {
+	$w yview scroll -1 units
+	set neighbor up
+    } else {
+	set neighbor $index
+    }
+    if { [$w cget -selectmode] == "single" } {
+	blt::tv::SetSelectionAnchor $w $neighbor
+    } else {
+	$w selection mark $index
+    }
+    set ::blt::tv::afterId [after 50 blt::tv::AutoScroll $w]
+}
+
+proc blt::tv::SetSelectionAnchor { w tagOrId } {
+    set index [$w index $tagOrId]
+    # If the anchor hasn't changed, don't do anything
+    if { $index != [$w index anchor] } {
+	$w selection clearall
+	$w see $index
+	$w focus $index
+	$w selection set $index
+	$w selection anchor $index
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# MoveFocus --
+#
+#	Invoked by KeyPress bindings.  Moves the active selection to
+#	the entry <where>, which is an index such as "up", "down",
+#	"prevsibling", "nextsibling", etc.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::MoveFocus { w tagOrId } {
+    catch {$w focus $tagOrId}
+    if { [$w cget -selectmode] == "single" } {
+        $w selection clearall
+        $w selection set focus
+	$w selection anchor focus
+    }
+    $w see focus
+}
+
+# ----------------------------------------------------------------------
+#
+# MovePage --
+#
+#	Invoked by KeyPress bindings.  Pages the current view up or
+#	down.  The <where> argument should be either "top" or
+#	"bottom".
+#
+# ----------------------------------------------------------------------
+proc blt::tv::MovePage { w where } {
+
+    # If the focus is already at the top/bottom of the window, we want
+    # to scroll a page. It's really one page minus an entry because we
+    # want to see the last entry on the next/last page.
+    if { [$w index focus] == [$w index view.$where] } {
+        if {$where == "top"} {
+	    $w yview scroll -1 pages
+	    $w yview scroll 1 units
+        } else {
+	    $w yview scroll 1 pages
+	    $w yview scroll -1 units
+        }
+    }
+    update
+
+    # Adjust the entry focus and the view.  Also activate the entry.
+    # just in case the mouse point is not in the widget.
+    $w entry highlight view.$where
+    $w focus view.$where
+    $w see view.$where
+    if { [$w cget -selectmode] == "single" } {
+        $w selection clearall
+        $w selection set focus
+    }
+}
+
+# ----------------------------------------------------------------------
+#
+# NextMatch --
+#
+#	Invoked by KeyPress bindings.  Searches for an entry that
+#	starts with the letter <char> and makes that entry active.
+#
+# ----------------------------------------------------------------------
+proc blt::tv::NextMatch { w key } {
+    if {[string match {[ -~]} $key]} {
+	set last [$w index focus]
+	set next [$w index next]
+	while { $next != $last } {
+	    set label [$w entry cget $next -label]
+	    set label [string index $label 0]
+	    if { [string tolower $label] == [string tolower $key] } {
+		break
+	    }
+	    set next [$w index -at $next next]
+	}
+	$w focus $next
+	if {[$w cget -selectmode] == "single"} {
+	    $w selection clearall
+	    $w selection set focus
+	}
+	$w see focus
+    }
+}
+
+#------------------------------------------------------------------------
+#
+# InsertText --
+#
+#	Inserts a text string into an entry at the insertion cursor.  
+#	If there is a selection in the entry, and it covers the point 
+#	of the insertion cursor, then delete the selection before 
+#	inserting.
+#
+# Arguments:
+#	w 	Widget where to insert the text.
+#	text	Text string to insert (usually just a single character)
+#
+#------------------------------------------------------------------------
+proc blt::tv::InsertText { w text } {
+    if { [string length $text] > 0 } {
+	set index [$w index insert]
+	if { ($index >= [$w index sel.first]) && 
+	     ($index <= [$w index sel.last]) } {
+	    $w delete sel.first sel.last
+	}
+	$w insert $index $text
+    }
+}
+
+#------------------------------------------------------------------------
+#
+# Transpose -
+#
+#	This procedure implements the "transpose" function for entry
+#	widgets.  It tranposes the characters on either side of the
+#	insertion cursor, unless the cursor is at the end of the line.
+#	In this case it transposes the two characters to the left of
+#	the cursor.  In either case, the cursor ends up to the right
+#	of the transposed characters.
+#
+# Arguments:
+#	w 	The entry window.
+#
+#------------------------------------------------------------------------
+proc blt::tv::Transpose { w } {
+    set i [$w index insert]
+    if {$i < [$w index end]} {
+	incr i
+    }
+    set first [expr {$i-2}]
+    if {$first < 0} {
+	return
+    }
+    set new [string index [$w get] [expr {$i-1}]][string index [$w get] $first]
+    $w delete $first $i
+    $w insert insert $new
+}
+
+#------------------------------------------------------------------------
+#
+# GetSelection --
+#
+#	Returns the selected text of the entry with respect to the
+#	-show option.
+#
+# Arguments:
+#	w          Entry window from which the text to get
+#
+#------------------------------------------------------------------------
+
+proc blt::tv::GetSelection { w } {
+    set text [string range [$w get] [$w index sel.first] \
+                       [expr [$w index sel.last] - 1]]
+    if {[$w cget -show] != ""} {
+	regsub -all . $text [string index [$w cget -show] 0] text
+    }
+    return $text
+}
+
+proc blt::tv::EditColumn { w x y } {
+    $w see current
+    if { [winfo exists $w.edit] } {
+	destroy $w.edit
+    }
+    if { ![$w edit -root -test $x $y] } {
+	return
+    }
+    $w edit -root $x $y
+    update
+    focus $w.edit
+    $w.edit selection range 0 end
+    grab set $w.edit
+    tkwait window $w.edit
+    grab release $w.edit
+}
+
+# 
+# ButtonPress assignments
+#
+#	B1-Enter	start auto-scrolling
+#	B1-Leave	stop auto-scrolling
+#	ButtonPress-2	start scan
+#	B2-Motion	adjust scan
+#	ButtonRelease-2 stop scan
+#
+
+bind ${className} <ButtonPress-2> {
+    set blt::tv::cursor [%W cget -cursor]
+    %W configure -cursor hand1
+    %W scan mark %x %y
+}
+
+bind ${className} <B2-Motion> {
+    %W scan dragto %x %y
+}
+
+bind ${className} <ButtonRelease-2> {
+    %W configure -cursor $blt::tv::cursor
+}
+
+bind ${className} <B1-Leave> {
+    if { $blt::tv::scroll } {
+	blt::tv::AutoScroll %W 
+    }
+}
+
+bind ${className} <B1-Enter> {
+    after cancel $blt::tv::afterId
+}
+
+# 
+# KeyPress assignments
+#
+#	Up			
+#	Down
+#	Shift-Up
+#	Shift-Down
+#	Prior (PageUp)
+#	Next  (PageDn)
+#	Left
+#	Right
+#	space		Start selection toggle of entry currently with focus.
+#	Return		Start selection toggle of entry currently with focus.
+#	Home
+#	End
+#	F1
+#	F2
+#	ASCII char	Go to next open entry starting with character.
+#
+# KeyRelease
+#
+#	space		Stop selection toggle of entry currently with focus.
+#	Return		Stop selection toggle of entry currently with focus.
+
+
+bind ${className} <KeyPress-Up> {
+    blt::tv::MoveFocus %W up
+    if { $blt::tv::space } {
+	%W selection toggle focus
+    }
+}
+
+bind ${className} <KeyPress-Down> {
+    blt::tv::MoveFocus %W down
+    if { $blt::tv::space } {
+	%W selection toggle focus
+    }
+}
+
+bind ${className} <Shift-KeyPress-Up> {
+    blt::tv::MoveFocus %W prevsibling
+}
+
+bind ${className} <Shift-KeyPress-Down> {
+    blt::tv::MoveFocus %W nextsibling
+}
+
+bind ${className} <KeyPress-Prior> {
+    blt::tv::MovePage %W top
+}
+
+bind ${className} <KeyPress-Next> {
+    blt::tv::MovePage %W bottom
+}
+
+bind ${className} <KeyPress-Left> {
+    %W close focus
+}
+bind ${className} <KeyPress-Right> {
+    %W open focus
+    %W see focus -anchor w
+}
+
+bind ${className} <KeyPress-space> {
+    if { [%W cget -selectmode] == "single" } {
+	if { [%W selection includes focus] } {
+	    %W selection clearall
+	} else {
+	    %W selection clearall
+	    %W selection set focus
+	}
+    } else {
+	%W selection toggle focus
+    }
+    set blt::tv::space on
+}
+
+bind ${className} <KeyRelease-space> { 
+    set blt::tv::space off
+}
+
+bind ${className} <KeyPress-Return> {
+    blt::tv::MoveFocus %W focus
+    set blt::tv::space on
+}
+
+bind ${className} <KeyRelease-Return> { 
+    set blt::tv::space off
+}
+
+bind ${className} <KeyPress> {
+    blt::tv::NextMatch %W %A
+}
+
+bind ${className} <KeyPress-Home> {
+    blt::tv::MoveFocus %W top
+}
+
+bind ${className} <KeyPress-End> {
+    blt::tv::MoveFocus %W bottom
+}
+
+bind ${className} <KeyPress-F1> {
+    %W open -r root
+}
+
+bind ${className} <KeyPress-F2> {
+    eval %W close -r [%W entry children root] 
+}
+
+#
+# Differences between id "current" and operation nearest.
+#
+#	set index [$w index current]
+#	set index [$w nearest $x $y]
+#
+#	o Nearest gives you the closest entry.
+#	o current is "" if
+#	   1) the pointer isn't over an entry.
+#	   2) the pointer is over a open/close button.
+#	   3) 
+#
+
+#
+#  Edit mode assignments
+#
+#	ButtonPress-3   Enables/disables edit mode on entry.  Sets focus to 
+#			entry.
+#
+#  KeyPress
+#
+#	Left		Move insertion position to previous.
+#	Right		Move insertion position to next.
+#	Up		Move insertion position up one line.
+#	Down		Move insertion position down one line.
+#	Return		End edit mode.
+#	Shift-Return	Line feed.
+#	Home		Move to first position.
+#	End		Move to last position.
+#	ASCII char	Insert character left of insertion point.
+#	Del		Delete character right of insertion point.
+#	Delete		Delete character left of insertion point.
+#	Ctrl-X		Cut
+#	Ctrl-V		Copy
+#	Ctrl-P		Paste
+#	
+#  KeyRelease
+#
+#	ButtonPress-1	Start selection if in entry, otherwise clear selection.
+#	B1-Motion	Extend/reduce selection.
+#	ButtonRelease-1 End selection if in entry, otherwise use last
+#			selection.
+#	B1-Enter	Disabled.
+#	B1-Leave	Disabled.
+#	ButtonPress-2	Same as above.
+#	B2-Motion	Same as above.
+#	ButtonRelease-2	Same as above.
+#	
+#
+
+
+# Standard Motif bindings:
+
+bind ${className}Editor <ButtonPress-1> {
+    %W icursor @%x,%y
+    %W selection clear
+}
+
+bind ${className}Editor <Left> {
+    %W icursor last
+    %W selection clear
+}
+
+bind ${className}Editor <Right> {
+    %W icursor next
+    %W selection clear
+}
+
+bind ${className}Editor <Shift-Left> {
+    set new [expr {[%W index insert] - 1}]
+    if {![%W selection present]} {
+	%W selection from insert
+	%W selection to $new
+    } else {
+	%W selection adjust $new
+    }
+    %W icursor $new
+}
+
+bind ${className}Editor <Shift-Right> {
+    set new [expr {[%W index insert] + 1}]
+    if {![%W selection present]} {
+	%W selection from insert
+	%W selection to $new
+    } else {
+	%W selection adjust $new
+    }
+    %W icursor $new
+}
+
+bind ${className}Editor <Home> {
+    %W icursor 0
+    %W selection clear
+}
+bind ${className}Editor <Shift-Home> {
+    set new 0
+    if {![%W selection present]} {
+	%W selection from insert
+	%W selection to $new
+    } else {
+	%W selection adjust $new
+    }
+    %W icursor $new
+}
+bind ${className}Editor <End> {
+    %W icursor end
+    %W selection clear
+}
+bind ${className}Editor <Shift-End> {
+    set new end
+    if {![%W selection present]} {
+	%W selection from insert
+	%W selection to $new
+    } else {
+	%W selection adjust $new
+    }
+    %W icursor $new
+}
+
+bind ${className}Editor <Delete> {
+    if { [%W selection present]} {
+	%W delete sel.first sel.last
+    } else {
+	%W delete insert
+    }
+}
+
+bind ${className}Editor <BackSpace> {
+    if { [%W selection present] } {
+	%W delete sel.first sel.last
+    } else {
+	set index [expr [%W index insert] - 1]
+	if { $index >= 0 } {
+	    %W delete $index $index
+	}
+    }
+}
+
+bind ${className}Editor <Control-space> {
+    %W selection from insert
+}
+
+bind ${className}Editor <Select> {
+    %W selection from insert
+}
+
+bind ${className}Editor <Control-Shift-space> {
+    %W selection adjust insert
+}
+
+bind ${className}Editor <Shift-Select> {
+    %W selection adjust insert
+}
+
+bind ${className}Editor <Control-slash> {
+    %W selection range 0 end
+}
+
+bind ${className}Editor <Control-backslash> {
+    %W selection clear
+}
+
+bind ${className}Editor <KeyPress> {
+    blt::tv::InsertText %W %A
+}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong.  Ditto for Escape, Return, and Tab.
+
+bind ${className}Editor <Alt-KeyPress> {
+    # nothing
+}
+
+bind ${className}Editor <Meta-KeyPress> {
+    # nothing
+}
+
+bind ${className}Editor <Control-KeyPress> {
+    # nothing
+}
+
+bind ${className}Editor <Escape> { 
+    %W cancel 
+}
+
+bind ${className}Editor <Return> { 
+    %W apply 
+}
+
+bind ${className}Editor <Shift-Return> {
+    blt::tv::InsertText %W "\n"
+}
+
+bind ${className}Editor <KP_Enter> {
+    # nothing
+}
+
+bind ${className}Editor <Tab> {
+    # nothing
+}
+
+if {![string compare $tcl_platform(platform) "macintosh"]} {
+    bind ${className}Editor <Command-KeyPress> {
+	# nothing
+    }
+}
+
+# On Windows, paste is done using Shift-Insert.  Shift-Insert already
+# generates the <<Paste>> event, so we don't need to do anything here.
+if { [string compare $tcl_platform(platform) "windows"] != 0 } {
+    bind ${className}Editor <Insert> {
+	catch {blt::tv::InsertText %W [selection get -displayof %W]}
+    }
+}
+
+# Additional emacs-like bindings:
+bind ${className}Editor <ButtonPress-3> {
+    set parent [winfo parent %W]
+    %W cancel
+    after idle {
+	blt::tv::EditColumn $parent %X %Y
+    }
+}
+
+bind ${className}Editor <Control-a> {
+    %W icursor 0
+    %W selection clear
+}
+
+bind ${className}Editor <Control-b> {
+    %W icursor [expr {[%W index insert] - 1}]
+    %W selection clear
+}
+
+bind ${className}Editor <Control-d> {
+    %W delete insert
+}
+
+bind ${className}Editor <Control-e> {
+    %W icursor end
+    %W selection clear
+}
+
+bind ${className}Editor <Control-f> {
+    %W icursor [expr {[%W index insert] + 1}]
+    %W selection clear
+}
+
+bind ${className}Editor <Control-h> {
+    if {[%W selection present]} {
+	%W delete sel.first sel.last
+    } else {
+	set index [expr [%W index insert] - 1]
+	if { $index >= 0 } {
+	    %W delete $index $index
+	}
+    }
+}
+
+bind ${className}Editor <Control-k> {
+    %W delete insert end
+}
+
+if 0 {
+    bind ${className}Editor <Control-t> {
+	blt::tv::Transpose %W
+    }
+    bind ${className}Editor <Meta-b> {
+	%W icursor [blt::tv::PreviousWord %W insert]
+	%W selection clear
+    }
+    bind ${className}Editor <Meta-d> {
+	%W delete insert [blt::tv::NextWord %W insert]
+    }
+    bind ${className}Editor <Meta-f> {
+	%W icursor [blt::tv::NextWord %W insert]
+	%W selection clear
+    }
+    bind ${className}Editor <Meta-BackSpace> {
+	%W delete [blt::tv::PreviousWord %W insert] insert
+    }
+    bind ${className}Editor <Meta-Delete> {
+	%W delete [blt::tv::PreviousWord %W insert] insert
+    }
+    # tkEntryNextWord -- Returns the index of the next word position
+    # after a given position in the entry.  The next word is platform
+    # dependent and may be either the next end-of-word position or the
+    # next start-of-word position after the next end-of-word position.
+    #
+    # Arguments:
+    # w -		The entry window in which the cursor is to move.
+    # start -	Position at which to start search.
+    
+    if {![string compare $tcl_platform(platform) "windows"]}  {
+	proc blt::tv::NextWord {w start} {
+	    set pos [tcl_endOfWord [$w get] [$w index $start]]
+	    if {$pos >= 0} {
+		set pos [tcl_startOfNextWord [$w get] $pos]
+	    }
+	    if {$pos < 0} {
+		return end
+	    }
+	    return $pos
+	}
+    } else {
+	proc blt::tv::NextWord {w start} {
+	    set pos [tcl_endOfWord [$w get] [$w index $start]]
+	    if {$pos < 0} {
+		return end
+	    }
+	    return $pos
+	}
+    }
+    
+    # PreviousWord --
+    #
+    # Returns the index of the previous word position before a given
+    # position in the entry.
+    #
+    # Arguments:
+    # w -		The entry window in which the cursor is to move.
+    # start -	Position at which to start search.
+    
+    proc blt::tv::PreviousWord {w start} {
+	set pos [tcl_startOfPreviousWord [$w get] [$w index $start]]
+	if {$pos < 0} {
+	    return 0
+	}
+	return $pos
+    }
+}
+
Index: trunk/kitgen/8.x/blt/library/treeview.xbm
===================================================================
--- trunk/kitgen/8.x/blt/library/treeview.xbm	(revision 175)
+++ trunk/kitgen/8.x/blt/library/treeview.xbm	(revision 175)
@@ -0,0 +1,8 @@
+#define test_width 16
+#define test_height 16
+#define test_x_hot 7
+#define test_y_hot 7
+static unsigned char test_bits[] = {
+   0x00, 0x00, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x64, 0x26,
+   0x66, 0x66, 0x7f, 0xfe, 0x66, 0x66, 0x64, 0x26, 0x60, 0x06, 0x60, 0x06,
+   0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x00, 0x00};
Index: trunk/kitgen/8.x/blt/library/treeview_m.xbm
===================================================================
--- trunk/kitgen/8.x/blt/library/treeview_m.xbm	(revision 175)
+++ trunk/kitgen/8.x/blt/library/treeview_m.xbm	(revision 175)
@@ -0,0 +1,8 @@
+#define testm_width 16
+#define testm_height 16
+#define testm_x_hot 7
+#define testm_y_hot 7
+static unsigned char testm_bits[] = {
+   0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xfc, 0x3f, 0xfe, 0x7f,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x0f,
+   0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f};
Index: trunk/kitgen/8.x/blt/pareto.tcl
===================================================================
--- trunk/kitgen/8.x/blt/pareto.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/pareto.tcl	(revision 175)
@@ -0,0 +1,112 @@
+package require BLT
+# --------------------------------------------------------------------------
+# Starting with Tcl 8.x, the BLT commands are stored in their own
+# namespace called "blt". The idea is to prevent name clashes with
+# Tcl commands and variables from other packages, such as a "table"
+# command in two different packages.
+#
+# You can access the BLT commands in a couple of ways. You can prefix
+# all the BLT commands with the namespace qualifier "blt::"
+#
+# blt::graph .g
+# blt::table . .g -resize both
+#
+# or you can import all the command into the global namespace.
+#
+# namespace import blt::*
+# graph .g
+# table . .g -resize both
+#
+# --------------------------------------------------------------------------
+
+namespace import blt::*
+
+#
+# Example of a pareto chart.
+#
+barchart .b \
+-title "Defects Found During Inspection" \
+-font {Helvetica 12} \
+-plotpady { 12 4 }
+table . .b -fill both
+set data {
+"Spot Weld" 82 yellow
+"Lathe" 49 orange
+"Gear Cut" 38 green
+"Drill" 24 blue
+"Grind" 17 red
+"Lapping" 12 brown
+"Press" 8 purple
+"De-burr" 4 pink
+"Packaging" 3 cyan
+"Other" 12 magenta
+}
+
+# Create an X-Y graph line element to trace the accumulated defects.
+.b line create accum -label "" -symbol none -color red
+# Define a bitmap to be used to stipple the background of each bar.
+bitmap define pattern1 { {4 4} {01 02 04 08} }
+# For each process, create a bar element to display the magnitude.
+set count 0
+set sum 0
+set ydata 0
+set xdata 0
+foreach { label value color } $data {
+incr count
+.b element create $label -xdata $count -ydata $value \
+-fg $color -relief solid -bd 1 -stipple pattern1 -bg lightblue
+set labels($count) $label
+# Keep a running total of defects
+set sum [expr $value + $sum]
+lappend ydata $sum
+lappend xdata $count
+}
+
+# Configure the coordinates of the accumulated defects,
+# now that we know what they are.
+.b line configure accum -xdata $xdata -ydata $ydata
+# Add text markers to label the percentage of total at each point.
+foreach x $xdata y $ydata {
+set percent [expr ($y * 100.0) / $sum]
+if { $x == 0 } {
+set text "0%"
+} else {
+set text [format %.1f $percent]
+}
+
+.b marker create text \
+-coords "$x $y" \
+-text $text \
+-anchor c \
+-yoffset -5
+}
+
+# Display an auxillary y-axis for percentages.
+.b axis configure y2 \
+-hide no \
+-min 0.0 \
+-max 100.0 \
+-title "Percentage"
+# Title the y-axis
+.b axis configure y -title "Defects"
+# Configure the x-axis to display the process names, instead of numbers.
+.b axis configure x \
+-title "Process" \
+-command FormatLabels \
+-rotate 90 \
+-subdivisions 0
+
+proc FormatLabels { widget value } {
+global labels
+set value [expr round($value)]
+if {[info exists labels($value)] } {
+return $labels($value)
+}
+
+return $value
+}
+
+# No legend needed.
+.b legend configure -hide yes
+# Configure the grid lines.
+.b grid configure -mapx x -color lightblue
Index: trunk/kitgen/8.x/blt/pkgIndex.tcl.in
===================================================================
--- trunk/kitgen/8.x/blt/pkgIndex.tcl.in	(revision 175)
+++ trunk/kitgen/8.x/blt/pkgIndex.tcl.in	(revision 175)
@@ -0,0 +1,5 @@
+#
+# Tcl package index file
+#
+package ifneeded @PACKAGE_NAME@ @PACKAGE_VERSION@ \
+    [list load [file join $dir @PKG_LIB_FILE@] @PACKAGE_NAME@]
Index: trunk/kitgen/8.x/blt/realtime_stripchart.tcl
===================================================================
--- trunk/kitgen/8.x/blt/realtime_stripchart.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/realtime_stripchart.tcl	(revision 175)
@@ -0,0 +1,51 @@
+package require Tk
+package require BLT
+namespace import blt::*
+
+# vector and stripchart are blt components.
+# if you have a vector v, you can update it in realtime with
+# v set $list
+
+# init the vectors to a fixed size.
+
+set Hz 200
+
+vector xvec($Hz) y1vec($Hz) y2vec($Hz) sx1($Hz) sy1($Hz)
+
+# fill xvec with 0 .. $Hz-1
+
+xvec seq 0 [expr {$Hz - 1}]
+#xvec populate sx1 $Hz
+sx1 seq 0 [expr {$Hz - 1}]
+
+stripchart .s1 -height 2i -width 8i -bufferelements no
+stripchart .s2 -height 2i -width 8i -bufferelements no
+
+pack .s1 .s2
+
+.s1 element create line1 -xdata xvec -ydata y1vec -symbol none
+.s2 element create spline -x sx1 -y sy1 -symbol none -color red
+
+#.s2 element create line2 -xdata xvec -ydata y2vec -symbol none -color red
+
+# update $Hz values with random data once per second
+
+proc proc1sec {} {
+
+       # this can be done more concisely with vector random,
+       # but if you need to fill a vector from scalar calculations,
+       # do it this way:
+
+       for {set i 0} {$i < $::Hz} {incr i} {
+               lappend y1list [expr {rand()}]
+               lappend y2list [expr {rand()}]
+       }
+       y1vec set $y1list
+       y2vec set $y2list
+
+       spline natural xvec y1vec sx1 sy1
+
+       after 200 proc1sec
+}
+
+proc1sec
Index: trunk/kitgen/8.x/blt/scroll_canvas.tcl
===================================================================
--- trunk/kitgen/8.x/blt/scroll_canvas.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/scroll_canvas.tcl	(revision 175)
@@ -0,0 +1,27 @@
+# scroll_canvas.tcl --
+#     Experiment with scrolling canvas items selectively
+#
+
+canvas .c -width 400 -height 200
+scrollbar .y -command scrollc
+
+grid .c .y -sticky news
+
+.c create text 200 10 -text "Title"
+.c lower [.c create rectangle 0 0 400 30 -fill white -outline {} ]
+
+.c lower [.c create text 10 50 -text "Item" -anchor w -tag Move]
+.c lower [.c create polygon 80 50 140 50 140 350 -fill green -tag Move]
+
+# Set the parameters for the scrollbar
+.y set 0.0 0.5
+set currentPos 0.0
+
+proc scrollc {operation number {unit ""}} {
+    # Ignore scroll operation
+    if { $operation == "moveto" } {
+	set dely [expr {400*($::currentPos-$number)}]
+	set ::currentPos $number
+	.c move Move 0 $dely
+    }
+}
Index: trunk/kitgen/8.x/blt/tclconfig/ChangeLog
===================================================================
--- trunk/kitgen/8.x/blt/tclconfig/ChangeLog	(revision 175)
+++ trunk/kitgen/8.x/blt/tclconfig/ChangeLog	(revision 175)
@@ -0,0 +1,886 @@
+2010-11-23  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: add some cross-compile support, borrowed from Tcl 8.6
+
+2010-09-16  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct HP-UX LDFLAGS (only used when building big shell)
+
+2010-09-14  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: add extra if check for .manifest file generation
+	Add notice about package name and version being built.
+
+2010-09-09  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: [FREQ #3058486] TEA_LOAD_CONFIG doesn't set all BUILD_ vars
+	Slightly related: defining BUILD_$1 on all platforms - not only win -
+	allows the -fvisibility feature to be used in extensions as well, at
+	least if you compile against tcl >= 8.5.
+
+2010-08-26  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: ensure safe quoting for autoheader usage
+
+2010-08-19  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: add TEA_ADD_CLEANFILES macro to make adding cleanfiles
+	easier, and add *.exp to CLEANFILES Windows default.
+	(TEA_MAKE_LIB): Enhanced to check for MSVC that requires manifests
+	and auto-embed it into proj DLL via MAKE_SHARED_LIB.  Also define
+	VC_MANIFEST_EMBED_DLL and VC_MANIFEST_EMBED_EXE that do the same
+	magic in case it is needed for extended TEA projects.
+
+2010-08-16  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Bump to TEA_VERSION 3.9 ***
+	If upgrading from TEA_VERSION 3.8, copy over tcl.m4, change
+	TEA_INIT to use 3.9 and reconfigure (ac-2.59+).
+	BUILD_${PACKAGE_NAME} will be auto-defined on Windows for
+	correct setting of TCL_STORAGE_CLASS.
+	TEA_LOAD_CONFIG users should remove the SHLIB_LD_LIBS setting done
+	in configure.in (LIBS will be automagically populated by
+	TEA_LOAD_CONFIG).
+	TEA_EXPORT_CONFIG has been added for ${pkg}Config.sh creators
+	SHLIB_LD_FLAGS was deprecated a while ago, remove it if it is
+	still in your Makefile.in.
+
+	* tcl.m4: add /usr/lib64 to set of auto-search dirs. [Bug 1230554]
+	Auto-define BUILD_$PACKAGE_NAME so users don't need to.  This
+	needs to correspond with $pkg.h define magic for TCL_STORAGE_CLASS.
+	Auto-define CLEANFILES.  Users can expand it.
+	(SHLIB_LD_LIBS): define to '${LIBS}' default and change it only if
+	necessary.  Platforms not using this may simply not work or have
+	very funky linkers.
+	(TEA_LOAD_CONFIG): When loading config for another extension,
+	auto-add stub libraries found with TEA_ADD_LIBS.  Eases
+	configure.in for modules like itk and img::*.
+	(TEA_EXPORT_CONFIG): Add standardized function for exporting a
+	${pkg}Config.sh.  See use by img::* and itcl.
+
+2010-08-12  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Bump to TEA_VERSION 3.8 ***
+	If upgrading from TEA_VERSION 3.7, copy over tcl.m4, change
+	TEA_INIT to use 3.8 and reconfigure (ac-2.59+).
+	No other changes should be necessary.
+
+	* tcl.m4: remove more vestigial bits from removed platforms.
+	Add back SCO_SV-3.2*.
+	Remove use of DL_LIBS and DL_OBJS and related baggage - these are
+	only needed by the core to support 'load'.
+	Allow for macosx in TEA_ADD_SOURCES.
+	Correct check for found_xincludes=no in TEA_PATH_UNIX_X.
+
+2010-08-11  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: remove the following old platform configurations:
+	UNIX_SV*|UnixWare-5*, SunOS-4.*, SINIX*5.4*, SCO_SV-3.2*<readded>,
+	OSF1-1.*, NEXTSTEP-*, NetBSD-1.*|FreeBSD-[[1-2]].*, MP-RAS-*,
+	IRIX-5.*, HP-UX-*.08.*|HP-UX-*.09.*|HP-UX-*.10.*, dgux*,
+	BSD/OS-2.1*|BSD/OS-3*
+	(AIX): drop AIX-pre4 support and use of ldAix, use -bexpall/-brtl
+
+2010-07-05  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: [Patch #1055668] removal of exported internals from
+	tclInt.h (EXTERN macro)
+
+2010-04-14  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4    - Backport a lot of quoting fixes from tcl8.6/unix/tcl.m4
+	            - Fix determination of CYGPATH for CYGWIN
+	  With those fixes, itcl and tdbc compile fine with CYGWIN
+
+2010-04-06  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* install-sh         [Bug 2982540] configure and install* script files
+	                     should always have LF
+
+2010-02-19  Stuart Cassoff  <stwo@users.sourceforge.net>
+
+	* tcl.m4: Correct compiler/linker flags for threaded builds on
+	OpenBSD.
+
+2010-01-19  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: Detect CYGWIN variant: win32 or unix
+
+2010-01-03  Donal K. Fellows  <dkf@users.sf.net>
+
+	* unix/tcl.m4 (TEA_CONFIG_CFLAGS): [Tcl Bug 1636685]: Use the
+	configuration for modern FreeBSD suggested by the FreeBSD porter.
+
+2009-10-22  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: [Tcl Patch #2883533] tcl.m4 support for Haiku OS
+
+2009-04-27  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): harden the check to add _r to CC on
+	AIX with threads.
+
+2009-04-10  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): check for 64-bit TkAqua.
+
+2009-03-26  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tclconfig/tcl.m4: Adapt LDFLAGS and LD_SEARCH_FLAGS
+	together with SHLIB_LD definition to unbreak building on HPUX.
+
+2009-03-20  Andreas Kupries  <andreask@activestate.com>
+
+	* tclconfig/tcl.m4: Changed SHLIB_LD definition to unbreak
+	building on HPUX.
+
+2009-03-16  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4(TEA_PUBLIC_TK_HEADERS): Look at ${TK_INCLUDE_SPEC}
+	(found in tkConfig.sh) when trying to guess where tk.h might be
+	[Patch 1960628].
+
+2009-03-11  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4: Allow ${SHLIB_SUFFIX} to be overridden at
+	configure-time [Patch 1960628].  Also fix some comment typos,
+	and an uninitialized variable bug-waiting-to-happen.
+
+2008-12-21  Jan Nijtmans  <nijtmans@users.sf.net>
+
+	* tcl.m4: [Bug 2073255] Tcl_GetString(NULL) doesn't crash on HP-UX
+	          (this bug report was for Tcl, but holds for TEA as well.)
+
+2008-12-20  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: sync with tdbc tcl.m4 changes
+	(SunOS-5.11): Sun cc SHLIB_LD: use LDFLAGS_DEFAULT instead of LDFLAGS
+
+2008-12-02  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Bump to TEA_VERSION 3.7 ***
+
+	* tcl.m4: in private header check, check for <plat>Port.h instead
+	of Int.h to ensure all private headers are available.
+
+2008-11-04  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): sync TEA_PRIVATE_TK_HEADERS handling of
+	Tk.framework PrivateHeaders with TEA_PRIVATE_TCL_HEADERS.
+
+2008-11-04  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_PATH_TCLCONFIG, TEA_PATH_TKCONFIG): exit with error
+	when tclConfig.sh cannot be found. [Bug #1997760]
+	(TEA_PRIVATE_TCL_HEADERS, TEA_PRIVATE_TK_HEADERS): allow for
+	finding the headers installed in the public areas, e.g. a result of
+	make install-private-headers. [Bug #1631922]
+
+2008-08-12  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): link shlib with current and compatiblity version
+	flags; look for libX11.dylib when searching for X11 libraries.
+
+2008-06-12  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (SunOS-5.11): fix 64bit amd64 support with gcc & Sun cc.
+
+2008-03-27  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (SunOS-5.1x): fix 64bit support for Sun cc. [Bug 1921166]
+
+2008-02-01  Donal K. Fellows  <donal.k.fellows@man.ac.uk>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): Updated to work at least in part with
+	more modern VC versions. Currently just made the linker flags more
+	flexible; more work may be needed.
+
+2007-10-26  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): add support for 64-bit X11.
+
+2007-10-23  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Tagged tea-3-branch to start TEA 4 development on HEAD ***
+
+2007-09-17  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4: use '${CC} -shared' instead of 'ld -Bshareable'
+	to build shared libraries on current NetBSDs [Bug 1749251].
+
+2007-09-15  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: 	replace all direct references to compiler by ${CC} to
+			enable CC overriding at configure & make time.
+	(SunOS-5.1x):	replace direct use of '/usr/ccs/bin/ld' in SHLIB_LD by
+			'cc' compiler driver.
+
+2007-08-08  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: check Ttk dir for Tk private headers (8.5).
+	Add some comments to other bits.
+
+2007-06-25  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_PROG_TCLSH, TEA_PROG_WISH): move where / is added.
+
+2007-06-13  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: fix --with-tkinclude alignment. [Bug 1506111]
+
+2007-06-06  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): fix 64bit arch removal in fat 32&64bit builds.
+
+2007-05-18  Donal K. Fellows  <donal.k.fellows@man.ac.uk>
+
+	* tcl.m4: Added quoting so that paths with spaces cause fewer
+	problems.
+
+2007-03-07  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): s/CFLAGS/CPPFLAGS/ in -mmacosx-version-min check.
+
+2007-02-15  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct private header check to search in generic subdir
+
+2007-02-09  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Bump to TEA_VERSION 3.6 ***
+
+	* tcl.m4: correct -d to -f
+	(TEA_CONFIG_CFLAGS): SHLIB_SUFFIX is .so on HP ia64 [Bug 1615058]
+
+2007-02-08  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_PRIVATE_TCL_HEADERS, TEA_PRIVATE_TK_HEADERS): check
+	that the dirs actually have private headers. [Bug 1631922]
+
+2007-02-04  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: add caching to -pipe check.
+
+2007-01-25  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: integrate CPPFLAGS into CFLAGS as late as possible and
+	move (rather than duplicate) -isysroot flags from CFLAGS to CPPFLAGS to
+	avoid errors about multiple -isysroot flags from some older gcc builds.
+
+2006-01-19  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: ensure CPPFLAGS env var is used when set. [Bug 1586861]
+	(Darwin): add -isysroot and -mmacosx-version-min flags to CPPFLAGS when
+	present in CFLAGS to avoid discrepancies between what headers configure
+	sees during preprocessing tests and compiling tests.
+
+2006-12-19  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): --enable-64bit: verify linking with 64bit -arch flag
+	succeeds before enabling 64bit build.
+
+2006-12-16  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Linux): fix previous change to use makefile variable
+	LDFLAGS_DEFAULT instead of LDFLAGS in SHLIB_LD, to ensure linker
+	flags in sampleextension Makefile are picked up.
+
+2006-11-26  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Linux): --enable-64bit support. [Patch 1597389], [Bug 1230558]
+
+2006-08-18  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): add support for --enable-64bit on x86_64, for
+	universal builds including x86_64 and for use of -mmacosx-version-min
+	instead of MACOSX_DEPLOYMENT_TARGET. For Tk extensions, remove 64-bit
+	arch flags from CFLAGS like in the Tk configure, as neither TkAqua nor
+	TkX11 can be built for 64-bit at present.
+
+2006-03-28  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: []-quote AC_DEFUN functions.
+	(TEA_PATH_TKCONFIG): Fixed Windows-specific check for tkConfig.sh.
+	(TEA_MAKE_LIB): Prepend 'lib' for Windows-gcc configs.
+
+2006-03-07  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4: Set SHLIB_LD_FLAGS='${LIBS}' on NetBSD,
+	as per the other *BSD variants [Bug 1334613].
+
+2006-01-25  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	*** Bump to TEA version 3.5 ***
+
+	* tcl.m4: keep LD_SEARCH_FLAGS and CC_SEARCH_FLAGS synchronous
+	with core tcl.m4 meaning.
+
+2006-01-24  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): use makefile variable LDFLAGS_DEFAULT instead of
+	LDFLAGS in SHLIB_LD, to ensure linker flags in sampleextension Makefile
+	are picked up. [Bug 1403343]
+
+2006-01-23  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: add C:/Tcl/lib and C:/Progra~1/Tcl/lib dirs to check for
+	*Config.sh on Windows. [Bug 1407544]
+
+2006-01-23  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): for Tk extensions, remove -arch ppc64 from CFLAGS
+	like in the Tk configure, as neither TkAqua nor TkX11 can be built for
+	64bit at present (no 64bit GUI libraries).
+
+2006-01-22  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: restore system=windows on Windows.
+	Remove error if 'ar' isn't found (it may not be on Windows).
+	Do not add -lxnet or define _XOPEN_SOURCE on HP-UX by default.
+	Ensure the C|LDFLAGS_DEFAULT gets the fully sub'd value at
+	configure time.
+
+2006-01-10  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: add caching, use AC_CACHE_CHECK instead of AC_CACHE_VAL
+	where possible, consistent message quoting, sync relevant
+	tcl/unix/tcl.m4 HEAD changes and gratuitous formatting differences
+	(notably sunc removal of support for for ancient BSD's, IRIX 4,
+	RISCos and Ultrix by kennykb), Darwin improvements to
+	TEA_LOAD_*CONFIG to make linking work against Tcl/Tk frameworks
+	installed in arbitrary location, change TEA_PROG_* search order
+	(look in *_BIN_DIR parents before *_PREFIX).
+
+2006-01-05  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: add dkf's system config refactor
+
+2006-01-04  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: remove extraneous ' that causes bash 3.1 to choke
+
+2005-12-19  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4 (TEA_PATH_TCLCONFIG &c): Look for tclConfig.sh &c
+	in ${libdir}, where they are installed by default [Patch #1377407].
+
+2005-12-05  Don Porter  <dgp@users.sf.net>
+
+	* tcl.m4 (TEA_PUBLIC_*_HEADERS):	Better support for finding
+	header files for uninstalled Tcl and Tk.
+
+2005-12-02  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correctly bump TEA_VERSION var to 3.4
+
+2005-12-01  Daniel Steffen  <das@users.sourceforge.net>
+
+	* unix/tcl.m4 (Darwin): fixed error when MACOSX_DEPLOYMENT_TARGET unset
+
+2005-11-29  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4:  *** Bump to TEA version 3.4 ***
+	Add Windows x64 build support.
+	Remove TEA_PATH_NOSPACE and handle the problem with ""s where
+	necessary - the macro relied on TCLSH_PROG which didn't work for
+	cross-compiles.
+
+2005-11-27  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): add 64bit support, add CFLAGS to SHLIB_LD to
+	support passing -isysroot in env(CFLAGS) to configure (flag can't
+	be present twice, so can't be in both CFLAGS and LDFLAGS during
+	configure), don't use -prebind when deploying on 10.4.
+	(TEA_ENABLE_LANGINFO, TEA_TIME_HANDLER): add/fix caching.
+
+2005-10-30  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: fixed two tests for TEA_WINDOWINGSYSTEM = "aqua" that
+	should have been for `uname -s` = "Darwin" instead; added some
+	missing quoting.
+	(TEA_PROG_TCLSH, TEA_PROG_WISH): fix incorrect assumption that
+	install location of tclConfig.sh/tkConfig.sh allows to determine
+	the tclsh/wish install dir via ../bin. Indeed tcl/tk can be
+	configured with arbitrary --libdir and --bindir (independent of
+	prefix) and such a configuration is in fact standard with Darwin
+	framework builds. At least now also check ${TCL_PREFIX}/bin
+	resp. ${TK_PREFIX}/bin for presence of tclsh resp. wish (if tcl/tk
+	have been configured with arbitrary --bindir, this will still not
+	find them, for a general solution *Config.sh would need to contain
+	the values of bindir/libdir/includedir passed to configure).
+
+2005-10-07  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: Fix Solaris 5.10 check and Solaris AMD64 64-bit builds.
+
+2005-10-04  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_PRIVATE_TCL_HEADERS): add / to finish sed macro
+	(TEA_ENABLE_THREADS): don't check for pthread_attr_setstacksize func
+
+2005-09-13  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: *** Update to TEA version 3.3 ***
+	define TEA_WINDOWINGSYSTEM in TEA_LOAD_TKCONFIG.
+	Make --enable-threads the default (users can --disable-threads).
+	Improve AIX ${CC}_r fix to better check existing ${CC} value.
+	Do the appropriate evals to not require the *TOP_DIR_NATIVE vars
+	be set for extensions that use private headers.
+	Make aqua check for Xlib compat headers the same as win32.
+
+2005-07-26  Mo DeJong  <mdejong@users.sourceforge.net>
+
+	* tcl.m4 (TEA_PROG_TCLSH, TEA_BUILD_TCLSH,
+	TEA_PROG_WISH, TEA_BUILD_WISH): Remove
+	TEA_BUILD_TCLSH and TEA_BUILD_WISH because
+	of complaints that it broke the build when
+	only an installed version of Tcl was available
+	at extension build time. The TEA_PROG_TCLSH and
+	TEA_PROG_WISH macros will no longer search the
+	path at all. The build tclsh or installed
+	tclsh shell will now be found by TEA_PROG_TCLSH.
+
+2005-07-24  Mo DeJong  <mdejong@users.sourceforge.net>
+
+	* tcl.m4 (TEA_PROG_TCLSH, TEA_BUILD_TCLSH,
+	TEA_PROG_WISH, TEA_BUILD_WISH):
+	Split confused search for tclsh on PATH and
+	build and install locations into two macros.
+	TEA_PROG_TCLSH and TEA_PROG_WISH search the
+	system PATH for an installed tclsh or wish.
+	The TEA_BUILD_TCLSH and TEA_BUILD_WISH
+	macros determine the name of tclsh or
+	wish in the Tcl or Tk build directory even
+	if tclsh or wish has not yet been built.
+	[Tcl bug 1160114]
+	[Tcl patch 1244153]
+
+2005-06-23  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (TEA_PRIVATE_TK_HEADERS): add ${TK_SRC_DIR}/macosx to
+	TK_INCLUDES when building against TkAqua.
+
+	* tcl.m4 (TEA_PATH_X): fixed missing comma in AC_DEFINE
+
+	* tcl.m4: changes to better support framework builds of Tcl and Tk out
+	of the box: search framework install locations for *Config.sh, and if in
+	presence of a framework build, use the framework's Headers and
+	PrivateHeaders directories for public and private includes. [FR 947735]
+
+2005-06-18  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): add -headerpad_max_install_names to LDFLAGS to
+	ensure we can always relocate binaries with install_name_tool.
+
+2005-06-04  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (TEA_PATH_X): for TEA_WINDOWINGSYSTEM == aqua, check if xlib
+	compat headers are available in tkheaders location, otherwise add xlib
+	sourcedir to TK_XINCLUDES.
+
+2005-04-25  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4: added AC_DEFINE* descriptions (from core tcl.m4) to allow
+	use with autoheader.
+	(Darwin): added configure checks for recently added linker flags
+	-single_module and -search_paths_first to allow building with older
+	tools (and on Mac OS X 10.1), use -single_module in SHLIB_LD.
+	(TEA_MISSING_POSIX_HEADERS): added caching of dirent.h check.
+	(TEA_BUGGY_STRTOD): added caching (sync with core tcl.m4).
+
+2005-03-24  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_TCL_64BIT_FLAGS): use Tcl header defaults for wide
+	int type only on Windows when __int64 is detected as valid.
+
+2005-03-24  Don Porter  <dgp@users.sf.net>
+
+	* README.txt:	Update reference to "SC_* macros" to "TEA_* macros".
+	* tcl.m4:	Incorporated recent improvements in SC_PATH_TCLCONFIG
+	and SC_PATH_TKCONFIG into TEA_PATH_TCLCONFIG and TEA_PATH_TKCONFIG.
+	Corrected search path in TEA_PATH_CONFIG and added
+	AC_SUBST($1_BIN_DIR) to TEA_LOAD_CONFIG so that packages that load
+	the configuration of another package can know where they loaded
+	it from.
+
+2005-03-18  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): correct 2005-03-17 change to have
+	variant LD_SEARCH_FLAGS for gcc and cc builds.
+
+	* tcl.m4 (TEA_PROG_TCLSH, TEA_PROG_WISH): correct x-compile check.
+
+2005-03-17  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: Correct gcc build and HP-UX-11.
+
+2005-02-08  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_ADD_LIBS): don't touch lib args starting with -.
+	(TEA_CONFIG_CFLAGS): only define _DLL for CE in shared build.
+	(TEA_MAKE_LIB): set RANLIB* to : on Windows (it's not needed).
+
+2005-02-01  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: redo of 2005-01-27 changes to correctly handle paths
+	with spaces.  Win/CE and Win/64 builds now require a prebuilt
+	tclsh to handle conversion to short pathnames.  This is done in
+	the new TEA_PATH_NOSPACE macro.  For Win/CE|64, make CC just the
+	compiler and move the necessary includes to CFLAGS.
+	(TEA_CONFIG_CFLAGS): Add Solaris 64-bit gcc build support.
+	(TEA_PROG_TCLSH, TEA_PROG_WISH): Allow TCLSH_PROG and WISH_PROG to
+	be set in the env and prevent resetting.
+	(TEA_ADD_LIBS): On Windows using GCC (mingw), convert foo.lib
+	args to -lfoo, for use with mingw.
+		*** POTENTIAL INCOMPATABILITY ***
+	(TEA_CONFIG_CFLAGS): Fix AIX gcc builds to work out-of-box.
+	Bumped TEA to 3.2.
+
+2005-01-27  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: remove cygpath calls to support msys.
+	Update base CE build assumption to "420,ARMV4,ARM,Pocket PC 2003".
+	Make STLIB_LD use $LINKBIN -lib.
+
+2005-01-25  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (Darwin): fixed bug with static build linking to dynamic
+	library in /usr/lib etc instead of linking to static library earlier
+	in search path. [Tcl Bug 956908]
+	Removed obsolete references to Rhapsody.
+
+2004-12-29  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: Updates for VC7 compatibility, fixing CFLAGS and LDFLAGS
+	options, using better default -O levels. [Bug 1092952, 1091967]
+
+2004-12-29  Joe English  <jenglish@users.sourceforge.net>
+
+	* tcl.m4: Do not use ${DBGX} suffix when building
+	shared libraries [patch #1081595, TIP #34]
+
+2004-09-07  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): support eVC4 Win/CE builds
+
+2004-08-10  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_INIT, TEA_PREFIX): update handling of exec_prefix to
+	work around subdir configures since autoconf only propagates the
+	prefix (not exec_prefix).
+
+2004-07-23  Daniel Steffen  <das@users.sourceforge.net>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): Darwin section: brought inline with
+	Tcl 8.5 HEAD config, removed core specific & obsolete settings.
+
+2004-07-22  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_PATH_X): check in TK_DEFS for MAC_OSX_TK to see if
+	we are compiling on Aqua.  Add TEA_WINDOWINGSYSTEM var that
+	reflects 'tk windowingsystem' value.
+
+2004-07-16  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_ENABLE_THREADS): force a threaded build when
+	building against a threaded core.
+	(CFLAGS_WARNING): Remove -Wconversion for gcc builds
+	(TEA_CONFIG_CFLAGS): Reorder configure.in for better 64-bit build
+	configuration, replacing EXTRA_CFLAGS with CFLAGS.  [Bug #874058]
+	Update to latest Tcl 8.5 head config settings.
+	Call this TEA version 3.1.
+
+2004-04-29  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_TCL_64BIT_FLAGS): replace AC_TRY_RUN test with
+	AC_TRY_COMPILE for the long vs. long long check. (kenny)
+
+2004-04-26  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_TCL_64BIT_FLAGS): update against core tcl.m4 to
+	define TCL_WIDE_INT_IS_LONG if 'using long'.
+
+2004-03-19  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct Windows builds getting LDFLAGS info in MAKE_LIB
+
+2004-02-11  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct TCL_INCLUDES for private headers on Windows - it
+	doesn't need the eval.
+
+2004-02-10  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: don't require TK_INCLUDES and TCL_INCLUDES to have the
+	DIR_NATIVE vars defined when using private headers on unix.
+	Allow $... to TEA_ADD_SOURCES for constructs like
+	TEA_ADD_SOURCES([\$(WIN_OBJECTS)]), that allow the developer to
+	place more in the Makefile.in.
+	tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
+	CHECK on limits.h
+
+2003-12-10  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* Makefile.in:      added TEA_ADD_LIBS, TEA_ADD_INCLUDES and
+	* configure:        TEA_ADD_CFLAGS to configurable parameters with
+	* configure.in:     PKG_* equivs in the Makefile.  This allows the
+	* tclconfig/tcl.m4: user to worry less about actual magic VAR names.
+	Corrected Makefile.in to note that TEA_ADD_TCL_SOURCES requires
+	exact file names.
+
+2003-12-09  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: updated OpenBSD support based on [Patch #775246] (cassoff)
+
+2003-12-05  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* configure:
+	* configure.in:
+	* Makefile.in (VPATH): readd $(srcdir) to front of VPATH as the
+	first part of VPATH can get chopped off.
+	Change .c.$(OBJEXT) rule to .c.@OBJEXT@ to support more makes.
+	* tclconfig/tcl.m4: add TEA_ADD_STUB_SOURCES to support libstub
+	generation and TEA_ADD_TCL_SOURCES to replace RUNTIME_SOURCES as
+	the way the user specifies library files.
+
+2003-12-03  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* configure:           Update of TEA spec to (hopefully) simplify
+	* configure.in:        some aspects of TEA by making use of more
+	* Makefile.in:         AC 2.5x features.  Use PACKAGE_NAME (instead
+	* generic/tclsample.c: of PACKAGE) and PACKAGE_VERSION (instead of
+	* tclconfig/tcl.m4:    VERSION) arguments to AC_INIT as the TEA
+	package name and version.
+	Provide a version argument to TEA_INIT - starting with 3.0.
+	Drop all use of interior shell substs that older makefiles didn't
+	like.  Use PKG_* naming convention instead.
+	Move specification of source files and public headers into
+	configure.in with TEA_ADD_SOURCES and TEA_ADD_HEADERS.  These will
+	be munged during ./configure into the right obj file names (no
+	$(SOURCES:.c=.obj) needed).
+	There is almost nothing that should be touched in Makefile.in now
+	for the developer.  May want to add a TEA_ADD_TCL_SOURCES for the
+	RUNTIME_SOURCES that remains.
+	Use SHLID_LD_FLAGS (instead of SHLID_LDFLAGS) as Tcl does.
+	Only specify the user requested LDFLAGS/CFLAGS in the Makefile,
+	don't mention the _OPTIMIZE/_DEBUG variants.
+
+2003-10-15  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: create a TEA_SETUP_COMPILER_CC the precedes the
+	TEA_SETUP_COMPILER macro.  They are split so the check for CC
+	occurs before any use of CC.  Also add AC_PROG_CPP to the compiler
+	checks.
+
+2003-10-06  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: Updated for autoconf 2.5x prereq.
+	Where TCL_WIDE_INT_TYPE would be __int64, defer to the code checks
+	in tcl.h, which also handles TCL_LL_MODIFIER* properly.
+
+2003-04-22  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct default setting of ARCH for WinCE builds.
+	Correct \ escaping for CE sed macros.
+
+2003-04-10  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: replace $(syscal) construct with older `syscall` for
+	systems where sh != bash.
+
+2003-04-09  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_WITH_CELIB): add --enable-wince and --with-celib
+	options for Windows/CE compilation support.  Requires the
+	Microsoft eMbedded SDK and Keuchel's celib emulation layer.
+
+2003-02-18  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_ENABLE_THREADS): Make sure -lpthread gets passed on
+	the link line when checking for the pthread_attr_setstacksize
+	symbol. (dejong)
+
+	* tcl.m4 (TEA_SETUP_COMPILER): added default calls to
+	TEA_TCL_EARLY_FLAGS, TEA_TCL_64BIT_FLAGS,
+	TEA_MISSING_POSIX_HEADERS and TEA_BUGGY_STRTOD.
+
+2003-02-14  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: correct HP-UX ia64 --enable-64bit build flags
+
+2003-01-29  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: check $prefix/lib as well as $exec_prefix/lib when
+	looking for tcl|tkConfig.sh, as this check is done before we would
+	set exec_prefix when the user does not define it.
+
+2003-01-21  Mo DeJong  <mdejong@users.sourceforge.net>
+
+	* tcl.m4 (TEA_CONFIG_CFLAGS): Fix build support
+	for mingw, the previous implementation would
+	use VC++ when compiling with mingw gcc. Don't
+	pass -fPIC since gcc always compiles pic code
+	under win32. Change some hard coded cases
+	of gcc to ${CC}.
+
+2002-10-15  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: move the CFLAGS definition from TEA_ENABLE_SHARED to
+	TEA_MAKE_LIB because setting too early confuses other AC_* macros.
+	Correct the HP-11 SHLIB_LD_LIBS setting.
+
+	* tcl.m4: add the CFLAGS definition into TEA_ENABLE_SHARED and
+	make it pick up the env CFLAGS at configure time.
+
+2002-10-09  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: add --enable-symbols=mem option to enable TCL_MEM_DEBUG.
+	Improved AIX 64-bit build support, allow it on AIX-4 as well.
+	Enable 64-bit HP-11 compilation with gcc.
+	Enable 64-bit IRIX64-6 cc build support.
+	Correct FreeBSD thread library linkage.
+	Add OSF1 static build support.
+	Improve SunOS-5 shared build SHLIB_LD macro.
+
+2002-07-20  Zoran Vasiljevic  <zoran@archiware.com>
+
+	* tcl.m4: Added MINGW32 to list of systems checked for Windows build.
+	Also, fixes some indentation issues with "--with-XXX" options.
+
+2002-04-23  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_ENABLE_THREADS): added USE_THREAD_ALLOC define to
+	use new threaded allocatory by default on Unix for Tcl 8.4.
+	(TEA_CONFIG_CFLAGS): corrected LD_SEARCH_FLAGS for FreeBSD-3+.
+
+2002-04-22  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4 (TEA_SETUP_COMPILER): removed call to AC_CYGWIN so that
+	we can use autoconf 2.5x as well as 2.13.  This prevents us from
+	being able to warn against the use of cygwin gcc at configure
+	time, but allows autoconf 2.5x, which is what is shipped with most
+	newer systems.
+
+2002-04-11  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: Enabled COFF as well as CV style debug info with
+	--enable-symbols to allow Dr. Watson users to see function info.
+	More info on debugging levels can be obtained at:
+	http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp
+
+2002-04-03  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: change all SC_* macros to TEA_*.  The SC_ was for
+	Scriptics, which is no more.  TEA represents a better, independent
+	prefix that won't need changing.
+	Added preliminary mingw gcc support. [Patch #538772]
+	Added TEA_PREFIX macro that handles defaulting the prefix and
+	exec_prefix vars to those used by Tcl if none were specified.
+	Added TEA_SETUP_COMPILER macro that encompasses the AC_PROG_CC
+	check and several other basic AC_PROG checks needed for making
+	executables.  This greatly simplifies user's configure.in files.
+	Collapsed AIX-5 defines into AIX-* with extra checks for doing the
+	ELF stuff on AIX-5-ia64.
+	Updated TEA_ENABLE_THREADS to take an optional arg to allow
+	switching it on by default (for Thread) and add sanity checking to
+	warn the user if configuring threads incompatibly.
+
+2002-03-29  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: made sure that SHLIB_LDFLAGS was set to LDFLAGS_DEFAULT.
+	Removed --enable-64bit support for AIX-4 because it wasn't correct.
+	Added -MT or -MD Windows linker switches to properly support
+	symbols-enabled builds.
+
+2002-03-28  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: called AC_MSG_ERROR when SC_TEA_INIT wasn't called first
+	instead of calling it as that inlines it each time in shell code.
+	Changed Windows CFLAGS_OPTIMIZE to use -O2 instead of -Oti.
+	Noted TCL_LIB_VERSIONS_OK=nodots for Windows builds.
+	A few changes to support itcl (and perhaps others):
+	Added support for making your own stub libraries to SC_MAKE_LIB.
+	New SC_PATH_CONFIG and SC_LOAD_CONFIG that take a package name arg
+	and find that ${pkg}Config.sh file.  itk uses this for itcl.
+
+2002-03-27  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: made SC_LOAD_TKCONFIG recognize when working with a Tk
+	build dir setup.
+	Added EXTRA_CFLAGS and SHLIB_LD_LIBS substs to SC_CONFIG_CFLAGS.
+	Added XLIBSW onto LIBS when it is defined.
+	Remove TCL_LIBS from MAKE_LIB and correctly use SHLIB_LD_LIBS
+	instead to not rely as much on tclConfig.sh cached info.
+	Add TK_BIN_DIR to paths to find wish in SC_PROG_WISH.
+	These move towards making TEA much more independent of *Config.sh.
+
+2002-03-19  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: corrected forgotten (UN)SHARED_LIB_SUFFIX and
+	SHLIB_SUFFIX defines for Win.
+	(SC_PATH_X): made this only do the check on unix platforms.
+
+2002-03-12  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* README.txt: updated to reflect fewer files
+
+2002-03-06  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* config.guess (removed):
+	* config.sub (removed): removed unnecessary files
+
+	* installFile.tcl (removed):
+	* mkinstalldirs (removed): these aren't really necessary for
+	making TEA work
+
+	* tcl.m4 (SC_PUBLIC_TCL_HEADERS, SC_PUBLIC_TK_HEADERS): don't
+	check /usr(/local)/include for includes on Windows when not using
+	gcc
+
+2002-03-05  Jeff Hobbs  <jeffh@ActiveState.com>
+
+	* tcl.m4: added warnings on Windows, removed RELPATH define and
+	added TCL_LIBS to MAKE_LIB macro.
+
+	This import represents 2.0.0, or a new start at attempting to
+	make TEA much easier for C extension developers.
+
+	**** moved from tclpro project to core tcl project, ****
+	**** renamed to 'tclconfig'                         ****
+
+2001-03-15    Karl Lehenbauer <karl@procplace.com>
+
+	* installFile.tcl: Added updating of the modification time of
+	  the target file whether we overwrote it or decided that it
+	  hadn't changed.  This was necessary for us to be able to
+	  determine whether or not a module install touched the file.
+
+2001-03-08    Karl Lehenbauer <karl@procplace.com>
+
+	* installFile.tcl: Added support for converting new-style (1.1+)
+	  Cygnus drive paths to Tcl-style.
+
+2001-01-15    <brent.welch@interwoven.com>
+
+	* tcl.m4: Added FreeBSD clause.
+
+2001-01-03    <brent.welch@interwoven.com>
+
+	* tcl.m4: Fixed typo in SC_LIB_SPEC where it is checking
+	for exec-prefix.
+
+2000-12-01    <brent.welch@interwoven.com>
+
+	* tcl.m4: Concatenated most of the Ajuba acsite.m4 file
+	so we don't need to modify the autoconf installation.
+	* config.guess:
+	* config.sub:
+	* installFile.tcl:
+	Added files from the itcl config subdirectory,
+	which should go away.
+
+2000-7-29    <welch@ajubasolutions.com>
+
+	* Fixed the use of TCL_SRC_DIR and TK_SRC_DIR within
+	TCL_PRIVATE_INCLUDES and TK_PRIVATE_INCLUDES to match their recent
+	change from $(srcdir) to $(srcdir)/..
Index: trunk/kitgen/8.x/blt/tclconfig/README.txt
===================================================================
--- trunk/kitgen/8.x/blt/tclconfig/README.txt	(revision 175)
+++ trunk/kitgen/8.x/blt/tclconfig/README.txt	(revision 175)
@@ -0,0 +1,26 @@
+These files comprise the basic building blocks for a Tcl Extension
+Architecture (TEA) extension.  For more information on TEA see:
+
+	http://www.tcl.tk/doc/tea/
+
+This package is part of the Tcl project at SourceForge, and latest
+sources should be available there:
+
+	http://tcl.sourceforge.net/
+
+This package is a freely available open source package.  You can do
+virtually anything you like with it, such as modifying it, redistributing
+it, and selling it either in whole or in part.
+
+CONTENTS
+========
+The following is a short description of the files you will find in
+the sample extension.
+
+README.txt	This file
+
+install-sh	Program used for copying binaries and script files
+		to their install locations.
+
+tcl.m4		Collection of Tcl autoconf macros.  Included by a package's
+		aclocal.m4 to define TEA_* macros.
Index: trunk/kitgen/8.x/blt/tclconfig/install-sh
===================================================================
--- trunk/kitgen/8.x/blt/tclconfig/install-sh	(revision 175)
+++ trunk/kitgen/8.x/blt/tclconfig/install-sh	(revision 175)
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:  no input file specified"
+	exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+	echo "install:  no destination specified"
+	exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+	dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
Index: trunk/kitgen/8.x/blt/tclconfig/tcl.m4
===================================================================
--- trunk/kitgen/8.x/blt/tclconfig/tcl.m4	(revision 175)
+++ trunk/kitgen/8.x/blt/tclconfig/tcl.m4	(revision 175)
@@ -0,0 +1,4040 @@
+# tcl.m4 --
+#
+#	This file provides a set of autoconf macros to help TEA-enable
+#	a Tcl extension.
+#
+# Copyright (c) 1999-2000 Ajuba Solutions.
+# Copyright (c) 2002-2005 ActiveState Corporation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: tcl.m4,v 1.153 2010/11/23 09:46:00 nijtmans Exp $
+
+AC_PREREQ(2.57)
+
+dnl TEA extensions pass us the version of TEA they think they
+dnl are compatible with (must be set in TEA_INIT below)
+dnl TEA_VERSION="3.9"
+
+# Possible values for key variables defined:
+#
+# TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem')
+# TEA_PLATFORM        - windows unix
+#
+
+#------------------------------------------------------------------------
+# TEA_PATH_TCLCONFIG --
+#
+#	Locate the tclConfig.sh file and perform a sanity check on
+#	the Tcl compile flags
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--with-tcl=...
+#
+#	Defines the following vars:
+#		TCL_BIN_DIR	Full path to the directory containing
+#				the tclConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TCLCONFIG], [
+    dnl TEA specific: Make sure we are initialized
+    AC_REQUIRE([TEA_INIT])
+    #
+    # Ok, lets find the tcl configuration
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-tcl
+    #
+
+    if test x"${no_tcl}" = x ; then
+	# we reset no_tcl in case something fails here
+	no_tcl=true
+	AC_ARG_WITH(tcl,
+	    AC_HELP_STRING([--with-tcl],
+		[directory containing tcl configuration (tclConfig.sh)]),
+	    with_tclconfig="${withval}")
+	AC_MSG_CHECKING([for Tcl configuration])
+	AC_CACHE_VAL(ac_cv_c_tclconfig,[
+
+	    # First check to see if --with-tcl was specified.
+	    if test x"${with_tclconfig}" != x ; then
+		case "${with_tclconfig}" in
+		    */tclConfig.sh )
+			if test -f "${with_tclconfig}"; then
+			    AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself])
+			    with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`"
+			fi ;;
+		esac
+		if test -f "${with_tclconfig}/tclConfig.sh" ; then
+		    ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`"
+		else
+		    AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
+		fi
+	    fi
+
+	    # then check for a private Tcl installation
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in \
+			../tcl \
+			`ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+			../../tcl \
+			`ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+			../../../tcl \
+			`ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # on Darwin, check in Framework installation locations
+	    if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+			`ls -d /Library/Frameworks 2>/dev/null` \
+			`ls -d /Network/Library/Frameworks 2>/dev/null` \
+			`ls -d /System/Library/Frameworks 2>/dev/null` \
+			; do
+		    if test -f "$i/Tcl.framework/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # TEA specific: on Windows, check in common installation locations
+	    if test "${TEA_PLATFORM}" = "windows" \
+		-a x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+			`ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+			; do
+		    if test -f "$i/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few common install locations
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in `ls -d ${libdir} 2>/dev/null` \
+			`ls -d ${exec_prefix}/lib 2>/dev/null` \
+			`ls -d ${prefix}/lib 2>/dev/null` \
+			`ls -d /usr/local/lib 2>/dev/null` \
+			`ls -d /usr/contrib/lib 2>/dev/null` \
+			`ls -d /usr/lib 2>/dev/null` \
+			`ls -d /usr/lib64 2>/dev/null` \
+			; do
+		    if test -f "$i/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few other private locations
+	    if test x"${ac_cv_c_tclconfig}" = x ; then
+		for i in \
+			${srcdir}/../tcl \
+			`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tclConfig.sh" ; then
+			ac_cv_c_tclconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+	])
+
+	if test x"${ac_cv_c_tclconfig}" = x ; then
+	    TCL_BIN_DIR="# no Tcl configs found"
+	    AC_MSG_ERROR([Can't find Tcl configuration definitions])
+	else
+	    no_tcl=
+	    TCL_BIN_DIR="${ac_cv_c_tclconfig}"
+	    AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh])
+	fi
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_TKCONFIG --
+#
+#	Locate the tkConfig.sh file
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--with-tk=...
+#
+#	Defines the following vars:
+#		TK_BIN_DIR	Full path to the directory containing
+#				the tkConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TKCONFIG], [
+    #
+    # Ok, lets find the tk configuration
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-tk
+    #
+
+    if test x"${no_tk}" = x ; then
+	# we reset no_tk in case something fails here
+	no_tk=true
+	AC_ARG_WITH(tk,
+	    AC_HELP_STRING([--with-tk],
+		[directory containing tk configuration (tkConfig.sh)]),
+	    with_tkconfig="${withval}")
+	AC_MSG_CHECKING([for Tk configuration])
+	AC_CACHE_VAL(ac_cv_c_tkconfig,[
+
+	    # First check to see if --with-tkconfig was specified.
+	    if test x"${with_tkconfig}" != x ; then
+		case "${with_tkconfig}" in
+		    */tkConfig.sh )
+			if test -f "${with_tkconfig}"; then
+			    AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself])
+			    with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`"
+			fi ;;
+		esac
+		if test -f "${with_tkconfig}/tkConfig.sh" ; then
+		    ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`"
+		else
+		    AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh])
+		fi
+	    fi
+
+	    # then check for a private Tk library
+	    if test x"${ac_cv_c_tkconfig}" = x ; then
+		for i in \
+			../tk \
+			`ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+			../../tk \
+			`ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+			../../../tk \
+			`ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # on Darwin, check in Framework installation locations
+	    if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tkconfig}" = x ; then
+		for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+			`ls -d /Library/Frameworks 2>/dev/null` \
+			`ls -d /Network/Library/Frameworks 2>/dev/null` \
+			`ls -d /System/Library/Frameworks 2>/dev/null` \
+			; do
+		    if test -f "$i/Tk.framework/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few common install locations
+	    if test x"${ac_cv_c_tkconfig}" = x ; then
+		for i in `ls -d ${libdir} 2>/dev/null` \
+			`ls -d ${exec_prefix}/lib 2>/dev/null` \
+			`ls -d ${prefix}/lib 2>/dev/null` \
+			`ls -d /usr/local/lib 2>/dev/null` \
+			`ls -d /usr/contrib/lib 2>/dev/null` \
+			`ls -d /usr/lib 2>/dev/null` \
+			`ls -d /usr/lib64 2>/dev/null` \
+			; do
+		    if test -f "$i/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # TEA specific: on Windows, check in common installation locations
+	    if test "${TEA_PLATFORM}" = "windows" \
+		-a x"${ac_cv_c_tkconfig}" = x ; then
+		for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+			`ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+			; do
+		    if test -f "$i/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i; pwd)`"
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few other private locations
+	    if test x"${ac_cv_c_tkconfig}" = x ; then
+		for i in \
+			${srcdir}/../tk \
+			`ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+			`ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+		    if test "${TEA_PLATFORM}" = "windows" \
+			    -a -f "$i/win/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+			break
+		    fi
+		    if test -f "$i/unix/tkConfig.sh" ; then
+			ac_cv_c_tkconfig="`(cd $i/unix; pwd)`"
+			break
+		    fi
+		done
+	    fi
+	])
+
+	if test x"${ac_cv_c_tkconfig}" = x ; then
+	    TK_BIN_DIR="# no Tk configs found"
+	    AC_MSG_ERROR([Can't find Tk configuration definitions])
+	else
+	    no_tk=
+	    TK_BIN_DIR="${ac_cv_c_tkconfig}"
+	    AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh])
+	fi
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TCLCONFIG --
+#
+#	Load the tclConfig.sh file
+#
+# Arguments:
+#
+#	Requires the following vars to be set:
+#		TCL_BIN_DIR
+#
+# Results:
+#
+#	Subst the following vars:
+#		TCL_BIN_DIR
+#		TCL_SRC_DIR
+#		TCL_LIB_FILE
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TCLCONFIG], [
+    AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh])
+
+    if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then
+        AC_MSG_RESULT([loading])
+	. "${TCL_BIN_DIR}/tclConfig.sh"
+    else
+        AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh])
+    fi
+
+    # eval is required to do the TCL_DBGX substitution
+    eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+    eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+
+    # If the TCL_BIN_DIR is the build directory (not the install directory),
+    # then set the common variable name to the value of the build variables.
+    # For example, the variable TCL_LIB_SPEC will be set to the value
+    # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
+    # instead of TCL_BUILD_LIB_SPEC since it will work with both an
+    # installed and uninstalled version of Tcl.
+    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+        TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}"
+        TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}"
+        TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}"
+    elif test "`uname -s`" = "Darwin"; then
+	# If Tcl was built as a framework, attempt to use the libraries
+	# from the framework at the given location so that linking works
+	# against Tcl.framework installed in an arbitrary location.
+	case ${TCL_DEFS} in
+	    *TCL_FRAMEWORK*)
+		if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then
+		    for i in "`cd "${TCL_BIN_DIR}"; pwd`" \
+			     "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do
+			if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then
+			    TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}"
+			    break
+			fi
+		    done
+		fi
+		if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then
+		    TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}"  | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}"
+		    TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"
+		fi
+		;;
+	esac
+    fi
+
+    # eval is required to do the TCL_DBGX substitution
+    eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+    eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+    eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
+    eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
+
+    AC_SUBST(TCL_VERSION)
+    AC_SUBST(TCL_PATCH_LEVEL)
+    AC_SUBST(TCL_BIN_DIR)
+    AC_SUBST(TCL_SRC_DIR)
+
+    AC_SUBST(TCL_LIB_FILE)
+    AC_SUBST(TCL_LIB_FLAG)
+    AC_SUBST(TCL_LIB_SPEC)
+
+    AC_SUBST(TCL_STUB_LIB_FILE)
+    AC_SUBST(TCL_STUB_LIB_FLAG)
+    AC_SUBST(TCL_STUB_LIB_SPEC)
+
+    case "`uname -s`" in
+	*CYGWIN_*)
+	    AC_MSG_CHECKING([for cygwin variant])
+	    case ${TCL_EXTRA_CFLAGS} in
+		*-mwin32*|*-mno-cygwin*)
+		    TEA_PLATFORM="windows"
+		    CFLAGS="$CFLAGS -mwin32"
+		    AC_MSG_RESULT([win32])
+		    ;;
+		*)
+		    TEA_PLATFORM="unix"
+		    AC_MSG_RESULT([unix])
+		    ;;
+	    esac
+	    EXEEXT=".exe"
+	    ;;
+	*)
+	    ;;
+    esac
+
+    # The BUILD_$pkg is to define the correct extern storage class
+    # handling when making this package
+    AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME}, [],
+	    [Building extension source?])
+    # Do this here as we have fully defined TEA_PLATFORM now
+    if test "${TEA_PLATFORM}" = "windows" ; then
+	CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp"
+    fi
+
+    # TEA specific:
+    AC_SUBST(CLEANFILES)
+    AC_SUBST(TCL_LIBS)
+    AC_SUBST(TCL_DEFS)
+    AC_SUBST(TCL_EXTRA_CFLAGS)
+    AC_SUBST(TCL_LD_FLAGS)
+    AC_SUBST(TCL_SHLIB_LD_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TKCONFIG --
+#
+#	Load the tkConfig.sh file
+#
+# Arguments:
+#
+#	Requires the following vars to be set:
+#		TK_BIN_DIR
+#
+# Results:
+#
+#	Sets the following vars that should be in tkConfig.sh:
+#		TK_BIN_DIR
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TKCONFIG], [
+    AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh])
+
+    if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then
+        AC_MSG_RESULT([loading])
+	. "${TK_BIN_DIR}/tkConfig.sh"
+    else
+        AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
+    fi
+
+    # eval is required to do the TK_DBGX substitution
+    eval "TK_LIB_FILE=\"${TK_LIB_FILE}\""
+    eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\""
+
+    # If the TK_BIN_DIR is the build directory (not the install directory),
+    # then set the common variable name to the value of the build variables.
+    # For example, the variable TK_LIB_SPEC will be set to the value
+    # of TK_BUILD_LIB_SPEC. An extension should make use of TK_LIB_SPEC
+    # instead of TK_BUILD_LIB_SPEC since it will work with both an
+    # installed and uninstalled version of Tcl.
+    if test -f "${TK_BIN_DIR}/Makefile" ; then
+        TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}"
+        TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}"
+        TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}"
+    elif test "`uname -s`" = "Darwin"; then
+	# If Tk was built as a framework, attempt to use the libraries
+	# from the framework at the given location so that linking works
+	# against Tk.framework installed in an arbitrary location.
+	case ${TK_DEFS} in
+	    *TK_FRAMEWORK*)
+		if test -f "${TK_BIN_DIR}/${TK_LIB_FILE}"; then
+		    for i in "`cd "${TK_BIN_DIR}"; pwd`" \
+			     "`cd "${TK_BIN_DIR}"/../..; pwd`"; do
+			if test "`basename "$i"`" = "${TK_LIB_FILE}.framework"; then
+			    TK_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TK_LIB_FILE}"
+			    break
+			fi
+		    done
+		fi
+		if test -f "${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"; then
+		    TK_STUB_LIB_SPEC="-L` echo "${TK_BIN_DIR}"  | sed -e 's/ /\\\\ /g'` ${TK_STUB_LIB_FLAG}"
+		    TK_STUB_LIB_PATH="${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"
+		fi
+		;;
+	esac
+    fi
+
+    # eval is required to do the TK_DBGX substitution
+    eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\""
+    eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\""
+    eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\""
+    eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\""
+
+    # TEA specific: Ensure windowingsystem is defined
+    if test "${TEA_PLATFORM}" = "unix" ; then
+	case ${TK_DEFS} in
+	    *MAC_OSX_TK*)
+		AC_DEFINE(MAC_OSX_TK, 1, [Are we building against Mac OS X TkAqua?])
+		TEA_WINDOWINGSYSTEM="aqua"
+		;;
+	    *)
+		TEA_WINDOWINGSYSTEM="x11"
+		;;
+	esac
+    elif test "${TEA_PLATFORM}" = "windows" ; then
+	TEA_WINDOWINGSYSTEM="win32"
+    fi
+
+    AC_SUBST(TK_VERSION)
+    AC_SUBST(TK_BIN_DIR)
+    AC_SUBST(TK_SRC_DIR)
+
+    AC_SUBST(TK_LIB_FILE)
+    AC_SUBST(TK_LIB_FLAG)
+    AC_SUBST(TK_LIB_SPEC)
+
+    AC_SUBST(TK_STUB_LIB_FILE)
+    AC_SUBST(TK_STUB_LIB_FLAG)
+    AC_SUBST(TK_STUB_LIB_SPEC)
+
+    # TEA specific:
+    AC_SUBST(TK_LIBS)
+    AC_SUBST(TK_XINCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_TCLSH
+#	Determine the fully qualified path name of the tclsh executable
+#	in the Tcl build directory or the tclsh installed in a bin
+#	directory. This macro will correctly determine the name
+#	of the tclsh executable even if tclsh has not yet been
+#	built in the build directory. The tclsh found is always
+#	associated with a tclConfig.sh file. This tclsh should be used
+#	only for running extension test cases. It should never be
+#	or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments
+#	none
+#
+# Results
+#	Subst's the following values:
+#		TCLSH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_TCLSH], [
+    AC_MSG_CHECKING([for tclsh])
+    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+        # tclConfig.sh is in Tcl build directory
+        if test "${TEA_PLATFORM}" = "windows"; then
+            TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+        else
+            TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
+        fi
+    else
+        # tclConfig.sh is in install location
+        if test "${TEA_PLATFORM}" = "windows"; then
+            TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+        else
+            TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}"
+        fi
+        list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
+              `ls -d ${TCL_BIN_DIR}/..     2>/dev/null` \
+              `ls -d ${TCL_PREFIX}/bin     2>/dev/null`"
+        for i in $list ; do
+            if test -f "$i/${TCLSH_PROG}" ; then
+                REAL_TCL_BIN_DIR="`cd "$i"; pwd`/"
+                break
+            fi
+        done
+        TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}"
+    fi
+    AC_MSG_RESULT([${TCLSH_PROG}])
+    AC_SUBST(TCLSH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_WISH
+#	Determine the fully qualified path name of the wish executable
+#	in the Tk build directory or the wish installed in a bin
+#	directory. This macro will correctly determine the name
+#	of the wish executable even if wish has not yet been
+#	built in the build directory. The wish found is always
+#	associated with a tkConfig.sh file. This wish should be used
+#	only for running extension test cases. It should never be
+#	or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments
+#	none
+#
+# Results
+#	Subst's the following values:
+#		WISH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_WISH], [
+    AC_MSG_CHECKING([for wish])
+    if test -f "${TK_BIN_DIR}/Makefile" ; then
+        # tkConfig.sh is in Tk build directory
+        if test "${TEA_PLATFORM}" = "windows"; then
+            WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+        else
+            WISH_PROG="${TK_BIN_DIR}/wish"
+        fi
+    else
+        # tkConfig.sh is in install location
+        if test "${TEA_PLATFORM}" = "windows"; then
+            WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+        else
+            WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}"
+        fi
+        list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \
+              `ls -d ${TK_BIN_DIR}/..     2>/dev/null` \
+              `ls -d ${TK_PREFIX}/bin     2>/dev/null`"
+        for i in $list ; do
+            if test -f "$i/${WISH_PROG}" ; then
+                REAL_TK_BIN_DIR="`cd "$i"; pwd`/"
+                break
+            fi
+        done
+        WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}"
+    fi
+    AC_MSG_RESULT([${WISH_PROG}])
+    AC_SUBST(WISH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SHARED --
+#
+#	Allows the building of shared libraries
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--enable-shared=yes|no
+#
+#	Defines the following vars:
+#		STATIC_BUILD	Used for building import/export libraries
+#				on Windows.
+#
+#	Sets the following vars:
+#		SHARED_BUILD	Value of 1 or 0
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SHARED], [
+    AC_MSG_CHECKING([how to build libraries])
+    AC_ARG_ENABLE(shared,
+	AC_HELP_STRING([--enable-shared],
+	    [build and link with shared libraries (default: on)]),
+	[tcl_ok=$enableval], [tcl_ok=yes])
+
+    if test "${enable_shared+set}" = set; then
+	enableval="$enable_shared"
+	tcl_ok=$enableval
+    else
+	tcl_ok=yes
+    fi
+
+    if test "$tcl_ok" = "yes" ; then
+	AC_MSG_RESULT([shared])
+	SHARED_BUILD=1
+    else
+	AC_MSG_RESULT([static])
+	SHARED_BUILD=0
+	AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
+    fi
+    AC_SUBST(SHARED_BUILD)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_THREADS --
+#
+#	Specify if thread support should be enabled.  If "yes" is specified
+#	as an arg (optional), threads are enabled by default, "no" means
+#	threads are disabled.  "yes" is the default.
+#
+#	TCL_THREADS is checked so that if you are compiling an extension
+#	against a threaded core, your extension must be compiled threaded
+#	as well.
+#
+#	Note that it is legal to have a thread enabled extension run in a
+#	threaded or non-threaded Tcl core, but a non-threaded extension may
+#	only run in a non-threaded Tcl core.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--enable-threads
+#
+#	Sets the following vars:
+#		THREADS_LIBS	Thread library(s)
+#
+#	Defines the following vars:
+#		TCL_THREADS
+#		_REENTRANT
+#		_THREAD_SAFE
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_THREADS], [
+    AC_ARG_ENABLE(threads,
+	AC_HELP_STRING([--enable-threads],
+	    [build with threads]),
+	[tcl_ok=$enableval], [tcl_ok=yes])
+
+    if test "${enable_threads+set}" = set; then
+	enableval="$enable_threads"
+	tcl_ok=$enableval
+    else
+	tcl_ok=yes
+    fi
+
+    if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then
+	TCL_THREADS=1
+
+	if test "${TEA_PLATFORM}" != "windows" ; then
+	    # We are always OK on Windows, so check what this platform wants:
+
+	    # USE_THREAD_ALLOC tells us to try the special thread-based
+	    # allocator that significantly reduces lock contention
+	    AC_DEFINE(USE_THREAD_ALLOC, 1,
+		[Do we want to use the threaded memory allocator?])
+	    AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+	    if test "`uname -s`" = "SunOS" ; then
+		AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+			[Do we really want to follow the standard? Yes we do!])
+	    fi
+	    AC_DEFINE(_THREAD_SAFE, 1, [Do we want the thread-safe OS API?])
+	    AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no)
+	    if test "$tcl_ok" = "no"; then
+		# Check a little harder for __pthread_mutex_init in the same
+		# library, as some systems hide it there until pthread.h is
+		# defined.  We could alternatively do an AC_TRY_COMPILE with
+		# pthread.h, but that will work with libpthread really doesn't
+		# exist, like AIX 4.2.  [Bug: 4359]
+		AC_CHECK_LIB(pthread, __pthread_mutex_init,
+		    tcl_ok=yes, tcl_ok=no)
+	    fi
+
+	    if test "$tcl_ok" = "yes"; then
+		# The space is needed
+		THREADS_LIBS=" -lpthread"
+	    else
+		AC_CHECK_LIB(pthreads, pthread_mutex_init,
+		    tcl_ok=yes, tcl_ok=no)
+		if test "$tcl_ok" = "yes"; then
+		    # The space is needed
+		    THREADS_LIBS=" -lpthreads"
+		else
+		    AC_CHECK_LIB(c, pthread_mutex_init,
+			tcl_ok=yes, tcl_ok=no)
+		    if test "$tcl_ok" = "no"; then
+			AC_CHECK_LIB(c_r, pthread_mutex_init,
+			    tcl_ok=yes, tcl_ok=no)
+			if test "$tcl_ok" = "yes"; then
+			    # The space is needed
+			    THREADS_LIBS=" -pthread"
+			else
+			    TCL_THREADS=0
+			    AC_MSG_WARN([Do not know how to find pthread lib on your system - thread support disabled])
+			fi
+		    fi
+		fi
+	    fi
+	fi
+    else
+	TCL_THREADS=0
+    fi
+    # Do checking message here to not mess up interleaved configure output
+    AC_MSG_CHECKING([for building with threads])
+    if test "${TCL_THREADS}" = 1; then
+	AC_DEFINE(TCL_THREADS, 1, [Are we building with threads enabled?])
+	AC_MSG_RESULT([yes (default)])
+    else
+	AC_MSG_RESULT([no])
+    fi
+    # TCL_THREADS sanity checking.  See if our request for building with
+    # threads is the same as the way Tcl was built.  If not, warn the user.
+    case ${TCL_DEFS} in
+	*THREADS=1*)
+	    if test "${TCL_THREADS}" = "0"; then
+		AC_MSG_WARN([
+    Building ${PACKAGE_NAME} without threads enabled, but building against Tcl
+    that IS thread-enabled.  It is recommended to use --enable-threads.])
+	    fi
+	    ;;
+	*)
+	    if test "${TCL_THREADS}" = "1"; then
+		AC_MSG_WARN([
+    --enable-threads requested, but building against a Tcl that is NOT
+    thread-enabled.  This is an OK configuration that will also run in
+    a thread-enabled core.])
+	    fi
+	    ;;
+    esac
+    AC_SUBST(TCL_THREADS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SYMBOLS --
+#
+#	Specify if debugging symbols should be used.
+#	Memory (TCL_MEM_DEBUG) debugging can also be enabled.
+#
+# Arguments:
+#	none
+#
+#	TEA varies from core Tcl in that C|LDFLAGS_DEFAULT receives
+#	the value of C|LDFLAGS_OPTIMIZE|DEBUG already substituted.
+#	Requires the following vars to be set in the Makefile:
+#		CFLAGS_DEFAULT
+#		LDFLAGS_DEFAULT
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--enable-symbols
+#
+#	Defines the following vars:
+#		CFLAGS_DEFAULT	Sets to $(CFLAGS_DEBUG) if true
+#				Sets to $(CFLAGS_OPTIMIZE) if false
+#		LDFLAGS_DEFAULT	Sets to $(LDFLAGS_DEBUG) if true
+#				Sets to $(LDFLAGS_OPTIMIZE) if false
+#		DBGX		Formerly used as debug library extension;
+#				always blank now.
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SYMBOLS], [
+    dnl TEA specific: Make sure we are initialized
+    AC_REQUIRE([TEA_CONFIG_CFLAGS])
+    AC_MSG_CHECKING([for build with symbols])
+    AC_ARG_ENABLE(symbols,
+	AC_HELP_STRING([--enable-symbols],
+	    [build with debugging symbols (default: off)]),
+	[tcl_ok=$enableval], [tcl_ok=no])
+    DBGX=""
+    if test "$tcl_ok" = "no"; then
+	CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE}"
+	LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
+	AC_MSG_RESULT([no])
+    else
+	CFLAGS_DEFAULT="${CFLAGS_DEBUG}"
+	LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}"
+	if test "$tcl_ok" = "yes"; then
+	    AC_MSG_RESULT([yes (standard debugging)])
+	fi
+    fi
+    # TEA specific:
+    if test "${TEA_PLATFORM}" != "windows" ; then
+	LDFLAGS_DEFAULT="${LDFLAGS}"
+    fi
+    AC_SUBST(CFLAGS_DEFAULT)
+    AC_SUBST(LDFLAGS_DEFAULT)
+    AC_SUBST(TCL_DBGX)
+
+    if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then
+	AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?])
+    fi
+
+    if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then
+	if test "$tcl_ok" = "all"; then
+	    AC_MSG_RESULT([enabled symbols mem debugging])
+	else
+	    AC_MSG_RESULT([enabled $tcl_ok debugging])
+	fi
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_LANGINFO --
+#
+#	Allows use of modern nl_langinfo check for better l10n.
+#	This is only relevant for Unix.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--enable-langinfo=yes|no (default is yes)
+#
+#	Defines the following vars:
+#		HAVE_LANGINFO	Triggers use of nl_langinfo if defined.
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_LANGINFO], [
+    AC_ARG_ENABLE(langinfo,
+	AC_HELP_STRING([--enable-langinfo],
+	    [use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]),
+	[langinfo_ok=$enableval], [langinfo_ok=yes])
+
+    HAVE_LANGINFO=0
+    if test "$langinfo_ok" = "yes"; then
+	AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no])
+    fi
+    AC_MSG_CHECKING([whether to use nl_langinfo])
+    if test "$langinfo_ok" = "yes"; then
+	AC_CACHE_VAL(tcl_cv_langinfo_h, [
+	    AC_TRY_COMPILE([#include <langinfo.h>], [nl_langinfo(CODESET);],
+		    [tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no])])
+	AC_MSG_RESULT([$tcl_cv_langinfo_h])
+	if test $tcl_cv_langinfo_h = yes; then
+	    AC_DEFINE(HAVE_LANGINFO, 1, [Do we have nl_langinfo()?])
+	fi
+    else
+	AC_MSG_RESULT([$langinfo_ok])
+    fi
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_SYSTEM
+#
+#	Determine what the system is (some things cannot be easily checked
+#	on a feature-driven basis, alas). This can usually be done via the
+#	"uname" command.
+#
+# Arguments:
+#	none
+#
+# Results:
+#	Defines the following var:
+#
+#	system -	System/platform/version identification code.
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_SYSTEM], [
+    AC_CACHE_CHECK([system version], tcl_cv_sys_version, [
+	# TEA specific:
+	if test "${TEA_PLATFORM}" = "windows" ; then
+	    tcl_cv_sys_version=windows
+	else
+	    tcl_cv_sys_version=`uname -s`-`uname -r`
+	    if test "$?" -ne 0 ; then
+		AC_MSG_WARN([can't find uname command])
+		tcl_cv_sys_version=unknown
+	    else
+		if test "`uname -s`" = "AIX" ; then
+		    tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
+		fi
+	    fi
+	fi
+    ])
+    system=$tcl_cv_sys_version
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_CFLAGS
+#
+#	Try to determine the proper flags to pass to the compiler
+#	for building shared libraries and other such nonsense.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines and substitutes the following vars:
+#
+#	DL_OBJS, DL_LIBS - removed for TEA, only needed by core.
+#       LDFLAGS -      Flags to pass to the compiler when linking object
+#                       files into an executable application binary such
+#                       as tclsh.
+#       LD_SEARCH_FLAGS-Flags to pass to ld, such as "-R /usr/local/tcl/lib",
+#                       that tell the run-time dynamic linker where to look
+#                       for shared libraries such as libtcl.so.  Depends on
+#                       the variable LIB_RUNTIME_DIR in the Makefile. Could
+#                       be the same as CC_SEARCH_FLAGS if ${CC} is used to link.
+#       CC_SEARCH_FLAGS-Flags to pass to ${CC}, such as "-Wl,-rpath,/usr/local/tcl/lib",
+#                       that tell the run-time dynamic linker where to look
+#                       for shared libraries such as libtcl.so.  Depends on
+#                       the variable LIB_RUNTIME_DIR in the Makefile.
+#       SHLIB_CFLAGS -  Flags to pass to cc when compiling the components
+#                       of a shared library (may request position-independent
+#                       code, among other things).
+#       SHLIB_LD -      Base command to use for combining object files
+#                       into a shared library.
+#       SHLIB_LD_LIBS - Dependent libraries for the linker to scan when
+#                       creating shared libraries.  This symbol typically
+#                       goes at the end of the "ld" commands that build
+#                       shared libraries. The value of the symbol defaults to
+#                       "${LIBS}" if all of the dependent libraries should
+#                       be specified when creating a shared library.  If
+#                       dependent libraries should not be specified (as on
+#                       SunOS 4.x, where they cause the link to fail, or in
+#                       general if Tcl and Tk aren't themselves shared
+#                       libraries), then this symbol has an empty string
+#                       as its value.
+#       SHLIB_SUFFIX -  Suffix to use for the names of dynamically loadable
+#                       extensions.  An empty string means we don't know how
+#                       to use shared libraries on this platform.
+#       LIB_SUFFIX -    Specifies everything that comes after the "libfoo"
+#                       in a static or shared library name, using the $VERSION variable
+#                       to put the version in the right place.  This is used
+#                       by platforms that need non-standard library names.
+#                       Examples:  ${VERSION}.so.1.1 on NetBSD, since it needs
+#                       to have a version after the .so, and ${VERSION}.a
+#                       on AIX, since a shared library needs to have
+#                       a .a extension whereas shared objects for loadable
+#                       extensions have a .so extension.  Defaults to
+#                       ${VERSION}${SHLIB_SUFFIX}.
+#	CFLAGS_DEBUG -
+#			Flags used when running the compiler in debug mode
+#	CFLAGS_OPTIMIZE -
+#			Flags used when running the compiler in optimize mode
+#	CFLAGS -	Additional CFLAGS added as necessary (usually 64-bit)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_CFLAGS], [
+    dnl TEA specific: Make sure we are initialized
+    AC_REQUIRE([TEA_INIT])
+
+    # Step 0.a: Enable 64 bit support?
+
+    AC_MSG_CHECKING([if 64bit support is requested])
+    AC_ARG_ENABLE(64bit,
+	AC_HELP_STRING([--enable-64bit],
+	    [enable 64bit support (default: off)]),
+	[do64bit=$enableval], [do64bit=no])
+    AC_MSG_RESULT([$do64bit])
+
+    # Step 0.b: Enable Solaris 64 bit VIS support?
+
+    AC_MSG_CHECKING([if 64bit Sparc VIS support is requested])
+    AC_ARG_ENABLE(64bit-vis,
+	AC_HELP_STRING([--enable-64bit-vis],
+	    [enable 64bit Sparc VIS support (default: off)]),
+	[do64bitVIS=$enableval], [do64bitVIS=no])
+    AC_MSG_RESULT([$do64bitVIS])
+    # Force 64bit on with VIS
+    AS_IF([test "$do64bitVIS" = "yes"], [do64bit=yes])
+
+    # Step 0.c: Check if visibility support is available. Do this here so
+    # that platform specific alternatives can be used below if this fails.
+
+    AC_CACHE_CHECK([if compiler supports visibility "hidden"],
+	tcl_cv_cc_visibility_hidden, [
+	hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror"
+	AC_TRY_LINK([
+	    extern __attribute__((__visibility__("hidden"))) void f(void);
+	    void f(void) {}], [f();], tcl_cv_cc_visibility_hidden=yes,
+	    tcl_cv_cc_visibility_hidden=no)
+	CFLAGS=$hold_cflags])
+    AS_IF([test $tcl_cv_cc_visibility_hidden = yes], [
+	AC_DEFINE(MODULE_SCOPE,
+	    [extern __attribute__((__visibility__("hidden")))],
+	    [Compiler support for module scope symbols])
+    ])
+
+    # Step 0.d: Disable -rpath support?
+
+    AC_MSG_CHECKING([if rpath support is requested])
+    AC_ARG_ENABLE(rpath,
+	AC_HELP_STRING([--disable-rpath],
+	    [disable rpath support (default: on)]),
+	[doRpath=$enableval], [doRpath=yes])
+    AC_MSG_RESULT([$doRpath])
+
+    # TEA specific: Cross-compiling options for Windows/CE builds?
+
+    AS_IF([test "${TEA_PLATFORM}" = windows], [
+	AC_MSG_CHECKING([if Windows/CE build is requested])
+	AC_ARG_ENABLE(wince,
+	    AC_HELP_STRING([--enable-wince],
+		[enable Win/CE support (where applicable)]),
+	    [doWince=$enableval], [doWince=no])
+	AC_MSG_RESULT([$doWince])
+    ])
+
+    # Set the variable "system" to hold the name and version number
+    # for the system.
+
+    TEA_CONFIG_SYSTEM
+
+    # Require ranlib early so we can override it in special cases below.
+
+    AC_REQUIRE([AC_PROG_RANLIB])
+
+    # Set configuration options based on system name and version.
+    # This is similar to Tcl's unix/tcl.m4 except that we've added a
+    # "windows" case and removed some core-only vars.
+
+    do64bit_ok=no
+    # default to '{$LIBS}' and set to "" on per-platform necessary basis
+    SHLIB_LD_LIBS='${LIBS}'
+    # When ld needs options to work in 64-bit mode, put them in
+    # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load]
+    # is disabled by the user. [Bug 1016796]
+    LDFLAGS_ARCH=""
+    UNSHARED_LIB_SUFFIX=""
+    # TEA specific: use PACKAGE_VERSION instead of VERSION
+    TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`'
+    ECHO_VERSION='`echo ${PACKAGE_VERSION}`'
+    TCL_LIB_VERSIONS_OK=ok
+    CFLAGS_DEBUG=-g
+    CFLAGS_OPTIMIZE=-O
+    AS_IF([test "$GCC" = yes], [
+	# TEA specific:
+	CFLAGS_OPTIMIZE=-O2
+	CFLAGS_WARNING="-Wall"
+    ], [CFLAGS_WARNING=""])
+    AC_CHECK_TOOL(AR, ar)
+    STLIB_LD='${AR} cr'
+    LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
+    AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"])
+    case $system in
+	# TEA specific:
+	windows)
+	    # This is a 2-stage check to make sure we have the 64-bit SDK
+	    # We have to know where the SDK is installed.
+	    # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs
+	    # MACHINE is IX86 for LINK, but this is used by the manifest,
+	    # which requires x86|amd64|ia64.
+	    MACHINE="X86"
+	    if test "$do64bit" != "no" ; then
+		if test "x${MSSDK}x" = "xx" ; then
+		    MSSDK="C:/Progra~1/Microsoft Platform SDK"
+		fi
+		MSSDK=`echo "$MSSDK" | sed -e  's!\\\!/!g'`
+		PATH64=""
+		case "$do64bit" in
+		    amd64|x64|yes)
+			MACHINE="AMD64" ; # default to AMD64 64-bit build
+			PATH64="${MSSDK}/Bin/Win64/x86/AMD64"
+			;;
+		    ia64)
+			MACHINE="IA64"
+			PATH64="${MSSDK}/Bin/Win64"
+			;;
+		esac
+		if test ! -d "${PATH64}" ; then
+		    AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode])
+		    AC_MSG_WARN([Ensure latest Platform SDK is installed])
+		    do64bit="no"
+		else
+		    AC_MSG_RESULT([   Using 64-bit $MACHINE mode])
+		    do64bit_ok="yes"
+		fi
+	    fi
+
+	    if test "$doWince" != "no" ; then
+		if test "$do64bit" != "no" ; then
+		    AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible])
+		fi
+		if test "$GCC" = "yes" ; then
+		    AC_MSG_ERROR([Windows/CE and GCC builds incompatible])
+		fi
+		TEA_PATH_CELIB
+		# Set defaults for common evc4/PPC2003 setup
+		# Currently Tcl requires 300+, possibly 420+ for sockets
+		CEVERSION=420; 		# could be 211 300 301 400 420 ...
+		TARGETCPU=ARMV4;	# could be ARMV4 ARM MIPS SH3 X86 ...
+		ARCH=ARM;		# could be ARM MIPS X86EM ...
+		PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
+		if test "$doWince" != "yes"; then
+		    # If !yes then the user specified something
+		    # Reset ARCH to allow user to skip specifying it
+		    ARCH=
+		    eval `echo $doWince | awk -F, '{ \
+	    if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \
+	    if ([$]1 < 400)   { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
+	    if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \
+	    if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \
+	    if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \
+		    }'`
+		    if test "x${ARCH}" = "x" ; then
+			ARCH=$TARGETCPU;
+		    fi
+		fi
+		OSVERSION=WCE$CEVERSION;
+	    	if test "x${WCEROOT}" = "x" ; then
+			WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
+		    if test ! -d "${WCEROOT}" ; then
+			WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
+		    fi
+		fi
+		if test "x${SDKROOT}" = "x" ; then
+		    SDKROOT="C:/Program Files/Windows CE Tools"
+		    if test ! -d "${SDKROOT}" ; then
+			SDKROOT="C:/Windows CE Tools"
+		    fi
+		fi
+		WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
+		SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
+		if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
+		    -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
+		    AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]])
+		    doWince="no"
+		else
+		    # We could PATH_NOSPACE these, but that's not important,
+		    # as long as we quote them when used.
+		    CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
+		    if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
+			CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
+		    fi
+		    CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
+    		fi
+	    fi
+
+	    if test "$GCC" != "yes" ; then
+	        if test "${SHARED_BUILD}" = "0" ; then
+		    runtime=-MT
+	        else
+		    runtime=-MD
+	        fi
+
+                if test "$do64bit" != "no" ; then
+		    # All this magic is necessary for the Win64 SDK RC1 - hobbs
+		    CC="\"${PATH64}/cl.exe\""
+		    CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
+		    RC="\"${MSSDK}/bin/rc.exe\""
+		    lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
+		    LINKBIN="\"${PATH64}/link.exe\""
+		    CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
+		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+		    # Avoid 'unresolved external symbol __security_cookie'
+		    # errors, c.f. http://support.microsoft.com/?id=894573
+		    TEA_ADD_LIBS([bufferoverflowU.lib])
+		elif test "$doWince" != "no" ; then
+		    CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
+		    if test "${TARGETCPU}" = "X86"; then
+			CC="\"${CEBINROOT}/cl.exe\""
+		    else
+			CC="\"${CEBINROOT}/cl${ARCH}.exe\""
+		    fi
+		    CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
+		    RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
+		    arch=`echo ${ARCH} | awk '{print tolower([$]0)}'`
+		    defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
+		    if test "${SHARED_BUILD}" = "1" ; then
+			# Static CE builds require static celib as well
+		    	defs="${defs} _DLL"
+		    fi
+		    for i in $defs ; do
+			AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i)
+		    done
+		    AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version])
+		    AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version])
+		    CFLAGS_DEBUG="-nologo -Zi -Od"
+		    CFLAGS_OPTIMIZE="-nologo -Ox"
+		    lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
+		    lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
+		    LINKBIN="\"${CEBINROOT}/link.exe\""
+		    AC_SUBST(CELIB_DIR)
+		else
+		    RC="rc"
+		    lflags="-nologo"
+    		    LINKBIN="link"
+		    CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
+		    CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+		fi
+	    fi
+
+	    if test "$GCC" = "yes"; then
+		# mingw gcc mode
+		RC="windres"
+		CFLAGS_DEBUG="-g"
+		CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+		SHLIB_LD="$CC -shared"
+		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+		LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
+		LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"
+	    else
+		SHLIB_LD="${LINKBIN} -dll ${lflags}"
+		# link -lib only works when -lib is the first arg
+		STLIB_LD="${LINKBIN} -lib ${lflags}"
+		UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib'
+		PATHTYPE=-w
+		# For information on what debugtype is most useful, see:
+		# http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp
+		# and also
+		# http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx
+		# This essentially turns it all on.
+		LDFLAGS_DEBUG="-debug -debugtype:cv"
+		LDFLAGS_OPTIMIZE="-release"
+		if test "$doWince" != "no" ; then
+		    LDFLAGS_CONSOLE="-link ${lflags}"
+		    LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
+		else
+		    LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
+		    LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
+		fi
+	    fi
+
+	    SHLIB_SUFFIX=".dll"
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll'
+
+	    TCL_LIB_VERSIONS_OK=nodots
+    	    ;;
+	AIX-*)
+	    AS_IF([test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"], [
+		# AIX requires the _r compiler when gcc isn't being used
+		case "${CC}" in
+		    *_r|*_r\ *)
+			# ok ...
+			;;
+		    *)
+			# Make sure only first arg gets _r
+		    	CC=`echo "$CC" | sed -e 's/^\([[^ ]]*\)/\1_r/'`
+			;;
+		esac
+		AC_MSG_RESULT([Using $CC for compiling with threads])
+	    ])
+	    LIBS="$LIBS -lc"
+	    SHLIB_CFLAGS=""
+	    SHLIB_SUFFIX=".so"
+
+	    LD_LIBRARY_PATH_VAR="LIBPATH"
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    AS_IF([test "$do64bit" = yes], [
+		AS_IF([test "$GCC" = yes], [
+		    AC_MSG_WARN([64bit mode not supported with GCC on $system])
+		], [
+		    do64bit_ok=yes
+		    CFLAGS="$CFLAGS -q64"
+		    LDFLAGS_ARCH="-q64"
+		    RANLIB="${RANLIB} -X64"
+		    AR="${AR} -X64"
+		    SHLIB_LD_FLAGS="-b64"
+		])
+	    ])
+
+	    AS_IF([test "`uname -m`" = ia64], [
+		# AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC
+		SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+		AS_IF([test "$GCC" = yes], [
+		    CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		], [
+		    CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}'
+		])
+		LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+	    ], [
+		AS_IF([test "$GCC" = yes], [
+		    SHLIB_LD='${CC} -shared -Wl,-bexpall'
+		], [
+		    SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry"
+		    LDFLAGS="$LDFLAGS -brtl"
+		])
+		SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}"
+		CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    ])
+	    ;;
+	BeOS*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -nostart'
+	    SHLIB_SUFFIX=".so"
+
+	    #-----------------------------------------------------------
+	    # Check for inet_ntoa in -lbind, for BeOS (which also needs
+	    # -lsocket, even if the network functions are in -lnet which
+	    # is always linked to, for compatibility.
+	    #-----------------------------------------------------------
+	    AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"])
+	    ;;
+	BSD/OS-4.*)
+	    SHLIB_CFLAGS="-export-dynamic -fPIC"
+	    SHLIB_LD='${CC} -shared'
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS="$LDFLAGS -export-dynamic"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	CYGWIN_*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD='${CC} -shared'
+	    SHLIB_SUFFIX=".dll"
+	    EXE_SUFFIX=".exe"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	Haiku*)
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
+	    AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"])
+	    ;;
+	HP-UX-*.11.*)
+	    # Use updated header definitions where possible
+	    AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, [Do we want to use the XOPEN network library?])
+	    # TEA specific: Needed by Tcl, but not most extensions
+	    #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?])
+	    #LIBS="$LIBS -lxnet"               # Use the XOPEN network library
+
+	    AS_IF([test "`uname -m`" = ia64], [
+		SHLIB_SUFFIX=".so"
+		# Use newer C++ library for C++ extensions
+		#if test "$GCC" != "yes" ; then
+		#   CPPFLAGS="-AA"
+		#fi
+	    ], [
+		SHLIB_SUFFIX=".sl"
+	    ])
+	    AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
+	    AS_IF([test "$tcl_ok" = yes], [
+		LDFLAGS="$LDFLAGS -Wl,-E"
+		CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
+		LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+		LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+	    ])
+	    AS_IF([test "$GCC" = yes], [
+		SHLIB_LD='${CC} -shared'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    ], [
+		CFLAGS="$CFLAGS -z"
+		# Users may want PA-RISC 1.1/2.0 portable code - needs HP cc
+		#CFLAGS="$CFLAGS +DAportable"
+		SHLIB_CFLAGS="+z"
+		SHLIB_LD="ld -b"
+	    ])
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    AS_IF([test "$do64bit" = "yes"], [
+		AS_IF([test "$GCC" = yes], [
+		    case `${CC} -dumpmachine` in
+			hppa64*)
+			    # 64-bit gcc in use.  Fix flags for GNU ld.
+			    do64bit_ok=yes
+			    SHLIB_LD='${CC} -shared'
+			    AS_IF([test $doRpath = yes], [
+				CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+			    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+			    ;;
+			*)
+			    AC_MSG_WARN([64bit mode not supported with GCC on $system])
+			    ;;
+		    esac
+		], [
+		    do64bit_ok=yes
+		    CFLAGS="$CFLAGS +DD64"
+		    LDFLAGS_ARCH="+DD64"
+		])
+	    ]) ;;
+	IRIX-6.*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD="ld -n32 -shared -rdata_shared"
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+	    AS_IF([test "$GCC" = yes], [
+		CFLAGS="$CFLAGS -mabi=n32"
+		LDFLAGS="$LDFLAGS -mabi=n32"
+	    ], [
+		case $system in
+		    IRIX-6.3)
+			# Use to build 6.2 compatible binaries on 6.3.
+			CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS"
+			;;
+		    *)
+			CFLAGS="$CFLAGS -n32"
+			;;
+		esac
+		LDFLAGS="$LDFLAGS -n32"
+	    ])
+	    ;;
+	IRIX64-6.*)
+	    SHLIB_CFLAGS=""
+	    SHLIB_LD="ld -n32 -shared -rdata_shared"
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+
+	    # Check to enable 64-bit flags for compiler/linker
+
+	    AS_IF([test "$do64bit" = yes], [
+	        AS_IF([test "$GCC" = yes], [
+	            AC_MSG_WARN([64bit mode not supported by gcc])
+	        ], [
+	            do64bit_ok=yes
+	            SHLIB_LD="ld -64 -shared -rdata_shared"
+	            CFLAGS="$CFLAGS -64"
+	            LDFLAGS_ARCH="-64"
+	        ])
+	    ])
+	    ;;
+	Linux*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+
+	    # TEA specific:
+	    CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+
+	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+	    SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"])
+	    AS_IF([test $do64bit = yes], [
+		AC_CACHE_CHECK([if compiler accepts -m64 flag], tcl_cv_cc_m64, [
+		    hold_cflags=$CFLAGS
+		    CFLAGS="$CFLAGS -m64"
+		    AC_TRY_LINK(,, tcl_cv_cc_m64=yes, tcl_cv_cc_m64=no)
+		    CFLAGS=$hold_cflags])
+		AS_IF([test $tcl_cv_cc_m64 = yes], [
+		    CFLAGS="$CFLAGS -m64"
+		    do64bit_ok=yes
+		])
+	   ])
+
+	    # The combo of gcc + glibc has a bug related to inlining of
+	    # functions like strtod(). The -fno-builtin flag should address
+	    # this problem but it does not work. The -fno-inline flag is kind
+	    # of overkill but it works. Disable inlining only when one of the
+	    # files in compat/*.c is being linked in.
+
+	    AS_IF([test x"${USE_COMPAT}" != x],[CFLAGS="$CFLAGS -fno-inline"])
+
+	    ;;
+	GNU*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+
+	    SHLIB_LD='${CC} -shared'
+	    LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"])
+	    ;;
+	Lynx*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_SUFFIX=".so"
+	    CFLAGS_OPTIMIZE=-02
+	    SHLIB_LD='${CC} -shared'
+	    LD_FLAGS="-Wl,--export-dynamic"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+	    ;;
+	OpenBSD-*)
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
+	    AC_CACHE_CHECK([for ELF], tcl_cv_ld_elf, [
+		AC_EGREP_CPP(yes, [
+#ifdef __ELF__
+	yes
+#endif
+		], tcl_cv_ld_elf=yes, tcl_cv_ld_elf=no)])
+	    AS_IF([test $tcl_cv_ld_elf = yes], [
+		LDFLAGS=-Wl,-export-dynamic
+	    ], [LDFLAGS=""])
+	    AS_IF([test "${TCL_THREADS}" = "1"], [
+		# OpenBSD builds and links with -pthread, never -lpthread.
+		LIBS=`echo $LIBS | sed s/-lpthread//`
+		CFLAGS="$CFLAGS -pthread"
+		SHLIB_CFLAGS="$SHLIB_CFLAGS -pthread"
+	    ])
+	    # OpenBSD doesn't do version numbers with dots.
+	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    TCL_LIB_VERSIONS_OK=nodots
+	    ;;
+	NetBSD-*|FreeBSD-[[3-4]].*)
+	    # FreeBSD 3.* and greater have ELF.
+	    # NetBSD 2.* has ELF and can use 'cc -shared' to build shared libs
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS="$LDFLAGS -export-dynamic"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+	    LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    AS_IF([test "${TCL_THREADS}" = "1"], [
+		# The -pthread needs to go in the CFLAGS, not LIBS
+		LIBS=`echo $LIBS | sed s/-pthread//`
+		CFLAGS="$CFLAGS -pthread"
+	    	LDFLAGS="$LDFLAGS -pthread"
+	    ])
+	    case $system in
+	    FreeBSD-3.*)
+	    	# FreeBSD-3 doesn't handle version numbers with dots.
+	    	UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    	SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
+	    	TCL_LIB_VERSIONS_OK=nodots
+		;;
+	    esac
+	    ;;
+	FreeBSD-*)
+	    # This configuration from FreeBSD Ports.
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD="${CC} -shared"
+	    TCL_SHLIB_LD_EXTRAS="-soname \$[@]"
+	    SHLIB_SUFFIX=".so"
+	    LDFLAGS=""
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+	    AS_IF([test "${TCL_THREADS}" = "1"], [
+		# The -pthread needs to go in the LDFLAGS, not LIBS
+		LIBS=`echo $LIBS | sed s/-pthread//`
+		CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+		LDFLAGS="$LDFLAGS $PTHREAD_LIBS"])
+	    # Version numbers are dot-stripped by system policy.
+	    TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .`
+	    UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+	    SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1'
+	    TCL_LIB_VERSIONS_OK=nodots
+	    ;;
+	Darwin-*)
+	    CFLAGS_OPTIMIZE="-Os"
+	    SHLIB_CFLAGS="-fno-common"
+	    # To avoid discrepancies between what headers configure sees during
+	    # preprocessing tests and compiling tests, move any -isysroot and
+	    # -mmacosx-version-min flags from CFLAGS to CPPFLAGS:
+	    CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \
+		awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+		if ([$]i~/^(isysroot|mmacosx-version-min)/) print "-"[$]i}'`"
+	    CFLAGS="`echo " ${CFLAGS}" | \
+		awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+		if (!([$]i~/^(isysroot|mmacosx-version-min)/)) print "-"[$]i}'`"
+	    AS_IF([test $do64bit = yes], [
+		case `arch` in
+		    ppc)
+			AC_CACHE_CHECK([if compiler accepts -arch ppc64 flag],
+				tcl_cv_cc_arch_ppc64, [
+			    hold_cflags=$CFLAGS
+			    CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+			    AC_TRY_LINK(,, tcl_cv_cc_arch_ppc64=yes,
+				    tcl_cv_cc_arch_ppc64=no)
+			    CFLAGS=$hold_cflags])
+			AS_IF([test $tcl_cv_cc_arch_ppc64 = yes], [
+			    CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+			    do64bit_ok=yes
+			]);;
+		    i386)
+			AC_CACHE_CHECK([if compiler accepts -arch x86_64 flag],
+				tcl_cv_cc_arch_x86_64, [
+			    hold_cflags=$CFLAGS
+			    CFLAGS="$CFLAGS -arch x86_64"
+			    AC_TRY_LINK(,, tcl_cv_cc_arch_x86_64=yes,
+				    tcl_cv_cc_arch_x86_64=no)
+			    CFLAGS=$hold_cflags])
+			AS_IF([test $tcl_cv_cc_arch_x86_64 = yes], [
+			    CFLAGS="$CFLAGS -arch x86_64"
+			    do64bit_ok=yes
+			]);;
+		    *)
+			AC_MSG_WARN([Don't know how enable 64-bit on architecture `arch`]);;
+		esac
+	    ], [
+		# Check for combined 32-bit and 64-bit fat build
+		AS_IF([echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \
+		    && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '], [
+		    fat_32_64=yes])
+	    ])
+	    # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+	    SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}'
+	    AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [
+		hold_ldflags=$LDFLAGS
+		LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module"
+		AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no)
+		LDFLAGS=$hold_ldflags])
+	    AS_IF([test $tcl_cv_ld_single_module = yes], [
+		SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
+	    ])
+	    # TEA specific: link shlib with current and compatiblity version flags
+	    vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
+	    SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
+	    SHLIB_SUFFIX=".dylib"
+	    # Don't use -prebind when building for Mac OS X 10.4 or later only:
+	    AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \
+		"`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [
+		LDFLAGS="$LDFLAGS -prebind"])
+	    LDFLAGS="$LDFLAGS -headerpad_max_install_names"
+	    AC_CACHE_CHECK([if ld accepts -search_paths_first flag],
+		    tcl_cv_ld_search_paths_first, [
+		hold_ldflags=$LDFLAGS
+		LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+		AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes,
+			tcl_cv_ld_search_paths_first=no)
+		LDFLAGS=$hold_ldflags])
+	    AS_IF([test $tcl_cv_ld_search_paths_first = yes], [
+		LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+	    ])
+	    AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [
+		AC_DEFINE(MODULE_SCOPE, [__private_extern__],
+		    [Compiler support for module scope symbols])
+		tcl_cv_cc_visibility_hidden=yes
+	    ])
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH"
+	    # TEA specific: for combined 32 & 64 bit fat builds of Tk
+	    # extensions, verify that 64-bit build is possible.
+	    AS_IF([test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"], [
+		AS_IF([test "${TEA_WINDOWINGSYSTEM}" = x11], [
+		    AC_CACHE_CHECK([for 64-bit X11], tcl_cv_lib_x11_64, [
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+			done
+			CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include"
+			LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11"
+			AC_TRY_LINK([#include <X11/Xlib.h>], [XrmInitialize();],
+			    tcl_cv_lib_x11_64=yes, tcl_cv_lib_x11_64=no)
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval $v'="$hold_'$v'"'
+			done])
+		])
+		AS_IF([test "${TEA_WINDOWINGSYSTEM}" = aqua], [
+		    AC_CACHE_CHECK([for 64-bit Tk], tcl_cv_lib_tk_64, [
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+			done
+			CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}"
+			LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}"
+			AC_TRY_LINK([#include <tk.h>], [Tk_InitStubs(NULL, "", 0);],
+			    tcl_cv_lib_tk_64=yes, tcl_cv_lib_tk_64=no)
+			for v in CFLAGS CPPFLAGS LDFLAGS; do
+			    eval $v'="$hold_'$v'"'
+			done])
+		])
+		# remove 64-bit arch flags from CFLAGS et al. if configuration
+		# does not support 64-bit.
+		AS_IF([test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no], [
+		    AC_MSG_NOTICE([Removing 64-bit architectures from compiler & linker flags])
+		    for v in CFLAGS CPPFLAGS LDFLAGS; do
+			eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"'
+		    done])
+	    ])
+	    ;;
+	OS/390-*)
+	    CFLAGS_OPTIMIZE=""		# Optimizer is buggy
+	    AC_DEFINE(_OE_SOCKETS, 1,	# needed in sys/socket.h
+		[Should OS/390 do the right thing with sockets?])
+	    ;;
+	OSF1-V*)
+	    # Digital OSF/1
+	    SHLIB_CFLAGS=""
+	    AS_IF([test "$SHARED_BUILD" = 1], [
+	        SHLIB_LD='ld -shared -expect_unresolved "*"'
+	    ], [
+	        SHLIB_LD='ld -non_shared -expect_unresolved "*"'
+	    ])
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test $doRpath = yes], [
+		CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+	    AS_IF([test "$GCC" = yes], [CFLAGS="$CFLAGS -mieee"], [
+		CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"])
+	    # see pthread_intro(3) for pthread support on osf1, k.furukawa
+	    AS_IF([test "${TCL_THREADS}" = 1], [
+		CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
+		CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
+		LIBS=`echo $LIBS | sed s/-lpthreads//`
+		AS_IF([test "$GCC" = yes], [
+		    LIBS="$LIBS -lpthread -lmach -lexc"
+		], [
+		    CFLAGS="$CFLAGS -pthread"
+		    LDFLAGS="$LDFLAGS -pthread"
+		])
+	    ])
+	    ;;
+	QNX-6*)
+	    # QNX RTP
+	    # This may work for all QNX, but it was only reported for v6.
+	    SHLIB_CFLAGS="-fPIC"
+	    SHLIB_LD="ld -Bshareable -x"
+	    SHLIB_LD_LIBS=""
+	    SHLIB_SUFFIX=".so"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	SCO_SV-3.2*)
+	    AS_IF([test "$GCC" = yes], [
+		SHLIB_CFLAGS="-fPIC -melf"
+		LDFLAGS="$LDFLAGS -melf -Wl,-Bexport"
+	    ], [
+	       SHLIB_CFLAGS="-Kpic -belf"
+	       LDFLAGS="$LDFLAGS -belf -Wl,-Bexport"
+	    ])
+	    SHLIB_LD="ld -G"
+	    SHLIB_LD_LIBS=""
+	    SHLIB_SUFFIX=".so"
+	    CC_SEARCH_FLAGS=""
+	    LD_SEARCH_FLAGS=""
+	    ;;
+	SunOS-5.[[0-6]])
+	    # Careful to not let 5.10+ fall into this case
+
+	    # Note: If _REENTRANT isn't defined, then Solaris
+	    # won't define thread-safe library routines.
+
+	    AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+	    AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+		[Do we really want to follow the standard? Yes we do!])
+
+	    SHLIB_CFLAGS="-KPIC"
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test "$GCC" = yes], [
+		SHLIB_LD='${CC} -shared'
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    ], [
+		SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+		CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+	    ])
+	    ;;
+	SunOS-5*)
+	    # Note: If _REENTRANT isn't defined, then Solaris
+	    # won't define thread-safe library routines.
+
+	    AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+	    AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+		[Do we really want to follow the standard? Yes we do!])
+
+	    SHLIB_CFLAGS="-KPIC"
+
+	    # Check to enable 64-bit flags for compiler/linker
+	    AS_IF([test "$do64bit" = yes], [
+		arch=`isainfo`
+		AS_IF([test "$arch" = "sparcv9 sparc"], [
+		    AS_IF([test "$GCC" = yes], [
+			AS_IF([test "`${CC} -dumpversion | awk -F. '{print [$]1}'`" -lt 3], [
+			    AC_MSG_WARN([64bit mode not supported with GCC < 3.2 on $system])
+			], [
+			    do64bit_ok=yes
+			    CFLAGS="$CFLAGS -m64 -mcpu=v9"
+			    LDFLAGS="$LDFLAGS -m64 -mcpu=v9"
+			    SHLIB_CFLAGS="-fPIC"
+			])
+		    ], [
+			do64bit_ok=yes
+			AS_IF([test "$do64bitVIS" = yes], [
+			    CFLAGS="$CFLAGS -xarch=v9a"
+			    LDFLAGS_ARCH="-xarch=v9a"
+			], [
+			    CFLAGS="$CFLAGS -xarch=v9"
+			    LDFLAGS_ARCH="-xarch=v9"
+			])
+			# Solaris 64 uses this as well
+			#LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64"
+		    ])
+		], [AS_IF([test "$arch" = "amd64 i386"], [
+		    AS_IF([test "$GCC" = yes], [
+			case $system in
+			    SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*)
+				do64bit_ok=yes
+				CFLAGS="$CFLAGS -m64"
+				LDFLAGS="$LDFLAGS -m64";;
+			    *)
+				AC_MSG_WARN([64bit mode not supported with GCC on $system]);;
+			esac
+		    ], [
+			do64bit_ok=yes
+			case $system in
+			    SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*)
+				CFLAGS="$CFLAGS -m64"
+				LDFLAGS="$LDFLAGS -m64";;
+			    *)
+				CFLAGS="$CFLAGS -xarch=amd64"
+				LDFLAGS="$LDFLAGS -xarch=amd64";;
+			esac
+		    ])
+		], [AC_MSG_WARN([64bit mode not supported for $arch])])])
+	    ])
+
+	    SHLIB_SUFFIX=".so"
+	    AS_IF([test "$GCC" = yes], [
+		SHLIB_LD='${CC} -shared'
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+		AS_IF([test "$do64bit_ok" = yes], [
+		    AS_IF([test "$arch" = "sparcv9 sparc"], [
+			# We need to specify -static-libgcc or we need to
+			# add the path to the sparv9 libgcc.
+			# JH: static-libgcc is necessary for core Tcl, but may
+			# not be necessary for extensions.
+			SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc"
+			# for finding sparcv9 libgcc, get the regular libgcc
+			# path, remove so name and append 'sparcv9'
+			#v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..."
+			#CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir"
+		    ], [AS_IF([test "$arch" = "amd64 i386"], [
+			# JH: static-libgcc is necessary for core Tcl, but may
+			# not be necessary for extensions.
+			SHLIB_LD="$SHLIB_LD -m64 -static-libgcc"
+		    ])])
+		])
+	    ], [
+		case $system in
+		    SunOS-5.[[1-9]][[0-9]]*)
+			# TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+			SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';;
+		    *)
+			SHLIB_LD='/usr/ccs/bin/ld -G -z text';;
+		esac
+		CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+		LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+	    ])
+	    ;;
+    esac
+
+    AS_IF([test "$do64bit" = yes -a "$do64bit_ok" = no], [
+	AC_MSG_WARN([64bit support being disabled -- don't know magic for this platform])
+    ])
+
+dnl # Add any CPPFLAGS set in the environment to our CFLAGS, but delay doing so
+dnl # until the end of configure, as configure's compile and link tests use
+dnl # both CPPFLAGS and CFLAGS (unlike our compile and link) but configure's
+dnl # preprocessing tests use only CPPFLAGS.
+    AC_CONFIG_COMMANDS_PRE([CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS=""])
+
+    # Add in the arch flags late to ensure it wasn't removed.
+    # Not necessary in TEA, but this is aligned with core
+    LDFLAGS="$LDFLAGS $LDFLAGS_ARCH"
+
+    # If we're running gcc, then change the C flags for compiling shared
+    # libraries to the right flags for gcc, instead of those for the
+    # standard manufacturer compiler.
+
+    AS_IF([test "$GCC" = yes], [
+	case $system in
+	    AIX-*) ;;
+	    BSD/OS*) ;;
+	    CYGWIN_*) ;;
+	    IRIX*) ;;
+	    NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
+	    Darwin-*) ;;
+	    SCO_SV-3.2*) ;;
+	    windows) ;;
+	    *) SHLIB_CFLAGS="-fPIC" ;;
+	esac])
+
+    AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [
+	AC_DEFINE(MODULE_SCOPE, [extern],
+	    [No Compiler support for module scope symbols])
+	AC_DEFINE(NO_VIZ, [], [No visibility hidden passed to zlib?])
+    ])
+
+    AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [
+	# TEA specific: use PACKAGE_VERSION instead of VERSION
+	SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'])
+    AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [
+	# TEA specific: use PACKAGE_VERSION instead of VERSION
+	UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'])
+
+    AC_SUBST(CFLAGS_DEBUG)
+    AC_SUBST(CFLAGS_OPTIMIZE)
+    AC_SUBST(CFLAGS_WARNING)
+
+    AC_SUBST(STLIB_LD)
+    AC_SUBST(SHLIB_LD)
+
+    AC_SUBST(SHLIB_LD_LIBS)
+    AC_SUBST(SHLIB_CFLAGS)
+
+    AC_SUBST(LD_LIBRARY_PATH_VAR)
+
+    # These must be called after we do the basic CFLAGS checks and
+    # verify any possible 64-bit or similar switches are necessary
+    TEA_TCL_EARLY_FLAGS
+    TEA_TCL_64BIT_FLAGS
+])
+
+#--------------------------------------------------------------------
+# TEA_SERIAL_PORT
+#
+#	Determine which interface to use to talk to the serial port.
+#	Note that #include lines must begin in leftmost column for
+#	some compilers to recognize them as preprocessor directives,
+#	and some build environments have stdin not pointing at a
+#	pseudo-terminal (usually /dev/null instead.)
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines only one of the following vars:
+#		HAVE_SYS_MODEM_H
+#		USE_TERMIOS
+#		USE_TERMIO
+#		USE_SGTTY
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_SERIAL_PORT], [
+    AC_CHECK_HEADERS(sys/modem.h)
+    AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [
+    AC_TRY_RUN([
+#include <termios.h>
+
+int main() {
+    struct termios t;
+    if (tcgetattr(0, &t) == 0) {
+	cfsetospeed(&t, 0);
+	t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+	return 0;
+    }
+    return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+    if test $tcl_cv_api_serial = no ; then
+	AC_TRY_RUN([
+#include <termio.h>
+
+int main() {
+    struct termio t;
+    if (ioctl(0, TCGETA, &t) == 0) {
+	t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+	return 0;
+    }
+    return 1;
+}], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+    fi
+    if test $tcl_cv_api_serial = no ; then
+	AC_TRY_RUN([
+#include <sgtty.h>
+
+int main() {
+    struct sgttyb t;
+    if (ioctl(0, TIOCGETP, &t) == 0) {
+	t.sg_ospeed = 0;
+	t.sg_flags |= ODDP | EVENP | RAW;
+	return 0;
+    }
+    return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+    fi
+    if test $tcl_cv_api_serial = no ; then
+	AC_TRY_RUN([
+#include <termios.h>
+#include <errno.h>
+
+int main() {
+    struct termios t;
+    if (tcgetattr(0, &t) == 0
+	|| errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+	cfsetospeed(&t, 0);
+	t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+	return 0;
+    }
+    return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+    fi
+    if test $tcl_cv_api_serial = no; then
+	AC_TRY_RUN([
+#include <termio.h>
+#include <errno.h>
+
+int main() {
+    struct termio t;
+    if (ioctl(0, TCGETA, &t) == 0
+	|| errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+	t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+	return 0;
+    }
+    return 1;
+    }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+    fi
+    if test $tcl_cv_api_serial = no; then
+	AC_TRY_RUN([
+#include <sgtty.h>
+#include <errno.h>
+
+int main() {
+    struct sgttyb t;
+    if (ioctl(0, TIOCGETP, &t) == 0
+	|| errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+	t.sg_ospeed = 0;
+	t.sg_flags |= ODDP | EVENP | RAW;
+	return 0;
+    }
+    return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none)
+    fi])
+    case $tcl_cv_api_serial in
+	termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);;
+	termio)  AC_DEFINE(USE_TERMIO, 1, [Use the termio API for serial lines]);;
+	sgtty)   AC_DEFINE(USE_SGTTY, 1, [Use the sgtty API for serial lines]);;
+    esac
+])
+
+#--------------------------------------------------------------------
+# TEA_MISSING_POSIX_HEADERS
+#
+#	Supply substitutes for missing POSIX header files.  Special
+#	notes:
+#	    - stdlib.h doesn't define strtol, strtoul, or
+#	      strtod in some versions of SunOS
+#	    - some versions of string.h don't declare procedures such
+#	      as strstr
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines some of the following vars:
+#		NO_DIRENT_H
+#		NO_ERRNO_H
+#		NO_VALUES_H
+#		HAVE_LIMITS_H or NO_LIMITS_H
+#		NO_STDLIB_H
+#		NO_STRING_H
+#		NO_SYS_WAIT_H
+#		NO_DLFCN_H
+#		HAVE_SYS_PARAM_H
+#
+#		HAVE_STRING_H ?
+#
+# tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
+# CHECK on limits.h
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [
+    AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [
+    AC_TRY_LINK([#include <sys/types.h>
+#include <dirent.h>], [
+#ifndef _POSIX_SOURCE
+#   ifdef __Lynx__
+	/*
+	 * Generate compilation error to make the test fail:  Lynx headers
+	 * are only valid if really in the POSIX environment.
+	 */
+
+	missing_procedure();
+#   endif
+#endif
+DIR *d;
+struct dirent *entryPtr;
+char *p;
+d = opendir("foobar");
+entryPtr = readdir(d);
+p = entryPtr->d_name;
+closedir(d);
+], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)])
+
+    if test $tcl_cv_dirent_h = no; then
+	AC_DEFINE(NO_DIRENT_H, 1, [Do we have <dirent.h>?])
+    fi
+
+    # TEA specific:
+    AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have <errno.h>?])])
+    AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have <float.h>?])])
+    AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have <values.h>?])])
+    AC_CHECK_HEADER(limits.h,
+	[AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have <limits.h>?])],
+	[AC_DEFINE(NO_LIMITS_H, 1, [Do we have <limits.h>?])])
+    AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
+    AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
+    AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
+    AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
+    if test $tcl_ok = 0; then
+	AC_DEFINE(NO_STDLIB_H, 1, [Do we have <stdlib.h>?])
+    fi
+    AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
+    AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
+    AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
+
+    # See also memmove check below for a place where NO_STRING_H can be
+    # set and why.
+
+    if test $tcl_ok = 0; then
+	AC_DEFINE(NO_STRING_H, 1, [Do we have <string.h>?])
+    fi
+
+    AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have <sys/wait.h>?])])
+    AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have <dlfcn.h>?])])
+
+    # OS/390 lacks sys/param.h (and doesn't need it, by chance).
+    AC_HAVE_HEADERS(sys/param.h)
+])
+
+#--------------------------------------------------------------------
+# TEA_PATH_X
+#
+#	Locate the X11 header files and the X11 library archive.  Try
+#	the ac_path_x macro first, but if it doesn't find the X stuff
+#	(e.g. because there's no xmkmf program) then check through
+#	a list of possible directories.  Under some conditions the
+#	autoconf macro will return an include directory that contains
+#	no include files, so double-check its result just to be safe.
+#
+#	This should be called after TEA_CONFIG_CFLAGS as setting the
+#	LIBS line can confuse some configure macro magic.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Sets the following vars:
+#		XINCLUDES
+#		XLIBSW
+#		PKG_LIBS (appends to)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_X], [
+    if test "${TEA_WINDOWINGSYSTEM}" = "x11" ; then
+	TEA_PATH_UNIX_X
+    fi
+])
+
+AC_DEFUN([TEA_PATH_UNIX_X], [
+    AC_PATH_X
+    not_really_there=""
+    if test "$no_x" = ""; then
+	if test "$x_includes" = ""; then
+	    AC_TRY_CPP([#include <X11/XIntrinsic.h>], , not_really_there="yes")
+	else
+	    if test ! -r $x_includes/X11/Intrinsic.h; then
+		not_really_there="yes"
+	    fi
+	fi
+    fi
+    if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
+	AC_MSG_CHECKING([for X11 header files])
+	found_xincludes="no"
+	AC_TRY_CPP([#include <X11/Intrinsic.h>], found_xincludes="yes", found_xincludes="no")
+	if test "$found_xincludes" = "no"; then
+	    dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
+	    for i in $dirs ; do
+		if test -r $i/X11/Intrinsic.h; then
+		    AC_MSG_RESULT([$i])
+		    XINCLUDES=" -I$i"
+		    found_xincludes="yes"
+		    break
+		fi
+	    done
+	fi
+    else
+	if test "$x_includes" != ""; then
+	    XINCLUDES="-I$x_includes"
+	    found_xincludes="yes"
+	fi
+    fi
+    if test "$found_xincludes" = "no"; then
+	AC_MSG_RESULT([couldn't find any!])
+    fi
+
+    if test "$no_x" = yes; then
+	AC_MSG_CHECKING([for X11 libraries])
+	XLIBSW=nope
+	dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib"
+	for i in $dirs ; do
+	    if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl -o -r $i/libX11.dylib; then
+		AC_MSG_RESULT([$i])
+		XLIBSW="-L$i -lX11"
+		x_libraries="$i"
+		break
+	    fi
+	done
+    else
+	if test "$x_libraries" = ""; then
+	    XLIBSW=-lX11
+	else
+	    XLIBSW="-L$x_libraries -lX11"
+	fi
+    fi
+    if test "$XLIBSW" = nope ; then
+	AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow)
+    fi
+    if test "$XLIBSW" = nope ; then
+	AC_MSG_RESULT([could not find any!  Using -lX11.])
+	XLIBSW=-lX11
+    fi
+    # TEA specific:
+    if test x"${XLIBSW}" != x ; then
+	PKG_LIBS="${PKG_LIBS} ${XLIBSW}"
+    fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BLOCKING_STYLE
+#
+#	The statements below check for systems where POSIX-style
+#	non-blocking I/O (O_NONBLOCK) doesn't work or is unimplemented.
+#	On these systems (mostly older ones), use the old BSD-style
+#	FIONBIO approach instead.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines some of the following vars:
+#		HAVE_SYS_IOCTL_H
+#		HAVE_SYS_FILIO_H
+#		USE_FIONBIO
+#		O_NONBLOCK
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BLOCKING_STYLE], [
+    AC_CHECK_HEADERS(sys/ioctl.h)
+    AC_CHECK_HEADERS(sys/filio.h)
+    TEA_CONFIG_SYSTEM
+    AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O])
+    case $system in
+	OSF*)
+	    AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?])
+	    AC_MSG_RESULT([FIONBIO])
+	    ;;
+	*)
+	    AC_MSG_RESULT([O_NONBLOCK])
+	    ;;
+    esac
+])
+
+#--------------------------------------------------------------------
+# TEA_TIME_HANDLER
+#
+#	Checks how the system deals with time.h, what time structures
+#	are used on the system, and what fields the structures have.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines some of the following vars:
+#		USE_DELTA_FOR_TZ
+#		HAVE_TM_GMTOFF
+#		HAVE_TM_TZADJ
+#		HAVE_TIMEZONE_VAR
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TIME_HANDLER], [
+    AC_CHECK_HEADERS(sys/time.h)
+    AC_HEADER_TIME
+    AC_STRUCT_TIMEZONE
+
+    AC_CHECK_FUNCS(gmtime_r localtime_r)
+
+    AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj, [
+	AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_tzadj;],
+	    tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)])
+    if test $tcl_cv_member_tm_tzadj = yes ; then
+	AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?])
+    fi
+
+    AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff, [
+	AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_gmtoff;],
+	    tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)])
+    if test $tcl_cv_member_tm_gmtoff = yes ; then
+	AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?])
+    fi
+
+    #
+    # Its important to include time.h in this check, as some systems
+    # (like convex) have timezone functions, etc.
+    #
+    AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long, [
+	AC_TRY_COMPILE([#include <time.h>],
+	    [extern long timezone;
+	    timezone += 1;
+	    exit (0);],
+	    tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)])
+    if test $tcl_cv_timezone_long = yes ; then
+	AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+    else
+	#
+	# On some systems (eg IRIX 6.2), timezone is a time_t and not a long.
+	#
+	AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time, [
+	    AC_TRY_COMPILE([#include <time.h>],
+		[extern time_t timezone;
+		timezone += 1;
+		exit (0);],
+		tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)])
+	if test $tcl_cv_timezone_time = yes ; then
+	    AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+	fi
+    fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BUGGY_STRTOD
+#
+#	Under Solaris 2.4, strtod returns the wrong value for the
+#	terminating character under some conditions.  Check for this
+#	and if the problem exists use a substitute procedure
+#	"fixstrtod" (provided by Tcl) that corrects the error.
+#	Also, on Compaq's Tru64 Unix 5.0,
+#	strtod(" ") returns 0.0 instead of a failure to convert.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Might defines some of the following vars:
+#		strtod (=fixstrtod)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BUGGY_STRTOD], [
+    AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0)
+    if test "$tcl_strtod" = 1; then
+	AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[
+	    AC_TRY_RUN([
+		extern double strtod();
+		int main() {
+		    char *infString="Inf", *nanString="NaN", *spaceString=" ";
+		    char *term;
+		    double value;
+		    value = strtod(infString, &term);
+		    if ((term != infString) && (term[-1] == 0)) {
+			exit(1);
+		    }
+		    value = strtod(nanString, &term);
+		    if ((term != nanString) && (term[-1] == 0)) {
+			exit(1);
+		    }
+		    value = strtod(spaceString, &term);
+		    if (term == (spaceString+1)) {
+			exit(1);
+		    }
+		    exit(0);
+		}], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy,
+		    tcl_cv_strtod_buggy=buggy)])
+	if test "$tcl_cv_strtod_buggy" = buggy; then
+	    AC_LIBOBJ([fixstrtod])
+	    USE_COMPAT=1
+	    AC_DEFINE(strtod, fixstrtod, [Do we want to use the strtod() in compat?])
+	fi
+    fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_LINK_LIBS
+#
+#	Search for the libraries needed to link the Tcl shell.
+#	Things like the math library (-lm) and socket stuff (-lsocket vs.
+#	-lnsl) are dealt with here.
+#
+# Arguments:
+#	Requires the following vars to be set in the Makefile:
+#		DL_LIBS (not in TEA, only needed in core)
+#		LIBS
+#		MATH_LIBS
+#
+# Results:
+#
+#	Subst's the following var:
+#		TCL_LIBS
+#		MATH_LIBS
+#
+#	Might append to the following vars:
+#		LIBS
+#
+#	Might define the following vars:
+#		HAVE_NET_ERRNO_H
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_LINK_LIBS], [
+    #--------------------------------------------------------------------
+    # On a few very rare systems, all of the libm.a stuff is
+    # already in libc.a.  Set compiler flags accordingly.
+    # Also, Linux requires the "ieee" library for math to work
+    # right (and it must appear before "-lm").
+    #--------------------------------------------------------------------
+
+    AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
+    AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+    #--------------------------------------------------------------------
+    # Interactive UNIX requires -linet instead of -lsocket, plus it
+    # needs net/errno.h to define the socket-related error codes.
+    #--------------------------------------------------------------------
+
+    AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"])
+    AC_CHECK_HEADER(net/errno.h, [
+	AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have <net/errno.h>?])])
+
+    #--------------------------------------------------------------------
+    #	Check for the existence of the -lsocket and -lnsl libraries.
+    #	The order here is important, so that they end up in the right
+    #	order in the command line generated by make.  Here are some
+    #	special considerations:
+    #	1. Use "connect" and "accept" to check for -lsocket, and
+    #	   "gethostbyname" to check for -lnsl.
+    #	2. Use each function name only once:  can't redo a check because
+    #	   autoconf caches the results of the last check and won't redo it.
+    #	3. Use -lnsl and -lsocket only if they supply procedures that
+    #	   aren't already present in the normal libraries.  This is because
+    #	   IRIX 5.2 has libraries, but they aren't needed and they're
+    #	   bogus:  they goof up name resolution if used.
+    #	4. On some SVR4 systems, can't use -lsocket without -lnsl too.
+    #	   To get around this problem, check for both libraries together
+    #	   if -lsocket doesn't work by itself.
+    #--------------------------------------------------------------------
+
+    tcl_checkBoth=0
+    AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1)
+    if test "$tcl_checkSocket" = 1; then
+	AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt,
+	    LIBS="$LIBS -lsocket", tcl_checkBoth=1)])
+    fi
+    if test "$tcl_checkBoth" = 1; then
+	tk_oldLibs=$LIBS
+	LIBS="$LIBS -lsocket -lnsl"
+	AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs])
+    fi
+    AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname,
+	    [LIBS="$LIBS -lnsl"])])
+
+    # TEA specific: Don't perform the eval of the libraries here because
+    # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS
+
+    TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
+    AC_SUBST(TCL_LIBS)
+    AC_SUBST(MATH_LIBS)
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_EARLY_FLAGS
+#
+#	Check for what flags are needed to be passed so the correct OS
+#	features are available.
+#
+# Arguments:
+#	None
+#
+# Results:
+#
+#	Might define the following vars:
+#		_ISOC99_SOURCE
+#		_LARGEFILE64_SOURCE
+#		_LARGEFILE_SOURCE64
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_EARLY_FLAG],[
+    AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]),
+	AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no,
+	    AC_TRY_COMPILE([[#define ]$1[ 1
+]$2], $3,
+		[tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes,
+		[tcl_cv_flag_]translit($1,[A-Z],[a-z])=no)))
+    if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then
+	AC_DEFINE($1, 1, [Add the ]$1[ flag when building])
+	tcl_flags="$tcl_flags $1"
+    fi
+])
+
+AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
+    AC_MSG_CHECKING([for required early compiler flags])
+    tcl_flags=""
+    TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include <stdlib.h>],
+	[char *p = (char *)strtoll; char *q = (char *)strtoull;])
+    TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include <sys/stat.h>],
+	[struct stat64 buf; int i = stat64("/", &buf);])
+    TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include <sys/stat.h>],
+	[char *p = (char *)open64;])
+    if test "x${tcl_flags}" = "x" ; then
+	AC_MSG_RESULT([none])
+    else
+	AC_MSG_RESULT([${tcl_flags}])
+    fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_64BIT_FLAGS
+#
+#	Check for what is defined in the way of 64-bit features.
+#
+# Arguments:
+#	None
+#
+# Results:
+#
+#	Might define the following vars:
+#		TCL_WIDE_INT_IS_LONG
+#		TCL_WIDE_INT_TYPE
+#		HAVE_STRUCT_DIRENT64
+#		HAVE_STRUCT_STAT64
+#		HAVE_TYPE_OFF64_T
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
+    AC_MSG_CHECKING([for 64-bit integer type])
+    AC_CACHE_VAL(tcl_cv_type_64bit,[
+	tcl_cv_type_64bit=none
+	# See if the compiler knows natively about __int64
+	AC_TRY_COMPILE(,[__int64 value = (__int64) 0;],
+	    tcl_type_64bit=__int64, tcl_type_64bit="long long")
+	# See if we should use long anyway  Note that we substitute in the
+	# type that is our current guess for a 64-bit type inside this check
+	# program, so it should be modified only carefully...
+        AC_TRY_COMPILE(,[switch (0) {
+            case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ;
+        }],tcl_cv_type_64bit=${tcl_type_64bit})])
+    if test "${tcl_cv_type_64bit}" = none ; then
+	AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?])
+	AC_MSG_RESULT([using long])
+    elif test "${tcl_cv_type_64bit}" = "__int64" \
+		-a "${TEA_PLATFORM}" = "windows" ; then
+	# TEA specific: We actually want to use the default tcl.h checks in
+	# this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER*
+	AC_MSG_RESULT([using Tcl header defaults])
+    else
+	AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit},
+	    [What type should be used to define wide integers?])
+	AC_MSG_RESULT([${tcl_cv_type_64bit}])
+
+	# Now check for auxiliary declarations
+	AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[
+	    AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/dirent.h>],[struct dirent64 p;],
+		tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)])
+	if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
+	    AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in <sys/types.h>?])
+	fi
+
+	AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[
+	    AC_TRY_COMPILE([#include <sys/stat.h>],[struct stat64 p;
+],
+		tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)])
+	if test "x${tcl_cv_struct_stat64}" = "xyes" ; then
+	    AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in <sys/stat.h>?])
+	fi
+
+	AC_CHECK_FUNCS(open64 lseek64)
+	AC_MSG_CHECKING([for off64_t])
+	AC_CACHE_VAL(tcl_cv_type_off64_t,[
+	    AC_TRY_COMPILE([#include <sys/types.h>],[off64_t offset;
+],
+		tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)])
+	dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the
+	dnl functions lseek64 and open64 are defined.
+	if test "x${tcl_cv_type_off64_t}" = "xyes" && \
+	        test "x${ac_cv_func_lseek64}" = "xyes" && \
+	        test "x${ac_cv_func_open64}" = "xyes" ; then
+	    AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in <sys/types.h>?])
+	    AC_MSG_RESULT([yes])
+	else
+	    AC_MSG_RESULT([no])
+	fi
+    fi
+])
+
+##
+## Here ends the standard Tcl configuration bits and starts the
+## TEA specific functions
+##
+
+#------------------------------------------------------------------------
+# TEA_INIT --
+#
+#	Init various Tcl Extension Architecture (TEA) variables.
+#	This should be the first called TEA_* macro.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		CYGPATH
+#		EXEEXT
+#	Defines only:
+#		TEA_VERSION
+#		TEA_INITED
+#		TEA_PLATFORM (windows or unix)
+#
+# "cygpath" is used on windows to generate native path names for include
+# files. These variables should only be used with the compiler and linker
+# since they generate native path names.
+#
+# EXEEXT
+#	Select the executable extension based on the host type.  This
+#	is a lightweight replacement for AC_EXEEXT that doesn't require
+#	a compiler.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_INIT], [
+    # TEA extensions pass this us the version of TEA they think they
+    # are compatible with.
+    TEA_VERSION="3.9"
+
+    AC_MSG_CHECKING([for correct TEA configuration])
+    if test x"${PACKAGE_NAME}" = x ; then
+	AC_MSG_ERROR([
+The PACKAGE_NAME variable must be defined by your TEA configure.in])
+    fi
+    if test x"$1" = x ; then
+	AC_MSG_ERROR([
+TEA version not specified.])
+    elif test "$1" != "${TEA_VERSION}" ; then
+	AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"])
+    else
+	AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
+    fi
+    case "`uname -s`" in
+	*win32*|*WIN32*|*MINGW32_*)
+	    AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
+	    EXEEXT=".exe"
+	    TEA_PLATFORM="windows"
+	    ;;
+	*CYGWIN_*)
+	    CYGPATH=echo
+	    EXEEXT=".exe"
+	    # TEA_PLATFORM is determined later in LOAD_TCLCONFIG
+	    ;;
+	*)
+	    CYGPATH=echo
+	    # Maybe we are cross-compiling....
+	    case ${host_alias} in
+		*mingw32*)
+		EXEEXT=".exe"
+		TEA_PLATFORM="windows"
+		;;
+	    *)
+		EXEEXT=""
+		TEA_PLATFORM="unix"
+		;;
+	    esac
+	    ;;
+    esac
+
+    # Check if exec_prefix is set. If not use fall back to prefix.
+    # Note when adjusted, so that TEA_PREFIX can correct for this.
+    # This is needed for recursive configures, since autoconf propagates
+    # $prefix, but not $exec_prefix (doh!).
+    if test x$exec_prefix = xNONE ; then
+	exec_prefix_default=yes
+	exec_prefix=$prefix
+    fi
+
+    AC_MSG_NOTICE([configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}])
+
+    AC_SUBST(EXEEXT)
+    AC_SUBST(CYGPATH)
+
+    # This package name must be replaced statically for AC_SUBST to work
+    AC_SUBST(PKG_LIB_FILE)
+    # Substitute STUB_LIB_FILE in case package creates a stub library too.
+    AC_SUBST(PKG_STUB_LIB_FILE)
+
+    # We AC_SUBST these here to ensure they are subst'ed,
+    # in case the user doesn't call TEA_ADD_...
+    AC_SUBST(PKG_STUB_SOURCES)
+    AC_SUBST(PKG_STUB_OBJECTS)
+    AC_SUBST(PKG_TCL_SOURCES)
+    AC_SUBST(PKG_HEADERS)
+    AC_SUBST(PKG_INCLUDES)
+    AC_SUBST(PKG_LIBS)
+    AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_SOURCES --
+#
+#	Specify one or more source files.  Users should check for
+#	the right platform before adding to their list.
+#	It is not important to specify the directory, as long as it is
+#	in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_SOURCES
+#		PKG_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_SOURCES], [
+    vars="$@"
+    for i in $vars; do
+	case $i in
+	    [\$]*)
+		# allow $-var names
+		PKG_SOURCES="$PKG_SOURCES $i"
+		PKG_OBJECTS="$PKG_OBJECTS $i"
+		;;
+	    *)
+		# check for existence - allows for generic/win/unix VPATH
+		# To add more dirs here (like 'src'), you have to update VPATH
+		# in Makefile.in as well
+		if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+		    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+		    -a ! -f "${srcdir}/macosx/$i" \
+		    ; then
+		    AC_MSG_ERROR([could not find source file '$i'])
+		fi
+		PKG_SOURCES="$PKG_SOURCES $i"
+		# this assumes it is in a VPATH dir
+		i=`basename $i`
+		# handle user calling this before or after TEA_SETUP_COMPILER
+		if test x"${OBJEXT}" != x ; then
+		    j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+		else
+		    j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+		fi
+		PKG_OBJECTS="$PKG_OBJECTS $j"
+		;;
+	esac
+    done
+    AC_SUBST(PKG_SOURCES)
+    AC_SUBST(PKG_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_STUB_SOURCES --
+#
+#	Specify one or more source files.  Users should check for
+#	the right platform before adding to their list.
+#	It is not important to specify the directory, as long as it is
+#	in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_STUB_SOURCES
+#		PKG_STUB_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_STUB_SOURCES], [
+    vars="$@"
+    for i in $vars; do
+	# check for existence - allows for generic/win/unix VPATH
+	if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+	    -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+	    -a ! -f "${srcdir}/macosx/$i" \
+	    ; then
+	    AC_MSG_ERROR([could not find stub source file '$i'])
+	fi
+	PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i"
+	# this assumes it is in a VPATH dir
+	i=`basename $i`
+	# handle user calling this before or after TEA_SETUP_COMPILER
+	if test x"${OBJEXT}" != x ; then
+	    j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+	else
+	    j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+	fi
+	PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j"
+    done
+    AC_SUBST(PKG_STUB_SOURCES)
+    AC_SUBST(PKG_STUB_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_TCL_SOURCES --
+#
+#	Specify one or more Tcl source files.  These should be platform
+#	independent runtime files.
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_TCL_SOURCES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_TCL_SOURCES], [
+    vars="$@"
+    for i in $vars; do
+	# check for existence, be strict because it is installed
+	if test ! -f "${srcdir}/$i" ; then
+	    AC_MSG_ERROR([could not find tcl source file '${srcdir}/$i'])
+	fi
+	PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i"
+    done
+    AC_SUBST(PKG_TCL_SOURCES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_HEADERS --
+#
+#	Specify one or more source headers.  Users should check for
+#	the right platform before adding to their list.
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_HEADERS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_HEADERS], [
+    vars="$@"
+    for i in $vars; do
+	# check for existence, be strict because it is installed
+	if test ! -f "${srcdir}/$i" ; then
+	    AC_MSG_ERROR([could not find header file '${srcdir}/$i'])
+	fi
+	PKG_HEADERS="$PKG_HEADERS $i"
+    done
+    AC_SUBST(PKG_HEADERS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_INCLUDES --
+#
+#	Specify one or more include dirs.  Users should check for
+#	the right platform before adding to their list.
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_INCLUDES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_INCLUDES], [
+    vars="$@"
+    for i in $vars; do
+	PKG_INCLUDES="$PKG_INCLUDES $i"
+    done
+    AC_SUBST(PKG_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_LIBS --
+#
+#	Specify one or more libraries.  Users should check for
+#	the right platform before adding to their list.  For Windows,
+#	libraries provided in "foo.lib" format will be converted to
+#	"-lfoo" when using GCC (mingw).
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_LIBS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_LIBS], [
+    vars="$@"
+    for i in $vars; do
+	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
+	    # Convert foo.lib to -lfoo for GCC.  No-op if not *.lib
+	    i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'`
+	fi
+	PKG_LIBS="$PKG_LIBS $i"
+    done
+    AC_SUBST(PKG_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_CFLAGS --
+#
+#	Specify one or more CFLAGS.  Users should check for
+#	the right platform before adding to their list.
+#
+# Arguments:
+#	one or more file names
+#
+# Results:
+#
+#	Defines and substs the following vars:
+#		PKG_CFLAGS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_CFLAGS], [
+    PKG_CFLAGS="$PKG_CFLAGS $@"
+    AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_CLEANFILES --
+#
+#	Specify one or more CLEANFILES.
+#
+# Arguments:
+#	one or more file names to clean target
+#
+# Results:
+#
+#	Appends to CLEANFILES, already defined for subst in LOAD_TCLCONFIG
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_CLEANFILES], [
+    CLEANFILES="$CLEANFILES $@"
+])
+
+#------------------------------------------------------------------------
+# TEA_PREFIX --
+#
+#	Handle the --prefix=... option by defaulting to what Tcl gave
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	If --prefix or --exec-prefix was not specified, $prefix and
+#	$exec_prefix will be set to the values given to Tcl when it was
+#	configured.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_PREFIX], [
+    if test "${prefix}" = "NONE"; then
+	prefix_default=yes
+	if test x"${TCL_PREFIX}" != x; then
+	    AC_MSG_NOTICE([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}])
+	    prefix=${TCL_PREFIX}
+	else
+	    AC_MSG_NOTICE([--prefix defaulting to /usr/local])
+	    prefix=/usr/local
+	fi
+    fi
+    if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \
+	-o x"${exec_prefix_default}" = x"yes" ; then
+	if test x"${TCL_EXEC_PREFIX}" != x; then
+	    AC_MSG_NOTICE([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}])
+	    exec_prefix=${TCL_EXEC_PREFIX}
+	else
+	    AC_MSG_NOTICE([--exec-prefix defaulting to ${prefix}])
+	    exec_prefix=$prefix
+	fi
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER_CC --
+#
+#	Do compiler checks the way we want.  This is just a replacement
+#	for AC_PROG_CC in TEA configure.in files to make them cleaner.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER_CC], [
+    # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE)
+    # in this macro, they need to go into TEA_SETUP_COMPILER instead.
+
+    # If the user did not set CFLAGS, set it now to keep
+    # the AC_PROG_CC macro from adding "-g -O2".
+    if test "${CFLAGS+set}" != "set" ; then
+	CFLAGS=""
+    fi
+
+    AC_PROG_CC
+    AC_PROG_CPP
+
+    AC_PROG_INSTALL
+
+    #--------------------------------------------------------------------
+    # Checks to see if the make program sets the $MAKE variable.
+    #--------------------------------------------------------------------
+
+    AC_PROG_MAKE_SET
+
+    #--------------------------------------------------------------------
+    # Find ranlib
+    #--------------------------------------------------------------------
+
+    AC_CHECK_TOOL(RANLIB, ranlib)
+
+    #--------------------------------------------------------------------
+    # Determines the correct binary file extension (.o, .obj, .exe etc.)
+    #--------------------------------------------------------------------
+
+    AC_OBJEXT
+    AC_EXEEXT
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER --
+#
+#	Do compiler checks that use the compiler.  This must go after
+#	TEA_SETUP_COMPILER_CC, which does the actual compiler check.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER], [
+    # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here.
+    AC_REQUIRE([TEA_SETUP_COMPILER_CC])
+
+    #------------------------------------------------------------------------
+    # If we're using GCC, see if the compiler understands -pipe. If so, use it.
+    # It makes compiling go faster.  (This is only a performance feature.)
+    #------------------------------------------------------------------------
+
+    if test -z "$no_pipe" -a -n "$GCC"; then
+	AC_CACHE_CHECK([if the compiler understands -pipe],
+	    tcl_cv_cc_pipe, [
+	    hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe"
+	    AC_TRY_COMPILE(,, tcl_cv_cc_pipe=yes, tcl_cv_cc_pipe=no)
+	    CFLAGS=$hold_cflags])
+	if test $tcl_cv_cc_pipe = yes; then
+	    CFLAGS="$CFLAGS -pipe"
+	fi
+    fi
+
+    #--------------------------------------------------------------------
+    # Common compiler flag setup
+    #--------------------------------------------------------------------
+
+    AC_C_BIGENDIAN
+    if test "${TEA_PLATFORM}" = "unix" ; then
+	TEA_TCL_LINK_LIBS
+	TEA_MISSING_POSIX_HEADERS
+	# Let the user call this, because if it triggers, they will
+	# need a compat/strtod.c that is correct.  Users can also
+	# use Tcl_GetDouble(FromObj) instead.
+	#TEA_BUGGY_STRTOD
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_MAKE_LIB --
+#
+#	Generate a line that can be used to build a shared/unshared library
+#	in a platform independent manner.
+#
+# Arguments:
+#	none
+#
+#	Requires:
+#
+# Results:
+#
+#	Defines the following vars:
+#	CFLAGS -	Done late here to note disturb other AC macros
+#       MAKE_LIB -      Command to execute to build the Tcl library;
+#                       differs depending on whether or not Tcl is being
+#                       compiled as a shared library.
+#	MAKE_SHARED_LIB	Makefile rule for building a shared library
+#	MAKE_STATIC_LIB	Makefile rule for building a static library
+#	MAKE_STUB_LIB	Makefile rule for building a stub library
+#	VC_MANIFEST_EMBED_DLL Makefile rule for embedded VC manifest in DLL
+#	VC_MANIFEST_EMBED_EXE Makefile rule for embedded VC manifest in EXE
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_MAKE_LIB], [
+    if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
+	MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)"
+	MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)"
+	AC_EGREP_CPP([manifest needed], [
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+print("manifest needed")
+#endif
+	], [
+	# Could do a CHECK_PROG for mt, but should always be with MSVC8+
+	VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;2 ; fi"
+	VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;1 ; fi"
+	MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}"
+	TEA_ADD_CLEANFILES([*.manifest])
+	])
+	MAKE_STUB_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_STUB_OBJECTS)"
+    else
+	MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)"
+	MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
+	MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)"
+    fi
+
+    if test "${SHARED_BUILD}" = "1" ; then
+	MAKE_LIB="${MAKE_SHARED_LIB} "
+    else
+	MAKE_LIB="${MAKE_STATIC_LIB} "
+    fi
+
+    #--------------------------------------------------------------------
+    # Shared libraries and static libraries have different names.
+    # Use the double eval to make sure any variables in the suffix is
+    # substituted. (@@@ Might not be necessary anymore)
+    #--------------------------------------------------------------------
+
+    if test "${TEA_PLATFORM}" = "windows" ; then
+	if test "${SHARED_BUILD}" = "1" ; then
+	    # We force the unresolved linking of symbols that are really in
+	    # the private libraries of Tcl and Tk.
+	    SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
+	    if test x"${TK_BIN_DIR}" != x ; then
+		SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\""
+	    fi
+	    eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+	else
+	    eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+	fi
+	# Some packages build their own stubs libraries
+	eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+	if test "$GCC" = "yes"; then
+	    PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
+	fi
+	# These aren't needed on Windows (either MSVC or gcc)
+	RANLIB=:
+	RANLIB_STUB=:
+    else
+	RANLIB_STUB="${RANLIB}"
+	if test "${SHARED_BUILD}" = "1" ; then
+	    SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}"
+	    if test x"${TK_BIN_DIR}" != x ; then
+		SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
+	    fi
+	    eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+	    RANLIB=:
+	else
+	    eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+	fi
+	# Some packages build their own stubs libraries
+	eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+    fi
+
+    # These are escaped so that only CFLAGS is picked up at configure time.
+    # The other values will be substituted at make time.
+    CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}"
+    if test "${SHARED_BUILD}" = "1" ; then
+	CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}"
+    fi
+
+    AC_SUBST(MAKE_LIB)
+    AC_SUBST(MAKE_SHARED_LIB)
+    AC_SUBST(MAKE_STATIC_LIB)
+    AC_SUBST(MAKE_STUB_LIB)
+    AC_SUBST(RANLIB_STUB)
+    AC_SUBST(VC_MANIFEST_EMBED_DLL)
+    AC_SUBST(VC_MANIFEST_EMBED_EXE)
+])
+
+#------------------------------------------------------------------------
+# TEA_LIB_SPEC --
+#
+#	Compute the name of an existing object library located in libdir
+#	from the given base name and produce the appropriate linker flags.
+#
+# Arguments:
+#	basename	The base name of the library without version
+#			numbers, extensions, or "lib" prefixes.
+#	extra_dir	Extra directory in which to search for the
+#			library.  This location is used first, then
+#			$prefix/$exec-prefix, then some defaults.
+#
+# Requires:
+#	TEA_INIT and TEA_PREFIX must be called first.
+#
+# Results:
+#
+#	Defines the following vars:
+#		${basename}_LIB_NAME	The computed library name.
+#		${basename}_LIB_SPEC	The computed linker flags.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LIB_SPEC], [
+    AC_MSG_CHECKING([for $1 library])
+
+    # Look in exec-prefix for the library (defined by TEA_PREFIX).
+
+    tea_lib_name_dir="${exec_prefix}/lib"
+
+    # Or in a user-specified location.
+
+    if test x"$2" != x ; then
+	tea_extra_lib_dir=$2
+    else
+	tea_extra_lib_dir=NONE
+    fi
+
+    for i in \
+	    `ls -dr ${tea_extra_lib_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+	    `ls -dr ${tea_extra_lib_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+	    `ls -dr ${tea_lib_name_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+	    `ls -dr ${tea_lib_name_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+	    `ls -dr /usr/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+	    `ls -dr /usr/lib/lib$1[[0-9]]* 2>/dev/null ` \
+	    `ls -dr /usr/lib64/$1[[0-9]]*.lib 2>/dev/null ` \
+	    `ls -dr /usr/lib64/lib$1[[0-9]]* 2>/dev/null ` \
+	    `ls -dr /usr/local/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+	    `ls -dr /usr/local/lib/lib$1[[0-9]]* 2>/dev/null ` ; do
+	if test -f "$i" ; then
+	    tea_lib_name_dir=`dirname $i`
+	    $1_LIB_NAME=`basename $i`
+	    $1_LIB_PATH_NAME=$i
+	    break
+	fi
+    done
+
+    if test "${TEA_PLATFORM}" = "windows"; then
+	$1_LIB_SPEC=\"`${CYGPATH} ${$1_LIB_PATH_NAME} 2>/dev/null`\"
+    else
+	# Strip off the leading "lib" and trailing ".a" or ".so"
+
+	tea_lib_name_lib=`echo ${$1_LIB_NAME}|sed -e 's/^lib//' -e 's/\.[[^.]]*$//' -e 's/\.so.*//'`
+	$1_LIB_SPEC="-L${tea_lib_name_dir} -l${tea_lib_name_lib}"
+    fi
+
+    if test "x${$1_LIB_NAME}" = x ; then
+	AC_MSG_ERROR([not found])
+    else
+	AC_MSG_RESULT([${$1_LIB_SPEC}])
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TCL_HEADERS --
+#
+#	Locate the private Tcl include files
+#
+# Arguments:
+#
+#	Requires:
+#		TCL_SRC_DIR	Assumes that TEA_LOAD_TCLCONFIG has
+#				already been called.
+#
+# Results:
+#
+#	Substs the following vars:
+#		TCL_TOP_DIR_NATIVE
+#		TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
+    # Allow for --with-tclinclude to take effect and define ${ac_cv_c_tclh}
+    AC_REQUIRE([TEA_PUBLIC_TCL_HEADERS])
+    AC_MSG_CHECKING([for Tcl private include files])
+
+    TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}`
+    TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\"
+
+    # Check to see if tcl<Plat>Port.h isn't already with the public headers
+    # Don't look for tclInt.h because that resides with tcl.h in the core
+    # sources, but the <plat>Port headers are in a different directory
+    if test "${TEA_PLATFORM}" = "windows" -a \
+	-f "${ac_cv_c_tclh}/tclWinPort.h"; then
+	result="private headers found with public headers"
+    elif test "${TEA_PLATFORM}" = "unix" -a \
+	-f "${ac_cv_c_tclh}/tclUnixPort.h"; then
+	result="private headers found with public headers"
+    else
+	TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\"
+	if test "${TEA_PLATFORM}" = "windows"; then
+	    TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\"
+	else
+	    TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\"
+	fi
+	# Overwrite the previous TCL_INCLUDES as this should capture both
+	# public and private headers in the same set.
+	# We want to ensure these are substituted so as not to require
+	# any *_NATIVE vars be defined in the Makefile
+	TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}"
+	if test "`uname -s`" = "Darwin"; then
+            # If Tcl was built as a framework, attempt to use
+            # the framework's Headers and PrivateHeaders directories
+            case ${TCL_DEFS} in
+	    	*TCL_FRAMEWORK*)
+		    if test -d "${TCL_BIN_DIR}/Headers" -a \
+			    -d "${TCL_BIN_DIR}/PrivateHeaders"; then
+			TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}"
+		    else
+			TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"
+		    fi
+	            ;;
+	    esac
+	    result="Using ${TCL_INCLUDES}"
+	else
+	    if test ! -f "${TCL_SRC_DIR}/generic/tclInt.h" ; then
+		AC_MSG_ERROR([Cannot find private header tclInt.h in ${TCL_SRC_DIR}])
+	    fi
+	    result="Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}"
+	fi
+    fi
+
+    AC_SUBST(TCL_TOP_DIR_NATIVE)
+
+    AC_SUBST(TCL_INCLUDES)
+    AC_MSG_RESULT([${result}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TCL_HEADERS --
+#
+#	Locate the installed public Tcl header files
+#
+# Arguments:
+#	None.
+#
+# Requires:
+#	CYGPATH must be set
+#
+# Results:
+#
+#	Adds a --with-tclinclude switch to configure.
+#	Result is cached.
+#
+#	Substs the following vars:
+#		TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [
+    AC_MSG_CHECKING([for Tcl public headers])
+
+    AC_ARG_WITH(tclinclude, [  --with-tclinclude       directory containing the public Tcl header files], with_tclinclude=${withval})
+
+    AC_CACHE_VAL(ac_cv_c_tclh, [
+	# Use the value from --with-tclinclude, if it was given
+
+	if test x"${with_tclinclude}" != x ; then
+	    if test -f "${with_tclinclude}/tcl.h" ; then
+		ac_cv_c_tclh=${with_tclinclude}
+	    else
+		AC_MSG_ERROR([${with_tclinclude} directory does not contain tcl.h])
+	    fi
+	else
+	    list=""
+	    if test "`uname -s`" = "Darwin"; then
+		# If Tcl was built as a framework, attempt to use
+		# the framework's Headers directory
+		case ${TCL_DEFS} in
+		    *TCL_FRAMEWORK*)
+			list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`"
+			;;
+		esac
+	    fi
+
+	    # Look in the source dir only if Tcl is not installed,
+	    # and in that situation, look there before installed locations.
+	    if test -f "${TCL_BIN_DIR}/Makefile" ; then
+		list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`"
+	    fi
+
+	    # Check order: pkg --prefix location, Tcl's --prefix location,
+	    # relative to directory of tclConfig.sh.
+
+	    eval "temp_includedir=${includedir}"
+	    list="$list \
+		`ls -d ${temp_includedir}        2>/dev/null` \
+		`ls -d ${TCL_PREFIX}/include     2>/dev/null` \
+		`ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+	    if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+		list="$list /usr/local/include /usr/include"
+		if test x"${TCL_INCLUDE_SPEC}" != x ; then
+		    d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+		    list="$list `ls -d ${d} 2>/dev/null`"
+		fi
+	    fi
+	    for i in $list ; do
+		if test -f "$i/tcl.h" ; then
+		    ac_cv_c_tclh=$i
+		    break
+		fi
+	    done
+	fi
+    ])
+
+    # Print a message based on how we determined the include path
+
+    if test x"${ac_cv_c_tclh}" = x ; then
+	AC_MSG_ERROR([tcl.h not found.  Please specify its location with --with-tclinclude])
+    else
+	AC_MSG_RESULT([${ac_cv_c_tclh}])
+    fi
+
+    # Convert to a native path and substitute into the output files.
+
+    INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}`
+
+    TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+    AC_SUBST(TCL_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TK_HEADERS --
+#
+#	Locate the private Tk include files
+#
+# Arguments:
+#
+#	Requires:
+#		TK_SRC_DIR	Assumes that TEA_LOAD_TKCONFIG has
+#				 already been called.
+#
+# Results:
+#
+#	Substs the following vars:
+#		TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [
+    # Allow for --with-tkinclude to take effect and define ${ac_cv_c_tkh}
+    AC_REQUIRE([TEA_PUBLIC_TK_HEADERS])
+    AC_MSG_CHECKING([for Tk private include files])
+
+    TK_SRC_DIR_NATIVE=`${CYGPATH} ${TK_SRC_DIR}`
+    TK_TOP_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}\"
+
+    # Check to see if tk<Plat>Port.h isn't already with the public headers
+    # Don't look for tkInt.h because that resides with tk.h in the core
+    # sources, but the <plat>Port headers are in a different directory
+    if test "${TEA_PLATFORM}" = "windows" -a \
+	-f "${ac_cv_c_tkh}/tkWinPort.h"; then
+	result="private headers found with public headers"
+    elif test "${TEA_PLATFORM}" = "unix" -a \
+	-f "${ac_cv_c_tkh}/tkUnixPort.h"; then
+	result="private headers found with public headers"
+    else
+	TK_GENERIC_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/generic\"
+	TK_XLIB_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/xlib\"
+	if test "${TEA_PLATFORM}" = "windows"; then
+	    TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/win\"
+	else
+	    TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/unix\"
+	fi
+	# Overwrite the previous TK_INCLUDES as this should capture both
+	# public and private headers in the same set.
+	# We want to ensure these are substituted so as not to require
+	# any *_NATIVE vars be defined in the Makefile
+	TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}"
+	# Detect and add ttk subdir
+	if test -d "${TK_SRC_DIR}/generic/ttk"; then
+	   TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/generic/ttk\""
+	fi
+	if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then
+	   TK_INCLUDES="${TK_INCLUDES} -I\"${TK_XLIB_DIR_NATIVE}\""
+	fi
+	if test "${TEA_WINDOWINGSYSTEM}" = "aqua"; then
+	   TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/macosx\""
+	fi
+	if test "`uname -s`" = "Darwin"; then
+	    # If Tk was built as a framework, attempt to use
+	    # the framework's Headers and PrivateHeaders directories
+	    case ${TK_DEFS} in
+		*TK_FRAMEWORK*)
+			if test -d "${TK_BIN_DIR}/Headers" -a \
+				-d "${TK_BIN_DIR}/PrivateHeaders"; then
+			    TK_INCLUDES="-I\"${TK_BIN_DIR}/Headers\" -I\"${TK_BIN_DIR}/PrivateHeaders\" ${TK_INCLUDES}"
+			else
+			    TK_INCLUDES="${TK_INCLUDES} ${TK_INCLUDE_SPEC} `echo "${TK_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"
+			fi
+			;;
+	    esac
+	    result="Using ${TK_INCLUDES}"
+	else
+	    if test ! -f "${TK_SRC_DIR}/generic/tkInt.h" ; then
+	       AC_MSG_ERROR([Cannot find private header tkInt.h in ${TK_SRC_DIR}])
+	    fi
+	    result="Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}"
+	fi
+    fi
+
+    AC_SUBST(TK_TOP_DIR_NATIVE)
+    AC_SUBST(TK_XLIB_DIR_NATIVE)
+
+    AC_SUBST(TK_INCLUDES)
+    AC_MSG_RESULT([${result}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TK_HEADERS --
+#
+#	Locate the installed public Tk header files
+#
+# Arguments:
+#	None.
+#
+# Requires:
+#	CYGPATH must be set
+#
+# Results:
+#
+#	Adds a --with-tkinclude switch to configure.
+#	Result is cached.
+#
+#	Substs the following vars:
+#		TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TK_HEADERS], [
+    AC_MSG_CHECKING([for Tk public headers])
+
+    AC_ARG_WITH(tkinclude, [  --with-tkinclude        directory containing the public Tk header files], with_tkinclude=${withval})
+
+    AC_CACHE_VAL(ac_cv_c_tkh, [
+	# Use the value from --with-tkinclude, if it was given
+
+	if test x"${with_tkinclude}" != x ; then
+	    if test -f "${with_tkinclude}/tk.h" ; then
+		ac_cv_c_tkh=${with_tkinclude}
+	    else
+		AC_MSG_ERROR([${with_tkinclude} directory does not contain tk.h])
+	    fi
+	else
+	    list=""
+	    if test "`uname -s`" = "Darwin"; then
+		# If Tk was built as a framework, attempt to use
+		# the framework's Headers directory.
+		case ${TK_DEFS} in
+		    *TK_FRAMEWORK*)
+			list="`ls -d ${TK_BIN_DIR}/Headers 2>/dev/null`"
+			;;
+		esac
+	    fi
+
+	    # Look in the source dir only if Tk is not installed,
+	    # and in that situation, look there before installed locations.
+	    if test -f "${TK_BIN_DIR}/Makefile" ; then
+		list="$list `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`"
+	    fi
+
+	    # Check order: pkg --prefix location, Tk's --prefix location,
+	    # relative to directory of tkConfig.sh, Tcl's --prefix location,
+	    # relative to directory of tclConfig.sh.
+
+	    eval "temp_includedir=${includedir}"
+	    list="$list \
+		`ls -d ${temp_includedir}        2>/dev/null` \
+		`ls -d ${TK_PREFIX}/include      2>/dev/null` \
+		`ls -d ${TK_BIN_DIR}/../include  2>/dev/null` \
+		`ls -d ${TCL_PREFIX}/include     2>/dev/null` \
+		`ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+	    if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+		list="$list /usr/local/include /usr/include"
+		if test x"${TK_INCLUDE_SPEC}" != x ; then
+		    d=`echo "${TK_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+		    list="$list `ls -d ${d} 2>/dev/null`"
+		fi
+	    fi
+	    for i in $list ; do
+		if test -f "$i/tk.h" ; then
+		    ac_cv_c_tkh=$i
+		    break
+		fi
+	    done
+	fi
+    ])
+
+    # Print a message based on how we determined the include path
+
+    if test x"${ac_cv_c_tkh}" = x ; then
+	AC_MSG_ERROR([tk.h not found.  Please specify its location with --with-tkinclude])
+    else
+	AC_MSG_RESULT([${ac_cv_c_tkh}])
+    fi
+
+    # Convert to a native path and substitute into the output files.
+
+    INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tkh}`
+
+    TK_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+    AC_SUBST(TK_INCLUDES)
+
+    if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then
+	# On Windows and Aqua, we need the X compat headers
+	AC_MSG_CHECKING([for X11 header files])
+	if test ! -r "${INCLUDE_DIR_NATIVE}/X11/Xlib.h"; then
+	    INCLUDE_DIR_NATIVE="`${CYGPATH} ${TK_SRC_DIR}/xlib`"
+	    TK_XINCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+	    AC_SUBST(TK_XINCLUDES)
+	fi
+	AC_MSG_RESULT([${INCLUDE_DIR_NATIVE}])
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_CONFIG --
+#
+#	Locate the ${1}Config.sh file and perform a sanity check on
+#	the ${1} compile flags.  These are used by packages like
+#	[incr Tk] that load *Config.sh files from more than Tcl and Tk.
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--with-$1=...
+#
+#	Defines the following vars:
+#		$1_BIN_DIR	Full path to the directory containing
+#				the $1Config.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CONFIG], [
+    #
+    # Ok, lets find the $1 configuration
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-$1
+    #
+
+    if test x"${no_$1}" = x ; then
+	# we reset no_$1 in case something fails here
+	no_$1=true
+	AC_ARG_WITH($1, [  --with-$1              directory containing $1 configuration ($1Config.sh)], with_$1config=${withval})
+	AC_MSG_CHECKING([for $1 configuration])
+	AC_CACHE_VAL(ac_cv_c_$1config,[
+
+	    # First check to see if --with-$1 was specified.
+	    if test x"${with_$1config}" != x ; then
+		case ${with_$1config} in
+		    */$1Config.sh )
+			if test -f ${with_$1config}; then
+			    AC_MSG_WARN([--with-$1 argument should refer to directory containing $1Config.sh, not to $1Config.sh itself])
+			    with_$1config=`echo ${with_$1config} | sed 's!/$1Config\.sh$!!'`
+			fi;;
+		esac
+		if test -f "${with_$1config}/$1Config.sh" ; then
+		    ac_cv_c_$1config=`(cd ${with_$1config}; pwd)`
+		else
+		    AC_MSG_ERROR([${with_$1config} directory doesn't contain $1Config.sh])
+		fi
+	    fi
+
+	    # then check for a private $1 installation
+	    if test x"${ac_cv_c_$1config}" = x ; then
+		for i in \
+			../$1 \
+			`ls -dr ../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+			`ls -dr ../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+			`ls -dr ../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+			../../$1 \
+			`ls -dr ../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+			`ls -dr ../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+			../../../$1 \
+			`ls -dr ../../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+			`ls -dr ../../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+			`ls -dr ../../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ../../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+			${srcdir}/../$1 \
+			`ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+			`ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+			`ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+			`ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+			; do
+		    if test -f "$i/$1Config.sh" ; then
+			ac_cv_c_$1config=`(cd $i; pwd)`
+			break
+		    fi
+		    if test -f "$i/unix/$1Config.sh" ; then
+			ac_cv_c_$1config=`(cd $i/unix; pwd)`
+			break
+		    fi
+		done
+	    fi
+
+	    # check in a few common install locations
+	    if test x"${ac_cv_c_$1config}" = x ; then
+		for i in `ls -d ${libdir} 2>/dev/null` \
+			`ls -d ${exec_prefix}/lib 2>/dev/null` \
+			`ls -d ${prefix}/lib 2>/dev/null` \
+			`ls -d /usr/local/lib 2>/dev/null` \
+			`ls -d /usr/contrib/lib 2>/dev/null` \
+			`ls -d /usr/lib 2>/dev/null` \
+			`ls -d /usr/lib64 2>/dev/null` \
+			; do
+		    if test -f "$i/$1Config.sh" ; then
+			ac_cv_c_$1config=`(cd $i; pwd)`
+			break
+		    fi
+		done
+	    fi
+	])
+
+	if test x"${ac_cv_c_$1config}" = x ; then
+	    $1_BIN_DIR="# no $1 configs found"
+	    AC_MSG_WARN([Cannot find $1 configuration definitions])
+	    exit 0
+	else
+	    no_$1=
+	    $1_BIN_DIR=${ac_cv_c_$1config}
+	    AC_MSG_RESULT([found $$1_BIN_DIR/$1Config.sh])
+	fi
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_CONFIG --
+#
+#	Load the $1Config.sh file
+#
+# Arguments:
+#
+#	Requires the following vars to be set:
+#		$1_BIN_DIR
+#
+# Results:
+#
+#	Subst the following vars:
+#		$1_SRC_DIR
+#		$1_LIB_FILE
+#		$1_LIB_SPEC
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_CONFIG], [
+    AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh])
+
+    if test -f "${$1_BIN_DIR}/$1Config.sh" ; then
+        AC_MSG_RESULT([loading])
+	. "${$1_BIN_DIR}/$1Config.sh"
+    else
+        AC_MSG_RESULT([file not found])
+    fi
+
+    #
+    # If the $1_BIN_DIR is the build directory (not the install directory),
+    # then set the common variable name to the value of the build variables.
+    # For example, the variable $1_LIB_SPEC will be set to the value
+    # of $1_BUILD_LIB_SPEC. An extension should make use of $1_LIB_SPEC
+    # instead of $1_BUILD_LIB_SPEC since it will work with both an
+    # installed and uninstalled version of Tcl.
+    #
+
+    if test -f "${$1_BIN_DIR}/Makefile" ; then
+	AC_MSG_WARN([Found Makefile - using build library specs for $1])
+        $1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
+        $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
+        $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
+        $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC}
+        $1_LIBRARY_PATH=${$1_LIBRARY_PATH}
+    fi
+
+    AC_SUBST($1_VERSION)
+    AC_SUBST($1_BIN_DIR)
+    AC_SUBST($1_SRC_DIR)
+
+    AC_SUBST($1_LIB_FILE)
+    AC_SUBST($1_LIB_SPEC)
+
+    AC_SUBST($1_STUB_LIB_FILE)
+    AC_SUBST($1_STUB_LIB_SPEC)
+    AC_SUBST($1_STUB_LIB_PATH)
+
+    # Allow the caller to prevent this auto-check by specifying any 2nd arg
+    AS_IF([test "x$2" = x], [
+	# Check both upper and lower-case variants
+	# If a dev wanted non-stubs libs, this function could take an option
+	# to not use _STUB in the paths below
+	AS_IF([test "x${$1_STUB_LIB_SPEC}" = x],
+	    [TEA_LOAD_CONFIG_LIB(translit($1,[a-z],[A-Z])_STUB)],
+	    [TEA_LOAD_CONFIG_LIB($1_STUB)])
+    ])
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_CONFIG_LIB --
+#
+#	Helper function to load correct library from another extension's
+#	${PACKAGE}Config.sh.
+#
+# Results:
+#	Adds to LIBS the appropriate extension library
+#
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_LOAD_CONFIG_LIB], [
+    AC_MSG_CHECKING([For $1 library for LIBS])
+    # This simplifies the use of stub libraries by automatically adding
+    # the stub lib to your path.  Normally this would add to SHLIB_LD_LIBS,
+    # but this is called before CONFIG_CFLAGS.  More importantly, this adds
+    # to PKG_LIBS, which becomes LIBS, and that is only used by SHLIB_LD.
+    if test "x${$1_LIB_SPEC}" != "x" ; then
+	if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes" ; then
+	    TEA_ADD_LIBS([\"`${CYGPATH} ${$1_LIB_PATH}`\"])
+	    AC_MSG_RESULT([using $1_LIB_PATH ${$1_LIB_PATH}])
+	else
+	    TEA_ADD_LIBS([${$1_LIB_SPEC}])
+	    AC_MSG_RESULT([using $1_LIB_SPEC ${$1_LIB_SPEC}])
+	fi
+    else
+	AC_MSG_RESULT([file not found])
+    fi
+])
+
+#------------------------------------------------------------------------
+# TEA_EXPORT_CONFIG --
+#
+#	Define the data to insert into the ${PACKAGE}Config.sh file
+#
+# Arguments:
+#
+#	Requires the following vars to be set:
+#		$1
+#
+# Results:
+#	Subst the following vars:
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_EXPORT_CONFIG], [
+    #--------------------------------------------------------------------
+    # These are for $1Config.sh
+    #--------------------------------------------------------------------
+
+    # pkglibdir must be a fully qualified path and (not ${exec_prefix}/lib)
+    eval pkglibdir="[$]{libdir}/$1${PACKAGE_VERSION}"
+    if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+	eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}${DBGX}"
+	eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}${DBGX}"
+    else
+	eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
+	eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
+    fi
+    $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}"
+    $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}"
+    $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}"
+    $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}"
+    $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}"
+    $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}"
+
+    AC_SUBST($1_BUILD_LIB_SPEC)
+    AC_SUBST($1_LIB_SPEC)
+    AC_SUBST($1_BUILD_STUB_LIB_SPEC)
+    AC_SUBST($1_STUB_LIB_SPEC)
+    AC_SUBST($1_BUILD_STUB_LIB_PATH)
+    AC_SUBST($1_STUB_LIB_PATH)
+
+    AC_SUBST(MAJOR_VERSION)
+    AC_SUBST(MINOR_VERSION)
+    AC_SUBST(PATCHLEVEL)
+])
+
+
+#------------------------------------------------------------------------
+# TEA_PATH_CELIB --
+#
+#	Locate Keuchel's celib emulation layer for targeting Win/CE
+#
+# Arguments:
+#	none
+#
+# Results:
+#
+#	Adds the following arguments to configure:
+#		--with-celib=...
+#
+#	Defines the following vars:
+#		CELIB_DIR	Full path to the directory containing
+#				the include and platform lib files
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CELIB], [
+    # First, look for one uninstalled.
+    # the alternative search directory is invoked by --with-celib
+
+    if test x"${no_celib}" = x ; then
+	# we reset no_celib in case something fails here
+	no_celib=true
+	AC_ARG_WITH(celib,[  --with-celib=DIR        use Windows/CE support library from DIR], with_celibconfig=${withval})
+	AC_MSG_CHECKING([for Windows/CE celib directory])
+	AC_CACHE_VAL(ac_cv_c_celibconfig,[
+	    # First check to see if --with-celibconfig was specified.
+	    if test x"${with_celibconfig}" != x ; then
+		if test -d "${with_celibconfig}/inc" ; then
+		    ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
+		else
+		    AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory])
+		fi
+	    fi
+
+	    # then check for a celib library
+	    if test x"${ac_cv_c_celibconfig}" = x ; then
+		for i in \
+			../celib-palm-3.0 \
+			../celib \
+			../../celib-palm-3.0 \
+			../../celib \
+			`ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \
+			${srcdir}/../celib-palm-3.0 \
+			${srcdir}/../celib \
+			`ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \
+			; do
+		    if test -d "$i/inc" ; then
+			ac_cv_c_celibconfig=`(cd $i; pwd)`
+			break
+		    fi
+		done
+	    fi
+	])
+	if test x"${ac_cv_c_celibconfig}" = x ; then
+	    AC_MSG_ERROR([Cannot find celib support library directory])
+	else
+	    no_celib=
+	    CELIB_DIR=${ac_cv_c_celibconfig}
+	    CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
+	    AC_MSG_RESULT([found $CELIB_DIR])
+	fi
+    fi
+])
+
+
+# Local Variables:
+# mode: autoconf
+# End:
Index: trunk/kitgen/8.x/blt/time_axis.tcl
===================================================================
--- trunk/kitgen/8.x/blt/time_axis.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/time_axis.tcl	(revision 175)
@@ -0,0 +1,52 @@
+package require BLT
+
+pack [blt::graph .g -width 10i]
+
+#  Proc passed as a callback to BLT to draw custom tick labels.
+#
+proc format_timeAxis_tick {win seconds} {
+    set hour [clock format $seconds -format "%H"]
+    regsub {^0*} $hour {} label
+    if {[string equal $label {}]} {
+        return "$label\n[string repeat { } $::nSpaces]\
+                [clock format $seconds -format "%d/%m"]"
+    } else {
+        return $label
+    }
+}
+
+#  Construct a list of major tick positions in seconds - the
+#  month, year and the range of days can be varied to suit
+#  the application.
+#
+for {set day 20} {$day <= 23} {incr day} {
+    foreach hours {0 4 8 12 16 20} {
+        lappend majorticks [clock scan "3/$day/2001 $hours:00"]
+    }
+}
+lappend majorticks [clock scan "3/$day/2001 00:00"]
+
+#  Create the graph.
+.g axis configure x                            \
+        -min          [lindex $majorticks 0]   \
+        -max          [lindex $majorticks end] \
+        -title        "Day"                    \
+        -majorticks   $majorticks
+
+#  Need to do an update to display the graph before the
+#  distance can be measured.
+update
+
+#  Measure the width of a day on the graph - the example
+#  dates need not be in the displayed range.
+set dayFieldWidth [expr {
+        [.g axis transform x [clock scan 3/2/2001]] -
+        [.g axis transform x [clock scan 3/1/2001]]}]
+
+#  Work out how many spaces this corresponds to in the
+#  font for the tick labels.
+set nSpaces [expr {$dayFieldWidth /
+                   [font measure [.g axis cget x -tickfont] " "]}]
+
+#  Configure the axis to use the custom label command.
+.g axis configure x -command format_timeAxis_tick
Index: trunk/kitgen/8.x/blt/unix/bltUnixImage.c
===================================================================
--- trunk/kitgen/8.x/blt/unix/bltUnixImage.c	(revision 175)
+++ trunk/kitgen/8.x/blt/unix/bltUnixImage.c	(revision 175)
@@ -0,0 +1,1345 @@
+
+/*
+ * bltUnixImage.c --
+ *
+ *	This module implements image processing procedures for the BLT
+ *	toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include "bltHash.h"
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+
+#define CLAMP(c)	((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+int redAdjust, greenAdjust, blueAdjust;
+int redMaskShift, greenMaskShift, blueMaskShift;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ShiftCount --
+ *
+ *	Returns the position of the least significant (low) bit in
+ *	the given mask.
+ *
+ *	For TrueColor and DirectColor visuals, a pixel value is
+ *	formed by OR-ing the red, green, and blue colormap indices
+ *	into a single 32-bit word.  The visual's color masks tell
+ *	you where in the word the indices are supposed to be.  The
+ *	masks contain bits only where the index is found.  By counting
+ *	the leading zeros in the mask, we know how many bits to shift
+ *	to the individual red, green, and blue values to form a pixel.
+ *
+ * Results:
+ *      The number of the least significant bit.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ShiftCount(mask)
+    register unsigned int mask;
+{
+    register int count;
+
+    for (count = 0; count < 32; count++) {
+	if (mask & 0x01) {
+	    break;
+	}
+	mask >>= 1;
+    }
+    return count;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CountBits --
+ *
+ *	Returns the number of bits set in the given mask.
+ *
+ *	Reference: Graphics Gems Volume 2.
+ *	
+ * Results:
+ *      The number of bits to set in the mask.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+CountBits(mask)
+    register unsigned long mask; /* 32  1-bit tallies */
+{
+    /* 16  2-bit tallies */
+    mask = (mask & 0x55555555) + ((mask >> 1) & (0x55555555));  
+    /* 8  4-bit tallies */
+    mask = (mask & 0x33333333) + ((mask >> 2) & (0x33333333)); 
+    /* 4  8-bit tallies */
+    mask = (mask & 0x07070707) + ((mask >> 4) & (0x07070707));  
+    /* 2 16-bit tallies */
+    mask = (mask & 0x000F000F) + ((mask >> 8) & (0x000F000F));  
+    /* 1 32-bit tally */
+    mask = (mask & 0x0000001F) + ((mask >> 16) & (0x0000001F));  
+    return mask;
+}
+
+static void
+ComputeMasks(visualPtr)
+    Visual *visualPtr;
+{
+    int count;
+
+    redMaskShift = ShiftCount((unsigned int)visualPtr->red_mask);
+    greenMaskShift = ShiftCount((unsigned int)visualPtr->green_mask);
+    blueMaskShift = ShiftCount((unsigned int)visualPtr->blue_mask);
+
+    redAdjust = greenAdjust = blueAdjust = 0;
+    count = CountBits((unsigned long)visualPtr->red_mask);
+    if (count < 8) {
+	redAdjust = 8 - count;
+    }
+    count = CountBits((unsigned long)visualPtr->green_mask);
+    if (count < 8) {
+	greenAdjust = 8 - count;
+    }
+    count = CountBits((unsigned long)visualPtr->blue_mask);
+    if (count < 8) {
+	blueAdjust = 8 - count;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TrueColorPixel --
+ *
+ *      Computes a pixel index from the 3 component RGB values.
+ *
+ * Results:
+ *      The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+TrueColorPixel(visualPtr, pixelPtr)
+    Visual *visualPtr;
+    Pix32 *pixelPtr;
+{
+    unsigned int red, green, blue;
+
+    /*
+     * The number of bits per color may be less than eight. For example,
+     * 15/16 bit displays (hi-color) use only 5 bits, 8-bit displays
+     * use 2 or 3 bits (don't ask me why you'd have an 8-bit TrueColor
+     * display). So shift off the least significant bits.
+     */
+    red = ((unsigned int)pixelPtr->Red >> redAdjust);
+    green = ((unsigned int)pixelPtr->Green >> greenAdjust);
+    blue = ((unsigned int)pixelPtr->Blue >> blueAdjust);
+
+    /* Shift each color into the proper location of the pixel index. */
+    red = (red << redMaskShift) & visualPtr->red_mask;
+    green = (green << greenMaskShift) & visualPtr->green_mask;
+    blue = (blue << blueMaskShift) & visualPtr->blue_mask;
+    return (red | green | blue);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DirectColorPixel --
+ *
+ *      Translates the 3 component RGB values into a pixel index.
+ *      This differs from TrueColor only in that it first translates
+ *	the RGB values through a color table.
+ *
+ * Results:
+ *      The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+DirectColorPixel(colorTabPtr, pixelPtr)
+    struct ColorTableStruct *colorTabPtr;
+    Pix32 *pixelPtr;
+{
+    unsigned int red, green, blue;
+
+    red = colorTabPtr->red[pixelPtr->Red];
+    green = colorTabPtr->green[pixelPtr->Green];
+    blue = colorTabPtr->blue[pixelPtr->Blue];
+    return (red | green | blue);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PseudoColorPixel --
+ *
+ *      Translates the 3 component RGB values into a pixel index.
+ *      This differs from TrueColor only in that it first translates
+ *	the RGB values through a color table.
+ *
+ * Results:
+ *      The pixel index is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static INLINE unsigned int
+PseudoColorPixel(pixelPtr, lut)
+    Pix32 *pixelPtr;
+    unsigned int *lut;
+{
+    int red, green, blue;
+    int pixel;
+
+    red = (pixelPtr->Red >> 3) + 1;
+    green = (pixelPtr->Green >> 3) + 1;
+    blue = (pixelPtr->Blue >> 3) + 1;
+    pixel = RGBIndex(red, green, blue);
+    return lut[pixel];
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPixmap --
+ *
+ *      Converts a color image into a pixmap.
+ *
+ *	Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ *      The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorImageToPixmap(interp, tkwin, image, colorTablePtr)
+    Tcl_Interp *interp;
+    Tk_Window tkwin;
+    Blt_ColorImage image;
+    ColorTable *colorTablePtr;	/* Points to array of colormap indices */
+{
+    Display *display;
+    int width, height;
+    Pixmap pixmap;
+    GC pixmapGC;
+    Visual *visualPtr;
+    XImage *imagePtr;
+    int nPixels;
+
+    visualPtr = Tk_Visual(tkwin);
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+    display = Tk_Display(tkwin);
+
+    ComputeMasks(visualPtr);
+
+    *colorTablePtr = NULL;
+    imagePtr = XCreateImage(Tk_Display(tkwin), visualPtr, Tk_Depth(tkwin),
+	ZPixmap, 0, (char *)NULL, width, height, 32, 0);
+    assert(imagePtr);
+
+    nPixels = width * height;
+    imagePtr->data = Blt_Malloc(sizeof(Pix32) * nPixels);
+    assert(imagePtr->data);
+
+    imagePtr->byte_order = MSBFirst;	/* Force the byte order */
+    imagePtr->bitmap_bit_order = imagePtr->byte_order;
+    imagePtr->bytes_per_line = width * sizeof(Pix32);
+
+    switch (visualPtr->class) {
+    case TrueColor:
+	{
+	    register int x, y;
+	    register Pix32 *srcPtr;
+	    register char *destPtr;
+	    unsigned int pixel;
+	    int rowOffset;
+
+	    /*
+	     * Compute the colormap locations directly from pixel RGB values.
+	     */
+	    srcPtr = Blt_ColorImageBits(image);
+	    rowOffset = 0;
+	    for (y = 0; y < height; y++) {
+		destPtr = imagePtr->data + rowOffset;
+		for (x = 0; x < width; x++, srcPtr++) {
+		    pixel = TrueColorPixel(visualPtr, srcPtr);
+		    switch (imagePtr->bits_per_pixel) {
+		    case 32:
+			*destPtr++ = (pixel >> 24) & 0xFF;
+			/*FALLTHRU*/
+		    case 24:
+			*destPtr++ = (pixel >> 16) & 0xFF;
+			/*FALLTHRU*/
+		    case 16:
+			*destPtr++ = (pixel >> 8) & 0xFF;
+			/*FALLTHRU*/
+		    case 8:
+			*destPtr++ = pixel & 0xFF;
+			/*FALLTHRU*/
+		    }
+		}
+		rowOffset += imagePtr->bytes_per_line;
+	    }
+	}
+	break;
+
+    case DirectColor:
+	{
+	    register int x, y;
+	    register Pix32 *srcPtr;
+	    register char *destPtr;
+	    unsigned int pixel;
+	    int rowOffset;
+	    struct ColorTableStruct *colorTabPtr;
+
+	    /* Build a color table first */
+	    colorTabPtr = Blt_DirectColorTable(interp, tkwin, image);
+
+	    /*
+	     * Compute the colormap locations directly from pixel RGB values.
+	     */
+	    srcPtr = Blt_ColorImageBits(image);
+	    rowOffset = 0;
+	    for (y = 0; y < height; y++) {
+		destPtr = imagePtr->data + rowOffset;
+		for (x = 0; x < width; x++, srcPtr++) {
+		    pixel = DirectColorPixel(colorTabPtr, srcPtr);
+		    switch (imagePtr->bits_per_pixel) {
+		    case 32:
+			*destPtr++ = (pixel >> 24) & 0xFF;
+			/*FALLTHRU*/
+		    case 24:
+			*destPtr++ = (pixel >> 16) & 0xFF;
+			/*FALLTHRU*/
+		    case 16:
+			*destPtr++ = (pixel >> 8) & 0xFF;
+			/*FALLTHRU*/
+		    case 8:
+			*destPtr++ = pixel & 0xFF;
+			/*FALLTHRU*/
+		    }
+		}
+		rowOffset += imagePtr->bytes_per_line;
+	    }
+	    *colorTablePtr = colorTabPtr;
+	}
+	break;
+
+    case GrayScale:
+    case StaticGray:
+    case PseudoColor:
+    case StaticColor:
+	{
+	    register int x, y;
+	    register Pix32 *srcPtr;
+	    register char *destPtr;
+	    unsigned int pixel;
+	    int rowOffset;
+	    struct ColorTableStruct *colorTabPtr;
+
+	    colorTabPtr = Blt_PseudoColorTable(interp, tkwin, image);
+
+	    srcPtr = Blt_ColorImageBits(image);
+	    rowOffset = 0;
+	    for (y = 0; y < height; y++) {
+		destPtr = imagePtr->data + rowOffset;
+		for (x = 0; x < width; x++, srcPtr++) {
+		    pixel = PseudoColorPixel(srcPtr, colorTabPtr->lut);
+		    switch (imagePtr->bits_per_pixel) {
+		    case 32:
+			*destPtr++ = (pixel >> 24) & 0xFF;
+			/*FALLTHRU*/
+		    case 24:
+			*destPtr++ = (pixel >> 16) & 0xFF;
+			/*FALLTHRU*/
+		    case 16:
+			*destPtr++ = (pixel >> 8) & 0xFF;
+			/*FALLTHRU*/
+		    case 8:
+			*destPtr++ = pixel & 0xFF;
+			/*FALLTHRU*/
+		    }
+		}
+		rowOffset += imagePtr->bytes_per_line;
+	    }
+	    Blt_Free(colorTabPtr->lut);
+	    *colorTablePtr = colorTabPtr;
+	}
+	break;
+    default:
+	return None;		/* Bad or unknown visual class. */
+    }
+    pixmapGC = Tk_GetGC(tkwin, 0L, (XGCValues *)NULL);
+    pixmap = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height,
+	Tk_Depth(tkwin));
+    XPutImage(display, pixmap, pixmapGC, imagePtr, 0, 0, 0, 0, width, height);
+    XDestroyImage(imagePtr);
+    Tk_FreeGC(display, pixmapGC);
+    return pixmap;
+}
+
+/* ARGSUSED */
+static int
+XGetImageErrorProc(clientData, errEventPtr)
+    ClientData clientData;
+    XErrorEvent *errEventPtr;
+{
+    int *errorPtr = clientData;
+
+    *errorPtr = TCL_ERROR;
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DrawableToColorImage --
+ *
+ *      Takes a snapshot of an X drawable (pixmap or window) and
+ *	converts it to a color image.
+ *
+ *	The trick here is to efficiently convert the pixel values
+ *	(indices into the color table) into RGB color values.  In the
+ *	days of 8-bit displays, it was simpler to get RGB values for
+ *	all 256 indices into the colormap.  Instead we'll build a
+ *	hashtable of unique pixels and from that an array of pixels to
+ *	pass to XQueryColors.  For TrueColor visuals, we'll simple
+ *	compute the colors from the pixel.
+ *
+ *	[I don't know how much faster it would be to take advantage
+ *	of all the different visual types.  This pretty much depends
+ *	on the size of the image and the number of colors it uses.]
+ *
+ * Results:
+ *      Returns a color image of the drawable.  If an error occurred,
+ *	NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_DrawableToColorImage(tkwin, drawable, x, y, width, height, inputGamma)
+    Tk_Window tkwin;
+    Drawable drawable;
+    register int x, y;		/* Offset of image from the drawable's
+				 * origin. */
+    int width, height;		/* Dimension of the image.  Image must
+				 * be completely contained by the
+				 * drawable. */
+    double inputGamma;
+{
+    XImage *imagePtr;
+    Blt_ColorImage image;
+    register Pix32 *destPtr;
+    unsigned long pixel;
+    int result = TCL_OK;
+    Tk_ErrorHandler errHandler;
+    Visual *visualPtr;
+    unsigned char lut[256];
+
+    errHandler = Tk_CreateErrorHandler(Tk_Display(tkwin), BadMatch,
+	X_GetImage, -1, XGetImageErrorProc, &result);
+    imagePtr = XGetImage(Tk_Display(tkwin), drawable, x, y, width, height, 
+	AllPlanes, ZPixmap);
+    Tk_DeleteErrorHandler(errHandler);
+    XSync(Tk_Display(tkwin), False);
+    if (result != TCL_OK) {
+	return NULL;
+    }
+
+    {
+	register int i;
+	double value;
+	
+	for (i = 0; i < 256; i++) {
+	    value = pow(i / 255.0, inputGamma) * 255.0 + 0.5;
+	    lut[i] = (unsigned char)CLAMP(value);
+	}
+    }
+    /*
+     * First allocate a color image to hold the screen snapshot.
+     */
+    image = Blt_CreateColorImage(width, height);
+    visualPtr = Tk_Visual(tkwin);
+    if (visualPtr->class == TrueColor) {
+	unsigned int red, green, blue;
+	/*
+	 * Directly compute the RGB color values from the pixel index
+	 * rather than of going through XQueryColors.
+	 */
+	ComputeMasks(visualPtr);
+	destPtr = Blt_ColorImageBits(image);
+	for (y = 0; y < height; y++) {
+	    for (x = 0; x < width; x++) {
+		pixel = XGetPixel(imagePtr, x, y);
+
+		red = ((pixel & visualPtr->red_mask) >> redMaskShift) << redAdjust;
+		green = ((pixel & visualPtr->green_mask) >> greenMaskShift) << greenAdjust;
+		blue = ((pixel & visualPtr->blue_mask) >> blueMaskShift) << blueAdjust;
+
+		/*
+		 * The number of bits per color in the pixel may be
+		 * less than eight. For example, 15/16 bit displays
+		 * (hi-color) use only 5 bits, 8-bit displays use 2 or
+		 * 3 bits (don't ask me why you'd have an 8-bit
+		 * TrueColor display). So shift back the least
+		 * significant bits.
+		 */
+		destPtr->Red = lut[red];
+		destPtr->Green = lut[green];
+		destPtr->Blue = lut[blue];
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+	XDestroyImage(imagePtr);
+    } else {
+	Blt_HashEntry *hPtr;
+	Blt_HashSearch cursor;
+	Blt_HashTable pixelTable;
+	XColor *colorPtr, *colorArr;
+	Pix32 *endPtr;
+	int nPixels;
+	int nColors;
+	int isNew;
+
+	/*
+	 * Fill the array with each pixel of the image. At the same time, build
+	 * up a hashtable of the pixels used.
+	 */
+	nPixels = width * height;
+	Blt_InitHashTableWithPool(&pixelTable, BLT_ONE_WORD_KEYS);
+	destPtr = Blt_ColorImageBits(image);
+	for (y = 0; y < height; y++) {
+	    for (x = 0; x < width; x++) {
+		pixel = XGetPixel(imagePtr, x, y);
+		hPtr = Blt_CreateHashEntry(&pixelTable, (char *)pixel, &isNew);
+		if (isNew) {
+		    Blt_SetHashValue(hPtr, (char *)pixel);
+		}
+		destPtr->value = pixel;
+		destPtr++;
+	    }
+	}
+	XDestroyImage(imagePtr);
+
+	/* 
+	 * Convert the hashtable of pixels into an array of XColors so
+	 * that we can call XQueryColors with it. XQueryColors will
+	 * convert the pixels into their RGB values.  
+	 */
+	nColors = pixelTable.numEntries;
+	colorArr = Blt_Malloc(sizeof(XColor) * nColors);
+	assert(colorArr);
+
+	colorPtr = colorArr;
+	for (hPtr = Blt_FirstHashEntry(&pixelTable, &cursor); hPtr != NULL;
+	    hPtr = Blt_NextHashEntry(&cursor)) {
+	    colorPtr->pixel = (unsigned long)Blt_GetHashValue(hPtr);
+	    Blt_SetHashValue(hPtr, (char *)colorPtr);
+	    colorPtr++;
+	}
+	XQueryColors(Tk_Display(tkwin), Tk_Colormap(tkwin), colorArr, nColors);
+
+	/* 
+	 * Go again through the array of pixels, replacing each pixel
+	 * of the image with its RGB value.  
+	 */
+	destPtr = Blt_ColorImageBits(image);
+	endPtr = destPtr + nPixels;
+	for (/* empty */; destPtr < endPtr; destPtr++) {
+	    hPtr = Blt_FindHashEntry(&pixelTable, (char *)destPtr->value);
+	    colorPtr = (XColor *)Blt_GetHashValue(hPtr);
+	    destPtr->Red = lut[colorPtr->red >> 8];
+	    destPtr->Green = lut[colorPtr->green >> 8];
+	    destPtr->Blue = lut[colorPtr->blue >> 8];
+	    destPtr->Alpha = (unsigned char)-1;
+	}
+	Blt_Free(colorArr);
+	Blt_DeleteHashTable(&pixelTable);
+    }
+    return image;
+}
+
+
+Pixmap
+Blt_PhotoImageMask(tkwin, src)
+    Tk_Window tkwin;
+    Tk_PhotoImageBlock src;
+{
+    Pixmap bitmap;
+    int arraySize, bytes_per_line;
+    int offset, count;
+    int value, bitMask;
+    register int x, y;
+    unsigned char *bits;
+    unsigned char *srcPtr;
+    unsigned char *destPtr;
+    unsigned long pixel;
+
+    bytes_per_line = (src.width + 7) / 8;
+    arraySize = src.height * bytes_per_line;
+    bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+    assert(bits);
+    destPtr = bits;
+    offset = count = 0;
+    for (y = 0; y < src.height; y++) {
+	value = 0, bitMask = 1;
+	srcPtr = src.pixelPtr + offset;
+	for (x = 0; x < src.width; /*empty*/ ) {
+	    pixel = (srcPtr[src.offset[3]] != 0x00);
+	    if (pixel) {
+		value |= bitMask;
+	    } else {
+		count++;	/* Count the number of transparent pixels. */
+	    }
+	    bitMask <<= 1;
+	    x++;
+	    if (!(x & 7)) {
+		*destPtr++ = (unsigned char)value;
+		value = 0, bitMask = 1;
+	    }
+	    srcPtr += src.pixelSize;
+	}
+	if (x & 7) {
+	    *destPtr++ = (unsigned char)value;
+	}
+	offset += src.pitch;
+    }
+    if (count > 0) {
+	Tk_MakeWindowExist(tkwin);
+	bitmap = XCreateBitmapFromData(Tk_Display(tkwin), Tk_WindowId(tkwin),
+	    (char *)bits, (unsigned int)src.width, (unsigned int)src.height);
+    } else {
+	bitmap = None;		/* Image is opaque. */
+    }
+    Blt_Free(bits);
+    return bitmap;
+}
+
+Pixmap
+Blt_ColorImageMask(tkwin, image)
+    Tk_Window tkwin;
+    Blt_ColorImage image;
+{
+    Pixmap bitmap;
+    int arraySize, bytes_per_line;
+    int count;
+    int value, bitMask;
+    register int x, y;
+    unsigned char *bits;
+    Pix32 *srcPtr;
+    unsigned char *destPtr;
+    unsigned long pixel;
+    int width, height;
+
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+    bytes_per_line = (width + 7) / 8;
+    arraySize = height * bytes_per_line;
+    bits = Blt_Malloc(sizeof(unsigned char) * arraySize);
+    assert(bits);
+    destPtr = bits;
+    count = 0;
+    srcPtr = Blt_ColorImageBits(image);
+    for (y = 0; y < height; y++) {
+	value = 0, bitMask = 1;
+	for (x = 0; x < width; /*empty*/ ) {
+	    pixel = (srcPtr->Alpha != 0x00);
+	    if (pixel) {
+		value |= bitMask;
+	    } else {
+		count++;	/* Count the number of transparent pixels. */
+	    }
+	    bitMask <<= 1;
+	    x++;
+	    if (!(x & 7)) {
+		*destPtr++ = (unsigned char)value;
+		value = 0, bitMask = 1;
+	    }
+	    srcPtr++;
+	}
+	if (x & 7) {
+	    *destPtr++ = (unsigned char)value;
+	}
+    }
+    if (count > 0) {
+	Tk_MakeWindowExist(tkwin);
+	bitmap = XCreateBitmapFromData(Tk_Display(tkwin), Tk_WindowId(tkwin),
+	    (char *)bits, (unsigned int)width, (unsigned int)height);
+    } else {
+	bitmap = None;		/* Image is opaque. */
+    }
+    Blt_Free(bits);
+    return bitmap;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_RotateBitmap --
+ *
+ *	Creates a new bitmap containing the rotated image of the given
+ *	bitmap.  We also need a special GC of depth 1, so that we do
+ *	not need to rotate more than one plane of the bitmap.
+ *
+ * Results:
+ *	Returns a new bitmap containing the rotated image.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_RotateBitmap(tkwin, srcBitmap, srcWidth, srcHeight, theta,
+    destWidthPtr, destHeightPtr)
+    Tk_Window tkwin;
+    Pixmap srcBitmap;		/* Source bitmap to be rotated */
+    int srcWidth, srcHeight;	/* Width and height of the source bitmap */
+    double theta;		/* Right angle rotation to perform */
+    int *destWidthPtr, *destHeightPtr;
+{
+    Display *display;		/* X display */
+    Window root;		/* Root window drawable */
+    Pixmap destBitmap;
+    int destWidth, destHeight;
+    XImage *src, *dest;
+    register int x, y;		/* Destination bitmap coordinates */
+    register int sx, sy;	/* Source bitmap coordinates */
+    unsigned long pixel;
+    GC bitmapGC;
+    double rotWidth, rotHeight;
+
+    display = Tk_Display(tkwin);
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+
+    /* Create a bitmap and image big enough to contain the rotated text */
+    Blt_GetBoundingBox(srcWidth, srcHeight, theta, &rotWidth, &rotHeight,
+	(Point2D *)NULL);
+    destWidth = ROUND(rotWidth);
+    destHeight = ROUND(rotHeight);
+    destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+    bitmapGC = Blt_GetBitmapGC(tkwin);
+    XSetForeground(display, bitmapGC, 0x0);
+    XFillRectangle(display, destBitmap, bitmapGC, 0, 0, destWidth, destHeight);
+
+    src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap);
+    dest = XGetImage(display, destBitmap, 0, 0, destWidth, destHeight, 1,
+	ZPixmap);
+    theta = FMOD(theta, 360.0);
+    if (FMOD(theta, (double)90.0) == 0.0) {
+	int quadrant;
+
+	/* Handle right-angle rotations specifically */
+
+	quadrant = (int)(theta / 90.0);
+	switch (quadrant) {
+	case ROTATE_270:	/* 270 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sx = y;
+		for (x = 0; x < destWidth; x++) {
+		    sy = destWidth - x - 1;
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_180:	/* 180 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sy = destHeight - y - 1;
+		for (x = 0; x < destWidth; x++) {
+		    sx = destWidth - x - 1, 
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_90:		/* 90 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sx = destHeight - y - 1;
+		for (x = 0; x < destWidth; x++) {
+		    sy = x;
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_0:		/* 0 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		for (x = 0; x < destWidth; x++) {
+		    pixel = XGetPixel(src, x, y);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	default:
+	    /* The calling routine should never let this happen. */
+	    break;
+	}
+    } else {
+	double radians, sinTheta, cosTheta;
+	double sox, soy;	/* Offset from the center of
+				 * the source rectangle. */
+	double destCX, destCY;	/* Offset to the center of the destination
+				 * rectangle. */
+	double tx, ty;		/* Translated coordinates from center */
+	double rx, ry;		/* Angle of rotation for x and y coordinates */
+
+	radians = (theta / 180.0) * M_PI;
+	sinTheta = sin(radians), cosTheta = cos(radians);
+
+	/*
+	 * Coordinates of the centers of the source and destination rectangles
+	 */
+	sox = srcWidth * 0.5;
+	soy = srcHeight * 0.5;
+	destCX = destWidth * 0.5;
+	destCY = destHeight * 0.5;
+
+	/* For each pixel of the destination image, transform back to the
+	 * associated pixel in the source image. */
+
+	for (y = 0; y < destHeight; y++) {
+	    ty = y - destCY;
+	    for (x = 0; x < destWidth; x++) {
+
+		/* Translate origin to center of destination image. */
+		tx = x - destCX;
+
+		/* Rotate the coordinates about the origin. */
+		rx = (tx * cosTheta) - (ty * sinTheta);
+		ry = (tx * sinTheta) + (ty * cosTheta);
+
+		/* Translate back to the center of the source image. */
+		rx += sox;
+		ry += soy;
+
+		sx = ROUND(rx);
+		sy = ROUND(ry);
+
+		/*
+		 * Verify the coordinates, since the destination image can be
+		 * bigger than the source.
+		 */
+
+		if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) ||
+		    (sy < 0)) {
+		    continue;
+		}
+		pixel = XGetPixel(src, sx, sy);
+		if (pixel) {
+		    XPutPixel(dest, x, y, pixel);
+		}
+	    }
+	}
+    }
+    /* Write the rotated image into the destination bitmap. */
+    XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, destWidth,
+	destHeight);
+
+    /* Clean up the temporary resources used. */
+    XDestroyImage(src), XDestroyImage(dest);
+    *destWidthPtr = destWidth;
+    *destHeightPtr = destHeight;
+    return destBitmap;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleBitmap --
+ *
+ *	Creates a new scaled bitmap from another bitmap. The new bitmap
+ *	is bounded by a specified region. Only this portion of the bitmap
+ *	is scaled from the original bitmap.
+ *
+ *	By bounding scaling to a region we can generate a new bitmap
+ *	which is no bigger than the specified viewport.
+ *
+ * Results:
+ *	The new scaled bitmap is returned.
+ *
+ * Side Effects:
+ *	A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleBitmap(tkwin, srcBitmap, srcWidth, srcHeight, destWidth, destHeight)
+    Tk_Window tkwin;
+    Pixmap srcBitmap;
+    int srcWidth, srcHeight, destWidth, destHeight;
+{
+    Display *display;
+    GC bitmapGC;
+    Pixmap destBitmap;
+    Window root;
+    XImage *src, *dest;
+    double xScale, yScale;
+    register int sx, sy;	/* Source bitmap coordinates */
+    register int x, y;		/* Destination bitmap coordinates */
+    unsigned long pixel;
+
+    /* Create a new bitmap the size of the region and clear it */
+
+    display = Tk_Display(tkwin);
+
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+    destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+    bitmapGC = Blt_GetBitmapGC(tkwin);
+    XSetForeground(display, bitmapGC, 0x0);
+    XFillRectangle(display, destBitmap, bitmapGC, 0, 0, destWidth, destHeight);
+
+    src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap);
+    dest = XGetImage(display, destBitmap, 0, 0, destWidth, destHeight, 1, 
+		     ZPixmap);
+
+    /*
+     * Scale each pixel of destination image from results of source
+     * image. Verify the coordinates, since the destination image can
+     * be bigger than the source
+     */
+    xScale = (double)srcWidth / (double)destWidth;
+    yScale = (double)srcHeight / (double)destHeight;
+
+    /* Map each pixel in the destination image back to the source. */
+    for (y = 0; y < destHeight; y++) {
+	sy = (int)(yScale * (double)y);
+	for (x = 0; x < destWidth; x++) {
+	    sx = (int)(xScale * (double)x);
+	    pixel = XGetPixel(src, sx, sy);
+	    if (pixel) {
+		XPutPixel(dest, x, y, pixel);
+	    }
+	}
+    }
+    /* Write the scaled image into the destination bitmap */
+
+    XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, 
+	destWidth, destHeight);
+    XDestroyImage(src), XDestroyImage(dest);
+    return destBitmap;
+}
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_RotateScaleBitmapRegion --
+ *
+ *	Creates a scaled and rotated bitmap from a given bitmap.  The
+ *	caller also provides (offsets and dimensions) the region of
+ *	interest in the destination bitmap.  This saves having to
+ *	process the entire destination bitmap is only part of it is
+ *	showing in the viewport.
+ *
+ *	This uses a simple rotation/scaling of each pixel in the 
+ *	destination image.  For each pixel, the corresponding 
+ *	pixel in the source bitmap is used.  This means that 
+ *	destination coordinates are first scaled to the size of 
+ *	the rotated source bitmap.  These coordinates are then
+ *	rotated back to their original orientation in the source.
+ *
+ * Results:
+ *	The new rotated and scaled bitmap is returned.
+ *
+ * Side Effects:
+ *	A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleRotateBitmapRegion(
+    Tk_Window tkwin,
+    Pixmap srcBitmap,		/* Source bitmap. */
+    unsigned int srcWidth, 
+    unsigned int srcHeight,	/* Size of source bitmap */
+    int regionX, 
+    int regionY,		/* Offset of region in virtual
+				 * destination bitmap. */
+    unsigned int regionWidth, 
+    unsigned int regionHeight,	/* Desire size of bitmap region. */
+    unsigned int destWidth,		
+    unsigned int destHeight,	/* Virtual size of destination bitmap. */
+    double theta)		/* Angle to rotate bitmap.  */
+{
+    Display *display;		/* X display */
+    Window root;		/* Root window drawable */
+    Pixmap destBitmap;
+    XImage *src, *dest;
+    register int x, y;		/* Destination bitmap coordinates */
+    register int sx, sy;	/* Source bitmap coordinates */
+    unsigned long pixel;
+    double xScale, yScale;
+    double rotWidth, rotHeight;
+    GC bitmapGC;
+
+    display = Tk_Display(tkwin);
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+
+    /* Create a bitmap and image big enough to contain the rotated text */
+    bitmapGC = Blt_GetBitmapGC(tkwin);
+    destBitmap = Tk_GetPixmap(display, root, regionWidth, regionHeight, 1);
+    XSetForeground(display, bitmapGC, 0x0);
+    XFillRectangle(display, destBitmap, bitmapGC, 0, 0, regionWidth, 
+	regionHeight);
+
+    src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap);
+    dest = XGetImage(display, destBitmap, 0, 0, regionWidth, regionHeight, 1,
+	ZPixmap);
+    theta = FMOD(theta, 360.0);
+
+    Blt_GetBoundingBox(srcWidth, srcHeight, theta, &rotWidth, &rotHeight,
+		       (Point2D *)NULL);
+    xScale = rotWidth / (double)destWidth;
+    yScale = rotHeight / (double)destHeight;
+
+    if (FMOD(theta, (double)90.0) == 0.0) {
+	int quadrant;
+
+	/* Handle right-angle rotations specifically */
+
+	quadrant = (int)(theta / 90.0);
+	switch (quadrant) {
+	case ROTATE_270:	/* 270 degrees */
+	    for (y = 0; y < regionHeight; y++) {
+		sx = (int)(yScale * (double)(y + regionY));
+		for (x = 0; x < regionWidth; x++) {
+		    sy = (int)(xScale *(double)(destWidth - (x + regionX) - 1));
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_180:	/* 180 degrees */
+	    for (y = 0; y < regionHeight; y++) {
+		sy = (int)(yScale * (double)(destHeight - (y + regionY) - 1));
+		for (x = 0; x < regionWidth; x++) {
+		    sx = (int)(xScale *(double)(destWidth - (x + regionX) - 1));
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_90:		/* 90 degrees */
+	    for (y = 0; y < regionHeight; y++) {
+		sx = (int)(yScale * (double)(destHeight - (y + regionY) - 1));
+		for (x = 0; x < regionWidth; x++) {
+		    sy = (int)(xScale * (double)(x + regionX));
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_0:		/* 0 degrees */
+	    for (y = 0; y < regionHeight; y++) {
+		sy = (int)(yScale * (double)(y + regionY));
+		for (x = 0; x < regionWidth; x++) {
+		    sx = (int)(xScale * (double)(x + regionX));
+		    pixel = XGetPixel(src, sx, sy);
+		    if (pixel) {
+			XPutPixel(dest, x, y, pixel);
+		    }
+		}
+	    }
+	    break;
+
+	default:
+	    /* The calling routine should never let this happen. */
+	    break;
+	}
+    } else {
+	double radians, sinTheta, cosTheta;
+	double sox, soy; 	/* Offset from the center of the
+				 * source rectangle. */
+	double rox, roy; 	/* Offset to the center of the
+				 * rotated rectangle. */
+	double tx, ty;		/* Translated coordinates from center */
+	double rx, ry;		/* Angle of rotation for x and y coordinates */
+
+	radians = (theta / 180.0) * M_PI;
+	sinTheta = sin(radians), cosTheta = cos(radians);
+
+	/*
+	 * Coordinates of the centers of the source and destination rectangles
+	 */
+	sox = srcWidth * 0.5;
+	soy = srcHeight * 0.5;
+	rox = rotWidth * 0.5;
+	roy = rotHeight * 0.5;
+
+	/* For each pixel of the destination image, transform back to the
+	 * associated pixel in the source image. */
+
+	for (y = 0; y < regionHeight; y++) {
+	    ty = (yScale * (double)(y + regionY)) - roy;
+	    for (x = 0; x < regionWidth; x++) {
+
+		/* Translate origin to center of destination image. */
+		tx = (xScale * (double)(x + regionX)) - rox;
+
+		/* Rotate the coordinates about the origin. */
+		rx = (tx * cosTheta) - (ty * sinTheta);
+		ry = (tx * sinTheta) + (ty * cosTheta);
+
+		/* Translate back to the center of the source image. */
+		rx += sox;
+		ry += soy;
+
+		sx = ROUND(rx);
+		sy = ROUND(ry);
+
+		/*
+		 * Verify the coordinates, since the destination image can be
+		 * bigger than the source.
+		 */
+
+		if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) ||
+		    (sy < 0)) {
+		    continue;
+		}
+		pixel = XGetPixel(src, sx, sy);
+		if (pixel) {
+		    XPutPixel(dest, x, y, pixel);
+		}
+	    }
+	}
+    }
+    /* Write the rotated image into the destination bitmap. */
+    XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, regionWidth,
+      regionHeight);
+
+    /* Clean up the temporary resources used. */
+    XDestroyImage(src), XDestroyImage(dest);
+    return destBitmap;
+
+}
+
+#if HAVE_JPEGLIB_H
+
+#undef HAVE_STDLIB_H
+#undef EXTERN
+#ifdef WIN32
+#define XMD_H	1
+#endif
+#include "jpeglib.h"
+#include <setjmp.h>
+
+typedef struct {
+    struct jpeg_error_mgr pub;	/* "public" fields */
+    jmp_buf jmpBuf;
+    Tcl_DString dString;
+} ReaderHandler;
+
+static void ErrorProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+static void MessageProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+static void
+ErrorProc(jpgPtr)
+    j_common_ptr jpgPtr;
+{
+    ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+
+    (*handlerPtr->pub.output_message) (jpgPtr);
+    longjmp(handlerPtr->jmpBuf, 1);
+}
+
+static void
+MessageProc(jpgPtr)
+    j_common_ptr jpgPtr;
+{
+    ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+    char buffer[JMSG_LENGTH_MAX];
+
+    /* Create the message and append it into the dynamic string. */
+    (*handlerPtr->pub.format_message) (jpgPtr, buffer);
+    Tcl_DStringAppend(&(handlerPtr->dString), " ", -1);
+    Tcl_DStringAppend(&(handlerPtr->dString), buffer, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToColorImage --
+ *
+ *      Reads a JPEG file and converts it into a color image.
+ *
+ * Results:
+ *      The color image is returned.  If an error occured, such
+ *	as the designated file could not be opened, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_JPEGToColorImage(interp, fileName)
+    Tcl_Interp *interp;
+    char *fileName;
+{
+    struct jpeg_decompress_struct jpg;
+    Blt_ColorImage image;
+    unsigned int imageWidth, imageHeight;
+    register Pix32 *destPtr;
+    ReaderHandler handler;
+    FILE *f;
+    JSAMPLE **readBuffer;
+    int row_stride;
+    register int i;
+    register JSAMPLE *bufPtr;
+
+    f = fopen(fileName, "rb");
+    if (f == NULL) {
+	Tcl_AppendResult(interp, "can't open \"", fileName, "\":",
+	    Tcl_PosixError(interp), (char *)NULL);
+	return NULL;
+    }
+    image = NULL;
+
+    /* Step 1: allocate and initialize JPEG decompression object */
+
+    /* We set up the normal JPEG error routines, then override error_exit. */
+    jpg.dct_method = JDCT_IFAST;
+    jpg.err = jpeg_std_error(&handler.pub);
+    handler.pub.error_exit = ErrorProc;
+    handler.pub.output_message = MessageProc;
+
+    Tcl_DStringInit(&handler.dString);
+    Tcl_DStringAppend(&handler.dString, "error reading \"", -1);
+    Tcl_DStringAppend(&handler.dString, fileName, -1);
+    Tcl_DStringAppend(&handler.dString, "\": ", -1);
+
+    if (setjmp(handler.jmpBuf)) {
+	jpeg_destroy_decompress(&jpg);
+	fclose(f);
+	Tcl_DStringResult(interp, &(handler.dString));
+	return NULL;
+    }
+    jpeg_create_decompress(&jpg);
+    jpeg_stdio_src(&jpg, f);
+
+    jpeg_read_header(&jpg, TRUE);	/* Step 3: read file parameters */
+
+    jpeg_start_decompress(&jpg);	/* Step 5: Start decompressor */
+    imageWidth = jpg.output_width;
+    imageHeight = jpg.output_height;
+    if ((imageWidth < 1) || (imageHeight < 1)) {
+	Tcl_AppendResult(interp, "bad JPEG image size", (char *)NULL);
+	fclose(f);
+	return NULL;
+    }
+    /* JSAMPLEs per row in output buffer */
+    row_stride = imageWidth * jpg.output_components;
+
+    /* Make a one-row-high sample array that will go away when done
+     * with image */
+    readBuffer = (*jpg.mem->alloc_sarray) ((j_common_ptr)&jpg, JPOOL_IMAGE, 
+	row_stride, 1);
+    image = Blt_CreateColorImage(imageWidth, imageHeight);
+    destPtr = Blt_ColorImageBits(image);
+
+    if (jpg.output_components == 1) {
+	while (jpg.output_scanline < imageHeight) {
+	    jpeg_read_scanlines(&jpg, readBuffer, 1);
+	    bufPtr = readBuffer[0];
+	    for (i = 0; i < (int)imageWidth; i++) {
+		destPtr->Red = destPtr->Green = destPtr->Blue = *bufPtr++;
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    } else {
+	while (jpg.output_scanline < imageHeight) {
+	    jpeg_read_scanlines(&jpg, readBuffer, 1);
+	    bufPtr = readBuffer[0];
+	    for (i = 0; i < (int)imageWidth; i++) {
+		destPtr->Red = *bufPtr++;
+		destPtr->Green = *bufPtr++;
+		destPtr->Blue = *bufPtr++;
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    }
+    jpeg_finish_decompress(&jpg);	/* We can ignore the return value
+					 * since suspension is not
+					 * possible with the stdio data
+					 * source.  */
+    jpeg_destroy_decompress(&jpg);
+
+
+    /*  
+     * After finish_decompress, we can close the input file.  Here we
+     * postpone it until after no more JPEG errors are possible, so as
+     * to simplify the setjmp error logic above.  (Actually, I don't
+     * think that jpeg_destroy can do an error exit, but why assume
+     * anything...)  
+     */
+    fclose(f);
+
+    /* 
+     * At this point you may want to check to see whether any corrupt-data
+     * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+     */
+    if (handler.pub.num_warnings > 0) {
+	Tcl_SetErrorCode(interp, "IMAGE", "JPEG", 
+		 Tcl_DStringValue(&(handler.dString)), (char *)NULL);
+    } else {
+	Tcl_SetErrorCode(interp, "NONE", (char *)NULL);
+    }
+    /*
+     * We're ready to call the Tk_Photo routines. They'll take the RGB
+     * array we've processed to build the Tk image of the JPEG.
+     */
+    Tcl_DStringFree(&(handler.dString));
+    return image;
+}
+
+#endif /* HAVE_JPEGLIB_H */
Index: trunk/kitgen/8.x/blt/vertical_axis.tcl
===================================================================
--- trunk/kitgen/8.x/blt/vertical_axis.tcl	(revision 175)
+++ trunk/kitgen/8.x/blt/vertical_axis.tcl	(revision 175)
@@ -0,0 +1,39 @@
+package require BLT
+
+pack [blt::graph .g]
+
+#  Proc passed as a callback to BLT to draw custom tick labels.
+#
+proc format_yAxis_tick {win value} {
+    if {$value == [$win axis cget y -max]} {
+        set yAxisHeightPixels [expr {abs (
+            [$win axis transform y [$win axis cget y -max]] -
+            [$win axis transform y [$win axis cget y -min]])}]
+
+        set font [$win axis cget y -tickfont]
+
+        set yAxisHeightLines  [expr {$yAxisHeightPixels /
+            [font metrics $font -linespace]}]
+
+        set v [string repeat "\n" [expr {$yAxisHeightLines / 2}]]
+        set h [string repeat " "  [expr {
+            [font measure $font "$::y_title           "] /
+            [font measure $font " "]}]]
+        return "$v$h$value$v$::y_title"
+    } else {
+        return $value
+    }
+}
+
+#  The following global is used by the callback "format_yAxis_tick".
+set y_title "Y axis title"
+
+#  Configure the vertical axis.
+.g axis configure y -min 0 -max 100 -majorticks     \
+        {0 10 20 30 40 50 60 70 80 90 100}
+
+#  Need to force the graph to be drawn before distances can be
+#  measured.
+update
+
+.g axis configure y -command format_yAxis_tick
Index: trunk/kitgen/8.x/blt/win/X11/X.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/X.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/X.h	(revision 175)
@@ -0,0 +1,673 @@
+/*
+ *	$XConsortium: X.h,v 1.66 88/09/06 15:55:56 jim Exp $
+ */
+
+/* Definitions for the X window system likely to be used by applications */
+
+#ifndef X_H
+#define X_H
+
+/***********************************************************
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
+and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
+
+                        All Rights Reserved
+
+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 names of Digital or MIT not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+#define X_PROTOCOL	11		/* current protocol version */
+#define X_PROTOCOL_REVISION 0		/* current minor version */
+
+#ifdef MAC_TCL
+#   define Cursor XCursor
+#   define Region XRegion
+#endif
+
+/* Resources */
+
+typedef unsigned long XID;
+
+typedef XID Window;
+typedef XID Drawable;
+typedef XID Font;
+typedef XID Pixmap;
+typedef XID Cursor;
+typedef XID Colormap;
+typedef XID GContext;
+typedef XID KeySym;
+
+typedef unsigned long Mask;
+
+typedef unsigned long Atom;
+
+typedef unsigned long VisualID;
+
+typedef unsigned long Time;
+
+typedef unsigned long KeyCode;	/* In order to use IME, the Macintosh needs
+				 * to pack 3 bytes into the keyCode field in
+				 * the XEvent.  In the real X.h, a KeyCode is
+				 * defined as a short, which wouldn't be big
+				 * enough. */
+
+/*****************************************************************
+ * RESERVED RESOURCE AND CONSTANT DEFINITIONS
+ *****************************************************************/
+
+#define None                 0L	/* universal null resource or null atom */
+
+#define ParentRelative       1L	/* background pixmap in CreateWindow
+				    and ChangeWindowAttributes */
+
+#define CopyFromParent       0L	/* border pixmap in CreateWindow
+				       and ChangeWindowAttributes
+				   special VisualID and special window
+				       class passed to CreateWindow */
+
+#define PointerWindow        0L	/* destination window in SendEvent */
+#define InputFocus           1L	/* destination window in SendEvent */
+
+#define PointerRoot          1L	/* focus window in SetInputFocus */
+
+#define AnyPropertyType      0L	/* special Atom, passed to GetProperty */
+
+#define AnyKey		     0L	/* special Key Code, passed to GrabKey */
+
+#define AnyButton            0L	/* special Button Code, passed to GrabButton */
+
+#define AllTemporary         0L	/* special Resource ID passed to KillClient */
+
+#define CurrentTime          0L	/* special Time */
+
+#define NoSymbol	     0L	/* special KeySym */
+
+/***************************************************************** 
+ * EVENT DEFINITIONS 
+ *****************************************************************/
+
+/* Input Event Masks. Used as event-mask window attribute and as arguments
+   to Grab requests.  Not to be confused with event names.  */
+
+#define NoEventMask			0L
+#define KeyPressMask			(1L<<0)  
+#define KeyReleaseMask			(1L<<1)  
+#define ButtonPressMask			(1L<<2)  
+#define ButtonReleaseMask		(1L<<3)  
+#define EnterWindowMask			(1L<<4)  
+#define LeaveWindowMask			(1L<<5)  
+#define PointerMotionMask		(1L<<6)  
+#define PointerMotionHintMask		(1L<<7)  
+#define Button1MotionMask		(1L<<8)  
+#define Button2MotionMask		(1L<<9)  
+#define Button3MotionMask		(1L<<10) 
+#define Button4MotionMask		(1L<<11) 
+#define Button5MotionMask		(1L<<12) 
+#define ButtonMotionMask		(1L<<13) 
+#define KeymapStateMask			(1L<<14)
+#define ExposureMask			(1L<<15) 
+#define VisibilityChangeMask		(1L<<16) 
+#define StructureNotifyMask		(1L<<17) 
+#define ResizeRedirectMask		(1L<<18) 
+#define SubstructureNotifyMask		(1L<<19) 
+#define SubstructureRedirectMask	(1L<<20) 
+#define FocusChangeMask			(1L<<21) 
+#define PropertyChangeMask		(1L<<22) 
+#define ColormapChangeMask		(1L<<23) 
+#define OwnerGrabButtonMask		(1L<<24) 
+
+/* Event names.  Used in "type" field in XEvent structures.  Not to be
+confused with event masks above.  They start from 2 because 0 and 1
+are reserved in the protocol for errors and replies. */
+
+#define KeyPress		2
+#define KeyRelease		3
+#define ButtonPress		4
+#define ButtonRelease		5
+#define MotionNotify		6
+#define EnterNotify		7
+#define LeaveNotify		8
+#define FocusIn			9
+#define FocusOut		10
+#define KeymapNotify		11
+#define Expose			12
+#define GraphicsExpose		13
+#define NoExpose		14
+#define VisibilityNotify	15
+#define CreateNotify		16
+#define DestroyNotify		17
+#define UnmapNotify		18
+#define MapNotify		19
+#define MapRequest		20
+#define ReparentNotify		21
+#define ConfigureNotify		22
+#define ConfigureRequest	23
+#define GravityNotify		24
+#define ResizeRequest		25
+#define CirculateNotify		26
+#define CirculateRequest	27
+#define PropertyNotify		28
+#define SelectionClear		29
+#define SelectionRequest	30
+#define SelectionNotify		31
+#define ColormapNotify		32
+#define ClientMessage		33
+#define MappingNotify		34
+#define LASTEvent		35	/* must be bigger than any event # */
+
+
+/* Key masks. Used as modifiers to GrabButton and GrabKey, results of QueryPointer,
+   state in various key-, mouse-, and button-related events. */
+
+#define ShiftMask		(1<<0)
+#define LockMask		(1<<1)
+#define ControlMask		(1<<2)
+#define Mod1Mask		(1<<3)
+#define Mod2Mask		(1<<4)
+#define Mod3Mask		(1<<5)
+#define Mod4Mask		(1<<6)
+#define Mod5Mask		(1<<7)
+
+/* modifier names.  Used to build a SetModifierMapping request or
+   to read a GetModifierMapping request.  These correspond to the
+   masks defined above. */
+#define ShiftMapIndex		0
+#define LockMapIndex		1
+#define ControlMapIndex		2
+#define Mod1MapIndex		3
+#define Mod2MapIndex		4
+#define Mod3MapIndex		5
+#define Mod4MapIndex		6
+#define Mod5MapIndex		7
+
+
+/* button masks.  Used in same manner as Key masks above. Not to be confused
+   with button names below. */
+
+#define Button1Mask		(1<<8)
+#define Button2Mask		(1<<9)
+#define Button3Mask		(1<<10)
+#define Button4Mask		(1<<11)
+#define Button5Mask		(1<<12)
+
+#define AnyModifier		(1<<15)  /* used in GrabButton, GrabKey */
+
+
+/* button names. Used as arguments to GrabButton and as detail in ButtonPress
+   and ButtonRelease events.  Not to be confused with button masks above.
+   Note that 0 is already defined above as "AnyButton".  */
+
+#define Button1			1
+#define Button2			2
+#define Button3			3
+#define Button4			4
+#define Button5			5
+
+/* Notify modes */
+
+#define NotifyNormal		0
+#define NotifyGrab		1
+#define NotifyUngrab		2
+#define NotifyWhileGrabbed	3
+
+#define NotifyHint		1	/* for MotionNotify events */
+		       
+/* Notify detail */
+
+#define NotifyAncestor		0
+#define NotifyVirtual		1
+#define NotifyInferior		2
+#define NotifyNonlinear		3
+#define NotifyNonlinearVirtual	4
+#define NotifyPointer		5
+#define NotifyPointerRoot	6
+#define NotifyDetailNone	7
+
+/* Visibility notify */
+
+#define VisibilityUnobscured		0
+#define VisibilityPartiallyObscured	1
+#define VisibilityFullyObscured		2
+
+/* Circulation request */
+
+#define PlaceOnTop		0
+#define PlaceOnBottom		1
+
+/* protocol families */
+
+#define FamilyInternet		0
+#define FamilyDECnet		1
+#define FamilyChaos		2
+
+/* Property notification */
+
+#define PropertyNewValue	0
+#define PropertyDelete		1
+
+/* Color Map notification */
+
+#define ColormapUninstalled	0
+#define ColormapInstalled	1
+
+/* GrabPointer, GrabButton, GrabKeyboard, GrabKey Modes */
+
+#define GrabModeSync		0
+#define GrabModeAsync		1
+
+/* GrabPointer, GrabKeyboard reply status */
+
+#define GrabSuccess		0
+#define AlreadyGrabbed		1
+#define GrabInvalidTime		2
+#define GrabNotViewable		3
+#define GrabFrozen		4
+
+/* AllowEvents modes */
+
+#define AsyncPointer		0
+#define SyncPointer		1
+#define ReplayPointer		2
+#define AsyncKeyboard		3
+#define SyncKeyboard		4
+#define ReplayKeyboard		5
+#define AsyncBoth		6
+#define SyncBoth		7
+
+/* Used in SetInputFocus, GetInputFocus */
+
+#define RevertToNone		(int)None
+#define RevertToPointerRoot	(int)PointerRoot
+#define RevertToParent		2
+
+/*****************************************************************
+ * ERROR CODES 
+ *****************************************************************/
+
+#define Success		   0	/* everything's okay */
+#define BadRequest	   1	/* bad request code */
+#define BadValue	   2	/* int parameter out of range */
+#define BadWindow	   3	/* parameter not a Window */
+#define BadPixmap	   4	/* parameter not a Pixmap */
+#define BadAtom		   5	/* parameter not an Atom */
+#define BadCursor	   6	/* parameter not a Cursor */
+#define BadFont		   7	/* parameter not a Font */
+#define BadMatch	   8	/* parameter mismatch */
+#define BadDrawable	   9	/* parameter not a Pixmap or Window */
+#define BadAccess	  10	/* depending on context:
+				 - key/button already grabbed
+				 - attempt to free an illegal 
+				   cmap entry 
+				- attempt to store into a read-only 
+				   color map entry.
+ 				- attempt to modify the access control
+				   list from other than the local host.
+				*/
+#define BadAlloc	  11	/* insufficient resources */
+#define BadColor	  12	/* no such colormap */
+#define BadGC		  13	/* parameter not a GC */
+#define BadIDChoice	  14	/* choice not in range or already used */
+#define BadName		  15	/* font or color name doesn't exist */
+#define BadLength	  16	/* Request length incorrect */
+#define BadImplementation 17	/* server is defective */
+
+#define FirstExtensionError	128
+#define LastExtensionError	255
+
+/*****************************************************************
+ * WINDOW DEFINITIONS 
+ *****************************************************************/
+
+/* Window classes used by CreateWindow */
+/* Note that CopyFromParent is already defined as 0 above */
+
+#define InputOutput		1
+#define InputOnly		2
+
+/* Window attributes for CreateWindow and ChangeWindowAttributes */
+
+#define CWBackPixmap		(1L<<0)
+#define CWBackPixel		(1L<<1)
+#define CWBorderPixmap		(1L<<2)
+#define CWBorderPixel           (1L<<3)
+#define CWBitGravity		(1L<<4)
+#define CWWinGravity		(1L<<5)
+#define CWBackingStore          (1L<<6)
+#define CWBackingPlanes	        (1L<<7)
+#define CWBackingPixel	        (1L<<8)
+#define CWOverrideRedirect	(1L<<9)
+#define CWSaveUnder		(1L<<10)
+#define CWEventMask		(1L<<11)
+#define CWDontPropagate	        (1L<<12)
+#define CWColormap		(1L<<13)
+#define CWCursor	        (1L<<14)
+
+/* ConfigureWindow structure */
+
+#define CWX			(1<<0)
+#define CWY			(1<<1)
+#define CWWidth			(1<<2)
+#define CWHeight		(1<<3)
+#define CWBorderWidth		(1<<4)
+#define CWSibling		(1<<5)
+#define CWStackMode		(1<<6)
+
+
+/* Bit Gravity */
+
+#define ForgetGravity		0
+#define NorthWestGravity	1
+#define NorthGravity		2
+#define NorthEastGravity	3
+#define WestGravity		4
+#define CenterGravity		5
+#define EastGravity		6
+#define SouthWestGravity	7
+#define SouthGravity		8
+#define SouthEastGravity	9
+#define StaticGravity		10
+
+/* Window gravity + bit gravity above */
+
+#define UnmapGravity		0
+
+/* Used in CreateWindow for backing-store hint */
+
+#define NotUseful               0
+#define WhenMapped              1
+#define Always                  2
+
+/* Used in GetWindowAttributes reply */
+
+#define IsUnmapped		0
+#define IsUnviewable		1
+#define IsViewable		2
+
+/* Used in ChangeSaveSet */
+
+#define SetModeInsert           0
+#define SetModeDelete           1
+
+/* Used in ChangeCloseDownMode */
+
+#define DestroyAll              0
+#define RetainPermanent         1
+#define RetainTemporary         2
+
+/* Window stacking method (in configureWindow) */
+
+#define Above                   0
+#define Below                   1
+#define TopIf                   2
+#define BottomIf                3
+#define Opposite                4
+
+/* Circulation direction */
+
+#define RaiseLowest             0
+#define LowerHighest            1
+
+/* Property modes */
+
+#define PropModeReplace         0
+#define PropModePrepend         1
+#define PropModeAppend          2
+
+/*****************************************************************
+ * GRAPHICS DEFINITIONS
+ *****************************************************************/
+
+/* graphics functions, as in GC.alu */
+
+#define	GXclear			0x0		/* 0 */
+#define GXand			0x1		/* src AND dst */
+#define GXandReverse		0x2		/* src AND NOT dst */
+#define GXcopy			0x3		/* src */
+#define GXandInverted		0x4		/* NOT src AND dst */
+#define	GXnoop			0x5		/* dst */
+#define GXxor			0x6		/* src XOR dst */
+#define GXor			0x7		/* src OR dst */
+#define GXnor			0x8		/* NOT src AND NOT dst */
+#define GXequiv			0x9		/* NOT src XOR dst */
+#define GXinvert		0xa		/* NOT dst */
+#define GXorReverse		0xb		/* src OR NOT dst */
+#define GXcopyInverted		0xc		/* NOT src */
+#define GXorInverted		0xd		/* NOT src OR dst */
+#define GXnand			0xe		/* NOT src OR NOT dst */
+#define GXset			0xf		/* 1 */
+
+/* LineStyle */
+
+#define LineSolid		0
+#define LineOnOffDash		1
+#define LineDoubleDash		2
+
+/* capStyle */
+
+#define CapNotLast		0
+#define CapButt			1
+#define CapRound		2
+#define CapProjecting		3
+
+/* joinStyle */
+
+#define JoinMiter		0
+#define JoinRound		1
+#define JoinBevel		2
+
+/* fillStyle */
+
+#define FillSolid		0
+#define FillTiled		1
+#define FillStippled		2
+#define FillOpaqueStippled	3
+
+/* fillRule */
+
+#define EvenOddRule		0
+#define WindingRule		1
+
+/* subwindow mode */
+
+#define ClipByChildren		0
+#define IncludeInferiors	1
+
+/* SetClipRectangles ordering */
+
+#define Unsorted		0
+#define YSorted			1
+#define YXSorted		2
+#define YXBanded		3
+
+/* CoordinateMode for drawing routines */
+
+#define CoordModeOrigin		0	/* relative to the origin */
+#define CoordModePrevious       1	/* relative to previous point */
+
+/* Polygon shapes */
+
+#define Complex			0	/* paths may intersect */
+#define Nonconvex		1	/* no paths intersect, but not convex */
+#define Convex			2	/* wholly convex */
+
+/* Arc modes for PolyFillArc */
+
+#define ArcChord		0	/* join endpoints of arc */
+#define ArcPieSlice		1	/* join endpoints to center of arc */
+
+/* GC components: masks used in CreateGC, CopyGC, ChangeGC, OR'ed into
+   GC.stateChanges */
+
+#define GCFunction              (1L<<0)
+#define GCPlaneMask             (1L<<1)
+#define GCForeground            (1L<<2)
+#define GCBackground            (1L<<3)
+#define GCLineWidth             (1L<<4)
+#define GCLineStyle             (1L<<5)
+#define GCCapStyle              (1L<<6)
+#define GCJoinStyle		(1L<<7)
+#define GCFillStyle		(1L<<8)
+#define GCFillRule		(1L<<9) 
+#define GCTile			(1L<<10)
+#define GCStipple		(1L<<11)
+#define GCTileStipXOrigin	(1L<<12)
+#define GCTileStipYOrigin	(1L<<13)
+#define GCFont 			(1L<<14)
+#define GCSubwindowMode		(1L<<15)
+#define GCGraphicsExposures     (1L<<16)
+#define GCClipXOrigin		(1L<<17)
+#define GCClipYOrigin		(1L<<18)
+#define GCClipMask		(1L<<19)
+#define GCDashOffset		(1L<<20)
+#define GCDashList		(1L<<21)
+#define GCArcMode		(1L<<22)
+
+#define GCLastBit		22
+/*****************************************************************
+ * FONTS 
+ *****************************************************************/
+
+/* used in QueryFont -- draw direction */
+
+#define FontLeftToRight		0
+#define FontRightToLeft		1
+
+#define FontChange		255
+
+/*****************************************************************
+ *  IMAGING 
+ *****************************************************************/
+
+/* ImageFormat -- PutImage, GetImage */
+
+#define XYBitmap		0	/* depth 1, XYFormat */
+#define XYPixmap		1	/* depth == drawable depth */
+#define ZPixmap			2	/* depth == drawable depth */
+
+/*****************************************************************
+ *  COLOR MAP STUFF 
+ *****************************************************************/
+
+/* For CreateColormap */
+
+#define AllocNone		0	/* create map with no entries */
+#define AllocAll		1	/* allocate entire map writeable */
+
+
+/* Flags used in StoreNamedColor, StoreColors */
+
+#define DoRed			(1<<0)
+#define DoGreen			(1<<1)
+#define DoBlue			(1<<2)
+
+/*****************************************************************
+ * CURSOR STUFF
+ *****************************************************************/
+
+/* QueryBestSize Class */
+
+#define CursorShape		0	/* largest size that can be displayed */
+#define TileShape		1	/* size tiled fastest */
+#define StippleShape		2	/* size stippled fastest */
+
+/***************************************************************** 
+ * KEYBOARD/POINTER STUFF
+ *****************************************************************/
+
+#define AutoRepeatModeOff	0
+#define AutoRepeatModeOn	1
+#define AutoRepeatModeDefault	2
+
+#define LedModeOff		0
+#define LedModeOn		1
+
+/* masks for ChangeKeyboardControl */
+
+#define KBKeyClickPercent	(1L<<0)
+#define KBBellPercent		(1L<<1)
+#define KBBellPitch		(1L<<2)
+#define KBBellDuration		(1L<<3)
+#define KBLed			(1L<<4)
+#define KBLedMode		(1L<<5)
+#define KBKey			(1L<<6)
+#define KBAutoRepeatMode	(1L<<7)
+
+#define MappingSuccess     	0
+#define MappingBusy        	1
+#define MappingFailed		2
+
+#define MappingModifier		0
+#define MappingKeyboard		1
+#define MappingPointer		2
+
+/*****************************************************************
+ * SCREEN SAVER STUFF 
+ *****************************************************************/
+
+#define DontPreferBlanking	0
+#define PreferBlanking		1
+#define DefaultBlanking		2
+
+#define DisableScreenSaver	0
+#define DisableScreenInterval	0
+
+#define DontAllowExposures	0
+#define AllowExposures		1
+#define DefaultExposures	2
+
+/* for ForceScreenSaver */
+
+#define ScreenSaverReset 0
+#define ScreenSaverActive 1
+
+/*****************************************************************
+ * HOSTS AND CONNECTIONS
+ *****************************************************************/
+
+/* for ChangeHosts */
+
+#define HostInsert		0
+#define HostDelete		1
+
+/* for ChangeAccessControl */
+
+#define EnableAccess		1      
+#define DisableAccess		0
+
+/* Display classes  used in opening the connection 
+ * Note that the statically allocated ones are even numbered and the
+ * dynamically changeable ones are odd numbered */
+
+#define StaticGray		0
+#define GrayScale		1
+#define StaticColor		2
+#define PseudoColor		3
+#define TrueColor		4
+#define DirectColor		5
+
+
+/* Byte order  used in imageByteOrder and bitmapBitOrder */
+
+#define LSBFirst		0
+#define MSBFirst		1
+
+#ifdef MAC_TCL
+#   undef Cursor
+#   undef Region
+#endif
+
+#endif /* X_H */
Index: trunk/kitgen/8.x/blt/win/X11/Xatom.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/Xatom.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/Xatom.h	(revision 175)
@@ -0,0 +1,79 @@
+#ifndef XATOM_H
+#define XATOM_H 1
+
+/* THIS IS A GENERATED FILE
+ *
+ * Do not change!  Changing this file implies a protocol change!
+ */
+
+#define XA_PRIMARY ((Atom) 1)
+#define XA_SECONDARY ((Atom) 2)
+#define XA_ARC ((Atom) 3)
+#define XA_ATOM ((Atom) 4)
+#define XA_BITMAP ((Atom) 5)
+#define XA_CARDINAL ((Atom) 6)
+#define XA_COLORMAP ((Atom) 7)
+#define XA_CURSOR ((Atom) 8)
+#define XA_CUT_BUFFER0 ((Atom) 9)
+#define XA_CUT_BUFFER1 ((Atom) 10)
+#define XA_CUT_BUFFER2 ((Atom) 11)
+#define XA_CUT_BUFFER3 ((Atom) 12)
+#define XA_CUT_BUFFER4 ((Atom) 13)
+#define XA_CUT_BUFFER5 ((Atom) 14)
+#define XA_CUT_BUFFER6 ((Atom) 15)
+#define XA_CUT_BUFFER7 ((Atom) 16)
+#define XA_DRAWABLE ((Atom) 17)
+#define XA_FONT ((Atom) 18)
+#define XA_INTEGER ((Atom) 19)
+#define XA_PIXMAP ((Atom) 20)
+#define XA_POINT ((Atom) 21)
+#define XA_RECTANGLE ((Atom) 22)
+#define XA_RESOURCE_MANAGER ((Atom) 23)
+#define XA_RGB_COLOR_MAP ((Atom) 24)
+#define XA_RGB_BEST_MAP ((Atom) 25)
+#define XA_RGB_BLUE_MAP ((Atom) 26)
+#define XA_RGB_DEFAULT_MAP ((Atom) 27)
+#define XA_RGB_GRAY_MAP ((Atom) 28)
+#define XA_RGB_GREEN_MAP ((Atom) 29)
+#define XA_RGB_RED_MAP ((Atom) 30)
+#define XA_STRING ((Atom) 31)
+#define XA_VISUALID ((Atom) 32)
+#define XA_WINDOW ((Atom) 33)
+#define XA_WM_COMMAND ((Atom) 34)
+#define XA_WM_HINTS ((Atom) 35)
+#define XA_WM_CLIENT_MACHINE ((Atom) 36)
+#define XA_WM_ICON_NAME ((Atom) 37)
+#define XA_WM_ICON_SIZE ((Atom) 38)
+#define XA_WM_NAME ((Atom) 39)
+#define XA_WM_NORMAL_HINTS ((Atom) 40)
+#define XA_WM_SIZE_HINTS ((Atom) 41)
+#define XA_WM_ZOOM_HINTS ((Atom) 42)
+#define XA_MIN_SPACE ((Atom) 43)
+#define XA_NORM_SPACE ((Atom) 44)
+#define XA_MAX_SPACE ((Atom) 45)
+#define XA_END_SPACE ((Atom) 46)
+#define XA_SUPERSCRIPT_X ((Atom) 47)
+#define XA_SUPERSCRIPT_Y ((Atom) 48)
+#define XA_SUBSCRIPT_X ((Atom) 49)
+#define XA_SUBSCRIPT_Y ((Atom) 50)
+#define XA_UNDERLINE_POSITION ((Atom) 51)
+#define XA_UNDERLINE_THICKNESS ((Atom) 52)
+#define XA_STRIKEOUT_ASCENT ((Atom) 53)
+#define XA_STRIKEOUT_DESCENT ((Atom) 54)
+#define XA_ITALIC_ANGLE ((Atom) 55)
+#define XA_X_HEIGHT ((Atom) 56)
+#define XA_QUAD_WIDTH ((Atom) 57)
+#define XA_WEIGHT ((Atom) 58)
+#define XA_POINT_SIZE ((Atom) 59)
+#define XA_RESOLUTION ((Atom) 60)
+#define XA_COPYRIGHT ((Atom) 61)
+#define XA_NOTICE ((Atom) 62)
+#define XA_FONT_NAME ((Atom) 63)
+#define XA_FAMILY_NAME ((Atom) 64)
+#define XA_FULL_NAME ((Atom) 65)
+#define XA_CAP_HEIGHT ((Atom) 66)
+#define XA_WM_CLASS ((Atom) 67)
+#define XA_WM_TRANSIENT_FOR ((Atom) 68)
+
+#define XA_LAST_PREDEFINED ((Atom) 68)
+#endif /* XATOM_H */
Index: trunk/kitgen/8.x/blt/win/X11/Xfuncproto.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/Xfuncproto.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/Xfuncproto.h	(revision 175)
@@ -0,0 +1,60 @@
+/* $XConsortium: Xfuncproto.h,v 1.7 91/05/13 20:49:21 rws Exp $ */
+/* 
+ * Copyright 1989, 1991 by the Massachusetts Institute of Technology
+ *
+ * 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 M.I.T. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific, 
+ * written prior permission. M.I.T. makes no representations about the 
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ */
+
+/* Definitions to make function prototypes manageable */
+
+#ifndef _XFUNCPROTO_H_
+#define _XFUNCPROTO_H_
+
+#ifndef NeedFunctionPrototypes
+#define NeedFunctionPrototypes 1
+#endif /* NeedFunctionPrototypes */
+
+#ifndef NeedVarargsPrototypes
+#define NeedVarargsPrototypes 0
+#endif /* NeedVarargsPrototypes */
+
+#if NeedFunctionPrototypes
+
+#ifndef NeedNestedPrototypes
+#define NeedNestedPrototypes 1
+#endif /* NeedNestedPrototypes */
+
+#ifndef _Xconst
+#define _Xconst const
+#endif /* _Xconst */
+
+#ifndef NeedWidePrototypes
+#ifdef NARROWPROTO
+#define NeedWidePrototypes 0
+#else
+#define NeedWidePrototypes 1		/* default to make interropt. easier */
+#endif
+#endif /* NeedWidePrototypes */
+
+#endif /* NeedFunctionPrototypes */
+
+#ifdef __cplusplus
+#define _XFUNCPROTOBEGIN extern "C" {
+#define _XFUNCPROTOEND }
+#endif
+
+#ifndef _XFUNCPROTOBEGIN
+#define _XFUNCPROTOBEGIN
+#define _XFUNCPROTOEND
+#endif /* _XFUNCPROTOBEGIN */
+
+#endif /* _XFUNCPROTO_H_ */
Index: trunk/kitgen/8.x/blt/win/X11/Xlib.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/Xlib.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/Xlib.h	(revision 175)
@@ -0,0 +1,1468 @@
+/* $XConsortium: Xlib.h,v 11.221 93/07/02 14:13:28 gildea Exp $ */
+/* 
+ * Copyright 1985, 1986, 1987, 1991 by the Massachusetts Institute of Technology
+ *
+ * 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 M.I.T. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific, 
+ * written prior permission. M.I.T. makes no representations about the 
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * X Window System is a Trademark of MIT.
+ *
+ */
+
+
+/*
+ *	Xlib.h - Header definition and support file for the C subroutine
+ *	interface library (Xlib) to the X Window System Protocol (V11).
+ *	Structures and symbols starting with "_" are private to the library.
+ */
+#ifndef _XLIB_H_
+#define _XLIB_H_
+
+#define XlibSpecificationRelease 5
+
+#ifdef MAC_TCL
+#   include <X.h>
+#   define Cursor XCursor
+#   define Region XRegion
+#else
+#   include <X11/X.h>
+#endif
+
+/* applications should not depend on these two headers being included! */
+#ifdef MAC_TCL
+#include <Xfuncproto.h>
+#else
+#include <X11/Xfuncproto.h>
+#endif
+
+#ifndef X_WCHAR
+#ifdef X_NOT_STDC_ENV
+#define X_WCHAR
+#endif
+#endif
+
+#ifndef X_WCHAR
+#include <stddef.h>
+#else
+/* replace this with #include or typedef appropriate for your system */
+typedef unsigned long wchar_t;
+#endif
+
+typedef char *XPointer;
+
+#define Bool int
+#ifdef MAC_TCL
+#define Status int
+#else
+typedef int Status;
+#endif
+#define True 1
+#define False 0
+
+#define QueuedAlready 0
+#define QueuedAfterReading 1
+#define QueuedAfterFlush 2
+
+#define ConnectionNumber(dpy) 	((dpy)->fd)
+#define RootWindow(dpy, scr) 	(((dpy)->screens[(scr)]).root)
+#define DefaultScreen(dpy) 	((dpy)->default_screen)
+#define DefaultRootWindow(dpy) 	(((dpy)->screens[(dpy)->default_screen]).root)
+#define DefaultVisual(dpy, scr) (((dpy)->screens[(scr)]).root_visual)
+#define DefaultGC(dpy, scr) 	(((dpy)->screens[(scr)]).default_gc)
+#define BlackPixel(dpy, scr) 	(((dpy)->screens[(scr)]).black_pixel)
+#define WhitePixel(dpy, scr) 	(((dpy)->screens[(scr)]).white_pixel)
+#define AllPlanes 		((unsigned long)~0L)
+#define QLength(dpy) 		((dpy)->qlen)
+#define DisplayWidth(dpy, scr) 	(((dpy)->screens[(scr)]).width)
+#define DisplayHeight(dpy, scr) (((dpy)->screens[(scr)]).height)
+#define DisplayWidthMM(dpy, scr)(((dpy)->screens[(scr)]).mwidth)
+#define DisplayHeightMM(dpy, scr)(((dpy)->screens[(scr)]).mheight)
+#define DisplayPlanes(dpy, scr) (((dpy)->screens[(scr)]).root_depth)
+#define DisplayCells(dpy, scr) 	(DefaultVisual((dpy), (scr))->map_entries)
+#define ScreenCount(dpy) 	((dpy)->nscreens)
+#define ServerVendor(dpy) 	((dpy)->vendor)
+#define ProtocolVersion(dpy) 	((dpy)->proto_major_version)
+#define ProtocolRevision(dpy) 	((dpy)->proto_minor_version)
+#define VendorRelease(dpy) 	((dpy)->release)
+#define DisplayString(dpy) 	((dpy)->display_name)
+#define DefaultDepth(dpy, scr) 	(((dpy)->screens[(scr)]).root_depth)
+#define DefaultColormap(dpy, scr)(((dpy)->screens[(scr)]).cmap)
+#define BitmapUnit(dpy) 	((dpy)->bitmap_unit)
+#define BitmapBitOrder(dpy) 	((dpy)->bitmap_bit_order)
+#define BitmapPad(dpy) 		((dpy)->bitmap_pad)
+#define ImageByteOrder(dpy) 	((dpy)->byte_order)
+#define NextRequest(dpy)	((dpy)->request + 1)
+#define LastKnownRequestProcessed(dpy)	((dpy)->request)
+
+/* macros for screen oriented applications (toolkit) */
+#define ScreenOfDisplay(dpy, scr)(&((dpy)->screens[(scr)]))
+#define DefaultScreenOfDisplay(dpy) (&((dpy)->screens[(dpy)->default_screen]))
+#define DisplayOfScreen(s)	((s)->display)
+#define RootWindowOfScreen(s)	((s)->root)
+#define BlackPixelOfScreen(s)	((s)->black_pixel)
+#define WhitePixelOfScreen(s)	((s)->white_pixel)
+#define DefaultColormapOfScreen(s)((s)->cmap)
+#define DefaultDepthOfScreen(s)	((s)->root_depth)
+#define DefaultGCOfScreen(s)	((s)->default_gc)
+#define DefaultVisualOfScreen(s)((s)->root_visual)
+#define WidthOfScreen(s)	((s)->width)
+#define HeightOfScreen(s)	((s)->height)
+#define WidthMMOfScreen(s)	((s)->mwidth)
+#define HeightMMOfScreen(s)	((s)->mheight)
+#define PlanesOfScreen(s)	((s)->root_depth)
+#define CellsOfScreen(s)	(DefaultVisualOfScreen((s))->map_entries)
+#define MinCmapsOfScreen(s)	((s)->min_maps)
+#define MaxCmapsOfScreen(s)	((s)->max_maps)
+#define DoesSaveUnders(s)	((s)->save_unders)
+#define DoesBackingStore(s)	((s)->backing_store)
+#define EventMaskOfScreen(s)	((s)->root_input_mask)
+
+/*
+ * Extensions need a way to hang private data on some structures.
+ */
+typedef struct _XExtData {
+	int number;		/* number returned by XRegisterExtension */
+	struct _XExtData *next;	/* next item on list of data for structure */
+	int (*free_private)();	/* called to free private storage */
+	XPointer private_data;	/* data private to this extension. */
+} XExtData;
+
+/*
+ * This file contains structures used by the extension mechanism.
+ */
+typedef struct {		/* public to extension, cannot be changed */
+	int extension;		/* extension number */
+	int major_opcode;	/* major op-code assigned by server */
+	int first_event;	/* first event number for the extension */
+	int first_error;	/* first error number for the extension */
+} XExtCodes;
+
+/*
+ * Data structure for retrieving info about pixmap formats.
+ */
+
+typedef struct {
+    int depth;
+    int bits_per_pixel;
+    int scanline_pad;
+} XPixmapFormatValues;
+
+
+/*
+ * Data structure for setting graphics context.
+ */
+typedef struct {
+	int function;		/* logical operation */
+	unsigned long plane_mask;/* plane mask */
+	unsigned long foreground;/* foreground pixel */
+	unsigned long background;/* background pixel */
+	int line_width;		/* line width */
+	int line_style;	 	/* LineSolid, LineOnOffDash, LineDoubleDash */
+	int cap_style;	  	/* CapNotLast, CapButt, 
+				   CapRound, CapProjecting */
+	int join_style;	 	/* JoinMiter, JoinRound, JoinBevel */
+	int fill_style;	 	/* FillSolid, FillTiled, 
+				   FillStippled, FillOpaeueStippled */
+	int fill_rule;	  	/* EvenOddRule, WindingRule */
+	int arc_mode;		/* ArcChord, ArcPieSlice */
+	Pixmap tile;		/* tile pixmap for tiling operations */
+	Pixmap stipple;		/* stipple 1 plane pixmap for stipping */
+	int ts_x_origin;	/* offset for tile or stipple operations */
+	int ts_y_origin;
+        Font font;	        /* default text font for text operations */
+	int subwindow_mode;     /* ClipByChildren, IncludeInferiors */
+	Bool graphics_exposures;/* boolean, should exposures be generated */
+	int clip_x_origin;	/* origin for clipping */
+	int clip_y_origin;
+	Pixmap clip_mask;	/* bitmap clipping; other calls for rects */
+	int dash_offset;	/* patterned/dashed line information */
+	char dashes;
+} XGCValues;
+
+/*
+ * Graphics context.  The contents of this structure are implementation
+ * dependent.  A GC should be treated as opaque by application code.
+ */
+
+typedef XGCValues *GC;
+
+/*
+ * Visual structure; contains information about colormapping possible.
+ */
+typedef struct {
+	XExtData *ext_data;	/* hook for extension to hang data */
+	VisualID visualid;	/* visual id of this visual */
+#if defined(__cplusplus) || defined(c_plusplus)
+	int c_class;		/* C++ class of screen (monochrome, etc.) */
+#else
+	int class;		/* class of screen (monochrome, etc.) */
+#endif
+	unsigned long red_mask, green_mask, blue_mask;	/* mask values */
+	int bits_per_rgb;	/* log base 2 of distinct color values */
+	int map_entries;	/* color map entries */
+} Visual;
+
+/*
+ * Depth structure; contains information for each possible depth.
+ */	
+typedef struct {
+	int depth;		/* this depth (Z) of the depth */
+	int nvisuals;		/* number of Visual types at this depth */
+	Visual *visuals;	/* list of visuals possible at this depth */
+} Depth;
+
+/*
+ * Information about the screen.  The contents of this structure are
+ * implementation dependent.  A Screen should be treated as opaque
+ * by application code.
+ */
+typedef struct {
+	XExtData *ext_data;	/* hook for extension to hang data */
+	struct _XDisplay *display;/* back pointer to display structure */
+	Window root;		/* Root window id. */
+	int width, height;	/* width and height of screen */
+	int mwidth, mheight;	/* width and height of  in millimeters */
+	int ndepths;		/* number of depths possible */
+	Depth *depths;		/* list of allowable depths on the screen */
+	int root_depth;		/* bits per pixel */
+	Visual *root_visual;	/* root visual */
+	GC default_gc;		/* GC for the root root visual */
+	Colormap cmap;		/* default color map */
+	unsigned long white_pixel;
+	unsigned long black_pixel;	/* White and Black pixel values */
+	int max_maps, min_maps;	/* max and min color maps */
+	int backing_store;	/* Never, WhenMapped, Always */
+	Bool save_unders;	
+	long root_input_mask;	/* initial root input mask */
+} Screen;
+
+/*
+ * Format structure; describes ZFormat data the screen will understand.
+ */
+typedef struct {
+	XExtData *ext_data;	/* hook for extension to hang data */
+	int depth;		/* depth of this image format */
+	int bits_per_pixel;	/* bits/pixel at this depth */
+	int scanline_pad;	/* scanline must padded to this multiple */
+} ScreenFormat;
+
+/*
+ * Data structure for setting window attributes.
+ */
+typedef struct {
+    Pixmap background_pixmap;	/* background or None or ParentRelative */
+    unsigned long background_pixel;	/* background pixel */
+    Pixmap border_pixmap;	/* border of the window */
+    unsigned long border_pixel;	/* border pixel value */
+    int bit_gravity;		/* one of bit gravity values */
+    int win_gravity;		/* one of the window gravity values */
+    int backing_store;		/* NotUseful, WhenMapped, Always */
+    unsigned long backing_planes;/* planes to be preseved if possible */
+    unsigned long backing_pixel;/* value to use in restoring planes */
+    Bool save_under;		/* should bits under be saved? (popups) */
+    long event_mask;		/* set of events that should be saved */
+    long do_not_propagate_mask;	/* set of events that should not propagate */
+    Bool override_redirect;	/* boolean value for override-redirect */
+    Colormap colormap;		/* color map to be associated with window */
+    Cursor cursor;		/* cursor to be displayed (or None) */
+} XSetWindowAttributes;
+
+typedef struct {
+    int x, y;			/* location of window */
+    int width, height;		/* width and height of window */
+    int border_width;		/* border width of window */
+    int depth;          	/* depth of window */
+    Visual *visual;		/* the associated visual structure */
+    Window root;        	/* root of screen containing window */
+#if defined(__cplusplus) || defined(c_plusplus)
+    int c_class;		/* C++ InputOutput, InputOnly*/
+#else
+    int class;			/* InputOutput, InputOnly*/
+#endif
+    int bit_gravity;		/* one of bit gravity values */
+    int win_gravity;		/* one of the window gravity values */
+    int backing_store;		/* NotUseful, WhenMapped, Always */
+    unsigned long backing_planes;/* planes to be preserved if possible */
+    unsigned long backing_pixel;/* value to be used when restoring planes */
+    Bool save_under;		/* boolean, should bits under be saved? */
+    Colormap colormap;		/* color map to be associated with window */
+    Bool map_installed;		/* boolean, is color map currently installed*/
+    int map_state;		/* IsUnmapped, IsUnviewable, IsViewable */
+    long all_event_masks;	/* set of events all people have interest in*/
+    long your_event_mask;	/* my event mask */
+    long do_not_propagate_mask; /* set of events that should not propagate */
+    Bool override_redirect;	/* boolean value for override-redirect */
+    Screen *screen;		/* back pointer to correct screen */
+} XWindowAttributes;
+
+/*
+ * Data structure for host setting; getting routines.
+ *
+ */
+
+typedef struct {
+	int family;		/* for example FamilyInternet */
+	int length;		/* length of address, in bytes */
+	char *address;		/* pointer to where to find the bytes */
+} XHostAddress;
+
+/*
+ * Data structure for "image" data, used by image manipulation routines.
+ */
+typedef struct _XImage {
+    int width, height;		/* size of image */
+    int xoffset;		/* number of pixels offset in X direction */
+    int format;			/* XYBitmap, XYPixmap, ZPixmap */
+    char *data;			/* pointer to image data */
+    int byte_order;		/* data byte order, LSBFirst, MSBFirst */
+    int bitmap_unit;		/* quant. of scanline 8, 16, 32 */
+    int bitmap_bit_order;	/* LSBFirst, MSBFirst */
+    int bitmap_pad;		/* 8, 16, 32 either XY or ZPixmap */
+    int depth;			/* depth of image */
+    int bytes_per_line;		/* accelarator to next line */
+    int bits_per_pixel;		/* bits per pixel (ZPixmap) */
+    unsigned long red_mask;	/* bits in z arrangment */
+    unsigned long green_mask;
+    unsigned long blue_mask;
+    XPointer obdata;		/* hook for the object routines to hang on */
+    struct funcs {		/* image manipulation routines */
+	struct _XImage *(*create_image)();
+#if NeedFunctionPrototypes
+	int (*destroy_image)        (struct _XImage *);
+	unsigned long (*get_pixel)  (struct _XImage *, int, int);
+	int (*put_pixel)            (struct _XImage *, int, int, unsigned long);
+	struct _XImage *(*sub_image)(struct _XImage *, int, int, unsigned int, unsigned int);
+	int (*add_pixel)            (struct _XImage *, long);
+#else
+	int (*destroy_image)();
+	unsigned long (*get_pixel)();
+	int (*put_pixel)();
+	struct _XImage *(*sub_image)();
+	int (*add_pixel)();
+#endif
+	} f;
+} XImage;
+
+/* 
+ * Data structure for XReconfigureWindow
+ */
+typedef struct {
+    int x, y;
+    int width, height;
+    int border_width;
+    Window sibling;
+    int stack_mode;
+} XWindowChanges;
+
+/*
+ * Data structure used by color operations
+ */
+typedef struct {
+	unsigned long pixel;
+	unsigned short red, green, blue;
+	char flags;  /* do_red, do_green, do_blue */
+	char pad;
+} XColor;
+
+/* 
+ * Data structures for graphics operations.  On most machines, these are
+ * congruent with the wire protocol structures, so reformatting the data
+ * can be avoided on these architectures.
+ */
+typedef struct {
+    short x1, y1, x2, y2;
+} XSegment;
+
+typedef struct {
+    short x, y;
+} XPoint;
+    
+typedef struct {
+    short x, y;
+    unsigned short width, height;
+} XRectangle;
+    
+typedef struct {
+    short x, y;
+    unsigned short width, height;
+    short angle1, angle2;
+} XArc;
+
+
+/* Data structure for XChangeKeyboardControl */
+
+typedef struct {
+        int key_click_percent;
+        int bell_percent;
+        int bell_pitch;
+        int bell_duration;
+        int led;
+        int led_mode;
+        int key;
+        int auto_repeat_mode;   /* On, Off, Default */
+} XKeyboardControl;
+
+/* Data structure for XGetKeyboardControl */
+
+typedef struct {
+        int key_click_percent;
+	int bell_percent;
+	unsigned int bell_pitch, bell_duration;
+	unsigned long led_mask;
+	int global_auto_repeat;
+	char auto_repeats[32];
+} XKeyboardState;
+
+/* Data structure for XGetMotionEvents.  */
+
+typedef struct {
+        Time time;
+	short x, y;
+} XTimeCoord;
+
+/* Data structure for X{Set,Get}ModifierMapping */
+
+typedef struct {
+ 	int max_keypermod;	/* The server's max # of keys per modifier */
+ 	KeyCode *modifiermap;	/* An 8 by max_keypermod array of modifiers */
+} XModifierKeymap;
+
+
+/*
+ * Display datatype maintaining display specific data.
+ * The contents of this structure are implementation dependent.
+ * A Display should be treated as opaque by application code.
+ */
+typedef struct _XDisplay {
+	XExtData *ext_data;	/* hook for extension to hang data */
+	struct _XFreeFuncs *free_funcs; /* internal free functions */
+	int fd;			/* Network socket. */
+	int conn_checker;         /* ugly thing used by _XEventsQueued */
+	int proto_major_version;/* maj. version of server's X protocol */
+	int proto_minor_version;/* minor version of servers X protocol */
+	char *vendor;		/* vendor of the server hardware */
+        XID resource_base;	/* resource ID base */
+	XID resource_mask;	/* resource ID mask bits */
+	XID resource_id;	/* allocator current ID */
+	int resource_shift;	/* allocator shift to correct bits */
+	XID (*resource_alloc)(); /* allocator function */
+	int byte_order;		/* screen byte order, LSBFirst, MSBFirst */
+	int bitmap_unit;	/* padding and data requirements */
+	int bitmap_pad;		/* padding requirements on bitmaps */
+	int bitmap_bit_order;	/* LeastSignificant or MostSignificant */
+	int nformats;		/* number of pixmap formats in list */
+	ScreenFormat *pixmap_format;	/* pixmap format list */
+	int vnumber;		/* Xlib's X protocol version number. */
+	int release;		/* release of the server */
+	struct _XSQEvent *head, *tail;	/* Input event queue. */
+	int qlen;		/* Length of input event queue */
+	unsigned long request;	/* sequence number of last request. */
+	char *last_req;		/* beginning of last request, or dummy */
+	char *buffer;		/* Output buffer starting address. */
+	char *bufptr;		/* Output buffer index pointer. */
+	char *bufmax;		/* Output buffer maximum+1 address. */
+	unsigned max_request_size; /* maximum number 32 bit words in request*/
+	struct _XrmHashBucketRec *db;
+	int (*synchandler)();	/* Synchronization handler */
+	char *display_name;	/* "host:display" string used on this connect*/
+	int default_screen;	/* default screen for operations */
+	int nscreens;		/* number of screens on this server*/
+	Screen *screens;	/* pointer to list of screens */
+	unsigned long motion_buffer;	/* size of motion buffer */
+	unsigned long flags;	/* internal connection flags */
+	int min_keycode;	/* minimum defined keycode */
+	int max_keycode;	/* maximum defined keycode */
+	KeySym *keysyms;	/* This server's keysyms */
+	XModifierKeymap *modifiermap;	/* This server's modifier keymap */
+	int keysyms_per_keycode;/* number of rows */
+	char *xdefaults;	/* contents of defaults from server */
+	char *scratch_buffer;	/* place to hang scratch buffer */
+	unsigned long scratch_length;	/* length of scratch buffer */
+	int ext_number;		/* extension number on this display */
+	struct _XExten *ext_procs; /* extensions initialized on this display */
+	/*
+	 * the following can be fixed size, as the protocol defines how
+	 * much address space is available. 
+	 * While this could be done using the extension vector, there
+	 * may be MANY events processed, so a search through the extension
+	 * list to find the right procedure for each event might be
+	 * expensive if many extensions are being used.
+	 */
+	Bool (*event_vec[128])();  /* vector for wire to event */
+	Status (*wire_vec[128])(); /* vector for event to wire */
+	KeySym lock_meaning;	   /* for XLookupString */
+	struct _XLockInfo *lock;   /* multi-thread state, display lock */
+	struct _XInternalAsync *async_handlers; /* for internal async */
+	unsigned long bigreq_size; /* max size of big requests */
+	struct _XLockPtrs *lock_fns; /* pointers to threads functions */
+	/* things above this line should not move, for binary compatibility */
+	struct _XKeytrans *key_bindings; /* for XLookupString */
+	Font cursor_font;	   /* for XCreateFontCursor */
+	struct _XDisplayAtoms *atoms; /* for XInternAtom */
+	unsigned int mode_switch;  /* keyboard group modifiers */
+	struct _XContextDB *context_db; /* context database */
+	Bool (**error_vec)();      /* vector for wire to error */
+	/*
+	 * Xcms information
+	 */
+	struct {
+	   XPointer defaultCCCs;  /* pointer to an array of default XcmsCCC */
+	   XPointer clientCmaps;  /* pointer to linked list of XcmsCmapRec */
+	   XPointer perVisualIntensityMaps;
+				  /* linked list of XcmsIntensityMap */
+	} cms;
+	struct _XIMFilter *im_filters;
+	struct _XSQEvent *qfree; /* unallocated event queue elements */
+	unsigned long next_event_serial_num; /* inserted into next queue elt */
+	int (*savedsynchandler)(); /* user synchandler when Xlib usurps */
+} Display;
+
+#if NeedFunctionPrototypes	/* prototypes require event type definitions */
+#undef _XEVENT_
+#endif
+#ifndef _XEVENT_
+
+#define XMaxTransChars 4
+
+/*
+ * Definitions of specific events.
+ */
+typedef struct {
+	int type;		/* of event */
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;	        /* "event" window it is reported relative to */
+	Window root;	        /* root window that the event occured on */
+	Window subwindow;	/* child window */
+	Time time;		/* milliseconds */
+	int x, y;		/* pointer x, y coordinates in event window */
+	int x_root, y_root;	/* coordinates relative to root */
+	unsigned int state;	/* key or button mask */
+	unsigned int keycode;	/* detail */
+	Bool same_screen;	/* same screen flag */
+        char trans_chars[XMaxTransChars];
+				/* translated characters */
+	int nbytes;
+} XKeyEvent;
+typedef XKeyEvent XKeyPressedEvent;
+typedef XKeyEvent XKeyReleasedEvent;
+
+typedef struct {
+	int type;		/* of event */
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;	        /* "event" window it is reported relative to */
+	Window root;	        /* root window that the event occured on */
+	Window subwindow;	/* child window */
+	Time time;		/* milliseconds */
+	int x, y;		/* pointer x, y coordinates in event window */
+	int x_root, y_root;	/* coordinates relative to root */
+	unsigned int state;	/* key or button mask */
+	unsigned int button;	/* detail */
+	Bool same_screen;	/* same screen flag */
+} XButtonEvent;
+typedef XButtonEvent XButtonPressedEvent;
+typedef XButtonEvent XButtonReleasedEvent;
+
+typedef struct {
+	int type;		/* of event */
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;	        /* "event" window reported relative to */
+	Window root;	        /* root window that the event occured on */
+	Window subwindow;	/* child window */
+	Time time;		/* milliseconds */
+	int x, y;		/* pointer x, y coordinates in event window */
+	int x_root, y_root;	/* coordinates relative to root */
+	unsigned int state;	/* key or button mask */
+	char is_hint;		/* detail */
+	Bool same_screen;	/* same screen flag */
+} XMotionEvent;
+typedef XMotionEvent XPointerMovedEvent;
+
+typedef struct {
+	int type;		/* of event */
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;	        /* "event" window reported relative to */
+	Window root;	        /* root window that the event occured on */
+	Window subwindow;	/* child window */
+	Time time;		/* milliseconds */
+	int x, y;		/* pointer x, y coordinates in event window */
+	int x_root, y_root;	/* coordinates relative to root */
+	int mode;		/* NotifyNormal, NotifyGrab, NotifyUngrab */
+	int detail;
+	/*
+	 * NotifyAncestor, NotifyVirtual, NotifyInferior, 
+	 * NotifyNonlinear,NotifyNonlinearVirtual
+	 */
+	Bool same_screen;	/* same screen flag */
+	Bool focus;		/* boolean focus */
+	unsigned int state;	/* key or button mask */
+} XCrossingEvent;
+typedef XCrossingEvent XEnterWindowEvent;
+typedef XCrossingEvent XLeaveWindowEvent;
+
+typedef struct {
+	int type;		/* FocusIn or FocusOut */
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;		/* window of event */
+	int mode;		/* NotifyNormal, NotifyGrab, NotifyUngrab */
+	int detail;
+	/*
+	 * NotifyAncestor, NotifyVirtual, NotifyInferior, 
+	 * NotifyNonlinear,NotifyNonlinearVirtual, NotifyPointer,
+	 * NotifyPointerRoot, NotifyDetailNone 
+	 */
+} XFocusChangeEvent;
+typedef XFocusChangeEvent XFocusInEvent;
+typedef XFocusChangeEvent XFocusOutEvent;
+
+/* generated on EnterWindow and FocusIn  when KeyMapState selected */
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	char key_vector[32];
+} XKeymapEvent;	
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	int x, y;
+	int width, height;
+	int count;		/* if non-zero, at least this many more */
+} XExposeEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Drawable drawable;
+	int x, y;
+	int width, height;
+	int count;		/* if non-zero, at least this many more */
+	int major_code;		/* core is CopyArea or CopyPlane */
+	int minor_code;		/* not defined in the core */
+} XGraphicsExposeEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Drawable drawable;
+	int major_code;		/* core is CopyArea or CopyPlane */
+	int minor_code;		/* not defined in the core */
+} XNoExposeEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	int state;		/* Visibility state */
+} XVisibilityEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window parent;		/* parent of the window */
+	Window window;		/* window id of window created */
+	int x, y;		/* window location */
+	int width, height;	/* size of window */
+	int border_width;	/* border width */
+	Bool override_redirect;	/* creation should be overridden */
+} XCreateWindowEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+} XDestroyWindowEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	Bool from_configure;
+} XUnmapEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	Bool override_redirect;	/* boolean, is override set... */
+} XMapEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window parent;
+	Window window;
+} XMapRequestEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	Window parent;
+	int x, y;
+	Bool override_redirect;
+} XReparentEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	int x, y;
+	int width, height;
+	int border_width;
+	Window above;
+	Bool override_redirect;
+} XConfigureEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	int x, y;
+} XGravityEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	int width, height;
+} XResizeRequestEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window parent;
+	Window window;
+	int x, y;
+	int width, height;
+	int border_width;
+	Window above;
+	int detail;		/* Above, Below, TopIf, BottomIf, Opposite */
+	unsigned long value_mask;
+} XConfigureRequestEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window event;
+	Window window;
+	int place;		/* PlaceOnTop, PlaceOnBottom */
+} XCirculateEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window parent;
+	Window window;
+	int place;		/* PlaceOnTop, PlaceOnBottom */
+} XCirculateRequestEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	Atom atom;
+	Time time;
+	int state;		/* NewValue, Deleted */
+} XPropertyEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	Atom selection;
+	Time time;
+} XSelectionClearEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window owner;
+	Window requestor;
+	Atom selection;
+	Atom target;
+	Atom property;
+	Time time;
+} XSelectionRequestEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window requestor;
+	Atom selection;
+	Atom target;
+	Atom property;		/* ATOM or None */
+	Time time;
+} XSelectionEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	Colormap colormap;	/* COLORMAP or None */
+#if defined(__cplusplus) || defined(c_plusplus)
+	Bool c_new;		/* C++ */
+#else
+	Bool new;
+#endif
+	int state;		/* ColormapInstalled, ColormapUninstalled */
+} XColormapEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;
+	Atom message_type;
+	int format;
+	union {
+		char b[20];
+		short s[10];
+		long l[5];
+		} data;
+} XClientMessageEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;	/* Display the event was read from */
+	Window window;		/* unused */
+	int request;		/* one of MappingModifier, MappingKeyboard,
+				   MappingPointer */
+	int first_keycode;	/* first keycode */
+	int count;		/* defines range of change w. first_keycode*/
+} XMappingEvent;
+
+typedef struct {
+	int type;
+	Display *display;	/* Display the event was read from */
+	XID resourceid;		/* resource id */
+	unsigned long serial;	/* serial number of failed request */
+	unsigned char error_code;	/* error code of failed request */
+	unsigned char request_code;	/* Major op-code of failed request */
+	unsigned char minor_code;	/* Minor op-code of failed request */
+} XErrorEvent;
+
+typedef struct {
+	int type;
+	unsigned long serial;	/* # of last request processed by server */
+	Bool send_event;	/* true if this came from a SendEvent request */
+	Display *display;/* Display the event was read from */
+	Window window;	/* window on which event was requested in event mask */
+} XAnyEvent;
+
+/*
+ * this union is defined so Xlib can always use the same sized
+ * event structure internally, to avoid memory fragmentation.
+ */
+typedef union _XEvent {
+        int type;		/* must not be changed; first element */
+	XAnyEvent xany;
+	XKeyEvent xkey;
+	XButtonEvent xbutton;
+	XMotionEvent xmotion;
+	XCrossingEvent xcrossing;
+	XFocusChangeEvent xfocus;
+	XExposeEvent xexpose;
+	XGraphicsExposeEvent xgraphicsexpose;
+	XNoExposeEvent xnoexpose;
+	XVisibilityEvent xvisibility;
+	XCreateWindowEvent xcreatewindow;
+	XDestroyWindowEvent xdestroywindow;
+	XUnmapEvent xunmap;
+	XMapEvent xmap;
+	XMapRequestEvent xmaprequest;
+	XReparentEvent xreparent;
+	XConfigureEvent xconfigure;
+	XGravityEvent xgravity;
+	XResizeRequestEvent xresizerequest;
+	XConfigureRequestEvent xconfigurerequest;
+	XCirculateEvent xcirculate;
+	XCirculateRequestEvent xcirculaterequest;
+	XPropertyEvent xproperty;
+	XSelectionClearEvent xselectionclear;
+	XSelectionRequestEvent xselectionrequest;
+	XSelectionEvent xselection;
+	XColormapEvent xcolormap;
+	XClientMessageEvent xclient;
+	XMappingEvent xmapping;
+	XErrorEvent xerror;
+	XKeymapEvent xkeymap;
+	long pad[24];
+} XEvent;
+#endif
+
+#define XAllocID(dpy) ((*(dpy)->resource_alloc)((dpy)))
+
+/*
+ * per character font metric information.
+ */
+typedef struct {
+    short	lbearing;	/* origin to left edge of raster */
+    short	rbearing;	/* origin to right edge of raster */
+    short	width;		/* advance to next char's origin */
+    short	ascent;		/* baseline to top edge of raster */
+    short	descent;	/* baseline to bottom edge of raster */
+    unsigned short attributes;	/* per char flags (not predefined) */
+} XCharStruct;
+
+/*
+ * To allow arbitrary information with fonts, there are additional properties
+ * returned.
+ */
+typedef struct {
+    Atom name;
+    unsigned long card32;
+} XFontProp;
+
+typedef struct {
+    XExtData	*ext_data;	/* hook for extension to hang data */
+    Font        fid;            /* Font id for this font */
+    unsigned	direction;	/* hint about direction the font is painted */
+    unsigned	min_char_or_byte2;/* first character */
+    unsigned	max_char_or_byte2;/* last character */
+    unsigned	min_byte1;	/* first row that exists */
+    unsigned	max_byte1;	/* last row that exists */
+    Bool	all_chars_exist;/* flag if all characters have non-zero size*/
+    unsigned	default_char;	/* char to print for undefined character */
+    int         n_properties;   /* how many properties there are */
+    XFontProp	*properties;	/* pointer to array of additional properties*/
+    XCharStruct	min_bounds;	/* minimum bounds over all existing char*/
+    XCharStruct	max_bounds;	/* maximum bounds over all existing char*/
+    XCharStruct	*per_char;	/* first_char to last_char information */
+    int		ascent;		/* log. extent above baseline for spacing */
+    int		descent;	/* log. descent below baseline for spacing */
+} XFontStruct;
+
+/*
+ * PolyText routines take these as arguments.
+ */
+typedef struct {
+    char *chars;		/* pointer to string */
+    int nchars;			/* number of characters */
+    int delta;			/* delta between strings */
+    Font font;			/* font to print it in, None don't change */
+} XTextItem;
+
+typedef struct {		/* normal 16 bit characters are two bytes */
+    unsigned char byte1;
+    unsigned char byte2;
+} XChar2b;
+
+typedef struct {
+    XChar2b *chars;		/* two byte characters */
+    int nchars;			/* number of characters */
+    int delta;			/* delta between strings */
+    Font font;			/* font to print it in, None don't change */
+} XTextItem16;
+
+
+typedef union { Display *display;
+		GC gc;
+		Visual *visual;
+		Screen *screen;
+		ScreenFormat *pixmap_format;
+		XFontStruct *font; } XEDataObject;
+
+typedef struct {
+    XRectangle      max_ink_extent;
+    XRectangle      max_logical_extent;
+} XFontSetExtents;
+
+typedef struct _XFontSet *XFontSet;
+
+typedef struct {
+    char           *chars;
+    int             nchars;
+    int             delta;
+    XFontSet        font_set;
+} XmbTextItem;
+
+typedef struct {
+    wchar_t        *chars;
+    int             nchars;
+    int             delta;
+    XFontSet        font_set;
+} XwcTextItem;
+
+typedef void (*XIMProc)();
+
+typedef struct _XIM *XIM;
+typedef struct _XIC *XIC;
+
+typedef unsigned long XIMStyle;
+
+typedef struct {
+    unsigned short count_styles;
+    XIMStyle *supported_styles;
+} XIMStyles;
+
+#define XIMPreeditArea		0x0001L
+#define XIMPreeditCallbacks	0x0002L
+#define XIMPreeditPosition	0x0004L
+#define XIMPreeditNothing	0x0008L
+#define XIMPreeditNone		0x0010L
+#define XIMStatusArea		0x0100L
+#define XIMStatusCallbacks	0x0200L
+#define XIMStatusNothing	0x0400L
+#define XIMStatusNone		0x0800L
+
+#define XNVaNestedList "XNVaNestedList"
+#define XNClientWindow "clientWindow"
+#define XNInputStyle "inputStyle"
+#define XNFocusWindow "focusWindow"
+#define XNResourceName "resourceName"
+#define XNResourceClass "resourceClass"
+#define XNGeometryCallback "geometryCallback"
+#define XNFilterEvents "filterEvents"
+#define XNPreeditStartCallback "preeditStartCallback"
+#define XNPreeditDoneCallback "preeditDoneCallback"
+#define XNPreeditDrawCallback "preeditDrawCallback"
+#define XNPreeditCaretCallback "preeditCaretCallback"
+#define XNPreeditAttributes "preeditAttributes"
+#define XNStatusStartCallback "statusStartCallback"
+#define XNStatusDoneCallback "statusDoneCallback"
+#define XNStatusDrawCallback "statusDrawCallback"
+#define XNStatusAttributes "statusAttributes"
+#define XNArea "area"
+#define XNAreaNeeded "areaNeeded"
+#define XNSpotLocation "spotLocation"
+#define XNColormap "colorMap"
+#define XNStdColormap "stdColorMap"
+#define XNForeground "foreground"
+#define XNBackground "background"
+#define XNBackgroundPixmap "backgroundPixmap"
+#define XNFontSet "fontSet"
+#define XNLineSpace "lineSpace"
+#define XNCursor "cursor"
+
+#define XBufferOverflow		-1
+#define XLookupNone		1
+#define XLookupChars		2
+#define XLookupKeySym		3
+#define XLookupBoth		4
+
+#if NeedFunctionPrototypes
+typedef void *XVaNestedList;
+#else
+typedef XPointer XVaNestedList;
+#endif
+
+typedef struct {
+    XPointer client_data;
+    XIMProc callback;
+} XIMCallback;
+
+typedef unsigned long XIMFeedback;
+
+#define XIMReverse	1
+#define XIMUnderline	(1<<1) 
+#define XIMHighlight	(1<<2)
+#define XIMPrimary 	(1<<5)
+#define XIMSecondary	(1<<6)
+#define XIMTertiary 	(1<<7)
+
+typedef struct _XIMText {
+    unsigned short length;
+    XIMFeedback *feedback;
+    Bool encoding_is_wchar; 
+    union {
+	char *multi_byte;
+	wchar_t *wide_char;
+    } string; 
+} XIMText;
+
+typedef struct _XIMPreeditDrawCallbackStruct {
+    int caret;		/* Cursor offset within pre-edit string */
+    int chg_first;	/* Starting change position */
+    int chg_length;	/* Length of the change in character count */
+    XIMText *text;
+} XIMPreeditDrawCallbackStruct;
+
+typedef enum {
+    XIMForwardChar, XIMBackwardChar,
+    XIMForwardWord, XIMBackwardWord,
+    XIMCaretUp, XIMCaretDown,
+    XIMNextLine, XIMPreviousLine,
+    XIMLineStart, XIMLineEnd, 
+    XIMAbsolutePosition,
+    XIMDontChange
+} XIMCaretDirection;
+
+typedef enum {
+    XIMIsInvisible,	/* Disable caret feedback */ 
+    XIMIsPrimary,	/* UI defined caret feedback */
+    XIMIsSecondary	/* UI defined caret feedback */
+} XIMCaretStyle;
+
+typedef struct _XIMPreeditCaretCallbackStruct {
+    int position;		 /* Caret offset within pre-edit string */
+    XIMCaretDirection direction; /* Caret moves direction */
+    XIMCaretStyle style;	 /* Feedback of the caret */
+} XIMPreeditCaretCallbackStruct;
+
+typedef enum {
+    XIMTextType,
+    XIMBitmapType
+} XIMStatusDataType;
+	
+typedef struct _XIMStatusDrawCallbackStruct {
+    XIMStatusDataType type;
+    union {
+	XIMText *text;
+	Pixmap  bitmap;
+    } data;
+} XIMStatusDrawCallbackStruct;
+
+typedef int (*XErrorHandler) (	    /* WARNING, this type not in Xlib spec */
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XErrorEvent*	/* error_event */
+#endif
+);
+
+_XFUNCPROTOBEGIN
+
+#include "X11/Xutil.h"
+
+extern void XSetDashes(Display * display, GC gc, int dash_offset, 
+	_Xconst char* dash_list, int n);
+
+extern XModifierKeymap *XGetModifierMapping(Display *display);
+
+extern XImage *XCreateImage(Display *display, Visual *visual, unsigned int ui1,
+	int i1, int i2, char* cp, unsigned int ui2, unsigned int ui3, 
+	int i3, int i4);
+
+extern XImage *XGetImage(Display* display,Drawable dr, int i1, int i2, 
+	unsigned int ui1, unsigned int ui2, unsigned long ul, int i3);
+
+extern char *XGetAtomName(Display *d, Atom a);
+
+extern char *XKeysymToString(KeySym k);
+
+extern Colormap XCreateColormap(Display *d, Window w, Visual* v, 
+	int i);
+
+extern Cursor XCreatePixmapCursor(Display *d, Pixmap p1, Pixmap p2,	
+	XColor* x1, XColor* x2, unsigned int ui1, unsigned int ui2);
+
+extern Cursor XCreateGlyphCursor(Display *d, Font f1, Font f2, 
+	unsigned int ui1, unsigned int ui2, XColor* x1, XColor* x2);
+
+extern GContext XGContextFromGC(GC g);
+
+extern XHostAddress *XListHosts(Display *d, int* i, Bool* b);
+
+extern KeySym XKeycodeToKeysym(Display *d, unsigned int k, int i);
+
+extern KeySym XStringToKeysym(_Xconst char* c);
+
+extern Window XRootWindow(Display *d, int i);
+
+extern XErrorHandler XSetErrorHandler(XErrorHandler x);
+
+extern Status XIconifyWindow(Display *d, Window w, int i);
+
+extern Status XWithdrawWindow(Display *d, Window w, int i);
+
+extern Status XGetWMColormapWindows(Display *d, Window w, Window** wpp, 
+	int* ip);
+
+extern Status XAllocColor(Display *d, Colormap c, XColor* xp);
+
+extern void XBell(Display *d, int i);
+
+extern void XChangeProperty(Display *d, Window w, Atom a1, Atom a2, int i1, 
+	int i2, _Xconst unsigned char* c, int i3);
+
+extern void XChangeWindowAttributes(Display *d, Window w, unsigned long ul, 
+	XSetWindowAttributes* x);
+
+extern void XClearWindow(Display *d, Window w);
+
+extern void XConfigureWindow(Display *d, Window w, unsigned int i, 
+	XWindowChanges* x);
+
+extern void XCopyArea(Display *d, Drawable dr1, Drawable dr2, GC g, int i1, 
+	int i2, unsigned int ui1, unsigned int ui2, int i3, int i4);
+
+extern void XCopyPlane(Display *d, Drawable dr1, Drawable dr2, GC g, int i1, 
+	int i2, unsigned int ui1, unsigned int ui2, int i3, int i4, 
+	unsigned long ul);
+
+extern Pixmap XCreateBitmapFromData(Display *display, Drawable d, 
+	_Xconst char* data, unsigned int width, unsigned int height);
+
+extern void XDefineCursor(Display *d, Window w, Cursor c);
+
+extern void XDeleteProperty(Display *d, Window w, Atom a);
+
+extern void XDestroyWindow(Display *d, Window w);
+
+extern void XDrawArc(Display *d, Drawable dr, GC g, int i1, int i2, 
+	unsigned int ui1, unsigned int ui2, int i3, int i4);
+
+extern void XDrawLines(Display *d, Drawable dr, GC g, XPoint* x, int i1, 
+	int i2);
+
+extern void XDrawRectangle(Display *d, Drawable dr, GC g, int i1, int i2, 
+	unsigned int ui1, unsigned int ui2);
+
+extern void XFillArc(Display *d, Drawable dr, GC g, int i1, int i2, 
+	unsigned int ui1, unsigned int ui2, int i3, int i4);
+
+extern void XFillPolygon(Display *d, Drawable dr, GC g, XPoint* x, int i1,
+	int i2, int i3);
+
+extern void XFillRectangles(Display *d, Drawable dr, GC g, XRectangle* x, 
+	int i);
+
+extern void XForceScreenSaver(Display *d, int i);
+
+extern void XFreeColormap(Display *d, Colormap c);
+
+extern void XFreeColors(Display *d, Colormap c, unsigned long* ulp, int i, 
+	unsigned long ul);
+
+extern void XFreeCursor(Display *d, Cursor c);
+
+extern void XFreeModifiermap(XModifierKeymap* x);
+
+extern Status XGetGeometry(Display *d, Drawable dr, Window* w, int* i1, 
+	int* i2, unsigned int* ui1, unsigned int* ui2, unsigned int* ui3, 
+	unsigned int* ui4);
+
+extern void XGetInputFocus(Display *d, Window* w, int* i);
+
+extern int XGetWindowProperty(Display *d, Window w, Atom a1, long l1, long l2,
+	Bool b, Atom a2, Atom* ap, int* ip, unsigned long* ulp1, 
+	unsigned long* ulp2, unsigned char** cpp);
+
+extern Status XGetWindowAttributes(Display *d, Window w, XWindowAttributes* x);
+
+extern int XGrabKeyboard(Display *d, Window w, Bool b, int i1, int i2, Time t);
+
+extern int XGrabPointer(Display *d, Window w1, Bool b, unsigned int ui, int i1,
+	int i2, Window w2, Cursor c, Time t);
+
+extern KeyCode XKeysymToKeycode(Display *d, KeySym k);
+
+extern Status XLookupColor(Display *d, Colormap c1, _Xconst char* c2, 
+	XColor* x1, XColor* x2);
+
+extern void XMapWindow(Display *d, Window w);
+
+extern void XMoveResizeWindow(Display *d, Window w, int i1, int i2, 
+	unsigned int ui1, unsigned int ui2);
+
+extern void XMoveWindow(Display *d, Window w, int i1, int i2);
+
+extern void XNextEvent(Display *d, XEvent* x);
+
+extern void XPutBackEvent(Display *d, XEvent* x);
+
+extern void XQueryColors(Display *d, Colormap c, XColor* x, int i);
+
+extern Bool XQueryPointer(Display *d, Window w1, Window* w2, Window* w3, 
+	int* i1, int* i2, int* i3, int* i4, unsigned int* ui);
+
+extern Status XQueryTree(Display *d, Window w1, Window* w2, Window* w3, 
+	Window** w4, unsigned int* ui);
+
+extern void XRaiseWindow(Display *d, Window w);
+
+extern void XRefreshKeyboardMapping(XMappingEvent* x);
+
+extern void XResizeWindow(Display *d, Window w, unsigned int ui1, 
+	unsigned int ui2);
+
+extern void XSelectInput(Display *d, Window w, long l);
+
+extern Status XSendEvent(Display *d, Window w, Bool b, long l, XEvent* x);
+
+extern void XSetCommand(Display *d, Window w, char** c, int i);
+
+extern void XSetIconName(Display *d, Window w, _Xconst char* c);
+
+extern void XSetInputFocus(Display *d, Window w, int i, Time t);
+
+extern void XSetSelectionOwner(Display *d, Atom a, Window w, Time t);
+
+extern void XSetWindowBackground(Display *d, Window w, unsigned long ul);
+
+extern void XSetWindowBackgroundPixmap(Display *d, Window w, Pixmap p);
+
+extern void XSetWindowBorder(Display *d, Window w, unsigned long ul);
+
+extern void XSetWindowBorderPixmap(Display *d, Window w, Pixmap p);
+
+extern void XSetWindowBorderWidth(Display *d, Window w, unsigned int ui);
+
+extern void XSetWindowColormap(Display *d, Window w, Colormap c);
+
+extern Bool XTranslateCoordinates(Display *d, Window w1, Window w2, int i1, 
+	int i2, int* i3, int* i4, Window* w3);
+
+extern void XUngrabKeyboard(Display *d, Time t);
+
+extern void XUngrabPointer(Display *d, Time t);
+
+extern void XUnmapWindow(Display *d, Window w);
+
+extern void XWindowEvent(Display *d, Window w, long l, XEvent* x);
+
+extern void XDestroyIC(XIC x);
+
+extern Bool XFilterEvent(XEvent* x, Window w);
+
+extern int XmbLookupString(XIC xi, XKeyPressedEvent* xk, char* c, int i, 
+	KeySym* k, Status* s);
+
+extern void TkPutImage(unsigned long * colors, int ncolors, Display *display, 
+	Drawable d, GC gc, XImage* image, int src_x, int src_y, 
+	int dest_x, int dest_y, unsigned int width, unsigned int height);
+
+extern Status XParseColor(Display * display, Colormap map, _Xconst char* spec, 
+	XColor * colorPtr);
+
+extern GC XCreateGC(Display *display, Drawable d, unsigned long valuemask, 
+	XGCValues* values);
+
+extern void XFreeGC(Display *display, GC gc);
+
+extern Atom XInternAtom(Display *display, _Xconst char* atom_name, 
+	Bool only_if_exists);
+
+extern void XSetBackground(Display *display, GC gc, unsigned long foreground);
+
+extern void XSetForeground(Display *display, GC gc, unsigned long foreground);
+
+extern void XSetClipMask(Display *display, GC gc, Pixmap pixmap);
+
+extern void XSetClipOrigin(Display *display, GC gc, int clip_x_origin, 
+	int clip_y_origin);
+
+extern void XSetTSOrigin(Display *display, GC gc, int ts_x_origin, 
+	int ts_y_origin);
+
+extern void XChangeGC(Display *display, GC gc, unsigned long mask, 
+	XGCValues * values);
+
+extern void XSetFont(Display *display, GC gc, Font font);
+
+extern void XSetArcMode(Display *display, GC gc, int arc_mode);
+
+extern void XSetStipple(Display * display, GC gc, 
+				Pixmap stipple);
+
+extern void XSetFillRule(Display *display, GC gc, int fill_rule);
+
+extern void XSetFillStyle(Display *display, GC gc, int fill_style);
+
+extern void XSetFunction(Display *display, GC gc, int function);
+
+extern void XSetLineAttributes(Display *display, GC gc, 
+	unsigned int line_width, int line_style, int cap_style, 
+	int join_style);
+
+extern int _XInitImageFuncPtrs(XImage * image);
+
+extern XIC XCreateIC(void);
+
+extern XVisualInfo *XGetVisualInfo(Display *display, long vinfo_mask, 
+	XVisualInfo* vinfo_template, int* nitems_return);
+
+extern void XSetWMClientMachine(Display *display, Window w, 
+	XTextProperty* text_prop);
+
+extern Status XStringListToTextProperty(char** list, int count, 
+	XTextProperty* text_prop_return);
+
+extern void XDrawLine(Display *d, Drawable dr, GC g, int x1, int y1, int x2, 
+	int y2);
+
+extern void XWarpPointer(Display *d, Window s, Window dw, int sx, int sy, 
+	unsigned int sw, unsigned int sh, int dx, int dy);
+
+extern void XFillRectangle(Display *display, Drawable d, GC gc, int x, int y, 
+	unsigned int width, unsigned int height);
+
+_XFUNCPROTOEND
+
+#ifdef MAC_TCL
+#   undef Cursor
+#   undef Region
+#endif
+
+#endif /* _XLIB_H_ */
Index: trunk/kitgen/8.x/blt/win/X11/Xutil.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/Xutil.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/Xutil.h	(revision 175)
@@ -0,0 +1,855 @@
+/* $XConsortium: Xutil.h,v 11.73 91/07/30 16:21:37 rws Exp $ */
+
+/***********************************************************
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
+and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
+
+                        All Rights Reserved
+
+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 names of Digital or MIT not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#ifndef _XUTIL_H_
+#define _XUTIL_H_
+
+/* You must include <X11/Xlib.h> before including this file */
+
+#ifdef MAC_TCL
+#   define Region XRegion
+#endif
+
+/* 
+ * Bitmask returned by XParseGeometry().  Each bit tells if the corresponding
+ * value (x, y, width, height) was found in the parsed string.
+ */
+#define NoValue		0x0000
+#define XValue  	0x0001
+#define YValue		0x0002
+#define WidthValue  	0x0004
+#define HeightValue  	0x0008
+#define AllValues 	0x000F
+#define XNegative 	0x0010
+#define YNegative 	0x0020
+
+/*
+ * new version containing base_width, base_height, and win_gravity fields;
+ * used with WM_NORMAL_HINTS.
+ */
+typedef struct {
+    	long flags;	/* marks which fields in this structure are defined */
+	int x, y;		/* obsolete for new window mgrs, but clients */
+	int width, height;	/* should set so old wm's don't mess up */
+	int min_width, min_height;
+	int max_width, max_height;
+    	int width_inc, height_inc;
+	struct {
+		int x;	/* numerator */
+		int y;	/* denominator */
+	} min_aspect, max_aspect;
+	int base_width, base_height;		/* added by ICCCM version 1 */
+	int win_gravity;			/* added by ICCCM version 1 */
+} XSizeHints;
+
+/*
+ * The next block of definitions are for window manager properties that
+ * clients and applications use for communication.
+ */
+
+/* flags argument in size hints */
+#define USPosition	(1L << 0) /* user specified x, y */
+#define USSize		(1L << 1) /* user specified width, height */
+
+#define PPosition	(1L << 2) /* program specified position */
+#define PSize		(1L << 3) /* program specified size */
+#define PMinSize	(1L << 4) /* program specified minimum size */
+#define PMaxSize	(1L << 5) /* program specified maximum size */
+#define PResizeInc	(1L << 6) /* program specified resize increments */
+#define PAspect		(1L << 7) /* program specified min and max aspect ratios */
+#define PBaseSize	(1L << 8) /* program specified base for incrementing */
+#define PWinGravity	(1L << 9) /* program specified window gravity */
+
+/* obsolete */
+#define PAllHints (PPosition|PSize|PMinSize|PMaxSize|PResizeInc|PAspect)
+
+
+
+typedef struct {
+	long flags;	/* marks which fields in this structure are defined */
+	Bool input;	/* does this application rely on the window manager to
+			get keyboard input? */
+	int initial_state;	/* see below */
+	Pixmap icon_pixmap;	/* pixmap to be used as icon */
+	Window icon_window; 	/* window to be used as icon */
+	int icon_x, icon_y; 	/* initial position of icon */
+	Pixmap icon_mask;	/* icon mask bitmap */
+	XID window_group;	/* id of related window group */
+	/* this structure may be extended in the future */
+} XWMHints;
+
+/* definition for flags of XWMHints */
+
+#define InputHint 		(1L << 0)
+#define StateHint 		(1L << 1)
+#define IconPixmapHint		(1L << 2)
+#define IconWindowHint		(1L << 3)
+#define IconPositionHint 	(1L << 4)
+#define IconMaskHint		(1L << 5)
+#define WindowGroupHint		(1L << 6)
+#define AllHints (InputHint|StateHint|IconPixmapHint|IconWindowHint| \
+IconPositionHint|IconMaskHint|WindowGroupHint)
+
+/* definitions for initial window state */
+#define WithdrawnState 0	/* for windows that are not mapped */
+#define NormalState 1	/* most applications want to start this way */
+#define IconicState 3	/* application wants to start as an icon */
+
+/*
+ * Obsolete states no longer defined by ICCCM
+ */
+#define DontCareState 0	/* don't know or care */
+#define ZoomState 2	/* application wants to start zoomed */
+#define InactiveState 4	/* application believes it is seldom used; */
+			/* some wm's may put it on inactive menu */
+
+
+/*
+ * new structure for manipulating TEXT properties; used with WM_NAME, 
+ * WM_ICON_NAME, WM_CLIENT_MACHINE, and WM_COMMAND.
+ */
+typedef struct {
+    unsigned char *value;		/* same as Property routines */
+    Atom encoding;			/* prop type */
+    int format;				/* prop data format: 8, 16, or 32 */
+    unsigned long nitems;		/* number of data items in value */
+} XTextProperty;
+
+#define XNoMemory -1
+#define XLocaleNotSupported -2
+#define XConverterNotFound -3
+
+typedef enum {
+    XStringStyle,		/* STRING */
+    XCompoundTextStyle,		/* COMPOUND_TEXT */
+    XTextStyle,			/* text in owner's encoding (current locale)*/
+    XStdICCTextStyle		/* STRING, else COMPOUND_TEXT */
+} XICCEncodingStyle;
+
+typedef struct {
+	int min_width, min_height;
+	int max_width, max_height;
+	int width_inc, height_inc;
+} XIconSize;
+
+typedef struct {
+	char *res_name;
+	char *res_class;
+} XClassHint;
+
+/*
+ * These macros are used to give some sugar to the image routines so that
+ * naive people are more comfortable with them.
+ */
+#define XDestroyImage(ximage) \
+	((*((ximage)->f.destroy_image))((ximage)))
+#define XGetPixel(ximage, x, y) \
+	((*((ximage)->f.get_pixel))((ximage), (x), (y)))
+#define XPutPixel(ximage, x, y, pixel) \
+	((*((ximage)->f.put_pixel))((ximage), (x), (y), (pixel)))
+#define XSubImage(ximage, x, y, width, height)  \
+	((*((ximage)->f.sub_image))((ximage), (x), (y), (width), (height)))
+#define XAddPixel(ximage, value) \
+	((*((ximage)->f.add_pixel))((ximage), (value)))
+
+/*
+ * Compose sequence status structure, used in calling XLookupString.
+ */
+typedef struct _XComposeStatus {
+    XPointer compose_ptr;	/* state table pointer */
+    int chars_matched;		/* match state */
+} XComposeStatus;
+
+/*
+ * Keysym macros, used on Keysyms to test for classes of symbols
+ */
+#define IsKeypadKey(keysym) \
+  (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
+
+#define IsCursorKey(keysym) \
+  (((unsigned)(keysym) >= XK_Home)     && ((unsigned)(keysym) <  XK_Select))
+
+#define IsPFKey(keysym) \
+  (((unsigned)(keysym) >= XK_KP_F1)     && ((unsigned)(keysym) <= XK_KP_F4))
+
+#define IsFunctionKey(keysym) \
+  (((unsigned)(keysym) >= XK_F1)       && ((unsigned)(keysym) <= XK_F35))
+
+#define IsMiscFunctionKey(keysym) \
+  (((unsigned)(keysym) >= XK_Select)   && ((unsigned)(keysym) <= XK_Break))
+
+#define IsModifierKey(keysym) \
+  ((((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R)) \
+   || ((unsigned)(keysym) == XK_Mode_switch) \
+   || ((unsigned)(keysym) == XK_Num_Lock))
+/*
+ * opaque reference to Region data type 
+ */
+typedef struct _XRegion *Region; 
+
+/* Return values from XRectInRegion() */
+ 
+#define RectangleOut 0
+#define RectangleIn  1
+#define RectanglePart 2
+ 
+
+/*
+ * Information used by the visual utility routines to find desired visual
+ * type from the many visuals a display may support.
+ */
+
+typedef struct {
+  Visual *visual;
+  VisualID visualid;
+  int screen;
+  int depth;
+#if defined(__cplusplus) || defined(c_plusplus)
+  int c_class;					/* C++ */
+#else
+  int class;
+#endif
+  unsigned long red_mask;
+  unsigned long green_mask;
+  unsigned long blue_mask;
+  int colormap_size;
+  int bits_per_rgb;
+} XVisualInfo;
+
+#define VisualNoMask		0x0
+#define VisualIDMask 		0x1
+#define VisualScreenMask	0x2
+#define VisualDepthMask		0x4
+#define VisualClassMask		0x8
+#define VisualRedMaskMask	0x10
+#define VisualGreenMaskMask	0x20
+#define VisualBlueMaskMask	0x40
+#define VisualColormapSizeMask	0x80
+#define VisualBitsPerRGBMask	0x100
+#define VisualAllMask		0x1FF
+
+/*
+ * This defines a window manager property that clients may use to
+ * share standard color maps of type RGB_COLOR_MAP:
+ */
+typedef struct {
+	Colormap colormap;
+	unsigned long red_max;
+	unsigned long red_mult;
+	unsigned long green_max;
+	unsigned long green_mult;
+	unsigned long blue_max;
+	unsigned long blue_mult;
+	unsigned long base_pixel;
+	VisualID visualid;		/* added by ICCCM version 1 */
+	XID killid;			/* added by ICCCM version 1 */
+} XStandardColormap;
+
+#define ReleaseByFreeingColormap ((XID) 1L)  /* for killid field above */
+
+
+/*
+ * return codes for XReadBitmapFile and XWriteBitmapFile
+ */
+#define BitmapSuccess		0
+#define BitmapOpenFailed 	1
+#define BitmapFileInvalid 	2
+#define BitmapNoMemory		3
+
+/****************************************************************
+ *
+ * Context Management
+ *
+ ****************************************************************/
+
+
+/* Associative lookup table return codes */
+
+#define XCSUCCESS 0	/* No error. */
+#define XCNOMEM   1    /* Out of memory */
+#define XCNOENT   2    /* No entry in table */
+
+typedef int XContext;
+
+#define XUniqueContext()       ((XContext) XrmUniqueQuark())
+#define XStringToContext(string)   ((XContext) XrmStringToQuark(string))
+
+_XFUNCPROTOBEGIN
+
+/* The following declarations are alphabetized. */
+
+extern XClassHint *XAllocClassHint (
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern XIconSize *XAllocIconSize (
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern XSizeHints *XAllocSizeHints (
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern XStandardColormap *XAllocStandardColormap (
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern XWMHints *XAllocWMHints (
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern void XClipBox(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    XRectangle*		/* rect_return */
+#endif
+);
+
+extern Region XCreateRegion(
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern char *XDefaultString(
+#if NeedFunctionPrototypes
+    void
+#endif
+);
+
+extern int XDeleteContext(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XID			/* rid */,
+    XContext		/* context */
+#endif
+);
+
+extern void XDestroyRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */
+#endif
+);
+
+extern void XEmptyRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */
+#endif
+);
+
+extern void XEqualRegion(
+#if NeedFunctionPrototypes
+    Region		/* r1 */,
+    Region		/* r2 */
+#endif
+);
+
+extern int XFindContext(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XID			/* rid */,
+    XContext		/* context */,
+    XPointer*		/* data_return */
+#endif
+);
+
+extern Status XGetClassHint(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XClassHint*		/* class_hints_return */
+#endif
+);
+
+extern Status XGetIconSizes(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XIconSize**		/* size_list_return */,
+    int*		/* count_return */
+#endif
+);
+
+extern Status XGetNormalHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints_return */
+#endif
+);
+
+extern Status XGetRGBColormaps(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XStandardColormap** /* stdcmap_return */,
+    int*		/* count_return */,
+    Atom		/* property */
+#endif
+);
+
+extern Status XGetSizeHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints_return */,
+    Atom		/* property */
+#endif
+);
+
+extern Status XGetStandardColormap(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XStandardColormap*	/* colormap_return */,
+    Atom		/* property */			    
+#endif
+);
+
+extern Status XGetTextProperty(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* window */,
+    XTextProperty*	/* text_prop_return */,
+    Atom		/* property */
+#endif
+);
+
+
+extern Status XGetWMClientMachine(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop_return */
+#endif
+);
+
+extern XWMHints *XGetWMHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */		      
+#endif
+);
+
+extern Status XGetWMIconName(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop_return */
+#endif
+);
+
+extern Status XGetWMName(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop_return */
+#endif
+);
+
+extern Status XGetWMNormalHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints_return */,
+    long*		/* supplied_return */ 
+#endif
+);
+
+extern Status XGetWMSizeHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints_return */,
+    long*		/* supplied_return */,
+    Atom		/* property */
+#endif
+);
+
+extern Status XGetZoomHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* zhints_return */
+#endif
+);
+
+extern void XIntersectRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XLookupString(
+#if NeedFunctionPrototypes
+    XKeyEvent*		/* event_struct */,
+    char*		/* buffer_return */,
+    int			/* bytes_buffer */,
+    KeySym*		/* keysym_return */,
+    XComposeStatus*	/* status_in_out */
+#endif
+);
+
+extern Status XMatchVisualInfo(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    int			/* screen */,
+    int			/* depth */,
+    int			/* class */,
+    XVisualInfo*	/* vinfo_return */
+#endif
+);
+
+extern void XOffsetRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* dx */,
+    int			/* dy */
+#endif
+);
+
+extern Bool XPointInRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* x */,
+    int			/* y */
+#endif
+);
+
+extern Region XPolygonRegion(
+#if NeedFunctionPrototypes
+    XPoint*		/* points */,
+    int			/* n */,
+    int			/* fill_rule */
+#endif
+);
+
+extern int XRectInRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* x */,
+    int			/* y */,
+    unsigned int	/* width */,
+    unsigned int	/* height */
+#endif
+);
+
+extern int XSaveContext(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XID			/* rid */,
+    XContext		/* context */,
+    _Xconst char*	/* data */
+#endif
+);
+
+extern void XSetClassHint(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XClassHint*		/* class_hints */
+#endif
+);
+
+extern void XSetIconSizes(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XIconSize*		/* size_list */,
+    int			/* count */    
+#endif
+);
+
+extern void XSetNormalHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints */
+#endif
+);
+
+extern void XSetRGBColormaps(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XStandardColormap*	/* stdcmaps */,
+    int			/* count */,
+    Atom		/* property */
+#endif
+);
+
+extern void XSetSizeHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints */,
+    Atom		/* property */
+#endif
+);
+
+extern void XSetStandardProperties(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    _Xconst char*	/* window_name */,
+    _Xconst char*	/* icon_name */,
+    Pixmap		/* icon_pixmap */,
+    char**		/* argv */,
+    int			/* argc */,
+    XSizeHints*		/* hints */
+#endif
+);
+
+extern void XSetTextProperty(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop */,
+    Atom		/* property */
+#endif
+);
+
+extern void XSetWMHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XWMHints*		/* wm_hints */
+#endif
+);
+
+extern void XSetWMIconName(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop */
+#endif
+);
+
+extern void XSetWMName(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* text_prop */
+#endif
+);
+
+extern void XSetWMNormalHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints */
+#endif
+);
+
+extern void XSetWMProperties(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XTextProperty*	/* window_name */,
+    XTextProperty*	/* icon_name */,
+    char**		/* argv */,
+    int			/* argc */,
+    XSizeHints*		/* normal_hints */,
+    XWMHints*		/* wm_hints */,
+    XClassHint*		/* class_hints */
+#endif
+);
+
+extern void XmbSetWMProperties(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    _Xconst char*	/* window_name */,
+    _Xconst char*	/* icon_name */,
+    char**		/* argv */,
+    int			/* argc */,
+    XSizeHints*		/* normal_hints */,
+    XWMHints*		/* wm_hints */,
+    XClassHint*		/* class_hints */
+#endif
+);
+
+extern void XSetWMSizeHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* hints */,
+    Atom		/* property */
+#endif
+);
+
+extern void XSetRegion(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    GC			/* gc */,
+    Region		/* r */
+#endif
+);
+
+extern void XSetStandardColormap(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XStandardColormap*	/* colormap */,
+    Atom		/* property */
+#endif
+);
+
+extern void XSetZoomHints(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    Window		/* w */,
+    XSizeHints*		/* zhints */
+#endif
+);
+
+extern void XShrinkRegion(
+#if NeedFunctionPrototypes
+    Region		/* r */,
+    int			/* dx */,
+    int			/* dy */
+#endif
+);
+
+extern void XSubtractRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XmbTextListToTextProperty(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    char**		/* list */,
+    int			/* count */,
+    XICCEncodingStyle	/* style */,
+    XTextProperty*	/* text_prop_return */
+#endif
+);
+
+extern int XwcTextListToTextProperty(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    wchar_t**		/* list */,
+    int			/* count */,
+    XICCEncodingStyle	/* style */,
+    XTextProperty*	/* text_prop_return */
+#endif
+);
+
+extern void XwcFreeStringList(
+#if NeedFunctionPrototypes
+    wchar_t**		/* list */
+#endif
+);
+
+extern Status XTextPropertyToStringList(
+#if NeedFunctionPrototypes
+    XTextProperty*	/* text_prop */,
+    char***		/* list_return */,
+    int*		/* count_return */
+#endif
+);
+
+extern int XmbTextPropertyToTextList(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XTextProperty*	/* text_prop */,
+    char***		/* list_return */,
+    int*		/* count_return */
+#endif
+);
+
+extern int XwcTextPropertyToTextList(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    XTextProperty*	/* text_prop */,
+    wchar_t***		/* list_return */,
+    int*		/* count_return */
+#endif
+);
+
+extern void XUnionRectWithRegion(
+#if NeedFunctionPrototypes
+    XRectangle*		/* rectangle */,
+    Region		/* src_region */,
+    Region		/* dest_region_return */
+#endif
+);
+
+extern void XUnionRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+extern int XWMGeometry(
+#if NeedFunctionPrototypes
+    Display*		/* display */,
+    int			/* screen_number */,
+    _Xconst char*	/* user_geometry */,
+    _Xconst char*	/* default_geometry */,
+    unsigned int	/* border_width */,
+    XSizeHints*		/* hints */,
+    int*		/* x_return */,
+    int*		/* y_return */,
+    int*		/* width_return */,
+    int*		/* height_return */,
+    int*		/* gravity_return */
+#endif
+);
+
+extern void XXorRegion(
+#if NeedFunctionPrototypes
+    Region		/* sra */,
+    Region		/* srb */,
+    Region		/* dr_return */
+#endif
+);
+
+_XFUNCPROTOEND
+
+#ifdef MAC_TCL
+#   undef Region
+#endif
+
+#endif /* _XUTIL_H_ */
Index: trunk/kitgen/8.x/blt/win/X11/cursorfont.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/cursorfont.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/cursorfont.h	(revision 175)
@@ -0,0 +1,79 @@
+/* $XConsortium: cursorfont.h,v 1.2 88/09/06 16:44:27 jim Exp $ */
+#define XC_num_glyphs 154
+#define XC_X_cursor 0
+#define XC_arrow 2
+#define XC_based_arrow_down 4
+#define XC_based_arrow_up 6
+#define XC_boat 8
+#define XC_bogosity 10
+#define XC_bottom_left_corner 12
+#define XC_bottom_right_corner 14
+#define XC_bottom_side 16
+#define XC_bottom_tee 18
+#define XC_box_spiral 20
+#define XC_center_ptr 22
+#define XC_circle 24
+#define XC_clock 26
+#define XC_coffee_mug 28
+#define XC_cross 30
+#define XC_cross_reverse 32
+#define XC_crosshair 34
+#define XC_diamond_cross 36
+#define XC_dot 38
+#define XC_dotbox 40
+#define XC_double_arrow 42
+#define XC_draft_large 44
+#define XC_draft_small 46
+#define XC_draped_box 48
+#define XC_exchange 50
+#define XC_fleur 52
+#define XC_gobbler 54
+#define XC_gumby 56
+#define XC_hand1 58
+#define XC_hand2 60
+#define XC_heart 62
+#define XC_icon 64
+#define XC_iron_cross 66
+#define XC_left_ptr 68
+#define XC_left_side 70
+#define XC_left_tee 72
+#define XC_leftbutton 74
+#define XC_ll_angle 76
+#define XC_lr_angle 78
+#define XC_man 80
+#define XC_middlebutton 82
+#define XC_mouse 84
+#define XC_pencil 86
+#define XC_pirate 88
+#define XC_plus 90
+#define XC_question_arrow 92
+#define XC_right_ptr 94
+#define XC_right_side 96
+#define XC_right_tee 98
+#define XC_rightbutton 100
+#define XC_rtl_logo 102
+#define XC_sailboat 104
+#define XC_sb_down_arrow 106
+#define XC_sb_h_double_arrow 108
+#define XC_sb_left_arrow 110
+#define XC_sb_right_arrow 112
+#define XC_sb_up_arrow 114
+#define XC_sb_v_double_arrow 116
+#define XC_shuttle 118
+#define XC_sizing 120
+#define XC_spider 122
+#define XC_spraycan 124
+#define XC_star 126
+#define XC_target 128
+#define XC_tcross 130
+#define XC_top_left_arrow 132
+#define XC_top_left_corner 134
+#define XC_top_right_corner 136
+#define XC_top_side 138
+#define XC_top_tee 140
+#define XC_trek 142
+#define XC_ul_angle 144
+#define XC_umbrella 146
+#define XC_ur_angle 148
+#define XC_watch 150
+#define XC_xterm 152
Index: trunk/kitgen/8.x/blt/win/X11/keysym.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/keysym.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/keysym.h	(revision 175)
@@ -0,0 +1,39 @@
+/* $XConsortium: keysym.h,v 1.13 91/03/13 20:09:49 rws Exp $ */
+
+/***********************************************************
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
+and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
+
+                        All Rights Reserved
+
+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 names of Digital or MIT not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/* default keysyms */
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_LATIN2
+#define XK_LATIN3
+#define XK_LATIN4
+#define XK_GREEK
+
+#ifdef MAC_TCL
+#include <keysymdef.h>
+#else
+#include <X11/keysymdef.h>
+#endif
Index: trunk/kitgen/8.x/blt/win/X11/keysymdef.h
===================================================================
--- trunk/kitgen/8.x/blt/win/X11/keysymdef.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/X11/keysymdef.h	(revision 175)
@@ -0,0 +1,1169 @@
+/* $XConsortium: keysymdef.h,v 1.15 93/04/02 10:57:36 rws Exp $ */
+
+/***********************************************************
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
+and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
+
+                        All Rights Reserved
+
+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 names of Digital or MIT not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define XK_VoidSymbol		0xFFFFFF	/* void symbol */
+
+#ifdef XK_MISCELLANY
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace		0xFF08	/* back space, back char */
+#define XK_Tab			0xFF09
+#define XK_Linefeed		0xFF0A	/* Linefeed, LF */
+#define XK_Clear		0xFF0B
+#define XK_Return		0xFF0D	/* Return, enter */
+#define XK_Pause		0xFF13	/* Pause, hold */
+#define XK_Scroll_Lock		0xFF14
+#define XK_Sys_Req		0xFF15
+#define XK_Escape		0xFF1B
+#define XK_Delete		0xFFFF	/* Delete, rubout */
+
+
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key		0xFF20  /* Multi-key character compose */
+
+/* Japanese keyboard support */
+
+#define XK_Kanji		0xFF21	/* Kanji, Kanji convert */
+#define XK_Muhenkan		0xFF22  /* Cancel Conversion */
+#define XK_Henkan_Mode		0xFF23  /* Start/Stop Conversion */
+#define XK_Henkan		0xFF23  /* Alias for Henkan_Mode */
+#define XK_Romaji		0xFF24  /* to Romaji */
+#define XK_Hiragana		0xFF25  /* to Hiragana */
+#define XK_Katakana		0xFF26  /* to Katakana */
+#define XK_Hiragana_Katakana	0xFF27  /* Hiragana/Katakana toggle */
+#define XK_Zenkaku		0xFF28  /* to Zenkaku */
+#define XK_Hankaku		0xFF29  /* to Hankaku */
+#define XK_Zenkaku_Hankaku	0xFF2A  /* Zenkaku/Hankaku toggle */
+#define XK_Touroku		0xFF2B  /* Add to Dictionary */
+#define XK_Massyo		0xFF2C  /* Delete from Dictionary */
+#define XK_Kana_Lock		0xFF2D  /* Kana Lock */
+#define XK_Kana_Shift		0xFF2E  /* Kana Shift */
+#define XK_Eisu_Shift		0xFF2F  /* Alphanumeric Shift */
+#define XK_Eisu_toggle		0xFF30  /* Alphanumeric toggle */
+
+/* Cursor control & motion */
+
+#define XK_Home			0xFF50
+#define XK_Left			0xFF51	/* Move left, left arrow */
+#define XK_Up			0xFF52	/* Move up, up arrow */
+#define XK_Right		0xFF53	/* Move right, right arrow */
+#define XK_Down			0xFF54	/* Move down, down arrow */
+#define XK_Prior		0xFF55	/* Prior, previous */
+#define XK_Page_Up		0xFF55
+#define XK_Next			0xFF56	/* Next */
+#define XK_Page_Down		0xFF56
+#define XK_End			0xFF57	/* EOL */
+#define XK_Begin		0xFF58	/* BOL */
+
+/* Special Windows keyboard keys */
+
+#define XK_Win_L		0xFF5B	/* Left-hand Windows */
+#define XK_Win_R		0xFF5C	/* Right-hand Windows */
+#define XK_App			0xFF5D	/* Menu key */
+
+/* Misc Functions */
+
+#define XK_Select		0xFF60	/* Select, mark */
+#define XK_Print		0xFF61
+#define XK_Execute		0xFF62	/* Execute, run, do */
+#define XK_Insert		0xFF63	/* Insert, insert here */
+#define XK_Undo			0xFF65	/* Undo, oops */
+#define XK_Redo			0xFF66	/* redo, again */
+#define XK_Menu			0xFF67
+#define XK_Find			0xFF68	/* Find, search */
+#define XK_Cancel		0xFF69	/* Cancel, stop, abort, exit */
+#define XK_Help			0xFF6A	/* Help, ? */
+#define XK_Break		0xFF6B
+#define XK_Mode_switch		0xFF7E	/* Character set switch */
+#define XK_script_switch        0xFF7E  /* Alias for mode_switch */
+#define XK_Num_Lock		0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space		0xFF80	/* space */
+#define XK_KP_Tab		0xFF89
+#define XK_KP_Enter		0xFF8D	/* enter */
+#define XK_KP_F1		0xFF91	/* PF1, KP_A, ... */
+#define XK_KP_F2		0xFF92
+#define XK_KP_F3		0xFF93
+#define XK_KP_F4		0xFF94
+#define XK_KP_Home		0xFF95
+#define XK_KP_Left		0xFF96
+#define XK_KP_Up		0xFF97
+#define XK_KP_Right		0xFF98
+#define XK_KP_Down		0xFF99
+#define XK_KP_Prior		0xFF9A
+#define XK_KP_Page_Up		0xFF9A
+#define XK_KP_Next		0xFF9B
+#define XK_KP_Page_Down		0xFF9B
+#define XK_KP_End		0xFF9C
+#define XK_KP_Begin		0xFF9D
+#define XK_KP_Insert		0xFF9E
+#define XK_KP_Delete		0xFF9F
+#define XK_KP_Equal		0xFFBD	/* equals */
+#define XK_KP_Multiply		0xFFAA
+#define XK_KP_Add		0xFFAB
+#define XK_KP_Separator		0xFFAC	/* separator, often comma */
+#define XK_KP_Subtract		0xFFAD
+#define XK_KP_Decimal		0xFFAE
+#define XK_KP_Divide		0xFFAF
+
+#define XK_KP_0			0xFFB0
+#define XK_KP_1			0xFFB1
+#define XK_KP_2			0xFFB2
+#define XK_KP_3			0xFFB3
+#define XK_KP_4			0xFFB4
+#define XK_KP_5			0xFFB5
+#define XK_KP_6			0xFFB6
+#define XK_KP_7			0xFFB7
+#define XK_KP_8			0xFFB8
+#define XK_KP_9			0xFFB9
+
+
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys;  Sun keyboards and a few other manufactures have such
+ * function key groups on the left and/or right sides of the keyboard.
+ * We've not found a keyboard with more than 35 function keys total.
+ */
+
+#define XK_F1			0xFFBE
+#define XK_F2			0xFFBF
+#define XK_F3			0xFFC0
+#define XK_F4			0xFFC1
+#define XK_F5			0xFFC2
+#define XK_F6			0xFFC3
+#define XK_F7			0xFFC4
+#define XK_F8			0xFFC5
+#define XK_F9			0xFFC6
+#define XK_F10			0xFFC7
+#define XK_F11			0xFFC8
+#define XK_L1			0xFFC8
+#define XK_F12			0xFFC9
+#define XK_L2			0xFFC9
+#define XK_F13			0xFFCA
+#define XK_L3			0xFFCA
+#define XK_F14			0xFFCB
+#define XK_L4			0xFFCB
+#define XK_F15			0xFFCC
+#define XK_L5			0xFFCC
+#define XK_F16			0xFFCD
+#define XK_L6			0xFFCD
+#define XK_F17			0xFFCE
+#define XK_L7			0xFFCE
+#define XK_F18			0xFFCF
+#define XK_L8			0xFFCF
+#define XK_F19			0xFFD0
+#define XK_L9			0xFFD0
+#define XK_F20			0xFFD1
+#define XK_L10			0xFFD1
+#define XK_F21			0xFFD2
+#define XK_R1			0xFFD2
+#define XK_F22			0xFFD3
+#define XK_R2			0xFFD3
+#define XK_F23			0xFFD4
+#define XK_R3			0xFFD4
+#define XK_F24			0xFFD5
+#define XK_R4			0xFFD5
+#define XK_F25			0xFFD6
+#define XK_R5			0xFFD6
+#define XK_F26			0xFFD7
+#define XK_R6			0xFFD7
+#define XK_F27			0xFFD8
+#define XK_R7			0xFFD8
+#define XK_F28			0xFFD9
+#define XK_R8			0xFFD9
+#define XK_F29			0xFFDA
+#define XK_R9			0xFFDA
+#define XK_F30			0xFFDB
+#define XK_R10			0xFFDB
+#define XK_F31			0xFFDC
+#define XK_R11			0xFFDC
+#define XK_F32			0xFFDD
+#define XK_R12			0xFFDD
+#define XK_F33			0xFFDE
+#define XK_R13			0xFFDE
+#define XK_F34			0xFFDF
+#define XK_R14			0xFFDF
+#define XK_F35			0xFFE0
+#define XK_R15			0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L		0xFFE1	/* Left shift */
+#define XK_Shift_R		0xFFE2	/* Right shift */
+#define XK_Control_L		0xFFE3	/* Left control */
+#define XK_Control_R		0xFFE4	/* Right control */
+#define XK_Caps_Lock		0xFFE5	/* Caps lock */
+#define XK_Shift_Lock		0xFFE6	/* Shift lock */
+
+#define XK_Meta_L		0xFFE7	/* Left meta */
+#define XK_Meta_R		0xFFE8	/* Right meta */
+#define XK_Alt_L		0xFFE9	/* Left alt */
+#define XK_Alt_R		0xFFEA	/* Right alt */
+#define XK_Super_L		0xFFEB	/* Left super */
+#define XK_Super_R		0xFFEC	/* Right super */
+#define XK_Hyper_L		0xFFED	/* Left hyper */
+#define XK_Hyper_R		0xFFEE	/* Right hyper */
+#endif /* XK_MISCELLANY */
+
+/*
+ *  Latin 1
+ *  Byte 3 = 0
+ */
+#ifdef XK_LATIN1
+#define XK_space               0x020
+#define XK_exclam              0x021
+#define XK_quotedbl            0x022
+#define XK_numbersign          0x023
+#define XK_dollar              0x024
+#define XK_percent             0x025
+#define XK_ampersand           0x026
+#define XK_apostrophe          0x027
+#define XK_quoteright          0x027	/* deprecated */
+#define XK_parenleft           0x028
+#define XK_parenright          0x029
+#define XK_asterisk            0x02a
+#define XK_plus                0x02b
+#define XK_comma               0x02c
+#define XK_minus               0x02d
+#define XK_period              0x02e
+#define XK_slash               0x02f
+#define XK_0                   0x030
+#define XK_1                   0x031
+#define XK_2                   0x032
+#define XK_3                   0x033
+#define XK_4                   0x034
+#define XK_5                   0x035
+#define XK_6                   0x036
+#define XK_7                   0x037
+#define XK_8                   0x038
+#define XK_9                   0x039
+#define XK_colon               0x03a
+#define XK_semicolon           0x03b
+#define XK_less                0x03c
+#define XK_equal               0x03d
+#define XK_greater             0x03e
+#define XK_question            0x03f
+#define XK_at                  0x040
+#define XK_A                   0x041
+#define XK_B                   0x042
+#define XK_C                   0x043
+#define XK_D                   0x044
+#define XK_E                   0x045
+#define XK_F                   0x046
+#define XK_G                   0x047
+#define XK_H                   0x048
+#define XK_I                   0x049
+#define XK_J                   0x04a
+#define XK_K                   0x04b
+#define XK_L                   0x04c
+#define XK_M                   0x04d
+#define XK_N                   0x04e
+#define XK_O                   0x04f
+#define XK_P                   0x050
+#define XK_Q                   0x051
+#define XK_R                   0x052
+#define XK_S                   0x053
+#define XK_T                   0x054
+#define XK_U                   0x055
+#define XK_V                   0x056
+#define XK_W                   0x057
+#define XK_X                   0x058
+#define XK_Y                   0x059
+#define XK_Z                   0x05a
+#define XK_bracketleft         0x05b
+#define XK_backslash           0x05c
+#define XK_bracketright        0x05d
+#define XK_asciicircum         0x05e
+#define XK_underscore          0x05f
+#define XK_grave               0x060
+#define XK_quoteleft           0x060	/* deprecated */
+#define XK_a                   0x061
+#define XK_b                   0x062
+#define XK_c                   0x063
+#define XK_d                   0x064
+#define XK_e                   0x065
+#define XK_f                   0x066
+#define XK_g                   0x067
+#define XK_h                   0x068
+#define XK_i                   0x069
+#define XK_j                   0x06a
+#define XK_k                   0x06b
+#define XK_l                   0x06c
+#define XK_m                   0x06d
+#define XK_n                   0x06e
+#define XK_o                   0x06f
+#define XK_p                   0x070
+#define XK_q                   0x071
+#define XK_r                   0x072
+#define XK_s                   0x073
+#define XK_t                   0x074
+#define XK_u                   0x075
+#define XK_v                   0x076
+#define XK_w                   0x077
+#define XK_x                   0x078
+#define XK_y                   0x079
+#define XK_z                   0x07a
+#define XK_braceleft           0x07b
+#define XK_bar                 0x07c
+#define XK_braceright          0x07d
+#define XK_asciitilde          0x07e
+
+#define XK_nobreakspace        0x0a0
+#define XK_exclamdown          0x0a1
+#define XK_cent        	       0x0a2
+#define XK_sterling            0x0a3
+#define XK_currency            0x0a4
+#define XK_yen                 0x0a5
+#define XK_brokenbar           0x0a6
+#define XK_section             0x0a7
+#define XK_diaeresis           0x0a8
+#define XK_copyright           0x0a9
+#define XK_ordfeminine         0x0aa
+#define XK_guillemotleft       0x0ab	/* left angle quotation mark */
+#define XK_notsign             0x0ac
+#define XK_hyphen              0x0ad
+#define XK_registered          0x0ae
+#define XK_macron              0x0af
+#define XK_degree              0x0b0
+#define XK_plusminus           0x0b1
+#define XK_twosuperior         0x0b2
+#define XK_threesuperior       0x0b3
+#define XK_acute               0x0b4
+#define XK_mu                  0x0b5
+#define XK_paragraph           0x0b6
+#define XK_periodcentered      0x0b7
+#define XK_cedilla             0x0b8
+#define XK_onesuperior         0x0b9
+#define XK_masculine           0x0ba
+#define XK_guillemotright      0x0bb	/* right angle quotation mark */
+#define XK_onequarter          0x0bc
+#define XK_onehalf             0x0bd
+#define XK_threequarters       0x0be
+#define XK_questiondown        0x0bf
+#define XK_Agrave              0x0c0
+#define XK_Aacute              0x0c1
+#define XK_Acircumflex         0x0c2
+#define XK_Atilde              0x0c3
+#define XK_Adiaeresis          0x0c4
+#define XK_Aring               0x0c5
+#define XK_AE                  0x0c6
+#define XK_Ccedilla            0x0c7
+#define XK_Egrave              0x0c8
+#define XK_Eacute              0x0c9
+#define XK_Ecircumflex         0x0ca
+#define XK_Ediaeresis          0x0cb
+#define XK_Igrave              0x0cc
+#define XK_Iacute              0x0cd
+#define XK_Icircumflex         0x0ce
+#define XK_Idiaeresis          0x0cf
+#define XK_ETH                 0x0d0
+#define XK_Eth                 0x0d0	/* deprecated */
+#define XK_Ntilde              0x0d1
+#define XK_Ograve              0x0d2
+#define XK_Oacute              0x0d3
+#define XK_Ocircumflex         0x0d4
+#define XK_Otilde              0x0d5
+#define XK_Odiaeresis          0x0d6
+#define XK_multiply            0x0d7
+#define XK_Ooblique            0x0d8
+#define XK_Ugrave              0x0d9
+#define XK_Uacute              0x0da
+#define XK_Ucircumflex         0x0db
+#define XK_Udiaeresis          0x0dc
+#define XK_Yacute              0x0dd
+#define XK_THORN               0x0de
+#define XK_Thorn               0x0de	/* deprecated */
+#define XK_ssharp              0x0df
+#define XK_agrave              0x0e0
+#define XK_aacute              0x0e1
+#define XK_acircumflex         0x0e2
+#define XK_atilde              0x0e3
+#define XK_adiaeresis          0x0e4
+#define XK_aring               0x0e5
+#define XK_ae                  0x0e6
+#define XK_ccedilla            0x0e7
+#define XK_egrave              0x0e8
+#define XK_eacute              0x0e9
+#define XK_ecircumflex         0x0ea
+#define XK_ediaeresis          0x0eb
+#define XK_igrave              0x0ec
+#define XK_iacute              0x0ed
+#define XK_icircumflex         0x0ee
+#define XK_idiaeresis          0x0ef
+#define XK_eth                 0x0f0
+#define XK_ntilde              0x0f1
+#define XK_ograve              0x0f2
+#define XK_oacute              0x0f3
+#define XK_ocircumflex         0x0f4
+#define XK_otilde              0x0f5
+#define XK_odiaeresis          0x0f6
+#define XK_division            0x0f7
+#define XK_oslash              0x0f8
+#define XK_ugrave              0x0f9
+#define XK_uacute              0x0fa
+#define XK_ucircumflex         0x0fb
+#define XK_udiaeresis          0x0fc
+#define XK_yacute              0x0fd
+#define XK_thorn               0x0fe
+#define XK_ydiaeresis          0x0ff
+#endif /* XK_LATIN1 */
+
+/*
+ *   Latin 2
+ *   Byte 3 = 1
+ */
+
+#ifdef XK_LATIN2
+#define XK_Aogonek             0x1a1
+#define XK_breve               0x1a2
+#define XK_Lstroke             0x1a3
+#define XK_Lcaron              0x1a5
+#define XK_Sacute              0x1a6
+#define XK_Scaron              0x1a9
+#define XK_Scedilla            0x1aa
+#define XK_Tcaron              0x1ab
+#define XK_Zacute              0x1ac
+#define XK_Zcaron              0x1ae
+#define XK_Zabovedot           0x1af
+#define XK_aogonek             0x1b1
+#define XK_ogonek              0x1b2
+#define XK_lstroke             0x1b3
+#define XK_lcaron              0x1b5
+#define XK_sacute              0x1b6
+#define XK_caron               0x1b7
+#define XK_scaron              0x1b9
+#define XK_scedilla            0x1ba
+#define XK_tcaron              0x1bb
+#define XK_zacute              0x1bc
+#define XK_doubleacute         0x1bd
+#define XK_zcaron              0x1be
+#define XK_zabovedot           0x1bf
+#define XK_Racute              0x1c0
+#define XK_Abreve              0x1c3
+#define XK_Lacute              0x1c5
+#define XK_Cacute              0x1c6
+#define XK_Ccaron              0x1c8
+#define XK_Eogonek             0x1ca
+#define XK_Ecaron              0x1cc
+#define XK_Dcaron              0x1cf
+#define XK_Dstroke             0x1d0
+#define XK_Nacute              0x1d1
+#define XK_Ncaron              0x1d2
+#define XK_Odoubleacute        0x1d5
+#define XK_Rcaron              0x1d8
+#define XK_Uring               0x1d9
+#define XK_Udoubleacute        0x1db
+#define XK_Tcedilla            0x1de
+#define XK_racute              0x1e0
+#define XK_abreve              0x1e3
+#define XK_lacute              0x1e5
+#define XK_cacute              0x1e6
+#define XK_ccaron              0x1e8
+#define XK_eogonek             0x1ea
+#define XK_ecaron              0x1ec
+#define XK_dcaron              0x1ef
+#define XK_dstroke             0x1f0
+#define XK_nacute              0x1f1
+#define XK_ncaron              0x1f2
+#define XK_odoubleacute        0x1f5
+#define XK_udoubleacute        0x1fb
+#define XK_rcaron              0x1f8
+#define XK_uring               0x1f9
+#define XK_tcedilla            0x1fe
+#define XK_abovedot            0x1ff
+#endif /* XK_LATIN2 */
+
+/*
+ *   Latin 3
+ *   Byte 3 = 2
+ */
+
+#ifdef XK_LATIN3
+#define XK_Hstroke             0x2a1
+#define XK_Hcircumflex         0x2a6
+#define XK_Iabovedot           0x2a9
+#define XK_Gbreve              0x2ab
+#define XK_Jcircumflex         0x2ac
+#define XK_hstroke             0x2b1
+#define XK_hcircumflex         0x2b6
+#define XK_idotless            0x2b9
+#define XK_gbreve              0x2bb
+#define XK_jcircumflex         0x2bc
+#define XK_Cabovedot           0x2c5
+#define XK_Ccircumflex         0x2c6
+#define XK_Gabovedot           0x2d5
+#define XK_Gcircumflex         0x2d8
+#define XK_Ubreve              0x2dd
+#define XK_Scircumflex         0x2de
+#define XK_cabovedot           0x2e5
+#define XK_ccircumflex         0x2e6
+#define XK_gabovedot           0x2f5
+#define XK_gcircumflex         0x2f8
+#define XK_ubreve              0x2fd
+#define XK_scircumflex         0x2fe
+#endif /* XK_LATIN3 */
+
+
+/*
+ *   Latin 4
+ *   Byte 3 = 3
+ */
+
+#ifdef XK_LATIN4
+#define XK_kra                 0x3a2
+#define XK_kappa               0x3a2	/* deprecated */
+#define XK_Rcedilla            0x3a3
+#define XK_Itilde              0x3a5
+#define XK_Lcedilla            0x3a6
+#define XK_Emacron             0x3aa
+#define XK_Gcedilla            0x3ab
+#define XK_Tslash              0x3ac
+#define XK_rcedilla            0x3b3
+#define XK_itilde              0x3b5
+#define XK_lcedilla            0x3b6
+#define XK_emacron             0x3ba
+#define XK_gcedilla            0x3bb
+#define XK_tslash              0x3bc
+#define XK_ENG                 0x3bd
+#define XK_eng                 0x3bf
+#define XK_Amacron             0x3c0
+#define XK_Iogonek             0x3c7
+#define XK_Eabovedot           0x3cc
+#define XK_Imacron             0x3cf
+#define XK_Ncedilla            0x3d1
+#define XK_Omacron             0x3d2
+#define XK_Kcedilla            0x3d3
+#define XK_Uogonek             0x3d9
+#define XK_Utilde              0x3dd
+#define XK_Umacron             0x3de
+#define XK_amacron             0x3e0
+#define XK_iogonek             0x3e7
+#define XK_eabovedot           0x3ec
+#define XK_imacron             0x3ef
+#define XK_ncedilla            0x3f1
+#define XK_omacron             0x3f2
+#define XK_kcedilla            0x3f3
+#define XK_uogonek             0x3f9
+#define XK_utilde              0x3fd
+#define XK_umacron             0x3fe
+#endif /* XK_LATIN4 */
+
+/*
+ * Katakana
+ * Byte 3 = 4
+ */
+
+#ifdef XK_KATAKANA
+#define XK_overline				       0x47e
+#define XK_kana_fullstop                               0x4a1
+#define XK_kana_openingbracket                         0x4a2
+#define XK_kana_closingbracket                         0x4a3
+#define XK_kana_comma                                  0x4a4
+#define XK_kana_conjunctive                            0x4a5
+#define XK_kana_middledot                              0x4a5  /* deprecated */
+#define XK_kana_WO                                     0x4a6
+#define XK_kana_a                                      0x4a7
+#define XK_kana_i                                      0x4a8
+#define XK_kana_u                                      0x4a9
+#define XK_kana_e                                      0x4aa
+#define XK_kana_o                                      0x4ab
+#define XK_kana_ya                                     0x4ac
+#define XK_kana_yu                                     0x4ad
+#define XK_kana_yo                                     0x4ae
+#define XK_kana_tsu                                    0x4af
+#define XK_kana_tu                                     0x4af  /* deprecated */
+#define XK_prolongedsound                              0x4b0
+#define XK_kana_A                                      0x4b1
+#define XK_kana_I                                      0x4b2
+#define XK_kana_U                                      0x4b3
+#define XK_kana_E                                      0x4b4
+#define XK_kana_O                                      0x4b5
+#define XK_kana_KA                                     0x4b6
+#define XK_kana_KI                                     0x4b7
+#define XK_kana_KU                                     0x4b8
+#define XK_kana_KE                                     0x4b9
+#define XK_kana_KO                                     0x4ba
+#define XK_kana_SA                                     0x4bb
+#define XK_kana_SHI                                    0x4bc
+#define XK_kana_SU                                     0x4bd
+#define XK_kana_SE                                     0x4be
+#define XK_kana_SO                                     0x4bf
+#define XK_kana_TA                                     0x4c0
+#define XK_kana_CHI                                    0x4c1
+#define XK_kana_TI                                     0x4c1  /* deprecated */
+#define XK_kana_TSU                                    0x4c2
+#define XK_kana_TU                                     0x4c2  /* deprecated */
+#define XK_kana_TE                                     0x4c3
+#define XK_kana_TO                                     0x4c4
+#define XK_kana_NA                                     0x4c5
+#define XK_kana_NI                                     0x4c6
+#define XK_kana_NU                                     0x4c7
+#define XK_kana_NE                                     0x4c8
+#define XK_kana_NO                                     0x4c9
+#define XK_kana_HA                                     0x4ca
+#define XK_kana_HI                                     0x4cb
+#define XK_kana_FU                                     0x4cc
+#define XK_kana_HU                                     0x4cc  /* deprecated */
+#define XK_kana_HE                                     0x4cd
+#define XK_kana_HO                                     0x4ce
+#define XK_kana_MA                                     0x4cf
+#define XK_kana_MI                                     0x4d0
+#define XK_kana_MU                                     0x4d1
+#define XK_kana_ME                                     0x4d2
+#define XK_kana_MO                                     0x4d3
+#define XK_kana_YA                                     0x4d4
+#define XK_kana_YU                                     0x4d5
+#define XK_kana_YO                                     0x4d6
+#define XK_kana_RA                                     0x4d7
+#define XK_kana_RI                                     0x4d8
+#define XK_kana_RU                                     0x4d9
+#define XK_kana_RE                                     0x4da
+#define XK_kana_RO                                     0x4db
+#define XK_kana_WA                                     0x4dc
+#define XK_kana_N                                      0x4dd
+#define XK_voicedsound                                 0x4de
+#define XK_semivoicedsound                             0x4df
+#define XK_kana_switch          0xFF7E  /* Alias for mode_switch */
+#endif /* XK_KATAKANA */
+
+/*
+ *  Arabic
+ *  Byte 3 = 5
+ */
+
+#ifdef XK_ARABIC
+#define XK_Arabic_comma                                0x5ac
+#define XK_Arabic_semicolon                            0x5bb
+#define XK_Arabic_question_mark                        0x5bf
+#define XK_Arabic_hamza                                0x5c1
+#define XK_Arabic_maddaonalef                          0x5c2
+#define XK_Arabic_hamzaonalef                          0x5c3
+#define XK_Arabic_hamzaonwaw                           0x5c4
+#define XK_Arabic_hamzaunderalef                       0x5c5
+#define XK_Arabic_hamzaonyeh                           0x5c6
+#define XK_Arabic_alef                                 0x5c7
+#define XK_Arabic_beh                                  0x5c8
+#define XK_Arabic_tehmarbuta                           0x5c9
+#define XK_Arabic_teh                                  0x5ca
+#define XK_Arabic_theh                                 0x5cb
+#define XK_Arabic_jeem                                 0x5cc
+#define XK_Arabic_hah                                  0x5cd
+#define XK_Arabic_khah                                 0x5ce
+#define XK_Arabic_dal                                  0x5cf
+#define XK_Arabic_thal                                 0x5d0
+#define XK_Arabic_ra                                   0x5d1
+#define XK_Arabic_zain                                 0x5d2
+#define XK_Arabic_seen                                 0x5d3
+#define XK_Arabic_sheen                                0x5d4
+#define XK_Arabic_sad                                  0x5d5
+#define XK_Arabic_dad                                  0x5d6
+#define XK_Arabic_tah                                  0x5d7
+#define XK_Arabic_zah                                  0x5d8
+#define XK_Arabic_ain                                  0x5d9
+#define XK_Arabic_ghain                                0x5da
+#define XK_Arabic_tatweel                              0x5e0
+#define XK_Arabic_feh                                  0x5e1
+#define XK_Arabic_qaf                                  0x5e2
+#define XK_Arabic_kaf                                  0x5e3
+#define XK_Arabic_lam                                  0x5e4
+#define XK_Arabic_meem                                 0x5e5
+#define XK_Arabic_noon                                 0x5e6
+#define XK_Arabic_ha                                   0x5e7
+#define XK_Arabic_heh                                  0x5e7  /* deprecated */
+#define XK_Arabic_waw                                  0x5e8
+#define XK_Arabic_alefmaksura                          0x5e9
+#define XK_Arabic_yeh                                  0x5ea
+#define XK_Arabic_fathatan                             0x5eb
+#define XK_Arabic_dammatan                             0x5ec
+#define XK_Arabic_kasratan                             0x5ed
+#define XK_Arabic_fatha                                0x5ee
+#define XK_Arabic_damma                                0x5ef
+#define XK_Arabic_kasra                                0x5f0
+#define XK_Arabic_shadda                               0x5f1
+#define XK_Arabic_sukun                                0x5f2
+#define XK_Arabic_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_ARABIC */
+
+/*
+ * Cyrillic
+ * Byte 3 = 6
+ */
+#ifdef XK_CYRILLIC
+#define XK_Serbian_dje                                 0x6a1
+#define XK_Macedonia_gje                               0x6a2
+#define XK_Cyrillic_io                                 0x6a3
+#define XK_Ukrainian_ie                                0x6a4
+#define XK_Ukranian_je                                 0x6a4  /* deprecated */
+#define XK_Macedonia_dse                               0x6a5
+#define XK_Ukrainian_i                                 0x6a6
+#define XK_Ukranian_i                                  0x6a6  /* deprecated */
+#define XK_Ukrainian_yi                                0x6a7
+#define XK_Ukranian_yi                                 0x6a7  /* deprecated */
+#define XK_Cyrillic_je                                 0x6a8
+#define XK_Serbian_je                                  0x6a8  /* deprecated */
+#define XK_Cyrillic_lje                                0x6a9
+#define XK_Serbian_lje                                 0x6a9  /* deprecated */
+#define XK_Cyrillic_nje                                0x6aa
+#define XK_Serbian_nje                                 0x6aa  /* deprecated */
+#define XK_Serbian_tshe                                0x6ab
+#define XK_Macedonia_kje                               0x6ac
+#define XK_Byelorussian_shortu                         0x6ae
+#define XK_Cyrillic_dzhe                               0x6af
+#define XK_Serbian_dze                                 0x6af  /* deprecated */
+#define XK_numerosign                                  0x6b0
+#define XK_Serbian_DJE                                 0x6b1
+#define XK_Macedonia_GJE                               0x6b2
+#define XK_Cyrillic_IO                                 0x6b3
+#define XK_Ukrainian_IE                                0x6b4
+#define XK_Ukranian_JE                                 0x6b4  /* deprecated */
+#define XK_Macedonia_DSE                               0x6b5
+#define XK_Ukrainian_I                                 0x6b6
+#define XK_Ukranian_I                                  0x6b6  /* deprecated */
+#define XK_Ukrainian_YI                                0x6b7
+#define XK_Ukranian_YI                                 0x6b7  /* deprecated */
+#define XK_Cyrillic_JE                                 0x6b8
+#define XK_Serbian_JE                                  0x6b8  /* deprecated */
+#define XK_Cyrillic_LJE                                0x6b9
+#define XK_Serbian_LJE                                 0x6b9  /* deprecated */
+#define XK_Cyrillic_NJE                                0x6ba
+#define XK_Serbian_NJE                                 0x6ba  /* deprecated */
+#define XK_Serbian_TSHE                                0x6bb
+#define XK_Macedonia_KJE                               0x6bc
+#define XK_Byelorussian_SHORTU                         0x6be
+#define XK_Cyrillic_DZHE                               0x6bf
+#define XK_Serbian_DZE                                 0x6bf  /* deprecated */
+#define XK_Cyrillic_yu                                 0x6c0
+#define XK_Cyrillic_a                                  0x6c1
+#define XK_Cyrillic_be                                 0x6c2
+#define XK_Cyrillic_tse                                0x6c3
+#define XK_Cyrillic_de                                 0x6c4
+#define XK_Cyrillic_ie                                 0x6c5
+#define XK_Cyrillic_ef                                 0x6c6
+#define XK_Cyrillic_ghe                                0x6c7
+#define XK_Cyrillic_ha                                 0x6c8
+#define XK_Cyrillic_i                                  0x6c9
+#define XK_Cyrillic_shorti                             0x6ca
+#define XK_Cyrillic_ka                                 0x6cb
+#define XK_Cyrillic_el                                 0x6cc
+#define XK_Cyrillic_em                                 0x6cd
+#define XK_Cyrillic_en                                 0x6ce
+#define XK_Cyrillic_o                                  0x6cf
+#define XK_Cyrillic_pe                                 0x6d0
+#define XK_Cyrillic_ya                                 0x6d1
+#define XK_Cyrillic_er                                 0x6d2
+#define XK_Cyrillic_es                                 0x6d3
+#define XK_Cyrillic_te                                 0x6d4
+#define XK_Cyrillic_u                                  0x6d5
+#define XK_Cyrillic_zhe                                0x6d6
+#define XK_Cyrillic_ve                                 0x6d7
+#define XK_Cyrillic_softsign                           0x6d8
+#define XK_Cyrillic_yeru                               0x6d9
+#define XK_Cyrillic_ze                                 0x6da
+#define XK_Cyrillic_sha                                0x6db
+#define XK_Cyrillic_e                                  0x6dc
+#define XK_Cyrillic_shcha                              0x6dd
+#define XK_Cyrillic_che                                0x6de
+#define XK_Cyrillic_hardsign                           0x6df
+#define XK_Cyrillic_YU                                 0x6e0
+#define XK_Cyrillic_A                                  0x6e1
+#define XK_Cyrillic_BE                                 0x6e2
+#define XK_Cyrillic_TSE                                0x6e3
+#define XK_Cyrillic_DE                                 0x6e4
+#define XK_Cyrillic_IE                                 0x6e5
+#define XK_Cyrillic_EF                                 0x6e6
+#define XK_Cyrillic_GHE                                0x6e7
+#define XK_Cyrillic_HA                                 0x6e8
+#define XK_Cyrillic_I                                  0x6e9
+#define XK_Cyrillic_SHORTI                             0x6ea
+#define XK_Cyrillic_KA                                 0x6eb
+#define XK_Cyrillic_EL                                 0x6ec
+#define XK_Cyrillic_EM                                 0x6ed
+#define XK_Cyrillic_EN                                 0x6ee
+#define XK_Cyrillic_O                                  0x6ef
+#define XK_Cyrillic_PE                                 0x6f0
+#define XK_Cyrillic_YA                                 0x6f1
+#define XK_Cyrillic_ER                                 0x6f2
+#define XK_Cyrillic_ES                                 0x6f3
+#define XK_Cyrillic_TE                                 0x6f4
+#define XK_Cyrillic_U                                  0x6f5
+#define XK_Cyrillic_ZHE                                0x6f6
+#define XK_Cyrillic_VE                                 0x6f7
+#define XK_Cyrillic_SOFTSIGN                           0x6f8
+#define XK_Cyrillic_YERU                               0x6f9
+#define XK_Cyrillic_ZE                                 0x6fa
+#define XK_Cyrillic_SHA                                0x6fb
+#define XK_Cyrillic_E                                  0x6fc
+#define XK_Cyrillic_SHCHA                              0x6fd
+#define XK_Cyrillic_CHE                                0x6fe
+#define XK_Cyrillic_HARDSIGN                           0x6ff
+#endif /* XK_CYRILLIC */
+
+/*
+ * Greek
+ * Byte 3 = 7
+ */
+
+#ifdef XK_GREEK
+#define XK_Greek_ALPHAaccent                           0x7a1
+#define XK_Greek_EPSILONaccent                         0x7a2
+#define XK_Greek_ETAaccent                             0x7a3
+#define XK_Greek_IOTAaccent                            0x7a4
+#define XK_Greek_IOTAdiaeresis                         0x7a5
+#define XK_Greek_OMICRONaccent                         0x7a7
+#define XK_Greek_UPSILONaccent                         0x7a8
+#define XK_Greek_UPSILONdieresis                       0x7a9
+#define XK_Greek_OMEGAaccent                           0x7ab
+#define XK_Greek_accentdieresis                        0x7ae
+#define XK_Greek_horizbar                              0x7af
+#define XK_Greek_alphaaccent                           0x7b1
+#define XK_Greek_epsilonaccent                         0x7b2
+#define XK_Greek_etaaccent                             0x7b3
+#define XK_Greek_iotaaccent                            0x7b4
+#define XK_Greek_iotadieresis                          0x7b5
+#define XK_Greek_iotaaccentdieresis                    0x7b6
+#define XK_Greek_omicronaccent                         0x7b7
+#define XK_Greek_upsilonaccent                         0x7b8
+#define XK_Greek_upsilondieresis                       0x7b9
+#define XK_Greek_upsilonaccentdieresis                 0x7ba
+#define XK_Greek_omegaaccent                           0x7bb
+#define XK_Greek_ALPHA                                 0x7c1
+#define XK_Greek_BETA                                  0x7c2
+#define XK_Greek_GAMMA                                 0x7c3
+#define XK_Greek_DELTA                                 0x7c4
+#define XK_Greek_EPSILON                               0x7c5
+#define XK_Greek_ZETA                                  0x7c6
+#define XK_Greek_ETA                                   0x7c7
+#define XK_Greek_THETA                                 0x7c8
+#define XK_Greek_IOTA                                  0x7c9
+#define XK_Greek_KAPPA                                 0x7ca
+#define XK_Greek_LAMDA                                 0x7cb
+#define XK_Greek_LAMBDA                                0x7cb
+#define XK_Greek_MU                                    0x7cc
+#define XK_Greek_NU                                    0x7cd
+#define XK_Greek_XI                                    0x7ce
+#define XK_Greek_OMICRON                               0x7cf
+#define XK_Greek_PI                                    0x7d0
+#define XK_Greek_RHO                                   0x7d1
+#define XK_Greek_SIGMA                                 0x7d2
+#define XK_Greek_TAU                                   0x7d4
+#define XK_Greek_UPSILON                               0x7d5
+#define XK_Greek_PHI                                   0x7d6
+#define XK_Greek_CHI                                   0x7d7
+#define XK_Greek_PSI                                   0x7d8
+#define XK_Greek_OMEGA                                 0x7d9
+#define XK_Greek_alpha                                 0x7e1
+#define XK_Greek_beta                                  0x7e2
+#define XK_Greek_gamma                                 0x7e3
+#define XK_Greek_delta                                 0x7e4
+#define XK_Greek_epsilon                               0x7e5
+#define XK_Greek_zeta                                  0x7e6
+#define XK_Greek_eta                                   0x7e7
+#define XK_Greek_theta                                 0x7e8
+#define XK_Greek_iota                                  0x7e9
+#define XK_Greek_kappa                                 0x7ea
+#define XK_Greek_lamda                                 0x7eb
+#define XK_Greek_lambda                                0x7eb
+#define XK_Greek_mu                                    0x7ec
+#define XK_Greek_nu                                    0x7ed
+#define XK_Greek_xi                                    0x7ee
+#define XK_Greek_omicron                               0x7ef
+#define XK_Greek_pi                                    0x7f0
+#define XK_Greek_rho                                   0x7f1
+#define XK_Greek_sigma                                 0x7f2
+#define XK_Greek_finalsmallsigma                       0x7f3
+#define XK_Greek_tau                                   0x7f4
+#define XK_Greek_upsilon                               0x7f5
+#define XK_Greek_phi                                   0x7f6
+#define XK_Greek_chi                                   0x7f7
+#define XK_Greek_psi                                   0x7f8
+#define XK_Greek_omega                                 0x7f9
+#define XK_Greek_switch         0xFF7E  /* Alias for mode_switch */
+#endif /* XK_GREEK */
+
+/*
+ * Technical
+ * Byte 3 = 8
+ */
+
+#ifdef XK_TECHNICAL
+#define XK_leftradical                                 0x8a1
+#define XK_topleftradical                              0x8a2
+#define XK_horizconnector                              0x8a3
+#define XK_topintegral                                 0x8a4
+#define XK_botintegral                                 0x8a5
+#define XK_vertconnector                               0x8a6
+#define XK_topleftsqbracket                            0x8a7
+#define XK_botleftsqbracket                            0x8a8
+#define XK_toprightsqbracket                           0x8a9
+#define XK_botrightsqbracket                           0x8aa
+#define XK_topleftparens                               0x8ab
+#define XK_botleftparens                               0x8ac
+#define XK_toprightparens                              0x8ad
+#define XK_botrightparens                              0x8ae
+#define XK_leftmiddlecurlybrace                        0x8af
+#define XK_rightmiddlecurlybrace                       0x8b0
+#define XK_topleftsummation                            0x8b1
+#define XK_botleftsummation                            0x8b2
+#define XK_topvertsummationconnector                   0x8b3
+#define XK_botvertsummationconnector                   0x8b4
+#define XK_toprightsummation                           0x8b5
+#define XK_botrightsummation                           0x8b6
+#define XK_rightmiddlesummation                        0x8b7
+#define XK_lessthanequal                               0x8bc
+#define XK_notequal                                    0x8bd
+#define XK_greaterthanequal                            0x8be
+#define XK_integral                                    0x8bf
+#define XK_therefore                                   0x8c0
+#define XK_variation                                   0x8c1
+#define XK_infinity                                    0x8c2
+#define XK_nabla                                       0x8c5
+#define XK_approximate                                 0x8c8
+#define XK_similarequal                                0x8c9
+#define XK_ifonlyif                                    0x8cd
+#define XK_implies                                     0x8ce
+#define XK_identical                                   0x8cf
+#define XK_radical                                     0x8d6
+#define XK_includedin                                  0x8da
+#define XK_includes                                    0x8db
+#define XK_intersection                                0x8dc
+#define XK_union                                       0x8dd
+#define XK_logicaland                                  0x8de
+#define XK_logicalor                                   0x8df
+#define XK_partialderivative                           0x8ef
+#define XK_function                                    0x8f6
+#define XK_leftarrow                                   0x8fb
+#define XK_uparrow                                     0x8fc
+#define XK_rightarrow                                  0x8fd
+#define XK_downarrow                                   0x8fe
+#endif /* XK_TECHNICAL */
+
+/*
+ *  Special
+ *  Byte 3 = 9
+ */
+
+#ifdef XK_SPECIAL
+#define XK_blank                                       0x9df
+#define XK_soliddiamond                                0x9e0
+#define XK_checkerboard                                0x9e1
+#define XK_ht                                          0x9e2
+#define XK_ff                                          0x9e3
+#define XK_cr                                          0x9e4
+#define XK_lf                                          0x9e5
+#define XK_nl                                          0x9e8
+#define XK_vt                                          0x9e9
+#define XK_lowrightcorner                              0x9ea
+#define XK_uprightcorner                               0x9eb
+#define XK_upleftcorner                                0x9ec
+#define XK_lowleftcorner                               0x9ed
+#define XK_crossinglines                               0x9ee
+#define XK_horizlinescan1                              0x9ef
+#define XK_horizlinescan3                              0x9f0
+#define XK_horizlinescan5                              0x9f1
+#define XK_horizlinescan7                              0x9f2
+#define XK_horizlinescan9                              0x9f3
+#define XK_leftt                                       0x9f4
+#define XK_rightt                                      0x9f5
+#define XK_bott                                        0x9f6
+#define XK_topt                                        0x9f7
+#define XK_vertbar                                     0x9f8
+#endif /* XK_SPECIAL */
+
+/*
+ *  Publishing
+ *  Byte 3 = a
+ */
+
+#ifdef XK_PUBLISHING
+#define XK_emspace                                     0xaa1
+#define XK_enspace                                     0xaa2
+#define XK_em3space                                    0xaa3
+#define XK_em4space                                    0xaa4
+#define XK_digitspace                                  0xaa5
+#define XK_punctspace                                  0xaa6
+#define XK_thinspace                                   0xaa7
+#define XK_hairspace                                   0xaa8
+#define XK_emdash                                      0xaa9
+#define XK_endash                                      0xaaa
+#define XK_signifblank                                 0xaac
+#define XK_ellipsis                                    0xaae
+#define XK_doubbaselinedot                             0xaaf
+#define XK_onethird                                    0xab0
+#define XK_twothirds                                   0xab1
+#define XK_onefifth                                    0xab2
+#define XK_twofifths                                   0xab3
+#define XK_threefifths                                 0xab4
+#define XK_fourfifths                                  0xab5
+#define XK_onesixth                                    0xab6
+#define XK_fivesixths                                  0xab7
+#define XK_careof                                      0xab8
+#define XK_figdash                                     0xabb
+#define XK_leftanglebracket                            0xabc
+#define XK_decimalpoint                                0xabd
+#define XK_rightanglebracket                           0xabe
+#define XK_marker                                      0xabf
+#define XK_oneeighth                                   0xac3
+#define XK_threeeighths                                0xac4
+#define XK_fiveeighths                                 0xac5
+#define XK_seveneighths                                0xac6
+#define XK_trademark                                   0xac9
+#define XK_signaturemark                               0xaca
+#define XK_trademarkincircle                           0xacb
+#define XK_leftopentriangle                            0xacc
+#define XK_rightopentriangle                           0xacd
+#define XK_emopencircle                                0xace
+#define XK_emopenrectangle                             0xacf
+#define XK_leftsinglequotemark                         0xad0
+#define XK_rightsinglequotemark                        0xad1
+#define XK_leftdoublequotemark                         0xad2
+#define XK_rightdoublequotemark                        0xad3
+#define XK_prescription                                0xad4
+#define XK_minutes                                     0xad6
+#define XK_seconds                                     0xad7
+#define XK_latincross                                  0xad9
+#define XK_hexagram                                    0xada
+#define XK_filledrectbullet                            0xadb
+#define XK_filledlefttribullet                         0xadc
+#define XK_filledrighttribullet                        0xadd
+#define XK_emfilledcircle                              0xade
+#define XK_emfilledrect                                0xadf
+#define XK_enopencircbullet                            0xae0
+#define XK_enopensquarebullet                          0xae1
+#define XK_openrectbullet                              0xae2
+#define XK_opentribulletup                             0xae3
+#define XK_opentribulletdown                           0xae4
+#define XK_openstar                                    0xae5
+#define XK_enfilledcircbullet                          0xae6
+#define XK_enfilledsqbullet                            0xae7
+#define XK_filledtribulletup                           0xae8
+#define XK_filledtribulletdown                         0xae9
+#define XK_leftpointer                                 0xaea
+#define XK_rightpointer                                0xaeb
+#define XK_club                                        0xaec
+#define XK_diamond                                     0xaed
+#define XK_heart                                       0xaee
+#define XK_maltesecross                                0xaf0
+#define XK_dagger                                      0xaf1
+#define XK_doubledagger                                0xaf2
+#define XK_checkmark                                   0xaf3
+#define XK_ballotcross                                 0xaf4
+#define XK_musicalsharp                                0xaf5
+#define XK_musicalflat                                 0xaf6
+#define XK_malesymbol                                  0xaf7
+#define XK_femalesymbol                                0xaf8
+#define XK_telephone                                   0xaf9
+#define XK_telephonerecorder                           0xafa
+#define XK_phonographcopyright                         0xafb
+#define XK_caret                                       0xafc
+#define XK_singlelowquotemark                          0xafd
+#define XK_doublelowquotemark                          0xafe
+#define XK_cursor                                      0xaff
+#endif /* XK_PUBLISHING */
+
+/*
+ *  APL
+ *  Byte 3 = b
+ */
+
+#ifdef XK_APL
+#define XK_leftcaret                                   0xba3
+#define XK_rightcaret                                  0xba6
+#define XK_downcaret                                   0xba8
+#define XK_upcaret                                     0xba9
+#define XK_overbar                                     0xbc0
+#define XK_downtack                                    0xbc2
+#define XK_upshoe                                      0xbc3
+#define XK_downstile                                   0xbc4
+#define XK_underbar                                    0xbc6
+#define XK_jot                                         0xbca
+#define XK_quad                                        0xbcc
+#define XK_uptack                                      0xbce
+#define XK_circle                                      0xbcf
+#define XK_upstile                                     0xbd3
+#define XK_downshoe                                    0xbd6
+#define XK_rightshoe                                   0xbd8
+#define XK_leftshoe                                    0xbda
+#define XK_lefttack                                    0xbdc
+#define XK_righttack                                   0xbfc
+#endif /* XK_APL */
+
+/*
+ * Hebrew
+ * Byte 3 = c
+ */
+
+#ifdef XK_HEBREW
+#define XK_hebrew_doublelowline                        0xcdf
+#define XK_hebrew_aleph                                0xce0
+#define XK_hebrew_bet                                  0xce1
+#define XK_hebrew_beth                                 0xce1  /* deprecated */
+#define XK_hebrew_gimel                                0xce2
+#define XK_hebrew_gimmel                               0xce2  /* deprecated */
+#define XK_hebrew_dalet                                0xce3
+#define XK_hebrew_daleth                               0xce3  /* deprecated */
+#define XK_hebrew_he                                   0xce4
+#define XK_hebrew_waw                                  0xce5
+#define XK_hebrew_zain                                 0xce6
+#define XK_hebrew_zayin                                0xce6  /* deprecated */
+#define XK_hebrew_chet                                 0xce7
+#define XK_hebrew_het                                  0xce7  /* deprecated */
+#define XK_hebrew_tet                                  0xce8
+#define XK_hebrew_teth                                 0xce8  /* deprecated */
+#define XK_hebrew_yod                                  0xce9
+#define XK_hebrew_finalkaph                            0xcea
+#define XK_hebrew_kaph                                 0xceb
+#define XK_hebrew_lamed                                0xcec
+#define XK_hebrew_finalmem                             0xced
+#define XK_hebrew_mem                                  0xcee
+#define XK_hebrew_finalnun                             0xcef
+#define XK_hebrew_nun                                  0xcf0
+#define XK_hebrew_samech                               0xcf1
+#define XK_hebrew_samekh                               0xcf1  /* deprecated */
+#define XK_hebrew_ayin                                 0xcf2
+#define XK_hebrew_finalpe                              0xcf3
+#define XK_hebrew_pe                                   0xcf4
+#define XK_hebrew_finalzade                            0xcf5
+#define XK_hebrew_finalzadi                            0xcf5  /* deprecated */
+#define XK_hebrew_zade                                 0xcf6
+#define XK_hebrew_zadi                                 0xcf6  /* deprecated */
+#define XK_hebrew_qoph                                 0xcf7
+#define XK_hebrew_kuf                                  0xcf7  /* deprecated */
+#define XK_hebrew_resh                                 0xcf8
+#define XK_hebrew_shin                                 0xcf9
+#define XK_hebrew_taw                                  0xcfa
+#define XK_hebrew_taf                                  0xcfa  /* deprecated */
+#define XK_Hebrew_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_HEBREW */
+
Index: trunk/kitgen/8.x/blt/win/bltWin.h
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWin.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWin.h	(revision 175)
@@ -0,0 +1,227 @@
+
+/*
+ * bltWin.h --
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#ifndef _BLT_WIN_H
+#define _BLT_WIN_H
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef STRICT
+#undef WIN32_LEAN_AND_MEAN
+#include <windowsx.h>
+
+
+#undef STD_NORMAL_BACKGROUND
+#undef STD_NORMAL_FOREGROUND
+#undef STD_SELECT_BACKGROUND
+#undef STD_SELECT_FOREGROUND
+#undef STD_TEXT_FOREGROUND
+#undef STD_FONT
+#undef STD_FONT_LARGE
+#undef STD_FONT_SMALL
+
+#define STD_NORMAL_BACKGROUND	"SystemButtonFace"
+#define STD_NORMAL_FOREGROUND	"SystemButtonText"
+#define STD_SELECT_BACKGROUND	"SystemHighlight"
+#define STD_SELECT_FOREGROUND	"SystemHighlightText"
+#define STD_TEXT_FOREGROUND	"SystemWindowText"
+#define STD_FONT		"Arial 8"
+#define STD_FONT_LARGE		"Arial 12"
+#define STD_FONT_SMALL		"Arial 6"
+
+#ifdef CHECK_UNICODE_CALLS
+#define _UNICODE
+#define UNICODE
+#define __TCHAR_DEFINED
+typedef float *_TCHAR;
+#define _TCHAR_DEFINED
+typedef float *TCHAR;
+#endif /* CHECK_UNICODE_CALLS */
+
+/* DOS Encapsulated PostScript File Header */
+#pragma pack(2)
+typedef struct {
+    BYTE magic[4];		/* Magic number for a DOS EPS file
+				 * C5,D0,D3,C6 */
+    DWORD psStart;		/* Offset of PostScript section. */
+    DWORD psLength;		/* Length of the PostScript section. */
+    DWORD wmfStart;		/* Offset of Windows Meta File section. */
+    DWORD wmfLength;		/* Length of Meta file section. */
+    DWORD tiffStart;		/* Offset of TIFF section. */
+    DWORD tiffLength;		/* Length of TIFF section. */
+    WORD checksum;		/* Checksum of header. If FFFF, ignore. */
+} DOSEPSHEADER;
+#pragma pack()
+
+/* Aldus Portable Metafile Header */
+#pragma pack(2)
+typedef struct {
+    DWORD key;			/* Type of metafile */
+    WORD hmf;			/* Unused. Must be NULL. */
+    SMALL_RECT bbox;		/* Bounding rectangle */
+    WORD inch;			/* Units per inch. */
+    DWORD reserved;		/* Unused. */
+    WORD checksum;		/* XOR of previous fields (10 32-bit words). */
+} APMHEADER;
+#pragma pack()
+
+extern double hypot(double x, double y);
+extern int Blt_AsyncRead(int fd, char *buffer, unsigned int size);
+extern int Blt_AsyncWrite(int fd, char *buffer, unsigned int size);
+extern void Blt_CreateFileHandler(int fd, int flags, Tcl_FileProc * proc,
+    ClientData clientData);
+extern void Blt_DeleteFileHandler(int fd);
+extern int Blt_GetPlatformId(void);
+extern char *Blt_LastError(void);
+extern int Blt_GetOpenPrinter(Tcl_Interp *interp, const char *id,
+    Drawable *drawablePtr);
+extern int Blt_PrintDialog(Tcl_Interp *interp, Drawable *drawablePtr);
+extern int Blt_OpenPrinterDoc(Tcl_Interp *interp, const char *id);
+extern int Blt_ClosePrinterDoc(Tcl_Interp *interp, const char *id);
+extern void Blt_GetPrinterScale(HDC dc, double *xRatio, double *yRatio);
+extern int Blt_StartPrintJob(Tcl_Interp *interp, Drawable drawable);
+extern int Blt_EndPrintJob(Tcl_Interp *interp, Drawable drawable);
+
+#undef EXPORT
+#define EXPORT __declspec(dllexport)
+
+#ifdef _MSC_VER
+#define strncasecmp(s1,s2,n)	_strnicmp(s1,s2,n)
+#define strcasecmp(s1,s2)	_stricmp(s1,s2)
+#define isnan(x)		_isnan(x)
+#endif /* _MSC_VER */
+
+#ifdef __BORLANDC__
+#define isnan(x)		_isnan(x)
+#endif
+
+#if defined(__BORLANDC__) || defined(_MSC_VER)
+#ifdef FINITE
+#undef FINITE
+#define FINITE(x)		_finite(x)
+#endif
+#endif /* __BORLANDC__ || _MSC_VER */
+
+#ifdef __GNUC__ 
+#include <wingdi.h>
+#include <windowsx.h>
+#undef Status
+#include <winspool.h>
+#define Status int
+/*
+ * Add definitions missing from windgi.h, windowsx.h, and winspool.h
+ */
+#include "missing.h" 
+#endif /* __GNUC__ */
+
+#define XCopyArea		Blt_EmulateXCopyArea
+#define XCopyPlane		Blt_EmulateXCopyPlane
+#define XDrawArcs		Blt_EmulateXDrawArcs
+#define XDrawLine		Blt_EmulateXDrawLine
+#define XDrawLines		Blt_EmulateXDrawLines
+#define XDrawPoints		Blt_EmulateXDrawPoints
+#define XDrawRectangle		Blt_EmulateXDrawRectangle
+#define XDrawRectangles		Blt_EmulateXDrawRectangles
+#define XDrawSegments		Blt_EmulateXDrawSegments
+#define XDrawString		Blt_EmulateXDrawString
+#define XFillArcs		Blt_EmulateXFillArcs
+#define XFillPolygon		Blt_EmulateXFillPolygon
+#define XFillRectangle		Blt_EmulateXFillRectangle
+#define XFillRectangles		Blt_EmulateXFillRectangles
+#define XFree			Blt_EmulateXFree
+#define XGetWindowAttributes	Blt_EmulateXGetWindowAttributes
+#define XLowerWindow		Blt_EmulateXLowerWindow
+#define XMaxRequestSize		Blt_EmulateXMaxRequestSize
+#define XRaiseWindow		Blt_EmulateXRaiseWindow
+#define XReparentWindow		Blt_EmulateXReparentWindow
+#define XSetDashes		Blt_EmulateXSetDashes
+#define XUnmapWindow		Blt_EmulateXUnmapWindow
+#define XWarpPointer		Blt_EmulateXWarpPointer
+
+EXTERN GC Blt_EmulateXCreateGC(Display *display, Drawable drawable,
+    unsigned long mask, XGCValues *valuesPtr);
+EXTERN void Blt_EmulateXCopyArea(Display *display, Drawable src, Drawable dest,
+    GC gc, int src_x, int src_y, unsigned int width, unsigned int height,
+    int dest_x, int dest_y);
+EXTERN void Blt_EmulateXCopyPlane(Display *display, Drawable src,
+    Drawable dest, GC gc, int src_x, int src_y, unsigned int width,
+    unsigned int height, int dest_x, int dest_y, unsigned long plane);
+EXTERN void Blt_EmulateXDrawArcs(Display *display, Drawable drawable, GC gc,
+    XArc *arcArr, int nArcs);
+EXTERN void Blt_EmulateXDrawLine(Display *display, Drawable drawable, GC gc,
+    int x1, int y1, int x2, int y2);
+EXTERN void Blt_EmulateXDrawLines(Display *display, Drawable drawable, GC gc,
+    XPoint *pointArr, int nPoints, int mode);
+EXTERN void Blt_EmulateXDrawPoints(Display *display, Drawable drawable, GC gc,
+    XPoint *pointArr, int nPoints, int mode);
+EXTERN void Blt_EmulateXDrawRectangle(Display *display, Drawable drawable,
+    GC gc, int x, int y, unsigned int width, unsigned int height);
+EXTERN void Blt_EmulateXDrawRectangles(Display *display, Drawable drawable,
+    GC gc, XRectangle *rectArr, int nRects);
+EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable,
+    GC gc, XSegment *segArr, int nSegments);
+EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable,
+    GC gc, XSegment *segArr, int nSegments);
+EXTERN void Blt_EmulateXDrawString(Display *display, Drawable drawable, GC gc,
+    int x, int y, _Xconst char *string, int length);
+EXTERN void Blt_EmulateXFillArcs(Display *display, Drawable drawable, GC gc,
+    XArc *arcArr, int nArcs);
+EXTERN void Blt_EmulateXFillPolygon(Display *display, Drawable drawable,
+    GC gc, XPoint *points, int nPoints,  int shape, int mode);
+EXTERN void Blt_EmulateXFillRectangle(Display *display, Drawable drawable,
+    GC gc, int x, int y, unsigned int width, unsigned int height);
+EXTERN void Blt_EmulateXFillRectangles(Display *display, Drawable drawable,
+    GC gc, XRectangle *rectArr, int nRects);
+EXTERN void Blt_EmulateXFree(void *ptr);
+EXTERN int Blt_EmulateXGetWindowAttributes(Display *display, Window window,
+    XWindowAttributes * attrsPtr);
+EXTERN void Blt_EmulateXLowerWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXMapWindow(Display *display, Window window);
+EXTERN long Blt_EmulateXMaxRequestSize(Display *display);
+EXTERN void Blt_EmulateXRaiseWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXReparentWindow(Display *display, Window window,
+    Window parent, int x, int y);
+EXTERN void Blt_EmulateXSetDashes(Display *display, GC gc, int dashOffset,
+    _Xconst char *dashList, int n);
+EXTERN void Blt_EmulateXUnmapWindow(Display *display, Window window);
+EXTERN void Blt_EmulateXWarpPointer(Display *display, Window srcWindow,
+    Window destWindow, int srcX, int srcY, unsigned int srcWidth,
+    unsigned int srcHeight, int destX, int destY);
+
+EXTERN void Blt_DrawLine2D(Display *display, Drawable drawable, GC gc,
+    POINT *screenPts, int nScreenPts);
+
+extern unsigned char *Blt_GetBitmapData _ANSI_ARGS_((Display *display,
+	Pixmap bitmap, int width, int height, int *pitchPtr));
+
+extern HFONT Blt_CreateRotatedFont _ANSI_ARGS_((Tk_Window tkwin,
+	unsigned long font, double theta));
+
+extern HPALETTE Blt_GetSystemPalette _ANSI_ARGS_((void));
+
+extern HPEN Blt_GCToPen _ANSI_ARGS_((HDC dc, GC gc));
+
+#endif /*_BLT_WIN_H*/
Index: trunk/kitgen/8.x/blt/win/bltWinConfig.h
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWinConfig.h	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWinConfig.h	(revision 175)
@@ -0,0 +1,140 @@
+/* src/bltConfig.h.  Generated automatically by configure.  */
+/* src/bltConfig.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define pid_t int
+#endif /* _MSC_VER || __BORLANDC__ */
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS	1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#undef WORDS_BIGENDIAN	
+
+
+
+/* Define if DBL_EPSILON is not defined in float.h */
+#undef BLT_DBL_EPSILON
+
+/* Define if drand48 isn't declared in math.h. */
+#define NEED_DECL_DRAND48	1
+
+/* Define if srand48 isn't declared in math.h. */
+#define NEED_DECL_SRAND48	1
+
+/* Define if strdup isn't declared in a standard header file. */
+#undef NEED_DECL_STRDUP
+
+/* Define if j1 isn't declared in a standard header file. */
+#define NEED_DECL_J1		1
+
+/* Define if union wait type is defined incorrectly.  */
+#undef HAVE_UNION_WAIT
+
+/* Define if isfinite is found in libm.  */
+#undef HAVE_ISFINITE
+
+/* The number of bytes in a long.  */
+#define SIZEOF_LONG		4
+
+/* The number of bytes in a long long.  */
+#define SIZEOF_LONG_LONG	8
+
+/* The number of bytes in a void *.  */
+#define SIZEOF_VOID_P		4
+
+/* Define if you have the XExtendedMaxRequestSize function.  */
+#undef HAVE_XEXTENDEDMAXREQUESTSIZE
+
+/* Define if you have the drand48 function.  */
+#define HAVE_DRAND48		1
+
+/* Define if you have the finite function.  */
+#undef HAVE_FINITE
+
+/* Define if you have the srand48 function.  */
+#define HAVE_SRAND48		1
+
+/* Define if you have the strdup function.  */
+#define HAVE_STRDUP		1
+
+#ifndef __BORLANDC__
+/* Define if you have the strcasecmp function.  */
+#define HAVE_STRCASECMP		1
+
+/* Define if you have the strncasecmp function.  */
+#define HAVE_STRNCASECMP		1
+#endif
+
+/* Define if you have the <ctype.h> header file.  */
+#define HAVE_CTYPE_H		1
+
+/* Define if you have the <errno.h> header file.  */
+#define HAVE_ERRNO_H		1
+
+/* Define if you have the <float.h> header file.  */
+#define HAVE_FLOAT_H		1
+
+/* Define if you have the <ieeefp.h> header file.  */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <jpeglib.h> header file.  */
+/* Defined in Makefile */
+/* #undef HAVE_JPEGLIB_H */
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H		1
+
+/* Define if you have the <malloc.h> header file.  */
+#define HAVE_MALLOC_H		1
+
+/* Define if you have the <math.h> header file.  */
+#define HAVE_MATH_H		1
+
+/* Define if you have the <memory.h> header file.  */
+#define HAVE_MEMORY_H		1
+
+/* Define if you have the <setjmp.h> header file.  */
+#define HAVE_SETJMP_H		1
+
+/* Define if you have the <stdlib.h> header file.  */
+#define HAVE_STDLIB_H		1
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H		1
+
+/* Define if you have the <sys/param.h> header file.  */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H		
+
+/* Define if you have the <sys/wait.h> header file.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <waitflags.h> header file.  */
+#undef HAVE_WAITFLAGS_H
+
+/* Define if you have the m library (-lm).  */
+#define HAVE_LIBM		1
+
+/* Define if you have the nsl library (-lnsl).  */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket).  */
+#undef HAVE_LIBSOCKET
+
Index: trunk/kitgen/8.x/blt/win/bltWinDraw.c
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWinDraw.c	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWinDraw.c	(revision 175)
@@ -0,0 +1,2694 @@
+/*
+ * bltWinDraw.c --
+ *
+ *	This module contains WIN32 routines not included in the Tcl/Tk
+ *	libraries.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include <X11/Xutil.h>
+#include <X11/Xlib.h>
+
+#define WINDEBUG 0
+
+/*
+ * Data structure for setting graphics context.
+ */
+typedef struct {
+    int function;		/* logical operation */
+    unsigned long plane_mask;	/* plane mask */
+    unsigned long foreground;	/* foreground pixel */
+    unsigned long background;	/* background pixel */
+    int line_width;		/* line width */
+    int line_style;		/* LineSolid, LineOnOffDash, LineDoubleDash */
+    int cap_style;		/* CapNotLast, CapButt,
+				   CapRound, CapProjecting */
+    int join_style;		/* JoinMiter, JoinRound, JoinBevel */
+    int fill_style;		/* FillSolid, FillTiled,
+				   FillStippled, FillOpaeueStippled */
+    int fill_rule;		/* EvenOddRule, WindingRule */
+    int arc_mode;		/* ArcChord, ArcPieSlice */
+    Pixmap tile;		/* tile pixmap for tiling operations */
+    Pixmap stipple;		/* stipple 1 plane pixmap for stipping */
+    int ts_x_origin;		/* offset for tile or stipple operations */
+    int ts_y_origin;
+    Font font;			/* default text font for text operations */
+    int subwindow_mode;		/* ClipByChildren, IncludeInferiors */
+    Bool graphics_exposures;	/* boolean, should exposures be generated */
+    int clip_x_origin;		/* origin for clipping */
+    int clip_y_origin;
+    Pixmap clip_mask;		/* bitmap clipping; other calls for rects */
+    int dash_offset;		/* patterned/dashed line information */
+    char dashes;		/* If -1, indicates that the extended
+				 * information below is available. */
+    int nDashValues;
+    char dashValues[12];
+} XGCValuesEx;
+
+static int tkpWinRopModes[] =
+{
+    R2_BLACK,			/* GXclear */
+    R2_MASKPEN,			/* GXand */
+    R2_MASKPENNOT,		/* GXandReverse */
+    R2_COPYPEN,			/* GXcopy */
+    R2_MASKNOTPEN,		/* GXandInverted */
+    R2_NOT,			/* GXnoop */
+    R2_XORPEN,			/* GXxor */
+    R2_MERGEPEN,		/* GXor */
+    R2_NOTMERGEPEN,		/* GXnor */
+    R2_NOTXORPEN,		/* GXequiv */
+    R2_NOT,			/* GXinvert */
+    R2_MERGEPENNOT,		/* GXorReverse */
+    R2_NOTCOPYPEN,		/* GXcopyInverted */
+    R2_MERGENOTPEN,		/* GXorInverted */
+    R2_NOTMASKPEN,		/* GXnand */
+    R2_WHITE			/* GXset */
+};
+
+#define MASKPAT		0x00E20746 /* dest = (src & pat) | (!src & dst) */
+#define COPYFG		0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG		0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+/*
+ * Translation table between X gc functions and Win32 BitBlt op modes.  Some
+ * of the operations defined in X don't have names, so we have to construct
+ * new opcodes for those functions.  This is arcane and probably not all that
+ * useful, but at least it's accurate.
+ */
+
+#define NOTSRCAND	(DWORD)0x00220326 /* dest = (NOT src) AND dest */
+#define NOTSRCINVERT	(DWORD)0x00990066 /* dest = (NOT src) XOR dest */
+#define SRCORREVERSE	(DWORD)0x00DD0228 /* dest = src OR (NOT dest) */
+#define SRCNAND		(DWORD)0x007700E6 /* dest = NOT (src AND dest) */
+
+static int bltModes[] =
+{
+    BLACKNESS,			/* GXclear */
+    SRCAND,			/* GXand */
+    SRCERASE,			/* GXandReverse */
+    SRCCOPY,			/* GXcopy */
+    NOTSRCAND,			/* GXandInverted */
+    PATCOPY,			/* GXnoop */
+    SRCINVERT,			/* GXxor */
+    SRCPAINT,			/* GXor */
+    NOTSRCERASE,		/* GXnor */
+    NOTSRCINVERT,		/* GXequiv */
+    DSTINVERT,			/* GXinvert */
+    SRCORREVERSE,		/* GXorReverse */
+    NOTSRCCOPY,			/* GXcopyInverted */
+    MERGEPAINT,			/* GXorInverted */
+    SRCNAND,			/* GXnand */
+    WHITENESS			/* GXset */
+};
+
+#if (TCL_VERSION_NUMBER <  _VERSION(8,1,0)) 
+typedef void *Tcl_Encoding;	/* Make up dummy type for encoding.  */
+#else 
+static Tcl_Encoding systemEncoding = NULL;
+#endif
+
+HPALETTE
+Blt_GetSystemPalette(void)
+{
+    HDC hDC;
+    HPALETTE hPalette;
+    DWORD flags;
+
+    hPalette = NULL;
+    hDC = GetDC(NULL);		/* Get the desktop device context */
+    flags = GetDeviceCaps(hDC, RASTERCAPS);
+    if (flags & RC_PALETTE) {
+	LOGPALETTE *palettePtr;
+
+	palettePtr = (LOGPALETTE *)
+	    GlobalAlloc(GPTR, sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY));
+	palettePtr->palVersion = 0x300;
+	palettePtr->palNumEntries = 256;
+	GetSystemPaletteEntries(hDC, 0, 256, palettePtr->palPalEntry);
+	hPalette = CreatePalette(palettePtr);
+	GlobalFree(palettePtr);
+    }
+    ReleaseDC(NULL, hDC);
+    return hPalette;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateRotatedFont --
+ *
+ *	Creates a rotated copy of the given font.  This only works 
+ *	for TrueType fonts.
+ *
+ * Results:
+ *	Returns the newly create font or NULL if the font could not
+ *	be created.
+ *
+ *----------------------------------------------------------------------
+ */
+HFONT
+CreateRotatedFont(
+    unsigned long fontId,	/* Font identifier (actually a Tk_Font) */
+    double theta)
+{				/* Number of degrees to rotate font */
+    TkFontAttributes *faPtr;	/* Set of attributes to match. */
+    TkFont *fontPtr;
+    HFONT hFont;
+    LOGFONTW lf;
+
+    fontPtr = (TkFont *) fontId;
+    faPtr = &fontPtr->fa;
+    ZeroMemory(&lf, sizeof(LOGFONT));
+    lf.lfHeight = -faPtr->pointsize;
+    if (lf.lfHeight < 0) {
+	HDC dc;
+
+	dc = GetDC(NULL);
+	lf.lfHeight = -MulDiv(faPtr->pointsize,
+	    GetDeviceCaps(dc, LOGPIXELSY), 72);
+	ReleaseDC(NULL, dc);
+    }
+    lf.lfWidth = 0;
+    lf.lfEscapement = lf.lfOrientation = ROUND(theta * 10.0);
+#define TK_FW_NORMAL	0
+    lf.lfWeight = (faPtr->weight == TK_FW_NORMAL) ? FW_NORMAL : FW_BOLD;
+    lf.lfItalic = faPtr->slant;
+    lf.lfUnderline = faPtr->underline;
+    lf.lfStrikeOut = faPtr->overstrike;
+    lf.lfCharSet = DEFAULT_CHARSET;
+    lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
+    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    lf.lfQuality = DEFAULT_QUALITY;
+    lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+    hFont = NULL;
+    if (faPtr->family == NULL) {
+	lf.lfFaceName[0] = '\0';
+    } else {
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0)) 
+	Tcl_DString dString;
+
+	Tcl_UtfToExternalDString(systemEncoding, faPtr->family, -1, &dString);
+
+	if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	    Tcl_UniChar *src, *dst;
+	    
+	    /*
+	     * We can only store up to LF_FACESIZE wide characters
+	     */
+	    if (Tcl_DStringLength(&dString) >= (LF_FACESIZE * sizeof(WCHAR))) {
+		Tcl_DStringSetLength(&dString, LF_FACESIZE);
+	    }
+	    src = (Tcl_UniChar *)Tcl_DStringValue(&dString);
+	    dst = (Tcl_UniChar *)lf.lfFaceName;
+	    while (*src != '\0') {
+		*dst++ = *src++;
+	    }
+	    *dst = '\0';
+	    hFont = CreateFontIndirectW((LOGFONTW *)&lf);
+	} else {
+	    /*
+	     * We can only store up to LF_FACESIZE characters
+	     */
+	    if (Tcl_DStringLength(&dString) >= LF_FACESIZE) {
+		Tcl_DStringSetLength(&dString, LF_FACESIZE);
+	    }
+	    strcpy((char *)lf.lfFaceName, Tcl_DStringValue(&dString));
+	    hFont = CreateFontIndirectA((LOGFONTA *)&lf);
+	}
+	Tcl_DStringFree(&dString);
+#else
+	strncpy((char *)lf.lfFaceName, faPtr->family, LF_FACESIZE - 1);
+	lf.lfFaceName[LF_FACESIZE] = '\0';
+#endif /* TCL_VERSION_NUMBER >= 8.1.0 */
+    }
+
+    if (hFont == NULL) {
+#if WINDEBUG
+	PurifyPrintf("can't create font: %s\n", Blt_LastError());
+#endif
+    } else { 
+	HFONT oldFont;
+	TEXTMETRIC tm;
+	HDC hRefDC;
+	int result;
+
+	/* Check if the rotated font is really a TrueType font. */
+
+	hRefDC = GetDC(NULL);		/* Get the desktop device context */
+	oldFont = SelectFont(hRefDC, hFont);
+	result = ((GetTextMetrics(hRefDC, &tm)) && 
+		  (tm.tmPitchAndFamily & TMPF_TRUETYPE));
+	SelectFont(hRefDC, oldFont);
+	ReleaseDC(NULL, hRefDC);
+	if (!result) {
+#if WINDEBUG
+	    PurifyPrintf("not a true type font\n");
+#endif
+	    DeleteFont(hFont);
+	    return NULL;
+	}
+    }
+    return hFont;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetBitmapData --
+ *
+ *	Returns the DIB bits from a bitmap.
+ *
+ * Results:
+ *	Returns a byte array of bitmap data or NULL if an error
+ *	occurred.  The parameter pitchPtr returns the number
+ *	of bytes per row.
+ *
+ *----------------------------------------------------------------------
+ */
+unsigned char *
+Blt_GetBitmapData(
+    Display *display,		/* Display of bitmap */
+    Pixmap bitmap,		/* Bitmap to query */
+    int width,			/* Width of bitmap */
+    int height,			/* Height of bitmap */
+    int *pitchPtr)		/* (out) Number of bytes per row */
+{			
+    TkWinDCState state;
+    HDC dc;
+    int result;
+    unsigned char *bits;
+    unsigned int size;
+    HBITMAP hBitmap;
+    BITMAPINFOHEADER *bmiPtr;
+    HANDLE hMem, hMem2;
+    int bytesPerRow, imageSize;
+
+    size = sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);
+    hMem = GlobalAlloc(GHND, size);
+    bmiPtr = (BITMAPINFOHEADER *)GlobalLock(hMem);
+    bmiPtr->biSize = sizeof(BITMAPINFOHEADER);
+    bmiPtr->biPlanes = 1;
+    bmiPtr->biBitCount = 1;
+    bmiPtr->biCompression = BI_RGB;
+    bmiPtr->biWidth = width;
+    bmiPtr->biHeight = height;
+
+    hBitmap = ((TkWinDrawable *)bitmap)->bitmap.handle;
+    dc = TkWinGetDrawableDC(display, bitmap, &state);
+    result = GetDIBits(dc, hBitmap, 0, height, (LPVOID)NULL, 
+	(BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
+    TkWinReleaseDrawableDC(bitmap, dc, &state);
+    if (!result) {
+	GlobalUnlock(hMem);
+	GlobalFree(hMem);
+	return NULL;
+    }
+    imageSize = bmiPtr->biSizeImage;
+    GlobalUnlock(hMem);
+    bytesPerRow = ((width + 31) & ~31) / 8;
+    if (imageSize == 0) {
+         imageSize = bytesPerRow * height;
+    }	
+    hMem2 = GlobalReAlloc(hMem, size + imageSize, 0);
+    if (hMem2 == NULL) {
+	GlobalFree(hMem);
+        return NULL;
+    }
+    hMem = hMem2;
+    bmiPtr = (LPBITMAPINFOHEADER)GlobalLock(hMem);
+    dc = TkWinGetDrawableDC(display, bitmap, &state);
+    result = GetDIBits(dc, hBitmap, 0, height, (unsigned char *)bmiPtr + size, 
+        (BITMAPINFO *)bmiPtr, DIB_RGB_COLORS);
+    TkWinReleaseDrawableDC(bitmap, dc, &state);
+    bits = NULL;
+    if (!result) {
+	OutputDebugString("GetDIBits failed\n");
+    } else {
+	bits = Blt_Malloc(imageSize);
+	if (bits != NULL) {
+	    memcpy (bits, (unsigned char *)bmiPtr + size, imageSize);
+	}
+    }
+    *pitchPtr = bytesPerRow;
+    GlobalUnlock(hMem);
+    GlobalFree(hMem);
+    return bits;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFree --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXFree(void *ptr)
+{
+    Blt_Free(ptr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XMaxRequestSize --
+ *
+ *----------------------------------------------------------------------
+ */
+long
+Blt_EmulateXMaxRequestSize(Display *display)
+{
+    return (SHRT_MAX / 4);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XLowerWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXLowerWindow(
+    Display *display, 
+    Window window)
+{
+    HWND hWnd;
+
+    hWnd = Tk_GetHWND(window);
+    display->request++;
+    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XRaiseWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXRaiseWindow(
+    Display *display, 
+    Window window)
+{
+    HWND hWnd;
+
+    hWnd = Tk_GetHWND(window);
+    display->request++;
+    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XUnmapWindow --
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXUnmapWindow(
+    Display *display, 
+    Window window)
+{
+    HWND hWnd;
+
+    hWnd = Tk_GetHWND(window);
+    display->request++;
+    ShowWindow(hWnd, SW_HIDE);
+    /* SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XWarpPointer --
+ *
+ *	If destWindow is None, moves the pointer by the offsets (destX,
+ *	destY) relative to the current position of the pointer.
+ *	If destWindow is a window, moves the pointer to the offsets
+ *	(destX, destY) relative to the origin of destWindow.  However,
+ *	if srcWindow is a window, the move only takes place if the window
+ *	srcWindow contains the pointer and if the specified rectangle of
+ *	srcWindow contains the pointer.
+ *
+ *	The srcX and srcY coordinates are relative to the origin of
+ *	srcWindow.  If srcHeight is zero, it is replaced with the current
+ *	height of srcWindow minus srcY.  If srcWidth is zero, it is
+ *	replaced with the current width of srcWindow minus srcX.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXWarpPointer(
+    Display *display,
+    Window srcWindow,
+    Window destWindow,
+    int srcX,
+    int srcY,
+    unsigned int srcWidth,
+    unsigned int srcHeight,
+    int destX,
+    int destY)
+{
+    HWND hWnd;
+    POINT point;
+
+    hWnd = Tk_GetHWND(destWindow);
+    point.x = destX, point.y = destY;
+    if (ClientToScreen(hWnd, &point)) {
+	SetCursorPos(point.x, point.y);
+    }
+}
+
+#ifdef notdef
+static Blt_HashTable gcTable;
+static int gcInitialized = FALSE;
+#endif
+
+typedef struct {
+    HDC dc;
+    int count;
+    COLORREF color;
+    int offset, nBits;
+} DashInfo;
+
+void
+Blt_SetDashes(Display *display, GC gc, Blt_Dashes *dashesPtr)
+{
+    XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+    /* This must be used only with a privately created GC */
+    assert((int)gcPtr->dashes == -1);
+    gcPtr->nDashValues = strlen(dashesPtr->values);
+    gcPtr->dash_offset = dashesPtr->offset;
+    strcpy(gcPtr->dashValues, dashesPtr->values);
+}
+
+static int
+GetDashInfo(
+    HDC dc, 
+    GC gc, 
+    DashInfo *infoPtr)
+{
+    int dashOffset, dashValue;
+
+    dashValue = 0;
+    dashOffset = gc->dash_offset;
+    if ((int)gc->dashes == -1) {
+	XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+	if (gcPtr->nDashValues == 1) {
+	    dashValue = gcPtr->dashValues[0];
+	}
+    } else if (gc->dashes > 0) {
+	dashValue = (int)gc->dashes;
+    }
+    if (dashValue == 0) {
+	return FALSE;
+    }
+    infoPtr->dc = dc;
+    infoPtr->nBits = dashValue;
+    infoPtr->offset = dashOffset;
+    infoPtr->count = 0;
+    infoPtr->color = gc->foreground;
+    return TRUE;
+}
+
+void
+Blt_SetROP2(HDC dc, int function)
+{
+    SetROP2(dc, tkpWinRopModes[function]);
+}
+
+static XGCValuesEx *
+CreateGC()
+{
+    XGCValuesEx *gcPtr;
+
+    gcPtr = Blt_Malloc(sizeof(XGCValuesEx));
+    if (gcPtr == NULL) {
+	return NULL;
+    }
+    gcPtr->arc_mode = ArcPieSlice;
+    gcPtr->background = 0xffffff;
+    gcPtr->cap_style = CapNotLast;
+    gcPtr->clip_mask = None;
+    gcPtr->clip_x_origin = gcPtr->clip_y_origin = 0;
+    gcPtr->dash_offset	= 0;
+    gcPtr->fill_rule = WindingRule;
+    gcPtr->fill_style = FillSolid;
+    gcPtr->font = None;
+    gcPtr->foreground = 0;
+    gcPtr->function = GXcopy;
+    gcPtr->graphics_exposures = True;
+    gcPtr->join_style = JoinMiter;
+    gcPtr->line_style = LineSolid;
+    gcPtr->line_width = 0;
+    gcPtr->plane_mask = ~0;
+    gcPtr->stipple = None;
+    gcPtr->subwindow_mode = ClipByChildren;
+    gcPtr->tile = None;
+    gcPtr->ts_x_origin = gcPtr->ts_y_origin = 0;
+
+    gcPtr->dashes = -1;    /* Mark that this an extended GC */
+    gcPtr->nDashValues	= 0;
+
+    return gcPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCreateGC --
+ *
+ *	Allocate a new extended GC, and initialize the specified fields.
+ *
+ * Results:
+ *	Returns a newly allocated GC.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_EmulateXCreateGC(
+    Display *display,
+    Drawable drawable,
+    unsigned long mask,
+    XGCValues *srcPtr)
+{
+    XGCValuesEx *destPtr;
+
+    destPtr = CreateGC();
+    if (destPtr == NULL) {
+	return None;
+    }
+    if (mask & GCFunction) {
+        destPtr->function = srcPtr->function;
+    }
+    if (mask & GCPlaneMask) {
+	destPtr->plane_mask = srcPtr->plane_mask;
+    }
+    if (mask & GCForeground) {
+	destPtr->foreground = srcPtr->foreground;
+    }
+    if (mask & GCBackground) {
+        destPtr->background = srcPtr->background;
+    }
+    if (mask & GCLineWidth) {
+	destPtr->line_width = srcPtr->line_width;
+    }
+    if (mask & GCLineStyle) {
+	destPtr->line_style = srcPtr->line_style;
+    }
+    if (mask & GCCapStyle) {
+	destPtr->cap_style = srcPtr->cap_style;
+    }
+    if (mask & GCJoinStyle) {
+	destPtr->join_style = srcPtr->join_style;
+    }
+    if (mask & GCFillStyle) {
+	destPtr->fill_style = srcPtr->fill_style;
+    }
+    if (mask & GCFillRule) {
+        destPtr->fill_rule = srcPtr->fill_rule;
+    }
+    if (mask & GCArcMode) {
+        destPtr->arc_mode = srcPtr->arc_mode;
+    }
+    if (mask & GCTile) {
+	destPtr->tile = srcPtr->tile;
+    }
+    if (mask & GCStipple) {
+        destPtr->stipple = srcPtr->stipple;
+    }
+    if (mask & GCTileStipXOrigin) {
+	destPtr->ts_x_origin = srcPtr->ts_x_origin;
+    }
+    if (mask & GCTileStipXOrigin) {
+	destPtr->ts_y_origin = srcPtr->ts_y_origin;
+    }
+    if (mask & GCFont) {
+        destPtr->font = srcPtr->font;
+    }
+    if (mask & GCSubwindowMode) {
+	destPtr->subwindow_mode = srcPtr->subwindow_mode;
+    }
+    if (mask & GCGraphicsExposures) {
+	destPtr->graphics_exposures = srcPtr->graphics_exposures;
+    }
+    if (mask & GCClipXOrigin) {
+	destPtr->clip_x_origin = srcPtr->clip_x_origin;
+    }
+    if (mask & GCClipYOrigin) {
+	destPtr->clip_y_origin = srcPtr->clip_y_origin;
+    }
+    if (mask & GCDashOffset) {
+	destPtr->dash_offset = srcPtr->dash_offset;
+    }
+    if (mask & GCDashList) {
+        destPtr->dashes = srcPtr->dashes;
+    }
+    if (mask & GCClipMask) {
+	struct ClipMask {
+	    int type;		/* TKP_CLIP_PIXMAP or TKP_CLIP_REGION */
+	    Pixmap pixmap;
+	} *clipPtr;
+
+	clipPtr = Blt_Malloc(sizeof(struct ClipMask));
+#define TKP_CLIP_PIXMAP 0
+	clipPtr->type = TKP_CLIP_PIXMAP;
+	clipPtr->pixmap = srcPtr->clip_mask;
+	destPtr->clip_mask = (Pixmap) clipPtr;
+    }
+    return (GC)destPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GCToPen --
+ *
+ *	Set up the graphics port from the given GC.
+ *
+ *	Geometric and cosmetic pens available under both 95 and NT.  
+ *	Geometric pens differ from cosmetic pens in that they can 
+ *	  1. Draw in world units (can have thick lines: line width > 1).
+ *	  2. Under NT, allow arbitrary line style.
+ *	  3. Can have caps and join (needed for thick lines).
+ *	  4. Draw very, very slowly.
+ *
+ *	Cosmetic pens are single line width only.
+ *
+ * 			 95	 98	 NT
+ *	  PS_SOLID	c,g	c,g	c,g
+ *	  PS_DASH		c,g	c,g	c,g
+ *	  PS_DOT		  c	  c	c,g
+ *	  PS_DASHDOT	  c	  - 	c,g
+ *	  PS_DASHDOTDOT	  c	  -	c,g
+ *	  PS_USERSTYLE	  -       -	c,g
+ *	  PS_ALTERNATE	  -	  - 	  c
+ *
+ *	Geometric only for 95/98
+ *
+ *	  PS_ENDCAP_ROUND
+ *	  PS_ENDCAP_SQUARE
+ *	  PS_ENDCAP_FLAT
+ *	  PS_JOIN_BEVEL
+ *	  PS_JOIN_ROUND
+ *	  PS_JOIN_MITER
+ * 
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The current port is adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+HPEN
+Blt_GCToPen(HDC dc, GC gc)
+{
+    DWORD lineAttrs, lineStyle;
+    DWORD dashArr[12];
+    DWORD *dashPtr;
+    int nValues, lineWidth;
+    LOGBRUSH lBrush;
+    HPEN pen;
+
+    nValues = 0;
+    lineWidth = (gc->line_width < 1) ? 1 : gc->line_width;
+    if ((gc->line_style == LineOnOffDash) ||
+	(gc->line_style == LineDoubleDash)) {
+	XGCValuesEx *gcPtr = (XGCValuesEx *)gc;
+
+	if ((int)gc->dashes == -1) {
+	    register int i;
+
+	    nValues = strlen(gcPtr->dashValues);
+	    for (i = 0; i < nValues; i++) {
+		dashArr[i] = (DWORD)gcPtr->dashValues[i];
+	    }
+	    if (nValues == 1) {
+		dashArr[1] = dashArr[0];
+		nValues = 2;
+	    }
+	} else {
+	    dashArr[1] = dashArr[0] = (DWORD) gc->dashes;
+	    nValues = 2;
+	    gc->dashes = -1;
+	}
+    }
+
+    switch (nValues) {
+    case 0:
+	lineStyle = PS_SOLID;
+	break;
+    case 3:
+	lineStyle = PS_DASHDOT;
+	break;
+    case 4:
+	lineStyle = PS_DASHDOTDOT;
+	break;
+    case 2:
+    default:
+	/* PS_DASH style dash length is too long. */
+	lineStyle = PS_DOT;
+	break;
+    }
+
+    lBrush.lbStyle = BS_SOLID;
+    lBrush.lbColor = gc->foreground;
+    lBrush.lbHatch = 0;		/* Value is ignored when style is BS_SOLID. */
+
+    lineAttrs = 0;
+    switch (gc->cap_style) {
+    case CapNotLast:
+    case CapButt:
+	lineAttrs |= PS_ENDCAP_FLAT;
+	break;
+    case CapRound:
+	lineAttrs |= PS_ENDCAP_ROUND;
+	break;
+    default:
+	lineAttrs |= PS_ENDCAP_SQUARE;
+	break;
+    }
+    switch (gc->join_style) {
+    case JoinMiter:
+	lineAttrs |= PS_JOIN_MITER;
+	break;
+    case JoinBevel:
+	lineAttrs |= PS_JOIN_BEVEL;
+	break;
+    case JoinRound:
+    default:
+	lineAttrs |= PS_JOIN_ROUND;
+	break;
+    }
+    SetBkMode(dc, TRANSPARENT);
+
+    if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	/* Windows NT/2000/XP. */
+	if (nValues > 0) {
+	    lineStyle = PS_USERSTYLE;
+	    dashPtr = dashArr;
+	} else {
+	    dashPtr = NULL;
+	}
+	if (lineWidth > 1) {
+	    /* Limit the use of geometric pens to thick lines. */
+	    pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
+		       &lBrush, nValues, dashPtr);
+	} else {
+	    /* Cosmetic pens are much faster. */
+	    pen = ExtCreatePen(PS_COSMETIC | lineAttrs | lineStyle, 1, &lBrush,
+		       nValues, dashPtr);
+	}	    
+    } else {
+	/* Windows 95/98. */
+	if ((lineStyle == PS_SOLID) && (lineWidth > 1)) {
+	    /* Use geometric pens with solid, thick lines only. */
+	    pen = ExtCreatePen(PS_GEOMETRIC | lineAttrs | lineStyle, lineWidth,
+		       &lBrush, 0, NULL);
+	} else {
+	    /* Otherwise sacrifice thick lines for dashes. */
+	    pen = ExtCreatePen(PS_COSMETIC | lineStyle, 1, &lBrush, 0, NULL);
+	}
+    } 
+    assert(pen != NULL);
+    return pen;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawRectangles --
+ *
+ *       Draws the outlines of the specified rectangles as if a
+ *       five-point PolyLine protocol request were specified for each
+ *       rectangle:
+ *
+ *             [x,y] [x+width,y] [x+width,y+height] [x,y+height]
+ *             [x,y]
+ *
+ *      For the specified rectangles, these functions do not draw a
+ *      pixel more than once.  XDrawRectangles draws the rectangles in
+ *      the order listed in the array.  If rectangles intersect, the
+ *      intersecting pixels are drawn multiple times.  Draws a
+ *      rectangle.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws rectangles on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawRectangles(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XRectangle *rectArr,
+    int nRects)
+{
+    HPEN pen, oldPen;
+    TkWinDCState state;
+    HBRUSH brush, oldBrush;
+    HDC dc;
+    register XRectangle *rectPtr;
+    register int i;
+
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    pen = Blt_GCToPen(dc, gc);
+    brush = GetStockObject(NULL_BRUSH);
+    oldPen = SelectPen(dc, pen);
+    oldBrush = SelectBrush(dc, brush);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    rectPtr = rectArr;
+    for (i = 0; i < nRects; i++, rectPtr++) {
+	Rectangle(dc, (int)rectPtr->x, (int)rectPtr->y,
+	    (int)(rectPtr->x + rectPtr->width + 1),
+	    (int)(rectPtr->y + rectPtr->height + 1));
+    }
+    DeletePen(SelectPen(dc, oldPen));
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#ifdef notdef
+/*
+ * Implements the "pixeling" of small arcs, because GDI-performance
+ * for this is awful
+ * was made especially for BLT, graph4 demo now runs 4x faster
+ *
+ */
+/* O-outer , I-inner, B-both */
+#define NEITHER_ 0
+#define OUTLINE 1
+#define FILL 2
+#define BOTH (OUTLINE|FILL)
+#define MINIARCS 5
+static int arcus0[1] =
+{
+    BOTH
+};
+static int arcus1[4] =
+{
+    BOTH, BOTH,
+    BOTH, BOTH
+};
+
+static int arcus2[9] =
+{
+    NEITHER, OUTLINE, NEITHER,
+    OUTLINE, FILL, OUTLINE,
+    NEITHER, OUTLINE, NEITHER
+};
+
+static int arcus3[16] =
+{
+    NEITHER, OUTLINE, OUTLINE, NEITHER,
+    OUTLINE, FILL, FILL, OUTLINE,
+    OUTLINE, FILL, FILL, OUTLINE,
+    NEITHER, OUTLINE, OUTLINE, NEITHER
+};
+
+static int arcus4[25] =
+{
+    NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER,
+    OUTLINE, FILL, FILL, FILL, OUTLINE,
+    OUTLINE, FILL, FILL, FILL, OUTLINE,
+    OUTLINE, FILL, FILL, FILL, OUTLINE,
+    NEITHER, OUTLINE, OUTLINE, OUTLINE, NEITHER
+};
+
+static int *arcis[MINIARCS] =
+{
+    arcus0, arcus1, arcus2, arcus3, arcus4
+};
+
+static void
+DrawMiniArc(
+    HDC dc,
+    int width,
+    int x,
+    int y,
+    int mask,
+    COLORREF inner,
+    COLORREF outer)
+{
+    int *arc;
+    int i, j;
+
+    if (width > MINIARCS) {
+	return;
+    }
+    arc = arcis[width];
+    for (i = 0; i <= width; i++) {
+	for (j = 0; j <= width; j++) {
+	    bit = (mask & *arc);
+	    if (bit & OUTLINE) {
+		SetPixelV(dc, x + i, y + j, outer);
+	    } else if (bit & FILL) {
+		SetPixelV(dc, x + i, y + j, inner);
+	    }
+	    arc++;
+	}
+    }
+}
+
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawArc --
+ *
+ *	This procedure handles the rendering of drawn or filled
+ *	arcs and chords.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Renders the requested arcs.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawArc(
+    HDC dc,
+    int arcMode,		/* Mode: either ArcChord or ArcPieSlice */
+    XArc *arcPtr,
+    HPEN pen,
+    HBRUSH brush)
+{
+    int start, extent, clockwise;
+    int xstart, ystart, xend, yend;
+    double radian_start, radian_end, xr, yr;
+    double dx, dy;
+
+    if ((arcPtr->angle1 == 0) && (arcPtr->angle2 == 23040)) {
+	/* Handle special case of circle or ellipse */
+	Ellipse(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+	    arcPtr->y + arcPtr->height + 1);
+	return;
+    }
+    start = arcPtr->angle1, extent = arcPtr->angle2;
+    clockwise = (extent < 0);	/* Non-zero if clockwise */
+
+    /*
+     * Compute the absolute starting and ending angles in normalized radians.
+     * Swap the start and end if drawing clockwise.
+     */
+    start = start % (64 * 360);
+    if (start < 0) {
+	start += (64 * 360);
+    }
+    extent = (start + extent) % (64 * 360);
+    if (extent < 0) {
+	extent += (64 * 360);
+    }
+    if (clockwise) {
+	int tmp = start;
+	start = extent;
+	extent = tmp;
+    }
+#define XAngleToRadians(a) ((double)(a) / 64 * M_PI / 180);
+    radian_start = XAngleToRadians(start);
+    radian_end = XAngleToRadians(extent);
+
+    /*
+     * Now compute points on the radial lines that define the starting and
+     * ending angles.  Be sure to take into account that the y-coordinate
+     * system is inverted.
+     */
+    dx = arcPtr->width * 0.5;
+    dy = arcPtr->height * 0.5;
+
+    xr = arcPtr->x + dx;
+    yr = arcPtr->y + dy;
+    xstart = (int)((xr + cos(radian_start) * dx) + 0.5);
+    ystart = (int)((yr + sin(-radian_start) * dy) + 0.5);
+    xend = (int)((xr + cos(radian_end) * dx) + 0.5);
+    yend = (int)((yr + sin(-radian_end) * dy) + 0.5);
+
+    /*
+     * Now draw a filled or open figure.  Note that we have to
+     * increase the size of the bounding box by one to account for the
+     * difference in pixel definitions between X and Windows.
+     */
+
+    if (brush == 0) {
+	/*
+	 * Note that this call will leave a gap of one pixel at the
+	 * end of the arc for thin arcs.  We can't use ArcTo because
+	 * it's only supported under Windows NT.
+	 */
+	Arc(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+	    arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+	/* FIXME: */
+    } else {
+	if (arcMode == ArcChord) {
+	    Chord(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+		arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+	} else if (arcMode == ArcPieSlice) {
+	    Pie(dc, arcPtr->x, arcPtr->y, arcPtr->x + arcPtr->width + 1,
+		arcPtr->y + arcPtr->height + 1, xstart, ystart, xend, yend);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawArcs --
+ *
+ *	Draws multiple circular or elliptical arcs.  Each arc is
+ *	specified by a rectangle and two angles.  The center of the
+ *	circle or ellipse is the center of the rect- angle, and the
+ *	major and minor axes are specified by the width and height.
+ *	Positive angles indicate counterclock- wise motion, and
+ *	negative angles indicate clockwise motion.  If the magnitude
+ *	of angle2 is greater than 360 degrees, XDrawArcs truncates it
+ *	to 360 degrees.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws an arc for each array element on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawArcs(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XArc *arcArr,
+    int nArcs)
+{
+    HPEN pen, oldPen;
+    HBRUSH brush, oldBrush;
+    HDC dc;
+    TkWinDCState state;
+    register XArc *arcPtr, *endPtr;
+    
+    display->request++;
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    pen = Blt_GCToPen(dc, gc);
+    oldPen = SelectPen(dc, pen);
+    brush = GetStockBrush(NULL_BRUSH);
+    oldBrush = SelectBrush(dc, brush);
+    endPtr = arcArr + nArcs;
+    for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
+	DrawArc(dc, gc->arc_mode, arcPtr, pen, 0);
+    }
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    DeletePen(SelectPen(dc, oldPen));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillArcs --
+ *
+ *	Draw a filled arc.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws a filled arc for each array element on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXFillArcs(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XArc *arcArr,
+    int nArcs)
+{
+    HBRUSH brush, oldBrush;
+    HPEN pen, oldPen;
+    HDC dc;
+    register XArc *arcPtr, *endPtr;
+    TkWinDCState state;
+
+    display->request++;
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    pen = Blt_GCToPen(dc, gc);
+    oldPen = SelectPen(dc, pen);
+    brush = CreateSolidBrush(gc->foreground);
+    oldBrush = SelectBrush(dc, brush);
+    endPtr = arcArr + nArcs;
+    for (arcPtr = arcArr; arcPtr < endPtr; arcPtr++) {
+	DrawArc(dc, gc->arc_mode, arcPtr, pen, brush);
+    }
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    DeletePen(SelectPen(dc, oldPen));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawLines --
+ *
+ *	Draw connected lines.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Renders a series of connected lines.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void CALLBACK
+DrawDot(
+    int x, int y,		/* Coordinates of point */
+    LPARAM clientData)
+{				/* Line information */
+    DashInfo *infoPtr = (DashInfo *) clientData;
+    int count;
+
+    infoPtr->count++;
+    count = (infoPtr->count + infoPtr->offset) / infoPtr->nBits;
+    if (count & 0x1) {
+	SetPixelV(infoPtr->dc, x, y, infoPtr->color);
+    }
+}
+
+
+void
+Blt_EmulateXDrawLine(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    int x1, int y1,
+    int x2, int y2)
+{
+    TkWinDCState state;
+    HDC dc;
+
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    if (gc->line_style != LineSolid) {
+	/* Handle dotted lines specially */
+	DashInfo info;
+
+	if (!GetDashInfo(dc, gc, &info)) {
+	    goto solidLine;
+	}
+	LineDDA(x1, y1, x2, y2, DrawDot, (LPARAM)&info);
+    } else {
+	HPEN pen, oldPen;
+	HBRUSH brush, oldBrush;
+
+      solidLine:
+	pen = Blt_GCToPen(dc, gc);
+	oldPen = SelectPen(dc, pen);
+	brush = CreateSolidBrush(gc->foreground);
+	oldBrush = SelectBrush(dc, brush);
+	MoveToEx(dc, x1, y1, (LPPOINT)NULL);
+	LineTo(dc, x2, y2);
+	DeletePen(SelectPen(dc, oldPen));
+	DeleteBrush(SelectBrush(dc, oldBrush));
+    }
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+static void
+DrawLine(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    POINT *points,
+    int nPoints)
+{
+    TkWinDCState state;
+    HDC dc;
+    register int i, n;
+    int start, extra, size;
+    HPEN pen, oldPen;
+    HBRUSH brush, oldBrush;
+
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    pen = Blt_GCToPen(dc, gc);
+    oldPen = SelectPen(dc, pen);
+    brush = CreateSolidBrush(gc->foreground);
+    oldBrush = SelectBrush(dc, brush);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    
+    start = extra = 0;
+    /*  
+     * Depending if the line is wide (> 1 pixel), arbitrarily break
+     * the line in sections of 100 points.  This bit of weirdness has
+     * to do with wide geometric pens.  The longer the polyline, the
+     * slower it draws.  The trade off is that we lose dash and 
+     * cap uniformity for unbearably slow polyline draws.  
+     */
+    if (gc->line_width > 1) {
+	size = 100;
+    } else {
+	size = nPoints;
+    }
+    for (i = nPoints; i > 0; i -= size) {
+	n = MIN(i, size);
+	Polyline(dc, points + start, n + extra);
+	start += (n - 1);
+	extra = 1;
+    }
+    DeletePen(SelectPen(dc, oldPen));
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+void
+Blt_EmulateXDrawLines(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XPoint *pointArr,
+    int nPoints,
+    int mode)
+{
+    if (drawable == None) {
+	return;
+    }
+    if (gc->line_style != LineSolid) { /* Handle dotted lines specially */
+	DashInfo info;
+	TkWinDCState state;
+	HDC dc;
+	int result;
+
+	dc = TkWinGetDrawableDC(display, drawable, &state);
+	SetROP2(dc, tkpWinRopModes[gc->function]);
+	result = GetDashInfo(dc, gc, &info);
+	if (result) {
+	    register XPoint *p1, *p2;
+	    register int i;
+
+	    p1 = pointArr;
+	    p2 = p1 + 1;
+	    for (i = 1; i < nPoints; i++, p1++, p2++) {
+		LineDDA(p1->x, p1->y, p2->x, p2->y, DrawDot, (LPARAM)&info);
+	    }
+	    result = TCL_OK;
+	}
+	TkWinReleaseDrawableDC(drawable, dc, &state);
+	if (result) {
+	    return;
+	}
+    } else {
+	POINT *points, *destPtr;
+	XPoint *srcPtr, *endPtr;
+
+	points = Blt_Malloc(sizeof(POINT) * nPoints);
+	if (points == NULL) {
+	    return;
+	}
+	endPtr = pointArr + nPoints;
+	if (mode == CoordModeOrigin) {
+	    destPtr = points;
+	    for (srcPtr = pointArr; srcPtr < endPtr; srcPtr++) {
+		destPtr->x = (int)srcPtr->x;
+		destPtr->y = (int)srcPtr->y;
+		destPtr++;
+	    }
+	} else {
+	    POINT *lastPtr;
+	    
+	    srcPtr = pointArr;
+	    destPtr = points;
+	    destPtr->x = (int)srcPtr->x;
+	    destPtr->y = (int)srcPtr->y;
+	    lastPtr = destPtr;
+	    srcPtr++, destPtr++;
+	    for (/*empty*/; srcPtr < endPtr; srcPtr++) {
+		destPtr->x = lastPtr->x + (int)srcPtr->x;
+		destPtr->y = lastPtr->y + (int)srcPtr->y;
+		lastPtr = destPtr;
+		destPtr++;
+	    }
+	}
+	DrawLine(display, drawable, gc, points, nPoints);
+	Blt_Free(points);
+    }
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawSegments --
+ *
+ *	Draws multiple, unconnected lines. For each segment, draws a
+ *	line between (x1, y1) and (x2, y2).  It draws the lines in the
+ *	order listed in the array of XSegment structures and does not
+ *	perform joining at coincident endpoints.  For any given line,
+ *	does not draw a pixel more than once. If lines intersect, the
+ *	intersecting pixels are drawn multiple times.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws unconnected line segments on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawSegments(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XSegment *segArr,
+    int nSegments)
+{
+    HDC dc;
+    register XSegment *segPtr, *endPtr;
+    TkWinDCState state;
+
+    display->request++;
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    if (gc->line_style != LineSolid) {
+	/* Handle dotted lines specially */
+	DashInfo info;
+
+	if (!GetDashInfo(dc, gc, &info)) {
+	    goto solidLine;
+	}
+	endPtr = segArr + nSegments;
+	for (segPtr = segArr; segPtr < endPtr; segPtr++) {
+	    info.count = 0; /* Reset dash counter after every segment. */
+	    LineDDA(segPtr->x1, segPtr->y1, segPtr->x2, segPtr->y2, DrawDot, 
+		(LPARAM)&info);
+	}
+    } else {
+	HPEN pen, oldPen;
+
+      solidLine:
+	pen = Blt_GCToPen(dc, gc);
+	oldPen = SelectPen(dc, pen);
+	endPtr = segArr + nSegments;
+	for (segPtr = segArr; segPtr < endPtr; segPtr++) {
+	    MoveToEx(dc, segPtr->x1, segPtr->y1, (LPPOINT)NULL);
+	    LineTo(dc, segPtr->x2, segPtr->y2);
+	}
+	DeletePen(SelectPen(dc, oldPen));
+    }
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawRectangle --
+ *
+ *       Draws the outlines of the specified rectangle as if a
+ *       five-point PolyLine protocol request were specified for each
+ *       rectangle:
+ *
+ *             [x,y] [x+width,y] [x+width,y+height] [x,y+height]
+ *             [x,y]
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws a rectangle on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawRectangle(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    int x, int y,
+    unsigned int width,
+    unsigned int height)
+{
+    TkWinDCState state;
+    HPEN pen, oldPen;
+    HBRUSH brush, oldBrush;
+    HDC dc;
+
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    pen = Blt_GCToPen(dc, gc);
+    brush = GetStockObject(NULL_BRUSH);
+    oldPen = SelectPen(dc, pen);
+    oldBrush = SelectBrush(dc, brush);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    if (gc->line_style != LineSolid) {
+	/* Handle dotted lines specially */
+	register int x2, y2;
+	DashInfo info;
+
+	if (!GetDashInfo(dc, gc, &info)) {
+	    goto solidLine;
+	}
+	x2 = x + width;
+	y2 = y + height;
+	LineDDA(x, y, x2, y, DrawDot, (LPARAM)&info);
+	LineDDA(x2, y, x2, y2, DrawDot, (LPARAM)&info);
+	LineDDA(x2, y2, x, y2, DrawDot, (LPARAM)&info);
+	LineDDA(x, y2, x, y, DrawDot, (LPARAM)&info);
+    } else {
+      solidLine:
+	Rectangle(dc, x, y, x + width + 1, y + height + 1);
+    }
+    DeletePen(SelectPen(dc, oldPen));
+    DeleteBrush(SelectBrush(dc, oldBrush));
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXDrawPoints --
+ *
+ *	Uses the foreground pixel and function components of the GC to
+ *	draw a multiple points into the specified drawable.
+ *      CoordModeOrigin treats all coordinates as relative to the
+ *	origin, and CoordModePrevious treats all coordinates after
+ *	the first as relative to the previous point.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws points on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawPoints(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XPoint *pointArr,
+    int nPoints,
+    int mode)
+{				/* Ignored. CoordModeOrigin is assumed. */
+    HDC dc;
+    register XPoint *pointPtr, *endPtr;
+    TkWinDCState state;
+
+    display->request++;
+    if (drawable == None) {
+	return;
+    }
+    dc = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(dc, tkpWinRopModes[gc->function]);
+    endPtr = pointArr + nPoints;
+    for (pointPtr = pointArr; pointPtr < endPtr; pointPtr++) {
+	SetPixelV(dc, pointPtr->x, pointPtr->y, gc->foreground);
+    }
+    TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXReparentWindow --
+ *
+ *	If the specified window is mapped, automatically performs an
+ *	UnmapWindow request on it, removes it from its current
+ *	position in the hierarchy, and inserts it as the child of the
+ *	specified parent.  The window is placed in the stacking order
+ *	on top with respect to sibling windows.
+ *
+ *	Note: In WIN32 you can't reparent to/from another application.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Reparents the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXReparentWindow(
+    Display *display,
+    Window window,
+    Window parent,
+    int x,
+    int y)
+{
+    HWND child, newParent;
+
+    child = Tk_GetHWND(window);
+    newParent = Tk_GetHWND(parent);
+    SetParent(child, newParent);
+    SetWindowLong(child, GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN |
+	WS_CLIPSIBLINGS);
+
+    XMoveWindow(display, window, x, y);
+    XRaiseWindow(display, window);
+    XMapWindow(display, window);
+}
+
+void
+Blt_EmulateXSetDashes(
+    Display *display,
+    GC gc,
+    int dashOffset,
+    _Xconst char *dashList,
+    int n)
+{
+    gc->dashes = (unsigned char)strlen(dashList);
+    gc->dash_offset = (int)dashList;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXDrawString --
+ *
+ *	Draw a single string in the current font.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Renders the specified string in the drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXDrawString(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    int x,
+    int y,
+    _Xconst char *string,
+    int length)
+{
+    if (drawable == None) {
+	return;
+    }
+    Tk_DrawChars(display, drawable, gc, (Tk_Font)gc->font, string, length, 
+	x, y);
+}
+
+static void
+TileArea(destDC, srcDC, tileOriginX, tileOriginY, tileWidth, tileHeight,
+	 x, y, width, height)
+    HDC destDC, srcDC;
+    int tileOriginX, tileOriginY, tileWidth,  tileHeight;
+    int x, y, width, height;
+{
+    int destX, destY;
+    int destWidth, destHeight;
+    int srcX, srcY;
+    int xOrigin, yOrigin;
+    int delta;
+    int left, top, right, bottom;
+
+    xOrigin = x, yOrigin = y;
+    if (x < tileOriginX) {
+	delta = (tileOriginX - x) % tileWidth;
+	if (delta > 0) {
+	    xOrigin -= (tileWidth - delta);
+	}
+    } else if (x > tileOriginX) {
+	delta = (x - tileOriginX) % tileWidth;
+	if (delta > 0) {
+	    xOrigin -= delta;
+	}
+    }
+    if (y < tileOriginY) {
+	delta = (tileOriginY - y) % tileHeight;
+	if (delta > 0) {
+	    yOrigin -= (tileHeight - delta);
+	}
+    } else if (y >= tileOriginY) {
+	delta = (y - tileOriginY) % tileHeight;
+	if (delta > 0) {
+	    yOrigin -= delta;
+	}
+    }
+#ifdef notdef
+    PurifyPrintf("tile is (%d,%d,%d,%d)\n", tileOriginX, tileOriginY, 
+		 tileWidth, tileHeight);
+    PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+    PurifyPrintf("starting at %d,%d\n", xOrigin, yOrigin);
+#endif
+    left = x;
+    right = x + width;
+    top = y;
+    bottom = y + height;
+    for (y = yOrigin; y < bottom; y += tileHeight) {
+	srcY = 0;
+	destY = y;
+	destHeight = tileHeight;
+	if (y < top) {
+	    srcY = (top - y);
+	    destHeight = tileHeight - srcY;
+	    destY = top;
+	} 
+	if ((destY + destHeight) > bottom) {
+	    destHeight = (bottom - destY);
+	}
+	for (x = xOrigin; x < right; x += tileWidth) {
+	    srcX = 0;
+	    destX = x;
+	    destWidth = tileWidth;
+	    if (x < left) {
+		srcX = (left - x);
+		destWidth = tileWidth - srcX;
+		destX = left;
+	    } 
+	    if ((destX + destWidth) > right) {
+		destWidth = (right - destX);
+	    }
+#ifdef notdef
+	    PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+		 srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+	    BitBlt(destDC, destX, destY, destWidth, destHeight, 
+		srcDC, srcX, srcY, SRCCOPY);
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmultateXFillRectangles --
+ *
+ *	Fill multiple rectangular areas in the given drawable.
+ *	Handles tiling.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Blt_EmulateXFillRectangles(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    XRectangle *rectArr,
+    int nRectangles)
+{
+    BITMAP bm;
+    HBITMAP oldBitmap, hBitmap;
+    HBRUSH oldBrush, hFgBrush, hBgBrush, hBrush;
+    HDC hDC;
+    HDC memDC;
+    RECT rect;
+    TkWinDCState state;
+    TkWinDrawable *twdPtr;
+    register XRectangle *rectPtr, *endPtr;
+    
+    if (drawable == None) {
+	return;
+    }
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(hDC, tkpWinRopModes[gc->function]);
+
+    switch(gc->fill_style) {
+    case FillTiled:
+	if (gc->tile == None) {
+	    goto fillSolid;
+        }
+#ifdef notdef
+	if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
+	    goto fillSolid;
+	}
+#endif
+        twdPtr = (TkWinDrawable *)gc->tile;
+	GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+	memDC = CreateCompatibleDC(hDC);
+	oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+	endPtr = rectArr + nRectangles;
+        for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth, 
+		bm.bmHeight, (int)rectPtr->x, (int)rectPtr->y, 
+		(int)rectPtr->width, (int)rectPtr->height);
+        }
+	SelectBitmap(memDC, oldBitmap);
+	DeleteDC(memDC);
+        break; 
+
+    case FillOpaqueStippled:
+    case FillStippled:
+	if (gc->stipple == None) {
+	    goto fillSolid;
+	}
+        twdPtr = (TkWinDrawable *)gc->stipple;
+	if (twdPtr->type != TWD_BITMAP) {
+	    panic("unexpected drawable type in stipple");
+	}
+	hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
+	SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
+	oldBrush = SelectBrush(hDC, hBrush);
+	memDC = CreateCompatibleDC(hDC);
+
+	/*
+	 * For each rectangle, create a drawing surface which is the size of
+	 * the rectangle and fill it with the background color.  Then merge the
+	 * result with the stipple pattern.
+	 */
+        hFgBrush = CreateSolidBrush(gc->foreground);
+	hBgBrush = CreateSolidBrush(gc->background);
+	endPtr = rectArr + nRectangles;
+        for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width, 
+		rectPtr->height);
+	    oldBitmap = SelectObject(memDC, hBitmap);
+	    rect.left = rect.top = 0;
+	    rect.right = rectPtr->width;
+	    rect.bottom = rectPtr->height;
+	    FillRect(memDC, &rect, hFgBrush);
+	    BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+		memDC, 0, 0, COPYBG);
+	    if (gc->fill_style == FillOpaqueStippled) {
+		FillRect(memDC, &rect, hBgBrush);
+		BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, 
+			rectPtr->height, memDC, 0, 0, COPYFG);
+	    }
+	    SelectObject(memDC, oldBitmap);
+	    DeleteObject(hBitmap);
+	}
+	DeleteBrush(hFgBrush);
+	DeleteBrush(hBgBrush);
+	DeleteDC(memDC);
+	SelectBrush(hDC, oldBrush);
+	DeleteBrush(hBrush);
+ 	break;
+
+    case FillSolid:
+	fillSolid:
+	memDC = CreateCompatibleDC(hDC);
+        hFgBrush = CreateSolidBrush(gc->foreground);
+	endPtr = rectArr + nRectangles;
+        for (rectPtr = rectArr; rectPtr < endPtr; rectPtr++) {
+	    hBitmap = CreateCompatibleBitmap(hDC, rectPtr->width, 
+		rectPtr->height);
+	    oldBitmap = SelectObject(memDC, hBitmap);
+	    rect.left = rect.top = 0;
+	    rect.right = rectPtr->width;
+	    rect.bottom = rectPtr->height;
+	    FillRect(memDC, &rect, hFgBrush);
+	    BitBlt(hDC, rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
+		   memDC, 0, 0, SRCCOPY);
+	    SelectObject(memDC, oldBitmap);
+	    DeleteObject(hBitmap);
+	}
+	DeleteBrush(hFgBrush);
+	DeleteDC(memDC);
+ 	break;
+    }
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+void
+Blt_EmulateXFillRectangle(
+    Display *display,
+    Drawable drawable,
+    GC gc,
+    int x,
+    int y,
+    unsigned int width,
+    unsigned int height)
+{
+    HDC hDC;
+    RECT rect;
+    TkWinDCState state;
+
+    if (drawable == None) {
+	return;
+    }
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(hDC, tkpWinRopModes[gc->function]);
+    rect.left = rect.top = 0;
+    rect.right = width;
+    rect.bottom = height;
+
+    switch(gc->fill_style) {
+    case FillTiled:
+	{
+	    TkWinDrawable *twdPtr;
+	    HBITMAP oldBitmap;
+	    HDC memDC;
+	    BITMAP bm;
+
+	    if (gc->tile == None) { 
+		goto fillSolid;
+	    }
+#ifdef notdef
+	    if ((GetDeviceCaps(hDC, RASTERCAPS) & RC_BITBLT) == 0) {
+		goto fillSolid;
+	    }
+#endif
+	    twdPtr = (TkWinDrawable *)gc->tile;
+	    /* The tiling routine needs to know the size of the bitmap */
+	    GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+
+	    memDC = CreateCompatibleDC(hDC);
+	    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+	    TileArea(hDC, memDC, gc->ts_x_origin, gc->ts_y_origin, bm.bmWidth, 
+		     bm.bmHeight, x, y, width, height);
+	    SelectBitmap(memDC, oldBitmap);
+	    DeleteDC(memDC);
+	}
+	break; 
+	    
+    case FillOpaqueStippled:
+    case FillStippled:
+	{
+	    TkWinDrawable *twdPtr;
+	    HBRUSH oldBrush, hBrush;
+	    HBRUSH hBrushFg, hBrushBg;
+	    HBITMAP oldBitmap, hBitmap;
+	    HDC memDC;
+
+	    if (gc->stipple == None) {
+		goto fillSolid;
+	    }
+	    twdPtr = (TkWinDrawable *)gc->stipple;
+	    if (twdPtr->type != TWD_BITMAP) {
+		panic("unexpected drawable type in stipple");
+	    }
+	    hBrush = CreatePatternBrush(twdPtr->bitmap.handle);
+	    SetBrushOrgEx(hDC, gc->ts_x_origin, gc->ts_y_origin, NULL);
+	    oldBrush = SelectBrush(hDC, hBrush);
+	    memDC = CreateCompatibleDC(hDC);
+	    
+	    hBrushFg = CreateSolidBrush(gc->foreground);
+	    hBrushBg = CreateSolidBrush(gc->background);
+	    hBitmap = CreateCompatibleBitmap(hDC, width, height);
+	    oldBitmap = SelectObject(memDC, hBitmap);
+	    FillRect(memDC, &rect, hBrushFg);
+	    SetBkMode(hDC, TRANSPARENT);
+	    BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYFG);
+	    if (gc->fill_style == FillOpaqueStippled) {
+		FillRect(memDC, &rect, hBrushBg);
+		BitBlt(hDC, x, y, width, height, memDC, 0, 0, COPYBG);
+	    }
+	    SelectBrush(hDC, oldBrush);
+	    SelectBitmap(memDC, oldBitmap);
+	    DeleteBrush(hBrushFg);
+	    DeleteBrush(hBrushBg);
+	    DeleteBrush(hBrush);
+	    DeleteBitmap(hBitmap);
+	    DeleteDC(memDC);
+	}
+	break;
+
+    case FillSolid:
+	{
+	    HBRUSH hBrush;
+	    HBITMAP oldBitmap, hBitmap;
+	    HDC memDC;
+
+	fillSolid:
+	    /* TkWinFillRect(hDC, x, y, width, height, gc->foreground);  */
+	    memDC = CreateCompatibleDC(hDC);
+	    hBrush = CreateSolidBrush(gc->foreground);
+	    hBitmap = CreateCompatibleBitmap(hDC, width, height);
+	    oldBitmap = SelectBitmap(memDC, hBitmap);
+	    rect.left = rect.top = 0;
+	    rect.right = width;
+	    rect.bottom = height;
+	    FillRect(memDC, &rect, hBrush);
+	    BitBlt(hDC, x, y, width, height, memDC, 0, 0, SRCCOPY);
+	    SelectObject(memDC, oldBitmap);
+	    DeleteBitmap(hBitmap);
+	    DeleteBrush(hBrush);
+	    DeleteDC(memDC);
+	}
+	break;
+    }
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
+
+static BOOL
+DrawChars(HDC dc, int x, int y, char *string, int length)
+{
+    BOOL result;
+
+#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0)) 
+    if (systemEncoding == NULL) {
+	result = TextOutA(dc, x, y, string, length);
+    } else {
+	const unsigned short *wstring;
+	Tcl_DString dString;
+
+	Tcl_DStringInit(&dString);
+	Tcl_UtfToExternalDString(systemEncoding, string, length, &dString);
+	length = Tcl_NumUtfChars(string, -1);
+	wstring = (const unsigned short *)Tcl_DStringValue(&dString);
+	result = TextOutW(dc, x, y, wstring, length);
+	Tcl_DStringFree(&dString);
+    }
+#else 
+    result = TextOutA(dc, x, y, string, length);
+#endif /* TCL_VERSION_NUMBER >= 8.1.0 */
+    return result;
+}
+
+int
+Blt_DrawRotatedText(
+    Display *display,
+    Drawable drawable,
+    int x, int y,
+    double theta,
+    TextStyle *tsPtr,
+    TextLayout *textPtr)
+{
+    HFONT hFont, oldFont;
+    TkWinDCState state;
+    HDC hDC;
+    int isActive;
+    int bbWidth, bbHeight;
+    double rotWidth, rotHeight;
+    double sinTheta, cosTheta;
+    Point2D p, q, center;
+    register TextFragment *fragPtr, *endPtr;
+#if (TCL_VERSION_NUMBER >=  _VERSION(8,1,0)) 
+    static int initialized = 0;
+
+    if (!initialized) {
+	if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	    /*
+	     * If running NT, then we will be calling some Unicode functions 
+	     * explictly.  So, even if the Tcl system encoding isn't Unicode, 
+	     * make sure we convert to/from the Unicode char set. 
+	     */
+	    systemEncoding = Tcl_GetEncoding(NULL, "unicode");
+	} 
+	initialized = 1;
+    }
+#endif
+    hFont = CreateRotatedFont(tsPtr->gc->font, theta);
+    if (hFont == NULL) {
+	return FALSE;
+    }
+    isActive = (tsPtr->state & STATE_ACTIVE);
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+    SetROP2(hDC, tsPtr->gc->function);
+    oldFont = SelectFont(hDC, hFont);
+
+    Blt_GetBoundingBox(textPtr->width, textPtr->height, theta, &rotWidth, 
+		       &rotHeight, (Point2D *)NULL);
+    bbWidth = ROUND(rotWidth);
+    bbHeight = ROUND(rotHeight);
+    Blt_TranslateAnchor(x, y, bbWidth, bbHeight, tsPtr->anchor, &x, &y);
+    center.x = (double)textPtr->width * -0.5;
+    center.y = (double)textPtr->height * -0.5;
+    theta = (-theta / 180.0) * M_PI;
+    sinTheta = sin(theta), cosTheta = cos(theta);
+
+    endPtr = textPtr->fragArr + textPtr->nFrags;
+
+    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+	p.x = center.x + (double)fragPtr->x;
+	p.y = center.y + (double)fragPtr->y;
+	q.x = x + (p.x * cosTheta) - (p.y * sinTheta) + (bbWidth * 0.5);
+	q.y = y + (p.x * sinTheta) + (p.y * cosTheta) + (bbHeight * 0.5);
+	fragPtr->sx = ROUND(q.x);
+	fragPtr->sy = ROUND(q.y);
+    }
+    SetBkMode(hDC, TRANSPARENT);
+    SetTextAlign(hDC, TA_LEFT | TA_BASELINE);
+
+    if (tsPtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+	TkBorder *borderPtr = (TkBorder *) tsPtr->border;
+	XColor *color1, *color2;
+	
+	color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+	if (tsPtr->state & STATE_EMPHASIS) {
+	    XColor *hold;
+	    
+	    hold = color1, color1 = color2, color2 = hold;
+	}
+	if (color1 != NULL) {
+	    SetTextColor(hDC, color1->pixel);
+	    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+		DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text, 
+			fragPtr->count); 
+	    }
+	}
+	if (color2 != NULL) {
+	    SetTextColor(hDC, color2->pixel);
+	    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+		DrawChars(hDC, fragPtr->sx + 1, fragPtr->sy + 1, fragPtr->text, 
+			fragPtr->count);
+	    }
+	}
+	goto done;		/* Done */
+    }
+    SetBkMode(hDC, TRANSPARENT);
+    if ((tsPtr->shadow.offset > 0) && (tsPtr->shadow.color != NULL)) {
+	SetTextColor(hDC, tsPtr->shadow.color->pixel);
+	for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+	    DrawChars(hDC, fragPtr->sx + tsPtr->shadow.offset, 
+		    fragPtr->sy + tsPtr->shadow.offset, fragPtr->text, 
+		    fragPtr->count);
+	}
+    }
+    if (isActive) {
+	SetTextColor(hDC, tsPtr->activeColor->pixel);
+    } else {
+	SetTextColor(hDC, tsPtr->color->pixel);
+    }	    
+
+    for (fragPtr = textPtr->fragArr; fragPtr < endPtr; fragPtr++) {
+	DrawChars(hDC, fragPtr->sx, fragPtr->sy, fragPtr->text, 
+		    fragPtr->count);
+    }
+
+    if (isActive) {
+	SetTextColor(hDC, tsPtr->color->pixel);
+    }	    
+ done:
+    SelectFont(hDC, oldFont);
+    DeleteFont(hFont);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+    return TRUE;
+}
+
+static void
+DrawPixel(
+    HDC hDC, 
+    int x, 
+    int y, 
+    COLORREF color)
+{
+    HDC memDC;
+    HBRUSH hBrushFg;
+    HBITMAP oldBitmap, hBitmap;
+    RECT rect;
+    int size;
+ 
+    size = 1;
+    memDC = CreateCompatibleDC(hDC);
+    hBrushFg = CreateSolidBrush(color);
+    hBitmap = CreateCompatibleBitmap(hDC, size, size);
+    oldBitmap = SelectObject(memDC, hBitmap);
+    rect.left = rect.top = 0;
+    rect.right = rect.bottom = size;
+    FillRect(memDC, &rect, hBrushFg);
+    BitBlt(hDC, x, y, size, size, memDC, 0, 0, SRCCOPY);
+    SelectObject(memDC, oldBitmap);
+    DeleteObject(hBitmap);
+    DeleteBrush(hBrushFg);
+    DeleteDC(memDC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PixelateBitmap --
+ *
+ *	Draws a masked bitmap in given device (should be printer)
+ *	context.  Bit operations on print devices usually fail because
+ *	there's no way to read back from the device surface to get the
+ *	previous pixel values, rendering BitBlt useless. The bandaid
+ *	solution here is to draw 1x1 pixel rectangles at each
+ *	coordinate as directed by the the mask and source bitmaps.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+PixelateBitmap(
+    Display *display,
+    Drawable drawable,
+    Pixmap srcBitmap,
+    Pixmap maskBitmap,
+    int width,
+    int height,
+    GC gc,
+    int destX,
+    int destY)
+{
+    register int x, y;
+    register int dx, dy;
+    int pixel;
+    unsigned char *srcBits;
+    register unsigned char *srcPtr;
+    int bitPos, bytesPerRow;
+    HDC hDC;
+    TkWinDCState state;
+
+    srcBits = Blt_GetBitmapData(display, srcBitmap, width, height, 
+	&bytesPerRow);
+    if (srcBits == NULL) {
+	return;
+    }
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+    if (maskBitmap != None) {
+        register unsigned char *maskPtr;
+        unsigned char *maskBits;
+        maskBits = Blt_GetBitmapData(display, maskBitmap, width, height,
+	    &bytesPerRow);
+        bytesPerRow = ((width + 31) & ~31) / 8;
+        for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
+	    maskPtr = maskBits + (bytesPerRow * y);
+	    srcPtr = srcBits + (bytesPerRow * y);
+	    for (dx = destX, x = 0; x < width; x++, dx++) {
+	        bitPos = x % 8;
+	        pixel = (*maskPtr & (0x80 >> bitPos));
+	        if (pixel) {
+		    pixel = (*srcPtr & (0x80 >> bitPos));
+		    DrawPixel(hDC, dx, dy, 
+		        (pixel) ? gc->foreground : gc->background);
+	        }
+	        if (bitPos == 7) {
+		    srcPtr++, maskPtr++;
+	        }
+	    }			/* x */
+        }
+        Blt_Free(maskBits);
+    } else {
+        bytesPerRow = ((width + 31) & ~31) / 8;
+        for (dy = destY, y = height - 1; y >= 0; y--, dy++) {
+	    srcPtr = srcBits + (bytesPerRow * y);
+	    for (dx = destX, x = 0; x < width; x++, dx++) {
+	        bitPos = x % 8;
+		pixel = (*srcPtr & (0x80 >> bitPos));
+		DrawPixel(hDC, dx, dy, 
+		        (pixel) ? gc->foreground : gc->background);
+	        if (bitPos == 7) {
+		    srcPtr++;
+	        }
+	    }
+	}
+    }
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+    Blt_Free(srcBits);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCopyPlane --
+ *
+ *	Simplified version of XCopyPlane.  Right now it ignores
+ *		function, 
+ *		clip_x_origin, 
+ *		clip_y_origin
+ *
+ *	The plane argument must always be 1.
+ *
+ *	This routine differs from the Tk version in how it handles 
+ *	transparency.  It uses a different method of drawing transparent
+ *	bitmaps that doesn't copy the background or use brushes.  The
+ *	second change is to call a special routine when the destDC is
+ *	a printer.   Stippling is done by a very slow brute-force
+ *	method of drawing 1x1 rectangles for each pixel (bleech).  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Changes the destination drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_EmulateXCopyPlane(
+    Display *display,
+    Drawable src,
+    Drawable dest,
+    GC gc,
+    int srcX,
+    int srcY,
+    unsigned int width,
+    unsigned int height,
+    int destX,
+    int destY,
+    unsigned long plane)
+{
+    HDC srcDC, destDC;
+    TkWinDCState srcState, destState;
+    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;
+
+    display->request++;
+
+    if (plane != 1) {
+	panic("Unexpected plane specified for XCopyPlane");
+    }
+    srcDC = TkWinGetDrawableDC(display, src, &srcState);
+
+    if (src != dest) {
+	destDC = TkWinGetDrawableDC(display, dest, &destState);
+    } else {
+	destDC = srcDC;
+    }
+    if ((clipPtr == NULL) || (clipPtr->type == TKP_CLIP_REGION)) {
+	/*
+	 * Case 1: opaque bitmaps.  Windows handles the conversion
+	 * from one bit to multiple bits by setting 0 to the
+	 * foreground color, and 1 to the background color (seems
+	 * backwards, but there you are).
+	 */
+	if ((clipPtr != NULL) && (clipPtr->type == TKP_CLIP_REGION)) {
+	    SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+	    OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+	}
+	SetBkMode(destDC, OPAQUE);
+	SetBkColor(destDC, gc->foreground);
+	SetTextColor(destDC, gc->background);
+	BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY,
+	    SRCCOPY);
+
+	SelectClipRgn(destDC, NULL);
+
+    } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
+	Drawable mask;
+	/*
+	 * Case 2: transparent bitmaps are handled by setting the
+	 * destination to the foreground color whenever the source
+	 * pixel is set.
+	 */
+	/*
+	 * Case 3: two arbitrary bitmaps.  Copy the source rectangle
+	 * into a color pixmap.  Use the result as a brush when
+	 * copying the clip mask into the destination.
+	 */
+	mask = clipPtr->value.pixmap;
+
+#if WINDEBUG
+	PurifyPrintf("mask %s src\n", (mask == src) ? "==" : "!=");
+	PurifyPrintf("GetDeviceCaps=%x\n", 
+		GetDeviceCaps(destDC, TECHNOLOGY) & DT_RASDISPLAY);
+#endif
+	{
+	    HDC maskDC;
+	    TkWinDCState maskState;
+
+	    if (mask != src) {
+		maskDC = TkWinGetDrawableDC(display, mask, &maskState);
+	    } else {
+		maskDC = srcDC;
+	    }
+	    SetBkMode(destDC, OPAQUE);
+	    SetTextColor(destDC, gc->background);
+	    SetBkColor(destDC, gc->foreground);
+	    BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY, 
+		   SRCINVERT);
+	    /* 
+	     * Make sure we treat the mask as a monochrome bitmap. 
+	     * We can get alpha-blending with non-black/white fg/bg 
+	     * color selections.
+	     */
+	    SetTextColor(destDC, RGB(255,255,255));
+	    SetBkColor(destDC, RGB(0,0,0));
+
+	    /* FIXME: Handle gc->clip_?_origin's */ 
+	    BitBlt(destDC, destX, destY, width, height, maskDC, 0, 0, SRCAND);
+
+	    SetTextColor(destDC, gc->background);
+	    SetBkColor(destDC, gc->foreground);
+	    BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY, 
+		   SRCINVERT);
+	    if (mask != src) {
+		TkWinReleaseDrawableDC(mask, maskDC, &maskState);
+	    }
+	}
+    }
+    if (src != dest) {
+	TkWinReleaseDrawableDC(dest, destDC, &destState);
+    }
+    TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXCopyArea --
+ *
+ *	Copies data from one drawable to another using block transfer
+ *	routines.  The small enhancement over the version in Tk is
+ *	that it doesn't assume that the source and destination devices
+ *	have the same resolution. This isn't true when the destination
+ *	device is a printer.
+ *
+ *	FIXME: not true anymore.  delete this routine.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Data is moved from a window or bitmap to a second window,
+ *	bitmap, or printer.
+ *
+ *---------------------------------------------------------------------- 
+ */
+void
+Blt_EmulateXCopyArea(
+    Display *display,
+    Drawable src,
+    Drawable dest,
+    GC gc,
+    int srcX,			/* Source X-coordinate */
+    int srcY,			/* Source Y-coordinate. */
+    unsigned int width,		/* Width of area. */
+    unsigned int height,	/* Height of area. */
+    int destX,			/* Destination X-coordinate (in screen 
+				 * coordinates). */
+    int destY)			/* Destination Y-coordinate (in screen
+				 * coordinates). */
+{
+    HDC srcDC, destDC;
+    TkWinDCState srcState, destState;
+    TkpClipMask *clipPtr;
+
+    srcDC = TkWinGetDrawableDC(display, src, &srcState);
+    if (src != dest) {
+	destDC = TkWinGetDrawableDC(display, dest, &destState);
+    } else {
+	destDC = srcDC;
+    }
+    clipPtr = (TkpClipMask *)gc->clip_mask;
+    if ((clipPtr != NULL) && (clipPtr->type == TKP_CLIP_REGION)) {
+	SelectClipRgn(destDC, (HRGN)clipPtr->value.region);
+	OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+    }
+
+    BitBlt(destDC, destX, destY, width, height, srcDC, srcX, srcY, 
+		bltModes[gc->function]);
+    SelectClipRgn(destDC, NULL);
+
+    if (src != dest) {
+	TkWinReleaseDrawableDC(dest, destDC, &destState);
+    }
+    TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+static void
+StippleRegion(
+    Display *display,
+    HDC hDC,			/* Device context: For polygons, clip
+				 * region will be installed. */
+    GC gc,
+    int x, int y,
+    int width, int height)
+{
+    BITMAP bm;
+    HBITMAP oldBitmap;
+    HDC maskDC, memDC;
+    Pixmap mask;
+    TkWinDCState maskState;
+    TkWinDrawable *twdPtr;
+    int destX, destY, destWidth, destHeight;
+    int dx, dy;
+    int left, top, right, bottom;
+    int srcX, srcY;
+    int startX, startY;		/* Starting upper left corner of region. */
+    
+    twdPtr = (TkWinDrawable *)gc->stipple;
+    GetObject(twdPtr->bitmap.handle, sizeof(BITMAP), &bm);
+
+    startX = x;
+    if (x < gc->ts_x_origin) {
+	dx = (gc->ts_x_origin - x) % bm.bmWidth;
+	if (dx > 0) {
+	    startX -= (bm.bmWidth - dx);
+	}
+    } else if (x > gc->ts_x_origin) {
+	dx = (x - gc->ts_x_origin) % bm.bmWidth;
+	if (dx > 0) {
+	    startX -= dx;
+	}
+    }
+    startY = y;
+    if (y < gc->ts_y_origin) {
+	dy = (gc->ts_y_origin - y) % bm.bmHeight;
+	if (dy > 0) {
+	    startY -= (bm.bmHeight - dy);
+	}
+    } else if (y >= gc->ts_y_origin) {
+	dy = (y - gc->ts_y_origin) % bm.bmHeight;
+	if (dy > 0) {
+	    startY -= dy;
+	}
+    }
+#ifdef notdef
+    PurifyPrintf("tile is (%d,%d,%d,%d)\n", gc->ts_x_origin, gc->ts_y_origin, 
+		 bm.bmWidth, bm.bmHeight);
+    PurifyPrintf("region is (%d,%d,%d,%d)\n", x, y, width, height);
+    PurifyPrintf("starting at %d,%d\n", startX, startY);
+#endif
+    left = x;
+    right = x + width;
+    top = y;
+    bottom = y + height;
+
+    maskDC = memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, twdPtr->bitmap.handle);
+    mask = gc->stipple;
+    if (gc->fill_style == FillStippled) { /* With transparency. */
+	if (gc->clip_mask != None) {
+	    TkpClipMask *clipPtr;
+	    
+	    mask = gc->stipple;
+	    clipPtr = (TkpClipMask *)gc->clip_mask;
+	    if  (clipPtr->type == TKP_CLIP_PIXMAP) {
+		mask = clipPtr->value.pixmap;
+	    }
+	}
+	if (mask != gc->stipple) {
+	    maskDC = TkWinGetDrawableDC(display, mask, &maskState);
+	}
+    }
+
+    for (y = startY; y < bottom; y += bm.bmHeight) {
+	srcY = 0;
+	destY = y;
+	destHeight = bm.bmHeight;
+	if (y < top) {
+	    srcY = (top - y);
+	    destHeight = bm.bmHeight - srcY;
+	    destY = top;
+	} 
+	if ((destY + destHeight) > bottom) {
+	    destHeight = (bottom - destY);
+	}
+	for (x = startX; x < right; x += bm.bmWidth) {
+	    srcX = 0;
+	    destX = x;
+	    destWidth = bm.bmWidth;
+	    if (x < left) {
+		srcX = (left - x);
+		destWidth = bm.bmWidth - srcX;
+		destX = left;
+	    } 
+	    if ((destX + destWidth) > right) {
+		destWidth = (right - destX);
+	    }
+#ifdef notdef
+	    PurifyPrintf("drawing pattern (%d,%d,%d,%d) at %d,%d\n",
+		 srcX , srcY, destWidth, destHeight, destX, destY);
+#endif
+	    if (gc->fill_style == FillStippled) { /* With transparency. */
+		SetBkMode(hDC, OPAQUE);
+		SetTextColor(hDC, gc->background);
+		SetBkColor(hDC, gc->foreground);
+		BitBlt(hDC, destX, destY, destWidth, destHeight, memDC, 
+		       srcX, srcY, SRCINVERT);
+		SetTextColor(hDC, RGB(255,255,255));
+		SetBkColor(hDC, RGB(0,0,0));
+		BitBlt(hDC, destX, destY, destWidth, destHeight, maskDC, 
+		       srcX, srcY, SRCAND);
+		SetTextColor(hDC, gc->background);
+		SetBkColor(hDC, gc->foreground);
+		BitBlt(hDC, destX, destY, destWidth, destHeight, memDC, 
+		       srcX, srcY, SRCINVERT);
+	    } else if (gc->fill_style == FillOpaqueStippled) { /* Opaque. */
+		SetBkColor(hDC, gc->foreground);
+		SetTextColor(hDC, gc->background);
+	        BitBlt(hDC, destX, destY, destWidth, destHeight, memDC, 
+			srcX, srcY, SRCCOPY);
+	    }
+	}
+    }
+    SelectBitmap(memDC, oldBitmap);
+    if (maskDC != memDC) {
+	TkWinReleaseDrawableDC(mask, maskDC, &maskState);
+    }
+    DeleteDC(memDC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_EmulateXFillPolygon --
+ *
+ *	This differs from Tk's XFillPolygon in that it works around
+ *	deficencies in Windows 95/98: 
+ *		1. Stippling bitmap is limited to 8x8.
+ *		2. No tiling (with or without mask).
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------- 
+ */
+void
+Blt_EmulateXFillPolygon(
+    Display *display, 
+    Drawable drawable, 
+    GC gc,
+    XPoint *pointPtr, 
+    int nPoints, 
+    int shape, 
+    int mode) 
+{
+    HDC hDC;
+    HRGN hRgn;
+    POINT *p, *winPts, *endPtr;
+    Region2D bbox;
+    TkWinDCState state;
+    int fillMode;
+
+    if (drawable == None) {
+	return;
+    }
+
+    /* Determine the bounding box of the polygon. */
+    bbox.left = bbox.right = pointPtr->x;
+    bbox.top = bbox.bottom = pointPtr->y;
+    
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+
+    /* Allocate array of POINTS to create the polygon's path. */
+    winPts = Blt_Malloc(sizeof(POINT) * nPoints);
+    endPtr = winPts + nPoints;
+    for (p = winPts; p < endPtr; p++) {
+	if (pointPtr->x < bbox.left) {
+	    bbox.left = pointPtr->x;
+	} 
+	if (pointPtr->x > bbox.right) {
+	    bbox.right = pointPtr->x;
+	}
+	if (pointPtr->y < bbox.top) {
+	    bbox.top = pointPtr->y;
+	} 
+	if (pointPtr->y > bbox.bottom) {
+	    bbox.bottom = pointPtr->y;
+	}
+	p->x = pointPtr->x;
+	p->y = pointPtr->y;
+	pointPtr++;
+    }
+
+    SetROP2(hDC, tkpWinRopModes[gc->function]);
+    fillMode = (gc->fill_rule == EvenOddRule) ? ALTERNATE : WINDING;
+
+    if ((gc->fill_style == FillStippled) || 
+	(gc->fill_style == FillOpaqueStippled)) {
+	int width, height;
+
+	/* Points are offsets within the bounding box. */
+	for (p = winPts; p < endPtr; p++) {
+	    p->x -= bbox.left;
+	    p->y -= bbox.top;
+	}
+	/* Use the polygon as a clip path. */
+	LPtoDP(hDC, winPts, nPoints);
+	hRgn = CreatePolygonRgn(winPts, nPoints, fillMode);
+	SelectClipRgn(hDC, hRgn);
+	OffsetClipRgn(hDC, bbox.left, bbox.top);
+	
+	/* Tile the bounding box. */
+	width = bbox.right - bbox.left + 1;
+	height = bbox.bottom - bbox.top + 1;
+	StippleRegion(display, hDC, gc, bbox.left, bbox.top, width, height);
+	
+	SelectClipRgn(hDC, NULL);
+	DeleteRgn(hRgn);
+    } else {
+	HPEN oldPen;
+	HBRUSH oldBrush;
+
+	/* 
+	 * FIXME: Right now, we're assuming that it's solid or
+	 * stippled and ignoring tiling. I'll merge the bits from
+	 * Blt_TilePolygon later. 
+	 */
+	oldPen = SelectPen(hDC, GetStockObject(NULL_PEN));
+	oldBrush = SelectBrush(hDC, CreateSolidBrush(gc->foreground));
+	SetPolyFillMode(hDC, fillMode);
+	Polygon(hDC, winPts, nPoints);
+	SelectPen(hDC, oldPen);
+	DeleteBrush(SelectBrush(hDC, oldBrush));
+    }
+    Blt_Free(winPts);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+}
Index: trunk/kitgen/8.x/blt/win/bltWinImage.c
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWinImage.c	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWinImage.c	(revision 175)
@@ -0,0 +1,1303 @@
+
+/*
+ * bltWinImage.c --
+ *
+ *	This module implements image processing procedures for the BLT
+ *	toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+#include "bltImage.h"
+#include <X11/Xutil.h>
+
+#define CLAMP(c)	((((c) < 0.0) ? 0.0 : ((c) > 255.0) ? 255.0 : (c)))
+
+#define GetBit(x, y) \
+   srcBits[(srcBytesPerRow * (srcHeight - y - 1)) + (x>>3)] & (0x80 >> (x&7))
+#define SetBit(x, y) \
+   destBits[(destBytesPerRow * (destHeight - y - 1)) + (x>>3)] |= (0x80 >>(x&7))
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPixmap --
+ *
+ *      Converts a color image into a pixmap.
+ *
+ *	Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ *      The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorImageToPixmap(
+    Tcl_Interp *interp,
+    Tk_Window tkwin,
+    Blt_ColorImage image,
+    ColorTable *colorTablePtr)	/* Points to array of colormap indices */
+{
+    HDC pixmapDC;
+    TkWinDCState state;
+    Display *display;
+    int width, height, depth;
+    Pixmap pixmap;
+    register int x, y;
+    register Pix32 *srcPtr;
+    COLORREF rgb;
+
+    *colorTablePtr = NULL;
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+    display = Tk_Display(tkwin);
+    depth = Tk_Depth(tkwin);
+
+    pixmap = Tk_GetPixmap(display, Tk_WindowId(tkwin), width, height, depth);
+    pixmapDC = TkWinGetDrawableDC(display, pixmap, &state);
+
+    srcPtr = Blt_ColorImageBits(image);
+    for (y = 0; y < height; y++) {
+	for (x = 0; x < width; x++) {
+	    rgb = PALETTERGB(srcPtr->Red, srcPtr->Green, srcPtr->Blue);
+	    SetPixelV(pixmapDC, x, y, rgb);
+	    srcPtr++;
+	}
+    }
+    TkWinReleaseDrawableDC(pixmap, pixmapDC, &state);
+    return pixmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_ColorImageToPixmap2 --
+ *
+ *      Converts a color image into a pixmap.
+ *
+ *	Right now this only handles TrueColor visuals.
+ *
+ * Results:
+ *      The new pixmap is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ColorImageToPixmap2(
+    Display *display,
+    int depth,
+    Blt_ColorImage image,
+    ColorTable *colorTablePtr)	/* Points to array of colormap indices */
+{
+    BITMAP bm;
+    HBITMAP hBitmap;
+    TkWinBitmap *twdPtr;
+    int width, height;
+    register Pix32 *srcPtr;
+    register int x, y;
+    register unsigned char *destPtr;
+    unsigned char *bits;
+
+    *colorTablePtr = NULL;
+    width = Blt_ColorImageWidth(image);
+    height = Blt_ColorImageHeight(image);
+
+    /* 
+     * Copy the color image RGB data into the DIB. The DIB scanlines
+     * are stored bottom-to-top and the order of the RGB color
+     * components is BGR. Who says Win32 GDI programming isn't
+     * backwards?  
+     */
+    bits = Blt_Malloc(width * height * sizeof(unsigned char));
+    assert(bits);
+    srcPtr = Blt_ColorImageBits(image);    
+    for (y = height - 1; y >= 0; y--) {
+	destPtr = bits + (y * width);
+	for (x = 0; x < width; x++) {
+	    *destPtr++ = srcPtr->Blue;
+	    *destPtr++ = srcPtr->Green;
+	    *destPtr++ = srcPtr->Red;
+	    *destPtr++ = (unsigned char)-1;
+	    srcPtr++;
+	}
+    }
+    bm.bmType = 0;
+    bm.bmWidth = width;
+    bm.bmHeight = height;
+    bm.bmWidthBytes = width;
+    bm.bmPlanes = 1;
+    bm.bmBitsPixel = 32;
+    bm.bmBits = bits;
+    hBitmap = CreateBitmapIndirect(&bm);
+
+    /* Create a windows version of a drawable. */
+    twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+    assert(twdPtr);
+    twdPtr->type = TWD_BITMAP;
+    twdPtr->handle = hBitmap;
+    twdPtr->depth = depth;
+    twdPtr->colormap = DefaultColormap(display, DefaultScreen(display));
+    return (Pixmap)twdPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DrawableToColorImage --
+ *
+ *      Takes a snapshot of an X drawable (pixmap or window) and
+ *	converts it to a color image.
+ *
+ * Results:
+ *      Returns a color image of the drawable.  If an error occurred,
+ *	NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_DrawableToColorImage(
+    Tk_Window tkwin,
+    Drawable drawable,
+    int x, int y,
+    int width, int height,	/* Dimension of the drawable. */
+    double inputGamma)
+{
+    void *data;
+    BITMAPINFO info;
+    DIBSECTION ds;
+    HBITMAP hBitmap, oldBitmap;
+    HPALETTE hPalette;
+    HDC memDC;
+    unsigned char *srcArr;
+    register unsigned char *srcPtr;
+    HDC hDC;
+    TkWinDCState state;
+    register Pix32 *destPtr;
+    Blt_ColorImage image;
+    unsigned char lut[256];
+
+    hDC = TkWinGetDrawableDC(Tk_Display(tkwin), drawable, &state);
+
+    /* Create the intermediate drawing surface at window resolution. */
+    ZeroMemory(&info, sizeof(info));
+    info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    info.bmiHeader.biWidth = width;
+    info.bmiHeader.biHeight = height;
+    info.bmiHeader.biPlanes = 1;
+    info.bmiHeader.biBitCount = 32;
+    info.bmiHeader.biCompression = BI_RGB;
+    hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+    memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, hBitmap);
+
+    hPalette = Blt_GetSystemPalette();
+    if (hPalette != NULL) {
+	SelectPalette(hDC, hPalette, FALSE);
+	RealizePalette(hDC);
+	SelectPalette(memDC, hPalette, FALSE);
+	RealizePalette(memDC);
+    }
+    image = NULL;
+    /* Copy the window contents to the memory surface. */
+    if (!BitBlt(memDC, 0, 0, width, height, hDC, x, y, SRCCOPY)) {
+#ifdef notdef
+	PurifyPrintf("can't blit: %s\n", Blt_LastError());
+#endif
+	goto done;
+    }
+    if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+#ifdef notdef
+	PurifyPrintf("can't get object: %s\n", Blt_LastError());
+#endif
+	goto done;
+    }
+    srcArr = (unsigned char *)ds.dsBm.bmBits;
+    image = Blt_CreateColorImage(width, height);
+    destPtr = Blt_ColorImageBits(image);
+
+    {
+	register int i;
+	double value;
+
+	for (i = 0; i < 256; i++) {
+	    value = pow(i / 255.0, inputGamma) * 255.0 + 0.5;
+	    lut[i] = (unsigned char)CLAMP(value);
+	}
+    }
+
+    /* 
+     * Copy the DIB RGB data into the color image. The DIB scanlines
+     * are stored bottom-to-top and the order of the RGB color
+     * components is BGR. Who says Win32 GDI programming isn't
+     * backwards?  
+     */
+
+    for (y = height - 1; y >= 0; y--) {
+	srcPtr = srcArr + (y * ds.dsBm.bmWidthBytes);
+	for (x = 0; x < width; x++) {
+	    destPtr->Blue = lut[*srcPtr++];
+	    destPtr->Green = lut[*srcPtr++];
+	    destPtr->Red = lut[*srcPtr++];
+	    destPtr->Alpha = (unsigned char)-1;
+	    destPtr++;
+	    srcPtr++;
+	}
+    }
+  done:
+    DeleteBitmap(SelectBitmap(memDC, oldBitmap));
+    DeleteDC(memDC);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+    if (hPalette != NULL) {
+	DeletePalette(hPalette);
+    }
+    return image;
+}
+
+
+Pixmap
+Blt_PhotoImageMask(
+    Tk_Window tkwin,
+    Tk_PhotoImageBlock src)
+{
+    TkWinBitmap *twdPtr;
+    int offset, count;
+    register int x, y;
+    unsigned char *srcPtr;
+    int destBytesPerRow;
+    int destHeight;
+    unsigned char *destBits;
+
+    destBytesPerRow = ((src.width + 31) & ~31) / 8;
+    destBits = Blt_Calloc(src.height, destBytesPerRow);
+    destHeight = src.height;
+
+    offset = count = 0;
+    /* FIXME: figure out why this is so! */
+    for (y = src.height - 1; y >= 0; y--) {
+	srcPtr = src.pixelPtr + offset;
+	for (x = 0; x < src.width; x++) {
+	    if (srcPtr[src.offset[3]] == 0x00) {
+		SetBit(x, y);
+		count++;
+	    }
+	    srcPtr += src.pixelSize;
+	}
+	offset += src.pitch;
+    }
+    if (count > 0) {
+	HBITMAP hBitmap;
+	BITMAP bm;
+
+	bm.bmType = 0;
+	bm.bmWidth = src.width;
+	bm.bmHeight = src.height;
+	bm.bmWidthBytes = destBytesPerRow;
+	bm.bmPlanes = 1;
+	bm.bmBitsPixel = 1;
+	bm.bmBits = destBits;
+	hBitmap = CreateBitmapIndirect(&bm);
+
+	twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+	assert(twdPtr);
+	twdPtr->type = TWD_BITMAP;
+	twdPtr->handle = hBitmap;
+	twdPtr->depth = 1;
+	if (Tk_WindowId(tkwin) == None) {
+	    twdPtr->colormap = DefaultColormap(Tk_Display(tkwin), 
+			 DefaultScreen(Tk_Display(tkwin)));
+	} else {
+	    twdPtr->colormap = Tk_Colormap(tkwin);
+	}
+    } else {
+	twdPtr = NULL;
+    }
+    if (destBits != NULL) {
+	Blt_Free(destBits);
+    }
+    return (Pixmap)twdPtr;
+}
+
+Pixmap
+Blt_ColorImageMask(
+    Tk_Window tkwin,
+    Blt_ColorImage image)
+{
+    TkWinBitmap *twdPtr;
+    int count;
+    register int x, y;
+    Pix32 *srcPtr;
+    int destBytesPerRow;
+    int destWidth, destHeight;
+    unsigned char *destBits;
+
+    destWidth = Blt_ColorImageWidth(image);
+    destHeight = Blt_ColorImageHeight(image);
+    destBytesPerRow = ((destWidth + 31) & ~31) / 8;
+    destBits = Blt_Calloc(destHeight, destBytesPerRow);
+    count = 0;
+    srcPtr = Blt_ColorImageBits(image);
+    for (y = 0; y < destHeight; y++) {
+	for (x = 0; x < destWidth; x++) {
+	    if (srcPtr->Alpha == 0x00) {
+		SetBit(x, y);
+		count++;
+	    }
+	    srcPtr++;
+	}
+    }
+    if (count > 0) {
+	HBITMAP hBitmap;
+	BITMAP bm;
+
+	bm.bmType = 0;
+	bm.bmWidth = Blt_ColorImageWidth(image);
+	bm.bmHeight = Blt_ColorImageHeight(image);
+	bm.bmWidthBytes = destBytesPerRow;
+	bm.bmPlanes = 1;
+	bm.bmBitsPixel = 1;
+	bm.bmBits = destBits;
+	hBitmap = CreateBitmapIndirect(&bm);
+
+	twdPtr = Blt_Malloc(sizeof(TkWinBitmap));
+	assert(twdPtr);
+	twdPtr->type = TWD_BITMAP;
+	twdPtr->handle = hBitmap;
+	twdPtr->depth = 1;
+	if (Tk_WindowId(tkwin) == None) {
+	    twdPtr->colormap = DefaultColormap(Tk_Display(tkwin), 
+			 DefaultScreen(Tk_Display(tkwin)));
+	} else {
+	    twdPtr->colormap = Tk_Colormap(tkwin);
+	}
+    } else {
+	twdPtr = NULL;
+    }
+    if (destBits != NULL) {
+	Blt_Free(destBits);
+    }
+    return (Pixmap)twdPtr;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * Blt_RotateBitmap --
+ *
+ *	Creates a new bitmap containing the rotated image of the given
+ *	bitmap.  We also need a special GC of depth 1, so that we do
+ *	not need to rotate more than one plane of the bitmap.
+ *
+ *	Note that under Windows, monochrome bitmaps are stored
+ *	bottom-to-top.  This is why the right angle rotations 0/180
+ *	and 90/270 look reversed.
+ *
+ * Results:
+ *	Returns a new bitmap containing the rotated image.
+ *
+ * -----------------------------------------------------------------
+ */
+Pixmap
+Blt_RotateBitmap(
+    Tk_Window tkwin,
+    Pixmap srcBitmap,		/* Source bitmap to be rotated */
+    int srcWidth, 
+    int srcHeight,		/* Width and height of the source bitmap */
+    double theta,		/* Right angle rotation to perform */
+    int *destWidthPtr, 
+    int *destHeightPtr)
+{
+    Display *display;		/* X display */
+    Window root;		/* Root window drawable */
+    Pixmap destBitmap;
+    double rotWidth, rotHeight;
+    HDC hDC;
+    TkWinDCState state;
+    register int x, y;		/* Destination bitmap coordinates */
+    register int sx, sy;	/* Source bitmap coordinates */
+    unsigned long pixel;
+    HBITMAP hBitmap;
+    int result;
+    struct MonoBitmap {
+	BITMAPINFOHEADER bi;
+	RGBQUAD colors[2];
+    } mb;
+    int srcBytesPerRow, destBytesPerRow;
+    int destWidth, destHeight;
+    unsigned char *srcBits, *destBits;
+
+    display = Tk_Display(tkwin);
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+    Blt_GetBoundingBox(srcWidth, srcHeight, theta, &rotWidth, &rotHeight,
+	(Point2D *)NULL);
+
+    destWidth = (int)ceil(rotWidth);
+    destHeight = (int)ceil(rotHeight);
+    destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+    if (destBitmap == None) {
+	return None;		/* Can't allocate pixmap. */
+    }
+    srcBits = Blt_GetBitmapData(display, srcBitmap, srcWidth, srcHeight,
+	&srcBytesPerRow);
+    if (srcBits == NULL) {
+	OutputDebugString("Blt_GetBitmapData failed");
+	return None;
+    }
+    destBytesPerRow = ((destWidth + 31) & ~31) / 8;
+    destBits = Blt_Calloc(destHeight, destBytesPerRow);
+
+    theta = FMOD(theta, 360.0);
+    if (FMOD(theta, (double)90.0) == 0.0) {
+	int quadrant;
+
+	/* Handle right-angle rotations specially. */
+
+	quadrant = (int)(theta / 90.0);
+	switch (quadrant) {
+	case ROTATE_270:	/* 270 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sx = y;
+		for (x = 0; x < destWidth; x++) {
+		    sy = destWidth - x - 1;
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_180:		/* 180 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sy = destHeight - y - 1;
+		for (x = 0; x < destWidth; x++) {
+		    sx = destWidth - x - 1;
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_90:		/* 90 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		sx = destHeight - y - 1;
+		for (x = 0; x < destWidth; x++) {
+		    sy = x;
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_0:		/* 0 degrees */
+	    for (y = 0; y < destHeight; y++) {
+		for (x = 0; x < destWidth; x++) {
+		    pixel = GetBit(x, y);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	default:
+	    /* The calling routine should never let this happen. */
+	    break;
+	}
+    } else {
+	double radians, sinTheta, cosTheta;
+	double srcCX, srcCY;	/* Center of source rectangle */
+	double destCX, destCY;	/* Center of destination rectangle */
+	double tx, ty;
+	double rx, ry;		/* Angle of rotation for x and y coordinates */
+
+	radians = (theta / 180.0) * M_PI;
+	sinTheta = sin(radians), cosTheta = cos(radians);
+
+	/*
+	 * Coordinates of the centers of the source and destination rectangles
+	 */
+	srcCX = srcWidth * 0.5;
+	srcCY = srcHeight * 0.5;
+	destCX = destWidth * 0.5;
+	destCY = destHeight * 0.5;
+
+	/* Rotate each pixel of dest image, placing results in source image */
+
+	for (y = 0; y < destHeight; y++) {
+	    ty = y - destCY;
+	    for (x = 0; x < destWidth; x++) {
+
+		/* Translate origin to center of destination image */
+		tx = x - destCX;
+
+		/* Rotate the coordinates about the origin */
+		rx = (tx * cosTheta) - (ty * sinTheta);
+		ry = (tx * sinTheta) + (ty * cosTheta);
+
+		/* Translate back to the center of the source image */
+		rx += srcCX;
+		ry += srcCY;
+
+		sx = ROUND(rx);
+		sy = ROUND(ry);
+
+		/*
+		 * Verify the coordinates, since the destination image can be
+		 * bigger than the source
+		 */
+
+		if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) ||
+		    (sy < 0)) {
+		    continue;
+		}
+		pixel = GetBit(sx, sy);
+		if (pixel) {
+		    SetBit(x, y);
+		}
+	    }
+	}
+    }
+    hBitmap = ((TkWinDrawable *)destBitmap)->bitmap.handle;
+    ZeroMemory(&mb, sizeof(mb));
+    mb.bi.biSize = sizeof(BITMAPINFOHEADER);
+    mb.bi.biPlanes = 1;
+    mb.bi.biBitCount = 1;
+    mb.bi.biCompression = BI_RGB;
+    mb.bi.biWidth = destWidth;
+    mb.bi.biHeight = destHeight;
+    mb.bi.biSizeImage = destBytesPerRow * destHeight;
+    mb.colors[0].rgbBlue = mb.colors[0].rgbRed = mb.colors[0].rgbGreen = 0x0;
+    mb.colors[1].rgbBlue = mb.colors[1].rgbRed = mb.colors[1].rgbGreen = 0xFF;
+    hDC = TkWinGetDrawableDC(display, destBitmap, &state);
+    result = SetDIBits(hDC, hBitmap, 0, destHeight, (LPVOID)destBits, 
+	(BITMAPINFO *)&mb, DIB_RGB_COLORS);
+    TkWinReleaseDrawableDC(destBitmap, hDC, &state);
+    if (!result) {
+#if WINDEBUG
+	PurifyPrintf("can't setDIBits: %s\n", Blt_LastError());
+#endif
+	destBitmap = None;
+    }
+    if (destBits != NULL) {
+         Blt_Free(destBits);
+    }
+    if (srcBits != NULL) {
+         Blt_Free(srcBits);
+    }
+
+    *destWidthPtr = destWidth;
+    *destHeightPtr = destHeight;
+    return destBitmap;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleBitmap --
+ *
+ *	Creates a new scaled bitmap from another bitmap. 
+ *
+ * Results:
+ *	The new scaled bitmap is returned.
+ *
+ * Side Effects:
+ *	A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleBitmap(
+    Tk_Window tkwin,
+    Pixmap srcBitmap,
+    int srcWidth, 
+    int srcHeight, 
+    int destWidth, 
+    int destHeight)
+{
+    TkWinDCState srcState, destState;
+    HDC src, dest;
+    Pixmap destBitmap;
+    Window root;
+    Display *display;
+
+    /* Create a new bitmap the size of the region and clear it */
+
+    display = Tk_Display(tkwin);
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+    destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1);
+    if (destBitmap == None) {
+	return None;
+    }
+    src = TkWinGetDrawableDC(display, srcBitmap, &srcState);
+    dest = TkWinGetDrawableDC(display, destBitmap, &destState);
+
+    StretchBlt(dest, 0, 0, destWidth, destHeight, src, 0, 0,
+	srcWidth, srcHeight, SRCCOPY);
+
+    TkWinReleaseDrawableDC(srcBitmap, src, &srcState);
+    TkWinReleaseDrawableDC(destBitmap, dest, &destState);
+    return destBitmap;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ScaleRotateBitmapRegion --
+ *
+ *	Creates a scaled and rotated bitmap from a given bitmap.  The
+ *	caller also provides (offsets and dimensions) the region of
+ *	interest in the destination bitmap.  This saves having to
+ *	process the entire destination bitmap is only part of it is
+ *	showing in the viewport.
+ *
+ *	This uses a simple rotation/scaling of each pixel in the 
+ *	destination image.  For each pixel, the corresponding 
+ *	pixel in the source bitmap is used.  This means that 
+ *	destination coordinates are first scaled to the size of 
+ *	the rotated source bitmap.  These coordinates are then
+ *	rotated back to their original orientation in the source.
+ *
+ * Results:
+ *	The new rotated and scaled bitmap is returned.
+ *
+ * Side Effects:
+ *	A new pixmap is allocated. The caller must release this.
+ *
+ * -----------------------------------------------------------------------
+ */
+Pixmap
+Blt_ScaleRotateBitmapRegion(
+    Tk_Window tkwin,
+    Pixmap srcBitmap,		/* Source bitmap. */
+    unsigned int srcWidth, 
+    unsigned int srcHeight,	/* Size of source bitmap */
+    int regionX, 
+    int regionY,		/* Offset of region in virtual
+				 * destination bitmap. */
+    unsigned int regionWidth, 
+    unsigned int regionHeight,	/* Desire size of bitmap region. */
+    unsigned int virtWidth,		
+    unsigned int virtHeight,	/* Virtual size of destination bitmap. */
+    double theta)		/* Angle to rotate bitmap.  */
+{
+    Display *display;		/* X display */
+    HBITMAP hBitmap;
+    HDC hDC;
+    Pixmap destBitmap;
+    TkWinDCState state;
+    Window root;		/* Root window drawable */
+    double rotWidth, rotHeight;
+    double xScale, yScale;
+    int srcBytesPerRow, destBytesPerRow;
+    int destHeight;
+    int result;
+    register int sx, sy;	/* Source bitmap coordinates */
+    register int x, y;		/* Destination bitmap coordinates */
+    unsigned char *srcBits, *destBits;
+    unsigned long pixel;
+    struct MonoBitmap {
+	BITMAPINFOHEADER bi;
+	RGBQUAD colors[2];
+    } mb;
+
+    display = Tk_Display(tkwin);
+    root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+
+    /* Create a bitmap and image big enough to contain the rotated text */
+    destBitmap = Tk_GetPixmap(display, root, regionWidth, regionHeight, 1);
+    if (destBitmap == None) {
+	return None;		/* Can't allocate pixmap. */
+    }
+    srcBits = Blt_GetBitmapData(display, srcBitmap, srcWidth, srcHeight,
+	&srcBytesPerRow);
+    if (srcBits == NULL) {
+	OutputDebugString("Blt_GetBitmapData failed");
+	return None;
+    }
+    destBytesPerRow = ((regionWidth + 31) & ~31) / 8;
+    destBits = Blt_Calloc(regionHeight, destBytesPerRow);
+    destHeight = regionHeight;
+
+    theta = FMOD(theta, 360.0);
+    Blt_GetBoundingBox(srcWidth, srcHeight, theta, &rotWidth, &rotHeight,
+	       (Point2D *)NULL);
+    xScale = rotWidth / (double)virtWidth;
+    yScale = rotHeight / (double)virtHeight;
+
+    if (FMOD(theta, (double)90.0) == 0.0) {
+	int quadrant;
+
+	/* Handle right-angle rotations specifically */
+
+	quadrant = (int)(theta / 90.0);
+	switch (quadrant) {
+	case ROTATE_270:	/* 270 degrees */
+	    for (y = 0; y < (int)regionHeight; y++) {
+		sx = (int)(yScale * (double)(y+regionY));
+		for (x = 0; x < (int)regionWidth; x++) {
+		    sy = (int)(xScale *(double)(virtWidth - (x+regionX) - 1));
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_180:	/* 180 degrees */
+	    for (y = 0; y < (int)regionHeight; y++) {
+		sy = (int)(yScale * (double)(virtHeight - (y + regionY) - 1));
+		for (x = 0; x < (int)regionWidth; x++) {
+		    sx = (int)(xScale *(double)(virtWidth - (x+regionX) - 1));
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_90:		/* 90 degrees */
+	    for (y = 0; y < (int)regionHeight; y++) {
+		sx = (int)(yScale * (double)(virtHeight - (y + regionY) - 1));
+		for (x = 0; x < (int)regionWidth; x++) {
+		    sy = (int)(xScale * (double)(x + regionX));
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	case ROTATE_0:		/* 0 degrees */
+	    for (y = 0; y < (int)regionHeight; y++) {
+		sy = (int)(yScale * (double)(y + regionY));
+		for (x = 0; x < (int)regionWidth; x++) {
+		    sx = (int)(xScale * (double)(x + regionX));
+		    pixel = GetBit(sx, sy);
+		    if (pixel) {
+			SetBit(x, y);
+		    }
+		}
+	    }
+	    break;
+
+	default:
+	    /* The calling routine should never let this happen. */
+	    break;
+	}
+    } else {
+	double radians, sinTheta, cosTheta;
+	double scx, scy; 	/* Offset from the center of the
+				 * source rectangle. */
+	double rcx, rcy; 	/* Offset to the center of the
+				 * rotated rectangle. */
+	double tx, ty;		/* Translated coordinates from center */
+	double rx, ry;		/* Angle of rotation for x and y coordinates */
+
+	radians = (theta / 180.0) * M_PI;
+	sinTheta = sin(radians), cosTheta = cos(radians);
+
+	/*
+	 * Coordinates of the centers of the source and destination rectangles
+	 */
+	scx = srcWidth * 0.5;
+	scy = srcHeight * 0.5;
+	rcx = rotWidth * 0.5;
+	rcy = rotHeight * 0.5;
+
+	/* For each pixel of the destination image, transform back to the
+	 * associated pixel in the source image. */
+
+	for (y = 0; y < (int)regionHeight; y++) {
+	    ty = (yScale * (double)(y + regionY)) - rcy;
+	    for (x = 0; x < (int)regionWidth; x++) {
+
+		/* Translate origin to center of destination image. */
+		tx = (xScale * (double)(x + regionX)) - rcx;
+
+		/* Rotate the coordinates about the origin. */
+		rx = (tx * cosTheta) - (ty * sinTheta);
+		ry = (tx * sinTheta) + (ty * cosTheta);
+
+		/* Translate back to the center of the source image. */
+		rx += scx;
+		ry += scy;
+
+		sx = ROUND(rx);
+		sy = ROUND(ry);
+
+		/*
+		 * Verify the coordinates, since the destination image can be
+		 * bigger than the source.
+		 */
+
+		if ((sx >= (int)srcWidth) || (sx < 0) || 
+		    (sy >= (int)srcHeight) || (sy < 0)) {
+		    continue;
+		}
+		pixel = GetBit(sx, sy);
+		if (pixel) {
+		    SetBit(x, y);
+		}
+	    }
+	}
+    }
+    /* Write the rotated image into the destination bitmap. */
+    hBitmap = ((TkWinDrawable *)destBitmap)->bitmap.handle;
+    ZeroMemory(&mb, sizeof(mb));
+    mb.bi.biSize = sizeof(BITMAPINFOHEADER);
+    mb.bi.biPlanes = 1;
+    mb.bi.biBitCount = 1;
+    mb.bi.biCompression = BI_RGB;
+    mb.bi.biWidth = regionWidth;
+    mb.bi.biHeight = regionHeight;
+    mb.bi.biSizeImage = destBytesPerRow * regionHeight;
+    mb.colors[0].rgbBlue = mb.colors[0].rgbRed = mb.colors[0].rgbGreen = 0x0;
+    mb.colors[1].rgbBlue = mb.colors[1].rgbRed = mb.colors[1].rgbGreen = 0xFF;
+    hDC = TkWinGetDrawableDC(display, destBitmap, &state);
+    result = SetDIBits(hDC, hBitmap, 0, regionHeight, (LPVOID)destBits, 
+	(BITMAPINFO *)&mb, DIB_RGB_COLORS);
+    TkWinReleaseDrawableDC(destBitmap, hDC, &state);
+    if (!result) {
+#if WINDEBUG
+	PurifyPrintf("can't setDIBits: %s\n", Blt_LastError());
+#endif
+	destBitmap = None;
+    }
+    if (destBits != NULL) {
+         Blt_Free(destBits);
+    }
+    if (srcBits != NULL) {
+         Blt_Free(srcBits);
+    }
+    return destBitmap;
+}
+
+#ifdef notdef
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_BlendColorImage --
+ *
+ *      Takes a snapshot of an X drawable (pixmap or window) and
+ *	converts it to a color image.
+ *
+ * Results:
+ *      Returns a color image of the drawable.  If an error occurred,
+ *	NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_BlendColorImage(
+    Tk_Window tkwin,
+    Drawable drawable,
+    int width, int height,	/* Dimension of the drawable. */
+    Region2D *regionPtr)	/* Region to be snapped. */
+{
+    void *data;
+    BITMAPINFO info;
+    DIBSECTION ds;
+    HBITMAP hBitmap, oldBitmap;
+    HPALETTE hPalette;
+    HDC memDC;
+    unsigned char *srcArr;
+    register unsigned char *srcPtr;
+    HDC hDC;
+    TkWinDCState state;
+    register Pix32 *destPtr;
+    Blt_ColorImage image;
+    register int x, y;
+
+    if (regionPtr == NULL) {
+	regionPtr = Blt_SetRegion(0, 0, ColorImageWidth(image), 
+		ColorImageHeight(image), &region);
+    }
+    if (regionPtr->left < 0) {
+	regionPtr->left = 0;
+    }
+    if (regionPtr->right >= destWidth) {
+	regionPtr->right = destWidth - 1;
+    }
+    if (regionPtr->top < 0) {
+	regionPtr->top = 0;
+    }
+    if (regionPtr->bottom >= destHeight) {
+	regionPtr->bottom = destHeight - 1;
+    }
+    width = RegionWidth(regionPtr);
+    height = RegionHeight(regionPtr);
+
+    hDC = TkWinGetDrawableDC(display, drawable, &state);
+
+    /* Create the intermediate drawing surface at window resolution. */
+    ZeroMemory(&info, sizeof(info));
+    info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    info.bmiHeader.biWidth = width;
+    info.bmiHeader.biHeight = height;
+    info.bmiHeader.biPlanes = 1;
+    info.bmiHeader.biBitCount = 32;
+    info.bmiHeader.biCompression = BI_RGB;
+    hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+    memDC = CreateCompatibleDC(hDC);
+    oldBitmap = SelectBitmap(memDC, hBitmap);
+
+    hPalette = Blt_GetSystemPalette();
+    if (hPalette != NULL) {
+	SelectPalette(hDC, hPalette, FALSE);
+	RealizePalette(hDC);
+	SelectPalette(memDC, hPalette, FALSE);
+	RealizePalette(memDC);
+    }
+    image = NULL;
+    /* Copy the window contents to the memory surface. */
+    if (!BitBlt(memDC, 0, 0, width, height, hDC, regionPtr->left, 
+	regionPtr->top, SRCCOPY)) {
+#ifdef notdef
+	PurifyPrintf("can't blit: %s\n", Blt_LastError());
+#endif
+	goto done;
+    }
+    if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+#ifdef notdef
+	PurifyPrintf("can't get object: %s\n", Blt_LastError());
+#endif
+	goto done;
+    }
+    srcArr = (unsigned char *)ds.dsBm.bmBits;
+    image = Blt_CreateColorImage(width, height);
+    destPtr = Blt_ColorImageBits(image);
+
+    /* 
+     * Copy the DIB RGB data into the color image. The DIB scanlines
+     * are stored bottom-to-top and the order of the RGBA color
+     * components is BGRA. Who says Win32 GDI programming isn't
+     * backwards?  
+     */
+    for (y = height - 1; y >= 0; y--) {
+	srcPtr = srcArr + (y * ds.dsBm.bmWidthBytes);
+	for (x = 0; x < width; x++) {
+	    if (destPtr->Alpha > 0) {
+		/* Blend colorimage with background. */
+		destPtr->Blue = *srcPtr++;
+		destPtr->Green = *srcPtr++;
+		destPtr->Red = *srcPtr++;
+		destPtr->Alpha = (unsigned char)-1;
+		srcPtr++;
+	    }
+	    destPtr++;
+	}
+    }
+  done:
+    DeleteBitmap(SelectBitmap(memDC, oldBitmap));
+    DeleteDC(memDC);
+    TkWinReleaseDrawableDC(drawable, hDC, &state);
+    if (hPalette != NULL) {
+	DeletePalette(hPalette);
+    }
+    return image;
+}
+#endif
+
+#ifdef HAVE_IJL_H
+
+#include <ijl.h>
+
+Blt_ColorImage
+Blt_JPEGToColorImage(interp, fileName)
+    Tcl_Interp *interp;
+    char *fileName;
+{
+    JPEG_CORE_PROPERTIES jpgProps;
+    Blt_ColorImage image;
+
+    ZeroMemory(&jpgProps, sizeof(JPEG_CORE_PROPERTIES));
+    if(ijlInit(&jpgProps) != IJL_OK) {
+	Tcl_AppendResult(interp, "can't initialize Intel JPEG library",
+			 (char *)NULL);
+	return NULL;
+    }
+    jpgProps.JPGFile = fileName;
+    if (ijlRead(&jpgProps, IJL_JFILE_READPARAMS) != IJL_OK) {
+	Tcl_AppendResult(interp, "can't read JPEG file header from \"",
+			 fileName, "\" file.", (char *)NULL);
+	goto error;
+    }
+
+    // !dudnik: to fix bug case 584680, [OT:287A305B]
+    // Set the JPG color space ... this will always be
+    // somewhat of an educated guess at best because JPEG
+    // is "color blind" (i.e., nothing in the bit stream
+    // tells you what color space the data was encoded from).
+    // However, in this example we assume that we are
+    // reading JFIF files which means that 3 channel images
+    // are in the YCbCr color space and 1 channel images are
+    // in the Y color space.
+    switch(jpgProps.JPGChannels) {
+    case 1:
+	jpgProps.JPGColor = IJL_G;
+	jpgProps.DIBChannels = 4;
+	jpgProps.DIBColor = IJL_RGBA_FPX;
+	break;
+	
+    case 3:
+	jpgProps.JPGColor = IJL_YCBCR;
+	jpgProps.DIBChannels = 4;
+	jpgProps.DIBColor = IJL_RGBA_FPX;
+	break;
+
+    case 4:
+	jpgProps.JPGColor = IJL_YCBCRA_FPX;
+	jpgProps.DIBChannels = 4;
+	jpgProps.DIBColor = IJL_RGBA_FPX;
+	break;
+
+    default:
+	/* This catches everything else, but no color twist will be
+           performed by the IJL. */
+	jpgProps.DIBColor = (IJL_COLOR)IJL_OTHER;
+ 	jpgProps.JPGColor = (IJL_COLOR)IJL_OTHER;
+	jpgProps.DIBChannels = jpgProps.JPGChannels;
+	break;
+    }
+
+    jpgProps.DIBWidth    = jpgProps.JPGWidth;
+    jpgProps.DIBHeight   = jpgProps.JPGHeight;
+    jpgProps.DIBPadBytes = IJL_DIB_PAD_BYTES(jpgProps.DIBWidth, 
+					     jpgProps.DIBChannels);
+
+    image = Blt_CreateColorImage(jpgProps.JPGWidth, jpgProps.JPGHeight);
+
+    jpgProps.DIBBytes = (BYTE *)Blt_ColorImageBits(image);
+    if (ijlRead(&jpgProps, IJL_JFILE_READWHOLEIMAGE) != IJL_OK) {
+	Tcl_AppendResult(interp, "can't read image data from \"", fileName,
+		 "\"", (char *)NULL);
+	goto error;
+    }
+    if (ijlFree(&jpgProps) != IJL_OK) {
+	fprintf(stderr, "can't free Intel(R) JPEG library\n");
+    }
+    return image;
+
+ error:
+    ijlFree(&jpgProps);
+    if (image != NULL) {
+	Blt_FreeColorImage(image);
+    }
+    ijlFree(&jpgProps);
+    return NULL;
+} 
+
+#else 
+
+#ifdef HAVE_JPEGLIB_H
+
+#undef HAVE_STDLIB_H
+#undef EXTERN
+#ifdef WIN32
+#define XMD_H	1
+#endif
+#include "jpeglib.h"
+#include <setjmp.h>
+
+typedef struct {
+    struct jpeg_error_mgr pub;	/* "public" fields */
+    jmp_buf jmpBuf;
+    Tcl_DString dString;
+} ReaderHandler;
+
+static void ErrorProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+static void MessageProc _ANSI_ARGS_((j_common_ptr jpegInfo));
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+static void
+ErrorProc(jpgPtr)
+    j_common_ptr jpgPtr;
+{
+    ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+
+    (*handlerPtr->pub.output_message) (jpgPtr);
+    longjmp(handlerPtr->jmpBuf, 1);
+}
+
+static void
+MessageProc(jpgPtr)
+    j_common_ptr jpgPtr;
+{
+    ReaderHandler *handlerPtr = (ReaderHandler *)jpgPtr->err;
+    char buffer[JMSG_LENGTH_MAX];
+
+    /* Create the message and append it into the dynamic string. */
+    (*handlerPtr->pub.format_message) (jpgPtr, buffer);
+    Tcl_DStringAppend(&(handlerPtr->dString), " ", -1);
+    Tcl_DStringAppend(&(handlerPtr->dString), buffer, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_JPEGToColorImage --
+ *
+ *      Reads a JPEG file and converts it into a color image.
+ *
+ * Results:
+ *      The color image is returned.  If an error occured, such
+ *	as the designated file could not be opened, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_ColorImage
+Blt_JPEGToColorImage(interp, fileName)
+    Tcl_Interp *interp;
+    char *fileName;
+{
+    struct jpeg_decompress_struct jpg;
+    Blt_ColorImage image;
+    unsigned int imageWidth, imageHeight;
+    register Pix32 *destPtr;
+    ReaderHandler handler;
+    FILE *f;
+    JSAMPLE **readBuffer;
+    int row_stride;
+    register int i;
+    register JSAMPLE *bufPtr;
+
+    f = fopen(fileName, "rb");
+    if (f == NULL) {
+	Tcl_AppendResult(interp, "can't open \"", fileName, "\":",
+	    Tcl_PosixError(interp), (char *)NULL);
+	return NULL;
+    }
+    image = NULL;
+
+    /* Step 1: allocate and initialize JPEG decompression object */
+
+    /* We set up the normal JPEG error routines, then override error_exit. */
+    jpg.dct_method = JDCT_IFAST;
+    jpg.err = jpeg_std_error(&handler.pub);
+    handler.pub.error_exit = ErrorProc;
+    handler.pub.output_message = MessageProc;
+
+    Tcl_DStringInit(&handler.dString);
+    Tcl_DStringAppend(&handler.dString, "error reading \"", -1);
+    Tcl_DStringAppend(&handler.dString, fileName, -1);
+    Tcl_DStringAppend(&handler.dString, "\": ", -1);
+
+    if (setjmp(handler.jmpBuf)) {
+	jpeg_destroy_decompress(&jpg);
+	fclose(f);
+	Tcl_DStringResult(interp, &(handler.dString));
+	return NULL;
+    }
+    jpeg_create_decompress(&jpg);
+    jpeg_stdio_src(&jpg, f);
+
+    jpeg_read_header(&jpg, TRUE);	/* Step 3: read file parameters */
+
+    jpeg_start_decompress(&jpg);	/* Step 5: Start decompressor */
+    imageWidth = jpg.output_width;
+    imageHeight = jpg.output_height;
+    if ((imageWidth < 1) || (imageHeight < 1)) {
+	Tcl_AppendResult(interp, "bad JPEG image size", (char *)NULL);
+	fclose(f);
+	return NULL;
+    }
+    /* JSAMPLEs per row in output buffer */
+    row_stride = imageWidth * jpg.output_components;
+
+    /* Make a one-row-high sample array that will go away when done
+     * with image */
+    readBuffer = (*jpg.mem->alloc_sarray) ((j_common_ptr)&jpg, JPOOL_IMAGE, 
+	row_stride, 1);
+    image = Blt_CreateColorImage(imageWidth, imageHeight);
+    destPtr = Blt_ColorImageBits(image);
+
+    if (jpg.output_components == 1) {
+	while (jpg.output_scanline < imageHeight) {
+	    jpeg_read_scanlines(&jpg, readBuffer, 1);
+	    bufPtr = readBuffer[0];
+	    for (i = 0; i < (int)imageWidth; i++) {
+		destPtr->Red = destPtr->Green = destPtr->Blue = *bufPtr++;
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    } else {
+	while (jpg.output_scanline < imageHeight) {
+	    jpeg_read_scanlines(&jpg, readBuffer, 1);
+	    bufPtr = readBuffer[0];
+	    for (i = 0; i < (int)imageWidth; i++) {
+		destPtr->Red = *bufPtr++;
+		destPtr->Green = *bufPtr++;
+		destPtr->Blue = *bufPtr++;
+		destPtr->Alpha = (unsigned char)-1;
+		destPtr++;
+	    }
+	}
+    }
+    jpeg_finish_decompress(&jpg);	/* We can ignore the return value
+					 * since suspension is not
+					 * possible with the stdio data
+					 * source.  */
+    jpeg_destroy_decompress(&jpg);
+
+
+    /*  
+     * After finish_decompress, we can close the input file.  Here we
+     * postpone it until after no more JPEG errors are possible, so as
+     * to simplify the setjmp error logic above.  (Actually, I don't
+     * think that jpeg_destroy can do an error exit, but why assume
+     * anything...)  
+     */
+    fclose(f);
+
+    /* 
+     * At this point you may want to check to see whether any corrupt-data
+     * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+     */
+    if (handler.pub.num_warnings > 0) {
+	Tcl_SetErrorCode(interp, "IMAGE", "JPEG", 
+		 Tcl_DStringValue(&(handler.dString)), (char *)NULL);
+    } else {
+	Tcl_SetErrorCode(interp, "NONE", (char *)NULL);
+    }
+    /*
+     * We're ready to call the Tk_Photo routines. They'll take the RGB
+     * array we've processed to build the Tk image of the JPEG.
+     */
+    Tcl_DStringFree(&(handler.dString));
+    return image;
+}
+
+#endif /* HAVE_JPEGLIB_H */
+#endif /* HAVE_IJL_H */
+
Index: trunk/kitgen/8.x/blt/win/bltWinPrnt.c
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWinPrnt.c	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWinPrnt.c	(revision 175)
@@ -0,0 +1,1588 @@
+
+/*
+ * bltWinPrnt.c --
+ *
+ *	This module implements Win32 printer access.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ */
+
+#include <bltInt.h>
+#include <bltHash.h>
+#ifndef NO_PRINTER
+#include <X11/Xutil.h>
+#undef Status
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#include <winspool.h>
+#endif /* _MSC_VER || __BORLANDC__ */
+
+/*
+  set pid [printer open name]
+  printer close $pid
+  printer write $pid $data
+  printer snap $pid .window
+  printer names
+  printer enum things
+  printer getattr $pid
+  printer setattr $pid
+
+  set pid [open ]
+  blt::printer open {\\alprint\2a211} p1
+  p1 getattr varName
+  p1 setattr varName
+  p1 write $data
+  .graph print p1
+  p1 snap .window
+  p1 close
+  blt::printer names
+  blt::printer emum things
+*/
+
+#define PRINTER_THREAD_KEY	"BLT Printer Data"
+
+typedef struct {
+    Blt_HashTable printerTable;	/* Hash table of printer structures keyed by 
+				 * the name of the printer. */
+    int nextId;
+} PrinterInterpData;
+
+typedef struct {
+    int type;
+    HDC hDC;
+} PrintDrawable;
+
+typedef struct {
+    Tcl_Interp *interp;
+    Tcl_Command cmdToken;	/* Token for vector's Tcl command. */
+    char *name;
+    char *fileName;
+    PrintDrawable drawable;
+    HANDLE hPrinter;
+    Blt_HashEntry *hashPtr;
+    Blt_HashTable *tablePtr;
+    char *driverName;
+    char *deviceName;
+    char *printerName;
+    char *docName;
+    char *portName;
+    DEVMODE *dmPtr;
+    int dmSize;
+} PrintQueue;
+
+typedef struct {
+    DWORD token;
+    char *string;
+} TokenString;
+
+static TokenString sizeTable[] =
+{
+ /* Letter 8 1/2 x 11 in */
+    { DMPAPER_LETTER, "Letter" },
+ /* Letter Small 8 1/2 x 11 in */
+    { DMPAPER_LETTERSMALL, "Letter Small" },
+ /* Tabloid 11 x 17 in */
+    { DMPAPER_TABLOID, "Tabloid" },
+ /* Ledger 17 x 11 in */
+    { DMPAPER_LEDGER, "Ledger" },
+ /* Legal 8 1/2 x 14 in */
+    { DMPAPER_LEGAL, "Legal" },
+ /* Statement 5 1/2 x 8 1/2 in */
+    { DMPAPER_STATEMENT, "Statement" },
+ /* Executive 7 1/4 x 10 1/2 in */
+    { DMPAPER_EXECUTIVE, "Executive" },
+ /* A3 297 x 420 mm */
+    { DMPAPER_A3, "A3" },
+ /* A4 210 x 297 mm */
+    { DMPAPER_A4, "A4" },
+ /* A4 Small 210 x 297 mm */
+    { DMPAPER_A4SMALL, "A4 Small" },
+ /* A5 148 x 210 mm */
+    { DMPAPER_A5, "A5" },
+ /* B4 (JIS) 250 x 354 */
+    { DMPAPER_B4, "B4 (JIS)" },
+ /* B5 (JIS) 182 x 257 mm */
+    { DMPAPER_B5, "B5 (JIS)" },
+ /* Folio 8 1/2 x 13 in */
+    { DMPAPER_FOLIO, "Folio" },
+ /* Quarto 215 x 275 mm */
+    { DMPAPER_QUARTO, "Quarto" },
+ /* 10x14 in */
+    { DMPAPER_10X14, "10x14" },
+ /* 11x17 in */
+    { DMPAPER_11X17, "11x17" },
+ /* Note 8 1/2 x 11 in */
+    { DMPAPER_NOTE, "Note" },
+ /* Envelope #9 3 7/8 x 8 7/8 */
+    { DMPAPER_ENV_9, "Envelope #9" },
+ /* Envelope #10 4 1/8 x 9 1/2 */
+    { DMPAPER_ENV_10, "Envelope #10" },
+ /* Envelope #11 4 1/2 x 10 3/8 */
+    { DMPAPER_ENV_11, "Envelope #11" },
+ /* Envelope #12 4 \276 x 11 */
+    { DMPAPER_ENV_12, "Envelope #12" },
+ /* Envelope #14 5 x 11 1/2 */
+    { DMPAPER_ENV_14, "Envelope #14" },
+ /* C size sheet */
+    { DMPAPER_CSHEET, "C size sheet" },
+ /* D size sheet */
+    { DMPAPER_DSHEET, "D size sheet" },
+ /* E size sheet */
+    { DMPAPER_ESHEET, "E size sheet" },
+ /* Envelope DL 110 x 220mm */
+    { DMPAPER_ENV_DL, "Envelope DL" },
+ /* Envelope C5 162 x 229 mm */
+    { DMPAPER_ENV_C5, "Envelope C5" },
+ /* Envelope C3  324 x 458 mm */
+    { DMPAPER_ENV_C3, "Envelope C3" },
+ /* Envelope C4  229 x 324 mm */
+    { DMPAPER_ENV_C4, "Envelope C4" },
+ /* Envelope C6  114 x 162 mm */
+    { DMPAPER_ENV_C6, "Envelope C6" },
+ /* Envelope C65 114 x 229 mm */
+    { DMPAPER_ENV_C65, "Envelope C65" },
+ /* Envelope B4  250 x 353 mm */
+    { DMPAPER_ENV_B4, "Envelope B4" },
+ /* Envelope B5  176 x 250 mm */
+    { DMPAPER_ENV_B5, "Envelope B5" },
+ /* Envelope B6  176 x 125 mm */
+    { DMPAPER_ENV_B6, "Envelope B6" },
+ /* Envelope 110 x 230 mm */
+    { DMPAPER_ENV_ITALY, "Envelope Italy" },
+ /* Env Monarch 3 7/8 x 7 1/2 in */
+    { DMPAPER_ENV_MONARCH, "Envelope Monarch" },
+ /* 6 3/4 Envelope 3 5/8 x 6 1/2 in */
+    { DMPAPER_ENV_PERSONAL, "6 3/4 Envelope" },
+ /* US Std Fanfold 14 7/8 x 11 in */
+    { DMPAPER_FANFOLD_US, "US Std Fanfold" },
+ /* German Std Fanfold 8 1/2 x 12 in */
+    { DMPAPER_FANFOLD_STD_GERMAN, "German Std Fanfold" },
+ /* German Legal Fanfold 8 1/2 x 13 in */
+    { DMPAPER_FANFOLD_LGL_GERMAN, "German Legal Fanfold" },
+ /* B4 (ISO) 250 x 353 mm */
+    { DMPAPER_ISO_B4, "ISOB4" },
+ /* Japanese Postcard 100 x 148 mm */
+    { DMPAPER_JAPANESE_POSTCARD, "Postcard (JIS)" },
+ /* 9 x 11 in */
+    { DMPAPER_9X11, "9x11" },
+ /* 10 x 11 in */
+    { DMPAPER_10X11, "10x11" },
+ /* 15 x 11 in */
+    { DMPAPER_15X11, "15x11" },
+ /* Envelope Invite 220 x 220 mm */
+    { DMPAPER_ENV_INVITE, "Envelope Invite" },
+ /* Letter Extra 9 \275 x 12 in */
+    { DMPAPER_LETTER_EXTRA, "Letter Extra" },
+ /* Legal Extra 9 \275 x 15 in */
+    { DMPAPER_LEGAL_EXTRA, "Legal Extra" },
+ /* Tabloid Extra 11.69 x 18 in */
+    { DMPAPER_TABLOID_EXTRA, "Tabloid Extra" },
+ /* A4 Extra 9.27 x 12.69 in */
+    { DMPAPER_A4_EXTRA, "A4 Extra" },
+ /* Letter Transverse 8 \275 x 11 in */
+    { DMPAPER_LETTER_TRANSVERSE, "Letter Transverse" },
+ /* A4 Transverse 210 x 297 mm */
+    { DMPAPER_A4_TRANSVERSE, "A4 Transverse" },
+ /* Letter Extra Transverse 9\275 x 12 in */
+    { DMPAPER_LETTER_EXTRA_TRANSVERSE, "Letter Extra Transverse" },
+ /* SuperA/SuperA/A4 227 x 356 mm */
+    { DMPAPER_A_PLUS, "Super A Plus" },
+ /* SuperB/SuperB/A3 305 x 487 mm */
+    { DMPAPER_B_PLUS, "Super B Plus" },
+ /* Letter Plus 8.5 x 12.69 in */
+    { DMPAPER_LETTER_PLUS, "Letter Plus" },
+ /* A4 Plus 210 x 330 mm */
+    { DMPAPER_A4_PLUS, "A4 Plus" },
+ /* A5 Transverse 148 x 210 mm */
+    { DMPAPER_A5_TRANSVERSE, "A5 Transverse" },
+ /* B5 (JIS) Transverse 182 x 257 mm */
+    { DMPAPER_B5_TRANSVERSE, "B5 Transverse" },
+ /* A3 Extra 322 x 445 mm */
+    { DMPAPER_A3_EXTRA, "A3 Extra" },
+ /* A5 Extra 174 x 235 mm */
+    { DMPAPER_A5_EXTRA, "A5 Extra" },
+ /* B5 (ISO) Extra 201 x 276 mm */
+    { DMPAPER_B5_EXTRA, "B5 Extra" },
+ /* A2 420 x 594 mm */
+    { DMPAPER_A2, "A2" },
+ /* A3 Transverse 297 x 420 mm */
+    { DMPAPER_A3_TRANSVERSE, "A3 Transverse" },
+ /* A3 Extra Transverse 322 x 445 mm   */
+    { DMPAPER_A3_EXTRA_TRANSVERSE, "A3 Extra Transverse" },
+    { 0, NULL }
+};
+
+static TokenString statusTable[] =
+{
+    { PRINTER_STATUS_BUSY, "Busy" },
+    { PRINTER_STATUS_DOOR_OPEN, "Door Open" },
+    { PRINTER_STATUS_ERROR, "Error" },
+    { PRINTER_STATUS_INITIALIZING, "Initializing" },
+    { PRINTER_STATUS_IO_ACTIVE, "IO Active" },
+    { PRINTER_STATUS_MANUAL_FEED, "Manual Feed" },
+    { PRINTER_STATUS_NOT_AVAILABLE, "Not Available" },
+    { PRINTER_STATUS_NO_TONER, "No Toner" },
+    { PRINTER_STATUS_OFFLINE, "Offline" },
+    { PRINTER_STATUS_OUTPUT_BIN_FULL, "Bin Full" },
+    { PRINTER_STATUS_OUT_OF_MEMORY, "Out Of Memory" },
+    { PRINTER_STATUS_PAGE_PUNT, "Page Punt" },
+    { PRINTER_STATUS_PAPER_JAM, "Paper Jam" },
+    { PRINTER_STATUS_PAPER_OUT, "Paper Out" },
+    { PRINTER_STATUS_PAPER_PROBLEM, "Paper Problem" },
+    { PRINTER_STATUS_PAUSED, "Paused" },
+    { PRINTER_STATUS_PENDING_DELETION, "Pending Deletion" },
+    { PRINTER_STATUS_POWER_SAVE, "Power Save" },
+    { PRINTER_STATUS_PRINTING, "Printing" },
+    { PRINTER_STATUS_PROCESSING, "Processing" },
+    { PRINTER_STATUS_SERVER_UNKNOWN, "Server Unknown" },
+    { PRINTER_STATUS_TONER_LOW, "Toner Low" },
+    { PRINTER_STATUS_USER_INTERVENTION, "User Intervention" },
+    { PRINTER_STATUS_WAITING, "Waiting" },
+    { PRINTER_STATUS_WARMING_UP, "Warming Up" },
+    { 0, NULL }
+};
+
+static TokenString attributeTable[] =
+{
+    { PRINTER_ATTRIBUTE_DEFAULT, "Default" },
+    { PRINTER_ATTRIBUTE_DIRECT, "Direct" },
+    { PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST, "Do Complete First" },
+    { PRINTER_ATTRIBUTE_ENABLE_BIDI, "Enable BIDI" },
+    { PRINTER_ATTRIBUTE_ENABLE_DEVQ, "Enable Devq" },
+    { PRINTER_ATTRIBUTE_HIDDEN, "Hidden" },
+    { PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS, "Keep Printed Jobs" },
+    { PRINTER_ATTRIBUTE_LOCAL, "Local" },
+    { PRINTER_ATTRIBUTE_NETWORK, "Network" },
+    { PRINTER_ATTRIBUTE_QUEUED, "Queued" },
+    { PRINTER_ATTRIBUTE_RAW_ONLY, "Raw Only" },
+    { PRINTER_ATTRIBUTE_SHARED, "Shared" },
+    { PRINTER_ATTRIBUTE_WORK_OFFLINE, "Offline" },
+    { 0, NULL }
+};
+
+static TokenString binTable[] =
+{
+    { DMBIN_UPPER, "Upper" },
+    { DMBIN_LOWER, "Lower" },
+    { DMBIN_MIDDLE, "Middle" },
+    { DMBIN_MANUAL, "Manual" },
+    { DMBIN_ENVELOPE, "Envelope" },
+    { DMBIN_ENVMANUAL, "Envelope Manual" },
+    { DMBIN_AUTO, "Automatic" },
+    { DMBIN_TRACTOR, "Tractor" },
+    { DMBIN_SMALLFMT, "Small Format" },
+    { DMBIN_LARGEFMT, "Large Format" },
+    { DMBIN_LARGECAPACITY, "Large Capacity" },
+    { DMBIN_CASSETTE, "Cassette" },
+    { DMBIN_FORMSOURCE, "Form Source" },
+    { 0, NULL }
+};
+
+static TokenString orientationTable[] =
+{
+    { DMORIENT_PORTRAIT, "Portrait" },
+    { DMORIENT_LANDSCAPE, "Landscape" },
+    { 0, NULL }
+};
+
+static TokenString qualityTable[] =
+{
+    { DMRES_HIGH, "High" },
+    { DMRES_MEDIUM, "Medium" },
+    { DMRES_LOW, "Low" },
+    { DMRES_DRAFT, "Draft" },
+    { 0, NULL }
+};
+
+static TokenString colorTable[] =
+{
+    { DMCOLOR_COLOR, "Color" },
+    { DMCOLOR_MONOCHROME, "Monochrome" },
+    { 0, NULL }
+};
+
+static TokenString duplexTable[] =
+{
+    { DMDUP_SIMPLEX, "Simplex" },
+    { DMDUP_HORIZONTAL, "Horizontal" },
+    { DMDUP_VERTICAL, "Vertical" },
+    { 0, NULL }
+};
+
+static TokenString ttOptionTable[] =
+{
+    { DMTT_BITMAP, "Bitmap" },
+    { DMTT_DOWNLOAD, "Download" },
+    { DMTT_SUBDEV, "Substitute Device" },
+    { DMTT_DOWNLOAD_OUTLINE, "Download Outline" },
+    { 0, NULL }
+};
+
+static Tcl_ObjCmdProc PrinterCmd;
+static Tcl_InterpDeleteProc PrinterInterpDeleteProc;
+
+void
+Blt_GetPrinterScale(HDC printerDC, double *xRatioPtr, double *yRatioPtr)
+{
+    double xScreen, yScreen;
+    double xPrinter, yPrinter;
+    HDC screenDC;
+
+    xPrinter = (double)GetDeviceCaps(printerDC, LOGPIXELSX);
+    yPrinter = (double)GetDeviceCaps(printerDC, LOGPIXELSY);
+    screenDC = GetDC(NULL);
+    xScreen = (double)GetDeviceCaps(screenDC, LOGPIXELSX);
+    yScreen = (double)GetDeviceCaps(screenDC, LOGPIXELSY);
+    ReleaseDC(NULL, screenDC);
+    *xRatioPtr = (xPrinter / xScreen);
+    *yRatioPtr = (yPrinter / yScreen);
+}
+
+static PrinterInterpData *
+GetPrinterInterpData(Tcl_Interp *interp)
+{
+    PrinterInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (PrinterInterpData *)
+	Tcl_GetAssocData(interp, PRINTER_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+	dataPtr = Blt_Malloc(sizeof(PrinterInterpData));
+	dataPtr->nextId = 0;
+	assert(dataPtr);
+	Tcl_SetAssocData(interp, PRINTER_THREAD_KEY, PrinterInterpDeleteProc,
+		dataPtr);
+	Blt_InitHashTable(&dataPtr->printerTable, BLT_STRING_KEYS);
+    }
+    return dataPtr;
+}
+
+static int
+GetQueue(
+    Tcl_Interp *interp,
+    const char *name,
+    PrintQueue **queuePtrPtr)
+{
+    Blt_HashEntry *hPtr;
+    PrinterInterpData *dataPtr;
+
+    dataPtr = GetPrinterInterpData(interp);
+    hPtr = Blt_FindHashEntry(&dataPtr->printerTable, name);
+    if (hPtr == NULL) {
+	Tcl_AppendResult(interp, "can't find printer \"", name, "\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    *queuePtrPtr = (PrintQueue *)Blt_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+static int
+GetQueueFromObj(
+    Tcl_Interp *interp,
+    Tcl_Obj *objPtr,
+    PrintQueue **queuePtrPtr)
+{
+    return GetQueue(interp, Tcl_GetString(objPtr), queuePtrPtr);
+}
+
+static void
+CloseQueue(
+    PrintQueue *queuePtr)
+{
+    ClosePrinter(queuePtr->hPrinter);
+    queuePtr->hPrinter = NULL;
+}
+
+static int
+OpenQueue(
+    Tcl_Interp *interp,
+    PrintQueue *queuePtr)
+{
+    PRINTER_DEFAULTS pd;
+    HANDLE hPrinter;
+
+    ZeroMemory(&pd, sizeof(pd));
+    pd.DesiredAccess = PRINTER_ALL_ACCESS;
+    if (!OpenPrinter(queuePtr->printerName, &hPrinter, &pd)) {
+        Tcl_AppendResult(interp, "can't open printer \"", 
+		queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	queuePtr->hPrinter = NULL;
+	return TCL_ERROR;
+    }
+    queuePtr->hPrinter = hPrinter;
+    return TCL_OK;
+}
+
+static HGLOBAL
+GetQueueProperties(
+    PrintQueue *queuePtr,
+    DEVMODE **dmPtrPtr)
+{
+    HWND hWnd;
+    unsigned int dmSize;
+    HGLOBAL hMem;
+    DEVMODE *dmPtr;
+
+    hWnd = GetDesktopWindow();
+    dmSize = DocumentProperties(hWnd, queuePtr->hPrinter, 
+	queuePtr->printerName, NULL, NULL, 0);
+    if (dmSize == 0) {
+	Tcl_AppendResult(queuePtr->interp,
+		"can't get document properties for \"", 
+		queuePtr->printerName,
+		"\": ", Blt_LastError(), (char *)NULL);
+	return NULL;
+    }
+    hMem = GlobalAlloc(GHND, dmSize);
+    dmPtr = (DEVMODE *)GlobalLock(hMem);
+    if (!DocumentProperties(hWnd, queuePtr->hPrinter, queuePtr->printerName, 
+	dmPtr, NULL, DM_OUT_BUFFER)) {
+	Tcl_AppendResult(queuePtr->interp,
+		"can't allocate document properties for \"",
+		queuePtr->printerName, "\": ", Blt_LastError(), 
+		(char *)NULL);
+	GlobalUnlock(hMem);
+	GlobalFree(hMem);
+	return NULL;
+    }
+    *dmPtrPtr = dmPtr;
+    queuePtr->dmSize = dmSize;
+    return hMem;
+}
+
+static int
+SetQueueProperties(
+    Tcl_Interp *interp, 
+    PrintQueue *queuePtr,
+    DEVMODE *dmPtr)
+{
+    HWND hWnd;
+    int result;
+
+    hWnd = GetDesktopWindow();
+    result = DocumentProperties(hWnd, queuePtr->hPrinter, 
+	queuePtr->printerName, dmPtr, dmPtr, DM_IN_BUFFER | DM_OUT_BUFFER);
+    if (result == 0) {
+	Tcl_AppendResult(interp, "can't set document properties for \"", 
+	    queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    } 
+    if (queuePtr->dmPtr != NULL) {
+	Blt_Free(queuePtr->dmPtr);
+    }
+    queuePtr->dmPtr = Blt_Malloc(queuePtr->dmSize);
+    *queuePtr->dmPtr = *dmPtr;
+    return TCL_OK;
+}
+
+static void
+DestroyQueue(PrintQueue *queuePtr)
+{
+    if (queuePtr->drawable.hDC != NULL) {
+	DeleteDC(queuePtr->drawable.hDC);
+    }
+    if (queuePtr->printerName != NULL) {
+	Blt_Free(queuePtr->printerName);
+    }
+    if (queuePtr->deviceName != NULL) {
+	Blt_Free(queuePtr->deviceName);
+    }
+    if (queuePtr->portName != NULL) {
+	Blt_Free(queuePtr->portName);
+    }
+    if (queuePtr->driverName != NULL) {
+	Blt_Free(queuePtr->driverName);
+    }
+    if (queuePtr->hashPtr != NULL) {
+	Blt_DeleteHashEntry(queuePtr->tablePtr, queuePtr->hashPtr);
+    }
+    if (queuePtr->dmPtr != NULL) {
+	Blt_Free(queuePtr->dmPtr);
+    }
+    Blt_Free(queuePtr);
+}
+
+static char *
+AttributesToString(DWORD attributes, Tcl_DString * resultPtr)
+{
+    register TokenString *p;
+
+    Tcl_DStringInit(resultPtr);
+    for (p = attributeTable; p->string != NULL; p++) {
+	if (attributes & p->token) {
+	    Tcl_DStringAppendElement(resultPtr, p->string);
+	}
+    }
+    return Tcl_DStringValue(resultPtr);
+}
+
+static char *
+StatusToString(DWORD status, Tcl_DString * resultPtr)
+{
+    register TokenString *p;
+
+    Tcl_DStringInit(resultPtr);
+    for (p = statusTable; p->string != NULL; p++) {
+	if (status & p->token) {
+	    Tcl_DStringAppendElement(resultPtr, p->string);
+	}
+    }
+    return Tcl_DStringValue(resultPtr);
+}
+
+static char *
+TokenToString(TokenString *table, DWORD token)
+{
+    register TokenString *p;
+
+    for (p = table; p->string != NULL; p++) {
+	if (token == p->token) {
+	    return p->string;
+	}
+    }
+    return "???";
+}
+
+static DWORD
+StringToToken(TokenString * table, char *string)
+{
+    register TokenString *p;
+    char c;
+
+    c = toupper(string[0]);
+    for (p = table; p->string != NULL; p++) {
+	if ((c == toupper(p->string[0])) &&
+	    (strcasecmp(string, p->string) == 0)) {
+	    return p->token;
+	}
+    }
+    return 0;
+}
+
+static void
+GetFormInfo(
+    Tcl_Interp *interp,
+    FORM_INFO_1 * infoArr,
+    int nForms,
+    char *varName)
+{
+    Tcl_DString dString;
+    register int i;
+
+    Tcl_DStringInit(&dString);
+    for (i = 0; i < nForms; i++) {
+	Tcl_DStringAppendElement(&dString, infoArr[i].pName);
+    }
+    Tcl_SetVar2(interp, varName, "EnumForms", Tcl_DStringValue(&dString),
+	TCL_LEAVE_ERR_MSG);
+    Tcl_DStringFree(&dString);
+}
+
+
+static int
+GetPrinterAttributes(
+    Tcl_Interp *interp,		/* Interpreter context. */
+    PrintQueue *queuePtr,
+    Tcl_Obj *objPtr)		/* Name of array variable to contain
+				 * printer device information. */
+{	
+    char *string;
+    Tcl_DString dString;
+    DEVMODE *dmPtr;
+    DWORD bytesNeeded;
+    HGLOBAL hMem1, hMem2;
+    PRINTER_INFO_2* pi2Ptr;
+    LPVOID buffer;
+    int result = TCL_ERROR;
+    char *varName;
+
+    if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    Tcl_DStringInit(&dString);
+    hMem2 = NULL;
+
+    GetPrinter(queuePtr->hPrinter, 2, NULL, 0, &bytesNeeded);
+
+    /* Windows 95/98 seems to only want locked memory. Allocating
+     * unlocked memory will sometimes crash the printer driver and
+     * therefore Windows itself.  */
+
+    hMem1 = GlobalAlloc(GHND, bytesNeeded);
+    if (hMem1 == NULL) {
+        Tcl_AppendResult(interp, "can't allocate memory for printer \"", 
+		queuePtr->name, "\": ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    buffer = (LPVOID)GlobalLock(hMem1);
+    if (!GetPrinter(queuePtr->hPrinter, 2, buffer, bytesNeeded, 
+	&bytesNeeded)) {
+        Tcl_AppendResult(interp, "can't get printer \"", queuePtr->name, "\": ",
+	    Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    hMem2 = GetQueueProperties(queuePtr, &dmPtr);
+    if (hMem2 == NULL) {
+        Tcl_AppendResult(interp, "can't allocate memory for printer \"", 
+		queuePtr->name, "\" properties: ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    pi2Ptr = (PRINTER_INFO_2 *)buffer;
+    varName = Tcl_GetString(objPtr);
+    Tcl_SetVar2(interp, varName, "ServerName", pi2Ptr->pServerName, 0);
+    Tcl_SetVar2(interp, varName, "PrinterName", pi2Ptr->pPrinterName, 0);
+    Tcl_SetVar2(interp, varName, "PortName", pi2Ptr->pPortName, 0);
+    Tcl_SetVar2(interp, varName, "DriverName", pi2Ptr->pDriverName, 0);
+    Tcl_SetVar2(interp, varName, "Comment", pi2Ptr->pComment, 0);
+    Tcl_SetVar2(interp, varName, "Location", pi2Ptr->pLocation, 0);
+    Tcl_SetVar2(interp, varName, "SepFile", pi2Ptr->pSepFile, 0);
+    Tcl_SetVar2(interp, varName, "PrintProcessor", pi2Ptr->pPrintProcessor, 0);
+    Tcl_SetVar2(interp, varName, "Datatype", pi2Ptr->pDatatype, 0);
+    Tcl_SetVar2(interp, varName, "Parameters", pi2Ptr->pParameters, 0);
+    Tcl_SetVar2(interp, varName, "Attributes",
+		AttributesToString(pi2Ptr->Attributes, &dString), 0);
+    Tcl_SetVar2(interp, varName, "Priority", Blt_Itoa(pi2Ptr->Priority), 0);
+    Tcl_SetVar2(interp, varName, "DefaultPriority",
+		Blt_Itoa(pi2Ptr->DefaultPriority), 0);
+    Tcl_SetVar2(interp, varName, "StartTime", Blt_Itoa(pi2Ptr->StartTime), 0);
+    Tcl_SetVar2(interp, varName, "UntilTime", Blt_Itoa(pi2Ptr->UntilTime), 0);
+    Tcl_SetVar2(interp, varName, "Status",
+		StatusToString(pi2Ptr->Status, &dString), 0);
+    Tcl_SetVar2(interp, varName, "Jobs", Blt_Itoa(pi2Ptr->cJobs), 0);
+    Tcl_SetVar2(interp, varName, "AveragePPM", Blt_Itoa(pi2Ptr->AveragePPM), 0);
+
+    if (dmPtr->dmFields & DM_ORIENTATION) {
+	Tcl_SetVar2(interp, varName, "Orientation",
+	    TokenToString(orientationTable, dmPtr->dmOrientation), 0);
+    }
+    if (dmPtr->dmFields & DM_PAPERSIZE) {
+	Tcl_SetVar2(interp, varName, "PaperSize",
+	    TokenToString(sizeTable, dmPtr->dmPaperSize), 0);
+    }
+    if (dmPtr->dmFields & DM_PAPERWIDTH) {
+	Tcl_SetVar2(interp, varName, "PaperWidth",
+	    Blt_Itoa(dmPtr->dmPaperWidth), 0);
+    }
+    if (dmPtr->dmFields & DM_PAPERLENGTH) {
+	Tcl_SetVar2(interp, varName, "PaperLength",
+	    Blt_Itoa(dmPtr->dmPaperLength), 0);
+    }
+    if (dmPtr->dmFields & DM_SCALE) {
+	Tcl_SetVar2(interp, varName, "Scale", Blt_Itoa(dmPtr->dmScale), 0);
+    }
+    if (dmPtr->dmFields & DM_COPIES) {
+	Tcl_SetVar2(interp, varName, "Copies", Blt_Itoa(dmPtr->dmCopies), 0);
+    }
+    if (dmPtr->dmFields & DM_DEFAULTSOURCE) {
+	Tcl_SetVar2(interp, varName, "DefaultSource",
+	    TokenToString(binTable, dmPtr->dmDefaultSource), 0);
+    }
+    if (dmPtr->dmFields & DM_PRINTQUALITY) {
+	if (dmPtr->dmPrintQuality < 0) {
+	    string = TokenToString(qualityTable, dmPtr->dmPrintQuality);
+	} else {
+	    string = Blt_Itoa(dmPtr->dmPrintQuality);
+	}
+	Tcl_SetVar2(interp, varName, "PrintQuality", string, 0);
+    }
+    if (dmPtr->dmFields & DM_COLOR) {
+	Tcl_SetVar2(interp, varName, "Color",
+	    TokenToString(colorTable, dmPtr->dmColor), 0);
+    }
+    if (dmPtr->dmFields & DM_DUPLEX) {
+	Tcl_SetVar2(interp, varName, "Duplex",
+	    TokenToString(duplexTable, dmPtr->dmDuplex), 0);
+    }
+    if (dmPtr->dmFields & DM_YRESOLUTION) {
+	Tcl_SetVar2(interp, varName, "YResolution",
+	    Blt_Itoa(dmPtr->dmYResolution), 0);
+    }
+    if (dmPtr->dmFields & DM_TTOPTION) {
+	Tcl_SetVar2(interp, varName, "TTOption",
+	    TokenToString(ttOptionTable, dmPtr->dmTTOption), 0);
+    }
+    if (dmPtr->dmFields & DM_COLLATE) {
+	if (dmPtr->dmCollate == DMCOLLATE_TRUE) {
+	    string = "true";
+	} else if (dmPtr->dmCollate == DMCOLLATE_FALSE) {
+	    string = "false";
+	} else {
+	    string = "???";
+	}
+	Tcl_SetVar2(interp, varName, "Collate", string, 0);
+    }
+    if (dmPtr->dmFields & DM_FORMNAME) {
+	Tcl_SetVar2(interp, varName, "FormName", dmPtr->dmFormName, 0);
+    }
+    Tcl_SetVar2(interp, varName, "OutputFile", dmPtr->dmDeviceName, 0);
+    result = TCL_OK;
+
+ error:
+    Tcl_DStringFree(&dString);
+    CloseQueue(queuePtr);
+    if (hMem1 != NULL) {
+	GlobalUnlock(hMem1);
+	GlobalFree(hMem1);
+    }
+    if (hMem2 != NULL) {
+	GlobalUnlock(hMem2);
+	GlobalFree(hMem2);
+    }
+    return result;
+}
+
+static int
+SetQueueAttributes(
+    Tcl_Interp *interp,
+    PrintQueue *queuePtr,
+    Tcl_Obj *objPtr)
+{
+    char *string;
+    DEVMODE *dmPtr;
+    int value;
+    HGLOBAL hMem;
+    int result;
+    char *varName;
+
+    if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    hMem = GetQueueProperties(queuePtr, &dmPtr);
+    CloseQueue(queuePtr);
+    if (hMem == NULL) {
+       return TCL_ERROR;
+    }
+    dmPtr->dmFields = 0;
+    varName = Tcl_GetString(objPtr);
+    string = (char *)Tcl_GetVar2(interp, varName, "Orientation", 0);
+    if (string != NULL) {
+	value = StringToToken(orientationTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_ORIENTATION;
+	    dmPtr->dmOrientation = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "PaperSize", 0);
+    if (string != NULL) {
+	value = StringToToken(sizeTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_PAPERSIZE;
+	    dmPtr->dmPaperSize = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "PaperWidth", 0);
+    if (string != NULL) {
+	if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_PAPERWIDTH;
+	    dmPtr->dmPaperWidth = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "PaperLength", 0);
+    if (string != NULL) {
+	if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_PAPERLENGTH;
+	    dmPtr->dmPaperLength = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "Scale", 0);
+    if (string != NULL) {
+	if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_SCALE;
+	    dmPtr->dmScale = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "Copies", 0);
+    if (string != NULL) {
+	if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_COPIES;
+	    dmPtr->dmCopies = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "DefaultSource", 0);
+    if (string != NULL) {
+	value = StringToToken(binTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_DEFAULTSOURCE;
+	    dmPtr->dmDefaultSource = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "PrintQuality", 0);
+    if (string != NULL) {
+	value = StringToToken(qualityTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_PRINTQUALITY;
+	    dmPtr->dmPrintQuality = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "Color", 0);
+    if (string != NULL) {
+	value = StringToToken(colorTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_COLOR;
+	    dmPtr->dmColor = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "Duplex", 0);
+    if (string != NULL) {
+	value = StringToToken(duplexTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_DUPLEX;
+	    dmPtr->dmDuplex = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "YResolution", 0);
+    if (string != NULL) {
+	if (Tcl_GetInt(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_YRESOLUTION;
+	    dmPtr->dmYResolution = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "TTOption", 0);
+    if (string != NULL) {
+	value = StringToToken(ttOptionTable, string);
+	if (value > 0) {
+	    dmPtr->dmFields |= DM_TTOPTION;
+	    dmPtr->dmTTOption = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "Collate", 0);
+    if (string != NULL) {
+	if (Tcl_GetBoolean(interp, string, &value) == TCL_OK) {
+	    dmPtr->dmFields |= DM_COLLATE;
+	    dmPtr->dmCollate = value;
+	}
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "OutputFile", 0);
+    if (string != NULL) {
+	if (queuePtr->fileName != NULL) {
+	    Blt_Free(queuePtr->fileName);
+	}
+	queuePtr->fileName = Blt_Strdup(string);
+    }
+    if (queuePtr->dmPtr != NULL) {
+	Blt_Free(queuePtr->dmPtr);
+    }
+    string = (char *)Tcl_GetVar2(interp, varName, "DocumentName", 0);
+    if (string != NULL) {
+	if (queuePtr->docName != NULL) {
+	    Blt_Free(queuePtr->docName);
+	}
+	queuePtr->docName = Blt_Strdup(string);
+    }
+    result = SetQueueProperties(interp, queuePtr, dmPtr);
+    GlobalUnlock(hMem);
+    GlobalFree(hMem);
+    CloseQueue(queuePtr);
+    return result;
+}
+
+/*ARGSUSED*/
+static int
+EnumOp(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    TokenString *p;
+    char c;
+    unsigned int length;
+    char *attr;
+
+    attr = Tcl_GetStringFromObj(objv[2], &length);
+    c = attr[0];
+    if ((c == 'p') && (strncmp(attr, "paper", length) == 0)) {
+	p = sizeTable;
+    } else if ((c == 'q') && (strncmp(attr, "quality", length) == 0)) {
+	p = qualityTable;
+    } else if ((c == 'b') && (strncmp(attr, "bin", length) == 0)) {
+	p = binTable;
+    } else if ((c == 'o') && (strncmp(attr, "orientation", length) == 0)) {
+	p = orientationTable;
+    } else if ((c == 'c') && (strncmp(attr, "color", length) == 0)) {
+	p = colorTable;
+    } else if ((c == 'd') && (strncmp(attr, "duplex", length) == 0)) {
+	p = duplexTable;
+    } else if ((c == 't') && (strncmp(attr, "ttoption", length) == 0)) {
+	p = ttOptionTable;
+    } else {
+	Tcl_AppendResult(interp, "bad enumeration field \"", attr, 
+"\": should be \"paper\", \"quality\", \"bin\", \"orientation\", \"color\", \"duplex\", or \"ttoption\"",
+	    (char *)NULL);
+	return TCL_ERROR;
+    }
+    for ( /*empty*/ ; p->string != NULL; p++) {
+	Tcl_AppendElement(interp, p->string);
+    }
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+OpenOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    PrinterInterpData *dataPtr = clientData;
+    PrintQueue *queuePtr;
+    LPVOID buffer;
+    PRINTER_INFO_2* pi2Ptr;
+    DWORD bytesNeeded;
+    int isNew;
+    Blt_HashEntry *hPtr;
+    HANDLE hMem;
+    char *name;
+
+    name = Tcl_GetString(objv[2]);
+    hPtr = Blt_CreateHashEntry(&dataPtr->printerTable, name, &isNew);
+    if (isNew) {
+	queuePtr = Blt_Calloc(1, sizeof(PrintQueue));
+	queuePtr->name = Blt_GetHashKey(&dataPtr->printerTable, hPtr);
+	queuePtr->interp = interp;
+	Tcl_SetResult(interp, name, TCL_VOLATILE);
+	Blt_SetHashValue(hPtr, queuePtr);
+	queuePtr->hashPtr = hPtr;
+	queuePtr->tablePtr = &dataPtr->printerTable;
+	queuePtr->printerName = Blt_Strdup(name);
+    } else {
+	Tcl_AppendResult(interp, "printer \"", name, "\" is already open",
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	DestroyQueue(queuePtr);
+	return TCL_ERROR;
+    }
+    /* Call the first time to determine the amount of memory needed. */
+    GetPrinter(queuePtr->hPrinter, 2, NULL, 0, &bytesNeeded);
+    if ((bytesNeeded == 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+	Tcl_AppendResult(interp, "can't get size of attribute buffer for \"",
+			 name, "\": ", Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    /* Allocate a buffer to contain all printer information. */
+    hMem = GlobalAlloc(GHND, bytesNeeded);
+    if (hMem == NULL) {
+	return TCL_ERROR;
+    }
+    buffer = (LPVOID)GlobalLock(hMem);
+
+    /* And call the again to actually get the printer. */
+    if (!GetPrinter(queuePtr->hPrinter, 2, buffer, bytesNeeded, 
+		    &bytesNeeded)) {
+	Tcl_AppendResult(interp, "can't get printer attributes for \"",
+	    name, "\": ", Blt_LastError(), (char *)NULL);
+        GlobalUnlock(hMem);
+        GlobalFree(hMem);
+	return TCL_ERROR;
+    }
+    pi2Ptr = (PRINTER_INFO_2 *)buffer;
+    if (pi2Ptr->pDevMode != NULL) {
+	queuePtr->deviceName = Blt_Strdup(pi2Ptr->pDevMode->dmDeviceName);
+    }
+    queuePtr->driverName = Blt_Strdup(pi2Ptr->pDriverName);
+    /*
+    queuePtr->printerName = Blt_Strdup(pi2Ptr->pPrinterName);
+    */
+    queuePtr->portName = Blt_Strdup(pi2Ptr->pPortName);
+    GlobalUnlock(hMem);
+    GlobalFree(hMem);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+NamesOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)	/* Not used. */
+{
+    DWORD nPrinters, bytesNeeded;
+    int elemSize, level;
+    unsigned char *buffer;
+    int result, flags;
+    HANDLE hMem;
+
+    if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	level = 4;
+	elemSize = sizeof(PRINTER_INFO_4);
+	flags = PRINTER_ENUM_NAME;
+    } else {
+	level = 5;
+	elemSize = sizeof(PRINTER_INFO_5);
+	flags = PRINTER_ENUM_LOCAL;
+    }
+    result = EnumPrinters(
+	flags,			/* Flags */
+	NULL,			/* Printer name */
+	level,			/* Information level: 1, 2, 4, or 5 */
+	NULL,			/* Array of returned information */
+	0,			/* Size of array */
+	&bytesNeeded,		/* Size needed for array */
+	&nPrinters);		/* Number of structures returned */
+
+    if ((!result) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+	Tcl_AppendResult(interp, "can't enumerate printers (memory alloc): ",
+	    Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    hMem = GlobalAlloc(GHND, bytesNeeded);
+    buffer = (unsigned char *)GlobalLock(hMem);
+
+    result = EnumPrinters(
+	flags,			/* Flags */
+	NULL,			/* Printer name */
+	level,			/* Information level: 1, 2, 4, or 5 */
+	buffer,			/* Array of returned information */
+	bytesNeeded,		/* Size of array */
+	&bytesNeeded,		/* Size needed for array */
+	&nPrinters);		/* Number of structures returned */
+
+    if (!result) {
+	Tcl_AppendResult(interp, "can't enumerate printers: ",
+	    Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (objc > 2) {	
+	register unsigned int i;
+	char *pattern;
+	char *p;
+
+	p = buffer;
+        pattern = Tcl_GetString(objv[2]);
+	for (i = 0; i < nPrinters; i++) {
+	    if (Tcl_StringMatch(p, pattern)) {
+		Tcl_AppendElement(interp, *(char **)p);
+	    }
+	    p += elemSize;
+	}
+    } else {
+	register unsigned int i;
+	char *p;
+
+	p = buffer;
+	for (i = 0; i < nPrinters; i++) {
+	    Tcl_AppendElement(interp, *(char **)p);
+	    p += elemSize;
+	}
+    }
+    GlobalUnlock(hMem);
+    GlobalFree(hMem);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+CloseOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    PrintQueue *queuePtr;
+
+    if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    DestroyQueue(queuePtr);
+    return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+GetAttrOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    PrintQueue *queuePtr;
+
+    if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return GetPrinterAttributes(interp, queuePtr, objv[3]);
+}
+
+/*ARGSUSED*/
+static int
+SetAttrOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    PrintQueue *queuePtr;
+
+    if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    return SetQueueAttributes(interp, queuePtr, objv[3]);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ *	Prints a snapshot of a Tk_Window to the designated printer.
+ *
+ * Results:
+ *	Returns a standard Tcl result.  If an error occurred
+ *	TCL_ERROR is returned and interp->result will contain an
+ *	error message.
+ *
+ * -------------------------------------------------------------------------
+ */
+static int
+SnapOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    BITMAPINFO bi;
+    DIBSECTION ds;
+    HBITMAP hBitmap;
+    HPALETTE hPalette;
+    HDC hDC, printDC, memDC;
+    void *data;
+    Tk_Window tkwin;
+    TkWinDCState state;
+    int result;
+    PrintQueue *queuePtr;
+    DOCINFO di;
+    double pageWidth, pageHeight;
+    int jobId;
+    char *driverName;
+    DEVMODE *dmPtr;
+    HGLOBAL hMem;
+    Tcl_DString dString;
+    char *path;
+
+    Tcl_DStringInit(&dString);
+    if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    path = Tcl_GetString(objv[3]);
+    tkwin = Tk_NameToWindow(interp, path, Tk_MainWindow(interp));
+    if (tkwin == NULL) {
+	return TCL_ERROR;
+    }
+    if (Tk_WindowId(tkwin) == None) {
+	Tk_MakeWindowExist(tkwin);
+    }
+    
+    result = TCL_ERROR;
+    hDC = TkWinGetDrawableDC(Tk_Display(tkwin), Tk_WindowId(tkwin), &state);
+
+    ZeroMemory(&bi, sizeof(bi));
+    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bi.bmiHeader.biWidth = Tk_Width(tkwin);
+    bi.bmiHeader.biHeight = Tk_Height(tkwin);
+    bi.bmiHeader.biPlanes = 1;
+    bi.bmiHeader.biBitCount = 32;
+    bi.bmiHeader.biCompression = BI_RGB;
+    hBitmap = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &data, NULL, 0);
+    memDC = CreateCompatibleDC(hDC);
+    SelectBitmap(memDC, hBitmap);
+    hPalette = Blt_GetSystemPalette();
+    if (hPalette != NULL) {
+	SelectPalette(hDC, hPalette, FALSE);
+	RealizePalette(hDC);
+	SelectPalette(memDC, hPalette, FALSE);
+	RealizePalette(memDC);
+    }
+    /* Copy the window contents to the memory surface. */
+    if (!BitBlt(memDC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), hDC, 0, 0,
+		SRCCOPY)) {
+	Tcl_AppendResult(interp, "can't blit \"", Tk_PathName(tkwin), "\": ",
+			 Blt_LastError(), (char *)NULL);
+	goto done;
+    }
+    /* Now that the DIB contains the image of the window, get the
+     * databits and write them to the printer device, stretching the
+     * image to the fit the printer's resolution.  */
+    if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+	Tcl_AppendResult(interp, "can't get DIB object: ", Blt_LastError(), 
+			 (char *)NULL);
+	goto done;
+    } 
+    driverName = NULL;
+    if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	driverName = queuePtr->driverName;
+    }
+    if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	goto done;
+    }
+    hMem = GetQueueProperties(queuePtr, &dmPtr);
+    if (hMem == NULL) {
+	goto done;
+    }
+    printDC = CreateDC(driverName, queuePtr->deviceName, NULL, dmPtr);
+    GlobalUnlock(hMem);
+    GlobalFree(hMem);
+    if (printDC == NULL) {
+	Tcl_AppendResult(interp, "can't allocate printer DC for \"",
+		queuePtr->name, "\": ", Blt_LastError(), (char *)NULL);
+	goto done;
+    }
+    {
+	double scale, sx, sy;
+
+	/* Get the resolution of the printer device. */
+	sx = (double)GetDeviceCaps(printDC, HORZRES)/(double)Tk_Width(tkwin);
+	sy = (double)GetDeviceCaps(printDC, VERTRES)/(double)Tk_Height(tkwin);
+	scale = MIN(sx, sy);
+	pageWidth = scale * Tk_Width(tkwin);
+	pageHeight = scale * Tk_Height(tkwin);
+    }
+    ZeroMemory(&di, sizeof(di));
+    di.cbSize = sizeof(di);
+    Tcl_DStringAppend(&dString, "Snapshot of \"", -1);
+    Tcl_DStringAppend(&dString, Tk_PathName(tkwin), -1);
+    Tcl_DStringAppend(&dString, "\"", -1);
+    di.lpszDocName = Tcl_DStringValue(&dString);
+    jobId = StartDoc(printDC, &di);
+    if (jobId <= 0) {
+	Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(), 
+		(char *)NULL);
+	goto done;
+    }
+    if (StartPage(printDC) <= 0) {
+	Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(), 
+		(char *)NULL);
+	goto done;
+    }
+    StretchDIBits(printDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0, 
+	Tk_Width(tkwin), Tk_Height(tkwin), ds.dsBm.bmBits, 
+	(LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
+    EndPage(printDC);
+    EndDoc(printDC);
+    DeleteDC(printDC);
+    Tcl_SetResult(interp, Blt_Itoa(jobId), TCL_VOLATILE);
+    result = TCL_OK;
+
+  done:
+    Tcl_DStringFree(&dString);
+    if (queuePtr->hPrinter != NULL) {
+	CloseQueue(queuePtr);
+    }    
+    DeleteBitmap(hBitmap);
+    DeleteDC(memDC);
+    TkWinReleaseDrawableDC(Tk_WindowId(tkwin), hDC, &state);
+    if (hPalette != NULL) {
+	DeletePalette(hPalette);
+    }
+    return result;
+}
+
+/*ARGSUSED*/
+static int
+WriteOp(
+    ClientData clientData,	/* Interpreter-specific data. */
+    Tcl_Interp *interp,
+    int objc,			/* Not used. */
+    Tcl_Obj *CONST *objv)
+{
+    DWORD bytesLeft, nBytes;
+    DOC_INFO_1 di1;
+    DWORD jobId;
+    char *title;
+    register char *data;
+    static int nextJob = 0;
+    char string[200];
+    PrintQueue *queuePtr;
+    int result;
+
+    if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc == 5) {
+	title = Tcl_GetString(objv[3]);
+	data = Tcl_GetStringFromObj(objv[4], &bytesLeft);
+    } else {
+	sprintf(string, "Print Job #%d", nextJob++);
+	title = string;
+	data = Tcl_GetStringFromObj(objv[3], &bytesLeft);
+    }
+    ZeroMemory(&di1, sizeof(DOC_INFO_1));
+    di1.pDocName = title;
+    if (queuePtr->fileName != NULL) {
+	di1.pOutputFile = queuePtr->fileName;
+    } else {
+	di1.pOutputFile = NULL;
+    }
+    di1.pDatatype = "RAW";
+
+    result = TCL_ERROR;
+    /* Start new document */
+    jobId = StartDocPrinter(queuePtr->hPrinter, 1, (unsigned char *)&di1);
+    if (jobId == 0) {
+	Tcl_AppendResult(interp, "error starting document on \"", 
+	 queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    /* Start new page */
+    if (!StartPagePrinter(queuePtr->hPrinter)) {
+	Tcl_AppendResult(interp, "error starting page on \"", 
+	 queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    do {
+	if (!WritePrinter(queuePtr->hPrinter, data, bytesLeft, &nBytes)) {
+	    Tcl_AppendResult(interp, "can't write data to \"", 
+		queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	    EndDocPrinter(queuePtr->hPrinter);
+	    goto error;
+	}
+	data += nBytes;
+	bytesLeft -= nBytes;
+    } while (bytesLeft > 0);
+    /* End last page */
+    if (!EndPagePrinter(queuePtr->hPrinter)) {
+	Tcl_AppendResult(interp, "error ending page on \"", 
+		queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    /* End document */
+    if (!EndDocPrinter(queuePtr->hPrinter)) {
+	Tcl_AppendResult(interp, "error ending document on \"", 
+		queuePtr->printerName, "\": ", Blt_LastError(), (char *)NULL);
+	goto error;
+    }
+    result = TCL_OK;
+ error:
+    CloseQueue(queuePtr);
+    return result;
+}
+
+static Blt_OpSpec printerOps[] =
+{
+    {"close", 1, (Blt_Op)CloseOp, 3, 3, "pid",},
+    {"enum", 1, (Blt_Op)EnumOp, 3, 3, "attribute",},
+    {"getattrs", 1, (Blt_Op)GetAttrOp, 4, 4, "pid varName",},
+    {"names", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
+    {"open", 1, (Blt_Op)OpenOp, 3, 3, "printerName",},
+    {"setattrs", 1, (Blt_Op)SetAttrOp, 4, 4, "pid varName",},
+    {"snap", 1, (Blt_Op)SnapOp, 4, 4, "pid window",},
+    {"write", 1, (Blt_Op)WriteOp, 4, 5, "pid ?title? string",},
+};
+static int nPrinterOps = sizeof(printerOps) / sizeof(Blt_OpSpec);
+
+/* ARGSUSED */
+static int
+PrinterCmd(
+    ClientData clientData,	/* Not used. */
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST *objv)
+{
+    Blt_Op proc;
+    int result;
+
+    proc = Blt_GetOpFromObj(interp, nPrinterOps, printerOps, BLT_OP_ARG1, 
+		    objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    result = (*proc) (clientData, interp, objc, objv);
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * PrinterInterpDeleteProc --
+ *
+ *	This is called when the interpreter hosting one or more printer 
+ *	commands is destroyed.  
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Closes and removes all open printers.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+PrinterInterpDeleteProc(clientData, interp)
+    ClientData clientData;	/* Interpreter-specific data. */
+    Tcl_Interp *interp;
+{
+    PrinterInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    PrintQueue *queuePtr;
+
+    for (hPtr = Blt_FirstHashEntry(&dataPtr->printerTable, &cursor);
+	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+	queuePtr = (PrintQueue *)Blt_GetHashValue(hPtr);
+	queuePtr->hashPtr = NULL;
+	DestroyQueue(queuePtr);
+    }
+    Blt_DeleteHashTable(&dataPtr->printerTable);
+    Tcl_DeleteAssocData(interp, PRINTER_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+
+int
+Blt_PrinterInit(Tcl_Interp *interp)
+{
+    static Blt_ObjCmdSpec cmdSpec = {
+	"printer", PrinterCmd
+    };
+    PrinterInterpData *dataPtr;
+
+    dataPtr = GetPrinterInterpData(interp);
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) {
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
+
+/* Public routines */
+int
+Blt_GetOpenPrinter(
+    Tcl_Interp *interp,
+    const char *name,
+    Drawable *drawablePtr)
+{
+    PrintQueue *queuePtr;
+    
+    if (GetQueue(interp, name, &queuePtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (queuePtr->drawable.hDC == NULL) {
+	char *driverName;
+	HGLOBAL hMem;
+	DEVMODE *dmPtr;
+	HDC hDC;
+
+	driverName = NULL;
+	if (Blt_GetPlatformId() == VER_PLATFORM_WIN32_NT) {
+	    driverName = queuePtr->driverName;
+	}
+	if (OpenQueue(interp, queuePtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	hMem = GetQueueProperties(queuePtr, &dmPtr);
+	if (hMem == NULL) {
+	    CloseQueue(queuePtr);
+	    return TCL_ERROR;
+	}
+	if (queuePtr->dmPtr != NULL) {
+	    *dmPtr = *queuePtr->dmPtr;
+	}
+	hDC = CreateDC(driverName, queuePtr->deviceName, NULL, dmPtr);
+	GlobalUnlock(hMem);
+	GlobalFree(hMem);
+	CloseQueue(queuePtr);
+	if (hDC == NULL) {
+	    Tcl_AppendResult(interp, "can't allocate printer DC for \"",
+		queuePtr->name, "\": ", Blt_LastError(), (char *)NULL);
+	    return TCL_ERROR;
+	}
+	queuePtr->drawable.hDC = hDC;
+	queuePtr->drawable.type = TWD_WINDC;
+    }
+    *drawablePtr = (Drawable)(&queuePtr->drawable);
+    return TCL_OK;
+}
+
+#include <commdlg.h>
+
+int
+Blt_PrintDialog(
+    Tcl_Interp *interp,
+    Drawable *drawablePtr)
+{
+    PRINTDLG dlg;
+    static PrintDrawable drawable;
+    int mode, result;
+    
+    ZeroMemory(&dlg, sizeof(PRINTDLG));
+    dlg.lStructSize = sizeof(PRINTDLG);
+    dlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;
+    mode = Tcl_SetServiceMode(TCL_SERVICE_NONE);
+    result = PrintDlg(&dlg);
+    Tcl_SetServiceMode(mode);
+    if (!result) {
+	if (!CommDlgExtendedError()) {
+	    return TCL_RETURN;	/* User canceled. */
+	}
+	Tcl_AppendResult(interp, "can't access printer:", Blt_LastError(), 
+			 (char *)NULL);
+	return TCL_ERROR;
+    } 
+    *drawablePtr = (Drawable)&drawable;
+    drawable.type = TWD_WINDC;
+    drawable.hDC = dlg.hDC;
+    return TCL_OK;
+}
+
+int
+Blt_StartPrintJob(
+    Tcl_Interp *interp,
+    Drawable drawable)
+{
+    DOCINFO di;
+    PrintDrawable *drawPtr = (PrintDrawable *)drawable;
+    int jobId;
+
+    ZeroMemory((char *)&di, sizeof(DOCINFO));
+    di.cbSize = sizeof(DOCINFO);
+    di.lpszDocName = "Unknown";
+    jobId = StartDoc(drawPtr->hDC, &di);
+    if (jobId == 0) {
+	Tcl_AppendResult(interp, "error starting document: ",
+ 			 Blt_LastError(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_EndPrintJob(
+    Tcl_Interp *interp,
+    Drawable drawable)
+{
+    PrintDrawable *drawPtr = (PrintDrawable *)drawable;
+
+    EndPage(drawPtr->hDC);
+    EndDoc(drawPtr->hDC);
+    return TCL_OK;
+}
+
+#endif /*NO_PRINTER*/
Index: trunk/kitgen/8.x/blt/win/bltWinUtil.c
===================================================================
--- trunk/kitgen/8.x/blt/win/bltWinUtil.c	(revision 175)
+++ trunk/kitgen/8.x/blt/win/bltWinUtil.c	(revision 175)
@@ -0,0 +1,79 @@
+/*
+ * bltWinUtil.c --
+ *
+ *	This module contains WIN32 routines not included in the Tcl/Tk
+ *	libraries.
+ *
+ * Copyright 1998 by Bell Labs Innovations for Lucent Technologies.
+ *
+ * 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 the
+ * copyright notice and warranty disclaimer appear in supporting documentation,
+ * and that the names of Lucent Technologies any of their entities not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this software,
+ * including all implied warranties of merchantability and fitness.  In no event
+ * shall Lucent Technologies be liable for any special, indirect or
+ * consequential damages or any damages whatsoever resulting from loss of use,
+ * data or profits, whether in an action of contract, negligence or other
+ * tortuous action, arising out of or in connection with the use or performance
+ * of this software.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "bltInt.h"
+
+double
+drand48(void)
+{
+    return (double) rand() / (double)RAND_MAX;
+}
+
+void
+srand48(long int seed)
+{
+    srand(seed);
+}
+
+int
+Blt_GetPlatformId(void)
+{
+    static int platformId = 0;
+
+    if (platformId == 0) {
+	OSVERSIONINFO opsysInfo;
+
+	opsysInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+	if (GetVersionEx(&opsysInfo)) {
+	    platformId = opsysInfo.dwPlatformId;
+	}
+    }
+    return platformId;
+}
+
+char *
+Blt_LastError(void)
+{
+    static char buffer[1024];
+    int length;
+
+    FormatMessage(
+	FORMAT_MESSAGE_FROM_SYSTEM,
+	NULL,
+	GetLastError(),
+	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	/* Default language */
+	buffer,
+	1024,
+	NULL);
+    length = strlen(buffer);
+    if (buffer[length - 2] == '\r') {
+	buffer[length - 2] = '\0';
+    }
+    return buffer;
+}
+
Index: trunk/kitgen/8.x/blt/win/makefile.vc
===================================================================
--- trunk/kitgen/8.x/blt/win/makefile.vc	(revision 175)
+++ trunk/kitgen/8.x/blt/win/makefile.vc	(revision 175)
@@ -0,0 +1,465 @@
+# makefile.vc --                                               -*- Makefile -*-
+#
+# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
+#
+# This makefile is based upon the Tcl 8.4 Makefile.vc and modified to 
+# make it suitable as a general package makefile. Look for the word EDIT
+# which marks sections that may need modification. As a minumum you will
+# need to change the PROJECT, DOTVERSION and DLLOBJS variables to values
+# relevant to your package.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+# Copyright (c) 1995-1996 Sun Microsystems, Inc.
+# Copyright (c) 1998-2000 Ajuba Solutions.
+# Copyright (c) 2001 ActiveState Corporation.
+# Copyright (c) 2001-2002 David Gravereaux.
+# Copyright (c) 2003-2006 Pat Thoyts
+#
+#-------------------------------------------------------------------------
+# RCS: @(#)$Id: makefile.vc,v 1.10 2008/06/18 11:01:39 patthoyts Exp $
+#-------------------------------------------------------------------------
+
+# Check to see we are configured to build with MSVC (MSDEVDIR or MSVCDIR)
+# or with the MS Platform SDK (MSSDK). Visual Studio .NET 2003 and 2005 define
+# VCINSTALLDIR instead. The MSVC Toolkit release defines yet another.
+!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(MSSDK) && !defined(VCINSTALLDIR) && !defined(VCToolkitInstallDir)
+MSG = ^
+You need to run vcvars32.bat from Developer Studio or setenv.bat from the^
+Platform SDK first to setup the environment.  Jump to this line to read^
+the build instructions.
+!error $(MSG)
+!endif
+
+#------------------------------------------------------------------------------
+# HOW TO USE this makefile:
+#
+# 1)  It is now necessary to have %MSVCDir% set in the environment.  This is
+#     used  as a check to see if vcvars32.bat had been run prior to running
+#     nmake or during the installation of Microsoft Visual C++, MSVCDir had
+#     been set globally and the PATH adjusted.  Either way is valid.
+#
+#     You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin
+#     directory to setup the proper environment, if needed, for your current
+#     setup.  This is a needed bootstrap requirement and allows the swapping of
+#     different environments to be easier.
+#
+# 2)  To use the Platform SDK (not expressly needed), run setenv.bat after
+#     vcvars32.bat according to the instructions for it.  This can also turn on
+#     the 64-bit compiler, if your SDK has it.
+#
+# 3)  Targets are:
+#	all       -- Builds everything.
+#       <project> -- Builds the project (eg: nmake sample)
+#	test      -- Builds and runs the test suite.
+#	install   -- Installs the built binaries and libraries to $(INSTALLDIR)
+#		     in an appropriate subdirectory.
+#	clean/realclean/distclean -- varying levels of cleaning.
+#
+# 4)  Macros usable on the commandline:
+#	INSTALLDIR=<path>
+#		Sets where to install Tcl from the built binaries.
+#		C:\Progra~1\Tcl is assumed when not specified.
+#
+#	OPTS=static,msvcrt,staticpkg,threads,symbols,profile,loimpact,none
+#		Sets special options for the core.  The default is for none.
+#		Any combination of the above may be used (comma separated).
+#		'none' will over-ride everything to nothing.
+#
+#		static  =  Builds a static library of the core instead of a
+#			   dll.  The shell will be static (and large), as well.
+#		msvcrt  =  Effects the static option only to switch it from
+#			   using libcmt(d) as the C runtime [by default] to
+#			   msvcrt(d). This is useful for static embedding
+#			   support.
+#		staticpkg = Effects the static option only to switch
+#			   tclshXX.exe to have the dde and reg extension linked
+#			   inside it.
+#		nothreads = Turns off multithreading support (not recommended)
+#		thrdalloc = Use the thread allocator (shared global free pool).
+#		symbols =  Adds symbols for step debugging.
+#		profile =  Adds profiling hooks.  Map file is assumed.
+#		loimpact =  Adds a flag for how NT treats the heap to keep memory
+#			   in use, low.  This is said to impact alloc performance.
+#
+#	STATS=memdbg,compdbg,none
+#		Sets optional memory and bytecode compiler debugging code added
+#		to the core.  The default is for none.  Any combination of the
+#		above may be used (comma separated).  'none' will over-ride
+#		everything to nothing.
+#
+#		memdbg   = Enables the debugging memory allocator.
+#		compdbg  = Enables byte compilation logging.
+#
+#	MACHINE=(IX86|IA64|ALPHA|AMD64)
+#		Set the machine type used for the compiler, linker, and
+#		resource compiler.  This hook is needed to tell the tools
+#		when alternate platforms are requested.  IX86 is the default
+#		when not specified. If the CPU environment variable has been
+#		set (ie: recent Platform SDK) then MACHINE is set from CPU.
+#
+#	TMP_DIR=<path>
+#	OUT_DIR=<path>
+#		Hooks to allow the intermediate and output directories to be
+#		changed.  $(OUT_DIR) is assumed to be 
+#		$(BINROOT)\(Release|Debug) based on if symbols are requested.
+#		$(TMP_DIR) will de $(OUT_DIR)\<buildtype> by default.
+#
+#	TESTPAT=<file>
+#		Reads the tests requested to be run from this file.
+#
+#	CFG_ENCODING=encoding
+#		name of encoding for configuration information. Defaults
+#		to cp1252
+#
+# 5)  Examples:
+#
+#	Basic syntax of calling nmake looks like this:
+#	nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]]
+#
+#                        Standard (no frills)
+#       c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
+#       Setting environment for using Microsoft Visual C++ tools.
+#       c:\tcl_src\win\>nmake -f makefile.vc all
+#       c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
+#
+#                         Building for Win64
+#       c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
+#       Setting environment for using Microsoft Visual C++ tools.
+#       c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL
+#       Targeting Windows pre64 RETAIL
+#       c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64
+#
+#------------------------------------------------------------------------------
+#==============================================================================
+###############################################################################
+#------------------------------------------------------------------------------
+
+!if !exist("makefile.vc")
+MSG = ^
+You must run this makefile only from the directory it is in.^
+Please `cd` to its location first.
+!error $(MSG)
+!endif
+
+#-------------------------------------------------------------------------
+# Project specific information (EDIT)
+#
+# You should edit this with the name and version of your project. This
+# information is used to generate the name of the package library and
+# it's install location.
+#
+# For example, the sample extension is  going to build sample04.dll and
+# would install it into $(INSTALLDIR)\lib\sample04
+#
+# You need to specify the object files that need to be linked into your
+# binary here.
+#
+#-------------------------------------------------------------------------
+
+PROJECT = sample
+
+# Uncomment the following line if this is a Tk extension.
+#PROJECT_REQUIRES_TK=1
+!include "rules.vc"
+
+DOTVERSION      = 0.5
+VERSION         = $(DOTVERSION:.=)
+STUBPREFIX      = $(PROJECT)stub
+
+DLLOBJS = \
+	$(TMP_DIR)\tclsample.obj \
+	$(TMP_DIR)\sample.obj \
+!if !$(STATIC_BUILD)
+	$(TMP_DIR)\sample.res
+!endif
+
+#-------------------------------------------------------------------------
+# Target names and paths ( shouldn't need changing )
+#-------------------------------------------------------------------------
+
+BINROOT		= .
+ROOT            = ..
+
+PRJIMPLIB	= $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+PRJLIBNAME	= $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+PRJLIB		= $(OUT_DIR)\$(PRJLIBNAME)
+
+PRJSTUBLIBNAME	= $(STUBPREFIX)$(VERSION).lib
+PRJSTUBLIB	= $(OUT_DIR)\$(PRJSTUBLIBNAME)
+
+### Make sure we use backslash only.
+PRJ_INSTALL_DIR         = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
+LIB_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
+BIN_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
+DOC_INSTALL_DIR		= $(PRJ_INSTALL_DIR)
+SCRIPT_INSTALL_DIR	= $(PRJ_INSTALL_DIR)
+INCLUDE_INSTALL_DIR	= $(_TCLDIR)\include
+
+### The following paths CANNOT have spaces in them.
+GENERICDIR	= $(ROOT)\generic
+WINDIR		= $(ROOT)\win
+LIBDIR          = $(ROOT)\library
+DOCDIR		= $(ROOT)\doc
+TOOLSDIR	= $(ROOT)\tools
+COMPATDIR	= $(ROOT)\compat
+
+#---------------------------------------------------------------------
+# Compile flags
+#---------------------------------------------------------------------
+
+!if !$(DEBUG)
+!if $(OPTIMIZING)
+### This cranks the optimization level to maximize speed
+cdebug	= $(OPTIMIZATIONS)
+!else
+cdebug	=
+!endif
+!else if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
+### Warnings are too many, can't support warnings into errors.
+cdebug	= -Zi -Od $(DEBUGFLAGS)
+!else
+cdebug	= -Zi -WX $(DEBUGFLAGS)
+!endif
+
+### Declarations common to all compiler options
+cwarn = $(WARNINGS) -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE
+cflags = -nologo -c $(COMPILERFLAGS) $(cwarn) -Fp$(TMP_DIR)^\
+
+!if $(MSVCRT)
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MDd
+!else
+crt = -MD
+!endif
+!else
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MTd
+!else
+crt = -MT
+!endif
+!endif
+
+!if !$(STATIC_BUILD)
+cflags = $(cflags) -DUSE_TCL_STUBS
+!if defined(TKSTUBLIB)
+cflags = $(cflags) -DUSE_TK_STUBS
+!endif
+!endif
+
+INCLUDES	= $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)"
+BASE_CFLAGS	= $(cflags) $(cdebug) $(crt) $(INCLUDES)
+CON_CFLAGS	= $(cflags) $(cdebug) $(crt) -DCONSOLE
+TCL_CFLAGS	= -DPACKAGE_NAME="\"$(PROJECT)\"" \
+		  -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
+		  -DBUILD_$(PROJECT) \
+		  $(BASE_CFLAGS) $(OPTDEFINES)
+
+#---------------------------------------------------------------------
+# Link flags
+#---------------------------------------------------------------------
+
+!if $(DEBUG)
+ldebug	= -debug:full -debugtype:cv
+!if $(MSVCRT)
+ldebug = $(ldebug) -nodefaultlib:msvcrt
+!endif
+!else
+ldebug	= -release -opt:ref -opt:icf,3
+!endif
+
+### Declarations common to all linker options
+lflags	= -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)
+
+!if $(PROFILE)
+lflags	= $(lflags) -profile
+!endif
+
+!if $(ALIGN98_HACK) && !$(STATIC_BUILD)
+### Align sections for PE size savings.
+lflags	= $(lflags) -opt:nowin98
+!else if !$(ALIGN98_HACK) && $(STATIC_BUILD)
+### Align sections for speed in loading by choosing the virtual page size.
+lflags	= $(lflags) -align:4096
+!endif
+
+!if $(LOIMPACT)
+lflags	= $(lflags) -ws:aggressive
+!endif
+
+dlllflags = $(lflags) -dll
+conlflags = $(lflags) -subsystem:console
+guilflags = $(lflags) -subsystem:windows
+!if !$(STATIC_BUILD)
+baselibs  = $(TCLSTUBLIB)
+!if defined(TKSTUBLIB)
+baselibs  = $(baselibs) $(TKSTUBLIB)
+!endif
+!endif
+
+# Avoid 'unresolved external symbol __security_cookie' errors.
+# c.f. http://support.microsoft.com/?id=894573
+!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
+!if $(VCVERSION) >= 1400 && $(VCVERSION) < 1500
+baselibs   = $(baselibs) bufferoverflowU.lib
+!endif
+!endif
+
+baselibs   = $(baselibs) user32.lib gdi32.lib
+
+#---------------------------------------------------------------------
+# TclTest flags
+#---------------------------------------------------------------------
+
+!IF "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!ENDIF
+
+#---------------------------------------------------------------------
+# Project specific targets (EDIT)
+#---------------------------------------------------------------------
+
+all:	    setup $(PROJECT)
+$(PROJECT): setup pkgIndex $(PRJLIB)
+install:    install-binaries install-libraries install-docs
+pkgIndex:   $(OUT_DIR)\pkgIndex.tcl
+
+test: setup $(PROJECT)
+	@set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+	@set TCLLIBPATH=$(OUT_DIR_PATH:\=/)
+!if $(TCLINSTALL)
+	@set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+	@set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+!if "$(OS)" == "Windows_NT"  || "$(MSVCDIR)" == "IDE"
+	$(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS)
+!else
+        @echo Please wait while the tests are collected...
+        $(DEBUGGER) $(TCLSH) "$(ROOT)/tests/all.tcl" $(TESTFLAGS) > tests.log
+	type tests.log | more
+!endif
+
+shell: setup $(PROJECT)
+	@set VLERQ_LIBRARY=$(LIBDIR:\=/)
+	@set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+	@set TCLLIBPATH=$(OUT_DIR_PATH:\=/)
+!if $(TCLINSTALL)
+	@set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+	@set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+	$(DEBUGGER) $(TCLSH) $(SCRIPT)
+
+setup:
+	@if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
+	@if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
+
+# See <tcl>/win/coffbase.txt for extension base addresses.
+$(PRJLIB): $(DLLOBJS)
+!if $(STATIC_BUILD)
+	$(lib32) -nologo -out:$@ @<<
+$**
+<<
+!else
+	$(link32) $(dlllflags) -base:0x10000000 -out:$@ $(baselibs) @<<
+$**
+<<
+	$(_VC_MANIFEST_EMBED_DLL)
+	-@del $*.exp
+!endif
+
+$(PRJSTUBLIB): $(PRJSTUBOBJS)
+	$(lib32) -nologo -out:$@ $(PRJSTUBOBJS)
+
+#---------------------------------------------------------------------
+# Implicit rules
+#---------------------------------------------------------------------
+
+{$(WINDIR)}.c{$(TMP_DIR)}.obj::
+    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
+    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
+    $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(WINDIR)}.rc{$(TMP_DIR)}.res:
+	$(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \
+		-DCOMMAVERSION=$(DOTVERSION:.=,),0,0 \
+		-DDOTVERSION=\"$(DOTVERSION)\" \
+		-DVERSION=\"$(VERSION)$(SUFX)\" \
+!if $(DEBUG)
+	-d DEBUG \
+!endif
+!if $(TCL_THREADS)
+	-d TCL_THREADS \
+!endif
+!if $(STATIC_BUILD)
+	-d STATIC_BUILD \
+!endif
+	$<
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
+
+#-------------------------------------------------------------------------
+# Explicit dependency rules
+#
+#-------------------------------------------------------------------------
+
+$(OUT_DIR)\pkgIndex.tcl: $(ROOT)\pkgIndex.tcl.in
+	@nmakehlp -s << $** > $@
+@PACKAGE_VERSION@    $(DOTVERSION)
+@PACKAGE_NAME@       $(PROJECT)
+@PKG_LIB_FILE@       $(PRJLIBNAME)
+<<
+
+#---------------------------------------------------------------------
+# Installation. (EDIT)
+#
+# You may need to modify this section to reflect the final distribution
+# of your files and possibly to generate documentation.
+#
+#---------------------------------------------------------------------
+
+install-binaries:
+	@echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
+	@if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+	@$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+install-libraries: $(OUT_DIR)\pkgIndex.tcl
+	@echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
+	@if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
+	@echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
+	@$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR)
+
+install-docs:
+	@echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+	@if exist $(DOCDIR) $(CPY) $(DOCDIR)\*.n "$(DOC_INSTALL_DIR)"
+
+#---------------------------------------------------------------------
+# Clean up
+#---------------------------------------------------------------------
+
+clean:
+	@if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
+	@if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
+	@if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
+	@if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
+	@if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
+
+realclean: clean
+	@if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+distclean: realclean
+	@if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
+	@if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
Index: trunk/kitgen/8.x/blt/win/nmakehlp.c
===================================================================
--- trunk/kitgen/8.x/blt/win/nmakehlp.c	(revision 175)
+++ trunk/kitgen/8.x/blt/win/nmakehlp.c	(revision 175)
@@ -0,0 +1,777 @@
+/*
+ * ----------------------------------------------------------------------------
+ * nmakehlp.c --
+ *
+ *	This is used to fix limitations within nmake and the environment.
+ *
+ * Copyright (c) 2002 by David Gravereaux.
+ * Copyright (c) 2006 by Pat Thoyts
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * ----------------------------------------------------------------------------
+ * RCS: @(#) $Id: nmakehlp.c,v 1.7 2008/06/18 11:01:42 patthoyts Exp $
+ * ----------------------------------------------------------------------------
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE
+#include <windows.h>
+#define NO_SHLWAPI_GDI
+#define NO_SHLWAPI_STREAM
+#define NO_SHLWAPI_REG
+#include <shlwapi.h>
+#pragma comment (lib, "user32.lib")
+#pragma comment (lib, "kernel32.lib")
+#pragma comment (lib, "shlwapi.lib")
+#include <stdio.h>
+#include <math.h>
+
+/*
+ * This library is required for x64 builds with _some_ versions of MSVC
+ */
+#if defined(_M_IA64) || defined(_M_AMD64)
+#if _MSC_VER >= 1400 && _MSC_VER < 1500
+#pragma comment(lib, "bufferoverflowU")
+#endif
+#endif
+
+/* ISO hack for dumb VC++ */
+#ifdef _MSC_VER
+#define   snprintf	_snprintf
+#endif
+
+
+
+/* protos */
+
+int		CheckForCompilerFeature(const char *option);
+int		CheckForLinkerFeature(const char *option);
+int		IsIn(const char *string, const char *substring);
+int		GrepForDefine(const char *file, const char *string);
+int		SubstituteFile(const char *substs, const char *filename);
+int		QualifyPath(const char *path);
+const char *    GetVersionFromFile(const char *filename, const char *match);
+DWORD WINAPI	ReadFromPipe(LPVOID args);
+
+/* globals */
+
+#define CHUNK	25
+#define STATICBUFFERSIZE    1000
+typedef struct {
+    HANDLE pipe;
+    char buffer[STATICBUFFERSIZE];
+} pipeinfo;
+
+pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
+pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
+
+
+/*
+ * exitcodes: 0 == no, 1 == yes, 2 == error
+ */
+
+int
+main(
+    int argc,
+    char *argv[])
+{
+    char msg[300];
+    DWORD dwWritten;
+    int chars;
+
+    /*
+     * Make sure children (cl.exe and link.exe) are kept quiet.
+     */
+
+    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+
+    /*
+     * Make sure the compiler and linker aren't effected by the outside world.
+     */
+
+    SetEnvironmentVariable("CL", "");
+    SetEnvironmentVariable("LINK", "");
+
+    if (argc > 1 && *argv[1] == '-') {
+	switch (*(argv[1]+1)) {
+	case 'c':
+	    if (argc != 3) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+		        "usage: %s -c <compiler option>\n"
+			"Tests for whether cl.exe supports an option\n"
+			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+			&dwWritten, NULL);
+		return 2;
+	    }
+	    return CheckForCompilerFeature(argv[2]);
+	case 'l':
+	    if (argc != 3) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+	       		"usage: %s -l <linker option>\n"
+			"Tests for whether link.exe supports an option\n"
+			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+			&dwWritten, NULL);
+		return 2;
+	    }
+	    return CheckForLinkerFeature(argv[2]);
+	case 'f':
+	    if (argc == 2) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+			"usage: %s -f <string> <substring>\n"
+			"Find a substring within another\n"
+			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+			&dwWritten, NULL);
+		return 2;
+	    } else if (argc == 3) {
+		/*
+		 * If the string is blank, there is no match.
+		 */
+
+		return 0;
+	    } else {
+		return IsIn(argv[2], argv[3]);
+	    }
+	case 'g':
+	    if (argc == 2) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+			"usage: %s -g <file> <string>\n"
+			"grep for a #define\n"
+			"exitcodes: integer of the found string (no decimals)\n",
+			argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+			&dwWritten, NULL);
+		return 2;
+	    }
+	    return GrepForDefine(argv[2], argv[3]);
+	case 's':
+	    if (argc == 2) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+			"usage: %s -s <substitutions file> <file>\n"
+			"Perform a set of string map type substutitions on a file\n"
+			"exitcodes: 0\n",
+			argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+			&dwWritten, NULL);
+		return 2;
+	    }
+	    return SubstituteFile(argv[2], argv[3]);
+	case 'V':
+	    if (argc != 4) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+		    "usage: %s -V filename matchstring\n"
+		    "Extract a version from a file:\n"
+		    "eg: pkgIndex.tcl \"package ifneeded http\"",
+		    argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+		    &dwWritten, NULL);
+		return 0;
+	    }
+	    printf("%s\n", GetVersionFromFile(argv[2], argv[3]));
+	    return 0;
+	case 'Q':
+	    if (argc != 3) {
+		chars = snprintf(msg, sizeof(msg) - 1,
+		    "usage: %s -q path\n"
+		    "Emit the fully qualified path\n"
+		    "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+		    &dwWritten, NULL);
+		return 2;
+	    }
+	    return QualifyPath(argv[2]);
+	}
+    }
+    chars = snprintf(msg, sizeof(msg) - 1,
+	    "usage: %s -c|-l|-f|-g|-V|-s|-Q ...\n"
+	    "This is a little helper app to equalize shell differences between WinNT and\n"
+	    "Win9x and get nmake.exe to accomplish its job.\n",
+	    argv[0]);
+    WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
+    return 2;
+}
+
+
+int
+CheckForCompilerFeature(
+    const char *option)
+{
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    SECURITY_ATTRIBUTES sa;
+    DWORD threadID;
+    char msg[300];
+    BOOL ok;
+    HANDLE hProcess, h, pipeThreads[2];
+    char cmdline[100];
+
+    hProcess = GetCurrentProcess();
+
+    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+    ZeroMemory(&si, sizeof(STARTUPINFO));
+    si.cb = sizeof(STARTUPINFO);
+    si.dwFlags   = STARTF_USESTDHANDLES;
+    si.hStdInput = INVALID_HANDLE_VALUE;
+
+    ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = FALSE;
+
+    /*
+     * Create a non-inheritible pipe.
+     */
+
+    CreatePipe(&Out.pipe, &h, &sa, 0);
+
+    /*
+     * Dupe the write side, make it inheritible, and close the original.
+     */
+
+    DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
+	    DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+    /*
+     * Same as above, but for the error side.
+     */
+
+    CreatePipe(&Err.pipe, &h, &sa, 0);
+    DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
+	    DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+    /*
+     * Base command line.
+     */
+
+    lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
+
+    /*
+     * Append our option for testing
+     */
+
+    lstrcat(cmdline, option);
+
+    /*
+     * Filename to compile, which exists, but is nothing and empty.
+     */
+
+    lstrcat(cmdline, " .\\nul");
+
+    ok = CreateProcess(
+	    NULL,	    /* Module name. */
+	    cmdline,	    /* Command line. */
+	    NULL,	    /* Process handle not inheritable. */
+	    NULL,	    /* Thread handle not inheritable. */
+	    TRUE,	    /* yes, inherit handles. */
+	    DETACHED_PROCESS, /* No console for you. */
+	    NULL,	    /* Use parent's environment block. */
+	    NULL,	    /* Use parent's starting directory. */
+	    &si,	    /* Pointer to STARTUPINFO structure. */
+	    &pi);	    /* Pointer to PROCESS_INFORMATION structure. */
+
+    if (!ok) {
+	DWORD err = GetLastError();
+	int chars = snprintf(msg, sizeof(msg) - 1,
+		"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
+
+	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
+		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+		(300-chars), 0);
+	WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL);
+	return 2;
+    }
+
+    /*
+     * Close our references to the write handles that have now been inherited.
+     */
+
+    CloseHandle(si.hStdOutput);
+    CloseHandle(si.hStdError);
+
+    WaitForInputIdle(pi.hProcess, 5000);
+    CloseHandle(pi.hThread);
+
+    /*
+     * Start the pipe reader threads.
+     */
+
+    pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
+    pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
+
+    /*
+     * Block waiting for the process to end.
+     */
+
+    WaitForSingleObject(pi.hProcess, INFINITE);
+    CloseHandle(pi.hProcess);
+
+    /*
+     * Wait for our pipe to get done reading, should it be a little slow.
+     */
+
+    WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
+    CloseHandle(pipeThreads[0]);
+    CloseHandle(pipeThreads[1]);
+
+    /*
+     * Look for the commandline warning code in both streams.
+     *  - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
+     */
+
+    return !(strstr(Out.buffer, "D4002") != NULL
+             || strstr(Err.buffer, "D4002") != NULL
+             || strstr(Out.buffer, "D9002") != NULL
+             || strstr(Err.buffer, "D9002") != NULL
+             || strstr(Out.buffer, "D2021") != NULL
+             || strstr(Err.buffer, "D2021") != NULL);
+}
+
+
+int
+CheckForLinkerFeature(
+    const char *option)
+{
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    SECURITY_ATTRIBUTES sa;
+    DWORD threadID;
+    char msg[300];
+    BOOL ok;
+    HANDLE hProcess, h, pipeThreads[2];
+    char cmdline[100];
+
+    hProcess = GetCurrentProcess();
+
+    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+    ZeroMemory(&si, sizeof(STARTUPINFO));
+    si.cb = sizeof(STARTUPINFO);
+    si.dwFlags   = STARTF_USESTDHANDLES;
+    si.hStdInput = INVALID_HANDLE_VALUE;
+
+    ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+
+    /*
+     * Create a non-inheritible pipe.
+     */
+
+    CreatePipe(&Out.pipe, &h, &sa, 0);
+
+    /*
+     * Dupe the write side, make it inheritible, and close the original.
+     */
+
+    DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
+	    DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+    /*
+     * Same as above, but for the error side.
+     */
+
+    CreatePipe(&Err.pipe, &h, &sa, 0);
+    DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
+	    DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+    /*
+     * Base command line.
+     */
+
+    lstrcpy(cmdline, "link.exe -nologo ");
+
+    /*
+     * Append our option for testing.
+     */
+
+    lstrcat(cmdline, option);
+
+    ok = CreateProcess(
+	    NULL,	    /* Module name. */
+	    cmdline,	    /* Command line. */
+	    NULL,	    /* Process handle not inheritable. */
+	    NULL,	    /* Thread handle not inheritable. */
+	    TRUE,	    /* yes, inherit handles. */
+	    DETACHED_PROCESS, /* No console for you. */
+	    NULL,	    /* Use parent's environment block. */
+	    NULL,	    /* Use parent's starting directory. */
+	    &si,	    /* Pointer to STARTUPINFO structure. */
+	    &pi);	    /* Pointer to PROCESS_INFORMATION structure. */
+
+    if (!ok) {
+	DWORD err = GetLastError();
+	int chars = snprintf(msg, sizeof(msg) - 1,
+		"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
+
+	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
+		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+		(300-chars), 0);
+	WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL);
+	return 2;
+    }
+
+    /*
+     * Close our references to the write handles that have now been inherited.
+     */
+
+    CloseHandle(si.hStdOutput);
+    CloseHandle(si.hStdError);
+
+    WaitForInputIdle(pi.hProcess, 5000);
+    CloseHandle(pi.hThread);
+
+    /*
+     * Start the pipe reader threads.
+     */
+
+    pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
+    pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
+
+    /*
+     * Block waiting for the process to end.
+     */
+
+    WaitForSingleObject(pi.hProcess, INFINITE);
+    CloseHandle(pi.hProcess);
+
+    /*
+     * Wait for our pipe to get done reading, should it be a little slow.
+     */
+
+    WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
+    CloseHandle(pipeThreads[0]);
+    CloseHandle(pipeThreads[1]);
+
+    /*
+     * Look for the commandline warning code in the stderr stream.
+     */
+
+    return !(strstr(Out.buffer, "LNK1117") != NULL ||
+	    strstr(Err.buffer, "LNK1117") != NULL ||
+	    strstr(Out.buffer, "LNK4044") != NULL ||
+	    strstr(Err.buffer, "LNK4044") != NULL);
+}
+
+
+DWORD WINAPI
+ReadFromPipe(
+    LPVOID args)
+{
+    pipeinfo *pi = (pipeinfo *) args;
+    char *lastBuf = pi->buffer;
+    DWORD dwRead;
+    BOOL ok;
+
+  again:
+    if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
+	CloseHandle(pi->pipe);
+	return (DWORD)-1;
+    }
+    ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
+    if (!ok || dwRead == 0) {
+	CloseHandle(pi->pipe);
+	return 0;
+    }
+    lastBuf += dwRead;
+    goto again;
+
+    return 0;  /* makes the compiler happy */
+}
+
+
+int
+IsIn(
+    const char *string,
+    const char *substring)
+{
+    return (strstr(string, substring) != NULL);
+}
+
+
+/*
+ * Find a specified #define by name.
+ *
+ * If the line is '#define TCL_VERSION "8.5"', it returns 85 as the result.
+ */
+
+int
+GrepForDefine(
+    const char *file,
+    const char *string)
+{
+    char s1[51], s2[51], s3[51];
+    FILE *f = fopen(file, "rt");
+
+    if (f == NULL) {
+	return 0;
+    }
+
+    do {
+	int r = fscanf(f, "%50s", s1);
+
+	if (r == 1 && !strcmp(s1, "#define")) {
+	    /*
+	     * Get next two words.
+	     */
+
+	    r = fscanf(f, "%50s %50s", s2, s3);
+	    if (r != 2) {
+		continue;
+	    }
+
+	    /*
+	     * Is the first word what we're looking for?
+	     */
+
+	    if (!strcmp(s2, string)) {
+		double d1;
+
+		fclose(f);
+
+		/*
+		 * Add 1 past first double quote char. "8.5"
+		 */
+
+		d1 = atof(s3 + 1);		  /*    8.5  */
+		while (floor(d1) != d1) {
+		    d1 *= 10.0;
+		}
+		return ((int) d1);		  /*    85   */
+	    }
+	}
+    } while (!feof(f));
+
+    fclose(f);
+    return 0;
+}
+
+
+/*
+ * GetVersionFromFile --
+ * 	Looks for a match string in a file and then returns the version
+ * 	following the match where a version is anything acceptable to
+ * 	package provide or package ifneeded.
+ */
+
+const char *
+GetVersionFromFile(
+    const char *filename,
+    const char *match)
+{
+    size_t cbBuffer = 100;
+    static char szBuffer[100];
+    char *szResult = NULL;
+    FILE *fp = fopen(filename, "rt");
+
+    if (fp != NULL) {
+	/*
+	 * Read data until we see our match string.
+	 */
+
+	while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+	    LPSTR p, q;
+
+	    p = strstr(szBuffer, match);
+	    if (p != NULL) {
+		/*
+		 * Skip to first digit.
+		 */
+
+		while (*p && !isdigit(*p)) {
+		    ++p;
+		}
+
+		/*
+		 * Find ending whitespace.
+		 */
+
+		q = p;
+		while (*q && (isalnum(*q) || *q == '.')) {
+		    ++q;
+		}
+
+		memcpy(szBuffer, p, q - p);
+		szBuffer[q-p] = 0;
+		szResult = szBuffer;
+		break;
+	    }
+	}
+	fclose(fp);
+    }
+    return szResult;
+}
+
+
+/*
+ * List helpers for the SubstituteFile function
+ */
+
+typedef struct list_item_t {
+    struct list_item_t *nextPtr;
+    char * key;
+    char * value;
+} list_item_t;
+
+/* insert a list item into the list (list may be null) */
+static list_item_t *
+list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
+{
+    list_item_t *itemPtr = malloc(sizeof(list_item_t));
+    if (itemPtr) {
+	itemPtr->key = strdup(key);
+	itemPtr->value = strdup(value);
+	itemPtr->nextPtr = NULL;
+
+	while(*listPtrPtr) {
+	    listPtrPtr = &(*listPtrPtr)->nextPtr;
+	}
+	*listPtrPtr = itemPtr;
+    }
+    return itemPtr;
+}
+
+static void
+list_free(list_item_t **listPtrPtr)
+{
+    list_item_t *tmpPtr, *listPtr = *listPtrPtr;
+    while (listPtr) {
+	tmpPtr = listPtr;
+	listPtr = listPtr->nextPtr;
+	free(tmpPtr->key);
+	free(tmpPtr->value);
+	free(tmpPtr);
+    }
+}
+
+
+/*
+ * SubstituteFile --
+ *	As windows doesn't provide anything useful like sed and it's unreliable
+ *	to use the tclsh you are building against (consider x-platform builds -
+ *	eg compiling AMD64 target from IX86) we provide a simple substitution
+ *	option here to handle autoconf style substitutions.
+ *	The substitution file is whitespace and line delimited. The file should
+ *	consist of lines matching the regular expression:
+ *	  \s*\S+\s+\S*$
+ *
+ *	Usage is something like:
+ *	  nmakehlp -S << $** > $@
+ *        @PACKAGE_NAME@ $(PACKAGE_NAME)
+ *        @PACKAGE_VERSION@ $(PACKAGE_VERSION)
+ *        <<
+ */
+
+int
+SubstituteFile(
+    const char *substitutions,
+    const char *filename)
+{
+    size_t cbBuffer = 1024;
+    static char szBuffer[1024], szCopy[1024];
+    char *szResult = NULL;
+    list_item_t *substPtr = NULL;
+    FILE *fp, *sp;
+
+    fp = fopen(filename, "rt");
+    if (fp != NULL) {
+
+	/*
+	 * Build a list of substutitions from the first filename
+	 */
+
+	sp = fopen(substitutions, "rt");
+	if (sp != NULL) {
+	    while (fgets(szBuffer, cbBuffer, sp) != NULL) {
+		char *ks, *ke, *vs, *ve;
+		ks = szBuffer;
+		while (ks && *ks && isspace(*ks)) ++ks;
+		ke = ks;
+		while (ke && *ke && !isspace(*ke)) ++ke;
+		vs = ke;
+		while (vs && *vs && isspace(*vs)) ++vs;
+		ve = vs;
+		while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
+		*ke = 0, *ve = 0;
+		list_insert(&substPtr, ks, vs);
+	    }
+	    fclose(sp);
+	}
+
+	/* debug: dump the list */
+#ifdef _DEBUG
+	{
+	    int n = 0;
+	    list_item_t *p = NULL;
+	    for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
+		fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
+	    }
+	}
+#endif
+	
+	/*
+	 * Run the substitutions over each line of the input
+	 */
+	
+	while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+	    list_item_t *p = NULL;
+	    for (p = substPtr; p != NULL; p = p->nextPtr) {
+		char *m = strstr(szBuffer, p->key);
+		if (m) {
+		    char *cp, *op, *sp;
+		    cp = szCopy;
+		    op = szBuffer;
+		    while (op != m) *cp++ = *op++;
+		    sp = p->value;
+		    while (sp && *sp) *cp++ = *sp++;
+		    op += strlen(p->key);
+		    while (*op) *cp++ = *op++;
+		    *cp = 0;
+		    memcpy(szBuffer, szCopy, sizeof(szCopy));
+		}
+	    }
+	    printf(szBuffer);
+	}
+	
+	list_free(&substPtr);
+    }
+    fclose(fp);
+    return 0;
+}
+
+
+/*
+ * QualifyPath --
+ *
+ *	This composes the current working directory with a provided path
+ *	and returns the fully qualified and normalized path.
+ *	Mostly needed to setup paths for testing.
+ */
+
+int
+QualifyPath(
+    const char *szPath)
+{
+    char szCwd[MAX_PATH + 1];
+    char szTmp[MAX_PATH + 1];
+    char *p;
+    GetCurrentDirectory(MAX_PATH, szCwd);
+    while ((p = strchr(szPath, '/')) && *p)
+	*p = '\\';
+    PathCombine(szTmp, szCwd, szPath);
+    PathCanonicalize(szCwd, szTmp);
+    printf("%s\n", szCwd);
+    return 0;
+}
+
+/*
+ * Local variables:
+ *   mode: c
+ *   c-basic-offset: 4
+ *   fill-column: 78
+ *   indent-tabs-mode: t
+ *   tab-width: 8
+ * End:
+ */
Index: trunk/kitgen/8.x/blt/win/rules.vc
===================================================================
--- trunk/kitgen/8.x/blt/win/rules.vc	(revision 175)
+++ trunk/kitgen/8.x/blt/win/rules.vc	(revision 175)
@@ -0,0 +1,622 @@
+#------------------------------------------------------------------------------
+# rules.vc --
+#
+#	Microsoft Visual C++ makefile include for decoding the commandline
+#	macros.  This file does not need editing to build Tcl.
+#
+#	This version is modified from the Tcl source version to support
+#	building extensions using nmake.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+# Copyright (c) 2001-2002 David Gravereaux.
+# Copyright (c) 2003-2008 Patrick Thoyts
+#
+#------------------------------------------------------------------------------
+# RCS: @(#) $Id: rules.vc,v 1.8 2008/06/18 11:02:11 patthoyts Exp $
+#------------------------------------------------------------------------------
+
+!ifndef _RULES_VC
+_RULES_VC = 1
+
+cc32		= $(CC)   # built-in default.
+link32		= link
+lib32		= lib
+rc32		= $(RC)   # built-in default.
+
+!ifndef INSTALLDIR
+### Assume the normal default.
+_INSTALLDIR	= C:\Program Files\Tcl
+!else
+### Fix the path separators.
+_INSTALLDIR	= $(INSTALLDIR:/=\)
+!endif
+
+!ifndef MACHINE
+!if "$(CPU)" == "" || "$(CPU)" == "i386"
+MACHINE         = IX86
+!else
+MACHINE         = $(CPU)
+!endif
+!endif
+
+!ifndef CFG_ENCODING
+CFG_ENCODING	= \"cp1252\"
+!endif
+
+#----------------------------------------------------------
+# Set the proper copy method to avoid overwrite questions
+# to the user when copying files and selecting the right
+# "delete all" method.
+#----------------------------------------------------------
+
+!if "$(OS)" == "Windows_NT"
+RMDIR	= rmdir /S /Q
+ERRNULL  = 2>NUL
+!if ![ver | find "4.0" > nul]
+CPY	= echo y | xcopy /i >NUL
+COPY	= copy >NUL
+!else
+CPY	= xcopy /i /y >NUL
+COPY	= copy /y >NUL
+!endif
+!else # "$(OS)" != "Windows_NT"
+CPY	= xcopy /i >_JUNK.OUT # On Win98 NUL does not work here.
+COPY	= copy >_JUNK.OUT # On Win98 NUL does not work here.
+RMDIR	= deltree /Y
+NULL    = \NUL # Used in testing directory existence
+ERRNULL = >NUL # Win9x shell cannot redirect stderr
+!endif
+MKDIR   = mkdir
+
+!message ===============================================================================
+
+#----------------------------------------------------------
+# build the helper app we need to overcome nmake's limiting
+# environment.
+#----------------------------------------------------------
+
+!if !exist(nmakehlp.exe)
+!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul]
+!endif
+!endif
+
+#----------------------------------------------------------
+# Test for compiler features
+#----------------------------------------------------------
+
+### test for optimizations
+!if [nmakehlp -c -Ot]
+!message *** Compiler has 'Optimizations'
+OPTIMIZING	= 1
+!else
+!message *** Compiler does not have 'Optimizations'
+OPTIMIZING	= 0
+!endif
+
+OPTIMIZATIONS  =
+
+!if [nmakehlp -c -Ot]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -Ot
+!endif
+
+!if [nmakehlp -c -Oi]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -Oi
+!endif
+
+!if [nmakehlp -c -Op]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -Op
+!endif
+
+!if [nmakehlp -c -fp:strict]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -fp:strict
+!endif
+
+!if [nmakehlp -c -Gs]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -Gs
+!endif
+
+!if [nmakehlp -c -GS]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -GS
+!endif
+
+!if [nmakehlp -c -GL]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -GL
+!endif
+
+DEBUGFLAGS     =
+
+!if [nmakehlp -c -RTC1]
+DEBUGFLAGS     = $(DEBUGFLAGS) -RTC1
+!elseif [nmakehlp -c -GZ]
+DEBUGFLAGS     = $(DEBUGFLAGS) -GZ
+!endif
+
+COMPILERFLAGS  =-W3
+
+# In v13 -GL and -YX are incompatible.
+!if [nmakehlp -c -YX]
+!if ![nmakehlp -c -GL]
+OPTIMIZATIONS  = $(OPTIMIZATIONS) -YX
+!endif
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for pentium errata
+!if [nmakehlp -c -QI0f]
+!message *** Compiler has 'Pentium 0x0f fix'
+COMPILERFLAGS  = $(COMPILERFLAGSS) -QI0f
+!else
+!message *** Compiler does not have 'Pentium 0x0f fix'
+!endif
+!endif
+
+!if "$(MACHINE)" == "IA64"
+### test for Itanium errata
+!if [nmakehlp -c -QIA64_Bx]
+!message *** Compiler has 'B-stepping errata workarounds'
+COMPILERFLAGS   = $(COMPILERFLAGS) -QIA64_Bx
+!else
+!message *** Compiler does not have 'B-stepping errata workarounds'
+!endif
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for -align:4096, when align:512 will do.
+!if [nmakehlp -l -opt:nowin98]
+!message *** Linker has 'Win98 alignment problem'
+ALIGN98_HACK	= 1
+!else
+!message *** Linker does not have 'Win98 alignment problem'
+ALIGN98_HACK	= 0
+!endif
+!else
+ALIGN98_HACK	= 0
+!endif
+
+LINKERFLAGS     =
+
+!if [nmakehlp -l -ltcg]
+LINKERFLAGS     =-ltcg
+!endif
+
+#----------------------------------------------------------
+# MSVC8 (ships with Visual Studio 2005) generates a manifest
+# file that we should link into the binaries. This is how.
+#----------------------------------------------------------
+
+_VC_MANIFEST_EMBED_EXE=
+_VC_MANIFEST_EMBED_DLL=
+VCVER=0
+!if ![echo VCVERSION=_MSC_VER > vercl.x] \
+    && ![cl -nologo -TC -P vercl.x $(ERRNULL)]
+!include vercl.i
+!if $(VCVERSION) >= 1500
+VCVER=9
+!elseif $(VCVERSION) >= 1400
+VCVER=8
+!elseif $(VCVERSION) >= 1300
+VCVER=7
+!elseif $(VCVERSION) >= 1200
+VCVER=6
+!endif
+!endif
+
+# Since MSVC8 we must deal with manifest resources.
+!if $(VCVERSION) >= 1400
+_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1
+_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
+!endif
+
+#----------------------------------------------------------
+# Decode the options requested.
+#----------------------------------------------------------
+
+!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]
+STATIC_BUILD	= 0
+TCL_THREADS	= 1
+DEBUG		= 0
+PROFILE		= 0
+MSVCRT		= 0
+LOIMPACT	= 0
+TCL_USE_STATIC_PACKAGES	= 0
+USE_THREAD_ALLOC = 1
+USE_THREAD_STORAGE = 1
+UNCHECKED       = 0
+!else
+!if [nmakehlp -f $(OPTS) "static"]
+!message *** Doing static
+STATIC_BUILD	= 1
+!else
+STATIC_BUILD	= 0
+!endif
+!if [nmakehlp -f $(OPTS) "msvcrt"]
+!message *** Doing msvcrt
+MSVCRT		= 1
+!else
+MSVCRT		= 0
+!endif
+!if [nmakehlp -f $(OPTS) "staticpkg"]
+!message *** Doing staticpkg
+TCL_USE_STATIC_PACKAGES	= 1
+!else
+TCL_USE_STATIC_PACKAGES	= 0
+!endif
+!if [nmakehlp -f $(OPTS) "nothreads"]
+!message *** Compile explicitly for non-threaded tcl
+TCL_THREADS	= 0
+!else
+TCL_THREADS     = 1
+!endif
+!if [nmakehlp -f $(OPTS) "symbols"]
+!message *** Doing symbols
+DEBUG		= 1
+!else
+DEBUG		= 0
+!endif
+!if [nmakehlp -f $(OPTS) "profile"]
+!message *** Doing profile
+PROFILE		= 1
+!else
+PROFILE		= 0
+!endif
+!if [nmakehlp -f $(OPTS) "loimpact"]
+!message *** Doing loimpact
+LOIMPACT	= 1
+!else
+LOIMPACT	= 0
+!endif
+!if [nmakehlp -f $(OPTS) "thrdalloc"]
+!message *** Doing thrdalloc
+USE_THREAD_ALLOC = 1
+!else
+USE_THREAD_ALLOC = 0
+!endif
+!if [nmakehlp -f $(OPTS) "thrdstorage"]
+!message *** Doing thrdstorage
+USE_THREAD_STORAGE = 1
+!else
+USE_THREAD_STORAGE = 0
+!endif
+!if [nmakehlp -f $(OPTS) "unchecked"]
+!message *** Doing unchecked
+UNCHECKED = 1
+!else
+UNCHECKED = 0
+!endif
+!endif
+
+
+!if !$(STATIC_BUILD)
+# Make sure we don't build overly fat DLLs.
+MSVCRT		= 1
+# We shouldn't statically put the extensions inside the shell when dynamic.
+TCL_USE_STATIC_PACKAGES = 0
+!endif
+
+
+#----------------------------------------------------------
+# Figure-out how to name our intermediate and output directories.
+# We wouldn't want different builds to use the same .obj files
+# by accident.
+#----------------------------------------------------------
+
+#----------------------------------------
+# Naming convention:
+#   t = full thread support.
+#   s = static library (as opposed to an
+#	import library)
+#   g = linked to the debug enabled C
+#	run-time.
+#   x = special static build when it
+#	links to the dynamic C run-time.
+#----------------------------------------
+SUFX	    = sgx
+
+!if $(DEBUG)
+BUILDDIRTOP = Debug
+!else
+BUILDDIRTOP = Release
+!endif
+
+!if "$(MACHINE)" != "IX86"
+BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE)
+!endif
+!if $(VCVER) > 6
+BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER)
+!endif
+
+!if !$(DEBUG) || $(DEBUG) && $(UNCHECKED)
+SUFX	    = $(SUFX:g=)
+!endif
+
+TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX
+
+!if !$(STATIC_BUILD)
+TMP_DIRFULL = $(TMP_DIRFULL:Static=)
+SUFX	    = $(SUFX:s=)
+EXT	    = dll
+!if $(MSVCRT)
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX	    = $(SUFX:x=)
+!endif
+!else
+TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
+EXT	    = lib
+!if !$(MSVCRT)
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX	    = $(SUFX:x=)
+!endif
+!endif
+
+!if !$(TCL_THREADS)
+TMP_DIRFULL = $(TMP_DIRFULL:Threaded=)
+SUFX	    = $(SUFX:t=)
+!endif
+
+!ifndef TMP_DIR
+TMP_DIR	    = $(TMP_DIRFULL)
+!ifndef OUT_DIR
+OUT_DIR	    = .\$(BUILDDIRTOP)
+!endif
+!else
+!ifndef OUT_DIR
+OUT_DIR	    = $(TMP_DIR)
+!endif
+!endif
+
+
+#----------------------------------------------------------
+# Decode the statistics requested.
+#----------------------------------------------------------
+
+!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"]
+TCL_MEM_DEBUG	    = 0
+TCL_COMPILE_DEBUG   = 0
+!else
+!if [nmakehlp -f $(STATS) "memdbg"]
+!message *** Doing memdbg
+TCL_MEM_DEBUG	    = 1
+!else
+TCL_MEM_DEBUG	    = 0
+!endif
+!if [nmakehlp -f $(STATS) "compdbg"]
+!message *** Doing compdbg
+TCL_COMPILE_DEBUG   = 1
+!else
+TCL_COMPILE_DEBUG   = 0
+!endif
+!endif
+
+
+#----------------------------------------------------------
+# Decode the checks requested.
+#----------------------------------------------------------
+
+!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"]
+TCL_NO_DEPRECATED	    = 0
+WARNINGS		    = -W3
+!else
+!if [nmakehlp -f $(CHECKS) "nodep"]
+!message *** Doing nodep check
+TCL_NO_DEPRECATED	    = 1
+!else
+TCL_NO_DEPRECATED	    = 0
+!endif
+!if [nmakehlp -f $(CHECKS) "fullwarn"]
+!message *** Doing full warnings check
+WARNINGS		    = -W4
+!if [nmakehlp -l -warn:3]
+LINKERFLAGS		    = $(LINKERFLAGS) -warn:3
+!endif
+!else
+WARNINGS		    = -W3
+!endif
+!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
+!message *** Doing 64bit portability warnings
+WARNINGS		    = $(WARNINGS) -Wp64
+!endif
+!endif
+
+#----------------------------------------------------------
+# Set our defines now armed with our options.
+#----------------------------------------------------------
+
+OPTDEFINES	= -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS
+
+!if $(TCL_MEM_DEBUG)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_MEM_DEBUG
+!endif
+!if $(TCL_COMPILE_DEBUG)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
+!endif
+!if $(TCL_THREADS)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_THREADS=1
+!if $(USE_THREAD_ALLOC)
+OPTDEFINES	= $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
+!endif
+!if $(USE_THREAD_STORAGE)
+OPTDEFINES	= $(OPTDEFINES) -DUSE_THREAD_STORAGE=1
+!endif
+!endif
+!if $(STATIC_BUILD)
+OPTDEFINES	= $(OPTDEFINES) -DSTATIC_BUILD
+!endif
+!if $(TCL_NO_DEPRECATED)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_NO_DEPRECATED
+!endif
+
+!if $(DEBUG)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_DEBUG
+!elseif $(OPTIMIZING)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
+!endif
+!if $(PROFILE)
+OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_PROFILED
+!endif
+!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
+OPTDEFINES	= $(OPTDEFINES) -DTCL_CFG_DO64BIT
+!endif
+
+
+#----------------------------------------------------------
+# Get common info used when building extensions.
+#----------------------------------------------------------
+
+!if "$(PROJECT)" != "tcl"
+
+# If INSTALLDIR set to tcl root dir then reset to the lib dir.
+!if exist("$(_INSTALLDIR)\include\tcl.h")
+_INSTALLDIR=$(_INSTALLDIR)\lib
+!endif
+
+!if !defined(TCLDIR)
+!if exist("$(_INSTALLDIR)\..\include\tcl.h")
+TCLINSTALL	= 1
+_TCLDIR		= $(_INSTALLDIR)\..
+_TCL_H          = $(_INSTALLDIR)\..\include\tcl.h
+TCLDIR          = $(_INSTALLDIR)\..
+!else
+MSG=^
+Failed to find tcl.h.  Set the TCLDIR macro.
+!error $(MSG)
+!endif
+!else
+_TCLDIR	= $(TCLDIR:/=\)
+!if exist("$(_TCLDIR)\include\tcl.h")
+TCLINSTALL	= 1
+_TCL_H          = $(_TCLDIR)\include\tcl.h
+!elseif exist("$(_TCLDIR)\generic\tcl.h")
+TCLINSTALL	= 0
+_TCL_H          = $(_TCLDIR)\generic\tcl.h
+!else
+MSG =^
+Failed to find tcl.h.  The TCLDIR macro does not appear correct.
+!error $(MSG)
+!endif
+!endif
+
+!if [echo REM = This file is generated from rules.vc > version.vc]
+!endif
+!if exist("$(_TCL_H)")
+!if [echo TCL_DOTVERSION = \>> version.vc] \
+   && [nmakehlp -V "$(_TCL_H)" TCL_VERSION >> version.vc]
+!endif
+!endif
+!include version.vc
+TCL_VERSION	= $(TCL_DOTVERSION:.=)
+
+!if $(TCLINSTALL)
+TCLSH		= "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe"
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH           = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe"
+!endif
+TCLSTUBLIB	= "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib"
+TCLIMPLIB	= "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib"
+TCL_LIBRARY	= $(_TCLDIR)\lib
+TCL_INCLUDES    = -I"$(_TCLDIR)\include"
+!else
+TCLSH		= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe"
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH		= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe"
+!endif
+TCLSTUBLIB	= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib"
+TCLIMPLIB	= "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib"
+TCL_LIBRARY	= $(_TCLDIR)\library
+TCL_INCLUDES	= -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
+!endif
+
+!endif
+
+#----------------------------------------------------------
+# Optionally check for Tk info for building extensions.
+#----------------------------------------------------------
+
+!ifdef PROJECT_REQUIRES_TK
+!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"
+
+!if !defined(TKDIR)
+!if exist("$(_INSTALLDIR)\..\include\tk.h")
+TKINSTALL      = 1
+_TKDIR         = $(_INSTALLDIR)\..
+_TK_H          = $(_TKDIR)\include\tk.h
+TKDIR          = $(_TKDIR)
+!elseif exist("$(_TCLDIR)\include\tk.h")
+TKINSTALL      = 1
+_TKDIR         = $(_TCLDIR)
+_TK_H          = $(_TKDIR)\include\tk.h
+TKDIR          = $(_TKDIR)
+!endif
+!else
+_TKDIR = $(TKDIR:/=\)
+!if exist("$(_TKDIR)\include\tk.h")
+TKINSTALL      = 1
+_TK_H          = $(_TKDIR)\include\tk.h
+!elseif exist("$(_TKDIR)\generic\tk.h")
+TKINSTALL      = 0
+_TK_H          = $(_TKDIR)\generic\tk.h
+!else
+MSG =^
+Failed to find tk.h. The TKDIR macro does not appear correct.
+!error $(MSG)
+!endif
+!endif
+
+!if defined(TKDIR)
+TK_DOTVERSION = 8.4
+!if exist("$(_TK_H)")
+!if [echo TK_DOTVERSION = \>> version.vc] \
+   && [nmakehlp -V "$(_TK_H)" TK_VERSION >> version.vc]
+!endif
+!endif
+!include version.vc
+TK_VERSION = $(TK_DOTVERSION:.=)
+
+!if $(TKINSTALL)
+WISH		= "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe"
+!if !exist($(WISH)) && $(TCL_THREADS)
+WISH		= "$(_TKDIR)\bin\wish$(TK_VERSION)t$(SUFX).exe"
+!endif
+TKSTUBLIB	= "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib"
+TKIMPLIB	= "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib"
+TK_INCLUDES     = -I"$(_TKDIR)\include"
+TK_LIBRARY	= $(_TKDIR)\lib
+!else
+WISH		= "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe"
+!if !exist($(WISH)) && $(TCL_THREADS)
+WISH		= "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)t$(SUFX).exe"
+!endif
+TKSTUBLIB	= "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib"
+TKIMPLIB	= "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib"
+TK_INCLUDES     = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+TK_LIBRARY	= $(_TKDIR)\library
+!endif
+
+!endif
+!endif
+!endif
+
+
+#----------------------------------------------------------
+# Setup the fully qualified OUT_DIR path as OUT_DIR_PATH
+#----------------------------------------------------------
+!if [echo OUT_DIR_PATH = \>> version.vc] \
+    && [nmakehlp -Q "$(OUT_DIR)" >> version.vc]
+!endif
+!include version.vc
+
+
+#----------------------------------------------------------
+# Display stats being used.
+#----------------------------------------------------------
+
+!message *** Intermediate directory will be '$(TMP_DIR)'
+!message *** Output directory will be '$(OUT_DIR)'
+!message *** Suffix for binaries will be '$(SUFX)'
+!message *** Optional defines are '$(OPTDEFINES)'
+!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
+!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)'
+!message *** Link options '$(LINKERFLAGS)'
+
+!endif
Index: trunk/kitgen/8.x/blt/win/sample.rc
===================================================================
--- trunk/kitgen/8.x/blt/win/sample.rc	(revision 175)
+++ trunk/kitgen/8.x/blt/win/sample.rc	(revision 175)
@@ -0,0 +1,38 @@
+// sample.rc - Copyright (C) 2006 Pat Thoyts <patthoyts@users.sourceforge.net>
+//
+// There is no need to modify this file.
+//
+
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION	COMMAVERSION
+ PRODUCTVERSION	COMMAVERSION
+ FILEFLAGSMASK	0x3fL
+#ifdef DEBUG
+ FILEFLAGS	VS_FF_DEBUG
+#else
+ FILEFLAGS	0x0L
+#endif
+ FILEOS		VOS__WINDOWS32
+ FILETYPE	VFT_DLL
+ FILESUBTYPE	0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription",  "Tcl Sample Extension " DOTVERSION "\0"
+            VALUE "OriginalFilename", "sample" VERSION ".dll\0"
+            VALUE "CompanyName",      "The Tcl Development Community\0"
+            VALUE "FileVersion",      DOTVERSION "\0"
+            VALUE "LegalCopyright",   "Copyright \251 1999 Scriptics Corp.\0"
+            VALUE "ProductName",      "Tcl Sample Extension " DOTVERSION "\0"
+            VALUE "ProductVersion",   DOTVERSION "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
Index: trunk/kitgen/8.x/libusb-win32/AUTHORS.txt
===================================================================
--- trunk/kitgen/8.x/libusb-win32/AUTHORS.txt	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/AUTHORS.txt	(revision 175)
@@ -0,0 +1,16 @@
+
+Library, Test Programs:
+
+Stephan Meyer, <ste_meyer@web.de>
+Johannes Erdfelt, <johannes@erdfelt.com>
+Thomas Sailer, <sailer@ife.ee.ethz.ch>
+
+Drivers, Installer:
+
+Stephan Meyer, <ste_meyer@web.de>
+Travis Robinson, <libusbdotnet@gmail.com>
+
+Testing, Technical support:
+
+Xiaofan Chen, <xiaofanc@gmail.com>
+
Index: trunk/kitgen/8.x/libusb-win32/COPYING_GPL.txt
===================================================================
--- trunk/kitgen/8.x/libusb-win32/COPYING_GPL.txt	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/COPYING_GPL.txt	(revision 175)
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Index: trunk/kitgen/8.x/libusb-win32/COPYING_LGPL.txt
===================================================================
--- trunk/kitgen/8.x/libusb-win32/COPYING_LGPL.txt	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/COPYING_LGPL.txt	(revision 175)
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
Index: trunk/kitgen/8.x/libusb-win32/Makefile
===================================================================
--- trunk/kitgen/8.x/libusb-win32/Makefile	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/Makefile	(revision 175)
@@ -0,0 +1,15 @@
+CC = gcc
+CFLAGS += -O2 -Wall -mno-cygwin -I.
+AR = ar
+
+OBJS = descriptors.o error.o usb.o windows.o
+
+libusb.a: $(OBJS)
+	$(AR) -rcsv $@ $^
+
+clean:
+	rm -f *.o *.a
+
+
+
+
Index: trunk/kitgen/8.x/libusb-win32/descriptors.c
===================================================================
--- trunk/kitgen/8.x/libusb-win32/descriptors.c	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/descriptors.c	(revision 175)
@@ -0,0 +1,572 @@
+/*
+ * Parses descriptors
+ *
+ * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "usbi.h"
+
+int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep,
+                                   unsigned char type, unsigned char index, void *buf, int size)
+{
+    memset(buf, 0, size);
+
+    return usb_control_msg(udev, ep | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+                           (type << 8) + index, 0, buf, size, 1000);
+}
+
+int usb_get_descriptor(usb_dev_handle *udev, unsigned char type,
+                       unsigned char index, void *buf, int size)
+{
+    memset(buf, 0, size);
+
+    return usb_control_msg(udev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+                           (type << 8) + index, 0, buf, size, 1000);
+}
+
+int usb_parse_descriptor(unsigned char *source, char *description, void *dest)
+{
+    unsigned char *sp = source, *dp = dest;
+    uint16_t w;
+    uint32_t d;
+    char *cp;
+
+    for (cp = description; *cp; cp++)
+    {
+        switch (*cp)
+        {
+        case 'b':	/* 8-bit byte */
+            *dp++ = *sp++;
+            break;
+        case 'w':	/* 16-bit word, convert from little endian to CPU */
+            w = (sp[1] << 8) | sp[0];
+            sp += 2;
+            //dp += ((unsigned long)dp & 1);	/* Align to word boundary */
+            *((uint16_t *)dp) = w;
+            dp += 2;
+            break;
+        case 'd':	/* 32-bit dword, convert from little endian to CPU */
+            d = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0];
+            sp += 4;
+            //dp += ((unsigned long)dp & 2);	/* Align to dword boundary */
+            *((uint32_t *)dp) = d;
+            dp += 4;
+            break;
+            /* These two characters are undocumented and just a hack for Linux */
+        case 'W':	/* 16-bit word, keep CPU endianess */
+            //dp += ((unsigned long)dp & 1);	/* Align to word boundary */
+            memcpy(dp, sp, 2);
+            sp += 2;
+            dp += 2;
+            break;
+        case 'D':	/* 32-bit dword, keep CPU endianess */
+            //dp += ((unsigned long)dp & 2);	/* Align to dword boundary */
+            memcpy(dp, sp, 4);
+            sp += 4;
+            dp += 4;
+            break;
+        }
+    }
+
+    return (int)(sp - source);
+}
+
+/*
+ * This code looks surprisingly similar to the code I wrote for the Linux
+ * kernel. It's not a coincidence :)
+ */
+
+static int usb_parse_endpoint(struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size)
+{
+    struct usb_descriptor_header header;
+    unsigned char *begin;
+    int parsed = 0, len, numskipped;
+
+    usb_parse_descriptor(buffer, "bb", &header);
+
+    /* Everything should be fine being passed into here, but we sanity */
+    /*  check JIC */
+    if (header.bLength > size)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "ran out of descriptors parsing\n");
+        return -1;
+    }
+
+    if (header.bDescriptorType != USB_DT_ENDPOINT)
+    {
+        if (usb_debug >= 2)
+            fprintf(stderr, "unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X\n",
+                    header.bDescriptorType, USB_DT_ENDPOINT);
+        return parsed;
+    }
+
+    if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
+        usb_parse_descriptor(buffer, "bbbbwbbb", endpoint);
+    else if (header.bLength >= ENDPOINT_DESC_LENGTH)
+        usb_parse_descriptor(buffer, "bbbbwb", endpoint);
+
+    buffer += header.bLength;
+    size -= header.bLength;
+    parsed += header.bLength;
+
+    /* Skip over the rest of the Class Specific or Vendor Specific */
+    /*  descriptors */
+    begin = buffer;
+    numskipped = 0;
+    while (size >= DESC_HEADER_LENGTH)
+    {
+        usb_parse_descriptor(buffer, "bb", &header);
+
+        if (header.bLength < 2)
+        {
+            if (usb_debug >= 1)
+                fprintf(stderr, "invalid descriptor length of %d\n", header.bLength);
+            return -1;
+        }
+
+        /* If we find another "proper" descriptor then we're done  */
+        if ((header.bDescriptorType == USB_DT_ENDPOINT) ||
+                (header.bDescriptorType == USB_DT_INTERFACE) ||
+                (header.bDescriptorType == USB_DT_CONFIG) ||
+                (header.bDescriptorType == USB_DT_DEVICE))
+            break;
+
+        if (usb_debug >= 1)
+            fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType);
+        numskipped++;
+
+        buffer += header.bLength;
+        size -= header.bLength;
+        parsed += header.bLength;
+    }
+
+    if (numskipped && usb_debug >= 2)
+        fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped);
+
+    /* Copy any unknown descriptors into a storage area for drivers */
+    /*  to later parse */
+    len = (int)(buffer - begin);
+    if (!len)
+    {
+        endpoint->extra = NULL;
+        endpoint->extralen = 0;
+        return parsed;
+    }
+
+    endpoint->extra = malloc(len);
+    if (!endpoint->extra)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "couldn't allocate memory for endpoint extra descriptors\n");
+        endpoint->extralen = 0;
+        return parsed;
+    }
+
+    memcpy(endpoint->extra, begin, len);
+    endpoint->extralen = len;
+
+    return parsed;
+}
+
+static int usb_parse_interface(struct usb_interface *interface,
+                               unsigned char *buffer, int size)
+{
+    int i, len, numskipped, retval, parsed = 0;
+    struct usb_descriptor_header header;
+    struct usb_interface_descriptor *ifp;
+    unsigned char *begin;
+
+    interface->num_altsetting = 0;
+
+    while (size >= INTERFACE_DESC_LENGTH)
+    {
+        interface->altsetting = realloc(interface->altsetting, sizeof(struct usb_interface_descriptor) * (interface->num_altsetting + 1));
+        if (!interface->altsetting)
+        {
+            if (usb_debug >= 1)
+                fprintf(stderr, "couldn't malloc interface->altsetting\n");
+            return -1;
+        }
+
+        ifp = interface->altsetting + interface->num_altsetting;
+        interface->num_altsetting++;
+
+        usb_parse_descriptor(buffer, "bbbbbbbbb", ifp);
+
+        /* Skip over the interface */
+        buffer += ifp->bLength;
+        parsed += ifp->bLength;
+        size -= ifp->bLength;
+
+        begin = buffer;
+        numskipped = 0;
+
+        /* Skip over any interface, class or vendor descriptors */
+        while (size >= DESC_HEADER_LENGTH)
+        {
+            usb_parse_descriptor(buffer, "bb", &header);
+
+            if (header.bLength < 2)
+            {
+                if (usb_debug >= 1)
+                    fprintf(stderr, "invalid descriptor length of %d\n", header.bLength);
+                return -1;
+            }
+
+            /* If we find another "proper" descriptor then we're done */
+            if ((header.bDescriptorType == USB_DT_INTERFACE) ||
+                    (header.bDescriptorType == USB_DT_ENDPOINT) ||
+                    (header.bDescriptorType == USB_DT_CONFIG) ||
+                    (header.bDescriptorType == USB_DT_DEVICE))
+                break;
+
+            numskipped++;
+
+            buffer += header.bLength;
+            parsed += header.bLength;
+            size -= header.bLength;
+        }
+
+        if (numskipped && usb_debug >= 2)
+            fprintf(stderr, "skipped %d class/vendor specific interface descriptors\n", numskipped);
+
+        /* Copy any unknown descriptors into a storage area for */
+        /*  drivers to later parse */
+        len = (int)(buffer - begin);
+        if (!len)
+        {
+            ifp->extra = NULL;
+            ifp->extralen = 0;
+        }
+        else
+        {
+            ifp->extra = malloc(len);
+            if (!ifp->extra)
+            {
+                if (usb_debug >= 1)
+                    fprintf(stderr, "couldn't allocate memory for interface extra descriptors\n");
+                ifp->extralen = 0;
+                return -1;
+            }
+            memcpy(ifp->extra, begin, len);
+            ifp->extralen = len;
+        }
+
+        /* Did we hit an unexpected descriptor? */
+        usb_parse_descriptor(buffer, "bb", &header);
+        if ((size >= DESC_HEADER_LENGTH) &&
+                ((header.bDescriptorType == USB_DT_CONFIG) ||
+                 (header.bDescriptorType == USB_DT_DEVICE)))
+            return parsed;
+
+        if (ifp->bNumEndpoints > USB_MAXENDPOINTS)
+        {
+            if (usb_debug >= 1)
+                fprintf(stderr, "too many endpoints\n");
+            return -1;
+        }
+
+        if (ifp->bNumEndpoints > 0)
+        {
+            ifp->endpoint = (struct usb_endpoint_descriptor *)
+                            malloc(ifp->bNumEndpoints *
+                                   sizeof(struct usb_endpoint_descriptor));
+            if (!ifp->endpoint)
+            {
+                if (usb_debug >= 1)
+                    fprintf(stderr, "couldn't allocate memory for ifp->endpoint\n");
+                return -1;
+            }
+
+            memset(ifp->endpoint, 0, ifp->bNumEndpoints *
+                   sizeof(struct usb_endpoint_descriptor));
+
+            for (i = 0; i < ifp->bNumEndpoints; i++)
+            {
+                usb_parse_descriptor(buffer, "bb", &header);
+
+                if (header.bLength > size)
+                {
+                    if (usb_debug >= 1)
+                        fprintf(stderr, "ran out of descriptors parsing\n");
+                    return -1;
+                }
+
+                retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
+                if (retval < 0)
+                    return retval;
+
+                buffer += retval;
+                parsed += retval;
+                size -= retval;
+            }
+        }
+        else
+            ifp->endpoint = NULL;
+
+        /* We check to see if it's an alternate to this one */
+        ifp = (struct usb_interface_descriptor *)buffer;
+        if (size < USB_DT_INTERFACE_SIZE ||
+                ifp->bDescriptorType != USB_DT_INTERFACE ||
+                !ifp->bAlternateSetting)
+            return parsed;
+    }
+
+    return parsed;
+}
+
+int usb_parse_configuration(struct usb_config_descriptor *config,
+                            unsigned char *buffer)
+{
+    int i, retval, size;
+    struct usb_descriptor_header header;
+
+    usb_parse_descriptor(buffer, "bbwbbbbb", config);
+    size = config->wTotalLength;
+
+    if (config->bNumInterfaces > USB_MAXINTERFACES)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "too many interfaces\n");
+        return -1;
+    }
+
+    config->interface = (struct usb_interface *)
+                        malloc(config->bNumInterfaces *
+                               sizeof(struct usb_interface));
+    if (!config->interface)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+
+    memset(config->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface));
+
+    buffer += config->bLength;
+    size -= config->bLength;
+
+    config->extra = NULL;
+    config->extralen = 0;
+
+    for (i = 0; i < config->bNumInterfaces; i++)
+    {
+        int numskipped, len;
+        unsigned char *begin;
+
+        /* Skip over the rest of the Class Specific or Vendor */
+        /*  Specific descriptors */
+        begin = buffer;
+        numskipped = 0;
+        while (size >= DESC_HEADER_LENGTH)
+        {
+            usb_parse_descriptor(buffer, "bb", &header);
+
+            if ((header.bLength > size) || (header.bLength < DESC_HEADER_LENGTH))
+            {
+                if (usb_debug >= 1)
+                    fprintf(stderr, "invalid descriptor length of %d\n", header.bLength);
+                return -1;
+            }
+
+            /* If we find another "proper" descriptor then we're done */
+            if ((header.bDescriptorType == USB_DT_ENDPOINT) ||
+                    (header.bDescriptorType == USB_DT_INTERFACE) ||
+                    (header.bDescriptorType == USB_DT_CONFIG) ||
+                    (header.bDescriptorType == USB_DT_DEVICE))
+                break;
+
+            if (usb_debug >= 2)
+                fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType);
+            numskipped++;
+
+            buffer += header.bLength;
+            size -= header.bLength;
+        }
+
+        if (numskipped && usb_debug >= 2)
+            fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped);
+
+        /* Copy any unknown descriptors into a storage area for */
+        /*  drivers to later parse */
+        len = (int)(buffer - begin);
+        if (len)
+        {
+            /* FIXME: We should realloc and append here */
+            if (!config->extralen)
+            {
+                config->extra = malloc(len);
+                if (!config->extra)
+                {
+                    if (usb_debug >= 1)
+                        fprintf(stderr, "couldn't allocate memory for config extra descriptors\n");
+                    config->extralen = 0;
+                    return -1;
+                }
+
+                memcpy(config->extra, begin, len);
+                config->extralen = len;
+            }
+        }
+
+        retval = usb_parse_interface(config->interface + i, buffer, size);
+        if (retval < 0)
+            return retval;
+
+        buffer += retval;
+        size -= retval;
+    }
+
+    return size;
+}
+
+void usb_destroy_configuration(struct usb_device *dev)
+{
+    int c, i, j, k;
+
+    if (!dev->config)
+        return;
+
+    for (c = 0; c < dev->descriptor.bNumConfigurations; c++)
+    {
+        struct usb_config_descriptor *cf = &dev->config[c];
+
+        if (!cf->interface)
+            continue;
+
+        for (i = 0; i < cf->bNumInterfaces; i++)
+        {
+            struct usb_interface *ifp = &cf->interface[i];
+
+            if (!ifp->altsetting)
+                continue;
+
+            for (j = 0; j < ifp->num_altsetting; j++)
+            {
+                struct usb_interface_descriptor *as = &ifp->altsetting[j];
+
+                if (as->extra)
+                    free(as->extra);
+
+                if (!as->endpoint)
+                    continue;
+
+                for (k = 0; k < as->bNumEndpoints; k++)
+                {
+                    if (as->endpoint[k].extra)
+                        free(as->endpoint[k].extra);
+                }
+                free(as->endpoint);
+            }
+
+            free(ifp->altsetting);
+        }
+
+        free(cf->interface);
+    }
+
+    free(dev->config);
+}
+
+void usb_fetch_and_parse_descriptors(usb_dev_handle *udev)
+{
+    struct usb_device *dev = udev->device;
+    int i;
+
+    if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "Too many configurations (%d > %d)\n", dev->descriptor.bNumConfigurations, USB_MAXCONFIG);
+        return;
+    }
+
+    if (dev->descriptor.bNumConfigurations < 1)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "Not enough configurations (%d < %d)\n", dev->descriptor.bNumConfigurations, 1);
+        return;
+    }
+
+    dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor));
+    if (!dev->config)
+    {
+        if (usb_debug >= 1)
+            fprintf(stderr, "Unable to allocate memory for config descriptor\n");
+        return;
+    }
+
+    memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+           sizeof(struct usb_config_descriptor));
+
+    for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+    {
+        unsigned char buffer[USB_DT_CONFIG_SIZE], *bigbuffer;
+        struct usb_config_descriptor config;
+        int res;
+
+        /* Get the first 8 bytes so we can figure out what the total length is */
+        res = usb_get_descriptor(udev, USB_DT_CONFIG, (unsigned char)i, buffer, USB_DT_CONFIG_SIZE);
+        if (res < USB_DT_CONFIG_SIZE)
+        {
+            if (usb_debug >= 1)
+            {
+                if (res < 0)
+                    fprintf(stderr, "Unable to get descriptor (%d)\n", res);
+                else
+                    fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", USB_DT_CONFIG_SIZE, res);
+            }
+
+            goto err;
+        }
+
+        usb_parse_descriptor(buffer, "bbw", &config);
+
+        bigbuffer = malloc(config.wTotalLength);
+        if (!bigbuffer)
+        {
+            if (usb_debug >= 1)
+                fprintf(stderr, "Unable to allocate memory for descriptors\n");
+            goto err;
+        }
+
+        res = usb_get_descriptor(udev, USB_DT_CONFIG, (unsigned char)i, bigbuffer,
+                                 config.wTotalLength);
+        if (res < config.wTotalLength)
+        {
+            if (usb_debug >= 1)
+            {
+                if (res < 0)
+                    fprintf(stderr, "Unable to get descriptor (%d)\n", res);
+                else
+                    fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, res);
+            }
+
+            free(bigbuffer);
+            goto err;
+        }
+
+        res = usb_parse_configuration(&dev->config[i], bigbuffer);
+        if (usb_debug >= 2)
+        {
+            if (res > 0)
+                fprintf(stderr, "Descriptor data still left\n");
+            else if (res < 0)
+                fprintf(stderr, "Unable to parse descriptors\n");
+        }
+
+        free(bigbuffer);
+    }
+
+    return;
+
+err:
+    free(dev->config);
+
+    dev->config = NULL;
+}
+
Index: trunk/kitgen/8.x/libusb-win32/driver_api.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/driver_api.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/driver_api.h	(revision 175)
@@ -0,0 +1,400 @@
+/* libusb-win32, Generic Windows USB Library
+ * Copyright (c) 2002-2005 Stephan Meyer <ste_meyer@web.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef __DRIVER_API_H__
+#define __DRIVER_API_H__
+
+enum
+{
+    LIBUSB_DEBUG_OFF,
+    LIBUSB_DEBUG_ERR,
+    LIBUSB_DEBUG_WRN,
+    LIBUSB_DEBUG_MSG,
+
+    LIBUSB_DEBUG_MAX = 0xff,
+};
+
+
+/* 64k */
+#define LIBUSB_MAX_READ_WRITE 0x10000
+
+#define LIBUSB_MAX_NUMBER_OF_DEVICES 256
+#define LIBUSB_MAX_NUMBER_OF_CHILDREN 32
+
+#define LIBUSB_IOCTL_SET_CONFIGURATION CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_CONFIGURATION CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_FEATURE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_CLEAR_FEATURE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80A, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80B, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_VENDOR_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80C, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_VENDOR_READ CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80D, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_RESET_ENDPOINT CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80E, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_ABORT_ENDPOINT CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x80F, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_RESET_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_DEBUG_LEVEL CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x811, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x812, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_ISOCHRONOUS_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x813, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_ISOCHRONOUS_READ CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x814, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_CLAIM_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x815, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_RELEASE_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x816, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/////////////////////////////////////////////////////////////////////////////
+// supported after 0.1.12.2
+/////////////////////////////////////////////////////////////////////////////
+
+// [trobinso] adds support for querying device properties
+#define LIBUSB_IOCTL_GET_DEVICE_PROPERTY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_CUSTOM_REG_PROPERTY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x901, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// supported after 1.2.0.0
+/////////////////////////////////////////////////////////////////////////////
+#define LIBUSB_IOCTL_GET_CACHED_CONFIGURATION CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
+/////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// supported after 1.2.2.0
+/////////////////////////////////////////////////////////////////////////////
+#define LIBUSB_IOCTL_GET_OBJECT_NAME CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x8FF, METHOD_BUFFERED, FILE_ANY_ACCESS)
+/////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+// supported after 1.2.3.0
+/////////////////////////////////////////////////////////////////////////////
+#define LIBUSB_IOCTL_QUERY_DEVICE_INFORMATION CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x904, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_PIPE_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x906, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_PIPE_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x907, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_SET_POWER_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x908, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_GET_POWER_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x909, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_CONTROL_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90A, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_CONTROL_READ CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90B, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+#define LIBUSB_IOCTL_FLUSH_PIPE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90C, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSBK_IOCTL_CLAIM_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90D, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSBK_IOCTL_RELEASE_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90E, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSBK_IOCTL_RELEASE_ALL_INTERFACES CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x90F, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSBK_IOCTL_SET_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x910, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define LIBUSBK_IOCTL_GET_INTERFACE CTL_CODE(FILE_DEVICE_UNKNOWN,\
+        0x911, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/////////////////////////////////////////////////////////////////////////////
+// supported after 1.2.4.8 (libusb0.sys only)
+/////////////////////////////////////////////////////////////////////////////
+#define LIBUSB_IOCTL_RESET_DEVICE_EX CTL_CODE(FILE_DEVICE_UNKNOWN,\
+0x817, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#include <pshpack1.h>
+
+enum LIBUSB0_TRANSFER_FLAGS
+{
+	TRANSFER_FLAGS_SHORT_NOT_OK = 1 << 0,
+	TRANSFER_FLAGS_ISO_SET_START_FRAME = 1 << 30,
+	TRANSFER_FLAGS_ISO_ADD_LATENCY = 1 << 31,
+};
+
+/*
+typedef struct
+{
+    unsigned int timeout;
+    union
+    {
+        struct
+        {
+            unsigned int configuration;
+        } configuration;
+        struct
+        {
+            unsigned int interface;
+            unsigned int altsetting;
+        } interface;
+        struct
+        {
+            unsigned int endpoint;
+            unsigned int packet_size;
+	
+			// TODO: max_transfer_size, short transfer not ok, use iso_start_frame
+			unsigned int max_transfer_size;
+			unsigned int transfer_flags;
+			unsigned int iso_start_frame_latency;
+        } endpoint;
+        struct
+        {
+            unsigned int type;
+            unsigned int recipient;
+            unsigned int request;
+            unsigned int value;
+            unsigned int index;
+        } vendor;
+        struct
+        {
+            unsigned int recipient;
+            unsigned int feature;
+            unsigned int index;
+        } feature;
+        struct
+        {
+            unsigned int recipient;
+            unsigned int index;
+            unsigned int status;
+        } status;
+        struct
+        {
+            unsigned int type;
+            unsigned int index;
+            unsigned int language_id;
+            unsigned int recipient;
+        } descriptor;
+        struct
+        {
+            unsigned int level;
+        } debug;
+        struct
+        {
+            unsigned int major;
+            unsigned int minor;
+            unsigned int micro;
+            unsigned int nano;
+			unsigned int mod_value;
+        } version;
+		struct
+		{
+			unsigned int property;
+		} device_property;
+		struct
+		{
+			unsigned int key_type;
+			unsigned int name_offset;
+			unsigned int value_offset;
+			unsigned int value_length;
+		} device_registry_key;
+		struct
+		{
+			// 0 - device plug and play registry key pathname
+			unsigned int objname_index;
+		} objname;
+    };
+} libusb_request;
+*/
+
+#pragma warning(disable:4201)
+
+typedef struct
+{
+	unsigned int interface_number;
+	unsigned int altsetting_number;
+
+	unsigned char intf_use_index:1;	// libusbK Only
+	unsigned char altf_use_index:1;	// libusbK Only
+	unsigned char:6;
+
+	short interface_index;		// libusbK Only
+	short altsetting_index;		// libusbK Only
+}interface_request_t;
+
+typedef struct
+{
+	unsigned int timeout;
+	union
+	{
+		struct
+		{
+			unsigned int configuration;
+		} configuration;
+
+		interface_request_t intf;
+
+		struct
+		{
+			unsigned int endpoint;
+			unsigned int packet_size;
+
+			// TODO: max_transfer_size, short transfer not ok, use iso_start_frame
+			unsigned int max_transfer_size;
+			unsigned int transfer_flags;
+			unsigned int iso_start_frame_latency;
+		} endpoint;
+		struct
+		{
+			unsigned int type;
+			unsigned int recipient;
+			unsigned int request;
+			unsigned int value;
+			unsigned int index;
+		} vendor;
+		struct
+		{
+			unsigned int recipient;
+			unsigned int feature;
+			unsigned int index;
+		} feature;
+		struct
+		{
+			unsigned int recipient;
+			unsigned int index;
+			unsigned int status;
+		} status;
+		struct
+		{
+			unsigned int type;
+			unsigned int index;
+			unsigned int language_id;
+			unsigned int recipient;
+		} descriptor;
+		struct
+		{
+			unsigned int level;
+		} debug;
+		struct
+		{
+			unsigned int major;
+			unsigned int minor;
+			unsigned int micro;
+			unsigned int nano;
+			unsigned int mod_value;
+		} version;
+		struct
+		{
+			unsigned int property;
+		} device_property;
+		struct
+		{
+			unsigned int key_type;
+			unsigned int name_offset;
+			unsigned int value_offset;
+			unsigned int value_length;
+		} device_registry_key;
+		struct
+		{
+			// 0 - device plug and play registry key pathname
+			unsigned int objname_index;
+		} objname;
+		struct
+		{
+			ULONG information_type;
+		} query_device;
+		struct
+		{
+			unsigned int interface_index;
+			unsigned int pipe_id;
+			unsigned int policy_type;
+		} pipe_policy;
+		struct
+		{
+			unsigned int policy_type;
+		} power_policy;
+		struct
+		{
+			unsigned int reset_type;
+		} reset_ex;
+
+		// WDF_USB_CONTROL_SETUP_PACKET control;
+		struct
+		{
+			UCHAR   RequestType;
+			UCHAR   Request;
+			USHORT  Value;
+			USHORT  Index;
+			USHORT  Length;
+		} control;
+	};
+} libusb_request;
+#pragma warning(default:4201)
+
+#include <poppack.h>
+
+#endif
Index: trunk/kitgen/8.x/libusb-win32/error.c
===================================================================
--- trunk/kitgen/8.x/libusb-win32/error.c	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/error.c	(revision 175)
@@ -0,0 +1,416 @@
+/* Error & Logging functions
+
+ Copyright (C) 2010 Travis Robinson. <libusbdotnet@gmail.com>
+ website: http://sourceforge.net/projects/libusb-win32
+ 
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by 
+ the Free Software Foundation; either version 2 of the License, or 
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful, but 
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, please visit www.gnu.org.
+*/
+ 
+#include "error.h"
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#if IS_DRIVER
+	#ifdef __GNUC__
+		#define OBJ_KERNEL_HANDLE       0x00000200L
+		#include <ddk/usb100.h>
+		#include <ddk/usbdi.h>
+		#include <ddk/winddk.h>
+		#include "usbdlib_gcc.h"
+	#else
+		#include <ntddk.h>
+	#endif
+#else
+	#include <windows.h>
+#endif
+
+#define USB_ERROR_BEGIN			500000
+
+#ifndef LOG_APPNAME
+#define LOG_APPNAME "LOG_APPNAME define missing"
+#endif
+
+#define GetLogLevel(UsbLogLevel) ((UsbLogLevel & LOG_LEVEL_MASK)>LOG_LEVEL_MAX?LOG_LEVEL_MAX:UsbLogLevel & LOG_LEVEL_MASK)
+#define GetLogOuput(LogOutputType) (LogOutputType>0?(_LOG_OUTPUT_TYPE & LogOutputType):1)
+
+void usb_err_v	(const char* function, const char* format, va_list args);
+void usb_wrn_v	(const char* function, const char* format, va_list args);
+void usb_msg_v	(const char* function, const char* format, va_list args);
+void usb_dbg_v	(const char* function, const char* format, va_list args);
+
+void usb_log_v	(enum USB_LOG_LEVEL level, const char* function, const char* format, va_list args);
+void _usb_log	(enum USB_LOG_LEVEL level, const char* app_name, const char* function, const char* format, ...);
+void _usb_log_v	(enum USB_LOG_LEVEL level, const char* app_name, const char* function, const char* format, va_list args);
+
+static int usb_log_def_handler(enum USB_LOG_LEVEL level, 
+								const char* app_name, 
+								const char* prefix, 
+								const char* func, 
+								int app_prefix_func_end,
+								char* message,
+								int message_length);
+
+#define STRIP_PREFIX(stringSrc, stringPrefix) \
+	(strstr(stringSrc,stringPrefix)==stringSrc?stringSrc+strlen(stringPrefix):stringSrc)
+
+static const char *log_level_string[LOG_LEVEL_MAX+1] =
+{
+    "off",
+    "err",
+    "wrn",
+    "",
+    "dbg",
+
+    "unknown",
+};
+
+static const char *skipped_function_prefix_list[] =
+{
+    "usb_registry_",
+    "usb_",
+	NULL
+};
+
+int usb_error_errno = 0;
+log_hander_t user_log_hander = NULL;
+
+#if (defined(_DEBUG) || defined(DEBUG) || defined(DBG))
+int __usb_log_level = LOG_LEVEL_MAX;
+#else
+int __usb_log_level = LOG_OFF;
+#endif
+
+usb_error_type_t usb_error_type = USB_ERROR_TYPE_NONE;
+
+const char** skipped_function_prefix = skipped_function_prefix_list;
+
+#if !IS_DRIVER
+
+char usb_error_str[LOGBUF_SIZE] = "";
+
+char *usb_strerror(void)
+{
+    switch (usb_error_type)
+    {
+    case USB_ERROR_TYPE_NONE:
+        return "No error";
+    case USB_ERROR_TYPE_STRING:
+        return usb_error_str;
+    case USB_ERROR_TYPE_ERRNO:
+        if (usb_error_errno > -USB_ERROR_BEGIN)
+            return strerror(usb_error_errno);
+        else
+            /* Any error we don't know falls under here */
+            return "Unknown error";
+    }
+
+    return "Unknown error";
+}
+
+/* returns Windows' last error in a human readable form */
+const char *usb_win_error_to_string(void)
+{
+    static char tmp[LOGBUF_SIZE];
+
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
+                  LANG_USER_DEFAULT, tmp, sizeof(tmp) - 1, NULL);
+
+    return tmp;
+}
+
+
+int usb_win_error_to_errno(void)
+{
+    switch (GetLastError())
+    {
+    case ERROR_SUCCESS:
+        return 0;
+    case ERROR_INVALID_PARAMETER:
+        return EINVAL;
+    case ERROR_SEM_TIMEOUT:
+    case ERROR_OPERATION_ABORTED:
+        return ETRANSFER_TIMEDOUT;
+    case ERROR_NOT_ENOUGH_MEMORY:
+        return ENOMEM;
+    default:
+        return EIO;
+    }
+}
+
+#endif
+
+void usb_err(const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    usb_err_v(function, format, args);
+    va_end(args);
+}
+void usb_wrn(const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    usb_wrn_v(function, format, args);
+    va_end(args);
+}
+
+void usb_msg(const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    usb_msg_v(function, format, args);
+    va_end(args);
+}
+
+void usb_dbg(const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    usb_dbg_v(function, format, args);
+    va_end(args);
+}
+
+void usb_log(enum USB_LOG_LEVEL level, const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    usb_log_v(level, function, format, args);
+    va_end(args);
+}
+
+void usb_err_v(const char* function, const char* format, va_list args)
+{
+    usb_log_v(LOG_ERROR, function, format, args);
+}
+
+void usb_wrn_v(const char* function, const char* format, va_list args)
+{
+    usb_log_v(LOG_WARNING, function, format, args);
+}
+
+void usb_msg_v(const char* function, const char* format, va_list args)
+{
+    usb_log_v(LOG_INFO, function, format, args);
+}
+
+void usb_dbg_v(const char* function, const char* format, va_list args)
+{
+    usb_log_v(LOG_DEBUG, function, format, args);
+}
+
+void usb_log_v(enum USB_LOG_LEVEL level, const char* function, const char* format, va_list args)
+{
+    _usb_log_v(level, LOG_APPNAME, function, format, args);
+}
+
+void _usb_log(enum USB_LOG_LEVEL level, const char* app_name, const char* function, const char* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    _usb_log_v(level, app_name, function, format, args);
+    va_end(args);
+}
+
+void _usb_log_v(enum USB_LOG_LEVEL level,
+                const char* app_name,
+                const char* function,
+                const char* format,
+                va_list args)
+{
+
+    char local_buffer[LOGBUF_SIZE];
+    int totalCount, count;
+    const char* prefix;
+    const char* func;
+    char* buffer;
+    int masked_level;
+	int app_prefix_func_end;
+#ifndef LOG_STYLE_SHORT
+	const char** skip_list = NULL;
+#endif
+
+	masked_level = GetLogLevel(level);
+
+    if (__usb_log_level < masked_level && masked_level != LOG_ERROR) return;
+    buffer = local_buffer;
+    totalCount = 0;
+    count = 0;
+    prefix = log_level_string[masked_level];
+	func = function;
+	app_prefix_func_end = 0;
+
+    if (masked_level > LOG_LEVEL_MAX) masked_level = LOG_LEVEL_MAX;
+
+    if ((level & LOG_RAW) == LOG_RAW)
+    {
+        count = _vsnprintf(buffer, LOGBUF_SIZE-1, format, args);
+        if (count > 0)
+        {
+            buffer += count;
+            totalCount += count;
+        }
+    }
+    else
+    {
+#ifdef LOG_STYLE_SHORT
+        if ((prefix) && strlen(prefix))
+        {
+		    count = _snprintf(buffer, (LOGBUF_SIZE-1), "%s: ",  prefix);
+        }
+        else
+        {
+		    count = 0;
+        }
+		func = "";
+#else
+		func = function;
+
+		if (func)
+		{
+			// strip some prefixes to shorten function names
+			skip_list=skipped_function_prefix;
+			while(*skip_list && ((func)) && func[0])
+			{
+				func = STRIP_PREFIX(func,skip_list[0]);
+				skip_list++;
+			}
+		}
+
+		if(!func) func="none";
+
+        // print app name, level string and short function name
+        if ((prefix) && strlen(prefix))
+        {
+            count = _snprintf(buffer, (LOGBUF_SIZE-1), "%s:%s [%s] ", app_name, prefix, func);
+        }
+        else
+        {
+            count = _snprintf(buffer, (LOGBUF_SIZE-1), "%s:[%s] ", app_name, func);
+        }
+#endif
+
+        if (count >= 0)
+        {
+			app_prefix_func_end = count;
+            buffer += count;
+            totalCount += count;
+            count = _vsnprintf(buffer, (LOGBUF_SIZE-1) - totalCount, format, args);
+            if (count > 0)
+            {
+                buffer += count;
+                totalCount += count;
+            }
+        }
+    }
+
+	if (count < 0)
+        totalCount = LOGBUF_SIZE - 1;
+
+    // make sure its null terminated
+    local_buffer[totalCount] = 0;
+
+#if (!IS_DRIVER)
+    if (masked_level == LOG_ERROR)
+    {
+        // if this is an error message then store it
+        strncpy(usb_error_str, local_buffer, totalCount);
+        usb_error_str[totalCount] = '\0';
+        usb_error_type = USB_ERROR_TYPE_STRING;
+    }
+#endif
+
+	if (user_log_hander)
+	{
+		if (user_log_hander(level, app_name, prefix, func, app_prefix_func_end, local_buffer, totalCount))
+			return;
+	}
+	if (__usb_log_level >= masked_level)
+	{
+		usb_log_def_handler(level, app_name, prefix, func, app_prefix_func_end, local_buffer, totalCount);
+	}
+}
+
+void usb_log_set_level(enum USB_LOG_LEVEL level)
+{
+	// Debug builds of the driver force all messages on; all the time;
+	// Application can no longer change this.
+	//
+#if (defined(_DEBUG) || defined(DEBUG) || defined(DBG))
+	__usb_log_level = LOG_LEVEL_MAX;
+#else
+    __usb_log_level = level > LOG_LEVEL_MAX ? LOG_LEVEL_MAX : level;
+#endif
+}
+
+int usb_log_get_level()
+{
+    return __usb_log_level;
+}
+
+/* Default log handler
+*/
+static int usb_log_def_handler(enum USB_LOG_LEVEL level, 
+								const char* app_name, 
+								const char* prefix, 
+								const char* func, 
+								int app_prefix_func_end,
+								char* message,
+								int message_length)
+{
+#if IS_DRIVER
+	DbgPrint("%s",message);
+#else
+	#if GetLogOuput(LOG_OUTPUT_TYPE_FILE)
+		FILE* file;
+		file = fopen(LOG_FILE_PATH,"a");
+		if (file)
+		{
+			fwrite(message,1,strlen(message),file);
+			fflush(file);
+			fclose(file);
+		}
+	#endif
+
+	#if GetLogOuput(LOG_OUTPUT_TYPE_STDERR)
+		fprintf(stderr, "%s", message);
+	#endif
+
+	#if GetLogOuput(LOG_OUTPUT_TYPE_DEBUGWINDOW)
+		OutputDebugStringA(message);
+	#endif
+
+
+	#if GetLogOuput(LOG_OUTPUT_TYPE_MSGBOX)
+		if (GetLogLevel(level)==LOG_ERROR)
+		{
+			message[app_prefix_func_end-1]='\0';
+			MessageBoxA(NULL,message+strlen(message),message,MB_OK|MB_ICONERROR);
+		}
+	#endif
+
+#endif // IS_DRIVER
+
+	return 1;
+}
+
+void usb_log_set_handler(log_hander_t log_hander)
+{
+	user_log_hander = log_hander;
+}
+
+log_hander_t usb_log_get_handler(void)
+{
+	return user_log_hander;
+}
Index: trunk/kitgen/8.x/libusb-win32/error.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/error.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/error.h	(revision 175)
@@ -0,0 +1,187 @@
+/* Error & Logging functions
+
+ Copyright (C) 2010 Travis Robinson. <libusbdotnet@gmail.com>
+ website: http://sourceforge.net/projects/libusb-win32
+ 
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU (LGPL) General Public License as published by 
+ the Free Software Foundation; either version 2 of the License, or 
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful, but 
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU (LGPL) General Public
+ License for more details.
+ 
+ You should have received a copy of the GNU (LGPL) General Public License
+ along with this program; if not, please visit www.gnu.org.
+*/
+
+#ifndef __ERROR_H__
+#define __ERROR_H__
+
+#include <stdarg.h>
+
+
+enum USB_LOG_LEVEL
+{
+	LOG_OFF,
+	LOG_ERROR,
+	LOG_WARNING,
+	LOG_INFO,
+	LOG_DEBUG,
+
+	LOG_LEVEL_MAX,
+	LOG_LEVEL_MASK=0xff,
+	LOG_RAW=0x100
+
+};
+
+/* Connection timed out */
+#define ETRANSFER_TIMEDOUT 116
+
+#define LOGBUF_SIZE 512
+
+// TARGETTYPEs
+#define PROGRAMconsole 0
+#define PROGRAMwindows 1
+#define DYNLINK 2
+#define DRIVER 3
+
+// default TARGETTYPE
+#ifndef TARGETTYPE
+#define TARGETTYPE PROGRAMconsole
+#endif
+
+#define IS_DRIVER			(TARGETTYPE==DRIVER)
+#define IS_CONSOLE_APP		(TARGETTYPE==PROGRAMconsole)
+#define IS_WINDOW_APP		(TARGETTYPE==PROGRAMwindows)
+#define IS_APP				(IS_CONSOLE_APP || IS_WINDOW_APP)
+#define IS_DLL				(TARGETTYPE==DYNLINK)
+
+// NOTE: LOG_OUTPUT_TYPEs can be combined
+// writes log messages to standard error output
+#define LOG_OUTPUT_TYPE_STDERR		0x001
+
+// writes log messages to Win32 OutputDebugString (DbgPrint for drivers)
+#define LOG_OUTPUT_TYPE_DEBUGWINDOW	0x0002
+#define LOG_OUTPUT_TYPE_DBGPRINT	0x0002
+
+// displays error log messages to a messagebox (not recommended)
+#define LOG_OUTPUT_TYPE_MSGBOX		0x0004
+
+// writes log messages to Kernel-mode DbgPrint
+
+// writes log messages directly to a file
+#define LOG_OUTPUT_TYPE_FILE		0x0010
+
+// strips all log messages except errors
+#define LOG_OUTPUT_TYPE_REMOVE		0x0020
+
+#define LOG_OUTPUT_TYPE_DEFAULT		0x0100
+
+// File logging is never enabled by default.
+// The LOG_OUTPUT_TYPE define must be manually
+// set to enable file logging.
+#if !IS_DRIVER
+	#ifndef LOG_DIRECTORY
+		#define LOG_FILE_PATH LOG_APPNAME ".log"
+	#else
+		#define LOG_FILE_PATH LOG_DIRECTORY LOG_APPNAME ".log"
+	#endif
+#endif
+
+#if (IS_DRIVER) || (IS_DLL) || (IS_WINDOW_APP)
+	// default logging for drivers and dlls
+	#define DEF_LOG_OUTPUT_TYPE LOG_OUTPUT_TYPE_DEBUGWINDOW
+#else
+	// default logging for applications and everything else
+	#define DEF_LOG_OUTPUT_TYPE LOG_OUTPUT_TYPE_STDERR
+#endif
+
+#define _usb_log_do_nothing() while(0)
+// Default logging output
+#ifdef LOG_OUTPUT_TYPE
+	// all log messages (except errors) are stripped
+	#if (LOG_OUTPUT_TYPE & LOG_OUTPUT_TYPE_REMOVE)
+		#define USBMSG(format,...) _usb_log_do_nothing()
+		#define USBWRN(format,...) _usb_log_do_nothing()
+		#define USBDBG(format,...) _usb_log_do_nothing()
+		#define USBRAWMSG(format,...) _usb_log_do_nothing()
+
+		#define USBMSG0(format) _usb_log_do_nothing()
+		#define USBWRN0(format) _usb_log_do_nothing()
+		#define USBDBG0(format) _usb_log_do_nothing()
+		#define USBRAWMSG0(format) _usb_log_do_nothing()
+	#endif
+
+	#if (LOG_OUTPUT_TYPE & LOG_OUTPUT_TYPE_DEFAULT)
+		#define _LOG_OUTPUT_TYPE ((LOG_OUTPUT_TYPE & 0xff)|DEF_LOG_OUTPUT_TYPE)
+	#else
+		#define _LOG_OUTPUT_TYPE (LOG_OUTPUT_TYPE)
+	#endif
+
+#else
+	// if the LOG_OUTPUT_TYPE has not been manually set use
+	// the as defaults.
+	#define _LOG_OUTPUT_TYPE DEF_LOG_OUTPUT_TYPE
+#endif
+
+// always keep error messages
+#define USBERR(format,...) usb_err(__FUNCTION__,format,__VA_ARGS__)
+#define USBERR0(format) usb_err(__FUNCTION__,"%s",format)
+
+// only keep debug log messages in debug builds
+#if !(defined(_DEBUG) || defined(DEBUG) || defined(DBG)) && !defined(USBDBG)
+	#define USBDBG(format,...) _usb_log_do_nothing()
+	#define USBDBG0(format) _usb_log_do_nothing()
+#endif
+
+// if USBMSG has not been defined as empty (see above)
+// then keep all the info and warning log messages
+#ifndef USBMSG
+	#define USBMSG(format,...) usb_msg(__FUNCTION__,format,__VA_ARGS__)
+	#define USBWRN(format,...) usb_wrn(__FUNCTION__,format,__VA_ARGS__)
+	#define USBRAWMSG(format,...) usb_log(LOG_INFO|LOG_RAW,__FUNCTION__,format,__VA_ARGS__)
+
+	#define USBMSG0(format) usb_msg(__FUNCTION__,"%s",format)
+	#define USBWRN0(format) usb_wrn(__FUNCTION__,"%s",format)
+	#define USBRAWMSG0(format) usb_log(LOG_INFO|LOG_RAW,__FUNCTION__,"%s",format)
+#endif
+
+// if USBDBG has not been defined as empty (see above)
+// then keep all the debug log messages
+#ifndef USBDBG
+	#define USBDBG(format,...) usb_dbg(__FUNCTION__,format,__VA_ARGS__)
+	#define USBDBG0(format) usb_dbg(__FUNCTION__,"%s",format)
+#endif
+
+typedef enum
+{
+    USB_ERROR_TYPE_NONE = 0,
+    USB_ERROR_TYPE_STRING,
+    USB_ERROR_TYPE_ERRNO,
+} usb_error_type_t;
+
+typedef int (*log_hander_t)(enum USB_LOG_LEVEL level, const char*,const char*,const char*, int, char*, int);
+ 
+#if (!IS_DRIVER)
+	const char *usb_win_error_to_string(void);
+	int usb_win_error_to_errno(void);
+#endif
+
+void usb_log_set_level(enum USB_LOG_LEVEL level);
+int usb_log_get_level(void);
+void usb_log_set_handler(log_hander_t log_hander);
+log_hander_t usb_log_get_handler(void);
+
+// these are the core logging functions used by the logging macros
+// (not used directly)
+void usb_err	(const char* function, const char* format, ...);
+void usb_wrn	(const char* function, const char* format, ...);
+void usb_msg	(const char* function, const char* format, ...);
+void usb_dbg	(const char* function, const char* format, ...);
+void usb_log	(enum USB_LOG_LEVEL level, const char* function, const char* format, ...);
+
+#endif /* _ERROR_H_ */
+
Index: trunk/kitgen/8.x/libusb-win32/libusb-win32_version.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/libusb-win32_version.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/libusb-win32_version.h	(revision 175)
@@ -0,0 +1,18 @@
+/* libusb-win32_version.h ++ auto-generated
+*/
+#ifndef __LIBUSB_WIN32_VERSION_H
+#define __LIBUSB_WIN32_VERSION_H
+
+#define __DEFTOSTR(x) #x
+#define  _DEFTOSTR(x) __DEFTOSTR(x)
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 2
+#define VERSION_MICRO 6
+#define VERSION_NANO  0
+#define VERSION_DATE 01/17/2012
+
+#define VERSION VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO.VERSION_NANO
+#define RC_VERSION VERSION_MAJOR,VERSION_MINOR,VERSION_MICRO,VERSION_NANO
+
+#endif
Index: trunk/kitgen/8.x/libusb-win32/lusb0_usb.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/lusb0_usb.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/lusb0_usb.h	(revision 175)
@@ -0,0 +1,427 @@
+#ifndef __USB_H__
+#define __USB_H__
+
+#include <stdlib.h>
+#include <windows.h>
+
+/*
+ * 'interface' is defined somewhere in the Windows header files. This macro
+ * is deleted here to avoid conflicts and compile errors.
+ */
+
+#ifdef interface
+#undef interface
+#endif
+
+/*
+ * PATH_MAX from limits.h can't be used on Windows if the dll and
+ * import libraries are build/used by different compilers
+ */
+
+#define LIBUSB_PATH_MAX 512
+
+
+/*
+ * USB spec information
+ *
+ * This is all stuff grabbed from various USB specs and is pretty much
+ * not subject to change
+ */
+
+/*
+ * Device and/or Interface Class codes
+ */
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_AUDIO			      1
+#define USB_CLASS_COMM			      2
+#define USB_CLASS_HID			        3
+#define USB_CLASS_PRINTER		      7
+#define USB_CLASS_MASS_STORAGE		8
+#define USB_CLASS_HUB			        9
+#define USB_CLASS_DATA			      10
+#define USB_CLASS_VENDOR_SPEC		  0xff
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE			0x01
+#define USB_DT_CONFIG			0x02
+#define USB_DT_STRING			0x03
+#define USB_DT_INTERFACE	0x04
+#define USB_DT_ENDPOINT		0x05
+
+#define USB_DT_HID			0x21
+#define USB_DT_REPORT		0x22
+#define USB_DT_PHYSICAL	0x23
+#define USB_DT_HUB			0x29
+
+/*
+ * Descriptor sizes per descriptor type
+ */
+#define USB_DT_DEVICE_SIZE		18
+#define USB_DT_CONFIG_SIZE		9
+#define USB_DT_INTERFACE_SIZE		9
+#define USB_DT_ENDPOINT_SIZE		7
+#define USB_DT_ENDPOINT_AUDIO_SIZE	9	/* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE		7
+
+
+/* ensure byte-packed structures */
+#include <pshpack1.h>
+
+
+/* All standard descriptors have these 2 fields in common */
+struct usb_descriptor_header
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+};
+
+/* String descriptor */
+struct usb_string_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned short wData[1];
+};
+
+/* HID descriptor */
+struct usb_hid_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned short bcdHID;
+    unsigned char  bCountryCode;
+    unsigned char  bNumDescriptors;
+};
+
+/* Endpoint descriptor */
+#define USB_MAXENDPOINTS	32
+struct usb_endpoint_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned char  bEndpointAddress;
+    unsigned char  bmAttributes;
+    unsigned short wMaxPacketSize;
+    unsigned char  bInterval;
+    unsigned char  bRefresh;
+    unsigned char  bSynchAddress;
+
+    unsigned char *extra;	/* Extra descriptors */
+    int extralen;
+};
+
+#define USB_ENDPOINT_ADDRESS_MASK	0x0f    /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK		  0x80
+
+#define USB_ENDPOINT_TYPE_MASK		0x03    /* in bmAttributes */
+#define USB_ENDPOINT_TYPE_CONTROL	    0
+#define USB_ENDPOINT_TYPE_ISOCHRONOUS	1
+#define USB_ENDPOINT_TYPE_BULK		    2
+#define USB_ENDPOINT_TYPE_INTERRUPT	  3
+
+/* Interface descriptor */
+#define USB_MAXINTERFACES	32
+struct usb_interface_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned char  bInterfaceNumber;
+    unsigned char  bAlternateSetting;
+    unsigned char  bNumEndpoints;
+    unsigned char  bInterfaceClass;
+    unsigned char  bInterfaceSubClass;
+    unsigned char  bInterfaceProtocol;
+    unsigned char  iInterface;
+
+    struct usb_endpoint_descriptor *endpoint;
+
+    unsigned char *extra;	/* Extra descriptors */
+    int extralen;
+};
+
+#define USB_MAXALTSETTING	128	/* Hard limit */
+
+struct usb_interface
+{
+    struct usb_interface_descriptor *altsetting;
+
+    int num_altsetting;
+};
+
+/* Configuration descriptor information.. */
+#define USB_MAXCONFIG		8
+struct usb_config_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned short wTotalLength;
+    unsigned char  bNumInterfaces;
+    unsigned char  bConfigurationValue;
+    unsigned char  iConfiguration;
+    unsigned char  bmAttributes;
+    unsigned char  MaxPower;
+
+    struct usb_interface *interface;
+
+    unsigned char *extra;	/* Extra descriptors */
+    int extralen;
+};
+
+/* Device descriptor */
+struct usb_device_descriptor
+{
+    unsigned char  bLength;
+    unsigned char  bDescriptorType;
+    unsigned short bcdUSB;
+    unsigned char  bDeviceClass;
+    unsigned char  bDeviceSubClass;
+    unsigned char  bDeviceProtocol;
+    unsigned char  bMaxPacketSize0;
+    unsigned short idVendor;
+    unsigned short idProduct;
+    unsigned short bcdDevice;
+    unsigned char  iManufacturer;
+    unsigned char  iProduct;
+    unsigned char  iSerialNumber;
+    unsigned char  bNumConfigurations;
+};
+
+struct usb_ctrl_setup
+{
+    unsigned char  bRequestType;
+    unsigned char  bRequest;
+    unsigned short wValue;
+    unsigned short wIndex;
+    unsigned short wLength;
+};
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS		    0x00
+#define USB_REQ_CLEAR_FEATURE	    0x01
+/* 0x02 is reserved */
+#define USB_REQ_SET_FEATURE		    0x03
+/* 0x04 is reserved */
+#define USB_REQ_SET_ADDRESS		    0x05
+#define USB_REQ_GET_DESCRIPTOR		0x06
+#define USB_REQ_SET_DESCRIPTOR		0x07
+#define USB_REQ_GET_CONFIGURATION	0x08
+#define USB_REQ_SET_CONFIGURATION	0x09
+#define USB_REQ_GET_INTERFACE		  0x0A
+#define USB_REQ_SET_INTERFACE		  0x0B
+#define USB_REQ_SYNCH_FRAME		    0x0C
+
+#define USB_TYPE_STANDARD		(0x00 << 5)
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+#define USB_TYPE_RESERVED		(0x03 << 5)
+
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE	0x01
+#define USB_RECIP_ENDPOINT	0x02
+#define USB_RECIP_OTHER			0x03
+
+/*
+ * Various libusb API related stuff
+ */
+
+#define USB_ENDPOINT_IN			0x80
+#define USB_ENDPOINT_OUT		0x00
+
+/* Error codes */
+#define USB_ERROR_BEGIN			500000
+
+/*
+ * This is supposed to look weird. This file is generated from autoconf
+ * and I didn't want to make this too complicated.
+ */
+#define USB_LE16_TO_CPU(x)
+
+/*
+ * Device reset types for usb_reset_ex.
+ * http://msdn.microsoft.com/en-us/library/ff537269%28VS.85%29.aspx
+ * http://msdn.microsoft.com/en-us/library/ff537243%28v=vs.85%29.aspx
+ */
+#define USB_RESET_TYPE_RESET_PORT (1 << 0)
+#define USB_RESET_TYPE_CYCLE_PORT (1 << 1)
+#define USB_RESET_TYPE_FULL_RESET (USB_RESET_TYPE_CYCLE_PORT | USB_RESET_TYPE_RESET_PORT)
+
+
+/* Data types */
+/* struct usb_device; */
+/* struct usb_bus; */
+
+struct usb_device
+{
+    struct usb_device *next, *prev;
+
+    char filename[LIBUSB_PATH_MAX];
+
+    struct usb_bus *bus;
+
+    struct usb_device_descriptor descriptor;
+    struct usb_config_descriptor *config;
+
+    void *dev;		/* Darwin support */
+
+    unsigned char devnum;
+
+    unsigned char num_children;
+    struct usb_device **children;
+};
+
+struct usb_bus
+{
+    struct usb_bus *next, *prev;
+
+    char dirname[LIBUSB_PATH_MAX];
+
+    struct usb_device *devices;
+    unsigned long location;
+
+    struct usb_device *root_dev;
+};
+
+/* Version information, Windows specific */
+struct usb_version
+{
+    struct
+    {
+        int major;
+        int minor;
+        int micro;
+        int nano;
+    } dll;
+    struct
+    {
+        int major;
+        int minor;
+        int micro;
+        int nano;
+    } driver;
+};
+
+
+struct usb_dev_handle;
+typedef struct usb_dev_handle usb_dev_handle;
+
+/* Variables */
+#ifndef __USB_C__
+#define usb_busses usb_get_busses()
+#endif
+
+
+
+#include <poppack.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    /* Function prototypes */
+
+    /* usb.c */
+    usb_dev_handle *usb_open(struct usb_device *dev);
+    int usb_close(usb_dev_handle *dev);
+    int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf,
+                       size_t buflen);
+    int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf,
+                              size_t buflen);
+
+    /* descriptors.c */
+    int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep,
+                                       unsigned char type, unsigned char index,
+                                       void *buf, int size);
+    int usb_get_descriptor(usb_dev_handle *udev, unsigned char type,
+                           unsigned char index, void *buf, int size);
+
+    /* <arch>.c */
+    int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+                       int timeout);
+    int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+                      int timeout);
+    int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+                            int timeout);
+    int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+                           int timeout);
+    int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
+                        int value, int index, char *bytes, int size,
+                        int timeout);
+    int usb_set_configuration(usb_dev_handle *dev, int configuration);
+    int usb_claim_interface(usb_dev_handle *dev, int interface);
+    int usb_release_interface(usb_dev_handle *dev, int interface);
+    int usb_set_altinterface(usb_dev_handle *dev, int alternate);
+    int usb_resetep(usb_dev_handle *dev, unsigned int ep);
+    int usb_clear_halt(usb_dev_handle *dev, unsigned int ep);
+    int usb_reset(usb_dev_handle *dev);
+    int usb_reset_ex(usb_dev_handle *dev, unsigned int reset_type);
+
+    char *usb_strerror(void);
+
+    void usb_init(void);
+    void usb_set_debug(int level);
+    int usb_find_busses(void);
+    int usb_find_devices(void);
+    struct usb_device *usb_device(usb_dev_handle *dev);
+    struct usb_bus *usb_get_busses(void);
+
+
+    /* Windows specific functions */
+
+#define LIBUSB_HAS_INSTALL_SERVICE_NP 1
+    int usb_install_service_np(void);
+    void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance,
+            LPSTR cmd_line, int cmd_show);
+
+#define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1
+    int usb_uninstall_service_np(void);
+    void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance,
+            LPSTR cmd_line, int cmd_show);
+
+#define LIBUSB_HAS_INSTALL_DRIVER_NP 1
+    int usb_install_driver_np(const char *inf_file);
+    void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance,
+            LPSTR cmd_line, int cmd_show);
+
+#define LIBUSB_HAS_TOUCH_INF_FILE_NP 1
+    int usb_touch_inf_file_np(const char *inf_file);
+    void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance,
+            LPSTR cmd_line, int cmd_show);
+
+#define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1
+    int usb_install_needs_restart_np(void);
+
+#define LIBUSB_HAS_INSTALL_NP 1
+    int usb_install_npW(HWND hwnd, HINSTANCE instance, LPCWSTR cmd_line, int starg_arg);
+    int usb_install_npA(HWND hwnd, HINSTANCE instance, LPCSTR cmd_line, int starg_arg);
+	#define usb_install_np usb_install_npA
+    void CALLBACK usb_install_np_rundll(HWND wnd, HINSTANCE instance, 
+            LPSTR cmd_line, int cmd_show);
+
+    const struct usb_version *usb_get_version(void);
+
+    int usb_isochronous_setup_async(usb_dev_handle *dev, void **context,
+                                    unsigned char ep, int pktsize);
+    int usb_bulk_setup_async(usb_dev_handle *dev, void **context,
+                             unsigned char ep);
+    int usb_interrupt_setup_async(usb_dev_handle *dev, void **context,
+                                  unsigned char ep);
+
+    int usb_submit_async(void *context, char *bytes, int size);
+    int usb_reap_async(void *context, int timeout);
+    int usb_reap_async_nocancel(void *context, int timeout);
+    int usb_cancel_async(void *context);
+    int usb_free_async(void **context);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USB_H__ */
+
Index: trunk/kitgen/8.x/libusb-win32/registry.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/registry.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/registry.h	(revision 175)
@@ -0,0 +1,221 @@
+/* libusb-win32, Generic Windows USB Library
+* Copyright (c) 2002-2005 Stephan Meyer <ste_meyer@web.de>
+* Copyright (c) 2010 Travis Robinson <libusbdotnet@gmail.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+
+#ifndef __USB_REGISTRY_H__
+#define __USB_REGISTRY_H__
+
+#include <windows.h>
+#include <setupapi.h>
+
+
+#define LIBUSB_DRIVER_NAME_NT "libusb0"
+#define LIBUSB_DRIVER_NAME_9X "libusb0.sys"
+
+typedef int bool_t;
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!(FALSE))
+#endif
+
+#define REGISTRY_BUF_SIZE 512
+
+typedef struct _filter_file_t filter_file_t;
+struct _filter_file_t
+{
+	filter_file_t* next;
+	char name[MAX_PATH];
+};
+
+typedef int filter_mode_e;
+enum _filter_mode_e
+{
+	FM_NONE    = 0,
+	FM_LIST    = 1 << 0,
+	FM_INSTALL = 1 << 1,
+	FM_REMOVE  = 1 << 2,
+};
+
+typedef int filter_type_e;
+enum _filter_type_e
+{
+	FT_NONE              = 0,
+	FT_CLASS_UPPERFILTER = 1 << 0,
+	FT_CLASS_LOWERFILTER = 1 << 1,
+	FT_DEVICE_UPPERFILTER = 1 << 2,
+	FT_DEVICE_LOWERFILTER = 1 << 3,
+};
+
+typedef struct _filter_hwid_t filter_hwid_t;
+struct _filter_hwid_t
+{
+	int vid;
+	int pid;
+	int mi;
+	int rev;
+};
+
+typedef struct _filter_device_t filter_device_t;
+struct _filter_device_t
+{
+	filter_device_t* next;
+
+	char device_name[MAX_PATH];
+	char device_hwid[MAX_PATH];
+	char device_mfg[MAX_PATH];
+	char device_uppers[MAX_PATH];
+	char device_lowers[MAX_PATH];
+	char device_id[MAX_PATH];
+
+	filter_type_e action;
+};
+
+typedef struct _filter_class_t  filter_class_t;
+struct _filter_class_t
+{
+	filter_class_t* next;
+
+	char name[MAX_PATH]; // key
+
+	char class_name[MAX_PATH];
+	char class_guid[MAX_PATH];
+	char class_uppers[MAX_PATH];
+	char class_lowers[MAX_PATH];
+	filter_device_t* class_filter_devices;
+	filter_type_e action;
+};
+
+typedef struct _filter_context_t filter_context_t;
+struct _filter_context_t
+{
+	union
+	{
+		int switches_value;
+		struct 
+		{
+			bool_t           add_all_classes:1;
+			bool_t           add_device_classes:1;
+			bool_t           add_default_classes:1;
+		};
+	}switches;
+
+	filter_mode_e       filter_mode;
+	filter_class_t*     class_filters;
+	filter_device_t*    device_filters;
+	filter_file_t*      inf_files;
+	bool_t				show_help_only;
+	bool_t              remove_all_device_filters;
+	bool_t				class_filters_modified;
+	char*				prompt_string;
+	char*				wait_string;
+};
+
+bool_t usb_registry_is_nt(void);
+
+bool_t usb_registry_restart_device(HDEVINFO dev_info,
+								   SP_DEVINFO_DATA *dev_info_data);
+bool_t usb_registry_stop_device(HDEVINFO dev_info,
+								SP_DEVINFO_DATA *dev_info_data);
+bool_t usb_registry_start_device(HDEVINFO dev_info,
+								 SP_DEVINFO_DATA *dev_info_data);
+
+bool_t usb_registry_get_property(DWORD which, HDEVINFO dev_info,
+								 SP_DEVINFO_DATA *dev_info_data,
+								 char *buf, int size);
+bool_t usb_registry_set_property(DWORD which, HDEVINFO dev_info,
+								 SP_DEVINFO_DATA *dev_info_data,
+								 char *buf, int size);
+
+bool_t usb_registry_restart_all_devices(void);
+
+
+void usb_registry_stop_libusb_devices(void);
+void usb_registry_start_libusb_devices(void);
+
+bool_t usb_registry_get_mz_value(const char *key, const char *value,
+								 char *buf, int size);
+bool_t usb_registry_set_mz_value(const char *key, const char *value,
+								 char *buf, int size);
+int usb_registry_mz_string_size(const char *src);
+char *usb_registry_mz_string_find(const char *src, const char *str, bool_t no_case);
+char *usb_registry_mz_string_find_sub(const char *src, const char *str);
+bool_t usb_registry_mz_string_insert(char *src, const char *str);
+bool_t usb_registry_mz_string_remove(char *src, const char *str, bool_t no_case);
+void usb_registry_mz_string_lower(char *src);
+
+bool_t usb_registry_get_hardware_id(HDEVINFO dev_info, 
+									SP_DEVINFO_DATA *dev_info_data, 
+									char* max_path_buffer);
+bool_t usb_registry_is_service_libusb(HDEVINFO dev_info,
+									  SP_DEVINFO_DATA *dev_info_data,
+									  bool_t* is_libusb_service);
+bool_t usb_registry_is_service_or_filter_libusb(HDEVINFO dev_info,
+												SP_DEVINFO_DATA *dev_info_data,
+												bool_t* is_libusb_service);
+
+bool_t usb_registry_insert_class_filter(filter_context_t* filter_context);
+bool_t usb_registry_remove_class_filter(filter_context_t* filter_context);
+bool_t usb_registry_remove_device_filter(filter_context_t* filter_context);
+bool_t usb_registry_free_class_keys(filter_class_t **head);
+bool_t usb_registry_get_usb_class_keys(filter_context_t* filter_context, bool_t refresh_only);
+bool_t usb_registry_get_all_class_keys(filter_context_t* filter_context, bool_t refresh_only);
+bool_t usb_registry_get_device_filter_type(HDEVINFO dev_info,
+										   SP_DEVINFO_DATA *dev_info_data,
+										   filter_type_e* filter_type);
+
+bool_t usb_registry_add_usb_class_key(filter_context_t* filter_context, const char* class_guid);
+bool_t usb_registry_add_filter_device_keys(filter_device_t** head,
+										   const char* id,
+										   const char* hwid,
+										   const char* name,
+										   const char* mfg,
+										   const char* uppers_mz,
+										   const char* lowers_mz,
+										   filter_device_t** found);
+
+bool_t usb_registry_add_filter_file_keys(filter_file_t** head,
+										 const char* name,
+										 filter_file_t** found);
+
+bool_t usb_registry_lookup_class_keys_by_name(filter_class_t** head);
+bool_t usb_registry_add_class_key(filter_class_t **head,
+								  const char *key,
+								  const char *class_name,
+								  const char *class_guid,
+								  filter_class_t **found,
+								  bool_t update_only);
+
+bool_t usb_registry_insert_device_filters(filter_context_t* filter_context);
+bool_t usb_registry_insert_device_filter(filter_context_t* filter_context, char* hwid, bool_t upper, 
+										 HDEVINFO dev_info, SP_DEVINFO_DATA *dev_info_data);
+
+bool_t usb_registry_free_filter_devices(filter_device_t **head);
+bool_t usb_registry_free_filter_files(filter_file_t **head);
+
+filter_device_t* usb_registry_match_filter_device(filter_device_t** head, 
+												  HDEVINFO dev_info, PSP_DEVINFO_DATA dev_info_data);
+
+bool_t usb_registry_mz_to_sz(char* buf_mz, char separator);
+bool_t usb_registry_fill_filter_hwid(const char* hwid, filter_hwid_t* filter_hwid);
+
+#endif
Index: trunk/kitgen/8.x/libusb-win32/usb.c
===================================================================
--- trunk/kitgen/8.x/libusb-win32/usb.c	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/usb.c	(revision 175)
@@ -0,0 +1,317 @@
+/*
+ * Main API entry point
+ *
+ * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ */
+
+#include <stdlib.h>	/* getenv */
+#include <stdio.h>	/* stderr */
+#include <string.h>	/* strcmp */
+#include <errno.h>
+
+#include "usbi.h"
+
+int usb_debug = 0;
+struct usb_bus *_usb_busses = NULL;
+
+int usb_find_busses(void)
+{
+    struct usb_bus *busses, *bus;
+    int ret, changes = 0;
+
+    ret = usb_os_find_busses(&busses);
+    if (ret < 0)
+        return ret;
+
+    /*
+     * Now walk through all of the busses we know about and compare against
+     * this new list. Any duplicates will be removed from the new list.
+     * If we don't find it in the new list, the bus was removed. Any
+     * busses still in the new list, are new to us.
+     */
+    bus = _usb_busses;
+    while (bus)
+    {
+        int found = 0;
+        struct usb_bus *nbus, *tbus = bus->next;
+
+        nbus = busses;
+        while (nbus)
+        {
+            struct usb_bus *tnbus = nbus->next;
+
+            if (!strcmp(bus->dirname, nbus->dirname))
+            {
+                /* Remove it from the new busses list */
+                LIST_DEL(busses, nbus);
+
+                usb_free_bus(nbus);
+                found = 1;
+                break;
+            }
+
+            nbus = tnbus;
+        }
+
+        if (!found)
+        {
+            /* The bus was removed from the system */
+            LIST_DEL(_usb_busses, bus);
+            usb_free_bus(bus);
+            changes++;
+        }
+
+        bus = tbus;
+    }
+
+    /*
+     * Anything on the *busses list is new. So add them to usb_busses and
+     * process them like the new bus it is.
+     */
+    bus = busses;
+    while (bus)
+    {
+        struct usb_bus *tbus = bus->next;
+
+        /*
+         * Remove it from the temporary list first and add it to the real
+         * usb_busses list.
+         */
+        LIST_DEL(busses, bus);
+
+        LIST_ADD(_usb_busses, bus);
+
+        changes++;
+
+        bus = tbus;
+    }
+
+    return changes;
+}
+
+int usb_find_devices(void)
+{
+    struct usb_bus *bus;
+    int ret, changes = 0;
+
+    for (bus = usb_busses; bus; bus = bus->next)
+    {
+        struct usb_device *devices, *dev;
+
+        /* Find all of the devices and put them into a temporary list */
+        ret = usb_os_find_devices(bus, &devices);
+        if (ret < 0)
+            return ret;
+
+        /*
+         * Now walk through all of the devices we know about and compare
+         * against this new list. Any duplicates will be removed from the new
+         * list. If we don't find it in the new list, the device was removed.
+         * Any devices still in the new list, are new to us.
+         */
+        dev = bus->devices;
+        while (dev)
+        {
+            int found = 0;
+            struct usb_device *ndev, *tdev = dev->next;
+
+            ndev = devices;
+            while (ndev)
+            {
+                struct usb_device *tndev = ndev->next;
+
+                if (!strcmp(dev->filename, ndev->filename))
+                {
+                    /* Remove it from the new devices list */
+                    LIST_DEL(devices, ndev);
+
+                    usb_free_dev(ndev);
+                    found = 1;
+                    break;
+                }
+
+                ndev = tndev;
+            }
+
+            if (!found)
+            {
+                /* The device was removed from the system */
+                LIST_DEL(bus->devices, dev);
+                usb_free_dev(dev);
+                changes++;
+            }
+
+            dev = tdev;
+        }
+
+        /*
+         * Anything on the *devices list is new. So add them to bus->devices and
+         * process them like the new device it is.
+         */
+        dev = devices;
+        while (dev)
+        {
+            struct usb_device *tdev = dev->next;
+
+            /*
+             * Remove it from the temporary list first and add it to the real
+             * bus->devices list.
+             */
+            LIST_DEL(devices, dev);
+
+            /*
+             * Some ports fetch the descriptors on scanning (like Linux) so we don't
+             * need to fetch them again.
+             */
+            if (!dev->config)
+            {
+                usb_dev_handle *udev;
+
+                udev = usb_open(dev);
+                if (udev)
+                {
+                    usb_fetch_and_parse_descriptors(udev);
+
+                    usb_close(udev);
+                }
+            }
+
+			// [ID:2928293 Tim Green] 
+			//
+			if (dev->config) 
+			{
+				LIST_ADD(bus->devices, dev);
+				changes++;
+			}
+
+            dev = tdev;
+        }
+
+        usb_os_determine_children(bus);
+    }
+
+    return changes;
+}
+
+void usb_init(void)
+{
+    if (getenv("USB_DEBUG"))
+        usb_set_debug(atoi(getenv("USB_DEBUG")));
+
+    usb_os_init();
+}
+
+usb_dev_handle *usb_open(struct usb_device *dev)
+{
+    usb_dev_handle *udev;
+
+    udev = malloc(sizeof(*udev));
+    if (!udev)
+        return NULL;
+
+    udev->fd = -1;
+    udev->device = dev;
+    udev->bus = dev->bus;
+    udev->config = udev->interface = udev->altsetting = -1;
+
+    if (usb_os_open(udev) < 0)
+    {
+        free(udev);
+        return NULL;
+    }
+
+    return udev;
+}
+
+int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf,
+                   size_t buflen)
+{
+    /*
+     * We can't use usb_get_descriptor() because it's lacking the index
+     * parameter. This will be fixed in libusb 1.0
+     */
+    return usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+                           (USB_DT_STRING << 8) + index, langid, buf, (int)buflen, 1000);
+}
+
+int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen)
+{
+    char tbuf[255];	/* Some devices choke on size > 255 */
+    int ret, langid, si, di;
+
+    /*
+     * Asking for the zero'th index is special - it returns a string
+     * descriptor that contains all the language IDs supported by the
+     * device. Typically there aren't many - often only one. The
+     * language IDs are 16 bit numbers, and they start at the third byte
+     * in the descriptor. See USB 2.0 specification, section 9.6.7, for
+     * more information on this. */
+    ret = usb_get_string(dev, 0, 0, tbuf, sizeof(tbuf));
+    if (ret < 0)
+        return ret;
+
+    if (ret < 4)
+        return -EIO;
+
+    langid = tbuf[2] | (tbuf[3] << 8);
+
+    ret = usb_get_string(dev, index, langid, tbuf, sizeof(tbuf));
+    if (ret < 0)
+        return ret;
+
+    if (tbuf[1] != USB_DT_STRING)
+        return -EIO;
+
+    if (tbuf[0] > ret)
+        return -EFBIG;
+
+    for (di = 0, si = 2; si < tbuf[0]; si += 2)
+    {
+        if (di >= ((int)buflen - 1))
+            break;
+
+        if (tbuf[si + 1])	/* high byte */
+            buf[di++] = '?';
+        else
+            buf[di++] = tbuf[si];
+    }
+
+    buf[di] = 0;
+
+    return di;
+}
+
+int usb_close(usb_dev_handle *dev)
+{
+    int ret;
+
+    ret = usb_os_close(dev);
+    free(dev);
+
+    return ret;
+}
+
+struct usb_device *usb_device(usb_dev_handle *dev)
+{
+    return dev->device;
+}
+
+void usb_free_dev(struct usb_device *dev)
+{
+    usb_destroy_configuration(dev);
+    free(dev->children);
+    free(dev);
+}
+
+struct usb_bus *usb_get_busses(void)
+{
+    return _usb_busses;
+}
+
+void usb_free_bus(struct usb_bus *bus)
+{
+    free(bus);
+}
+
Index: trunk/kitgen/8.x/libusb-win32/usbi.h
===================================================================
--- trunk/kitgen/8.x/libusb-win32/usbi.h	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/usbi.h	(revision 175)
@@ -0,0 +1,79 @@
+#ifndef _USBI_H_
+#define _USBI_H_
+
+#include "lusb0_usb.h"
+
+#include "error.h"
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+
+extern int usb_debug;
+
+/* Some quick and generic macros for the simple kind of lists we use */
+#define LIST_ADD(begin, ent) \
+	do { \
+	  if (begin) { \
+	    ent->next = begin; \
+	    ent->next->prev = ent; \
+	  } else \
+	    ent->next = NULL; \
+	  ent->prev = NULL; \
+	  begin = ent; \
+	} while(0)
+
+#define LIST_DEL(begin, ent) \
+	do { \
+	  if (ent->prev) \
+	    ent->prev->next = ent->next; \
+	  else \
+	    begin = ent->next; \
+	  if (ent->next) \
+	    ent->next->prev = ent->prev; \
+	  ent->prev = NULL; \
+	  ent->next = NULL; \
+	} while (0)
+
+#define DESC_HEADER_LENGTH		2
+#define DEVICE_DESC_LENGTH		18
+#define CONFIG_DESC_LENGTH		9
+#define INTERFACE_DESC_LENGTH		9
+#define ENDPOINT_DESC_LENGTH		7
+#define ENDPOINT_AUDIO_DESC_LENGTH	9
+
+struct usb_dev_handle
+{
+    int fd;
+
+    struct usb_bus *bus;
+    struct usb_device *device;
+
+    int config;
+    int interface;
+    int altsetting;
+
+    /* Added by RMT so implementations can store other per-open-device data */
+    void *impl_info;
+};
+
+/* descriptors.c */
+int usb_parse_descriptor(unsigned char *source, char *description, void *dest);
+int usb_parse_configuration(struct usb_config_descriptor *config,
+                            unsigned char *buffer);
+void usb_fetch_and_parse_descriptors(usb_dev_handle *udev);
+void usb_destroy_configuration(struct usb_device *dev);
+
+/* OS specific routines */
+int usb_os_find_busses(struct usb_bus **busses);
+int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices);
+int usb_os_determine_children(struct usb_bus *bus);
+void usb_os_init(void);
+int usb_os_open(usb_dev_handle *dev);
+int usb_os_close(usb_dev_handle *dev);
+
+void usb_free_dev(struct usb_device *dev);
+void usb_free_bus(struct usb_bus *bus);
+
+#endif /* _USBI_H_ */
+
Index: trunk/kitgen/8.x/libusb-win32/windows.c
===================================================================
--- trunk/kitgen/8.x/libusb-win32/windows.c	(revision 175)
+++ trunk/kitgen/8.x/libusb-win32/windows.c	(revision 175)
@@ -0,0 +1,1281 @@
+/* libusb-win32, Generic Windows USB Library
+ * Copyright (c) 2002-2005 Stephan Meyer <ste_meyer@web.de>
+ * Copyright (c) 2000-2005 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <windows.h>
+#include <winioctl.h>
+#include <setupapi.h>
+
+#include "lusb0_usb.h"
+#include "error.h"
+#include "usbi.h"
+#include "driver_api.h"
+#include "registry.h"
+#include "libusb-win32_version.h"
+
+#define LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT
+
+#define LIBUSB_DEFAULT_TIMEOUT 5000
+#define LIBUSB_DEVICE_NAME "\\\\.\\libusb0-"
+#define LIBUSB_BUS_NAME "bus-0"
+#define LIBUSB_MAX_DEVICES 256
+
+typedef struct
+{
+    usb_dev_handle *dev;
+    libusb_request req;
+    char *bytes;
+    int size;
+    DWORD control_code;
+    OVERLAPPED ol;
+} usb_context_t;
+
+
+static struct usb_version _usb_version =
+{
+    { VERSION_MAJOR,
+        VERSION_MINOR,
+        VERSION_MICRO,
+        VERSION_NANO },
+    { -1, -1, -1, -1 }
+};
+
+
+static int _usb_setup_async(usb_dev_handle *dev, void **context,
+                            DWORD control_code,
+                            unsigned char ep, int pktsize);
+static int _usb_transfer_sync(usb_dev_handle *dev, int control_code,
+                              int ep, int pktsize, char *bytes, int size,
+                              int timeout);
+
+static int usb_get_configuration(usb_dev_handle *dev, bool_t cached);
+static int _usb_cancel_io(usb_context_t *context);
+static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep);
+
+static int _usb_io_sync(HANDLE dev, unsigned int code, void *in, int in_size,
+                        void *out, int out_size, int *ret);
+static int _usb_reap_async(void *context, int timeout, int cancel);
+static int _usb_add_virtual_hub(struct usb_bus *bus);
+
+static void _usb_free_bus_list(struct usb_bus *bus);
+static void _usb_free_dev_list(struct usb_device *dev);
+static void _usb_deinit(void);
+
+/* DLL main entry point */
+BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID reserved)
+{
+    switch (reason)
+    {
+    case DLL_PROCESS_ATTACH:
+        break;
+    case DLL_PROCESS_DETACH:
+        _usb_deinit();
+        break;
+    case DLL_THREAD_ATTACH:
+        break;
+    case DLL_THREAD_DETACH:
+        break;
+    default:
+        break;
+    }
+    return TRUE;
+}
+
+
+static int usb_get_configuration(usb_dev_handle *dev, bool_t cached)
+{
+	int ret;
+	char config;
+	libusb_request request;
+
+	if (cached)
+	{
+		memset(&request, 0, sizeof(request));
+		request.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+		if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_GET_CACHED_CONFIGURATION,
+			&request, sizeof(request), &request, sizeof(request), &ret))
+		{
+			USBERR("sending get cached configuration ioctl failed, win error: %s\n", usb_win_error_to_string());
+			ret = -usb_win_error_to_errno();
+		}
+
+		if (ret < 1)
+			ret = -EINVAL;
+		else
+			config = *((char*)&request);
+	}
+	else
+	{
+		ret = usb_control_msg(dev, USB_RECIP_DEVICE | USB_ENDPOINT_IN,
+			USB_REQ_GET_CONFIGURATION, 0, 0, &config, 1,
+			LIBUSB_DEFAULT_TIMEOUT); 
+	}
+
+	if(ret < 0)
+		return ret;
+
+	return config;
+}
+
+int usb_os_open(usb_dev_handle *dev)
+{
+	char dev_name[LIBUSB_PATH_MAX];
+	char *p;
+	int config;
+	if (!dev)
+	{
+		USBERR("invalid device handle %p", dev);
+		return -EINVAL;
+	}
+
+	dev->impl_info = INVALID_HANDLE_VALUE;
+	dev->config = 0;
+	dev->interface = -1;
+	dev->altsetting = -1;
+
+	if (!dev->device->filename)
+	{
+		USBERR0("invalid file name\n");
+		return -ENOENT;
+	}
+
+	/* build the Windows file name from the unique device name */
+	strcpy(dev_name, dev->device->filename);
+
+	p = strstr(dev_name, "--");
+
+	if (!p)
+	{
+		USBERR("invalid file name %s\n", dev->device->filename);
+		return -ENOENT;
+	}
+
+	*p = 0;
+
+	dev->impl_info = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
+		FILE_FLAG_OVERLAPPED, NULL);
+
+	if (dev->impl_info == INVALID_HANDLE_VALUE)
+	{
+		USBERR("failed to open %s: win error: %s",
+			dev->device->filename, usb_win_error_to_string());
+		return -ENOENT;
+	}
+
+	// get the cached configuration (no device i/o)
+	config = usb_get_configuration(dev, TRUE);
+	if (config > 0)
+	{
+		dev->config = config;
+		dev->interface = -1;
+		dev->altsetting = -1;
+	}
+
+	return 0;
+}
+
+int usb_os_close(usb_dev_handle *dev)
+{
+    if (dev->impl_info != INVALID_HANDLE_VALUE)
+    {
+        if (dev->interface >= 0)
+        {
+            usb_release_interface(dev, dev->interface);
+        }
+
+        CloseHandle(dev->impl_info);
+        dev->impl_info = INVALID_HANDLE_VALUE;
+        dev->interface = -1;
+        dev->altsetting = -1;
+    }
+
+    return 0;
+}
+
+int usb_set_configuration(usb_dev_handle *dev, int configuration)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("error: device not open\n");
+        return -EINVAL;
+    }
+
+    if (dev->config == configuration)
+    {
+        return 0;
+    }
+
+    if (dev->interface >= 0)
+    {
+        USBERR0("can't change configuration, an interface is still in use (claimed)\n");
+        return -EINVAL;
+    }
+
+    req.configuration.configuration = configuration;
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_CONFIGURATION,
+                      &req, sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not set config %d: "
+                  "win error: %s", configuration, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    dev->config = configuration;
+    dev->interface = -1;
+    dev->altsetting = -1;
+
+    return 0;
+}
+
+int usb_claim_interface(usb_dev_handle *dev, int interface)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    if (!dev->config)
+    {
+        USBERR("could not claim interface %d, invalid configuration %d\n", interface, dev->config);
+        return -EINVAL;
+    }
+
+    if (dev->interface == interface)
+    {
+        return 0;
+    }
+
+    req.intf.interface_number = interface;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_CLAIM_INTERFACE,
+                      &req, sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not claim interface %d, "
+                  "win error: %s", interface, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+    else
+    {
+        dev->interface = interface;
+        dev->altsetting = 0;
+        return 0;
+    }
+}
+
+int usb_release_interface(usb_dev_handle *dev, int interface)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    if (!dev->config)
+    {
+        USBERR("could not release interface %d, invalid configuration %d\n", interface, dev->config);
+        return -EINVAL;
+    }
+
+    req.intf.interface_number = interface;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RELEASE_INTERFACE,
+                      &req, sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not release interface %d, "
+                  "win error: %s", interface, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+    else
+    {
+        dev->interface = -1;
+        dev->altsetting = -1;
+
+        return 0;
+    }
+}
+
+int usb_set_altinterface(usb_dev_handle *dev, int alternate)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    if (dev->config <= 0)
+    {
+        USBERR("could not set alt interface %d: invalid configuration %d\n", alternate, dev->config);
+        return -EINVAL;
+    }
+
+    if (dev->interface < 0)
+    {
+        USBERR("could not set alt interface %d: no interface claimed\n", alternate);
+        return -EINVAL;
+    }
+
+    req.intf.interface_number = dev->interface;
+    req.intf.altsetting_number = alternate;
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_INTERFACE,
+                      &req, sizeof(libusb_request),
+                      NULL, 0, NULL))
+    {
+        USBERR("could not set alt interface "
+                  "%d/%d: win error: %s",
+                  dev->interface, alternate, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    dev->altsetting = alternate;
+
+    return 0;
+}
+
+static int _usb_setup_async(usb_dev_handle *dev, void **context,
+                            DWORD control_code,
+                            unsigned char ep, int pktsize)
+{
+    usb_context_t **c = (usb_context_t **)context;
+
+    if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE)
+            || (control_code == LIBUSB_IOCTL_ISOCHRONOUS_WRITE))
+            && (ep & USB_ENDPOINT_IN))
+    {
+        USBERR("invalid endpoint 0x%02x", ep);
+        return -EINVAL;
+    }
+
+    if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ)
+            || (control_code == LIBUSB_IOCTL_ISOCHRONOUS_READ))
+            && !(ep & USB_ENDPOINT_IN))
+    {
+        USBERR("invalid endpoint 0x%02x\n", ep);
+        return -EINVAL;
+    }
+
+    *c = malloc(sizeof(usb_context_t));
+
+    if (!*c)
+    {
+        USBERR0("memory allocation error\n");
+        return -ENOMEM;
+    }
+
+    memset(*c, 0, sizeof(usb_context_t));
+
+    (*c)->dev = dev;
+    (*c)->req.endpoint.endpoint = ep;
+    (*c)->req.endpoint.packet_size = pktsize;
+    (*c)->control_code = control_code;
+
+    (*c)->ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    if (!(*c)->ol.hEvent)
+    {
+        free(*c);
+        *c = NULL;
+        USBERR("creating event failed: win error: %s",
+                  usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+int usb_submit_async(void *context, char *bytes, int size)
+{
+    usb_context_t *c = (usb_context_t *)context;
+
+    if (!c)
+    {
+        USBERR0("invalid context");
+        return -EINVAL;
+    }
+
+    if (c->dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    if (c->dev->config <= 0)
+    {
+        USBERR("invalid configuration %d\n", c->dev->config);
+        return -EINVAL;
+    }
+
+    if (c->dev->interface < 0)
+    {
+        USBERR("invalid interface %d\n", c->dev->interface);
+        return -EINVAL;
+    }
+
+
+    c->ol.Offset = 0;
+    c->ol.OffsetHigh = 0;
+    c->bytes = bytes;
+    c->size = size;
+
+    ResetEvent(c->ol.hEvent);
+
+    if (!DeviceIoControl(c->dev->impl_info,
+                         c->control_code,
+                         &c->req, sizeof(libusb_request),
+                         c->bytes,
+                         c->size, NULL, &c->ol))
+    {
+        if (GetLastError() != ERROR_IO_PENDING)
+        {
+            USBERR("submitting request failed, "
+                      "win error: %s", usb_win_error_to_string());
+            return -usb_win_error_to_errno();
+        }
+    }
+
+    return 0;
+}
+
+static int _usb_reap_async(void *context, int timeout, int cancel)
+{
+    usb_context_t *c = (usb_context_t *)context;
+    ULONG ret = 0;
+
+    if (!c)
+    {
+        USBERR0("invalid context\n");
+        return -EINVAL;
+    }
+
+    if (WaitForSingleObject(c->ol.hEvent, timeout) == WAIT_TIMEOUT)
+    {
+        /* request timed out */
+        if (cancel)
+        {
+            _usb_cancel_io(c);
+        }
+
+        USBERR0("timeout error\n");
+        return -ETRANSFER_TIMEDOUT;
+    }
+
+    if (!GetOverlappedResult(c->dev->impl_info, &c->ol, &ret, TRUE))
+    {
+        USBERR("reaping request failed, win error: %s\n",usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return ret;
+}
+
+int usb_reap_async(void *context, int timeout)
+{
+    return _usb_reap_async(context, timeout, TRUE);
+}
+
+int usb_reap_async_nocancel(void *context, int timeout)
+{
+    return _usb_reap_async(context, timeout, FALSE);
+}
+
+
+int usb_cancel_async(void *context)
+{
+    /* NOTE that this function will cancel all pending URBs */
+    /* on the same endpoint as this particular context, or even */
+    /* all pending URBs for this particular device. */
+
+    usb_context_t *c = (usb_context_t *)context;
+
+    if (!c)
+    {
+        USBERR0("invalid context\n");
+        return -EINVAL;
+    }
+
+    if (c->dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    _usb_cancel_io(c);
+
+    return 0;
+}
+
+int usb_free_async(void **context)
+{
+    usb_context_t **c = (usb_context_t **)context;
+
+    if (!*c)
+    {
+        USBERR0("invalid context\n");
+        return -EINVAL;
+    }
+
+    CloseHandle((*c)->ol.hEvent);
+
+    free(*c);
+    *c = NULL;
+
+    return 0;
+}
+
+static int _usb_transfer_sync(usb_dev_handle *dev, int control_code,
+                              int ep, int pktsize, char *bytes, int size,
+                              int timeout)
+{
+    void *context = NULL;
+    int transmitted = 0;
+    int ret;
+    int requested;
+
+	if (!timeout) timeout=INFINITE;
+    ret = _usb_setup_async(dev, &context, control_code, (unsigned char )ep,
+                           pktsize);
+
+    if (ret < 0)
+    {
+        return ret;
+    }
+
+    do
+    {
+#ifdef LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT
+        requested = size > LIBUSB_MAX_READ_WRITE ? LIBUSB_MAX_READ_WRITE : size;
+#else
+        requested = size;
+#endif
+        ret = usb_submit_async(context, bytes, requested);
+
+        if (ret < 0)
+        {
+            transmitted = ret;
+            break;
+        }
+
+        ret = usb_reap_async(context, timeout);
+
+        if (ret < 0)
+        {
+            transmitted = ret;
+            break;
+        }
+
+        transmitted += ret;
+        bytes += ret;
+        size -= ret;
+    }
+    while (size > 0 && ret == requested);
+
+    usb_free_async(&context);
+
+    return transmitted;
+}
+
+int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+                   int timeout)
+{
+    return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
+                              ep, 0, bytes, size, timeout);
+}
+
+int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+                  int timeout)
+{
+    return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
+                              ep, 0, bytes, size, timeout);
+}
+
+int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+                        int timeout)
+{
+    return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
+                              ep, 0, bytes, size, timeout);
+}
+
+int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+                       int timeout)
+{
+    return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
+                              ep, 0, bytes, size, timeout);
+}
+
+int usb_isochronous_setup_async(usb_dev_handle *dev, void **context,
+                                unsigned char ep, int pktsize)
+{
+    if (ep & 0x80)
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_READ,
+                                ep, pktsize);
+    else
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_WRITE,
+                                ep, pktsize);
+}
+
+int usb_bulk_setup_async(usb_dev_handle *dev, void **context, unsigned char ep)
+{
+    if (ep & 0x80)
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
+                                ep, 0);
+    else
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
+                                ep, 0);
+}
+
+int usb_interrupt_setup_async(usb_dev_handle *dev, void **context,
+                              unsigned char ep)
+{
+    if (ep & 0x80)
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
+                                ep, 0);
+    else
+        return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
+                                ep, 0);
+}
+
+int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
+                    int value, int index, char *bytes, int size, int timeout)
+{
+    int read = 0;
+    libusb_request req;
+    void *out = &req;
+    int out_size = sizeof(libusb_request);
+    void *in = bytes;
+    int in_size = size;
+    int code;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    req.timeout = timeout;
+
+    /* windows doesn't support generic control messages, so it needs to be */
+    /* split up */
+    switch (requesttype & (0x03 << 5))
+    {
+    case USB_TYPE_STANDARD:
+        switch (request)
+        {
+        case USB_REQ_GET_STATUS:
+            req.status.recipient = requesttype & 0x1F;
+            req.status.index = index;
+            code = LIBUSB_IOCTL_GET_STATUS;
+            break;
+
+        case USB_REQ_CLEAR_FEATURE:
+            req.feature.recipient = requesttype & 0x1F;
+            req.feature.feature = value;
+            req.feature.index = index;
+            code = LIBUSB_IOCTL_CLEAR_FEATURE;
+            break;
+
+        case USB_REQ_SET_FEATURE:
+            req.feature.recipient = requesttype & 0x1F;
+            req.feature.feature = value;
+            req.feature.index = index;
+            code = LIBUSB_IOCTL_SET_FEATURE;
+            break;
+
+        case USB_REQ_GET_DESCRIPTOR:
+            req.descriptor.recipient = requesttype & 0x1F;
+            req.descriptor.type = (value >> 8) & 0xFF;
+            req.descriptor.index = value & 0xFF;
+            req.descriptor.language_id = index;
+            code = LIBUSB_IOCTL_GET_DESCRIPTOR;
+            break;
+
+        case USB_REQ_SET_DESCRIPTOR:
+            req.descriptor.recipient = requesttype & 0x1F;
+            req.descriptor.type = (value >> 8) & 0xFF;
+            req.descriptor.index = value & 0xFF;
+            req.descriptor.language_id = index;
+            code = LIBUSB_IOCTL_SET_DESCRIPTOR;
+            break;
+
+        case USB_REQ_GET_CONFIGURATION:
+            code = LIBUSB_IOCTL_GET_CONFIGURATION;
+            break;
+
+        case USB_REQ_SET_CONFIGURATION:
+            req.configuration.configuration = value;
+            code = LIBUSB_IOCTL_SET_CONFIGURATION;
+            break;
+
+        case USB_REQ_GET_INTERFACE:
+            req.intf.interface_number = index;
+            code = LIBUSB_IOCTL_GET_INTERFACE;
+            break;
+
+        case USB_REQ_SET_INTERFACE:
+            req.intf.interface_number = index;
+            req.intf.altsetting_number = value;
+            code = LIBUSB_IOCTL_SET_INTERFACE;
+            break;
+
+        default:
+            USBERR("invalid request 0x%x", request);
+            return -EINVAL;
+        }
+        break;
+
+    case USB_TYPE_VENDOR:
+    case USB_TYPE_CLASS:
+
+        req.vendor.type = (requesttype >> 5) & 0x03;
+        req.vendor.recipient = requesttype & 0x1F;
+        req.vendor.request = request;
+        req.vendor.value = value;
+        req.vendor.index = index;
+
+        if (requesttype & 0x80)
+            code = LIBUSB_IOCTL_VENDOR_READ;
+        else
+            code = LIBUSB_IOCTL_VENDOR_WRITE;
+        break;
+
+    case USB_TYPE_RESERVED:
+    default:
+        USBERR("invalid or unsupported request type: %x",
+                  requesttype);
+        return -EINVAL;
+    }
+
+    /* out request? */
+    if (!(requesttype & USB_ENDPOINT_IN))
+    {
+        if (!(out = malloc(sizeof(libusb_request) + size)))
+        {
+            USBERR0("memory allocation failed\n");
+            return -ENOMEM;
+        }
+
+        memcpy(out, &req, sizeof(libusb_request));
+        memcpy((char *)out + sizeof(libusb_request), bytes, size);
+        out_size = sizeof(libusb_request) + size;
+        in = NULL;
+        in_size = 0;
+    }
+
+    if (!_usb_io_sync(dev->impl_info, code, out, out_size, in, in_size, &read))
+    {
+        USBERR("sending control message failed, win error: %s\n", usb_win_error_to_string());
+        if (!(requesttype & USB_ENDPOINT_IN))
+        {
+            free(out);
+        }
+        return -usb_win_error_to_errno();
+    }
+
+    /* out request? */
+    if (!(requesttype & USB_ENDPOINT_IN))
+    {
+        free(out);
+        return size;
+    }
+    else
+        return read;
+}
+
+
+int usb_os_find_busses(struct usb_bus **busses)
+{
+    struct usb_bus *bus = NULL;
+
+    /* create one 'virtual' bus */
+
+    bus = malloc(sizeof(struct usb_bus));
+
+    if (!bus)
+    {
+        USBERR0("memory allocation failed\n");
+        return -ENOMEM;
+    }
+
+    memset(bus, 0, sizeof(*bus));
+    strcpy(bus->dirname, LIBUSB_BUS_NAME);
+
+    USBMSG("found %s\n", bus->dirname);
+
+    *busses = bus;
+
+    return 0;
+}
+
+int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
+{
+    int i;
+    struct usb_device *dev, *fdev = NULL;
+    char dev_name[LIBUSB_PATH_MAX];
+    int ret;
+    HANDLE handle;
+    libusb_request req;
+
+    for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
+    {
+        ret = 0;
+
+        _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
+                  LIBUSB_DEVICE_NAME, i);
+
+        if (!(dev = malloc(sizeof(*dev))))
+        {
+            USBERR0("memory allocation failed\n");
+            return -ENOMEM;
+        }
+
+        memset(dev, 0, sizeof(*dev));
+        dev->bus = bus;
+        dev->devnum = (unsigned char)i;
+
+        handle = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
+                            FILE_FLAG_OVERLAPPED, NULL);
+
+        if (handle == INVALID_HANDLE_VALUE)
+        {
+            free(dev);
+            continue;
+        }
+
+        /* retrieve device descriptor */
+        req.descriptor.type = USB_DT_DEVICE;
+        req.descriptor.recipient = USB_RECIP_DEVICE;
+        req.descriptor.index = 0;
+        req.descriptor.language_id = 0;
+        req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+        _usb_io_sync(handle, LIBUSB_IOCTL_GET_DESCRIPTOR,
+                     &req, sizeof(libusb_request),
+                     &dev->descriptor, USB_DT_DEVICE_SIZE, &ret);
+
+        if (ret < USB_DT_DEVICE_SIZE)
+        {
+            USBERR0("couldn't read device descriptor\n");
+            free(dev);
+            CloseHandle(handle);
+            continue;
+        }
+
+        _snprintf(dev->filename, LIBUSB_PATH_MAX - 1, "%s--0x%04x-0x%04x",
+                  dev_name, dev->descriptor.idVendor, dev->descriptor.idProduct);
+
+        CloseHandle(handle);
+
+        LIST_ADD(fdev, dev);
+
+        USBMSG("found %s on %s\n", dev->filename, bus->dirname);
+    }
+
+    *devices = fdev;
+
+    return 0;
+}
+
+
+void usb_os_init(void)
+{
+    HANDLE dev;
+    libusb_request req;
+    int i;
+    int ret;
+    char dev_name[LIBUSB_PATH_MAX];
+
+    USBMSG("dll version: %d.%d.%d.%d\n",
+                VERSION_MAJOR, VERSION_MINOR,
+                VERSION_MICRO, VERSION_NANO);
+
+
+    for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
+    {
+        /* build the Windows file name */
+        _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
+                  LIBUSB_DEVICE_NAME, i);
+
+        dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
+                         FILE_FLAG_OVERLAPPED, NULL);
+
+        if (dev == INVALID_HANDLE_VALUE)
+        {
+            continue;
+        }
+
+        if (!_usb_io_sync(dev, LIBUSB_IOCTL_GET_VERSION,
+                          &req, sizeof(libusb_request),
+                          &req, sizeof(libusb_request), &ret)
+                || (ret < sizeof(libusb_request)))
+        {
+            USBERR0("getting driver version failed\n");
+            CloseHandle(dev);
+            continue;
+        }
+        else
+        {
+            _usb_version.driver.major = req.version.major;
+            _usb_version.driver.minor = req.version.minor;
+            _usb_version.driver.micro = req.version.micro;
+            _usb_version.driver.nano = req.version.nano;
+
+            USBMSG("driver version: %d.%d.%d.%d\n",
+                        req.version.major, req.version.minor,
+                        req.version.micro, req.version.nano);
+
+            /* set debug level */
+            req.timeout = 0;
+            req.debug.level = usb_log_get_level();
+
+            if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL,
+                              &req, sizeof(libusb_request),
+                              NULL, 0, NULL))
+            {
+                USBERR0("setting debug level failed");
+            }
+
+            CloseHandle(dev);
+            break;
+        }
+    }
+}
+
+
+int usb_resetep(usb_dev_handle *dev, unsigned int ep)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    req.endpoint.endpoint = (int)ep;
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req,
+                      sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req,
+                      sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not reset ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+int usb_clear_halt(usb_dev_handle *dev, unsigned int ep)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    req.endpoint.endpoint = (int)ep;
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req,
+                      sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not clear halt, ep 0x%02x, "
+                  "win error: %s", ep, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+int usb_reset(usb_dev_handle *dev)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_DEVICE,
+                      &req, sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not reset device, win error: %s\n", usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+int usb_reset_ex(usb_dev_handle *dev, unsigned int reset_type)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open\n");
+        return -EINVAL;
+    }
+
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+    req.reset_ex.reset_type = reset_type;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_DEVICE_EX,
+                      &req, sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not reset device, win error: %s\n", usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+const struct usb_version *usb_get_version(void)
+{
+    return &_usb_version;
+}
+
+void usb_set_debug(int level)
+{
+    HANDLE dev;
+    libusb_request req;
+    int i;
+    char dev_name[LIBUSB_PATH_MAX];
+
+    if (usb_log_get_level() || level)
+    {
+        USBMSG("setting debugging level to %d (%s)\n",
+                    level, level ? "on" : "off");
+    }
+
+    usb_log_set_level(level);
+
+    /* find a valid device */
+    for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
+    {
+        /* build the Windows file name */
+        _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
+                  LIBUSB_DEVICE_NAME, i);
+
+        dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
+                         FILE_FLAG_OVERLAPPED, NULL);
+
+        if (dev == INVALID_HANDLE_VALUE)
+        {
+            continue;
+        }
+
+        /* set debug level */
+        req.timeout = 0;
+        req.debug.level = usb_log_get_level();
+
+        if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL,
+                          &req, sizeof(libusb_request),
+                          NULL, 0, NULL))
+        {
+            USBERR0("setting debug level failed\n");
+        }
+
+        CloseHandle(dev);
+
+        break;
+    }
+}
+
+int usb_os_determine_children(struct usb_bus *bus)
+{
+    struct usb_device *dev;
+    int i = 0;
+
+    /* add a virtual hub to the bus to emulate this feature */
+    if (_usb_add_virtual_hub(bus))
+    {
+        if (bus->root_dev->children)
+        {
+            free(bus->root_dev->children);
+        }
+
+        bus->root_dev->num_children = 0;
+        for (dev = bus->devices; dev; dev = dev->next)
+            bus->root_dev->num_children++;
+
+        bus->root_dev->children
+        = malloc(sizeof(struct usb_device *) * bus->root_dev->num_children);
+
+        for (dev = bus->devices; dev; dev = dev->next)
+            bus->root_dev->children[i++] = dev;
+    }
+
+    return 0;
+}
+
+static int _usb_cancel_io(usb_context_t *context)
+{
+    int ret;
+    ret = _usb_abort_ep(context->dev, context->req.endpoint.endpoint);
+    WaitForSingleObject(context->ol.hEvent, 0);
+    return ret;
+}
+
+static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep)
+{
+    libusb_request req;
+
+    if (dev->impl_info == INVALID_HANDLE_VALUE)
+    {
+        USBERR0("device not open");
+        return -EINVAL;
+    }
+
+    req.endpoint.endpoint = (int)ep;
+    req.timeout = LIBUSB_DEFAULT_TIMEOUT;
+
+    if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req,
+                      sizeof(libusb_request), NULL, 0, NULL))
+    {
+        USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
+        return -usb_win_error_to_errno();
+    }
+
+    return 0;
+}
+
+static int _usb_io_sync(HANDLE dev, unsigned int code, void *out, int out_size,
+                        void *in, int in_size, int *ret)
+{
+    OVERLAPPED ol;
+    DWORD _ret;
+
+    memset(&ol, 0, sizeof(ol));
+
+    if (ret)
+        *ret = 0;
+
+    ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    if (!ol.hEvent)
+        return FALSE;
+
+    if (!DeviceIoControl(dev, code, out, out_size, in, in_size, NULL, &ol))
+    {
+        if (GetLastError() != ERROR_IO_PENDING)
+        {
+            CloseHandle(ol.hEvent);
+            return FALSE;
+        }
+    }
+
+    if (GetOverlappedResult(dev, &ol, &_ret, TRUE))
+    {
+        if (ret)
+            *ret = (int)_ret;
+        CloseHandle(ol.hEvent);
+        return TRUE;
+    }
+
+    CloseHandle(ol.hEvent);
+    return FALSE;
+}
+
+static int _usb_add_virtual_hub(struct usb_bus *bus)
+{
+    struct usb_device *dev;
+
+    if (!bus->root_dev)
+    {
+        if (!(dev = malloc(sizeof(*dev))))
+            return FALSE;
+
+        memset(dev, 0, sizeof(*dev));
+        strcpy(dev->filename, "virtual-hub");
+        dev->bus = bus;
+
+        dev->descriptor.bLength = USB_DT_DEVICE_SIZE;
+        dev->descriptor.bDescriptorType = USB_DT_DEVICE;
+        dev->descriptor.bcdUSB = 0x0200;
+        dev->descriptor.bDeviceClass = USB_CLASS_HUB;
+        dev->descriptor.bDeviceSubClass = 0;
+        dev->descriptor.bDeviceProtocol = 0;
+        dev->descriptor.bMaxPacketSize0 = 64;
+        dev->descriptor.idVendor = 0;
+        dev->descriptor.idProduct = 0;
+        dev->descriptor.bcdDevice = 0x100;
+        dev->descriptor.iManufacturer = 0;
+        dev->descriptor.iProduct = 0;
+        dev->descriptor.iSerialNumber = 0;
+        dev->descriptor.bNumConfigurations = 0;
+
+        bus->root_dev = dev;
+    }
+
+    return TRUE;
+}
+
+static void _usb_free_bus_list(struct usb_bus *bus)
+{
+    if (bus)
+    {
+        _usb_free_bus_list(bus->next);
+        if (bus->root_dev)
+            usb_free_dev(bus->root_dev);
+        _usb_free_dev_list(bus->devices);
+        usb_free_bus(bus);
+    }
+}
+
+static void _usb_free_dev_list(struct usb_device *dev)
+{
+    if (dev)
+    {
+        _usb_free_dev_list(dev->next);
+        usb_free_dev(dev);
+    }
+}
+
+static void _usb_deinit(void)
+{
+    _usb_free_bus_list(usb_get_busses());
+}
Index: trunk/kitgen/Makefile
===================================================================
--- trunk/kitgen/Makefile	(revision 175)
+++ trunk/kitgen/Makefile	(revision 175)
@@ -0,0 +1,61 @@
+GSL_URL    = http://ftp.gnu.org/gnu/gsl/gsl-1.15.tar.gz
+TDOM_URL   = http://github.com/downloads/tDOM/tdom/tDOM-0.8.3.tgz
+ZLIB_URL   = http://downloads.sourceforge.net/project/libpng/zlib/1.2.7/zlib-1.2.7.tar.gz
+XOTCL_URL  = http://downloads.sourceforge.net/project/xotcl/xotcl/1.6.7/xotcl-1.6.7.tar.gz
+SQLITE_URL = http://www.sqlite.org/sqlite-autoconf-3071401.tar.gz
+LIBUSB_URL = http://heanet.dl.sourceforge.net/project/libusb/libusb-0.1%20%28LEGACY%29/0.1.12/libusb-0.1.12.tar.gz
+
+TCL_CVS    = :pserver:anonymous@tcl.cvs.sourceforge.net:/cvsroot/tcl
+TK_CVS     = :pserver:anonymous@tktoolkit.cvs.sourceforge.net:/cvsroot/tktoolkit
+
+VFS_CVS    = :pserver:anonymous@tclvfs.cvs.sourceforge.net:/cvsroot/tclvfs
+TCLLIB_CVS = :pserver:anonymous@tcllib.cvs.sourceforge.net:/cvsroot/tcllib
+
+unspecified-target:
+
+cvs:
+	mkdir -p 8.x && cd 8.x && \
+	  cvs -d $(VFS_CVS) co tclvfs && \
+	  cvs -d ${TCLLIB_CVS} co tcllib
+	mkdir -p 8.5 && cd 8.5 && \
+	  cvs -d $(TCL_CVS) co -r core-8-5-branch tcl && \
+	  cvs -d $(TK_CVS) co -r core-8-5-branch tk
+
+tars:
+	mkdir -p 8.x && cd 8.x && \
+	  wget -q $(GSL_URL) && \
+	  wget -q $(TDOM_URL) && \
+	  wget -q $(ZLIB_URL) && \
+	  wget -q $(XOTCL_URL) && \
+	  wget -q $(SQLITE_URL) && \
+	  wget -q $(LIBUSB_URL)
+
+untar:
+	mkdir -p 8.x && cd 8.x && \
+	  tar xfz gsl-1.15.tar.gz && \
+	  tar xfz tDOM-0.8.3.tgz && \
+	  tar xfz zlib-1.2.7.tar.gz && \
+	  tar xfz xotcl-1.6.7.tar.gz && \
+	  tar xfz sqlite-autoconf-3071401.tar.gz && \
+	  tar xfz libusb-0.1.12.tar.gz && \
+	  mv gsl-1.15 gsl && \
+	  mv tDOM-0.8.3 tdom && \
+	  mv zlib-1.2.7 zlib && \
+	  mv xotcl-1.6.7 xotcl && \
+	  mv sqlite-autoconf-3071401 sqlite && \
+	  mv libusb-0.1.12 libusb
+
+configs:
+	sh config.sh 8.5/base-std
+	sh config.sh 8.5/kit-small gui
+
+small: configs
+	cd 8.5/kit-small && $(MAKE)
+
+base tidy:
+	for i in 8*/base-*/Makefile; do (cd `dirname $$i`; $(MAKE) $@); done
+
+all clean distclean tclkit-gui:
+	for i in 8*/kit-*/Makefile; do (cd `dirname $$i`; $(MAKE) $@); done
+
+.PHONY: all base tidy clean distclean small tars configs
Index: trunk/kitgen/README
===================================================================
--- trunk/kitgen/README	(revision 175)
+++ trunk/kitgen/README	(revision 175)
@@ -0,0 +1,337 @@
+Kitgen
+======
+
+[Kitgen][1] consists of a makefile, scripts, and C source code to generate
+variations of [Tclkit Lite][2], a version of [Tclkit][3] based on [Vlerq][4].
+
+  [1]: http://www.equi4.com/kitgen.html
+  [2]: http://www.equi4.com/tclkitlite.html
+  [3]: http://www.equi4.com/tclkit.html
+  [4]: http://www.vlerq.org/
+
+Kitgen is pronounced "kit-chen".
+
+
+News
+----
+
+  * 2007-05-03 : changes in cvs & build structure
+  * 2007-04-04 : added kbs.tcl, the Kitgen Build System - see README.kbs
+  * 2007-01-11 : fixed a vfs thread issue in boot.tcl, by Jeff Hobbs
+  * 2006-12-20 : fixed typo, mention the FFF feedback forum below
+  * 2006-12-15 : added *BSD support (use gmake i.s.o. make)
+  * 2006-12-12 : doc tweaks, added "cvs" target, support vlerq safe interps
+  * 2006-12-06 : updated to vlerq 4.1 and vfs::m2m 1.8
+  * 2006-12-05 : added "base" & "tidy" targets for Tcl/Tk-only builds
+  * 2006-12-04 : added "gcov" and "gprof" options, keep symbols with "sym" opt
+  * 2006-12-01 : added "b64" option to enable 64-bit compiles
+  * 2006-11-21 : simpler makefile, tweaked to work with Mingw on Win32
+  * 2006-11-19 : first release, tested on Mac OS X and Linux x86 only so far
+  
+----
+
+**UPDATE, MAY 2007:** _The build notes below are no longer working.  The CVS repository at equi4.com has been closed and will be replaced by an SVN mirror, hopefully by the end of May 2007.  For now, you can get copies of all the important pieces at [www.equi4.com](http://www.equi4.com/pub/tk/tars/) - the latest source for the "Vlerq" extension is in "vqtcl.tgz" and the latest kitgen source is in "kitgen.tgz". See also the new [mailing list](http://groups.google.com/group/starkit) for more details._
+
+----
+
+Overview
+--------
+
+To build Tclkit Lite, you need to bring several pieces together:
+
+  * sources for Tcl and Tk
+  * sources for the Thread, TclVFS, and Vlerq extensions
+  * sources for Zlib
+  * the files in this Kitgen package
+  
+There are several ways to go about this, depending on whether you want to use
+the latest sources in cvs, or tarfile releases, or your own custom versions.
+
+
+Quick start
+-----------
+
+These instructions are for a Unix-type system and do a build using CVS sources:
+
+    cvs -d :pserver:anonymous@equi4.com:/home/cvs co kitgen
+    cd kitgen
+    make tars
+    make small
+    
+That's it.  This produces two executables in `8.4/kit-small/`:
+
+  * `tclkit-cli` - a tclsh-like console app
+  * `tclkit-dyn` - wish-like, after a `package require Tk`
+
+Or, to build a threaded Tcl/Tk 8.5 build with all encodings and more included:
+
+    cvs -d :pserver:anonymous@equi4.com:/home/cvs co kitgen
+    cd kitgen
+    make tars
+    make large
+    
+In this case a third executable is also produced:
+
+  * `tclkit-gui` - same as "tclkit-dyn", but with Tk linked-in statically
+
+On **Mac OS X**, "make large" builds a universal binary for PowerPC and Intel.
+
+On **Windows** you can use this approach if you download `msys_mingw 0.8` from
+<http://tcl.sourceforge.net/> (make sure you extract to a path without spaces).
+Then launch `msys.bat` to get a bash shell and proceed further as above.
+The binaries will end up as "tclkit-{cli,dyn,gui}.exe" in this case.
+
+You can use `make cvs` to update all cvs areas under `8.4/` and `8.5/`.
+
+
+Tcl/Tk builds
+-------------
+
+Kitgen can also be used to build Tcl/Tk binaries in various configurations.
+There is a make target called "base" which builds just `tclsh` and `wish` and
+installs them in the `build/{bin,include,lib}` directories. The Tcl/Tk headers
+are included so that these areas can be used as basis for building extensions.
+
+In the top-level makefile, `make base` rebuilds all areas matching `8.*/base-*`.
+This can be used to rebuild all build variants you have set up in one step,
+after a source change to Tcl and/or Tk.
+
+If you've set up the `8.4/` and `8.5/` directories as described above, then a
+`base-std` configuration will already have been set up, with symbols enabled.
+So after the quick-start, you can do the following to build 8.4 and 8.5 versions
+of `tclsh` and `wish` and clean up all intermediate files with one command:
+
+    make base tidy
+
+The `make tidy` command removes all intermediate build files, but is careful to
+keep the `8.*/base-*/build/{bin,include,lib}` directories.
+
+
+In detail
+---------
+
+The makefile in kitgen/ is only a convenience wrapper to make the above
+possible. The real work is carried out by two other scripts:
+
+  * `config.sh` is a script to set up specific build configurations
+  * `setupvfs.tcl` is the script used internally to construct the final app
+
+Kitgen is designed to proceed in several phases, so many variants can be built:
+
+  1. place all necessary source code in directories named `8.4` or `8.5`
+  2. configure one or more build directories using the config.sh script
+  3. go to any of these build directories and type `make`
+  4. remove intermediate build results with `make clean`
+  5. copy and rename the final tclkit-* files to your ~/bin/ or some such
+  6. remove the generated executables as well with `make distclean`
+  7. forget about kitgen, until you need to update your builds
+
+Note: after updating any of the sources, you can do a `make all` in the kitgen/
+directory to rebuild all executables (assuming you did `make clean` before).
+
+
+Directory structure
+-------------------
+
+The key trick is to get the directory structure right so that `sh config.sh` and
+`make` will do the right thing. Both assume the following structure exists:
+
+    kitgen/
+        8.4/
+            base-*/
+            kit-*/
+            tcl/
+            tk/
+        8.5/
+            base-*/
+            kit-*/
+            tcl/
+            tk/
+        8.x/
+            tclvfs/
+            thread/
+            vqtcl/
+            zlib/
+        config.sh
+        files/
+        ...
+
+The 8.x/ directory contains the source code which works with both 8.4 and 8.5.
+
+You do not have to have both 8.4/ and 8.5/, nor do you have to give them exactly
+these names, but they must start with "8". There can be multiple sets of code
+sources co-existing next to each other even, if needed. Symlinks should work.
+
+
+config.sh
+---------
+
+The config.sh script creates a Makefile with settings that specify exactly what
+type of executable(s) are to be generated. All these makefiles end up in sub-
+directories of 8.4/, 8.5/, or whatever other 8-prefixed name you work with.
+
+The `make small` example given in the quick start uses default settings:
+
+    sh config.sh 8.4/kit-small cli dyn
+
+The result is a makefile called "8.4/kit-small/Makefile". To build that setup,
+just do `cd 8.4/kit-small && make` . Since cli & dyn were specified, only those
+two executables will be built, but you can do an explicit `make tclkit-gui` .
+
+The `make large` example in the quick start uses these more elaborate settings:
+
+    sh config.sh 8.5/kit-large aqua univ thread allenc allmsgs tzdata
+    
+Again, `cd 8.5/kit-large && make` is all it takes to build that configuation.
+
+The first argument of config.sh is the build name. It must be a two-part name,
+and the first part must be one of your existing "8*/" directory areas. The
+second part could be any name, but the suggested name is "kit-something".
+
+The remaining arguments of config.sh specify one or more build options:
+
+  * `allenc` - include all encodings, not just the usual set of 7
+  * `allmsgs` - include all localized message catalogs _(8.5 only)_
+  * `aqua` - build Tk for Aqua i.s.o. X11 _(Mac OS X only)_
+  * `b64` - generate 64-bit binaries
+  * `cli` - build the "tclkit-cli" command-line version
+  * `dyn` - build the "tclkit-dyn" version which loads Tk dynamically
+  * `gcov` - enable code coverage _(implies `sym`)_
+  * `gprof` - enable profiling _(implies `sym`)_
+  * `gui` - build the "tclkit-gui" version which has Tk linked-in statically
+  * `ppc` - build for PowerPC _(Mac OS X only)_
+  * `sym` - enable & keep debugger symbols in the executable
+  * `thread` - build with threading and include the Thread extension
+  * `tzdata` - include the complete set of timezone data files _(8.5 only)_
+  * `univ` - build for both PowerPC and Intel _(Mac OS X only)_
+  * `x86` - build for Intel _(Mac OS X only)_
+  
+When not specified, the default is to build all `cli`, `dyn`, `gui` variants.
+
+
+Makefile
+--------
+
+All makefiles created by config.sh or manually need to reside in subdirectories
+of some 8*/ source directory. That location determines which source code will
+be used, since all builds are done relative to their parent dirs.
+
+To generate a Makefile with config and then do a build, proceed as follows:
+
+    sh config.sh 8.4/kit-mybuild <config options ...>
+    cd 8.4/kit-mybuild
+    make
+
+Often, that's all you'll need.  However, to debug or tweak things, read on...
+
+The common parts of these makefiles is read in during use, through the
+
+    include ../../makefile.include
+    
+line at the end of each makefile.  It defines the following main targets:
+
+  * `all` - builds all targets given to config.sh (default is cli + dyn + gui)
+  * `tclkit-cli` - builds just that executable (same for -dyn and -gui)
+  * `clean` - removes all intermediate build results
+  * `distclean` - removes the tclkit-* executables as well
+
+Note that these same build targets also exist in the top-level makefile in
+kitgen/ - when used there, the corresponding target in *all* subdirectory
+makefiles will be invoked. To prevent a specific makefile from being run that
+way, give it some other name than "Makefile".
+
+The individual make's are configured mostly by setting make variables:
+
+  * `GUI_OPTS` - options needed to build with Tk
+  * `KIT_OPTS` - flags used by the setupvfs.tcl script
+  * `PLAT` - either "unix" or "win" to select the proper source directory
+  * `PRIV` - normally "install-private-headers", but omitted on Windows
+  * `TCL_OPTS` - flags for configuring the build of Tcl
+  * `TK_OPTS` - flags for configuring the build of Tk
+  * `TKDYN_OPTS` - flags for configuring the build of Tk as shared lib
+  * `THREADDYN_OPTS` - flags for configuring the build of Thread as shared lib
+  * `VFS_OPTS` - flags for configuring the build of the TclVFS extension
+  * `VLERQ_OPTS` - flags for configuring the build of the Vlerq extension
+
+Other variables such as CFLAGS and LDFLAGS also affect the build settings.
+
+Doing `make` will build the executables.  This only does a full build when there
+are no build/* directories present.  One way to force this is `make clean` 
+which does a `rm -rf build` .  Otherwise, for directories which already exist,
+the rebuild is skipped.  To force a rebuild of only the vlerq extension, do:
+
+    rm -rf build/vqtcl && make
+    
+The sub-directories of build/ are the areas where each call to the respective
+"configure" script places its results.  When debugging either a build or the
+extension itself, it may be more convenient to work in that specific subdir:
+
+    cd builds/vqtcl && make
+    
+After that, you can do `cd ../.. && make` to complete the tclkit builds.
+
+
+setupvfs.tcl
+------------
+
+This is an internal script used as last step by the makefiles to construct the
+virtual file system (VFS) containing runtime scripts at the end of every tclkit.
+See the makefile.include file for exact details.
+
+The setupvfs.tcl script is special in that it can only be used by a "raw" kit,
+i.o.w. a tclkit executable which does not yet have the VFS part appended to it.
+It is essentially a way for tclkit to bootstrap itself into becoming usable.
+
+The reason things are done this way is that it avoids the need to have a working
+tclkit around to construct a new one, which'd be a chicken-and-egg situation.
+So this approach makes it possible to build a tclkit totally from scratch
+without requiring any binary data files (as "genkit" did).
+
+Some variations in generating the VFS data are configured via the command line:
+
+  * `-d` - output some debugging info from this setup script
+  * `-e` - include all encodings i.s.o. 7 basic ones (encodings/)
+  * `-m` - include all localized message files (tcl 8.5, msgs/)
+  * `-t` - include the thread extension as shared lib in vfs
+  * `-z` - include timezone data files (tcl 8.5, tzdata/)
+
+As with the makefiles, most of these details are dealt with automatically if you
+use the config.sh script to create your configurations.
+
+
+License & support
+-----------------
+
+The Tclkit-specific sources are license free, they just have a copyright. Hold
+the author(s) harmless and any lawful use is permitted.
+
+This does *not* apply to any of the sources of the other major Open Source
+Software used in Tclkit, which each have very liberal BSD/MIT-like licenses:
+
+  * Tcl/Tk, TclVFS, Thread, Vlerq, Zlib
+
+If kitgen does not work right on your platform, please post to the [Starkit][5] mailing list. Or you can use the feedback forum at [FFF][6] to report bugs.
+
+  [5]: http://www.equi4.com/mailman/listinfo/starkit
+  [6]: http://www.equi4.com/fff/Home
+
+
+Acknowledgements
+----------------
+
+With thanks to John Ousterhout for creating Tcl/Tk, Matt Newman and Vince Darley
+for developing the virtual file system, and the members of the Tcl Core Team for
+diligently maintaining and taking forward the Tcl/Tk code base plus extensions.
+
+A special thanks to Daniel Steffen for making Tcl/Tk work so well on Mac OS X.
+
+Thanks also to Eolas Technologies Inc for sponsoring the Vlerq project on which
+Tclkit Lite is based. There'd not be a Tclkit Lite, nor kitgen, without them.
+
+Contributors & testers:
+
+  * Brian Theado (mingw/win32)
+  * Pat Thoyts (more mingw/win32 fixes)
+
+Lastly, many thanks to all those who have contributed to the evolution of Tclkit
+over the years, with suggestions, bug reports, encouragement, and enthusiasm.
Index: trunk/kitgen/blt2.4z-patch-2
===================================================================
--- trunk/kitgen/blt2.4z-patch-2	(revision 175)
+++ trunk/kitgen/blt2.4z-patch-2	(revision 175)
@@ -0,0 +1,948 @@
+diff -cr blt2.4z/src/bltGrAxis.c blt2.4z-new/src/bltGrAxis.c
+*** blt2.4z/src/bltGrAxis.c	2002-09-18 17:30:51.000000000 -0500
+--- blt2.4z-new/src/bltGrAxis.c	2002-12-02 22:39:26.000000000 -0600
+***************
+*** 1424,1482 ****
+      double majorStep, minorStep;
+      int nMajor, nMinor;
+  
+!     min = (min != 0.0) ? log10(FABS(min)) : 0.0;
+!     max = (max != 0.0) ? log10(FABS(max)) : 1.0;
+! 
+!     tickMin = floor(min);
+!     tickMax = ceil(max);
+!     range = tickMax - tickMin;
+! 
+!     if (range > 10) {
+! 	/* There are too many decades to display a major tick at every
+! 	 * decade.  Instead, treat the axis as a linear scale.  */
+! 	range = NiceNum(range, 0);
+! 	majorStep = NiceNum(range / DEF_NUM_TICKS, 1);
+! 	tickMin = UFLOOR(tickMin, majorStep);
+! 	tickMax = UCEIL(tickMax, majorStep);
+! 	nMajor = (int)((tickMax - tickMin) / majorStep) + 1;
+! 	minorStep = EXP10(floor(log10(majorStep)));
+! 	if (minorStep == majorStep) {
+! 	    nMinor = 4, minorStep = 0.2;
+  	} else {
+! 	    nMinor = Round(majorStep / minorStep) - 1;
+! 	}
+!     } else {
+! 	if (tickMin == tickMax) {
+! 	    tickMax++;
+! 	}
+! 	majorStep = 1.0;
+! 	nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */
+! 
+! 	minorStep = 0.0;	/* This is a special hack to pass
+  				 * information to the GenerateTicks
+  				 * routine. An interval of 0.0 tells
+  				 *	1) this is a minor sweep and 
+  				 *	2) the axis is log scale.  
+  				 */
+! 	nMinor = 10;
+!     }
+!     if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+! 	((axisPtr->looseMin == TICK_RANGE_LOOSE) && 
+! 	 (DEFINED(axisPtr->reqMin)))) {
+! 	tickMin = min;
+! 	nMajor++;
+!     }
+!     if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+! 	((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+! 	 (DEFINED(axisPtr->reqMax)))) {
+! 	tickMax = max;
+      }
+      axisPtr->majorSweep.step = majorStep;
+      axisPtr->majorSweep.initial = floor(tickMin);
+      axisPtr->majorSweep.nSteps = nMajor;
+      axisPtr->minorSweep.initial = axisPtr->minorSweep.step = minorStep;
+      axisPtr->minorSweep.nSteps = nMinor;
+- 
+      SetAxisRange(&axisPtr->axisRange, tickMin, tickMax);
+  }
+  
+--- 1424,1485 ----
+      double majorStep, minorStep;
+      int nMajor, nMinor;
+  
+!     nMajor = nMinor = 0;
+!     majorStep = minorStep = 0.0;
+!     if (min < max) {
+! 	min = (min != 0.0) ? log10(FABS(min)) : 0.0;
+! 	max = (max != 0.0) ? log10(FABS(max)) : 1.0;
+! 	
+! 	tickMin = floor(min);
+! 	tickMax = ceil(max);
+! 	range = tickMax - tickMin;
+! 	
+! 	if (range > 10) {
+! 	    /* There are too many decades to display a major tick at every
+! 	     * decade.  Instead, treat the axis as a linear scale.  */
+! 	    range = NiceNum(range, 0);
+! 	    majorStep = NiceNum(range / DEF_NUM_TICKS, 1);
+! 	    tickMin = UFLOOR(tickMin, majorStep);
+! 	    tickMax = UCEIL(tickMax, majorStep);
+! 	    nMajor = (int)((tickMax - tickMin) / majorStep) + 1;
+! 	    minorStep = EXP10(floor(log10(majorStep)));
+! 	    if (minorStep == majorStep) {
+! 		nMinor = 4, minorStep = 0.2;
+! 	    } else {
+! 		nMinor = Round(majorStep / minorStep) - 1;
+! 	    }
+  	} else {
+! 	    if (tickMin == tickMax) {
+! 		tickMax++;
+! 	    }
+! 	    majorStep = 1.0;
+! 	    nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */
+! 	    
+! 	    minorStep = 0.0;	/* This is a special hack to pass
+  				 * information to the GenerateTicks
+  				 * routine. An interval of 0.0 tells
+  				 *	1) this is a minor sweep and 
+  				 *	2) the axis is log scale.  
+  				 */
+! 	    nMinor = 10;
+! 	}
+! 	if ((axisPtr->looseMin == TICK_RANGE_TIGHT) ||
+! 	    ((axisPtr->looseMin == TICK_RANGE_LOOSE) && 
+! 	     (DEFINED(axisPtr->reqMin)))) {
+! 	    tickMin = min;
+! 	    nMajor++;
+! 	}
+! 	if ((axisPtr->looseMax == TICK_RANGE_TIGHT) ||
+! 	    ((axisPtr->looseMax == TICK_RANGE_LOOSE) &&
+! 	     (DEFINED(axisPtr->reqMax)))) {
+! 	    tickMax = max;
+! 	}
+      }
+      axisPtr->majorSweep.step = majorStep;
+      axisPtr->majorSweep.initial = floor(tickMin);
+      axisPtr->majorSweep.nSteps = nMajor;
+      axisPtr->minorSweep.initial = axisPtr->minorSweep.step = minorStep;
+      axisPtr->minorSweep.nSteps = nMinor;
+      SetAxisRange(&axisPtr->axisRange, tickMin, tickMax);
+  }
+  
+***************
+*** 1551,1581 ****
+      double axisMin, axisMax;
+      int nTicks;
+  
+!     range = max - min;
+! 
+!     /* Calculate the major tick stepping. */
+!     if (axisPtr->reqStep > 0.0) {
+! 	/* An interval was designated by the user.  Keep scaling it
+! 	 * until it fits comfortably within the current range of the
+! 	 * axis.  */
+! 	step = axisPtr->reqStep;
+! 	while ((2 * step) >= range) {
+! 	    step *= 0.5;
+  	}
+!     } else {
+! 	range = NiceNum(range, 0);
+! 	step = NiceNum(range / DEF_NUM_TICKS, 1);
+      }
+- 
+-     /* Find the outer tick values. Add 0.0 to prevent getting -0.0. */
+-     axisMin = tickMin = floor(min / step) * step + 0.0;
+-     axisMax = tickMax = ceil(max / step) * step + 0.0;
+- 
+-     nTicks = Round((tickMax - tickMin) / step) + 1;
+      axisPtr->majorSweep.step = step;
+      axisPtr->majorSweep.initial = tickMin;
+      axisPtr->majorSweep.nSteps = nTicks;
+! 
+      /*
+       * The limits of the axis are either the range of the data
+       * ("tight") or at the next outer tick interval ("loose").  The
+--- 1554,1588 ----
+      double axisMin, axisMax;
+      int nTicks;
+  
+!     nTicks = 0;
+!     tickMin = tickMax = 0.0;
+!     if (min < max) {
+! 	range = max - min;
+! 	
+! 	/* Calculate the major tick stepping. */
+! 	if (axisPtr->reqStep > 0.0) {
+! 	    /* An interval was designated by the user.  Keep scaling it
+! 	     * until it fits comfortably within the current range of the
+! 	     * axis.  */
+! 	    step = axisPtr->reqStep;
+! 	    while ((2 * step) >= range) {
+! 		step *= 0.5;
+! 	    }
+! 	} else {
+! 	    range = NiceNum(range, 0);
+! 	    step = NiceNum(range / DEF_NUM_TICKS, 1);
+  	}
+! 	
+! 	/* Find the outer tick values. Add 0.0 to prevent getting -0.0. */
+! 	axisMin = tickMin = floor(min / step) * step + 0.0;
+! 	axisMax = tickMax = ceil(max / step) * step + 0.0;
+! 	
+! 	nTicks = Round((tickMax - tickMin) / step) + 1;
+      }
+      axisPtr->majorSweep.step = step;
+      axisPtr->majorSweep.initial = tickMin;
+      axisPtr->majorSweep.nSteps = nTicks;
+!     
+      /*
+       * The limits of the axis are either the range of the data
+       * ("tight") or at the next outer tick interval ("loose").  The
+***************
+*** 1596,1604 ****
+  	axisMax = max;
+      }
+      SetAxisRange(&axisPtr->axisRange, axisMin, axisMax);
+! 
+      /* Now calculate the minor tick step and number. */
+! 
+      if ((axisPtr->reqNumMinorTicks > 0) && 
+  	((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0)) {
+  	nTicks = axisPtr->reqNumMinorTicks - 1;
+--- 1603,1611 ----
+  	axisMax = max;
+      }
+      SetAxisRange(&axisPtr->axisRange, axisMin, axisMax);
+!     
+      /* Now calculate the minor tick step and number. */
+!     
+      if ((axisPtr->reqNumMinorTicks > 0) && 
+  	((axisPtr->flags & AXIS_CONFIG_MAJOR) == 0)) {
+  	nTicks = axisPtr->reqNumMinorTicks - 1;
+***************
+*** 1614,1620 ****
+      axisPtr->minorSweep.nSteps = nTicks;
+  }
+  
+- 
+  static void
+  SweepTicks(axisPtr)
+      Axis *axisPtr;
+--- 1621,1626 ----
+***************
+*** 1684,1692 ****
+      for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+  	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+  	elemPtr = Blt_ChainGetValue(linkPtr);
+! 	(*elemPtr->procsPtr->extentsProc) (elemPtr, &exts);
+! 	GetDataLimits(elemPtr->axes.x, exts.left, exts.right);
+! 	GetDataLimits(elemPtr->axes.y, exts.top, exts.bottom);
+      }
+      /*
+       * Step 3:  Now that we know the range of data values for each axis,
+--- 1690,1700 ----
+      for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+  	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+  	elemPtr = Blt_ChainGetValue(linkPtr);
+! 	if (!elemPtr->hidden) {
+! 	    (*elemPtr->procsPtr->extentsProc) (elemPtr, &exts);
+! 	    GetDataLimits(elemPtr->axes.x, exts.left, exts.right);
+! 	    GetDataLimits(elemPtr->axes.y, exts.top, exts.bottom);
+! 	}
+      }
+      /*
+       * Step 3:  Now that we know the range of data values for each axis,
+diff -cr blt2.4z/src/bltGrElem.c blt2.4z-new/src/bltGrElem.c
+*** blt2.4z/src/bltGrElem.c	2002-09-18 17:30:51.000000000 -0500
+--- blt2.4z-new/src/bltGrElem.c	2002-12-02 22:42:31.000000000 -0600
+***************
+*** 1215,1223 ****
+  {
+      int nNames;			/* Number of names found in Tcl name list */
+      char **nameArr;		/* Broken out array of element names */
+-     Blt_HashSearch cursor;
+      register int i;
+-     register Blt_HashEntry *hPtr;
+      Element *elemPtr;		/* Element information record */
+  
+      if (Tcl_SplitList(graphPtr->interp, newList, &nNames, &nameArr) != TCL_OK) {
+--- 1215,1221 ----
+***************
+*** 1227,1243 ****
+      }
+      /* Clear the display list and mark all elements as hidden.  */
+      Blt_ChainReset(graphPtr->elements.displayList);
+-     for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor);
+- 	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+- 	elemPtr = (Element *)Blt_GetHashValue(hPtr);
+- 	elemPtr->hidden = TRUE;
+-     }
+  
+      /* Rebuild the display list, checking that each name it exists
+       * (currently ignoring invalid element names).  */
+      for (i = 0; i < nNames; i++) {
+  	if (NameToElement(graphPtr, nameArr[i], &elemPtr) == TCL_OK) {
+- 	    elemPtr->hidden = FALSE;
+  	    Blt_ChainAppend(graphPtr->elements.displayList, elemPtr);
+  	}
+      }
+--- 1225,1235 ----
+***************
+*** 1399,1406 ****
+  	    /* Comment the PostScript to indicate the start of the element */
+  	    Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n", 
+  		elemPtr->name);
+! 	    (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken, 
+! 		elemPtr);
+  	}
+      }
+  }
+--- 1391,1397 ----
+  	    /* Comment the PostScript to indicate the start of the element */
+  	    Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n", 
+  		elemPtr->name);
+! 	    (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken, elemPtr);
+  	}
+      }
+  }
+***************
+*** 1426,1433 ****
+  	if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+  	    Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n",
+  		elemPtr->name);
+! 	    (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken, 
+! 						   elemPtr);
+  	}
+      }
+  }
+--- 1417,1423 ----
+  	if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) {
+  	    Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n",
+  		elemPtr->name);
+! 	    (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken, elemPtr);
+  	}
+      }
+  }
+***************
+*** 1671,1676 ****
+--- 1661,1667 ----
+      ClosestSearch search;
+      int i, x, y;
+      int flags = TCL_LEAVE_ERR_MSG;
++     int found;
+  
+      if (graphPtr->flags & RESET_AXES) {
+  	Blt_ResetAxes(graphPtr);
+***************
+*** 1715,1727 ****
+      search.dist = (double)(search.halo + 1);
+  
+      if (i < argc) {
+  	for ( /* empty */ ; i < argc; i++) {
+  	    if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+  		return TCL_ERROR;	/* Can't find named element */
+  	    }
+! 	    if (elemPtr->hidden) {
+  		Tcl_AppendResult(interp, "element \"", argv[i], "\" is hidden",
+! 		    (char *)NULL);
+  		return TCL_ERROR;	/* Element isn't visible */
+  	    }
+  	    /* Check if the X or Y vectors have notifications pending */
+--- 1706,1728 ----
+      search.dist = (double)(search.halo + 1);
+  
+      if (i < argc) {
++ 	Blt_ChainLink *linkPtr;
++ 
+  	for ( /* empty */ ; i < argc; i++) {
+  	    if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) {
+  		return TCL_ERROR;	/* Can't find named element */
+  	    }
+! 	    found = FALSE;
+! 	    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+! 		 linkPtr == NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+! 		if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+! 		    found = TRUE;
+! 		    break;
+! 		}
+! 	    }
+! 	    if ((!found) || (elemPtr->hidden)) {
+  		Tcl_AppendResult(interp, "element \"", argv[i], "\" is hidden",
+! 			(char *)NULL);
+  		return TCL_ERROR;	/* Element isn't visible */
+  	    }
+  	    /* Check if the X or Y vectors have notifications pending */
+***************
+*** 1744,1759 ****
+  	for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+  	    linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+  	    elemPtr = Blt_ChainGetValue(linkPtr);
+- 
+  	    /* Check if the X or Y vectors have notifications pending */
+! 	    if ((elemPtr->flags & MAP_ITEM) ||
+  		(Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+  		(Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+  		continue;
+  	    }
+! 	    if (!elemPtr->hidden) {
+! 		(*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+! 	    }
+  	}
+  
+      }
+--- 1745,1758 ----
+  	for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList);
+  	    linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+  	    elemPtr = Blt_ChainGetValue(linkPtr);
+  	    /* Check if the X or Y vectors have notifications pending */
+! 	    if ((elemPtr->hidden) || 
+! 		(elemPtr->flags & MAP_ITEM) ||
+  		(Blt_VectorNotifyPending(elemPtr->x.clientId)) ||
+  		(Blt_VectorNotifyPending(elemPtr->y.clientId))) {
+  		continue;
+  	    }
+! 	    (*elemPtr->procsPtr->closestProc)(graphPtr, elemPtr, &search);
+  	}
+  
+      }
+***************
+*** 1859,1888 ****
+  	    return TCL_ERROR;	/* Failed to configure element */
+  	}
+  	if (Blt_ConfigModified(elemPtr->specsPtr, "-hide", (char *)NULL)) {
+- 	    Blt_ChainLink *linkPtr;
+- 
+- 	    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
+- 		linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+- 		if (elemPtr == Blt_ChainGetValue(linkPtr)) {
+- 		    break;
+- 		}
+- 	    }
+- 	    if ((elemPtr->hidden) != (linkPtr == NULL)) {
+- 
+- 		/* The element's "hidden" variable is out of sync with
+- 		 * the display list. [That's what you get for having
+- 		 * two ways to do the same thing.]  This affects what
+- 		 * elements are considered for axis ranges and
+- 		 * displayed in the legend. Update the display list by
+- 		 * either by adding or removing the element.  */
+- 
+- 		if (linkPtr == NULL) {
+- 		    Blt_ChainPrepend(graphPtr->elements.displayList, elemPtr);
+- 		} else {
+- 		    Blt_ChainDeleteLink(graphPtr->elements.displayList, 
+- 					linkPtr);
+- 		}
+- 	    }
+  	    graphPtr->flags |= RESET_AXES;
+  	    elemPtr->flags |= MAP_ITEM;
+  	}
+--- 1858,1863 ----
+diff -cr blt2.4z/src/bltGrMarker.c blt2.4z-new/src/bltGrMarker.c
+*** blt2.4z/src/bltGrMarker.c	2002-09-18 17:30:51.000000000 -0500
+--- blt2.4z-new/src/bltGrMarker.c	2002-12-11 01:55:27.000000000 -0600
+***************
+*** 29,34 ****
+--- 29,37 ----
+  #include "bltChain.h"
+  #include "bltGrElem.h"
+  
++ #define GETBITMAP(b) \
++ 	(((b)->destBitmap == None) ? (b)->srcBitmap : (b)->destBitmap)
++ 
+  #define MAX_OUTLINE_POINTS	12
+  
+  /* Map graph coordinates to normalized coordinates [0..1] */
+***************
+*** 812,818 ****
+  
+      /* Polygon specific attributes and fields */
+  
+!     Point2D *screenPts;
+  
+      ColorPair outline;
+      ColorPair fill;
+--- 815,827 ----
+  
+      /* Polygon specific attributes and fields */
+  
+!     Point2D *screenPts;		/* Array of points representing the
+! 				 * polygon in screen coordinates. It's
+! 				 * not used for drawing, but to
+! 				 * generate the outlinePts and fillPts
+! 				 * arrays that are the coordinates of
+! 				 * the possibly clipped outline and
+! 				 * filled polygon. */
+  
+      ColorPair outline;
+      ColorPair fill;
+***************
+*** 1563,1571 ****
+      if (bmPtr->srcBitmap == None) {
+  	return TCL_OK;
+      }
+-     if (bmPtr->destBitmap == None) {
+- 	bmPtr->destBitmap = bmPtr->srcBitmap;
+-     }
+      bmPtr->theta = FMOD(bmPtr->rotate, 360.0);
+      if (bmPtr->theta < 0.0) {
+  	bmPtr->theta += 360.0;
+--- 1572,1577 ----
+***************
+*** 1650,1658 ****
+      if (bmPtr->srcBitmap == None) {
+  	return;
+      }
+!     if (bmPtr->destBitmap != bmPtr->srcBitmap) {
+  	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+! 	bmPtr->destBitmap = bmPtr->srcBitmap;
+      }
+      /* 
+       * Collect the coordinates.  The number of coordinates will determine
+--- 1656,1664 ----
+      if (bmPtr->srcBitmap == None) {
+  	return;
+      }
+!     if (bmPtr->destBitmap != None) {
+  	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+! 	bmPtr->destBitmap = None;
+      }
+      /* 
+       * Collect the coordinates.  The number of coordinates will determine
+***************
+*** 1752,1758 ****
+      } else {
+  	bmPtr->destWidth = srcWidth;
+  	bmPtr->destHeight = srcHeight;
+! 	bmPtr->destBitmap = bmPtr->srcBitmap;
+      }
+      bmPtr->anchorPos = anchorPos;
+      {
+--- 1758,1764 ----
+      } else {
+  	bmPtr->destWidth = srcWidth;
+  	bmPtr->destHeight = srcHeight;
+! 	bmPtr->destBitmap = None;
+      }
+      bmPtr->anchorPos = anchorPos;
+      {
+***************
+*** 1909,1917 ****
+      Graph *graphPtr = markerPtr->graphPtr;
+      BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+      double theta;
+  
+!     if ((bmPtr->destBitmap == None) || (bmPtr->destWidth < 1) || 
+! 	(bmPtr->destHeight < 1)) {
+  	return;
+      }
+      theta = FMOD(bmPtr->theta, (double)90.0);
+--- 1915,1924 ----
+      Graph *graphPtr = markerPtr->graphPtr;
+      BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+      double theta;
++     Pixmap bitmap;
+  
+!     bitmap = GETBITMAP(bmPtr);
+!     if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) {
+  	return;
+      }
+      theta = FMOD(bmPtr->theta, (double)90.0);
+***************
+*** 1934,1947 ****
+  	    XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+  		 polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin);
+  	}
+! 	XSetClipMask(graphPtr->display, bmPtr->gc, bmPtr->destBitmap);
+  	XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x, 
+  	       (int)bmPtr->anchorPos.y);
+      } else {
+  	XSetClipMask(graphPtr->display, bmPtr->gc, None);
+  	XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+      }
+!     XCopyPlane(graphPtr->display, bmPtr->destBitmap, drawable, bmPtr->gc, 0, 0,
+  	bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x, 
+  	(int)bmPtr->anchorPos.y, 1);
+  }
+--- 1941,1954 ----
+  	    XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+  		 polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin);
+  	}
+! 	XSetClipMask(graphPtr->display, bmPtr->gc, bitmap);
+  	XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x, 
+  	       (int)bmPtr->anchorPos.y);
+      } else {
+  	XSetClipMask(graphPtr->display, bmPtr->gc, None);
+  	XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+      }
+!     XCopyPlane(graphPtr->display, bitmap, drawable, bmPtr->gc, 0, 0,
+  	bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x, 
+  	(int)bmPtr->anchorPos.y, 1);
+  }
+***************
+*** 1965,1972 ****
+  {
+      Graph *graphPtr = markerPtr->graphPtr;
+      BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+  
+!     if (bmPtr->destBitmap == None) {
+  	return;
+      }
+      if (bmPtr->fillColor != NULL) {
+--- 1972,1981 ----
+  {
+      Graph *graphPtr = markerPtr->graphPtr;
+      BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
++     Pixmap bitmap;
+  
+!     bitmap = GETBITMAP(bmPtr);
+!     if (bitmap == None) {
+  	return;
+      }
+      if (bmPtr->fillColor != NULL) {
+***************
+*** 1982,1988 ****
+      Blt_FormatToPostScript(psToken, "    %d %d true [%d 0 0 %d 0 %d] {",
+  	bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth, 
+  	-bmPtr->destHeight, bmPtr->destHeight);
+!     Blt_BitmapDataToPostScript(psToken, graphPtr->display, bmPtr->destBitmap,
+  	bmPtr->destWidth, bmPtr->destHeight);
+      Blt_AppendToPostScript(psToken, "    } imagemask\n",
+  	"grestore\n", (char *)NULL);
+--- 1991,1997 ----
+      Blt_FormatToPostScript(psToken, "    %d %d true [%d 0 0 %d 0 %d] {",
+  	bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth, 
+  	-bmPtr->destHeight, bmPtr->destHeight);
+!     Blt_BitmapDataToPostScript(psToken, graphPtr->display, bitmap,
+  	bmPtr->destWidth, bmPtr->destHeight);
+      Blt_AppendToPostScript(psToken, "    } imagemask\n",
+  	"grestore\n", (char *)NULL);
+***************
+*** 2018,2024 ****
+      if (bmPtr->fillGC != NULL) {
+  	Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+      }
+!     if (bmPtr->destBitmap != bmPtr->srcBitmap) {
+  	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+      }
+  }
+--- 2027,2033 ----
+      if (bmPtr->fillGC != NULL) {
+  	Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+      }
+!     if (bmPtr->destBitmap != None) {
+  	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+      }
+  }
+***************
+*** 2127,2134 ****
+  	    imPtr->tkImage = Tk_GetImage(interp, graphPtr->tkwin,
+  		imPtr->imageName, ImageChangedProc, imPtr);
+  	    if (imPtr->tkImage == NULL) {
+- 		Tcl_AppendResult(interp, "can't find an image \"", 
+- 			imPtr->imageName, "\"", (char *)NULL);
+  		Blt_Free(imPtr->imageName);
+  		imPtr->imageName = NULL;
+  		return TCL_ERROR;
+--- 2136,2141 ----
+***************
+*** 2494,2499 ****
+--- 2501,2509 ----
+      if (imPtr->srcImage != NULL) {
+  	Blt_FreeColorImage(imPtr->srcImage);
+      }
++     if (imPtr->gc != NULL) {
++ 	Tk_FreeGC(graphPtr->display, imPtr->gc);
++     }
+  }
+  
+  /*
+***************
+*** 3747,3757 ****
+  {
+      PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+  
+!     if (pmPtr->nWorldPts < 2) {
+! 	return FALSE;
+      }
+!     return Blt_PointInPolygon(samplePtr, pmPtr->screenPts, 
+! 	pmPtr->nWorldPts + 1);
+  }
+  
+  /*
+--- 3757,3767 ----
+  {
+      PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+  
+!     if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+! 	return Blt_PointInPolygon(samplePtr, pmPtr->screenPts, 
+! 		  pmPtr->nWorldPts + 1);
+      }
+!     return FALSE;
+  }
+  
+  /*
+***************
+*** 3769,3775 ****
+  {
+      PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+      
+!     if (pmPtr->nWorldPts >= 3) {
+  	return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts,
+  	       enclosed);
+      }
+--- 3779,3785 ----
+  {
+      PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+      
+!     if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+  	return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts,
+  	       enclosed);
+      }
+***************
+*** 4036,4041 ****
+--- 4046,4054 ----
+      if (pmPtr->outlinePts != NULL) {
+  	Blt_Free(pmPtr->outlinePts);
+      }
++     if (pmPtr->screenPts != NULL) {
++ 	Blt_Free(pmPtr->screenPts);
++     }
+      Blt_FreeColorPair(&pmPtr->outline);
+      Blt_FreeColorPair(&pmPtr->fill);
+  }
+***************
+*** 4260,4265 ****
+--- 4273,4279 ----
+      int nNames, nOpts;
+      char **options;
+      register int i;
++     int under;
+  
+      /* Figure out where the option value pairs begin */
+      argc -= 3;
+***************
+*** 4289,4294 ****
+--- 4303,4309 ----
+  	}
+  	/* Save the old marker. */
+  	oldName = markerPtr->name;
++ 	under = markerPtr->drawUnder;
+  	if (Tk_ConfigureWidget(interp, graphPtr->tkwin, 
+  		markerPtr->classPtr->configSpecs, nOpts, options, 
+  		(char *)markerPtr, flags) != TCL_OK) {
+***************
+*** 4304,4309 ****
+--- 4319,4327 ----
+  	if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+  	    return TCL_ERROR;
+  	}
++ 	if (markerPtr->drawUnder != under) {
++ 	    graphPtr->flags |= REDRAW_BACKING_STORE;
++ 	}
+      }
+      return TCL_OK;
+  }
+***************
+*** 4942,4948 ****
+--- 4960,4973 ----
+      for (linkPtr = Blt_ChainLastLink(graphPtr->markers.displayList);
+  	linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) {
+  	markerPtr = Blt_ChainGetValue(linkPtr);
++ 	/* 
++ 	 * Don't consider markers that are pending to be mapped. Even
++ 	 * if the marker has already been mapped, the coordinates
++ 	 * could be invalid now.  Better to pick no marker than the
++ 	 * wrong marker.
++ 	 */
+  	if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) && 
++ 	    ((markerPtr->flags & MAP_ITEM) == 0) && 
+  	    (!markerPtr->hidden) && (markerPtr->state == STATE_NORMAL)) {
+  	    if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
+  		return markerPtr;
+Only in blt2.4z-new/src: bltHash.h
+diff -cr blt2.4z/src/bltInit.c blt2.4z-new/src/bltInit.c
+*** blt2.4z/src/bltInit.c	2002-09-10 00:12:33.000000000 -0500
+--- blt2.4z-new/src/bltInit.c	2002-12-02 22:25:33.000000000 -0600
+***************
+*** 38,54 ****
+  #endif
+  #endif
+  
+  double bltNaN;
+  #if (TCL_MAJOR_VERSION > 7)
+  Tcl_Obj *bltEmptyStringObjPtr;
+  #endif
+  
+  static Tcl_MathProc MinMathProc, MaxMathProc;
+- static int tclLoaded = FALSE;
+- #ifndef TCL_ONLY
+- static int tkLoaded = FALSE;
+- #endif
+- 
+  static char libPath[1024] =
+  {
+      BLT_LIBRARY
+--- 38,53 ----
+  #endif
+  #endif
+  
++ #define BLT_THREAD_KEY		"BLT Initialized"
++ #define BLT_TCL_CMDS		(1<<0)
++ #define BLT_TK_CMDS		(1<<1)
++ 
+  double bltNaN;
+  #if (TCL_MAJOR_VERSION > 7)
+  Tcl_Obj *bltEmptyStringObjPtr;
+  #endif
+  
+  static Tcl_MathProc MinMathProc, MaxMathProc;
+  static char libPath[1024] =
+  {
+      BLT_LIBRARY
+***************
+*** 404,410 ****
+  Blt_Init(interp)
+      Tcl_Interp *interp;		/* Interpreter to add extra commands */
+  {
+!     if (!tclLoaded) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_Namespace *nsPtr;
+  	Tcl_ValueType args[2];
+--- 403,412 ----
+  Blt_Init(interp)
+      Tcl_Interp *interp;		/* Interpreter to add extra commands */
+  {
+!     int flags;
+! 
+!     flags = (int)Tcl_GetAssocData(interp, BLT_THREAD_KEY, NULL);
+!     if ((flags & BLT_TCL_CMDS) == 0) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_Namespace *nsPtr;
+  	Tcl_ValueType args[2];
+***************
+*** 451,460 ****
+  	if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+  	    return TCL_ERROR;
+  	}
+! 	tclLoaded = TRUE;
+      }
+  #ifndef TCL_ONLY
+!     if (!tkLoaded) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_Namespace *nsPtr;
+  
+--- 453,463 ----
+  	if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+  	    return TCL_ERROR;
+  	}
+! 	Tcl_SetAssocData(interp, BLT_THREAD_KEY, NULL, 
+! 		(ClientData)(flags | BLT_TCL_CMDS));
+      }
+  #ifndef TCL_ONLY
+!     if ((flags & BLT_TK_CMDS) == 0) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_Namespace *nsPtr;
+  
+***************
+*** 486,492 ****
+  	    }
+  	}
+  	Blt_InitEpsCanvasItem(interp);
+! 	tkLoaded = TRUE;
+      }
+  #endif
+      return TCL_OK;
+--- 489,496 ----
+  	    }
+  	}
+  	Blt_InitEpsCanvasItem(interp);
+! 	Tcl_SetAssocData(interp, BLT_THREAD_KEY, NULL, 
+! 		(ClientData)(flags | BLT_TK_CMDS));
+      }
+  #endif
+      return TCL_OK;
+***************
+*** 499,505 ****
+  Blt_Init(interp)
+      Tcl_Interp *interp;		/* Interpreter to add extra commands */
+  {
+!     if (!tclLoaded) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_ValueType args[2];
+  
+--- 503,512 ----
+  Blt_Init(interp)
+      Tcl_Interp *interp;		/* Interpreter to add extra commands */
+  {
+!     int flags;
+! 
+!     flags = (int)Tcl_GetAssocData(interp, BLT_THREAD_KEY, NULL);
+!     if ((flags & BLT_TCL_CMDS) == 0) {
+  	register Tcl_AppInitProc **p;
+  	Tcl_ValueType args[2];
+  
+***************
+*** 537,546 ****
+  	if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+  	    return TCL_ERROR;
+  	}
+! 	tclLoaded = TRUE;
+      }
+  #ifndef TCL_ONLY
+!     if (!tkLoaded) {
+  	register Tcl_AppInitProc **p;
+  
+  #if (TCL_VERSION_NUMBER >= _VERSION(8,1,0)) 
+--- 544,554 ----
+  	if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) {
+  	    return TCL_ERROR;
+  	}
+! 	Tcl_SetAssocData(interp, BLT_THREAD_KEY, NULL, 
+! 		(ClientData)(flags | BLT_TCL_CMDS));
+      }
+  #ifndef TCL_ONLY
+!     if ((flags & BLT_TK_CMDS) == 0) {
+  	register Tcl_AppInitProc **p;
+  
+  #if (TCL_VERSION_NUMBER >= _VERSION(8,1,0)) 
+***************
+*** 560,566 ****
+  	    }
+  	}
+  	Blt_InitEpsCanvasItem(interp);
+! 	tkLoaded = TRUE;
+      }
+  #endif
+      return TCL_OK;
+--- 568,575 ----
+  	    }
+  	}
+  	Blt_InitEpsCanvasItem(interp);
+! 	Tcl_SetAssocData(interp, BLT_THREAD_KEY, NULL, 
+! 		(ClientData)(flags | BLT_TK_CMDS));
+      }
+  #endif
+      return TCL_OK;
+diff -cr blt2.4z/src/bltTreeView.c blt2.4z-new/src/bltTreeView.c
+*** blt2.4z/src/bltTreeView.c	2002-08-15 23:15:04.000000000 -0500
+--- blt2.4z-new/src/bltTreeView.c	2003-03-04 11:15:34.000000000 -0600
+***************
+*** 3866,3871 ****
+--- 3866,3872 ----
+  	assert(tvPtr->visibleArr);
+      }
+      tvPtr->nVisible = 0;
++     tvPtr->visibleArr[0] = NULL;
+  
+      if (tvPtr->rootPtr->flags & ENTRY_HIDDEN) {
+  	return TCL_OK;		/* Root node is hidden. */
+***************
+*** 4631,4636 ****
+--- 4632,4640 ----
+      int width;
+      int x0, cx, xOffset;
+  
++     if (tvPtr->titleHeight < 1) {
++ 	return;
++     }
+      columnWidth = columnPtr->width;
+      cx = x;
+      if (columnPtr->position == Blt_ChainGetLength(tvPtr->colChainPtr)) {
+Only in blt2.4z/src: pure_api.c
Index: trunk/kitgen/config.sh
===================================================================
--- trunk/kitgen/config.sh	(revision 175)
+++ trunk/kitgen/config.sh	(revision 175)
@@ -0,0 +1,197 @@
+#set -x
+
+args="$*"
+
+verbose=0; case $1 in -v) verbose=1; shift ;; esac
+
+root=`dirname $1`
+base=`basename $1`
+shift
+
+case $root in .) root=8.4;; esac
+path=$root/$base
+  
+if test ! -d $root
+  then echo "error: directory '$root' does not exist"; exit 1; fi
+
+for v in allenc allmsgs aqua b64 cli dyn gui ppc \
+          gcov gprof sym thread tzdata univ x86
+  do eval $v=0; done
+
+while test $# != 0
+  do eval $1=1; shift; done
+
+#for v in thread allenc allmsgs tzdata cli dyn gui aqua x86 ppc univ
+#  do eval val=$`echo $v`; echo $v = "$val"; done
+
+make=$path/Makefile
+mach=`uname`
+plat=unix
+
+echo "Configuring $make for $mach."
+mkdir -p $path
+
+case $cli-$dyn-$gui in 0-0-0) cli=1 dyn=1 gui=1 ;; esac
+
+( echo "# Generated `date`:"
+  echo "#   `basename $0` $args"
+  echo
+  
+  case $mach in
+  
+    Darwin)
+      case $aqua in
+        1) echo "GUI_OPTS   = -framework Carbon -framework IOKit" ;;
+        *) echo "GUI_OPTS   = -L/usr/X11R6/lib -lX11 -weak-lXss -lXext" ;;
+      esac
+      
+      echo "LDFLAGS    = -framework CoreFoundation"
+      echo "LDSTRIP    = -x"
+      
+      case $b64-$univ-$ppc-$x86 in
+        0-0-0-0) ;;
+        0-0-1-0) echo "CFLAGS    += -arch ppc" ;;
+        0-0-0-1) echo "CFLAGS    += -arch x86" ;;
+        0-?-?-?) echo "CFLAGS    += -arch ppc -arch i386" ;;
+        1-0-1-0) echo "CFLAGS    += -arch ppc64" ;;
+        1-0-0-1) echo "CFLAGS    += -arch x86_64" ;;
+        1-?-?-?) echo "CFLAGS    += -arch ppc64 -arch x86_64" ;;
+      esac
+      echo "CFLAGS    += -isysroot /Developer/SDKs/MacOSX10.4u.sdk" \
+                          "-mmacosx-version-min=10.4"
+      
+      case $aqua in 1)
+        echo "TK_OPTS    = --enable-aqua"
+        echo "TKDYN_OPTS = --enable-aqua" ;;
+      esac
+      ;;
+      
+    Linux)
+      echo "LDFLAGS    = -ldl -lm"
+      echo "GUI_OPTS   = -L/usr/X11R6/lib -lX11 -lXft -lXss -lfontconfig"
+      case $b64 in 1)
+        echo "CFLAGS     += -m64" ;; 
+      esac
+      ;;
+
+    *BSD)
+      echo "CFLAGS    += -I/usr/X11R6/include"
+      echo "LDFLAGS    = -lm"
+      echo "GUI_OPTS   = -L/usr/X11R6/lib -lX11 -lXss"
+      case $b64 in 1)
+        echo "CFLAGS     += -m64" ;; 
+      esac
+      ;;
+
+    MINGW*)
+      echo 'LDFLAGS    = -lws2_32 build/lib/dde1*/tcldde1*.a build/lib/reg1*/tclreg1*.a'
+      echo 'GUI_OPTS   = -lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32'
+      echo 'GUI_OPTS  += -lole32 -loleaut32 -luuid -lwinspool'
+      echo 'GUI_OPTS  += build/tk/wish.res.o -mwindows'
+      echo 'CLIOBJ     = $(OBJ) $(OUTDIR)/tclAppInit.o $(OUTDIR)/tclkitsh.res.o'
+      echo 'DYNOBJ     = $(CLIOBJ) $(OUTDIR)/tkdyn/wish.res.o'
+      echo 'GUIOBJ     = $(OBJ) $(OUTDIR)/winMain.o $(OUTDIR)/tclkit.res.o'
+      echo 'PRIV       = install-private-headers'
+      echo 'EXE        = .exe'
+      plat=win
+      ;;
+
+    SunOS)
+      echo "LDFLAGS    = -ldl -lsocket -lnsl -lm"
+      echo "GUI_OPTS   = -lX11 -lXext"
+      ;;
+
+    *) echo "warning: no settings known for '$mach'" >&2 ;;
+  esac
+
+  echo "PLAT       = $plat"
+  case $plat in unix)
+    echo "PRIV       = install-private-headers" ;;
+  esac
+  case $b64 in 1)
+    echo "TCL_OPTS   += --enable-64bit" 
+    echo "TK_OPTS    += --enable-64bit" 
+    echo "VFS_OPTS   += --enable-64bit" 
+    echo "VLERQ_OPTS += --enable-64bit" ;; 
+  esac
+
+  #case $verbose in 1) kitopts=" -d" ;; esac
+  case $allenc  in 1) kitopts="$kitopts -e" ;; esac
+  case $allmsgs in 1) kitopts="$kitopts -m" ;; esac
+  case $tzdata  in 1) kitopts="$kitopts -z" ;; esac
+  
+  case $thread in
+    1) case $mach in Linux|SunOS)
+	       echo "LDFLAGS   += -lpthread" ;;
+       esac
+       echo "TCL_OPTS   = --enable-threads"
+       echo "KIT_OPTS   = -t$kitopts" ;;
+    0) echo "KIT_OPTS   =$kitopts" ;;
+  esac
+  
+  case $tzdata in 1) echo "TCL_OPTS  += --with-tzdata" ;; esac
+
+  case $gprof in 1) 
+    echo "CFLAGS    += -pg"
+    sym=1 ;; 
+  esac
+
+  case $gcov in 1) 
+    echo "CFLAGS    += -fprofile-arcs -ftest-coverage -O0"
+    echo "LDFLAGS   += -lgcov"
+    sym=1 ;; 
+  esac
+
+  case $sym in 1)
+    echo "STRIP      = :"
+    echo
+    echo "TCL_OPTS       += --enable-symbols"
+    echo "THREADDYN_OPTS += --enable-symbols"
+    echo "TK_OPTS        += --enable-symbols"
+    echo "TKDYN_OPTS     += --enable-symbols"
+    echo "VFS_OPTS       += --enable-symbols"
+    echo "VLERQ_OPTS     += --enable-symbols"
+    echo ;;
+  esac
+  
+  case $cli in 1) targets="$targets tclkit-cli" ;; esac
+  case $dyn in 1) targets="$targets tclkit-dyn" ;; esac
+  case $gui in 1) targets="$targets tclkit-gui" ;; esac
+
+  case $thread in
+    1) echo "all: threaded$targets" ;;
+    0) echo "all:$targets" ;;
+  esac
+
+  case $mach in MINGW*)
+    echo
+    echo "tclkit-cli: tclkit-cli.exe"
+    echo "tclkit-dyn: tclkit-dyn.exe"
+    echo "tclkit-gui: tclkit-gui.exe"
+  esac
+  
+  echo
+  echo "include ../../makefile.include"
+  
+) >$make
+
+case $verbose in 1)
+  echo
+  echo "Contents of $make:"
+  echo "======================================================================="
+  cat $make
+  echo "======================================================================="
+  echo
+  echo "To build, run these commands:"
+  echo "    cd $path"
+  echo "    make"
+  echo
+  echo "This produces the following executable(s):"
+  case $cli in 1) echo "    $path/tclkit-cli   (command-line)" ;; esac
+  case $dyn in 1) echo "    $path/tclkit-dyn   (Tk as shared lib)" ;; esac
+  case $gui in 1) echo "    $path/tclkit-gui   (Tk linked statically)" ;; esac
+  echo
+  echo "To remove all intermediate builds, use 'make clean'."
+  echo "To remove all executables as well, use 'make distclean'."
+  echo
+esac
Index: trunk/kitgen/csr_test.c
===================================================================
--- trunk/kitgen/csr_test.c	(revision 175)
+++ trunk/kitgen/csr_test.c	(revision 175)
@@ -0,0 +1,688 @@
+#include <string.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <stdio.h>
+#include <math.h>
+#include <tcl.h>
+
+/* ----------------------------------------------------------------- */
+/*  Peak detection using undecimated wavelet transform (UWT)         */
+/* ----------------------------------------------------------------- */
+
+/* Memory requirements */
+/* tapped delay line: 24 registers */
+/* noise buffer: 16 registers */
+
+#define UWT_ZERO 1000
+#define UWT_LEVEL 3
+
+#define UWT_NOISE_BUFFER_SIZE 16
+#define UWT_NOISE_BUFFER_SIZE_LOG2 4
+
+#define UWT_TYPICAL_START_TIME 16
+#define UWT_TYPICAL_NOISE_TIME 16
+#define UWT_MAX_RISE_TIME 40
+#define UWT_MIN_PEAK_DIST 60
+
+#define UWT_SIGNAL_THRESHOLD 10
+
+static void uwt_bior31_step(int input, int *tap,
+  int *d_reg, int *a_reg, int *peak_reg, int *tmp1_reg, int *tmp2_reg, int *flag1_reg, int *flag2_reg, int *less_reg, int *more_reg,
+  int *d, int *a, int *peak, int *is_min, int *is_max, int level)
+{
+    int i;
+  	int	index1		  =	1 << (level - 1);
+  	int	index2		  =	2 << (level - 1);
+  	int	index3		  =	3 << (level - 1);
+  	int	peak_index	=	((3 << (level - 1)) + 1) >> 1;
+  	int	peak_shift	=	((level - 1) << 1) + (level - 1);
+
+    int d_next, a_next, peak_next;
+    int tmp1_next, tmp2_next, less_next, more_next;
+
+
+    // Tapped delay line: shift one
+    for(i = index3; i > 0; --i)
+    {
+        tap[i] = tap[i-1];
+    }
+
+    // Input in register 0
+    tap[0] = input;
+
+      *d		=	*d_reg;
+      *a		=	*a_reg;
+      *peak	=	*peak_reg;
+      *is_min	=	*flag2_reg;
+      *is_max	=	*flag1_reg;
+
+  		tmp1_next = tap[index3] + (tap[index2] << 1) + tap[index2];
+  		tmp2_next = (tap[index1] << 1) + tap[index1] + tap[0];
+
+  		d_next = UWT_ZERO - *tmp1_reg + *tmp2_reg;
+  		a_next = *tmp1_reg + *tmp2_reg;
+
+  		more_next = (*d_reg > UWT_ZERO);
+  		less_next = (*d_reg < UWT_ZERO);
+
+  		peak_next = (tap[peak_index + 1] >> peak_shift);
+
+			*flag2_reg = (*more_reg) && (!more_next);
+			*flag1_reg = (*less_reg) && (!less_next);
+
+     	*d_reg = d_next;
+			*a_reg = a_next;
+			*peak_reg = peak_next;
+
+			*tmp1_reg = tmp1_next;
+			*tmp2_reg = tmp2_next;
+			*less_reg = less_next;
+			*more_reg = more_next;
+
+
+/*
+    *d = UWT_ZERO - (tap[index3])
+       - (tap[index2] << 1) - tap[index2]
+       + (tap[index1] << 1) + tap[index1]
+       + (tap[0]);
+
+    *a = (tap[index3])
+       + (tap[index2] << 1) + tap[index2]
+       + (tap[index1] << 1) + tap[index1]
+       + (tap[0]);
+
+    *peak = (tap[peak_index] >> peak_shift);
+
+    *is_min = (*d_reg < UWT_ZERO) && (*d >= UWT_ZERO) ? 1 : 0;
+    *is_max = (*d_reg > UWT_ZERO) && (*d <= UWT_ZERO) ? 1 : 0;
+*/
+/*
+    if(((*is_min) || (*is_max)) && (level == 3)) printf("peak -> %d -> %d -> %d\n", (*peak), (*d_reg), (*d));
+*/
+/*
+    *d_reg = *d;
+*/
+}
+
+/* ----------------------------------------------------------------- */
+
+static void uwt_process(int *input, int *noise, int *peak, int *is_min, int *is_max, int *is_valid, int n)
+{
+    int i, j, k;
+    int sample;
+
+    int mode = 0;
+
+    int time_from_start = 0;
+    int time_from_min = 0;
+    int time_from_max = 0;
+
+    int noise_average = 0;
+
+    int noise_buffer[UWT_NOISE_BUFFER_SIZE];
+
+    int pulse_height = 0;
+
+    int size;
+  	int d_reg[UWT_LEVEL];
+  	int a_reg[UWT_LEVEL], peak_reg[UWT_LEVEL];
+    int tmp1_reg[UWT_LEVEL], tmp2_reg[UWT_LEVEL];
+    int flag1_reg[UWT_LEVEL], flag2_reg[UWT_LEVEL];
+    int less_reg[UWT_LEVEL], more_reg[UWT_LEVEL];
+  	int a, d;
+  	int *tap[UWT_LEVEL];
+
+    for(j = 0; j < UWT_NOISE_BUFFER_SIZE; ++j)
+    {
+        noise_buffer[j] = 0;
+    }
+
+  	// Tapped delay line
+    for(j = 0; j < UWT_LEVEL; ++j)
+    {
+        size = ((3 << j) + 1)*sizeof(int);
+        printf("%d -> %d\n", j+1, ((3 << j) + 1));
+        fflush(stdout);
+       	tap[j] = (int *) malloc(size);
+      	memset(tap[j], 0, size);
+      	d_reg[j] = UWT_ZERO;
+      	a_reg[j] = UWT_ZERO;
+      	peak_reg[j] = UWT_ZERO;
+      	tmp1_reg[j] = UWT_ZERO;
+      	tmp2_reg[j] = UWT_ZERO;
+      	flag1_reg[j] = UWT_ZERO;
+      	flag2_reg[j] = UWT_ZERO;
+      	less_reg[j] = UWT_ZERO;
+      	more_reg[j] = UWT_ZERO;
+    }
+
+    for(i = 0; i < n; ++i)
+    {
+        sample = input[i];
+        is_valid[i] = 0;
+
+        for(j = 0; j < UWT_LEVEL; ++j)
+        {
+            uwt_bior31_step(sample, tap[j],
+              d_reg+j, a_reg+j, peak_reg+j, tmp1_reg+j, tmp2_reg+j, flag1_reg+j, flag2_reg+j, less_reg+j, more_reg+j,
+              &d, &a, peak+i, is_min+i, is_max+i, j+1);
+            sample = a;
+        }
+        
+        if(is_min[i] && mode > 0)
+        {
+            for(j = 0; j < UWT_NOISE_BUFFER_SIZE - 1; ++j)
+            {
+                noise_buffer[j] = noise_buffer[j+1] + peak[i];
+            }
+            noise_buffer[UWT_NOISE_BUFFER_SIZE-1] = peak[i];
+
+            noise_average = noise_buffer[0] >> UWT_NOISE_BUFFER_SIZE_LOG2;
+
+            time_from_min = 0;
+        }
+
+        noise[i] = noise_average;
+
+        switch(mode)
+        {
+            case 0:
+                ++time_from_start;
+
+                if(time_from_start >= UWT_TYPICAL_START_TIME)
+                {
+                    mode = 1;
+                    time_from_start = 0;
+                }
+                break;
+
+            case 1:
+                if(is_min[i]) ++time_from_start;
+
+                if(time_from_start >= UWT_TYPICAL_NOISE_TIME)
+                {
+                    mode = 2;
+                    time_from_start = 0;
+                }
+                break;
+
+            case 2:
+                if(is_max[i])
+                {
+                    pulse_height = peak[i] - noise_average;
+
+                    if(pulse_height > UWT_SIGNAL_THRESHOLD)
+                    {
+                        printf("%d -> %d -> %d\n", pulse_height, time_from_min, time_from_max);
+                        if(time_from_min < UWT_MAX_RISE_TIME &&
+                           time_from_max > UWT_MIN_PEAK_DIST
+                           )
+                        {
+                            is_valid[i] = 1;
+                        }
+                        time_from_max = 0;
+                    }
+                }
+
+                ++time_from_min;
+                ++time_from_max;
+
+                break;
+
+             default:
+                /* Do nothing */
+                break;
+        }
+    }
+
+    for(j = 0; j < UWT_LEVEL; ++j)
+    {   
+        free(tap[j]);
+    }
+
+}
+
+/* ----------------------------------------------------------------- */
+/*  Peak detection using "snake" algorithm                           */
+/* ----------------------------------------------------------------- */
+
+/* Memory requirements */
+/* noise buffer: 32 registers */
+/* peak buffer: 7 registers */
+
+static void sum8(int *input, int *output, int n)
+{
+    int i, j;
+
+  	// Tapped delay line
+  	int *tap = (int *) malloc(8*sizeof(int));
+  	memset(tap, 0, 8*sizeof(int));
+
+    for(i = 0; i < n; ++i)
+    {
+        // Tapped delay line: shift one
+  			for(j = 7; j > 0; --j)
+  			{
+  				tap[j] = tap[j-1];
+  			}
+
+  			// Input in register 0
+  			tap[0] = input[i];
+
+        output[i] = 0;
+
+        for(j = 0; j < 8; ++j)
+        {
+          output[i] += tap[j];
+        }
+
+        output[i] = output[i] >> 3;
+
+    }
+
+    free(tap);
+}
+
+/* ----------------------------------------------------------------- */
+
+#define PEAK_BUFFER_SIZE 7
+#define NOISE_BUFFER_SIZE 32
+#define NOISE_BUFFER_SIZE_LOG2 5
+
+#define TYPICAL_START_TIME 64
+#define TYPICAL_RISE_TIME 12
+#define TYPICAL_PILEUP_TIME 20
+
+#define SIGNAL_THRESHOLD 10
+
+static void csr_process(int *input, int *noise, int *peak, int *is_max, int n)
+{
+    int i, j, k;
+    int sample;
+
+
+    int mode = 0;
+    int min_sample = 4095;
+
+    int time_from_start = 0;
+    int time_from_min = 0;
+    int time_from_peak = 0;
+
+    int noise_average = 0;
+    int min_noise_average = 4095;
+
+    int noise_buffer[NOISE_BUFFER_SIZE];
+    int peak_buffer[PEAK_BUFFER_SIZE];
+
+    int peak_value = 0;
+    int pulse_height = 0;
+    
+    int is_raising, is_stable, is_stored, is_valid, is_pileup;
+
+    for(j = 0; j < NOISE_BUFFER_SIZE; ++j)
+    {
+        noise_buffer[j] = 4095;
+    }
+
+    for(i = 0; i < n; ++i)
+    {
+        sample = input[i];
+        noise[i] = 0;
+        peak[i] = 0;
+        is_max[i] = 0;
+
+        if(mode == 0 || mode == 1)
+        {
+            for(j = 0; j < NOISE_BUFFER_SIZE - 1; ++j)
+            {
+                noise_buffer[j] = noise_buffer[j+1];
+            }
+            noise_buffer[NOISE_BUFFER_SIZE-1] = sample;
+
+            noise_average = 0;
+            for(j = 0; j < NOISE_BUFFER_SIZE; ++j)
+            {
+                noise_average += noise_buffer[j];
+            }
+            noise_average = noise_average >> NOISE_BUFFER_SIZE_LOG2;
+        }
+
+        noise[i] = noise_average;
+
+        for(j = 0; j < PEAK_BUFFER_SIZE - 1; ++j)
+        {
+            peak_buffer[j] = peak_buffer[j+1];
+        }
+        peak_buffer[PEAK_BUFFER_SIZE-1] = sample;
+
+        switch(mode)
+        {
+            case 0:
+                if(noise_average < min_noise_average)
+                {
+                    min_noise_average = noise_average;
+                }
+
+                ++time_from_start;
+
+                if(time_from_start >= TYPICAL_START_TIME)
+                {
+                    mode = 1;
+                    noise_average = min_noise_average;
+                    for(j = 0; j < NOISE_BUFFER_SIZE; ++j)
+                    {
+                        noise_buffer[j] = min_noise_average;
+                    }
+                }
+                break;
+
+            case 1:
+                is_raising = 0;
+                is_stable = 0;
+                if(sample >= min_sample)
+                {
+                    is_stable = 1;
+                }
+                else
+                {
+                    min_sample = sample;
+                }
+                if(peak_buffer[0] < peak_buffer[3]) is_raising = 1;
+                if(is_stable && is_raising)
+                {
+                   ++time_from_min;
+                }
+                else
+                {
+                   time_from_min = 0;
+                   if(!is_raising) min_sample = peak_buffer[3];
+                }
+                if(time_from_min >= TYPICAL_RISE_TIME)
+                {
+                   mode = 2;
+                   is_stored = 0;
+                   is_valid = 0;
+                }
+                break;
+
+            case 2:
+                if(!is_stored)
+                {
+                    if(peak_buffer[3] > peak_buffer[0] &&
+                       peak_buffer[3] > peak_buffer[6] &&
+                       peak_buffer[3] >= peak_buffer[2] &&
+                       peak_buffer[3] >= peak_buffer[4])
+                    {
+                        peak_value = peak_buffer[3];
+                        time_from_peak = 3;
+                        pulse_height = peak_value - noise_average;
+                        if(pulse_height > SIGNAL_THRESHOLD)
+                        {
+                           is_valid = 1;
+                           peak[i] = peak_value;
+                           is_max[i] = 1;
+                           is_stored = 1;
+                        }
+                        mode = 3;
+                    }
+                }
+                ++time_from_min;
+                is_pileup = 0;
+                break;
+
+            case 3:
+                ++time_from_min;
+                ++time_from_peak;
+                if(time_from_min > TYPICAL_PILEUP_TIME ||
+                   time_from_peak > TYPICAL_PILEUP_TIME)
+                {
+                    is_pileup = 1;
+                }
+                if(peak_buffer[3] < peak_buffer[0] &&
+                   peak_buffer[3] < peak_buffer[6] &&
+                   peak_buffer[3] <= peak_buffer[2] &&
+                   peak_buffer[3] <= peak_buffer[4])
+                {
+                    min_sample = peak_buffer[3];
+                    mode = 1;
+                }
+                break;
+
+            default:
+                /* Do nothing */
+                break;
+        }
+    }
+}
+
+/* ----------------------------------------------------------------- */
+/* Interface with Tcl                                                */
+/* ----------------------------------------------------------------- */
+
+static int UwtProcessObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                                int objc, Tcl_Obj *CONST objv[])
+{
+  int size = 0;
+  int i = 0;
+  int level = 0;
+  double value = 0.0;
+  int *input, *noise, *peak, *is_min, *is_max, *is_valid;
+  Tcl_Obj *result = NULL;
+  Tcl_Obj *listnoise = NULL;
+  Tcl_Obj *listpeak = NULL;
+  Tcl_Obj *listmin = NULL;
+  Tcl_Obj *listmax = NULL;
+  Tcl_Obj *listvalid = NULL;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_ListObjLength(interp, objv[1], &size))
+  {
+    return TCL_ERROR;
+  }
+
+  input = (int *) malloc(size*sizeof(int));
+  noise = (int *) malloc(size*sizeof(int));
+  peak = (int *) malloc(size*sizeof(int));
+  is_min = (int *) malloc(size*sizeof(int));
+  is_max = (int *) malloc(size*sizeof(int));
+  is_valid = (int *) malloc(size*sizeof(int));
+
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjIndex(interp, objv[1], i, &result);
+    if(TCL_OK != Tcl_GetDoubleFromObj(interp, result, &value))
+    {
+      free(input);
+      free(noise);
+      free(peak);
+      free(is_min);
+      free(is_max);
+      free(is_valid);
+      return TCL_ERROR;
+    }
+    input[i] = (int) value;
+/*
+    printf("%d -> %g\n", i, input[i]);
+*/
+  }
+
+  uwt_process(input, noise, peak, is_min, is_max, is_valid, size);
+
+  result = Tcl_GetObjResult(interp);
+  listnoise = Tcl_NewObj();
+  listpeak = Tcl_NewObj();
+  listmin = Tcl_NewObj();
+  listmax = Tcl_NewObj();
+  listvalid = Tcl_NewObj();
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjAppendElement(interp, listnoise, Tcl_NewIntObj(noise[i]));
+    Tcl_ListObjAppendElement(interp, listpeak, Tcl_NewIntObj(peak[i]));
+    Tcl_ListObjAppendElement(interp, listmin, Tcl_NewIntObj(is_min[i]*peak[i]));
+    Tcl_ListObjAppendElement(interp, listmax, Tcl_NewIntObj(is_max[i]*peak[i]));
+    Tcl_ListObjAppendElement(interp, listvalid, Tcl_NewIntObj(is_valid[i]*peak[i]));
+  }
+
+  Tcl_ListObjAppendElement(interp, result, listnoise);
+  Tcl_ListObjAppendElement(interp, result, listpeak);
+  Tcl_ListObjAppendElement(interp, result, listmin);
+  Tcl_ListObjAppendElement(interp, result, listmax);
+  Tcl_ListObjAppendElement(interp, result, listvalid);
+
+  free(input);
+  free(noise);
+  free(peak);
+  free(is_min);
+  free(is_max);
+  free(is_valid);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int Sum8ObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                          int objc, Tcl_Obj *CONST objv[])
+{
+  int size = 0;
+  int i = 0;
+  int level = 0;
+  double value = 0.0;
+  int *input, *output;
+  Tcl_Obj *result = NULL;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_ListObjLength(interp, objv[1], &size))
+  {
+    return TCL_ERROR;
+  }
+
+  input = (int *) malloc(size*sizeof(int));
+  output = (int *) malloc(size*sizeof(int));
+
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjIndex(interp, objv[1], i, &result);
+    if(TCL_OK != Tcl_GetDoubleFromObj(interp, result, &value))
+    {
+      free(input);
+      free(output);
+      return TCL_ERROR;
+    }
+    input[i] = (int) value;
+/*
+    printf("%d -> %g\n", i, input[i]);
+*/
+  }
+
+  sum8(input, output, size);
+
+  result = Tcl_GetObjResult(interp);
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(output[i]));
+/*
+    printf("%d -> %g\n", i, d[i]);
+*/
+	}
+
+  free(input);
+  free(output);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int CsrProcessObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                                int objc, Tcl_Obj *CONST objv[])
+{
+  int size = 0;
+  int i = 0;
+  int level = 0;
+  double value = 0.0;
+  int *input, *noise, *peak, *is_max;
+  Tcl_Obj *result = NULL;
+  Tcl_Obj *listnoise = NULL;
+  Tcl_Obj *listpeak = NULL;
+  Tcl_Obj *listmax = NULL;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_ListObjLength(interp, objv[1], &size))
+  {
+    return TCL_ERROR;
+  }
+
+  input = (int *) malloc(size*sizeof(int));
+  noise = (int *) malloc(size*sizeof(int));
+  peak = (int *) malloc(size*sizeof(int));
+  is_max = (int *) malloc(size*sizeof(int));
+
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjIndex(interp, objv[1], i, &result);
+    if(TCL_OK != Tcl_GetDoubleFromObj(interp, result, &value))
+    {
+      free(input);
+      free(noise);
+      free(peak);
+      free(is_max);
+      return TCL_ERROR;
+    }
+    input[i] = (int) value;
+/*
+    printf("%d -> %g\n", i, input[i]);
+*/
+  }
+
+  csr_process(input, noise, peak, is_max, size);
+
+  result = Tcl_GetObjResult(interp);
+  listnoise = Tcl_NewObj();
+  listpeak = Tcl_NewObj();
+  listmax = Tcl_NewObj();
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjAppendElement(interp, listnoise, Tcl_NewIntObj(noise[i]));
+    Tcl_ListObjAppendElement(interp, listpeak, Tcl_NewIntObj(peak[i]));
+    Tcl_ListObjAppendElement(interp, listmax, Tcl_NewIntObj(is_max[i]));
+  }
+
+  Tcl_ListObjAppendElement(interp, result, listnoise);
+  Tcl_ListObjAppendElement(interp, result, listpeak);
+  Tcl_ListObjAppendElement(interp, result, listmax);
+
+  free(input);
+  free(noise);
+  free(peak);
+  free(is_max);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+int Csr_Init(Tcl_Interp *interp)
+{
+    Tcl_CreateObjCommand(interp, "csr_uwt_process", UwtProcessObjCmdProc, 0, 0);
+    Tcl_CreateObjCommand(interp, "csr_sum8", Sum8ObjCmdProc, 0, 0);
+    Tcl_CreateObjCommand(interp, "csr_process", CsrProcessObjCmdProc, 0, 0);
+
+    return Tcl_PkgProvide(interp, "csr", "0.1");
+}
+
Index: trunk/kitgen/files/BLT2.4-pkgIndex.tcl
===================================================================
--- trunk/kitgen/files/BLT2.4-pkgIndex.tcl	(revision 175)
+++ trunk/kitgen/files/BLT2.4-pkgIndex.tcl	(revision 175)
@@ -0,0 +1,9 @@
+package ifneeded BLT 2.4 [list BLT_load $dir]
+
+proc BLT_load {dir} {
+    global blt_library
+    load "" BLT
+    source [file join $dir graph.tcl]
+    set blt_library $dir
+    rename BLT_load {}
+}
Index: trunk/kitgen/files/tdom0.8.3-pkgIndex.tcl
===================================================================
--- trunk/kitgen/files/tdom0.8.3-pkgIndex.tcl	(revision 175)
+++ trunk/kitgen/files/tdom0.8.3-pkgIndex.tcl	(revision 175)
@@ -0,0 +1,7 @@
+package ifneeded tdom 0.8.3 [list tdom_load $dir]
+
+proc tdom_load {dir} {
+    load "" tdom
+    source [file join $dir tdom.tcl]
+    rename tdom_load {}
+}
Index: trunk/kitgen/files/tk8.5-pkgIndex.tcl
===================================================================
--- trunk/kitgen/files/tk8.5-pkgIndex.tcl	(revision 175)
+++ trunk/kitgen/files/tk8.5-pkgIndex.tcl	(revision 175)
@@ -0,0 +1,8 @@
+package ifneeded Tk $::tcl_patchLevel \
+  [string map [list @@ [file join $dir .. libtk$::tcl_version[info sharedlibext]]] {
+    if {[lsearch -exact [info loaded] {{} Tk}] >= 0} {
+      load "" Tk
+    } else {
+      load @@ Tk
+    }
+  }]
Index: trunk/kitgen/files/vfs1.4.1-pkgIndex.tcl
===================================================================
--- trunk/kitgen/files/vfs1.4.1-pkgIndex.tcl	(revision 175)
+++ trunk/kitgen/files/vfs1.4.1-pkgIndex.tcl	(revision 175)
@@ -0,0 +1,5 @@
+package ifneeded vfs        1.4.1  [list load {} vfs]
+package ifneeded starkit    1.3.3  [list source [file join $dir starkit.tcl]]
+package ifneeded vfslib     1.4    [list source [file join $dir vfslib.tcl]]
+package ifneeded vfs::mk4   1.10.1 [list source [file join $dir mk4vfs.tcl]]
+package ifneeded vfs::zip   1.0.3  [list source [file join $dir zipvfs.tcl]]
Index: trunk/kitgen/g2lite.c
===================================================================
--- trunk/kitgen/g2lite.c	(revision 175)
+++ trunk/kitgen/g2lite.c	(revision 175)
@@ -0,0 +1,281 @@
+
+/*
+  Copyright (c) 2007, Pavel Demin
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms,
+  with or without modification, are permitted
+  provided that the following conditions are met:
+
+      * Redistributions of source code must retain
+        the above copyright notice, this list of conditions
+        and the following disclaimer.
+      * Redistributions in binary form must reproduce
+        the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation
+        and/or other materials provided with the distribution.
+      * Neither the name of the SRMlite nor the names of its
+        contributors may be used to endorse or promote products
+        derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <tcl.h>
+
+#define G2_POS_MAX 512
+
+enum
+{
+  G2_MODE_S    = 1,
+  G2_MODE_AT   = 2,
+  G2_MODE_VAR  = 4,
+  G2_MODE_COPY = 8,
+  G2_MODE_IGNORE_SPACES = 16
+};
+
+/* ----------------------------------------------------------------- */
+
+typedef struct
+{
+  int pos;
+  int level;
+  unsigned char mode;
+  Tcl_UniChar buffer[G2_POS_MAX + 8];
+} g2state;
+
+/* ----------------------------------------------------------------- */
+
+static int G2liteObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                            int objc, Tcl_Obj *CONST objv[])
+{
+  int size = 0;
+  int i = 0;
+  int j = 0;
+  Tcl_UniChar ch, tmp;
+  Tcl_UniChar *inbuffer = NULL;
+  Tcl_Obj *result = NULL;
+  Tcl_Obj *command_begin = NULL;
+  Tcl_Obj *command_append = NULL;
+  Tcl_Obj *command_end = NULL;
+
+  g2state g2;
+  g2.pos = 0;
+  g2.mode = G2_MODE_COPY;
+  g2.level = 0;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "string");
+		return TCL_ERROR;
+  }
+
+  inbuffer = Tcl_GetUnicodeFromObj(objv[1], &size);
+
+  result = Tcl_NewObj();
+  command_begin = Tcl_NewStringObj("variable g2result {}\n", -1);
+  command_append = Tcl_NewStringObj("\nappend g2result \"", -1);
+  command_end = Tcl_NewStringObj("\nreturn $g2result", -1);
+
+  Tcl_IncrRefCount(result);
+  Tcl_IncrRefCount(command_begin);
+  Tcl_IncrRefCount(command_append);
+  Tcl_IncrRefCount(command_end);
+
+  Tcl_AppendObjToObj(result, command_begin);
+
+  while(i < size)
+  {
+    ch = inbuffer[i++];
+
+    if(g2.pos >= G2_POS_MAX)
+    {
+      Tcl_AppendUnicodeToObj(result, g2.buffer, g2.pos);
+      g2.pos = 0;
+    }
+
+    /* if current symbol is @ */
+    if('@' == ch)
+    {
+      /* if previous symbol was @ => switch output mode */
+      if(g2.mode & G2_MODE_AT)
+      {
+        if(g2.mode & G2_MODE_COPY)
+        {
+          Tcl_AppendUnicodeToObj(result, g2.buffer, g2.pos);
+          Tcl_AppendObjToObj(result, command_append);
+          g2.pos = 0;
+        }
+        else
+        {
+          g2.buffer[g2.pos++] = '\"';
+          g2.buffer[g2.pos++] = '\n';
+        }
+
+        g2.mode ^= G2_MODE_COPY;
+        g2.mode |= G2_MODE_IGNORE_SPACES;
+      }
+
+      g2.mode ^= G2_MODE_AT;
+      continue;
+    }
+
+    /* current symbol is not @ */
+
+    if(g2.mode & G2_MODE_IGNORE_SPACES)
+    {
+      g2.mode ^= G2_MODE_IGNORE_SPACES;
+
+      /* if spaces => skip all spaces and new line character */
+      tmp = ch;
+      j = i;
+      while(j < size && Tcl_UniCharIsSpace(tmp) && '\n' != tmp)
+      {
+        tmp = inbuffer[j++];
+      }
+      if('\n' == tmp)
+      {
+        i = j;
+        continue;
+      }
+    }
+
+    /* if previous symbol was single @ => output @ */
+    if(g2.mode & G2_MODE_AT)
+    {
+      g2.mode ^= G2_MODE_AT;
+      g2.buffer[g2.pos++] = '@';
+    }
+
+    /* if output mode is COPY => output current symbol */
+    if(g2.mode & G2_MODE_COPY)
+    {
+      g2.buffer[g2.pos++] = ch;
+      continue;
+    }
+
+    /* output mode is not COPY *
+
+    /* if current symbol is $ */
+    if('$' == ch)
+    {
+      /* if previous symbol was $ => switch to variable substitute mode*/
+      if(g2.mode & G2_MODE_S)
+      {
+        g2.mode |= G2_MODE_VAR;
+        g2.level = 0;
+
+        g2.buffer[g2.pos++] = '$';
+      }
+
+      g2.mode ^= G2_MODE_S;
+      continue;
+    }
+
+    /* current symbol is not $ */
+
+    /* if previous symbol was single $ => output \$ */
+    if(g2.mode & G2_MODE_S)
+    {
+      g2.buffer[g2.pos++] = '\\';
+      g2.buffer[g2.pos++] = '$';
+      g2.mode ^= G2_MODE_S;
+    }
+
+    /* if in variable substitute mode => output current symbol, count { and } */
+    if(g2.mode & G2_MODE_VAR)
+    {
+      g2.buffer[g2.pos++] = ch;
+
+      /* if current symbol is { => stay in variable substitute mode */
+      if('{' == ch)
+      {
+        g2.level++;
+      }
+      else if('}' == ch)
+      {
+        g2.level--;
+      }
+
+      if(0 == g2.level)
+      {
+        g2.mode ^= G2_MODE_VAR;
+      }
+
+      continue;
+    }
+
+    /* not in variable substitute mode */
+
+    switch(ch)
+    {
+      case '\\':
+      case '{':
+      case '}':
+      case '[':
+      case ']':
+      case '\'':
+      case '"':
+        g2.buffer[g2.pos++] = '\\';
+        break;
+    }
+
+    g2.buffer[g2.pos++] = ch;
+  }
+
+  /* if last symbol was single @ => output @ */
+  if(g2.mode & G2_MODE_AT)
+  {
+    g2.buffer[g2.pos++] = '@';
+  }
+
+  /* if last symbol was single $ => output \$ */
+  if(g2.mode & G2_MODE_S)
+  {
+    g2.buffer[g2.pos++] = '\\';
+    g2.buffer[g2.pos++] = '$';
+  }
+
+  /* if output mode is not COPY => switch output mode */
+  if(!(g2.mode & G2_MODE_COPY))
+  {
+    g2.buffer[g2.pos++] = '\"';
+    g2.buffer[g2.pos++] = '\n';
+  }
+
+  if(g2.pos > 0)
+  {
+    Tcl_AppendUnicodeToObj(result, g2.buffer, g2.pos);
+    g2.pos = 0;
+  }
+
+  Tcl_AppendObjToObj(result, command_end);
+
+  Tcl_SetObjResult(interp, result);
+
+  Tcl_DecrRefCount(command_end);
+  Tcl_DecrRefCount(command_append);
+  Tcl_DecrRefCount(command_begin);
+  Tcl_DecrRefCount(result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+int G2lite_Init(Tcl_Interp *interp)
+{
+    Tcl_CreateObjCommand(interp, "g2lite", G2liteObjCmdProc, 0, 0);
+    return Tcl_PkgProvide(interp, "g2lite", "0.1");
+}
Index: trunk/kitgen/globus-export_sec_context.patch
===================================================================
--- trunk/kitgen/globus-export_sec_context.patch	(revision 175)
+++ trunk/kitgen/globus-export_sec_context.patch	(revision 175)
@@ -0,0 +1,19 @@
+--- globus/source-trees/gsi/gssapi/source/library/export_sec_context.c	2006-01-19 06:56:09.000000000 +0100
++++ globus-patched/source-trees/gsi/gssapi/source/library/export_sec_context.c	2009-02-24 18:10:28.000000000 +0100
+@@ -247,6 +247,7 @@ GSS_CALLCONV gss_export_sec_context(
+     globus_mutex_unlock(&context->mutex);
+     
+     /* Now delete the GSS context as per RFC */
++/*
+     major_status = gss_delete_sec_context(&local_minor_status,
+                                           context_handle_P,
+                                           GSS_C_NO_BUFFER);
+@@ -257,7 +258,7 @@ GSS_CALLCONV gss_export_sec_context(
+             GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT);
+         goto exit;
+     }
+-
++*/
+     goto exit;
+ 
+  unlock_mutex:
Index: trunk/kitgen/kitInit.c
===================================================================
--- trunk/kitgen/kitInit.c	(revision 175)
+++ trunk/kitgen/kitInit.c	(revision 175)
@@ -0,0 +1,209 @@
+/*
+ * tclAppInit.c --
+ *
+ *  Provides a default version of the main program and Tcl_AppInit
+ *  procedure for Tcl applications (without Tk).  Note that this
+ *  program must be built in Win32 console mode to work properly.
+ *
+ * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
+ * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2000-2006 Jean-Claude Wippler <jcw@equi4.com>
+ * Copyright (c) 2003-2006 ActiveState Software Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: kitInit.c,v 1.3 2008/05/11 20:36:50 demin Exp $
+ */
+
+#ifdef KIT_INCLUDES_TK
+#include <tk.h>
+#else
+#include <tcl.h>
+#endif
+
+#include <string.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+/* defined in tclInt.h */
+extern Tcl_Obj* TclGetStartupScriptPath();
+extern void TclSetStartupScriptPath(Tcl_Obj*);
+
+Tcl_AppInitProc	Vfs_Init, Zlib_Init;
+#ifdef TCL_THREADS
+Tcl_AppInitProc	Thread_Init;
+#endif
+#ifdef _WIN32
+Tcl_AppInitProc	Dde_Init, Registry_Init;
+#endif
+#ifdef KIT_INCLUDES_TK
+Tcl_AppInitProc	Blt_Init, Blt_SafeInit;
+#endif
+
+Tcl_AppInitProc	Tdom_Init, Tdom_SafeInit;
+Tcl_AppInitProc G2lite_Init;
+Tcl_AppInitProc Usb_Init;
+Tcl_AppInitProc Swt_Init;
+Tcl_AppInitProc Csr_Init;
+Tcl_AppInitProc Xotcl_Init;
+Tcl_AppInitProc Sqlite3_Init;
+
+#ifdef WIN32
+#define DEV_NULL "NUL"
+#else
+#define DEV_NULL "/dev/null"
+#endif
+
+static void TclKit_InitStdChannels(void);
+
+static char appInitCmd[] =
+"proc tclKitInit {} {\n"
+    "rename tclKitInit {}\n"
+    "if {![info exists ::tcl::basekit]} {\n"
+        "namespace eval ::tcl { variable basekit [info nameofexecutable] }\n"
+    "}\n"
+    "if {[file isfile /zvfs/boot.tcl]} {\n"
+        "source /zvfs/boot.tcl\n"
+    "} elseif {[lindex $::argv 0] eq \"-init-\"} {\n"
+        "uplevel #0 { source [lindex $::argv 1] }\n"
+        "exit\n"
+    "} else {\n"
+        "error \"\n  $::tcl::basekit has no VFS data to start up\"\n"
+    "}\n"
+"}\n"
+"tclKitInit"
+;
+
+static const char initScript[] =
+"if {[file isfile [file join /zvfs main.tcl]]} {\n"
+    "if {[info commands console] != {}} { console hide }\n"
+    "set tcl_interactive 0\n"
+    "incr argc\n"
+    "set argv [linsert $argv 0 $argv0]\n"
+    "set argv0 [file join /zvfs main.tcl]\n"
+"} else continue\n"
+;
+
+#ifdef WIN32
+__declspec(dllexport) int
+#else
+extern int
+#endif
+TclKit_AppInit(Tcl_Interp *interp)
+{
+    /*
+     * Ensure that std channels exist (creating them if necessary)
+     */
+    TclKit_InitStdChannels();
+
+    Tcl_StaticPackage(0, "vfs", Vfs_Init, NULL);
+    Tcl_StaticPackage(0, "zlib", Zlib_Init, NULL);
+#ifdef TCL_THREADS
+    Tcl_StaticPackage(0, "Thread", Thread_Init, NULL);
+#endif
+#ifdef _WIN32
+    Tcl_StaticPackage(0, "dde", Dde_Init, NULL);
+    Tcl_StaticPackage(0, "registry", Registry_Init, NULL);
+#endif
+#ifdef KIT_INCLUDES_TK
+    Tcl_StaticPackage(0, "Tk", Tk_Init, Tk_SafeInit);
+    Tcl_StaticPackage(0, "Blt", Blt_Init, Blt_SafeInit);
+#endif
+
+    Tcl_StaticPackage(0, "XOTcl", Xotcl_Init, NULL);
+    Tcl_StaticPackage(0, "g2lite", G2lite_Init, NULL);
+    Tcl_StaticPackage(0, "usb", Usb_Init, NULL);
+    Tcl_StaticPackage(0, "swt", Swt_Init, NULL);
+    Tcl_StaticPackage(0, "csr", Csr_Init, NULL);
+    Tcl_StaticPackage(0, "sqlite3", Sqlite3_Init, NULL);
+    Tcl_StaticPackage(0, "tdom", Tdom_Init, Tdom_SafeInit);
+
+    /* the tcl_rcFileName variable only exists in the initial interpreter */
+#ifdef _WIN32
+    Tcl_SetVar(interp, "tcl_rcFileName", "~/tclkitrc.tcl", TCL_GLOBAL_ONLY);
+#else
+    Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclkitrc", TCL_GLOBAL_ONLY);
+#endif
+
+    Zvfs_Init(interp);
+    Tcl_SetVar(interp, "extname", "", TCL_GLOBAL_ONLY);
+    Zvfs_Mount(interp, (char *)Tcl_GetNameOfExecutable(), "/zvfs");
+    Tcl_SetVar2(interp, "env", "TCL_LIBRARY", "/zvfs/lib/tcl", TCL_GLOBAL_ONLY);
+    Tcl_SetVar2(interp, "env", "TK_LIBRARY", "/zvfs/lib/tk", TCL_GLOBAL_ONLY);
+
+    if ((Tcl_EvalEx(interp, appInitCmd, -1, TCL_EVAL_GLOBAL) == TCL_ERROR)
+	    || (Tcl_Init(interp) == TCL_ERROR))
+        goto error;
+
+#ifdef KIT_INCLUDES_TK
+    if (Tk_Init(interp) == TCL_ERROR)
+        goto error;
+#ifdef _WIN32
+    if (Tk_CreateConsoleWindow(interp) == TCL_ERROR)
+        goto error;
+#endif
+#endif
+
+    /* messy because TclSetStartupScriptPath is called slightly too late */
+    if (Tcl_Eval(interp, initScript) == TCL_OK) {
+        Tcl_Obj* path = TclGetStartupScriptPath();
+      	TclSetStartupScriptPath(Tcl_GetObjResult(interp));
+      	if (path == NULL)
+        	  Tcl_Eval(interp, "incr argc -1; set argv [lrange $argv 1 end]");
+    }
+
+    Tcl_SetVar(interp, "errorInfo", "", TCL_GLOBAL_ONLY);
+    Tcl_ResetResult(interp);
+    return TCL_OK;
+
+error:
+#if defined(KIT_INCLUDES_TK) && defined(_WIN32)
+    MessageBeep(MB_ICONEXCLAMATION);
+    MessageBox(NULL, Tcl_GetStringResult(interp), "Error in Tclkit",
+        MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
+    ExitProcess(1);
+    /* we won't reach this, but we need the return */
+#endif
+    return TCL_ERROR;
+}
+
+static void
+TclKit_InitStdChannels(void)
+{
+    Tcl_Channel chan;
+
+    /*
+     * We need to verify if we have the standard channels and create them if
+     * not.  Otherwise internals channels may get used as standard channels
+     * (like for encodings) and panic.
+     */
+    chan = Tcl_GetStdChannel(TCL_STDIN);
+    if (chan == NULL) {
+      	chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "r", 0);
+      	if (chan != NULL) {
+      	    Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
+      	}
+      	Tcl_SetStdChannel(chan, TCL_STDIN);
+    }
+    chan = Tcl_GetStdChannel(TCL_STDOUT);
+    if (chan == NULL) {
+      	chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "w", 0);
+      	if (chan != NULL) {
+      	    Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
+      	}
+      	Tcl_SetStdChannel(chan, TCL_STDOUT);
+    }
+    chan = Tcl_GetStdChannel(TCL_STDERR);
+    if (chan == NULL) {
+      	chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "w", 0);
+      	if (chan != NULL) {
+      	    Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
+      	}
+      	Tcl_SetStdChannel(chan, TCL_STDERR);
+    }
+}
Index: trunk/kitgen/makefile.include
===================================================================
--- trunk/kitgen/makefile.include	(revision 175)
+++ trunk/kitgen/makefile.include	(revision 175)
@@ -0,0 +1,191 @@
+# -*- Makefile -*-
+# NOTE: this file requires GNU Make.
+#
+#PLAT = unix
+#PRIV = install-private-headers
+#
+# Linux:
+#LDFLAGS = -L/usr/X11R6/lib -lX11 -ldl -lm # -lpthread
+#
+# Mac OS X:
+#LDFLAGS = -framework CoreFoundation -L/usr/X11R6/lib -lX11 -weak-lXss -lXext
+#LDFLAGS = -framework CoreFoundation -framework Carbon -framework IOKit
+#LDSTRIP = -x
+
+EXTDIR = ../../../../8.x
+STATIC = --disable-shared --enable-static
+OUTDIR = $(shell pwd)/build
+OBJ    = $(OUTDIR)/zvfs$O $(OUTDIR)/zlib$O $(OUTDIR)/usb$O \
+	 $(OUTDIR)/g2lite$O $(OUTDIR)/swt$O $(OUTDIR)/csr_test$O
+GUIOBJ ?= $(OBJ) $(OUTDIR)/tclAppInit$O
+TCLDIR = --with-tcl=$(OUTDIR)/lib --prefix=$(OUTDIR) --exec-prefix=$(OUTDIR)
+STRIP ?= strip
+UPX   ?= :
+O     ?=.o
+A     ?=.a
+SO    ?=.so
+
+BLT_OPTS = --disable-threads
+VFS_OPTS = --disable-threads
+TDOM_OPTS = --disable-threads
+TCLX_OPTS = --disable-threads
+XOTCL_OPTS = --disable-threads
+SQLITE_OPTS = --disable-threads
+ZLIB_OPTS = --prefix=$(OUTDIR)
+LIBUSB_OPTS = --prefix=$(OUTDIR)
+
+tclkit-gui$(EXE): kit-gui$(EXE) ../../setupvfs.tcl build/files
+	cp kit-gui$(EXE) $@
+	sleep 3
+	$(STRIP) $@
+	$(UPX) $@
+	./kit-gui -init- ../../setupvfs.tcl $(KIT_OPTS) $@ gui
+
+ifeq ($(PLAT), unix)
+kit-gui$(EXE): build/tcl build/tk build/blt build/tclvfs build/zlib \
+	build/tdom build/tcllib build/xotcl build/sqlite build/libusb $(GUIOBJ)
+	$(CC) -o $@ $(CFLAGS) ../../kitInit.c $(GUIOBJ) \
+	  -Ibuild/include -DKIT_LITE -DSTATIC_BUILD \
+	  build/lib/vfs1*/*vfs1*$A \
+	  -DKIT_INCLUDES_TK build/lib/*tk8*$A \
+	  build/lib/libz$A build/lib/libusb$A \
+	  build/lib/*tcl8*$A \
+	  build/lib/BLT2*/*BLT2*$A \
+	  build/lib/tdom0*/*tdom0*$A \
+	  build/lib/sqlite3*/*sqlite3*$A \
+	  build/lib/xotcl1*/*xotcl1*$A \
+	  $(LDFLAGS) $(GUI_OPTS)
+endif
+
+ifeq ($(PLAT), win)
+kit-gui$(EXE): build/tcl build/tk build/blt build/tclvfs build/zlib \
+	build/tdom build/tcllib build/xotcl build/sqlite build/libusb-win32 $(GUIOBJ)
+	$(CC) -o $@ $(CFLAGS) ../../kitInit.c $(GUIOBJ) \
+	  -Ibuild/include -DKIT_LITE -DSTATIC_BUILD \
+	  build/lib/vfs1*/*vfs1*$A \
+	  -DKIT_INCLUDES_TK build/lib/*tk8*$A \
+	  build/lib/libz$A build/lib/libusb$A \
+	  build/lib/*tcl8*$A \
+	  build/lib/BLT2*/*BLT2*$A \
+	  build/lib/tdom0*/*tdom0*$A \
+	  build/lib/sqlite3*/*sqlite3*$A \
+	  build/lib/xotcl1*/*xotcl1*$A \
+	  $(LDFLAGS) $(GUI_OPTS)
+endif
+
+
+
+build/files:
+	mkdir -p $@ && cd $@ && ln -s ../../../../files/* .
+
+build/tcl:
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh ../../../tcl/$(PLAT)/configure $(STATIC) $(TCL_OPTS) \
+	    --prefix=$(OUTDIR) --exec-prefix=$(OUTDIR) && \
+	  $(MAKE) install-binaries install-libraries $(PRIV)
+
+build/tk: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh ../../../tk/$(PLAT)/configure $(STATIC) $(TCLDIR) $(TK_OPTS) && \
+	  $(MAKE) install-binaries install-libraries $(PRIV)
+
+build/blt: build/tk
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/blt/configure $(STATIC) $(TCLDIR) $(BLT_OPTS) && \
+	  $(MAKE) install-binaries install-libraries
+
+build/tclvfs: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/tclvfs/configure $(STATIC) $(TCLDIR) $(VFS_OPTS) && \
+	  $(MAKE) install
+
+build/zlib: build/tcl
+	cp -R ../../8.x/zlib/. $@
+	cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/zlib/configure $(ZLIB_OPTS) && \
+	  $(MAKE) install
+
+build/libusb-win32: build/tcl
+	cp -R ../../8.x/libusb-win32/. $@
+	cd $@ && $(MAKE) && cp libusb.a ../lib/ && cp lusb0_usb.h ../include/usb.h
+
+build/libusb: build/tcl
+	cp -R ../../8.x/libusb/. $@
+	cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh configure $(LIBUSB_OPTS) && \
+	  $(MAKE) && $(MAKE) install
+
+build/tdom: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/tdom/configure $(STATIC) $(TCLDIR) $(TDOM_OPTS) && \
+	  $(MAKE) install
+
+build/tcllib: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/tcllib/configure $(STATIC) $(TCLDIR) $(TCLLIB_OPTS) && \
+	  $(MAKE) install-libraries
+
+build/xotcl: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/xotcl/configure $(STATIC) $(TCLDIR) $(XOTCL_OPTS) && \
+	  $(MAKE) install-binaries
+
+build/sqlite: build/tcl
+	mkdir -p $@ && cd $@ && CFLAGS="$(CFLAGS)" && export CFLAGS && \
+	  sh $(EXTDIR)/sqlite/tea/configure $(STATIC) $(TCLDIR) $(SQLITE_OPTS) && \
+	  $(MAKE) install-binaries
+
+base: build/tcl build/tk
+	ls -l build/bin
+
+tidy: cleanext
+	rm -rf build/tcl build/tk build/tkdyn
+
+cleanext:
+	rm -rf build/tclvfs build/thread build/zlib build/*.o
+	rm -rf build/lib/vfs* build/lib/thread*
+
+clean:
+	rm -rf build kit-gui$(EXE)
+
+distclean: clean
+	rm -f tclkit-gui$(EXE)
+
+.PHONY: all base clean distclean threaded tidy
+
+$(OUTDIR)/zvfs$O: ../../zvfs.c
+	$(CC) -o $@ $(CFLAGS) -DHAVE_UNISTD_H=1 -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/zlib$O: ../../zlib.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/g2lite$O: ../../g2lite.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/usb$O: ../../usb.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/swt$O: ../../swt.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/csr_test$O: ../../csr_test.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include -c $<
+
+$(OUTDIR)/tclAppInit$O: ../tcl/$(PLAT)/tclAppInit.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include \
+	  -DTCL_LOCAL_APPINIT=TclKit_AppInit -c $<
+
+$(OUTDIR)/winMain$O: ../tk/$(PLAT)/winMain.c
+	$(CC) -o $@ $(CFLAGS) -DSTATIC_BUILD -Ibuild/include \
+	  -DTK_LOCAL_APPINIT=TclKit_AppInit -c $<
+
+$(OUTDIR)/tclkit.res.o: ../../tclkit.rc
+	cp $< build/tk/tclkit.rc
+	windres -o $@ --define STATIC_BUILD --define TCLKIT_WITH_TK \
+	  --define BASE_NO_TK_ICON --include build/include \
+	  --include build/tk --include ../../files --include ../tk/win/rc \
+	  build/tk/tclkit.rc
+
+$(OUTDIR)/tclkitsh.res.o: ../../tclkit.rc
+	windres -o $@ --define STATIC_BUILD --include build/include \
+	  --include ../../files $<
Index: trunk/kitgen/setupvfs.tcl
===================================================================
--- trunk/kitgen/setupvfs.tcl	(revision 175)
+++ trunk/kitgen/setupvfs.tcl	(revision 175)
@@ -0,0 +1,366 @@
+# setupvfs.tcl -- new tclkit-{cli,gui} generation bootstrap
+#
+# jcw, 2006-11-16
+
+proc history {args} {} ;# since this runs so early, all debugging support helps
+
+if {[lindex $argv 0] ne "-init-"} {
+  puts stderr "setupvfs.tcl has to be run by kit-cli with the '-init-' flag"
+  exit 1
+}
+
+set argv [lrange $argv 2 end] ;# strip off the leading "-init- setupvfs.tcl"
+
+set debugOpt 0
+set encOpt 0
+set msgsOpt 0
+set threadOpt 0
+set tzOpt 0
+
+while {1} {
+  switch -- [lindex $argv 0] {
+    -d { incr debugOpt }
+    -e { incr encOpt }
+    -m { incr msgsOpt }
+    -t { incr threadOpt }
+    -z { incr tzOpt }
+    default { break }
+  }
+  set argv [lrange $argv 1 end]
+}
+
+if {[llength $argv] != 2} {
+  puts stderr "Usage: [file tail [info nameofexe]] -init- [info script]\
+    ?-d? ?-e? ?-m? ?-t? ?-z? destfile (cli|gui)
+    -d    output some debugging info from this setup script
+    -e    include all encodings i.s.o. 7 basic ones (encodings/)
+    -m    include all localized message files (tcl 8.5, msgs/)
+    -t    include the thread extension as shared lib in vfs
+    -z    include timezone data files (tcl 8.5, tzdata/)"
+  exit 1
+}
+
+load {} zlib
+load {} vfs
+load {} sqlite3
+load {} xotcl
+load {} tdom
+load {} g2lite
+load {} usb
+load {} swt
+load {} csr
+
+# map of proper version numbers to replace @ markers in paths given to vfscopy
+# this relies on having all necessary extensions already loaded at this point
+set versmap [list tcl8@ tcl$tcl_version tk8@ tk$tcl_version \
+                  zlib1@ zlib[package require zlib] \
+                  vfs1@ vfs[package require vfs] \
+                  sqlite3@ sqlite[package require sqlite3] \
+                  xotcl1@ xotcl[package require XOTcl] \
+                  tdom0@ tdom[package require tdom] \
+                  g2lite0@ g2lite[package require g2lite] \
+                  usb0@ usb[package require usb] \
+                  swt0@ swt[package require swt] \
+                  csr0@ csr[package require csr]]
+
+if {$debugOpt} {
+  puts "Starting [info script]"
+  puts "     exe: [info nameofexe]"
+  puts "    argv: $argv"
+  puts "   tcltk: $tcl_version"
+  puts "  loaded: [info loaded]"
+  puts " versmap: $versmap"
+  puts ""
+}
+
+set tcl_library ../tcl/library
+source ../tcl/library/init.tcl ;# for tcl::CopyDirectory
+
+# Create package index files for the static extensions.
+set exts {swt csr usb g2lite XOTcl zlib}
+foreach ext $exts {
+  load {} $ext
+  set dst [file join lib "[string tolower $ext][package provide $ext]" pkgIndex.tcl]
+  puts $dst
+  set index($dst) "package ifneeded $ext [package provide $ext] {load {} [string tolower $ext]}"
+}
+set index(lib/sqlite[package provide sqlite3]/pkgIndex.tcl) "package ifneeded sqlite3 [package provide sqlite3] {load {} sqlite3}"\
+
+set clifiles {
+  boot.tcl
+  main.tcl
+  config.tcl
+  lib/tcl8@/auto.tcl
+  lib/tcl8@/history.tcl
+  lib/tcl8@/init.tcl
+  lib/tcl8@/opt0.4
+  lib/tcl8@/package.tcl
+  lib/tcl8@/parray.tcl
+  lib/tcl8@/safe.tcl
+  lib/tcl8@/tclIndex
+  lib/tcl8@/word.tcl
+  lib/vfs1@/mk4vfs.tcl
+  lib/vfs1@/pkgIndex.tcl
+  lib/vfs1@/starkit.tcl
+  lib/vfs1@/vfslib.tcl
+  lib/vfs1@/vfsUtils.tcl
+  lib/vfs1@/zipvfs.tcl
+  lib/sqlite3@/pkgIndex.tcl
+  lib/xotcl1@/pkgIndex.tcl
+  lib/tdom0@/pkgIndex.tcl
+  lib/tdom0@/tdom.tcl
+  lib/g2lite0@/pkgIndex.tcl
+  lib/usb0@/pkgIndex.tcl
+  lib/swt0@/pkgIndex.tcl
+  lib/csr0@/pkgIndex.tcl
+  lib/zlib1@/pkgIndex.tcl
+  lib/tcllib1.14/pkgIndex.tcl
+  lib/tcllib1.14/asn
+  lib/tcllib1.14/base64
+  lib/tcllib1.14/comm
+  lib/tcllib1.14/cmdline
+  lib/tcllib1.14/fileutil
+  lib/tcllib1.14/ldap
+  lib/tcllib1.14/log
+  lib/tcllib1.14/math
+  lib/tcllib1.14/snit
+  lib/tcllib1.14/uri}
+
+set guifiles {
+  tclkit.ico
+  lib/tk8@/bgerror.tcl
+  lib/tk8@/button.tcl
+  lib/tk8@/choosedir.tcl
+  lib/tk8@/clrpick.tcl
+  lib/tk8@/comdlg.tcl
+  lib/tk8@/console.tcl
+  lib/tk8@/dialog.tcl
+  lib/tk8@/entry.tcl
+  lib/tk8@/focus.tcl
+  lib/tk8@/listbox.tcl
+  lib/tk8@/menu.tcl
+  lib/tk8@/mkpsenc.tcl
+  lib/tk8@/msgbox.tcl
+  lib/tk8@/msgs
+  lib/tk8@/obsolete.tcl
+  lib/tk8@/optMenu.tcl
+  lib/tk8@/palette.tcl
+  lib/tk8@/panedwindow.tcl
+  lib/tk8@/pkgIndex.tcl
+  lib/tk8@/safetk.tcl
+  lib/tk8@/scale.tcl
+  lib/tk8@/scrlbar.tcl
+  lib/tk8@/spinbox.tcl
+  lib/tk8@/tclIndex
+  lib/tk8@/tearoff.tcl
+  lib/tk8@/text.tcl
+  lib/tk8@/tk.tcl
+  lib/tk8@/tkfbox.tcl
+  lib/tk8@/unsupported.tcl
+  lib/tk8@/xmfbox.tcl
+  lib/BLT2.4/pkgIndex.tcl
+  lib/BLT2.4/graph.tcl
+  lib/BLT2.4/tabnotebook.tcl
+  lib/BLT2.4/treeview.cur
+  lib/BLT2.4/treeview.xbm
+  lib/BLT2.4/treeview.tcl
+  lib/BLT2.4/treeview_m.xbm
+  lib/BLT2.4/bltCanvEps.pro
+  lib/BLT2.4/bltGraph.pro
+}
+
+if {$encOpt} {
+  lappend clifiles lib/tcl8@/encoding
+} else {
+  lappend clifiles lib/tcl8@/encoding/ascii.enc \
+                   lib/tcl8@/encoding/cp1251.enc \
+                   lib/tcl8@/encoding/cp1252.enc \
+                   lib/tcl8@/encoding/iso8859-1.enc \
+                   lib/tcl8@/encoding/iso8859-2.enc \
+                   lib/tcl8@/encoding/iso8859-15.enc \
+                   lib/tcl8@/encoding/koi8-r.enc \
+                   lib/tcl8@/encoding/macRoman.enc
+}
+
+if {$threadOpt} {
+  lappend clifiles lib/[glob -tails -dir build/lib thread2*]
+}
+
+if {$tcl_version eq "8.4"} {
+  lappend clifiles lib/tcl8@/http2.5 \
+            	   lib/tcl8@/ldAout.tcl \
+            	   lib/tcl8@/msgcat1.3 \
+            	   lib/tcl8@/tcltest2.2
+} else {
+  lappend clifiles lib/tcl8 \
+                   lib/tcl8@/clock.tcl \
+                   lib/tcl8@/tm.tcl
+
+  lappend guifiles lib/tk8@/ttk
+
+  if {$msgsOpt} {
+    lappend clifiles lib/tcl8@/msgs
+  }
+  if {$tzOpt} {
+    lappend clifiles lib/tcl8@/tzdata
+  }
+}
+
+# look for a/b/c in three places:
+#   1) build/files/b-c
+#   2) build/files/a/b/c
+#   3) build/a/b/c
+
+proc timet_to_dos {time_t} {
+    set s [clock format $time_t -format {%Y %m %e %k %M %S}]
+    scan $s {%d %d %d %d %d %d} year month day hour min sec
+    expr {(($year-1980) << 25) | ($month << 21) | ($day << 16)
+          | ($hour << 11) | ($min << 5) | ($sec >> 1)}
+}
+
+proc walk {path} {
+    set result {}
+    set files [glob -nocomplain -types f -directory $path *]
+    foreach file $files {
+        set excluded 0
+        foreach glob $excludes {
+            if {[string match $glob $file]} {
+                set excluded 1
+                break
+            }
+        }
+        if {!$excluded} {lappend result $file}
+    }
+    foreach dir [glob -nocomplain -types d -directory $path $match] {
+        set subdir [walk $dir $excludes $match]
+        if {[llength $subdir]>0} {
+            set result [concat $result $dir $subdir]
+        }
+    }
+    return $result
+}
+
+proc mkzipfile {zipchan dst {comment {}}} {
+  global index
+
+  set mtime [timet_to_dos [clock seconds]]
+  set utfpath [encoding convertto utf-8 $dst]
+  set utfcomment [encoding convertto utf-8 $comment]
+  set flags [expr {(1<<10)}] ;# use utf-8
+  set method 0               ;# store 0, deflate 8
+  set attr 0                 ;# text or binary (default binary)
+  set extra ""
+  set crc 0
+  set csize 0
+  set version 20
+
+  if {[info exists index($dst)]} {
+    set data $index($dst)
+  } else {
+    set a [file split $dst]
+    set src build/files/[lindex $a end-1]-[lindex $a end]
+    if {[file exists $src]} {
+      if {$::debugOpt} {
+        puts "  $src  ==>  $dst"
+      }
+    } else {
+      set src build/files/$dst
+      if {[file exists $src]} {
+        if {$::debugOpt} {
+          puts "  $src  ==>  $dst"
+        }
+      } else {
+        set src build/$dst
+      }
+    }
+    if {[file isfile $src]} {
+      set mtime [timet_to_dos [file mtime $src]]
+      set fin [open $src r]
+      fconfigure $fin -translation binary -encoding binary
+      set data [read $fin]
+      close $fin
+    } else {
+      error "cannot find $src"
+    }
+  }
+
+  set attrex 0x81b60000  ;# 0o100666 (-rw-rw-rw-)
+  if {[file extension $dst] eq ".tcl"} {
+    set attr 1         ;# text
+  }
+
+  set size [string length $data]
+  set crc [zlib crc32 $data]
+  set cdata [zlib deflate $data]
+  if {[string length $cdata] < $size} {
+    set method 8
+    set data $cdata
+  }
+  set csize [string length $data]
+
+
+  set local [binary format a4sssiiiiss PK\03\04 \
+    $version $flags $method $mtime $crc $csize $size \
+    [string length $utfpath] [string length $extra]]
+  append local $utfpath $extra
+
+  set offset [tell $zipchan]
+  puts -nonewline $zipchan $local
+  puts -nonewline $zipchan $data
+
+  set hdr [binary format a4ssssiiiisssssii PK\01\02 0x0317 \
+    $version $flags $method $mtime $crc $csize $size \
+    [string length $utfpath] [string length $extra]\
+    [string length $utfcomment] 0 $attr $attrex $offset]
+  append hdr $utfpath $extra $utfcomment
+
+  return $hdr
+}
+
+# copy file to vfs
+proc vfscopy {zf argv} {
+  global versmap count cd
+  
+  foreach f $argv {
+    set dst [string map $versmap $f]
+
+    if {[file isdirectory build/$dst]} {
+      vfscopy $zf [glob -nocomplain -tails -directory build $dst/*]
+      continue
+    }
+
+    append cd [mkzipfile $zf $dst]
+    incr count
+  }
+}
+
+set zf [open [lindex $argv 0] a]
+fconfigure $zf -translation binary -encoding binary
+
+set count 0
+set cd ""
+
+switch [lindex $argv 1] {
+  cli {
+    vfscopy $zf $clifiles
+  }
+  gui {
+    vfscopy $zf $clifiles
+    vfscopy $zf $guifiles
+  }
+  default {
+    puts stderr "Unknown type, must be cli or gui"
+    exit 1
+  }
+}
+
+set cdoffset [tell $zf]
+set endrec [binary format a4ssssiis PK\05\06 0 0 \
+    $count $count [string length $cd] $cdoffset 0]
+puts -nonewline $zf $cd
+puts -nonewline $zf $endrec
+close $zf
+
+if {$debugOpt} {
+  puts "\nDone with [info script]"
+}
Index: trunk/kitgen/starfish-noyp.patch
===================================================================
--- trunk/kitgen/starfish-noyp.patch	(revision 175)
+++ trunk/kitgen/starfish-noyp.patch	(revision 175)
@@ -0,0 +1,201 @@
+diff -pruN starfish/starfishCmd.c starfish-patched/starfishCmd.c
+--- starfish/starfishCmd.c	2003-03-11 01:47:59.000000000 +0100
++++ starfish-patched/starfishCmd.c	2007-10-19 01:55:36.880301248 +0200
+@@ -53,27 +53,12 @@
+ 
+ #include "starfishInt.h"
+ 
+-#if defined(sun) && defined (__SVR4)
+-#include <rpc/rpcent.h>
+-#else
+-#include <rpc/rpc.h>
+-#endif
+-
+-#if defined(__OpenBSD__)
+-#include <rpcsvc/ypclnt.h>
+-#endif
+-
+-#include <rpcsvc/yp_prot.h>
+-
+ #include <sys/ioctl.h>
+ #include <netinet/in.h>
+ #include <net/if.h>
+ 
+ #define KEEPALIVE	1
+ 
+-#define YP_SUCC		0
+-#define YP_FAIL		1
+-
+ #define SYS_ERROR	-1
+ 
+ #define BUFLEN		256
+@@ -115,33 +100,6 @@ static int Starfish_NetdbMap
+   char *cmd, char *subcmd));
+ 
+ /************************************************************************/
+-/* Support functions.							*/
+-/************************************************************************/
+-
+-static int Callback(int status, char *key, int keylen, char *val, int vallen,
+-  char *data)
+-  {
+-    Tcl_Interp *interp = (Tcl_Interp *) data;
+-
+-    switch (status)
+-      {
+-        case YP_TRUE:
+-	  key[keylen] = 0;
+-	  val[vallen] = 0;
+-	  Tcl_AppendResult(interp, "{", key, " ", val, "}", (char *) NULL);
+-	  return YP_SUCC;
+-	  break;
+-        case YP_NOMORE:
+-	  break;
+-        default:
+-	  Tcl_AppendResult(interp, "cannot lookup NIS map", (char *) NULL);
+-	  break;
+-      }
+-
+-    return YP_FAIL;
+-  }
+-
+-/************************************************************************/
+ /* Subcommand functions.						*/
+ /************************************************************************/
+ 
+@@ -860,91 +818,6 @@ Starfish_NetdbIp(Tcl_Interp *interp, int
+     return TCL_ERROR;
+ }
+ 
+-/*
+- *----------------------------------------------------------------------
+- *
+- * Starfish_NetdbMap --
+- *
+- *      This procedure is invoked to process the default case of the
+- *      "netdb" command.
+- *	See the user documentation for details on what it does.
+- *
+- * Results:
+- *      A standard Tcl result.
+- *
+- * Side effects:
+- *      See the user documentation.
+- *
+- *----------------------------------------------------------------------
+- */
+-
+-static int
+-Starfish_NetdbMap(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[],
+-  char *cmd, char *subcmd)
+-{
+-    struct ypall_callback obj;
+-    char *self, *domain, *map, *key, *val;
+-    int result, keylen, vallen;
+-
+-    /*
+-     * Process the default "netdb" command option:
+-     */
+-
+-    result = yp_get_default_domain(&domain);
+-
+-    if (result != YP_SUCC) {
+-	Tcl_AppendResult(interp, "NIS domain not set", (char *) NULL);
+-	return TCL_ERROR;
+-    }
+-
+-    switch (objc)
+-      {
+-        case 2:
+-	  map		= Tcl_GetString(objv[1]);
+-
+-	  obj.foreach	= Callback;
+-	  obj.data	= (char *) interp;
+-
+-	  result = yp_all(domain, map, &obj);
+-
+-	  if (result != YP_SUCC)
+-	    {
+-	      Tcl_AppendResult(interp, 
+-			       "cannot lookup NIS map \"", map, "\"",
+-			       (char *) NULL);
+-	      return TCL_ERROR;
+-	    }
+-
+-	  return TCL_OK;
+-	  break;
+-
+-        case 3:
+-	  map		= Tcl_GetString(objv[1]);
+-	  key		= Tcl_GetString(objv[2]);
+-
+-	  result = yp_match(domain, map, key, strlen(key), &val, &vallen);
+-
+-	  if (result != YP_SUCC)
+-	    {
+-	      Tcl_AppendResult(interp,
+-			       "cannot match NIS map \"", map, "\"",
+-			       (char *) NULL);
+-	      return TCL_ERROR;
+-	    }
+-
+-	  val[vallen] = 0;
+-	  Tcl_AppendResult(interp, val, (char *) NULL);
+-	  return TCL_OK;
+-	  break;
+-
+-        default:
+-	  Tcl_AppendResult(interp, "wrong # args: should be \"",
+-	    cmd, " map ?key?\"", (char *) NULL);
+-	  return TCL_ERROR;
+-	  break;
+-      }
+-}
+-
+ /************************************************************************/
+ /* Entry points.							*/
+ /************************************************************************/
+@@ -995,7 +868,9 @@ Starfish_NetdbCmd(ClientData clientData,
+     } else if (strcmp(subcmd, "ip")		== 0) {
+ 	result = Starfish_NetdbIp(interp, objc, objv, cmd, subcmd);
+     } else {
+-	result = Starfish_NetdbMap(interp, objc, objv, cmd, subcmd);
++    	  Tcl_AppendResult(interp, "wrong # args: should be \"",
++	        cmd, " map ?key?\"", (char *) NULL);
++        result = TCL_ERROR;
+     }
+ 
+ #ifdef TCL_THREADS
+diff -pruN starfish/starfishInit.c starfish-patched/starfishInit.c
+--- starfish/starfishInit.c	2003-03-11 01:48:05.000000000 +0100
++++ starfish-patched/starfishInit.c	2007-10-19 01:55:36.880301248 +0200
+@@ -86,14 +86,10 @@ Starfish_Init(Tcl_Interp *interp)
+     }
+ #endif
+ 
+-#ifdef STARFISH_FORCE_PACKAGE
+-    /* Ordinarily we are loaded as part of an enclosing package definition.
+-    */
+     result = Tcl_PkgProvide(interp, STARFISH_PACKAGE, STARFISH_VERSION);
+     if (result != TCL_OK) {
+         return result;
+     }
+-#endif
+ 
+     Starfish_Limit();
+     Starfish_InitSafeCmds(interp);
+@@ -115,14 +111,10 @@ Starfish_SafeInit(Tcl_Interp *interp)
+   {
+     int result;
+ 
+-#ifdef STARFISH_FORCE_PACKAGE
+-    /* Ordinarily we are loaded as part of an enclosing package definition.
+-    */
+     result = Tcl_PkgProvide(interp, STARFISH_PACKAGE, STARFISH_VERSION);
+     if (result != TCL_OK) {
+         return result;
+     }
+-#endif
+ 
+     Starfish_Limit();
+     Starfish_InitSafeCmds(interp);
Index: trunk/kitgen/swt.c
===================================================================
--- trunk/kitgen/swt.c	(revision 175)
+++ trunk/kitgen/swt.c	(revision 175)
@@ -0,0 +1,250 @@
+
+/*
+  Copyright (c) 2007, Pavel Demin
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms,
+  with or without modification, are permitted
+  provided that the following conditions are met:
+
+      * Redistributions of source code must retain
+        the above copyright notice, this list of conditions
+        and the following disclaimer.
+      * Redistributions in binary form must reproduce
+        the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation
+        and/or other materials provided with the distribution.
+      * Neither the name of the SRMlite nor the names of its
+        contributors may be used to endorse or promote products
+        derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <stdio.h>
+#include <math.h>
+#include <tcl.h>
+
+#define SQRT_2 1.4142135623730951
+
+static double bior_3_1_hi[4] = {-0.25, -0.75, 0.75, 0.25};
+static double bior_3_1_lo[4] = {0.125, 0.375, 0.375, 0.125};
+
+/*
+static double bior_3_1_hi[4] = {-1.0, -3.0, 3.0, 1.0};
+static double bior_3_1_lo[4] = {1.0, 3.0, 3.0, 1.0};
+*/
+
+static void uwt_forward(double *input, double *d, double *a, int n, int level,
+                        double *hi, double *lo, int l)
+{
+    int i, j, k;
+    double entry;
+
+    for(i = 0; i < n; ++i)
+    {
+        k = i + (((l - 1) << (level - 1)) + 1)/2;
+        entry = (k >= 0 && k < n) ? input[k] : 0.0;
+        d[i] = hi[0] * entry;
+        a[i] = lo[0] * entry;
+/*
+        printf("%d %d -> %g * %g -> %g\n", i, 0, hi[0], entry, d[i]);
+*/
+        for(j = 1; j < l; ++j)
+        {
+          k -= (1 << (level - 1));
+          entry = (k >= 0 && k < n) ? input[k] : 0.0;
+          d[i] += hi[j] * entry;
+          a[i] += lo[j] * entry;
+/*
+          printf("%d %d -> + %g * %g -> %g\n", i, j, hi[j], entry, d[i]);
+*/
+        }
+    }
+}
+
+/* ----------------------------------------------------------------- */
+
+static int UwtObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                         int objc, Tcl_Obj *CONST objv[])
+{
+  int shift = 0;
+  int size = 0;
+  int i = 0;
+  int level = 0;
+  double value = 0.0;
+  double *tmp, *input, *d, *a;
+  Tcl_Obj *result = NULL;
+  Tcl_Obj *lista = NULL;
+  Tcl_Obj *listd = NULL;
+
+  if(objc != 3)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "level data");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &level))
+  {
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_ListObjLength(interp, objv[2], &size))
+  {
+    return TCL_ERROR;
+  }
+
+  input = (double *) malloc(size*sizeof(double));
+  d = (double *) malloc(size*sizeof(double));
+  a = (double *) malloc(size*sizeof(double));
+
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjIndex(interp, objv[2], i, &result);
+    if(TCL_OK != Tcl_GetDoubleFromObj(interp, result, &value))
+    {
+      free(input);
+      free(d);
+      free(a);
+      return TCL_ERROR;
+    }
+    input[i] = value;
+/*
+    printf("%d -> %g\n", i, input[i]);
+*/
+  }
+
+  for(i = 1; i <= level; ++i)
+  {
+    uwt_forward(input, d, a, size, i, bior_3_1_hi, bior_3_1_lo, 4);
+    tmp = input;
+    input = a;
+    a = tmp;
+  }
+
+  result = Tcl_GetObjResult(interp);
+  lista = Tcl_NewObj();
+  listd = Tcl_NewObj();
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjAppendElement(interp, listd, Tcl_NewDoubleObj(d[i]));
+    Tcl_ListObjAppendElement(interp, lista, Tcl_NewDoubleObj(input[i]));
+/*
+    printf("%d -> %g\n", i, d[i]);
+*/	}
+
+  Tcl_ListObjAppendElement(interp, result, listd);
+  Tcl_ListObjAppendElement(interp, result, lista);
+
+  free(input);
+  free(d);
+  free(a);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static void sum8(double *input, double *output, int n)
+{
+    int i, j, k;
+    double entry;
+
+    for(i = 0; i < n; ++i)
+    {
+        k = i + 4;
+        entry = (k >= 0 && k < n) ? input[k] : 0.0;
+        output[i] = entry;
+        for(j = 1; j < 8; ++j)
+        {
+          --k;
+          entry = (k >= 0 && k < n) ? input[k] : 0.0;
+          output[i] += entry;
+        }
+        
+        output[i] = output[i]/8.0;
+
+    }
+}
+
+/* ----------------------------------------------------------------- */
+
+static int Sum8ObjCmdProc(ClientData clientData, Tcl_Interp *interp,
+                          int objc, Tcl_Obj *CONST objv[])
+{
+  int size = 0;
+  int i = 0;
+  int level = 0;
+  double value = 0.0;
+  double *input, *output;
+  Tcl_Obj *result = NULL;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_ListObjLength(interp, objv[1], &size))
+  {
+    return TCL_ERROR;
+  }
+
+  input = (double *) malloc(size*sizeof(double));
+  output = (double *) malloc(size*sizeof(double));
+
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjIndex(interp, objv[1], i, &result);
+    if(TCL_OK != Tcl_GetDoubleFromObj(interp, result, &value))
+    {
+      free(input);
+      free(output);
+      return TCL_ERROR;
+    }
+    input[i] = value;
+/*
+    printf("%d -> %g\n", i, input[i]);
+*/
+  }
+
+  sum8(input, output, size);
+
+  result = Tcl_GetObjResult(interp);
+  for(i = 0; i < size; ++i)
+  {
+    Tcl_ListObjAppendElement(interp, result, Tcl_NewDoubleObj(output[i]));
+/*
+    printf("%d -> %g\n", i, d[i]);
+*/
+	}
+
+  free(input);
+  free(output);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+int Swt_Init(Tcl_Interp *interp)
+{
+    Tcl_CreateObjCommand(interp, "uwt", UwtObjCmdProc, 0, 0);
+    Tcl_CreateObjCommand(interp, "sum8", Sum8ObjCmdProc, 0, 0);
+
+    return Tcl_PkgProvide(interp, "swt", "0.1");
+}
Index: trunk/kitgen/tclkit.rc
===================================================================
--- trunk/kitgen/tclkit.rc	(revision 175)
+++ trunk/kitgen/tclkit.rc	(revision 175)
@@ -0,0 +1,115 @@
+/* RCS: @(#) $Id: tclkit.rc,v 1.2 2006/01/12 09:00:23 jcw Exp $
+ *
+ * Version Resource Script
+ *
+ */
+
+#define RESOURCE_INCLUDED
+
+#include <winver.h>
+#include <tcl.h>
+
+#ifdef TCLKIT_WITH_TK
+#include <tk.h>
+#endif
+
+#define STRINGIFY1(x)	    #x
+#define STRINGIFY(x)	    STRINGIFY1(x) 
+
+
+/*
+ * build-up the name suffix that defines the type of build this is.
+ */
+
+#ifdef TCL_THREADS
+#define SUFFIX_THREADS	    "t"
+#else
+#define SUFFIX_THREADS	    ""
+#endif
+
+#if STATIC_BUILD
+#define SUFFIX_STATIC	    "s"
+#else
+#define SUFFIX_STATIC	    ""
+#endif
+
+#ifdef DEBUG
+#define SUFFIX_DEBUG	    "g"
+#else
+#define SUFFIX_DEBUG	    ""
+#endif
+
+#define SUFFIX		    SUFFIX_THREADS SUFFIX_STATIC SUFFIX_DEBUG
+
+
+LANGUAGE 0x9, 0x1	/* LANG_ENGLISH, SUBLANG_DEFAULT */
+
+VS_VERSION_INFO	VERSIONINFO
+#ifdef TCLKIT_WITH_TK
+ FILEVERSION	TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+ PRODUCTVERSION	TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+#else
+ FILEVERSION	TCL_MAJOR_VERSION,TCL_MINOR_VERSION,TCL_RELEASE_LEVEL,TCL_RELEASE_SERIAL
+ PRODUCTVERSION	TCL_MAJOR_VERSION,TCL_MINOR_VERSION,TCL_RELEASE_LEVEL,TCL_RELEASE_SERIAL
+#endif
+ FILEFLAGSMASK	0x3fL
+#ifdef DEBUG
+ FILEFLAGS	VS_FF_DEBUG
+#else
+ FILEFLAGS	0x0L
+#endif
+ FILEOS		VOS__WINDOWS32
+ FILETYPE	VFT_DLL
+ FILESUBTYPE	0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription", "Tclkit, a standalone runtime for Tcl/Tk\0"
+            VALUE "OriginalFilename", "tclkit-gui.exe\0"
+            VALUE "CompanyName", "Equi4 Software\0"
+            VALUE "LegalCopyright", "Copyright \251 1989-2003 by J.Ousterhout et al.\0"
+#ifdef TCLKIT_WITH_TK
+            VALUE "FileVersion", TK_PATCH_LEVEL
+            VALUE "ProductName", "Tclkit " TK_VERSION " for Windows\0"
+            VALUE "ProductVersion", TK_PATCH_LEVEL
+#else
+            VALUE "FileVersion", TCL_PATCH_LEVEL
+            VALUE "ProductName", "Tclkit " TCL_VERSION " for Windows\0"
+            VALUE "ProductVersion", TCL_PATCH_LEVEL
+#endif
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+/*
+ * Icon
+ */
+
+tk                    ICON    DISCARDABLE     "tclkit.ico"
+
+#ifdef TCLKIT_WITH_TK
+/*
+ *  Include the base resources.
+ */
+
+#include "tk_base.rc"
+
+/*
+ * This enables themed scrollbars in XP by trying to use comctl32 v6.
+ */
+
+#ifndef RT_MANIFEST
+#define RT_MANIFEST     24
+#endif
+#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+#endif
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "wish.exe.manifest"
+
+#endif
Index: trunk/kitgen/usb.c
===================================================================
--- trunk/kitgen/usb.c	(revision 175)
+++ trunk/kitgen/usb.c	(revision 175)
@@ -0,0 +1,1266 @@
+
+/*
+  Copyright (c) 2009, Pavel Demin
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms,
+  with or without modification, are permitted
+  provided that the following conditions are met:
+
+      * Redistributions of source code must retain
+        the above copyright notice, this list of conditions
+        and the following disclaimer.
+      * Redistributions in binary form must reproduce
+        the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation
+        and/or other materials provided with the distribution.
+      * Neither the name of the SRMlite nor the names of its
+        contributors may be used to endorse or promote products
+        derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <tcl.h>
+
+#include <stdint.h>
+
+#include <usb.h>
+#include <blt.h>
+
+/* ----------------------------------------------------------------- */
+
+typedef struct UsbDevice {
+  Tcl_Command token;
+  usb_dev_handle *device;
+} UsbDevice;
+
+/* ----------------------------------------------------------------- */
+
+static void
+UsbDeviceDestroy(ClientData clientData)
+{
+  UsbDevice *statePtr = (UsbDevice *) clientData;
+
+  if(statePtr->device != NULL)
+  {
+    usb_close(statePtr->device);
+  }
+
+  ckfree((char *)statePtr);
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbReadRawObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST obj)
+{
+  unsigned char *data;
+  int read_size, size;
+
+  Tcl_Obj *result;
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, obj, &size))
+  {
+    Tcl_AppendResult(interp, "Parameter size is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  data = ckalloc((unsigned int)(size + 2));
+  read_size = usb_bulk_read(statePtr->device, 0x86, data, size + 2, 1000);
+
+  if(read_size < 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+
+  if(data[0] > 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Busy", NULL);
+
+    return 5;
+  }
+
+  if(read_size != size + 2)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Read less than requested", NULL);
+
+    return TCL_ERROR;
+  }
+
+  result = Tcl_NewByteArrayObj(data + 2, size);
+
+  ckfree(data);
+
+  Tcl_SetObjResult(interp, result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbReadHexObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST objv[])
+{
+  unsigned char *buffer;
+  unsigned char *data;
+  int word_size, char_size, data_size, read_size, i, j, pos;
+
+  unsigned char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+  Tcl_Obj *result;
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[0], &word_size))
+  {
+    Tcl_AppendResult(interp, "Parameter width is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &data_size))
+  {
+    Tcl_AppendResult(interp, "Parameter size is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  char_size = 3 + 2*word_size;
+
+  data = ckalloc((unsigned int)(data_size*word_size + 2));
+  buffer = ckalloc((unsigned int)(data_size*char_size));
+
+  read_size = usb_bulk_read(statePtr->device, 0x86, data, data_size*word_size + 2, 1000);
+
+  if(read_size < 0)
+  {
+    ckfree(buffer);
+    ckfree(data);
+
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+
+  if(data[0] > 0)
+  {
+    ckfree(buffer);
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Busy", NULL);
+
+    return 5;
+  }
+
+  if(read_size != data_size*word_size + 2)
+  {
+    ckfree(buffer);
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Read less than requested", NULL);
+
+    return TCL_ERROR;
+  }
+
+  for(i = 0; i < data_size; ++i)
+  {
+    buffer[i*char_size + 0] = '0';
+    buffer[i*char_size + 1] = 'x';
+    for(j = 1; j <= word_size; ++j)
+    {
+      pos = 2 + i*word_size + word_size - j;
+      buffer[i*char_size + 0 + 2*j] = hexval[(data[pos] >> 4) & 0x0F];
+      buffer[i*char_size + 1 + 2*j] = hexval[(data[pos]) & 0x0F];
+    }
+    buffer[i*char_size + 2*word_size + 2] = ' ';
+  }
+
+  result = Tcl_NewStringObj(buffer, data_size*char_size);
+
+  ckfree(buffer);
+  ckfree(data);
+
+  Tcl_SetObjResult(interp, result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbReadBltObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int word_size, data_size, read_size, i, j, pos;
+  unsigned long long int value;
+
+  char *name;
+  Blt_Vector *vec;
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[0], &word_size))
+  {
+    Tcl_AppendResult(interp, "Parameter width is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &data_size))
+  {
+    Tcl_AppendResult(interp, "Parameter size is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  name =  Tcl_GetString(objv[2]);
+  if(TCL_OK != Blt_GetVector(interp, name, &vec))
+  {
+    Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+    return TCL_ERROR;
+  }
+
+  if(Blt_VecSize(vec) < data_size)
+  {
+    Tcl_AppendResult(interp, "BLT vector size is less than the data size", NULL);
+    return TCL_ERROR;
+  }
+
+  data = ckalloc((unsigned int)(data_size*word_size + 2));
+
+  read_size = usb_bulk_read(statePtr->device, 0x86, data, data_size*word_size + 2, 1000);
+
+  if(read_size < 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+
+  if(data[0] > 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Busy", NULL);
+
+    return 5;
+  }
+
+  if(read_size != data_size*word_size + 2)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Read less than requested", NULL);
+
+    return TCL_ERROR;
+  }
+
+  for(i = 0; i < data_size; ++i)
+  {
+    value = 0;
+    for(j = 1; j <= word_size; ++j)
+    {
+      pos = 2 + i*word_size + word_size - j;
+      value = (value << 8) | data[pos];
+    }
+    Blt_VecData(vec)[i] = (double)value;
+  }
+
+  ckfree(data);
+
+  Blt_ResetVector(vec, Blt_VecData(vec), data_size, Blt_VecSize(vec), TCL_STATIC);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbReadOscObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int data_size, read_size, i, j, pos, value;
+
+  Tcl_DictSearch search;
+  Tcl_Obj *key, *object;
+  int done;
+
+  char *name;
+  Blt_Vector *vec[15];
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[0], &data_size))
+  {
+    Tcl_AppendResult(interp, "Parameter size is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_DictObjFirst(interp, objv[1], &search, &key, &object, &done))
+  {
+    Tcl_AppendResult(interp, "Cannot read dict variable", name, NULL);
+    return TCL_ERROR;
+  }
+
+  for(i = 0; (i < 15) && (!done) ; ++i, Tcl_DictObjNext(&search, &key, &object, &done))
+  {
+    name =  Tcl_GetString(object);
+    if(TCL_OK != Blt_GetVector(interp, name, &vec[i]))
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+      return TCL_ERROR;
+    }
+    if(Blt_VecSize(vec[i]) < data_size)
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "BLT vector size is less than the data size", NULL);
+      return TCL_ERROR;
+    }
+  }
+  Tcl_DictObjDone(&search);
+
+  data = ckalloc((unsigned int)(data_size * 8 + 2));
+
+  read_size = usb_bulk_read(statePtr->device, 0x86, data, data_size * 8 + 2, 1000);
+
+  if(read_size < 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+
+  if(data[0] > 0)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Busy", NULL);
+
+    return 5;
+  }
+
+  if(read_size != data_size * 8 + 2)
+  {
+    ckfree(data);
+
+    Tcl_AppendResult(interp, "Read less than requested", NULL);
+
+    return TCL_ERROR;
+  }
+
+  for(i = 0; i < data_size; ++i)
+  {
+    pos = 2 + i * 8;
+
+    value = data[pos + 1] & 0x0F;
+    value = (value << 8) | data[pos];
+    Blt_VecData(vec[0])[i] = (double)value;
+
+    value = data[pos + 2];
+    value = (value << 4) | (data[pos + 1] >> 4);
+    Blt_VecData(vec[1])[i] = (double)value;
+
+    value = data[pos + 4] & 0x0F;
+    value = (value << 8) | data[pos + 3];
+    Blt_VecData(vec[2])[i] = (double)value;
+
+    value = data[pos + 5];
+    value = (value << 4) | (data[pos + 4] >> 4);
+
+    for(j = 0; j < 12; ++j)
+    {
+      Blt_VecData(vec[j+3])[i] = (double)((value & 1) + (j * 2));
+      value >>= 1;
+    }
+  }
+
+  ckfree(data);
+
+  for(i = 0; i < 15; ++i)
+  {
+    Blt_ResetVector(vec[i], Blt_VecData(vec[i]), data_size, Blt_VecSize(vec[i]), TCL_STATIC);
+  }
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbWriteRawObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST obj)
+{
+  unsigned char *data;
+  int size, i;
+
+  Tcl_Obj *result;
+
+  data = Tcl_GetByteArrayFromObj(obj, &size);
+/*
+  for(i = 0; i < size; ++i) printf(" %02x", data[i]);
+  printf("\n");
+  printf("size = %d\n", size);
+  fflush(stdout);
+*/
+//  size = usb_bulk_write(statePtr->device , 0x06, data, size, 1000);
+  size = usb_bulk_write(statePtr->device , 0x08, data, size, 1000);
+
+  if(size >= 0)
+  {
+    result = Tcl_NewIntObj(size);
+
+    Tcl_SetObjResult(interp, result);
+
+    return TCL_OK;
+  }
+  else
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbControlObjCmd(UsbDevice *statePtr, Tcl_Interp *interp, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int addr, size, i;
+
+  Tcl_Obj *result;
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[0], &addr))
+  {
+    Tcl_AppendResult(interp, "Parameter addr is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  data = Tcl_GetByteArrayFromObj(objv[1], &size);
+
+  size = usb_control_msg(statePtr->device, 0x40, 0xa0, addr, 0, data, size, 1000);
+
+  if(size >= 0)
+  {
+    result = Tcl_NewIntObj(size);
+
+    Tcl_SetObjResult(interp, result);
+
+    return TCL_OK;
+  }
+  else
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+
+    return TCL_ERROR;
+  }
+}
+
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbDeviceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  char *option;
+
+  UsbDevice *statePtr = (UsbDevice *) clientData;
+
+  if(objc < 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "command ?arg?");
+    return TCL_ERROR;
+  }
+
+  option = Tcl_GetStringFromObj(objv[1], NULL);
+
+  if(strcmp(option, "readRaw") == 0)
+  {
+    if(objc != 3)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "readRaw size");
+      return TCL_ERROR;
+    }
+    return UsbReadRawObjCmd(statePtr, interp, objv[2]);
+  }
+  else if(strcmp(option, "readHex") == 0)
+  {
+    if(objc != 4)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "readHex width size");
+      return TCL_ERROR;
+    }
+    return UsbReadHexObjCmd(statePtr, interp, objv + 2);
+  }
+  else if(strcmp(option, "readBlt") == 0)
+  {
+    if(objc != 5)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "readBlt width size vector");
+      return TCL_ERROR;
+    }
+    return UsbReadBltObjCmd(statePtr, interp, objv + 2);
+  }
+  else if(strcmp(option, "readOsc") == 0)
+  {
+    if(objc != 4)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "readOsc size dict");
+      return TCL_ERROR;
+    }
+    return UsbReadOscObjCmd(statePtr, interp, objv + 2);
+  }
+  else if(strcmp(option, "writeRaw") == 0)
+  {
+    if(objc != 3)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "writeRaw data");
+      return TCL_ERROR;
+    }
+    return UsbWriteRawObjCmd(statePtr, interp, objv[2]);
+  }
+  else if(strcmp(option, "control") == 0)
+  {
+    if(objc != 4)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "control addr data");
+      return TCL_ERROR;
+    }
+    return UsbControlObjCmd(statePtr, interp, objv + 2);
+  }
+  else if(strcmp(option, "disconnect") == 0)
+  {
+    if(objc != 2)
+    {
+      Tcl_WrongNumArgs(interp, 1, objv, "disconnect");
+      return TCL_ERROR;
+    }
+    Tcl_DeleteCommandFromToken(interp, statePtr->token);
+    return TCL_OK;
+  }
+
+  Tcl_AppendResult(interp, "bad option \"", option,
+    "\": must be read, read8, read16, read32, write, control, convert or disconnect", NULL);
+  return TCL_ERROR;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConnectObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  char cmdName[256];
+  Tcl_CmdInfo cmdInfo;
+  int cmdCounter;
+
+  int idVendor, idProduct;
+  int bConfigurationValue, bInterfaceNumber, bAlternateSetting;
+
+  UsbDevice *statePtr = (UsbDevice *) clientData;
+
+  struct usb_bus *bus;
+  struct usb_device *dev;
+  struct usb_device *current_device;
+  struct usb_dev_handle *device_handle;
+
+  int rc;
+
+  if(objc != 6)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "idVendor idProduct bConfigurationValue bInterfaceNumber bAlternateSetting");
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &idVendor))
+  {
+    Tcl_AppendResult(interp, "Parameter idVendor is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[2], &idProduct))
+  {
+    Tcl_AppendResult(interp, "Parameter idProduct is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &bConfigurationValue))
+  {
+    Tcl_AppendResult(interp, "Parameter bConfigurationValue is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[4], &bInterfaceNumber))
+  {
+    Tcl_AppendResult(interp, "Parameter bInterfaceNumber is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[5], &bAlternateSetting))
+  {
+    Tcl_AppendResult(interp, "Parameter bAlternateSetting is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  usb_find_busses();
+  usb_find_devices();
+
+  current_device = NULL;
+  for(bus = usb_get_busses(); bus && current_device == NULL; bus = bus->next)
+  {
+    for(dev = bus->devices; dev && current_device == NULL; dev = dev->next)
+    {
+   		if((dev->descriptor.idVendor == idVendor) &&
+         (dev->descriptor.idProduct == idProduct)) {
+          current_device = dev;
+      }
+    }
+  }
+  
+  if(current_device == NULL)
+  {
+    Tcl_AppendResult(interp, "No CY7C68013 device present", NULL);
+    return TCL_ERROR;
+  }
+
+  device_handle = usb_open(current_device);
+  if(device_handle == NULL)
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+    return TCL_ERROR;
+  }
+
+  rc = usb_set_configuration(device_handle, bConfigurationValue);
+  if(rc != 0)
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+    return TCL_ERROR;
+  }
+
+  rc = usb_claim_interface(device_handle, bInterfaceNumber);
+  if(rc != 0)
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+    return TCL_ERROR;
+  }
+
+  rc = usb_set_altinterface(device_handle, bAlternateSetting);
+  if(rc != 0)
+  {
+    Tcl_AppendResult(interp, usb_strerror(), NULL);
+    return TCL_ERROR;
+  }
+
+  statePtr = (UsbDevice *) ckalloc((unsigned int) sizeof(UsbDevice));
+  memset(statePtr, 0, sizeof(UsbDevice));
+
+  statePtr->device = device_handle;
+
+  cmdCounter = 0;
+  do {
+    sprintf(cmdName, "::usb::device_%d_%d_%d", idVendor, idProduct, cmdCounter);
+    cmdCounter++;
+  } while(Tcl_GetCommandInfo(interp, cmdName, &cmdInfo));
+
+  statePtr->token = Tcl_CreateObjCommand(interp, cmdName, UsbDeviceObjCmd,
+    (ClientData) statePtr, UsbDeviceDestroy);
+
+  Tcl_SetResult(interp, cmdName, TCL_VOLATILE);
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConvertObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  unsigned char *buffer, tmp[3];
+  unsigned char *data;
+  int length, size, i;
+
+  Tcl_Obj *result;
+
+  if(objc != 2)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data");
+		return TCL_ERROR;
+  }
+
+  data = Tcl_GetStringFromObj(objv[1], &size);
+
+  buffer = ckalloc((unsigned int) (size/2));
+
+  tmp[2] = 0;
+  length = 0;
+
+  for(i = 1; i < size; i += 2)
+  {
+    tmp[0] = data[i - 1];
+    tmp[1] = data[i];
+  	buffer[length++] = strtoul(tmp, 0, 16);
+  }
+
+  result = Tcl_NewByteArrayObj(buffer, length);
+
+  ckfree(buffer);
+
+  Tcl_SetObjResult(interp, result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConvertBltObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int size, width, length, i, j, pos, value;
+
+  char *name;
+  Blt_Vector *vec;
+
+  if(objc != 4)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data width vector");
+		return TCL_ERROR;
+  }
+
+  data = Tcl_GetByteArrayFromObj(objv[1], &size);
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[2], &width))
+  {
+    Tcl_AppendResult(interp, "Parameter width is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  length = size / width;
+
+  name =  Tcl_GetString(objv[3]);
+  if(TCL_OK != Blt_GetVector(interp, name, &vec))
+  {
+    Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+    return TCL_ERROR;
+  }
+
+  if(Blt_VecSize(vec) < length)
+  {
+    Tcl_AppendResult(interp, "BLT vector size is less than the data length", NULL);
+    return TCL_ERROR;
+  }
+
+  for(i = 0; i < length; ++i)
+  {
+    value = 0;
+    for(j = 1; j <= width; ++j)
+    {
+      pos = i*width + width - j;
+      value = (value << 8) | data[pos];
+    }
+    Blt_VecData(vec)[i] = (double)value;
+  }
+
+  Blt_ResetVector(vec, Blt_VecData(vec), length, Blt_VecSize(vec), TCL_STATIC);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConvertEptObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int size, length, i, j, pos, value;
+
+  Tcl_DictSearch search;
+  Tcl_Obj *key, *object;
+  int done;
+
+  char *name;
+  Blt_Vector *vec[16];
+
+  if(objc != 3)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data dict");
+		return TCL_ERROR;
+  }
+
+  data = Tcl_GetByteArrayFromObj(objv[1], &size);
+  length = size / 8;
+
+  if(TCL_OK != Tcl_DictObjFirst(interp, objv[2], &search, &key, &object, &done))
+  {
+    Tcl_AppendResult(interp, "Cannot read dict variable", name, NULL);
+    return TCL_ERROR;
+  }
+
+  for(i = 0; (i < 16) && (!done) ; ++i, Tcl_DictObjNext(&search, &key, &object, &done))
+  {
+    name =  Tcl_GetString(object);
+    if(TCL_OK != Blt_GetVector(interp, name, &vec[i]))
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+      return TCL_ERROR;
+    }
+    if(Blt_VecSize(vec[i]) < length)
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "BLT vector size is less than the data size", NULL);
+      return TCL_ERROR;
+    }
+  }
+  Tcl_DictObjDone(&search);
+
+  for(i = 0; i < length; ++i)
+  {
+    pos = i * 8;
+
+    value = data[pos + 1] & 0x0F;
+    value = (value << 8) | data[pos];
+    Blt_VecData(vec[0])[i] = (double)value;
+
+    value = data[pos + 2];
+    value = (value << 4) | (data[pos + 1] >> 4);
+    Blt_VecData(vec[1])[i] = (double)value;
+
+    value = data[pos + 4] & 0x0F;
+    value = (value << 8) | data[pos + 3];
+    Blt_VecData(vec[2])[i] = (double)value;
+
+    value = data[pos + 5];
+    value = (value << 4) | (data[pos + 4] >> 4);
+    Blt_VecData(vec[3])[i] = (double)value;
+
+    value = data[pos + 7] & 0x0F;
+    value = (value << 8) | data[pos + 6];
+
+    for(j = 0; j < 12; ++j)
+    {
+      Blt_VecData(vec[j+4])[i] = (double)((value & 1) + (j * 2));
+      value >>= 1;
+    }
+  }
+
+  for(i = 0; i < 16; ++i)
+  {
+    Blt_ResetVector(vec[i], Blt_VecData(vec[i]), length, Blt_VecSize(vec[i]), TCL_STATIC);
+  }
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConvertOscObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int size, length, i, j, pos, value;
+
+  Tcl_DictSearch search;
+  Tcl_Obj *key, *object;
+  int done;
+
+  char *name;
+  Blt_Vector *vec[16];
+
+  if(objc != 3)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data dict");
+		return TCL_ERROR;
+  }
+
+  data = Tcl_GetByteArrayFromObj(objv[1], &size);
+  length = size / 8;
+
+  if(TCL_OK != Tcl_DictObjFirst(interp, objv[2], &search, &key, &object, &done))
+  {
+    Tcl_AppendResult(interp, "Cannot read dict variable", name, NULL);
+    return TCL_ERROR;
+  }
+
+  for(i = 0; (i < 16) && (!done) ; ++i, Tcl_DictObjNext(&search, &key, &object, &done))
+  {
+    name =  Tcl_GetString(object);
+    if(TCL_OK != Blt_GetVector(interp, name, &vec[i]))
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+      return TCL_ERROR;
+    }
+    if(Blt_VecSize(vec[i]) < length)
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "BLT vector size is less than the data size", NULL);
+      return TCL_ERROR;
+    }
+  }
+  Tcl_DictObjDone(&search);
+
+  for(i = 0; i < length; ++i)
+  {
+    pos = i * 8;
+
+    value = data[pos + 1] & 0x0F;
+    value = (value << 8) | data[pos];
+    Blt_VecData(vec[0])[i] = (double)value;
+
+    value = data[pos + 2];
+    value = (value << 4) | (data[pos + 1] >> 4);
+    Blt_VecData(vec[1])[i] = (double)value;
+
+    value = data[pos + 4] & 0x0F;
+    value = (value << 8) | data[pos + 3];
+    Blt_VecData(vec[2])[i] = (double)value;
+
+    value = data[pos + 5];
+    value = (value << 4) | (data[pos + 4] >> 4);
+    Blt_VecData(vec[3])[i] = (double)value;
+
+    value = data[pos + 7] & 0x0F;
+    value = (value << 8) | data[pos + 6];
+    Blt_VecData(vec[4])[i] = (double)value;
+
+    value = data[pos + 7] >> 4;
+
+    for(j = 0; j < 4; ++j)
+    {
+      Blt_VecData(vec[j+5])[i] = (double)((value & 1) << 8);
+      value >>= 1;
+    }
+  }
+
+  for(i = 0; i < 9; ++i)
+  {
+    Blt_ResetVector(vec[i], Blt_VecData(vec[i]), length, Blt_VecSize(vec[i]), TCL_STATIC);
+  }
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbConvertOsc16ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  unsigned char *data;
+  int size, length, i, j, pos, value;
+
+  Tcl_DictSearch search;
+  Tcl_Obj *key, *object;
+  int done;
+
+  char *name;
+  Blt_Vector *vec[18];
+
+  if(objc != 3)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "data dict");
+		return TCL_ERROR;
+  }
+
+  data = Tcl_GetByteArrayFromObj(objv[1], &size);
+  length = size / 16;
+
+  if(TCL_OK != Tcl_DictObjFirst(interp, objv[2], &search, &key, &object, &done))
+  {
+    Tcl_AppendResult(interp, "Cannot read dict variable", name, NULL);
+    return TCL_ERROR;
+  }
+
+  for(i = 0; (i < 18) && (!done) ; ++i, Tcl_DictObjNext(&search, &key, &object, &done))
+  {
+    name =  Tcl_GetString(object);
+    if(TCL_OK != Blt_GetVector(interp, name, &vec[i]))
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+      return TCL_ERROR;
+    }
+    if(Blt_VecSize(vec[i]) < length)
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "BLT vector size is less than the data size", NULL);
+      return TCL_ERROR;
+    }
+  }
+  Tcl_DictObjDone(&search);
+
+  for(i = 0; i < length; ++i)
+  {
+    pos = i * 16;
+
+    value = data[pos + 1] & 0x0F;
+    value = (value << 8) | data[pos];
+    Blt_VecData(vec[0])[i] = (double)value;
+
+    value = data[pos + 2];
+    value = (value << 4) | (data[pos + 1] >> 4);
+    Blt_VecData(vec[1])[i] = (double)value;
+
+    value = data[pos + 4] & 0x0F;
+    value = (value << 8) | data[pos + 3];
+    Blt_VecData(vec[2])[i] = (double)value;
+
+    value = data[pos + 5];
+    value = (value << 4) | (data[pos + 4] >> 4);
+    Blt_VecData(vec[3])[i] = (double)value;
+
+    value = data[pos + 7] & 0x0F;
+    value = (value << 8) | data[pos + 6];
+    Blt_VecData(vec[4])[i] = (double)value;
+
+    value = data[pos + 8];
+    value = (value << 4) | (data[pos + 7] >> 4);
+    Blt_VecData(vec[5])[i] = (double)value;
+
+    value = data[pos + 10] & 0x0F;
+    value = (value << 8) | data[pos + 9];
+    Blt_VecData(vec[6])[i] = (double)value;
+
+    value = data[pos + 11];
+    value = (value << 4) | (data[pos + 10] >> 4);
+    Blt_VecData(vec[7])[i] = (double)value;
+
+    value = data[pos + 13] & 0x0F;
+    value = (value << 8) | data[pos + 12];
+    Blt_VecData(vec[8])[i] = (double)value;
+
+    value = data[pos + 14];
+    value = (value << 4) | (data[pos + 13] >> 4);
+    Blt_VecData(vec[9])[i] = (double)value;
+
+    value = data[pos + 15];
+
+    for(j = 0; j < 8; ++j)
+    {
+      Blt_VecData(vec[j+10])[i] = (double)((value & 1) << 8);
+      value >>= 1;
+    }
+  }
+
+  for(i = 0; i < 18; ++i)
+  {
+    Blt_ResetVector(vec[i], Blt_VecData(vec[i]), length, Blt_VecSize(vec[i]), TCL_STATIC);
+  }
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbFormatOscCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  char *name;
+  int first, last, done, max, i, j;
+
+  Tcl_DictSearch search;
+  Tcl_Obj *key, *object, *result, *eol;
+
+  Blt_Vector *vec[32];
+
+  if(objc != 4)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "dict first last");
+		return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_DictObjFirst(interp, objv[1], &search, &key, &object, &done))
+  {
+    Tcl_AppendResult(interp, "Cannot read dict variable", name, NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[2], &first))
+  {
+    Tcl_AppendResult(interp, "Parameter first is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &last))
+  {
+    Tcl_AppendResult(interp, "Parameter last is not an integer", NULL);
+    return TCL_ERROR;
+  }
+  
+  if(last < first)
+  {
+    return TCL_OK;
+  }
+
+  for(i = 0; (i < 32) && (!done); ++i, Tcl_DictObjNext(&search, &key, &object, &done))
+  {
+    name =  Tcl_GetString(object);
+    if(TCL_OK != Blt_GetVector(interp, name, &vec[i]))
+    {
+      Tcl_DictObjDone(&search);
+      Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+      return TCL_ERROR;
+    }
+    if(Blt_VecLength(vec[i]) <= last)
+    {
+      last = Blt_VecLength(vec[i]) - 1;
+    }
+    max = i;
+  }
+  Tcl_DictObjDone(&search);
+
+  if(first < 0)
+  {
+    first = 0;
+  }
+
+  result = Tcl_NewObj();
+  eol = Tcl_NewStringObj("\n", -1);
+
+  Tcl_IncrRefCount(result);
+  Tcl_IncrRefCount(eol);
+
+  for(i = first; i <= last; ++i)
+  {
+    for(j = 0; j <= max; ++j)
+    {
+      if(j > 0)
+      {
+        Tcl_AppendPrintfToObj(result, "\t%g", Blt_VecData(vec[j])[i]);
+      }
+      else
+      {
+        Tcl_AppendPrintfToObj(result, "%g", Blt_VecData(vec[j])[i]);
+      }
+    }
+    Tcl_AppendObjToObj(result, eol);
+  }
+
+  Tcl_SetObjResult(interp, result);
+
+  Tcl_DecrRefCount(eol);
+  Tcl_DecrRefCount(result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+static int
+UsbIntegrateBltObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+  int length, i, xmin, xmax, flag;
+  double entr, mean;
+
+  char *name;
+  Blt_Vector *vec;
+
+  Tcl_Obj *result;
+
+  if(objc != 5)
+  {
+    Tcl_WrongNumArgs(interp, 1, objv, "vector xmin xmax flag");
+		return TCL_ERROR;
+  }
+
+  name =  Tcl_GetString(objv[1]);
+  if(TCL_OK != Blt_GetVector(interp, name, &vec))
+  {
+    Tcl_AppendResult(interp, "Cannot find BLT vector", name, NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[2], &xmin))
+  {
+    Tcl_AppendResult(interp, "Parameter xmin is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[3], &xmax))
+  {
+    Tcl_AppendResult(interp, "Parameter xmax is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  if(TCL_OK != Tcl_GetIntFromObj(interp, objv[4], &flag))
+  {
+    Tcl_AppendResult(interp, "Parameter flag is not an integer", NULL);
+    return TCL_ERROR;
+  }
+
+  length = Blt_VecLength(vec);
+  entr = 0.0;
+  mean = 0.0;
+  
+  if(xmin < 0) xmin = 0;
+  if(xmax >= length) xmax = length - 1;
+  if(xmax < xmin) xmax = xmin;
+
+  for(i = xmin; i <= xmax; ++i)
+  {
+    entr += Blt_VecData(vec)[i];
+    mean += i * Blt_VecData(vec)[i];
+  }
+
+  if(flag)
+  {
+    result = Tcl_NewDoubleObj(entr > 0.0 ? mean / entr : 0.0);
+  }
+  else
+  {
+    result = Tcl_NewDoubleObj(entr);
+  }
+
+  Tcl_SetObjResult(interp, result);
+
+  return TCL_OK;
+}
+
+/* ----------------------------------------------------------------- */
+
+int
+Usb_Init(Tcl_Interp *interp)
+{
+  usb_init();
+
+  Tcl_CreateObjCommand(interp, "usb::connect", UsbConnectObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::convert", UsbConvertObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::convertBlt", UsbConvertBltObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::convertEpt", UsbConvertOscObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::convertOsc", UsbConvertOscObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::convertOsc16", UsbConvertOsc16ObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+	Tcl_CreateObjCommand(interp, "usb::formatOsc", UsbFormatOscCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  Tcl_CreateObjCommand(interp, "usb::integrateBlt", UsbIntegrateBltObjCmd,
+    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+  return Tcl_PkgProvide(interp, "usb", "0.1");
+}
Index: trunk/kitgen/zlib.c
===================================================================
--- trunk/kitgen/zlib.c	(revision 175)
+++ trunk/kitgen/zlib.c	(revision 175)
@@ -0,0 +1,221 @@
+/* Written by Jean-Claude Wippler, as part of Tclkit.
+ * March 2003 - placed in the public domain by the author.
+ *
+ * Interface to the "zlib" compression library
+ */
+
+#include "zlib.h"
+#include <tcl.h>
+
+typedef struct {
+  z_stream stream;
+  Tcl_Obj *indata;
+} zlibstream;
+
+static int
+zstreamincmd(ClientData cd, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[])
+{
+  zlibstream *zp = (zlibstream*) cd;
+  int count = 0;
+  int e, index;
+  Tcl_Obj *obj;
+
+  static CONST84 char* cmds[] = { "fill", "drain", NULL, };
+
+  if (objc < 2 || objc > 3) {
+    Tcl_WrongNumArgs(ip, 2, objv, "fill|drain data");
+    return TCL_ERROR;
+  }
+
+  if (Tcl_GetIndexFromObj(ip, objv[1], cmds, "option", 0, &index) != TCL_OK)
+    return TCL_ERROR;
+
+  switch (index) {
+
+    case 0: /* fill ?data? */
+      if (objc >= 3) {
+      	Tcl_IncrRefCount(objv[2]);
+      	Tcl_DecrRefCount(zp->indata);
+      	zp->indata = objv[2];
+      	zp->stream.next_in = Tcl_GetByteArrayFromObj(zp->indata,
+						  (int*) &zp->stream.avail_in);
+      }
+      Tcl_SetObjResult(ip, Tcl_NewIntObj(zp->stream.avail_in));
+      break;
+
+    case 1: /* drain count */
+      if (objc != 3) {
+      	Tcl_WrongNumArgs(ip, 2, objv, "count");
+      	return TCL_ERROR;
+      }
+      if (Tcl_GetIntFromObj(ip, objv[2], &count) != TCL_OK)
+      	return TCL_ERROR;
+      obj = Tcl_GetObjResult(ip);
+      Tcl_SetByteArrayLength(obj, count);
+      zp->stream.next_out = Tcl_GetByteArrayFromObj(obj,
+						  (int*) &zp->stream.avail_out);
+      e = inflate(&zp->stream, Z_NO_FLUSH);
+      if (e != 0 && e != Z_STREAM_END) {
+      	Tcl_SetResult(ip, (char*) zError(e), TCL_STATIC);
+      	return TCL_ERROR;
+      }
+      Tcl_SetByteArrayLength(obj, count - zp->stream.avail_out);
+      break;
+  }
+  return TCL_OK;
+}
+
+void zstreamdelproc(ClientData cd)
+{
+  zlibstream *zp = (zlibstream*) cd;
+  inflateEnd(&zp->stream);
+  Tcl_DecrRefCount(zp->indata);
+  Tcl_Free((void*) zp);
+}
+
+static int
+ZlibCmd(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[])
+{
+  int e = TCL_OK, index, dlen, wbits = -MAX_WBITS;
+  long flag;
+  Byte *data;
+  z_stream stream;
+  Tcl_Obj *obj = Tcl_GetObjResult(ip);
+
+  static CONST84 char* cmds[] = {
+    "adler32", "crc32", "compress", "deflate", "decompress", "inflate", 
+    "sdecompress", "sinflate", NULL,
+  };
+
+  if (objc < 3 || objc > 4) {
+    Tcl_WrongNumArgs(ip, 1, objv, "option data ?...?");
+    return TCL_ERROR;
+  }
+
+  if (Tcl_GetIndexFromObj(ip, objv[1], cmds, "option", 0, &index) != TCL_OK ||
+      objc > 3 && Tcl_GetLongFromObj(ip, objv[3], &flag) != TCL_OK)
+    return TCL_ERROR;
+
+  data = Tcl_GetByteArrayFromObj(objv[2], &dlen);
+
+  switch (index) {
+
+    case 0: /* adler32 str ?start? -> checksum */
+      if (objc < 4)
+      	flag = (long) adler32(0, 0, 0);
+      Tcl_SetLongObj(obj, (long) adler32((uLong) flag, data, dlen));
+      return TCL_OK;
+
+    case 1: /* crc32 str ?start? -> checksum */
+      if (objc < 4)
+      	flag = (long) crc32(0, 0, 0);
+      Tcl_SetLongObj(obj, (long) crc32((uLong) flag, data, dlen));
+      return TCL_OK;
+      
+    case 2: /* compress data ?level? -> data */
+      wbits = MAX_WBITS;
+    case 3: /* deflate data ?level? -> data */
+      if (objc < 4)
+      	flag = Z_DEFAULT_COMPRESSION;
+
+      stream.avail_in = (uInt) dlen;
+      stream.next_in = data;
+
+      stream.avail_out = (uInt) dlen + dlen / 1000 + 12;
+      Tcl_SetByteArrayLength(obj, stream.avail_out);
+      stream.next_out = Tcl_GetByteArrayFromObj(obj, NULL);
+
+      stream.zalloc = 0;
+      stream.zfree = 0;
+      stream.opaque = 0;
+
+      e = deflateInit2(&stream, (int) flag, Z_DEFLATED, wbits,
+			      MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+      if (e != Z_OK)
+      	break;
+
+      e = deflate(&stream, Z_FINISH);
+      if (e != Z_STREAM_END) {
+      	deflateEnd(&stream);
+      	if (e == Z_OK) e = Z_BUF_ERROR;
+      } else
+      	e = deflateEnd(&stream);
+      break;
+      
+    case 4: /* decompress data ?bufsize? -> data */
+      wbits = MAX_WBITS;
+    case 5: /* inflate data ?bufsize? -> data */
+    {
+      if (flag < 1) {
+        Tcl_SetResult(ip, "invalid buffer size", TCL_STATIC);
+        return TCL_ERROR;
+      }
+
+      if (objc < 4)
+      	flag = 16 * 1024;
+
+      for (;;) {
+      	stream.zalloc = 0;
+      	stream.zfree = 0;
+
+      	/* +1 because ZLIB can "over-request" input (but ignore it) */
+      	stream.avail_in = (uInt) dlen +  1;
+      	stream.next_in = data;
+
+      	stream.avail_out = (uInt) flag;
+      	Tcl_SetByteArrayLength(obj, stream.avail_out);
+      	stream.next_out = Tcl_GetByteArrayFromObj(obj, NULL);
+
+      	/* Negative value suppresses ZLIB header */
+      	e = inflateInit2(&stream, wbits);
+      	if (e == Z_OK) {
+      	  e = inflate(&stream, Z_FINISH);
+      	  if (e != Z_STREAM_END) {
+      	    inflateEnd(&stream);
+      	    if (e == Z_OK) e = Z_BUF_ERROR;
+      	  } else
+      	    e = inflateEnd(&stream);
+      	}
+
+      	if (e == Z_OK || e != Z_BUF_ERROR) break;
+
+      	Tcl_SetByteArrayLength(obj, 0);
+      	flag *= 2;
+      }
+
+      break;
+    }
+      
+    case 6: /* sdecompress cmdname -> */
+      wbits = MAX_WBITS;
+    case 7: /* sinflate cmdname -> */
+    {
+      zlibstream *zp = (zlibstream*) Tcl_Alloc(sizeof (zlibstream));
+      zp->indata = Tcl_NewObj();
+      Tcl_IncrRefCount(zp->indata);
+      zp->stream.zalloc = 0;
+      zp->stream.zfree = 0;
+      zp->stream.opaque = 0;
+      zp->stream.next_in = 0;
+      zp->stream.avail_in = 0;
+      inflateInit2(&zp->stream, wbits);
+      Tcl_CreateObjCommand(ip, Tcl_GetStringFromObj(objv[2], 0), zstreamincmd,
+      				(ClientData) zp, zstreamdelproc);
+      return TCL_OK;
+    }
+  }
+
+  if (e != Z_OK) {
+    Tcl_SetResult(ip, (char*) zError(e), TCL_STATIC);
+    return TCL_ERROR;
+  }
+
+  Tcl_SetByteArrayLength(obj, stream.total_out);
+  return TCL_OK;
+}
+
+int Zlib_Init(Tcl_Interp *interp)
+{
+    Tcl_CreateObjCommand(interp, "zlib", ZlibCmd, 0, 0);
+    return Tcl_PkgProvide( interp, "zlib", "1.1");
+}
Index: trunk/kitgen/zvfs.c
===================================================================
--- trunk/kitgen/zvfs.c	(revision 175)
+++ trunk/kitgen/zvfs.c	(revision 175)
@@ -0,0 +1,1962 @@
+/*
+** By the overt act of typing this comment, the author of this code
+** releases it into the public domain.  No claim of copyright is made.
+** In place of a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+***************************************************************************
+** A ZIP archive virtual filesystem for Tcl.
+**
+** This package of routines enables Tcl to use a Zip file as
+** a virtual file system.  Each of the content files of the Zip
+** archive appears as a real file to Tcl.
+**
+** Converted to Tcl VFS by Peter MacDonald
+**   peter@pdqi.com
+**   http://pdqi.com
+**
+**
+** Modified by Damon Courtney to complete the VFS work.
+**
+** @(#) $Id: zvfs.c,v 1.1.1.1 2002/01/27 17:44:02 cvs Exp $
+*/
+
+#include "tclInt.h"
+#include "tclPort.h"
+#include <zlib.h>
+
+/*
+ * Size of the decompression input buffer
+ */
+#define COMPR_BUF_SIZE   8192
+
+#ifdef __WIN32__
+#define NOCASE_PATHS 1
+#else
+#define NOCASE_PATHS 0
+#endif
+
+/*
+ * All static variables are collected into a structure named "local".
+ * That way, it is clear in the code when we are using a static
+ * variable because its name begins with "local.".
+ */
+static struct {
+    Tcl_HashTable fileHash;     /* One entry for each file in the ZVFS.  The
+                                 * The key is the virtual filename.  The data
+                                 * an an instance of the ZvfsFile structure.
+                                 */
+    Tcl_HashTable archiveHash;  /* One entry for each archive.
+                                 * Key is the name.
+                                 * Data is the ZvfsArchive structure.
+                                 */
+    int isInit;                 /* True after initialization */
+} local;
+
+/*
+ * Each ZIP archive file that is mounted is recorded as an instance
+ * of this structure
+ */
+typedef struct ZvfsArchive {
+    int refCount;
+    Tcl_Obj *zName;         /* Name of the archive */
+    Tcl_Obj *zMountPoint;   /* Where this archive is mounted */
+} ZvfsArchive;
+
+/*
+ * Particulars about each virtual file are recorded in an instance
+ * of the following structure.
+ */
+typedef struct ZvfsFile {
+    int refCount;             /* Reference count */
+    Tcl_Obj *zName;           /* The full pathname of the virtual file */
+    ZvfsArchive *pArchive;    /* The ZIP archive holding this file data */
+    int iOffset;              /* Offset into the ZIP archive of the data */
+    int nByte;                /* Uncompressed size of the virtual file */
+    int nByteCompr;           /* Compressed size of the virtual file */
+    int isdir;		      /* Set to 1 if directory */
+    int timestamp;            /* Modification time */
+    int iCRC;                 /* Cyclic Redundancy Check of the data */
+    struct ZvfsFile *parent;  /* Parent directory. */
+    Tcl_HashTable children;   /* For directory entries, a hash table of
+                               * all of the files in the directory.
+                               */
+} ZvfsFile;
+
+/*
+ * Whenever a ZVFS file is opened, an instance of this structure is
+ * attached to the open channel where it will be available to the
+ * ZVFS I/O routines below.  All state information about an open
+ * ZVFS file is held in this structure.
+ */
+typedef struct ZvfsChannelInfo {
+    unsigned int nByte;       /* number of bytes of read uncompressed data */
+    unsigned int nByteCompr;  /* number of bytes of unread compressed data */
+    unsigned int nData;       /* total number of bytes of compressed data */
+    int readSoFar;            /* Number of bytes read so far */
+    long startOfData;         /* File position of data in ZIP archive */
+    int isCompressed;         /* True data is compressed */
+    Tcl_Channel chan;         /* Open to the archive file */
+    unsigned char *zBuf;      /* buffer used by the decompressor */
+    z_stream stream;          /* state of the decompressor */
+} ZvfsChannelInfo;
+
+/* The attributes defined for each file in the archive.
+ * These are accessed via the 'file attributes' command in Tcl.
+ */
+static CONST char *ZvfsAttrs[] = {
+    "-archive", "-compressedsize", "-crc", "-mount", "-offset",
+    "-uncompressedsize", (char *)NULL
+};
+
+enum {
+    ZVFS_ATTR_ARCHIVE, ZVFS_ATTR_COMPSIZE, ZVFS_ATTR_CRC,
+    ZVFS_ATTR_MOUNT, ZVFS_ATTR_OFFSET, ZVFS_ATTR_UNCOMPSIZE
+};
+
+/* Forward declarations for the callbacks to the Tcl filesystem. */
+
+static Tcl_FSPathInFilesystemProc       PathInFilesystem;
+static Tcl_FSDupInternalRepProc		DupInternalRep;
+static Tcl_FSFreeInternalRepProc	FreeInternalRep;
+static Tcl_FSInternalToNormalizedProc	InternalToNormalized;
+static Tcl_FSFilesystemPathTypeProc     FilesystemPathType;
+static Tcl_FSFilesystemSeparatorProc    FilesystemSeparator;
+static Tcl_FSStatProc                   Stat;
+static Tcl_FSAccessProc                 Access;
+static Tcl_FSOpenFileChannelProc        OpenFileChannel;
+static Tcl_FSMatchInDirectoryProc       MatchInDirectory;
+static Tcl_FSListVolumesProc            ListVolumes;
+static Tcl_FSFileAttrStringsProc        FileAttrStrings;
+static Tcl_FSFileAttrsGetProc           FileAttrsGet;
+static Tcl_FSFileAttrsSetProc           FileAttrsSet;
+static Tcl_FSChdirProc                  Chdir;
+
+static Tcl_Filesystem zvfsFilesystem = {
+    "zvfs",
+    sizeof(Tcl_Filesystem),
+    TCL_FILESYSTEM_VERSION_1,
+    &PathInFilesystem,
+    &DupInternalRep,
+    &FreeInternalRep,
+    &InternalToNormalized,
+    NULL,			/* &CreateInternalRep, */
+    NULL,                       /* &NormalizePath, */
+    &FilesystemPathType,
+    &FilesystemSeparator,
+    &Stat,
+    &Access,
+    &OpenFileChannel,
+    &MatchInDirectory,
+    NULL,                       /* &Utime, */
+    NULL,                       /* &Link, */
+    &ListVolumes,
+    &FileAttrStrings,
+    &FileAttrsGet,
+    &FileAttrsSet,
+    NULL,			/* &CreateDirectory, */
+    NULL,			/* &RemoveDirectory, */
+    NULL,			/* &DeleteFile, */
+    NULL,			/* &CopyFile, */
+    NULL,			/* &RenameFile, */
+    NULL,			/* &CopyDirectory, */
+    NULL,                       /* &Lstat, */
+    NULL,			/* &LoadFile, */
+    NULL,			/* &GetCwd, */
+    &Chdir
+};
+
+
+/*
+ * Forward declarations describing the channel type structure for
+ * opening and reading files inside of an archive.
+ */
+static Tcl_DriverCloseProc        DriverClose;
+static Tcl_DriverInputProc        DriverInput;
+static Tcl_DriverOutputProc       DriverOutput;
+static Tcl_DriverSeekProc         DriverSeek;
+static Tcl_DriverWatchProc        DriverWatch;
+static Tcl_DriverGetHandleProc    DriverGetHandle;
+
+static Tcl_ChannelType vfsChannelType = {
+    "zvfs",                /* Type name.                                    */
+    TCL_CHANNEL_VERSION_2, /* Set blocking/nonblocking behaviour. NULL'able */
+    DriverClose,           /* Close channel, clean instance data            */
+    DriverInput,           /* Handle read request                           */
+    DriverOutput,          /* Handle write request                          */
+    DriverSeek,            /* Move location of access point.      NULL'able */
+    NULL,                  /* Set options.                        NULL'able */
+    NULL,                  /* Get options.                        NULL'able */
+    DriverWatch,           /* Initialize notifier                           */
+    DriverGetHandle        /* Get OS handle from the channel.               */
+};
+
+/*
+ * Macros to read 16-bit and 32-bit big-endian integers into the
+ * native format of this local processor.  B is an array of
+ * characters and the integer begins at the N-th character of
+ * the array.
+ */
+#define INT16(B, N) (B[N] + (B[N+1]<<8))
+#define INT32(B, N) (INT16(B,N) + (B[N+2]<<16) + (B[N+3]<<24))
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DosTimeDate --
+ *
+ * 	Convert DOS date and time from a zip archive into clock seconds.
+ *
+ * Results:
+ * 	Clock seconds
+ *
+ *----------------------------------------------------------------------
+ */
+
+static time_t
+DosTimeDate( int dosDate, int dosTime )
+{
+    time_t now;
+    struct tm *tm;
+    now=time(NULL);
+    tm = localtime(&now);
+    tm->tm_year=(((dosDate&0xfe00)>>9) + 80);
+    tm->tm_mon=((dosDate&0x1e0)>>5)-1;
+    tm->tm_mday=(dosDate & 0x1f);
+    tm->tm_hour=(dosTime&0xf800)>>11;
+    tm->tm_min=(dosTime&0x7e)>>5;
+    tm->tm_sec=(dosTime&0x1f);
+    return mktime(tm);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StrDup --
+ *
+ * 	Create a copy of the given string and lower it if necessary.
+ *
+ * Results:
+ * 	Pointer to the new string.  Space to hold the returned
+ * 	string is obtained from Tcl_Alloc() and should be freed
+ * 	by the calling function.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+StrDup( char *str, int lower )
+{
+    int i, c, len;
+    char *newstr;
+
+    len = strlen(str);
+
+    newstr = Tcl_Alloc( len + 1 );
+    memcpy( newstr, str, len );
+    newstr[len] = '\0';
+
+    if( lower ) {
+        for( i = 0; (c = newstr[i]) != 0; ++i )
+        {
+            if( isupper(c) ) {
+                newstr[i] = tolower(c);
+            }
+        }
+    }
+
+    return newstr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CanonicalPath --
+ *
+ * 	Concatenate zTail onto zRoot to form a pathname.  After
+ * 	concatenation, simplify the pathname by removing ".." and
+ * 	"." directories.
+ *
+ * Results:
+ * 	Pointer to the new pathname.  Space to hold the returned
+ * 	path is obtained from Tcl_Alloc() and should be freed by
+ * 	the calling function.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+CanonicalPath( const char *zRoot, const char *zTail )
+{
+    char *zPath;
+    int i, j, c;
+    int len = strlen(zRoot) + strlen(zTail) + 2;
+
+#ifdef __WIN32__
+    if( isalpha(zTail[0]) && zTail[1]==':' ){ zTail += 2; }
+    if( zTail[0]=='\\' ){ zRoot = ""; zTail++; }
+    if( zTail[0]=='\\' ){ zRoot = "/"; zTail++; } // account for UNC style path
+#endif
+    if( zTail[0]=='/' ){ zRoot = ""; zTail++; }
+    if( zTail[0]=='/' ){ zRoot = "/"; zTail++; }  // account for UNC style path
+
+    zPath = Tcl_Alloc( len );
+    if( !zPath ) return NULL;
+
+    sprintf( zPath, "%s/%s", zRoot, zTail );
+    for( i = j = 0; (c = zPath[i]) != 0; i++ )
+    {
+#ifdef __WIN32__
+        if( c == '\\' ) {
+            c = '/';
+        }
+#endif
+        if( c == '/' ) {
+            int c2 = zPath[i+1];
+            if( c2 == '/' ) continue;
+            if( c2 == '.' ) {
+                int c3 = zPath[i+2];
+                if( c3 == '/' || c3 == 0 ) {
+                    i++;
+                    continue;
+                }
+                if( c3 == '.' && (zPath[i+3] == '.' || zPath[i+3] == 0) ) {
+                    i += 2;
+                    while( j > 0 && zPath[j-1] != '/' ) { j--; }
+                    continue;
+                }
+            }
+        }
+        zPath[j++] = c;
+    }
+
+    if( j == 0 ) {
+        zPath[j++] = '/';
+    }
+
+    zPath[j] = 0;
+
+    return zPath;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AbsolutePath --
+ *
+ * 	Construct an absolute pathname from the given pathname.  On
+ * 	Windows, all backslash (\) characters are converted to
+ * 	forward slash (/), and if NOCASE_PATHS is true, all letters
+ * 	are converted to lowercase.  The drive letter, if present, is
+ * 	preserved.
+ *
+ * Results:
+ * 	Pointer to the new pathname.  Space to hold the returned
+ * 	path is obtained from Tcl_Alloc() and should be freed by
+ * 	the calling function.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+AbsolutePath( const char *z )
+{
+    int len;
+    char *zResult;
+
+    if( *z != '/'
+#ifdef __WIN32__
+        && *z != '\\' && (!isalpha(*z) || z[1] != ':' )
+#endif
+    ) {
+        /* Case 1:  "z" is a relative path, so prepend the current
+         * working directory in order to generate an absolute path.
+         */
+        Tcl_Obj *pwd = Tcl_FSGetCwd(NULL);
+        zResult = CanonicalPath( Tcl_GetString(pwd), z );
+        Tcl_DecrRefCount(pwd);
+    } else {
+        /* Case 2:  "z" is an absolute path already, so we
+         * just need to make a copy of it.
+         */
+        zResult = StrDup( (char *)z, 0);
+    }
+
+    /* If we're on Windows, we want to convert all backslashes to
+     * forward slashes.  If NOCASE_PATHS is true, we want to also
+     * lower the alpha characters in the path.
+     */
+#if NOCASE_PATHS || defined(__WIN32__)
+    {
+        int i, c;
+        for( i = 0; (c = zResult[i]) != 0; i++ )
+        {
+#if NOCASE_PATHS
+            if( isupper(c) ) {
+                zResult[i] = tolower(c);
+            }
+#endif
+#ifdef __WIN32__
+            if( c == '\\' ) {
+                zResult[i] = '/';
+            }
+#endif
+        }
+    }
+#endif /* NOCASE_PATHS || defined(__WIN32__) */
+
+    len = strlen(zResult);
+    /* Strip the trailing / from any directory. */
+    if( zResult[len-1] == '/' ) {
+        zResult[len-1] = 0;
+    }
+
+    return zResult;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddPathToArchive --
+ *
+ * 	Add the given pathname to the given archive.  zName is usually
+ * 	the pathname pulled from the file header in a zip archive.  We
+ * 	concatenate it onto the archive's mount point to obtain a full
+ * 	path before adding it to our hash table.
+ *
+ * 	All parent directories of the given path will be created and
+ * 	added to the hash table.
+ *
+ * Results:
+ * 	Pointer to the new file structure or to the old file structure
+ * 	if it already existed.  newPath will be true if this path is
+ * 	new to this archive or false if we already had it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ZvfsFile *
+AddPathToArchive( ZvfsArchive *pArchive, char *zName, int *newPath )
+{
+    int i, len, isNew;
+    char *zFullPath, *izFullPath;
+    char *zParentPath, *izParentPath;
+    Tcl_HashEntry *pEntry;
+    Tcl_Obj *nameObj, *pathObj, *listObj;
+    ZvfsFile *pZvfs, *parent = NULL;
+
+    zFullPath = CanonicalPath( Tcl_GetString(pArchive->zMountPoint), zName );
+    izFullPath = zFullPath;
+
+    pathObj = Tcl_NewStringObj( zFullPath, -1 );
+    Tcl_IncrRefCount( pathObj );
+
+    listObj = Tcl_FSSplitPath( pathObj, &len );
+    Tcl_IncrRefCount( listObj );
+    Tcl_DecrRefCount( pathObj );
+
+    /* Walk through all the parent directories of this
+     * file and add them all to our archive.  This is
+     * because some zip files don't store directory
+     * entries in the archive, but we need to know all
+     * of the directories to create the proper filesystem.
+     */
+    for( i = 1; i < len; ++i )
+    {
+        pathObj = Tcl_FSJoinPath( listObj, i );
+
+        izParentPath = zParentPath  = Tcl_GetString(pathObj);
+#if NOCASE_PATHS
+        izParentPath = StrDup( zParentPath, 1 );
+#endif
+        pEntry = Tcl_CreateHashEntry( &local.fileHash, izParentPath, &isNew );
+#if NOCASE_PATHS
+        Tcl_Free( izParentPath );
+#endif
+
+        if( !isNew ) {
+            /* We already have this directory in our archive. */
+            parent = Tcl_GetHashValue( pEntry );
+            continue;
+        }
+
+        Tcl_ListObjIndex( NULL, listObj, i-1, &nameObj );
+        Tcl_IncrRefCount(nameObj);
+
+        /* We don't have this directory in our archive yet.  Add it. */
+        pZvfs = (ZvfsFile*)Tcl_Alloc( sizeof(*pZvfs) );
+        pZvfs->refCount   = 1;
+        pZvfs->zName      = nameObj;
+        pZvfs->pArchive   = pArchive;
+        pZvfs->isdir      = 1;
+        pZvfs->iOffset    = 0;
+        pZvfs->timestamp  = 0;
+        pZvfs->iCRC       = 0;
+        pZvfs->nByteCompr = 0;
+        pZvfs->nByte      = 0;
+        pZvfs->parent     = parent;
+        Tcl_InitHashTable( &pZvfs->children, TCL_STRING_KEYS );
+
+        Tcl_SetHashValue( pEntry, pZvfs );
+
+        if( parent ) {
+            /* Add this directory to its parent's list of children. */
+            pEntry = Tcl_CreateHashEntry(&parent->children,zParentPath,&isNew);
+            if( isNew ) {
+                Tcl_SetHashValue( pEntry, pZvfs );
+            }
+        }
+
+        parent = pZvfs;
+    }
+
+    /* Check to see if we already have this file in our archive. */
+#if NOCASE_PATHS
+    izFullPath = StrDup( zFullPath, 1 );
+#endif
+    pEntry = Tcl_CreateHashEntry(&local.fileHash, izFullPath, newPath);
+#if NOCASE_PATHS
+    Tcl_Free( izFullPath );
+#endif
+
+    if( *newPath ) {
+        /* We don't have this file in our archive.  Add it. */
+        Tcl_ListObjIndex( NULL, listObj, len-1, &nameObj );
+        Tcl_IncrRefCount(nameObj);
+
+        pZvfs = (ZvfsFile*)Tcl_Alloc( sizeof(*pZvfs) );
+        pZvfs->refCount   = 1;
+        pZvfs->zName      = nameObj;
+        pZvfs->pArchive   = pArchive;
+
+        Tcl_SetHashValue( pEntry, pZvfs );
+
+        /* Add this path to its parent's list of children. */
+        pEntry = Tcl_CreateHashEntry(&parent->children, zFullPath, &isNew);
+
+        if( isNew ) {
+            Tcl_SetHashValue( pEntry, pZvfs );
+        }
+    } else {
+        /* We already have this file.  Set the pointer and return. */
+        pZvfs = Tcl_GetHashValue( pEntry );
+    }
+
+    Tcl_DecrRefCount(listObj);
+    Tcl_Free(zFullPath);
+
+    return pZvfs;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Zvfs_Mount --
+ *
+ * 	Read a zip archive and make entries in the file hash table for
+ * 	all of the files in the archive.  If Zvfs has not been initialized,
+ * 	it will be initialized here before mounting the archive.
+ *
+ * Results:
+ * 	Standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Zvfs_Mount(
+    Tcl_Interp *interp,      /* Leave error messages in this interpreter */
+    CONST char *zArchive,    /* The ZIP archive file */
+    CONST char *zMountPoint  /* Mount contents at this directory */
+) {
+    Tcl_Channel chan = NULL;   /* Used for reading the ZIP archive file */
+    char *zArchiveName = 0;    /* A copy of zArchive */
+    char *zFullMountPoint = 0; /* Absolute path to the mount point */
+    int nFile;                 /* Number of files in the archive */
+    int iPos;                  /* Current position in the archive file */
+    int code = TCL_ERROR;      /* Return code */
+    int update = 1;            /* Whether to update the mounts */
+    ZvfsArchive *pArchive;     /* The ZIP archive being mounted */
+    Tcl_HashEntry *pEntry;     /* Hash table entry */
+    int isNew;                 /* Flag to tell use when a hash entry is new */
+    unsigned char zBuf[100];   /* Buffer to read from the ZIP archive */
+    ZvfsFile *pZvfs;           /* A new virtual file */
+    Tcl_Obj *hashKeyObj = NULL;
+    Tcl_Obj *resultObj = Tcl_GetObjResult(interp);
+    Tcl_Obj *readObj = Tcl_NewObj();
+    Tcl_IncrRefCount(readObj);
+
+    if( !local.isInit ) {
+        if( Zvfs_Init( interp ) == TCL_ERROR ) {
+            Tcl_SetStringObj( resultObj, "failed to initialize zvfs", -1 );
+            return TCL_ERROR;
+        }
+    }
+
+    /* If zArchive is NULL, set the result to a list of all
+     * mounted files.
+     */
+    if( !zArchive ) {
+        Tcl_HashSearch zSearch;
+
+        for( pEntry = Tcl_FirstHashEntry( &local.archiveHash,&zSearch );
+                pEntry; pEntry = Tcl_NextHashEntry(&zSearch) )
+        {
+            if( (pArchive = Tcl_GetHashValue(pEntry)) ) {
+                Tcl_ListObjAppendElement( interp, resultObj,
+                        Tcl_DuplicateObj(pArchive->zName) );
+            }
+        }
+        code = TCL_OK;
+        update = 0;
+        goto done;
+    }
+
+    /* If zMountPoint is NULL, set the result to the mount point
+     * for the specified archive file.
+     */
+    if( !zMountPoint ) {
+        int found = 0;
+        Tcl_HashSearch zSearch;
+
+        zArchiveName = AbsolutePath( zArchive );
+        for( pEntry = Tcl_FirstHashEntry(&local.archiveHash,&zSearch);
+                pEntry; pEntry = Tcl_NextHashEntry(&zSearch) )
+        {
+            pArchive = Tcl_GetHashValue(pEntry);
+            if ( !strcmp( Tcl_GetString(pArchive->zName), zArchiveName ) ) {
+                ++found;
+                Tcl_SetStringObj( resultObj,
+                        Tcl_GetString(pArchive->zMountPoint), -1 );
+                break;
+            }
+        }
+
+        if( !found ) {
+            Tcl_SetStringObj( resultObj, "archive not mounted by zvfs", -1 );
+        }
+
+        code = found ? TCL_OK : TCL_ERROR;
+        update = 0;
+        goto done;
+    }
+
+    if( !(chan = Tcl_OpenFileChannel(interp, zArchive, "r", 0)) ) {
+        goto done;
+    }
+
+    if(Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK) {
+        goto done;
+    }
+
+    /* Read the "End Of Central Directory" record from the end of the
+     * ZIP archive.
+     */
+    iPos = Tcl_Seek( chan, -22, SEEK_END );
+    Tcl_Read( chan, zBuf, 22 );
+    if( memcmp(zBuf, "\120\113\05\06", 4) ) {
+        Tcl_SetStringObj( resultObj, "bad end of central directory record", -1);
+        goto done;
+    }
+
+    zArchiveName    = AbsolutePath( zArchive );
+    zFullMountPoint = AbsolutePath( zMountPoint );
+
+    hashKeyObj = Tcl_NewObj();
+    Tcl_IncrRefCount(hashKeyObj);
+    Tcl_AppendStringsToObj( hashKeyObj, zArchiveName, ":", zFullMountPoint,
+            (char *)NULL );
+
+    pEntry = Tcl_CreateHashEntry( &local.archiveHash,
+            Tcl_GetString(hashKeyObj), &isNew );
+
+    if( !isNew ) {
+        /* This archive is already mounted.  Set the result to
+         * the current mount point and return.
+         */
+        pArchive = Tcl_GetHashValue(pEntry);
+        code = TCL_OK;
+        update = 0;
+        goto done;
+    }
+
+    pArchive = (ZvfsArchive*)Tcl_Alloc(sizeof(*pArchive));
+    pArchive->refCount    = 1;
+    pArchive->zName       = Tcl_NewStringObj(zArchiveName,-1);
+    pArchive->zMountPoint = Tcl_NewStringObj(zFullMountPoint,-1);
+    Tcl_SetHashValue(pEntry, pArchive);
+
+    /* Add the root mount point to our list of archive files as a directory. */
+    pEntry = Tcl_CreateHashEntry(&local.fileHash, zFullMountPoint, &isNew);
+
+    if( isNew ) {
+        pZvfs = (ZvfsFile*)Tcl_Alloc( sizeof(*pZvfs) );
+        pZvfs->refCount   = 1;
+        pZvfs->zName      = Tcl_NewStringObj(zFullMountPoint,-1);
+        pZvfs->pArchive   = pArchive;
+        pZvfs->isdir      = 1;
+        pZvfs->iOffset    = 0;
+        pZvfs->timestamp  = 0;
+        pZvfs->iCRC       = 0;
+        pZvfs->nByteCompr = 0;
+        pZvfs->nByte      = 0;
+        pZvfs->parent     = NULL;
+        Tcl_InitHashTable( &pZvfs->children, TCL_STRING_KEYS );
+
+        Tcl_SetHashValue( pEntry, pZvfs );
+    }
+
+    /* Compute the starting location of the directory for the
+     * ZIP archive in iPos then seek to that location.
+     */
+    nFile = INT16(zBuf,8);
+    iPos -= INT32(zBuf,12);
+    Tcl_Seek( chan, iPos, SEEK_SET );
+
+    while( nFile-- > 0 )
+    {
+        int isdir = 0;
+        int iData;              /* Offset to start of file data */
+        int lenName;            /* Length of the next filename */
+        int lenExtra;           /* Length of "extra" data for next file */
+        int attributes;         /* DOS attributes */
+        char *zName;
+        char *zFullPath;        /* Full pathname of the virtual file */
+        char *izFullPath;       /* Lowercase full pathname */
+        ZvfsFile *parent;
+
+        /* Read the next directory entry.  Extract the size of the filename,
+         * the size of the "extra" information, and the offset into the archive
+         * file of the file data.
+         */
+        Tcl_Read( chan, zBuf, 46 );
+        if( memcmp(zBuf, "\120\113\01\02", 4) ) {
+            Zvfs_Unmount( interp, zArchiveName );
+            Tcl_SetStringObj( resultObj, "bad central file record", -1 );
+            goto done;
+        }
+
+        lenName  = INT16(zBuf,28);
+        lenExtra = INT16(zBuf,30) + INT16(zBuf,32);
+        iData    = INT32(zBuf,42);
+
+        /* Construct an entry in local.fileHash for this virtual file. */
+        Tcl_ReadChars( chan, readObj, lenName, 0 );
+
+        zName = Tcl_GetString(readObj);
+
+        if( zName[--lenName] == '/' ) {
+            isdir = 1;
+            Tcl_SetObjLength( readObj, lenName );
+        }
+
+        pZvfs = AddPathToArchive( pArchive, zName, &isNew );
+
+        pZvfs->isdir      = isdir;
+        pZvfs->iOffset    = iData;
+        pZvfs->timestamp  = DosTimeDate(INT16(zBuf, 14), INT16(zBuf, 12));
+        pZvfs->iCRC       = INT32(zBuf, 16);
+        pZvfs->nByteCompr = INT32(zBuf, 20);
+        pZvfs->nByte      = INT32(zBuf, 24);
+
+        /* If this is a directory we want to initialize the
+         * hash table to store its children if it has any.
+         */
+        if( isNew && isdir ) {
+            Tcl_InitHashTable( &pZvfs->children, TCL_STRING_KEYS );
+        }
+
+        /* Skip over the extra information so that the next read
+         * will be from the beginning of the next directory entry.
+         */
+        Tcl_Seek( chan, lenExtra, SEEK_CUR );
+    }
+
+    code = TCL_OK;
+
+done:
+    if( chan ) Tcl_Close( interp, chan );
+
+    if( readObj ) Tcl_DecrRefCount(readObj);
+    if( hashKeyObj ) Tcl_DecrRefCount(hashKeyObj);
+
+    if( zArchiveName ) Tcl_Free(zArchiveName);
+    if( zFullMountPoint ) Tcl_Free(zFullMountPoint);
+
+    if( code == TCL_OK && update ) {
+        Tcl_FSMountsChanged( &zvfsFilesystem );
+        Tcl_SetStringObj( resultObj, zMountPoint, -1 );
+    }
+
+    return code;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Zvfs_Unmount --
+ *
+ * 	Unmount all the files in the given zip archive.  All the
+ * 	entries in the file hash table for the archive are deleted
+ * 	as well as the entry in the archive hash table.
+ *
+ * 	Any memory associated with the entries will be freed as well.
+ *
+ * Results:
+ * 	Standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Zvfs_Unmount( Tcl_Interp *interp, CONST char *zMountPoint )
+{
+    int found = 0;
+    ZvfsFile *pFile;
+    ZvfsArchive *pArchive;
+    Tcl_HashEntry *pEntry;
+    Tcl_HashSearch zSearch;
+    Tcl_HashEntry *fEntry;
+    Tcl_HashSearch fSearch;
+
+    for( pEntry = Tcl_FirstHashEntry( &local.archiveHash, &zSearch );
+            pEntry; pEntry = Tcl_NextHashEntry(&zSearch) )
+    {
+        pArchive = Tcl_GetHashValue(pEntry);
+        if( !Tcl_StringCaseMatch( zMountPoint,
+                Tcl_GetString(pArchive->zMountPoint), NOCASE_PATHS ) ) continue;
+
+        found++;
+
+        for( fEntry = Tcl_FirstHashEntry( &local.fileHash, &fSearch );
+                fEntry; fEntry = Tcl_NextHashEntry(&fSearch) )
+        {
+            pFile = Tcl_GetHashValue(fEntry);
+            if( pFile->pArchive == pArchive ) {
+                FreeInternalRep( (ClientData)pFile );
+                Tcl_DeleteHashEntry(fEntry);
+            }
+        }
+
+        Tcl_DeleteHashEntry(pEntry);
+        Tcl_DecrRefCount(pArchive->zName);
+        Tcl_DecrRefCount(pArchive->zMountPoint);
+        Tcl_Free( (char *)pArchive );
+    }
+
+    if( !found ) {
+        if( interp ) {
+            Tcl_AppendStringsToObj( Tcl_GetObjResult(interp),
+                    zMountPoint, " is not a zvfs mount", (char *)NULL );
+        }
+        return TCL_ERROR;
+    }
+
+    Tcl_FSMountsChanged( &zvfsFilesystem );
+
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ZvfsLookup --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Look into the file hash table for a given path and see if
+ * 	it belongs to our filesystem.
+ *
+ * Results:
+ * 	Pointer to the file structure or NULL if it was not found.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ZvfsFile *
+ZvfsLookup( Tcl_Obj *pathPtr )
+{
+    char *zTrueName;
+    Tcl_HashEntry *pEntry;
+
+    zTrueName = AbsolutePath( Tcl_GetString(pathPtr) );
+    pEntry = Tcl_FindHashEntry( &local.fileHash, zTrueName );
+    Tcl_Free(zTrueName);
+
+    return pEntry ? Tcl_GetHashValue(pEntry) : NULL;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetZvfsFile --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	For a given pathPtr, return the internal representation
+ * 	of the path for our filesystem.
+ *
+ * Results:
+ * 	Pointer to the file structure or NULL if it was not found.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ZvfsFile *
+GetZvfsFile( Tcl_Obj *pathPtr )
+{
+    ZvfsFile *pFile = (ZvfsFile *)Tcl_FSGetInternalRep(pathPtr,&zvfsFilesystem);
+    return pFile == NULL || pFile->pArchive->refCount == 0 ? NULL : pFile;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ZvfsFileMatchesType --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	See if the given ZvfsFile matches the type data given.
+ *
+ * Results:
+ * 	1 if true, 0 if false
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ZvfsFileMatchesType( ZvfsFile *pFile, Tcl_GlobTypeData *types )
+{
+    if( types ) {
+        if( types->type & TCL_GLOB_TYPE_FILE && pFile->isdir ) {
+            return 0;
+        }
+
+        if( types->type & (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_MOUNT)
+                && !pFile->isdir ) {
+            return 0;
+        }
+
+        if( types->type & TCL_GLOB_TYPE_MOUNT && pFile->parent ) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverExit --
+ *
+ * 	This function is called as an exit handler for the channel
+ * 	driver.  If we do not set pInfo.chan to NULL, Tcl_Close()
+ * 	will be called twice on that channel when Tcl_Exit runs.
+ * 	This will lead to a core dump
+ *
+ * Results:
+ * 	None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DriverExit( void *pArg )
+{
+    ZvfsChannelInfo *pInfo = (ZvfsChannelInfo*)pArg;
+    pInfo->chan = 0;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverClose --
+ *
+ * 	Called when a channel is closed.
+ *
+ * Results:
+ * 	Returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DriverClose(
+  ClientData  instanceData,    /* A ZvfsChannelInfo structure */
+  Tcl_Interp *interp           /* The TCL interpreter */
+) {
+    ZvfsChannelInfo* pInfo = (ZvfsChannelInfo*)instanceData;
+
+    if( pInfo->zBuf ){
+        Tcl_Free(pInfo->zBuf);
+        inflateEnd(&pInfo->stream);
+    }
+
+    if( pInfo->chan ){
+        Tcl_Close(interp, pInfo->chan);
+        Tcl_DeleteExitHandler(DriverExit, pInfo);
+    }
+
+    Tcl_Free((char*)pInfo);
+
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverInput --
+ *
+ * 	The Tcl channel system calls this function on each read
+ * 	from a channel.  The channel is opened into the actual
+ * 	archive file, but the data is read from the individual
+ * 	file entry inside the zip archive.
+ *
+ * Results:
+ * 	Number of bytes read.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DriverInput (
+  ClientData instanceData, /* The channel to read from */
+  char *buf,               /* Buffer to fill */
+  int toRead,              /* Requested number of bytes */
+  int *pErrorCode          /* Location of error flag */
+) {
+    ZvfsChannelInfo* pInfo = (ZvfsChannelInfo*) instanceData;
+
+    if( toRead > pInfo->nByte ) {
+        toRead = pInfo->nByte;
+    }
+
+    if( toRead == 0 ) {
+        return 0;
+    }
+
+    if( pInfo->isCompressed ) {
+        int err = Z_OK;
+        z_stream *stream = &pInfo->stream;
+        stream->next_out = buf;
+        stream->avail_out = toRead;
+        while (stream->avail_out) {
+            if (!stream->avail_in) {
+                int len = pInfo->nByteCompr;
+                if (len > COMPR_BUF_SIZE) {
+                    len = COMPR_BUF_SIZE;
+                }
+                len = Tcl_Read(pInfo->chan, pInfo->zBuf, len);
+                pInfo->nByteCompr -= len;
+                stream->next_in = pInfo->zBuf;
+                stream->avail_in = len;
+            }
+
+            err = inflate(stream, Z_NO_FLUSH);
+            if (err) break;
+        }
+
+        if (err == Z_STREAM_END) {
+            if ((stream->avail_out != 0)) {
+                *pErrorCode = err; /* premature end */
+                return -1;
+            }
+        } else if( err ) {
+            *pErrorCode = err; /* some other zlib error */
+            return -1;
+        }
+    } else {
+        toRead = Tcl_Read(pInfo->chan, buf, toRead);
+    }
+
+    pInfo->nByte -= toRead;
+    pInfo->readSoFar += toRead;
+    *pErrorCode = 0;
+
+    return toRead;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverOutput --
+ *
+ * 	Called to write to a file.  Since this is a read-only file
+ * 	system, this function will always return an error.
+ *
+ * Results:
+ * 	Returns -1.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DriverOutput(
+  ClientData instanceData,   /* The channel to write to */
+  CONST char *buf,                 /* Data to be stored. */
+  int toWrite,               /* Number of bytes to write. */
+  int *pErrorCode            /* Location of error flag. */
+) {
+    *pErrorCode = EINVAL;
+    return -1;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverSeek --
+ *
+ * 	Seek along the open channel to another point.
+ *
+ * Results:
+ * 	Offset into the file.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DriverSeek(
+  ClientData instanceData,    /* The file structure */
+  long offset,                /* Offset to seek to */
+  int mode,                   /* One of SEEK_CUR, SEEK_SET or SEEK_END */
+  int *pErrorCode             /* Write the error code here */
+){
+    ZvfsChannelInfo* pInfo = (ZvfsChannelInfo*) instanceData;
+
+    switch( mode ) {
+    case SEEK_CUR:
+        offset += pInfo->readSoFar;
+        break;
+    case SEEK_END:
+        offset += pInfo->readSoFar + pInfo->nByte;
+        break;
+    default:
+        /* Do nothing */
+        break;
+    }
+
+    if( !pInfo->isCompressed ){
+        /* dont seek behind end of data */
+	if (pInfo->nData < (unsigned long)offset) {
+	    return -1;
+        }
+
+	/* do the job, save and check the result */
+	offset = Tcl_Seek(pInfo->chan, offset + pInfo->startOfData, SEEK_SET);
+	if (offset == -1) {
+	    return -1;
+        }
+
+	 /* adjust the counters (use real offset) */
+	pInfo->readSoFar = offset - pInfo->startOfData;
+	pInfo->nByte = pInfo->nData - pInfo->readSoFar; 
+    } else {
+        if( offset<pInfo->readSoFar ) {
+            z_stream *stream = &pInfo->stream;
+            inflateEnd(stream);
+            stream->zalloc   = (alloc_func)0;
+            stream->zfree    = (free_func)0;
+            stream->opaque   = (voidpf)0;
+            stream->avail_in = 2;
+            stream->next_in  = pInfo->zBuf;
+            pInfo->zBuf[0]   = 0x78;
+            pInfo->zBuf[1]   = 0x01;
+            inflateInit(&pInfo->stream);
+            Tcl_Seek(pInfo->chan, pInfo->startOfData, SEEK_SET);
+            pInfo->nByte += pInfo->readSoFar;
+            pInfo->nByteCompr = pInfo->nData;
+            pInfo->readSoFar = 0;
+        }
+
+        while( pInfo->readSoFar < offset )
+        {
+            int toRead, errCode;
+            char zDiscard[100];
+            toRead = offset - pInfo->readSoFar;
+            if( toRead>sizeof(zDiscard) ) toRead = sizeof(zDiscard);
+            DriverInput(instanceData, zDiscard, toRead, &errCode);
+        }
+    }
+
+    return pInfo->readSoFar;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverWatch --
+ *
+ * 	Called to handle events on the channel.  Since zvfs files
+ * 	don't generate events, this is a no-op.
+ *
+ * Results:
+ * 	None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DriverWatch(
+  ClientData instanceData,   /* Channel to watch */
+  int mask                   /* Events of interest */
+) {
+    return;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DriverGetHandle --
+ *
+ * 	Retrieve a device-specific handle from the given channel.
+ * 	Since we don't have a device-specific handle, this is a no-op.
+ *
+ * Results:
+ * 	Returns TCL_ERROR.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DriverGetHandle(
+  ClientData  instanceData,   /* Channel to query */
+  int direction,              /* Direction of interest */
+  ClientData* handlePtr       /* Space to the handle into */
+) {
+    return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PathInFilesystem --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Check to see if the given path is part of our filesystem.
+ * 	We check the file hash table for the path, and if we find
+ * 	it, set clientDataPtr to the ZvfsFile pointer so that Tcl
+ * 	will cache it for later.
+ *
+ * Results:
+ * 	TCL_OK on success, or -1 on failure
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+PathInFilesystem( Tcl_Obj *pathPtr, ClientData *clientDataPtr )
+{
+    ZvfsFile *pFile = ZvfsLookup(pathPtr);
+
+    if( pFile ) {
+        *clientDataPtr = DupInternalRep((ClientData)pFile);
+        return TCL_OK;
+    }
+    return -1;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DupInternalRep --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Duplicate the ZvfsFile "native" rep of a path.
+ *
+ * Results:
+ * 	Returns clientData, with refcount incremented.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ClientData
+DupInternalRep( ClientData clientData )
+{
+    ZvfsFile *pFile = (ZvfsFile *)clientData;
+    pFile->refCount++;
+    return (ClientData)pFile;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeInternalRep --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Free one reference to the ZvfsFile "native" rep of a path.
+ * 	When all references are gone, free the struct.
+ *
+ * Side effects:
+ * 	May free memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeInternalRep( ClientData clientData )
+{
+    ZvfsFile *pFile = (ZvfsFile *)clientData;
+
+    if (--pFile->refCount <= 0) {
+        if( pFile->isdir ) {
+            /* Delete the hash table containing the children
+             * of this directory.  We don't need to free the
+             * data for each entry in the table because they're
+             * just pointers to the ZvfsFiles, and those will
+             * be freed below.
+             */
+            Tcl_DeleteHashTable( &pFile->children );
+        }
+        Tcl_DecrRefCount(pFile->zName);
+        Tcl_Free((char *)pFile);
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InternalToNormalized --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	From a ZvfsFile representation, produce the path string rep.
+ *
+ * Results:
+ * 	Returns a Tcl_Obj holding the string rep.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+InternalToNormalized( ClientData clientData )
+{
+    ZvfsFile *pFile = (ZvfsFile *)clientData;
+    if( !pFile->parent ) {
+        return Tcl_DuplicateObj( pFile->zName );
+    } else {
+        return Tcl_FSJoinToPath( pFile->parent->zName, 1, &pFile->zName );
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FilesystemPathType --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Used for informational purposes only.  Return a Tcl_Obj
+ * 	which describes the "type" of path this is.  For our
+ * 	little filesystem, they're all "zip".
+ *
+ * Results:
+ * 	Tcl_Obj with 0 refCount
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+FilesystemPathType( Tcl_Obj *pathPtr )
+{
+    return Tcl_NewStringObj( "zip", -1 );
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileSystemSeparator --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Return a Tcl_Obj describing the separator character for
+ * 	our filesystem.  We like things the old-fashioned way,
+ * 	so we'll just use /.
+ *
+ * Results:
+ * 	Tcl_Obj with 0 refCount
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+FilesystemSeparator( Tcl_Obj *pathPtr )
+{ 
+    return Tcl_NewStringObj( "/", -1 );
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Stat --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Does a stat() system call for a zvfs file.  Fill the stat
+ * 	buf with as much information as we have.
+ *
+ * Results:
+ * 	0 on success, -1 on failure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+Stat( Tcl_Obj *pathPtr, Tcl_StatBuf *buf )
+{
+    ZvfsFile *pFile;
+
+    if( !(pFile = GetZvfsFile(pathPtr)) ) {
+        return -1;
+    }
+
+    memset(buf, 0, sizeof(*buf));
+    if (pFile->isdir) {
+        buf->st_mode = 040555;
+    } else {
+        buf->st_mode = 0100555;
+    }
+
+    buf->st_size  = pFile->nByte;
+    buf->st_mtime = pFile->timestamp;
+    buf->st_ctime = pFile->timestamp;
+    buf->st_atime = pFile->timestamp;
+
+    return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Access --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Does an access() system call for a zvfs file.
+ *
+ * Results:
+ * 	0 on success, -1 on failure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+Access( Tcl_Obj *pathPtr, int mode )
+{
+    if( mode & 3 || !GetZvfsFile(pathPtr) ) return -1;
+    return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * OpenFileChannel --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Called when Tcl wants to open a file inside a zvfs file system.
+ * 	We actually open the zip file back up and seek to the offset
+ * 	of the given file.  The channel driver will take care of the
+ * 	rest.
+ *
+ * Results:
+ * 	New channel on success, NULL on failure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Channel
+OpenFileChannel( Tcl_Interp *interp, Tcl_Obj *pathPtr,
+    int mode, int permissions )
+{
+    ZvfsFile *pFile;
+    ZvfsChannelInfo *pInfo;
+    Tcl_Channel chan;
+    static int count = 1;
+    char zName[50];
+    unsigned char zBuf[50];
+
+    if( !(pFile = GetZvfsFile(pathPtr)) ) {
+        return NULL;
+    }
+
+    if(!(chan = Tcl_OpenFileChannel(interp,
+                    Tcl_GetString(pFile->pArchive->zName), "r", 0))) {
+        return NULL;
+    }
+
+    if( Tcl_SetChannelOption(interp, chan, "-translation", "binary") ) {
+        /* this should never happen */
+        Tcl_Close( NULL, chan );
+        return NULL;
+    }
+
+    Tcl_Seek(chan, pFile->iOffset, SEEK_SET);
+    Tcl_Read(chan, zBuf, 30);
+
+    if( memcmp(zBuf, "\120\113\03\04", 4) ){
+        if( interp ) {
+            Tcl_SetStringObj( Tcl_GetObjResult(interp),
+                    "bad central file record", -1 );
+        }
+        Tcl_Close( interp, chan );
+        return NULL;
+    }
+
+    pInfo = (ZvfsChannelInfo*)Tcl_Alloc( sizeof(*pInfo) );
+    pInfo->chan = chan;
+    Tcl_CreateExitHandler(DriverExit, pInfo);
+    pInfo->isCompressed = INT16(zBuf, 8);
+
+    if( pInfo->isCompressed ) {
+        z_stream *stream = &pInfo->stream;
+        pInfo->zBuf      = Tcl_Alloc(COMPR_BUF_SIZE);
+        stream->zalloc   = (alloc_func)0;
+        stream->zfree    = (free_func)0;
+        stream->opaque   = (voidpf)0;
+        stream->avail_in = 2;
+        stream->next_in  = pInfo->zBuf;
+        pInfo->zBuf[0]   = 0x78;
+        pInfo->zBuf[1]   = 0x01;
+        inflateInit(&pInfo->stream);
+    } else {
+        pInfo->zBuf = 0;
+    }
+
+    pInfo->nByte      = INT32(zBuf,22);
+    pInfo->nByteCompr = pInfo->nData = INT32(zBuf,18);
+    pInfo->readSoFar  = 0;
+    Tcl_Seek( chan, INT16(zBuf,26) + INT16(zBuf,28), SEEK_CUR );
+    pInfo->startOfData = Tcl_Tell(chan);
+    sprintf( zName, "zvfs%x%x", ((int)pFile)>>12, count++ );
+
+    return Tcl_CreateChannel( &vfsChannelType, zName, 
+                                (ClientData)pInfo, TCL_READABLE );
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MatchInDirectory --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Called when Tcl is globbing around through the filesystem.
+ * 	This function can be called when Tcl is looking for mount
+ * 	points or when it is looking for files within a mount point
+ * 	that it has already determined belongs to us.
+ *
+ * 	Any matching file in our filesystem is appended to the
+ * 	result pointer.
+ *
+ * Results:
+ * 	Standard Tcl result
+ *
+ *----------------------------------------------------------------------
+ */
+
+/* Function to process a 'MatchInDirectory()'.
+ * If not implemented, then glob and recursive
+ * copy functionality will be lacking in the filesystem.
+ */
+static int
+MatchInDirectory(
+    Tcl_Interp* interp,
+    Tcl_Obj *result,
+    Tcl_Obj *pathPtr,
+    CONST char *pattern,
+    Tcl_GlobTypeData *types
+) {
+    ZvfsFile *pFile;
+    Tcl_HashEntry *pEntry;
+    Tcl_HashSearch sSearch;
+
+    if( types && types->type & TCL_GLOB_TYPE_MOUNT ) {
+        /* Tcl is looking for a list of our mount points that
+         * match the given pattern.  This is so that Tcl can
+         * append vfs mounted directories to a list of actual
+         * filesystem directories.
+         */
+        char *path, *zPattern;
+        ZvfsArchive *pArchive;
+        Tcl_Obj *patternObj = Tcl_NewObj();
+
+        path = AbsolutePath( Tcl_GetString(pathPtr) );
+        Tcl_AppendStringsToObj( patternObj, path, "/", pattern, (char *)NULL );
+        Tcl_Free(path);
+        zPattern = Tcl_GetString( patternObj );
+
+        for( pEntry = Tcl_FirstHashEntry( &local.archiveHash, &sSearch );
+                pEntry; pEntry = Tcl_NextHashEntry( &sSearch ) )
+        {
+            pArchive = Tcl_GetHashValue(pEntry);
+            if( Tcl_StringCaseMatch( Tcl_GetString(pArchive->zMountPoint),
+                        zPattern, NOCASE_PATHS ) ) {
+                Tcl_ListObjAppendElement( NULL, result,
+                        Tcl_DuplicateObj(pArchive->zMountPoint) );
+            }
+        }
+
+        Tcl_DecrRefCount(patternObj);
+
+        return TCL_OK;
+    }
+
+    if( !(pFile = GetZvfsFile(pathPtr)) ) {
+        Tcl_SetStringObj( Tcl_GetObjResult(interp), "stale file handle", -1 );
+        return TCL_ERROR;
+    }
+
+    if( !pattern ) {
+        /* If pattern is null, Tcl is actually just checking to
+         * see if this file exists in our filesystem.  Check to
+         * make sure the path matches any type data and then
+         * append it to the result and return.
+         */
+        if( ZvfsFileMatchesType( pFile, types ) ) {
+            Tcl_ListObjAppendElement( NULL, result, pathPtr );
+        }
+        return TCL_OK;
+    }
+
+    /* We've determined that the requested path is in our filesystem,
+     * so now we want to walk through the children of the directory
+     * and find any that match the given pattern and type.  Any we
+     * find will be appended to the result.
+     */
+
+    for( pEntry = Tcl_FirstHashEntry(&pFile->children, &sSearch);
+        pEntry; pEntry = Tcl_NextHashEntry(&sSearch) )
+    {
+        char *zName;
+        pFile = Tcl_GetHashValue(pEntry);
+        zName = Tcl_GetString(pFile->zName);
+
+        if( ZvfsFileMatchesType( pFile, types )
+                && Tcl_StringCaseMatch(zName, pattern, NOCASE_PATHS) ) {
+            Tcl_ListObjAppendElement( NULL, result,
+                    Tcl_FSJoinToPath(pathPtr, 1, &pFile->zName ) );
+        }
+    }
+
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListVolumes --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Called when Tcl is looking for a list of open volumes
+ * 	for our filesystem.  The mountpoint for each open archive
+ * 	is appended to a list object.
+ *
+ * Results:
+ * 	A Tcl_Obj with 0 refCount
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+ListVolumes(void)
+{
+    Tcl_HashEntry *pEntry;    /* Hash table entry */
+    Tcl_HashSearch zSearch;   /* Search all mount points */
+    ZvfsArchive *pArchive;    /* The ZIP archive being mounted */
+    Tcl_Obj *pVols = Tcl_NewObj();
+  
+    for( pEntry = Tcl_FirstHashEntry(&local.archiveHash,&zSearch);
+            pEntry; pEntry = Tcl_NextHashEntry(&zSearch) )
+    {
+        pArchive = Tcl_GetHashValue(pEntry);
+
+        Tcl_ListObjAppendElement( NULL, pVols,
+                Tcl_DuplicateObj(pArchive->zMountPoint) );
+    }
+
+    return pVols;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileAttrStrings --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Return an array of strings for all of the possible
+ * 	attributes for a file in zvfs.
+ *
+ * Results:
+ * 	Pointer to ZvfsAttrs
+ *
+ *----------------------------------------------------------------------
+ */
+
+static CONST char **
+FileAttrStrings( Tcl_Obj *pathPtr, Tcl_Obj** objPtrRef )
+{
+    return ZvfsAttrs;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileAttrsGet --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Called for a "file attributes" command from Tcl
+ * 	to return the attributes for a file in our filesystem.
+ *
+ * 	objPtrRef will point to a 0 refCount Tcl_Obj on success.
+ *
+ * Results:
+ * 	Standard Tcl result
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FileAttrsGet( Tcl_Interp *interp, int index,
+    Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef )
+{
+    char *zFilename;
+    ZvfsFile *pFile;
+    zFilename = Tcl_GetString(pathPtr);
+
+    if( !(pFile = GetZvfsFile(pathPtr)) ) {
+        return TCL_ERROR;
+    }
+
+    switch(index) {
+    case ZVFS_ATTR_ARCHIVE:
+        *objPtrRef= Tcl_DuplicateObj(pFile->pArchive->zName);
+        return TCL_OK;
+    case ZVFS_ATTR_COMPSIZE:
+        *objPtrRef=Tcl_NewIntObj(pFile->nByteCompr);
+        return TCL_OK;
+    case ZVFS_ATTR_CRC:
+        *objPtrRef=Tcl_NewIntObj(pFile->iCRC);
+        return TCL_OK;
+    case ZVFS_ATTR_MOUNT:
+        *objPtrRef= Tcl_DuplicateObj(pFile->pArchive->zMountPoint);
+        return TCL_OK;
+    case ZVFS_ATTR_OFFSET:
+        *objPtrRef= Tcl_NewIntObj(pFile->nByte);
+        return TCL_OK;
+    case ZVFS_ATTR_UNCOMPSIZE:
+        *objPtrRef= Tcl_NewIntObj(pFile->nByte);
+        return TCL_OK;
+    default:
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileAttrsSet --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Called to set the value of an attribute for the
+ * 	given file.  Since we're a read-only filesystem, this
+ * 	always returns an error.
+ *
+ * Results:
+ * 	Returns TCL_ERROR
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FileAttrsSet( Tcl_Interp *interp, int index,
+                            Tcl_Obj *pathPtr, Tcl_Obj *objPtr )
+{
+    return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Chdir --
+ *
+ * 	Part of the "zvfs" Tcl_Filesystem.
+ * 	Handles a chdir() call for the filesystem.  Tcl has
+ * 	already determined that the directory belongs to us,
+ * 	so we just need to check and make sure that the path
+ * 	is actually a directory in our filesystem and not a
+ * 	regular file.
+ *
+ * Results:
+ * 	0 on success, -1 on failure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+Chdir( Tcl_Obj *pathPtr )
+{
+    ZvfsFile *zFile = GetZvfsFile(pathPtr);
+    if( !zFile || !zFile->isdir ) return -1;
+    return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MountObjCmd --
+ *
+ * 	This function implements the [zvfs::mount] command.
+ *
+ * 	zvfs::mount ?zipFile? ?mountPoint?
+ *
+ * 	Creates a new mount point to the given zip archive.
+ * 	All files in the zip archive will be added to the
+ * 	virtual filesystem and be available to Tcl as regular
+ * 	files and directories.
+ *
+ * Results:
+ * 	Standard Tcl result
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+MountObjCmd(
+    ClientData clientData,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST objv[]
+) {
+    char *zipFile = NULL, *mountPoint = NULL;
+
+    if( objc > 3 ) {
+        Tcl_WrongNumArgs( interp, 1, objv, "?zipFile? ?mountPoint?" );
+        return TCL_ERROR;
+    }
+
+    if( objc > 1 ) {
+        zipFile = Tcl_GetString( objv[1] );
+    }
+
+    if( objc > 2 ) {
+        mountPoint = Tcl_GetString( objv[2] );
+    }
+
+    return Zvfs_Mount( interp, zipFile, mountPoint );
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnmountObjCmd --
+ *
+ * 	This function implements the [zvfs::unmount] command.
+ *
+ * 	zvfs::unmount mountPoint
+ *
+ * 	Unmount the given mountPoint if it is mounted in our
+ * 	filesystem.
+ *
+ * Results:
+ * 	0 on success, -1 on failure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+UnmountObjCmd(
+    ClientData clientData,
+    Tcl_Interp *interp,
+    int objc,
+    Tcl_Obj *CONST objv[]
+) {
+    if( objc != 2 ) {
+        Tcl_WrongNumArgs( interp, objc, objv, "mountPoint" );
+        return TCL_ERROR;
+    }
+
+    return Zvfs_Unmount( interp, Tcl_GetString(objv[1]) );
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Zvfs_Init, Zvfs_SafeInit --
+ *
+ * 	Initialize the zvfs package.
+ *
+ * 	Safe interpreters do not receive the ability to mount and
+ * 	unmount zip files.
+ *
+ * Results:
+ * 	Standard Tcl result
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Zvfs_SafeInit( Tcl_Interp *interp )
+{
+#ifdef USE_TCL_STUBS
+    if( Tcl_InitStubs( interp, "8.0", 0 ) == TCL_ERROR ) return TCL_ERROR;
+#endif
+
+    if( !local.isInit ) {
+        /* Register the filesystem and initialize the hash tables. */
+	Tcl_FSRegister( 0, &zvfsFilesystem );
+	Tcl_InitHashTable( &local.fileHash, TCL_STRING_KEYS );
+	Tcl_InitHashTable( &local.archiveHash, TCL_STRING_KEYS );
+
+	local.isInit = 1;
+    }
+
+    Tcl_PkgProvide( interp, "zvfs", "1.0" );
+
+    return TCL_OK;
+}
+
+int
+Zvfs_Init( Tcl_Interp *interp )
+{
+    if( Zvfs_SafeInit( interp ) == TCL_ERROR ) return TCL_ERROR;
+
+    if( !Tcl_IsSafe(interp) ) {
+        Tcl_CreateObjCommand(interp, "zvfs::mount", MountObjCmd, 0, 0);
+        Tcl_CreateObjCommand(interp, "zvfs::unmount", UnmountObjCmd, 0, 0);
+    }
+
+    return TCL_OK;
+}
