From 51219bf94e57870b142db498f63180828d6990d9 Mon Sep 17 00:00:00 2001 From: treectrl Date: Tue, 17 Dec 2002 05:04:00 +0000 Subject: Initial revision --- Makefile.in | 451 +++ aclocal.m4 | 9 + configure | 7052 +++++++++++++++++++++++++++++++++++++++++ configure.ac | 210 ++ demo.tcl | 935 ++++++ demos/bitmaps.tcl | 55 + demos/explorer.tcl | 367 +++ demos/help.tcl | 347 ++ demos/imovie.tcl | 111 + demos/layout.tcl | 98 + demos/mailwasher.tcl | 183 ++ demos/outlook-folders.tcl | 106 + demos/outlook-newgroup.tcl | 382 +++ demos/random.tcl | 475 +++ demos/www-options.tcl | 299 ++ doc/manual.txt | 415 +++ generic/qebind.c | 1992 ++++++++++++ generic/qebind.h | 67 + generic/tkTreeColumn.c | 1547 +++++++++ generic/tkTreeCtrl.c | 2988 +++++++++++++++++ generic/tkTreeCtrl.h | 488 +++ generic/tkTreeDisplay.c | 3618 +++++++++++++++++++++ generic/tkTreeDrag.c | 436 +++ generic/tkTreeElem.c | 3436 ++++++++++++++++++++ generic/tkTreeElem.h | 79 + generic/tkTreeItem.c | 3662 +++++++++++++++++++++ generic/tkTreeMarquee.c | 364 +++ generic/tkTreeNotify.c | 389 +++ generic/tkTreeStyle.c | 3652 +++++++++++++++++++++ generic/tkTreeUtils.c | 877 +++++ library/filelist-bindings.tcl | 788 +++++ library/treectrl.tcl | 825 +++++ pics/big-dll.gif | Bin 0 -> 437 bytes pics/big-exe.gif | Bin 0 -> 368 bytes pics/big-file.gif | Bin 0 -> 466 bytes pics/big-folder.gif | Bin 0 -> 459 bytes pics/big-txt.gif | Bin 0 -> 392 bytes pics/checked.gif | Bin 0 -> 78 bytes pics/file.gif | Bin 0 -> 279 bytes pics/folder-closed.gif | Bin 0 -> 111 bytes pics/folder-open.gif | Bin 0 -> 120 bytes pics/help-book-closed.gif | Bin 0 -> 115 bytes pics/help-book-open.gif | Bin 0 -> 128 bytes pics/help-page.gif | Bin 0 -> 132 bytes pics/imovie-01.gif | Bin 0 -> 5406 bytes pics/imovie-02.gif | Bin 0 -> 5912 bytes pics/imovie-03.gif | Bin 0 -> 4696 bytes pics/imovie-04.gif | Bin 0 -> 5783 bytes pics/imovie-05.gif | Bin 0 -> 3238 bytes pics/imovie-06.gif | Bin 0 -> 3509 bytes pics/imovie-07.gif | Bin 0 -> 2091 bytes pics/internet-check-off.gif | Bin 0 -> 70 bytes pics/internet-check-on.gif | Bin 0 -> 76 bytes pics/internet-print.gif | Bin 0 -> 124 bytes pics/internet-radio-off.gif | Bin 0 -> 68 bytes pics/internet-radio-on.gif | Bin 0 -> 71 bytes pics/internet-search.gif | Bin 0 -> 114 bytes pics/internet-security.gif | Bin 0 -> 108 bytes pics/mac-collapse.gif | Bin 0 -> 275 bytes pics/mac-expand.gif | Bin 0 -> 277 bytes pics/outlook-arrow.gif | Bin 0 -> 73 bytes pics/outlook-clip.gif | Bin 0 -> 73 bytes pics/outlook-deleted.gif | Bin 0 -> 138 bytes pics/outlook-draft.gif | Bin 0 -> 134 bytes pics/outlook-folder.gif | Bin 0 -> 133 bytes pics/outlook-group.gif | Bin 0 -> 144 bytes pics/outlook-inbox.gif | Bin 0 -> 133 bytes pics/outlook-local.gif | Bin 0 -> 146 bytes pics/outlook-main.gif | Bin 0 -> 174 bytes pics/outlook-outbox.gif | Bin 0 -> 136 bytes pics/outlook-read-2.gif | Bin 0 -> 343 bytes pics/outlook-read.gif | Bin 0 -> 304 bytes pics/outlook-sent.gif | Bin 0 -> 132 bytes pics/outlook-server.gif | Bin 0 -> 163 bytes pics/outlook-unread.gif | Bin 0 -> 303 bytes pics/outlook-watch.gif | Bin 0 -> 98 bytes pics/small-dll.gif | Bin 0 -> 311 bytes pics/small-exe.gif | Bin 0 -> 115 bytes pics/small-file.gif | Bin 0 -> 338 bytes pics/small-folder.gif | Bin 0 -> 307 bytes pics/small-txt.gif | Bin 0 -> 302 bytes pics/unchecked.gif | Bin 0 -> 72 bytes pkgIndex.tcl.in | 9 + tclconfig/ChangeLog | 161 + tclconfig/README.txt | 26 + tclconfig/install-sh | 119 + tclconfig/tcl.m4 | 3171 ++++++++++++++++++ 87 files changed, 40189 insertions(+) create mode 100644 Makefile.in create mode 100644 aclocal.m4 create mode 100644 configure create mode 100644 configure.ac create mode 100644 demo.tcl create mode 100644 demos/bitmaps.tcl create mode 100644 demos/explorer.tcl create mode 100644 demos/help.tcl create mode 100644 demos/imovie.tcl create mode 100644 demos/layout.tcl create mode 100644 demos/mailwasher.tcl create mode 100644 demos/outlook-folders.tcl create mode 100644 demos/outlook-newgroup.tcl create mode 100644 demos/random.tcl create mode 100644 demos/www-options.tcl create mode 100644 doc/manual.txt create mode 100644 generic/qebind.c create mode 100644 generic/qebind.h create mode 100644 generic/tkTreeColumn.c create mode 100644 generic/tkTreeCtrl.c create mode 100644 generic/tkTreeCtrl.h create mode 100644 generic/tkTreeDisplay.c create mode 100644 generic/tkTreeDrag.c create mode 100644 generic/tkTreeElem.c create mode 100644 generic/tkTreeElem.h create mode 100644 generic/tkTreeItem.c create mode 100644 generic/tkTreeMarquee.c create mode 100644 generic/tkTreeNotify.c create mode 100644 generic/tkTreeStyle.c create mode 100644 generic/tkTreeUtils.c create mode 100644 library/filelist-bindings.tcl create mode 100644 library/treectrl.tcl create mode 100644 pics/big-dll.gif create mode 100644 pics/big-exe.gif create mode 100644 pics/big-file.gif create mode 100644 pics/big-folder.gif create mode 100644 pics/big-txt.gif create mode 100644 pics/checked.gif create mode 100644 pics/file.gif create mode 100644 pics/folder-closed.gif create mode 100644 pics/folder-open.gif create mode 100644 pics/help-book-closed.gif create mode 100644 pics/help-book-open.gif create mode 100644 pics/help-page.gif create mode 100644 pics/imovie-01.gif create mode 100644 pics/imovie-02.gif create mode 100644 pics/imovie-03.gif create mode 100644 pics/imovie-04.gif create mode 100644 pics/imovie-05.gif create mode 100644 pics/imovie-06.gif create mode 100644 pics/imovie-07.gif create mode 100644 pics/internet-check-off.gif create mode 100644 pics/internet-check-on.gif create mode 100644 pics/internet-print.gif create mode 100644 pics/internet-radio-off.gif create mode 100644 pics/internet-radio-on.gif create mode 100644 pics/internet-search.gif create mode 100644 pics/internet-security.gif create mode 100644 pics/mac-collapse.gif create mode 100644 pics/mac-expand.gif create mode 100644 pics/outlook-arrow.gif create mode 100644 pics/outlook-clip.gif create mode 100644 pics/outlook-deleted.gif create mode 100644 pics/outlook-draft.gif create mode 100644 pics/outlook-folder.gif create mode 100644 pics/outlook-group.gif create mode 100644 pics/outlook-inbox.gif create mode 100644 pics/outlook-local.gif create mode 100644 pics/outlook-main.gif create mode 100644 pics/outlook-outbox.gif create mode 100644 pics/outlook-read-2.gif create mode 100644 pics/outlook-read.gif create mode 100644 pics/outlook-sent.gif create mode 100644 pics/outlook-server.gif create mode 100644 pics/outlook-unread.gif create mode 100644 pics/outlook-watch.gif create mode 100644 pics/small-dll.gif create mode 100644 pics/small-exe.gif create mode 100644 pics/small-file.gif create mode 100644 pics/small-folder.gif create mode 100644 pics/small-txt.gif create mode 100644 pics/unchecked.gif create mode 100644 pkgIndex.tcl.in create mode 100644 tclconfig/ChangeLog create mode 100644 tclconfig/README.txt create mode 100644 tclconfig/install-sh create mode 100644 tclconfig/tcl.m4 diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..b6b4d28 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,451 @@ +# 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 ActiveState SRL. +# +# 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.1 2002/12/17 05:04:20 treectrl Exp $ + +#======================================================================== +# Edit the following few lines when writing a new extension +#======================================================================== + +#======================================================================== +# Enumerate the names of the source files included in this package. +# This will be used when a dist target is added to the Makefile. +# EXTRA_SOURCES will be replaced by WIN_SOURCES or UNIX_SOURCES, as is +# appropriate for your platform. It is not important to specify the +# directory, as long as it is the $(srcdir) or in the generic, win or +# unix subdirectory. +#======================================================================== + +treectrl_SOURCES = \ + qebind.c \ + tkTreeColumn.c \ + tkTreeCtrl.c \ + tkTreeDisplay.c \ + tkTreeDrag.c \ + tkTreeElem.c \ + tkTreeItem.c \ + tkTreeMarquee.c \ + tkTreeNotify.c \ + tkTreeStyle.c \ + tkTreeUtils.c \ + @EXTRA_SOURCES@ + +WIN_SOURCES = +UNIX_SOURCES = + +#======================================================================== +# Identify the object files. This replaces .c with .$(OBJEXT) for all +# the named source files. These objects are created and linked into the +# final library. In these do not correspond directly to the source files +# above, you will need to enumerate the object files here. +# Normally we would use $(OBJEXT), but certain make executables won't do +# the extra macro in a macro conversion properly. +# +# "sample_LIB_FILE" refers to the library (dynamic or static as per +# configuration options) composed of the named objects. +#======================================================================== + +treectrl_OBJECTS = $(treectrl_SOURCES:.c=.@OBJEXT@) +treectrl_LIB_FILE = @treectrl_LIB_FILE@ + +#======================================================================== +# RUNTIME_SOURCES identifies Tcl runtime files that are associated with +# this package that need to be installed, if any. +#======================================================================== + +RUNTIME_SOURCES = treectrl.tcl filelist-bindings.tcl + +#======================================================================== +# This is a list of header files to be installed +#======================================================================== + +GENERIC_HDRS = + +#======================================================================== +# Add additional lines to handle any additional AC_SUBST cases that +# have been added to the configure script. +#======================================================================== + +#SAMPLE_NEW_VAR = @SAMPLE_NEW_VAR@ + +#======================================================================== +# Nothing of the variables below this line need to be changed. Please +# check the TARGETS section below to make sure the make targets are +# correct. +#======================================================================== + +#======================================================================== +# The variable "$(PACKAGE)_LIB_FILE" is the parameterized name of the +# library that we are building. +#======================================================================== + +lib_BINARIES = $($(PACKAGE)_LIB_FILE) +BINARIES = $(lib_BINARIES) + +SHELL = @SHELL@ + +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +libdir = @libdir@ +datadir = @datadir@ +mandir = @mandir@ +includedir = @includedir@ + +DESTDIR = + +PKG_DIR = $(PACKAGE)$(VERSION) +pkgdatadir = $(datadir)/$(PKG_DIR) +pkglibdir = $(libdir)/$(PKG_DIR) +pkgincludedir = $(includedir)/$(PKG_DIR) + +top_builddir = . + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ +CC = @CC@ +CFLAGS_DEBUG = @CFLAGS_DEBUG@ +CFLAGS_DEFAULT = @CFLAGS_DEFAULT@ +CFLAGS_OPTIMIZE = @CFLAGS_OPTIMIZE@ +CLEANFILES = @CLEANFILES@ +EXEEXT = @EXEEXT@ +LDFLAGS_DEBUG = @LDFLAGS_DEBUG@ +LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@ +LDFLAGS_OPTIMIZE = @LDFLAGS_OPTIMIZE@ +MAKE_LIB = @MAKE_LIB@ +MAKE_SHARED_LIB = @MAKE_SHARED_LIB@ +MAKE_STATIC_LIB = @MAKE_STATIC_LIB@ +OBJEXT = @OBJEXT@ +RANLIB = @RANLIB@ +SHLIB_CFLAGS = @SHLIB_CFLAGS@ +SHLIB_LD = @SHLIB_LD@ +SHLIB_LDFLAGS = @SHLIB_LDFLAGS@ +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@ +# This is necessary for packages that use private Tcl headers +TCL_TOP_DIR_NATIVE = @TCL_TOP_DIR_NATIVE@ +TK_SRC_DIR = @TK_SRC_DIR@ +TK_TOP_DIR_NATIVE = @TK_TOP_DIR_NATIVE@ +# 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) +TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \ + LD_LIBRARY_PATH="$(EXTRA_PATH):$(LD_LIBRARY_PATH)" \ + LIBPATH="$(EXTRA_PATH):${LIBPATH}" \ + SHLIB_PATH="$(EXTRA_PATH):${SHLIB_PATH}" \ + PATH="$(EXTRA_PATH):$(PATH)" \ + TCLLIBPATH="$(top_builddir)" +TCLSH_PROG = @TCLSH_PROG@ +TCLSH = $(TCLSH_ENV) $(TCLSH_PROG) +SHARED_BUILD = @SHARED_BUILD@ + +INCLUDES = @TCL_INCLUDES@ @TK_INCLUDES@ + +EXTRA_CFLAGS = $(MEM_DEBUG_FLAGS) @EXTRA_CFLAGS@ + +DEFS = $(TCL_DEFS) @DEFS@ $(EXTRA_CFLAGS) + +CONFIG_CLEAN_FILES = Makefile + +CPPFLAGS = @CPPFLAGS@ +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) pkgIndex.tcl + +libraries: + +doc-x: + @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" + +doc: + @echo "No docs to build" + +install: all install-binaries install-libraries install-doc + +install-binaries: binaries install-lib-binaries install-bin-binaries + if test "x$(SHARED_BUILD)" = "x1"; then \ + $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \ + fi + +#======================================================================== +# This rule installs platform-independent files, such as header files. +#======================================================================== + +install-libraries: libraries + @mkdir -p $(DESTDIR)$(includedir) + @echo "Installing header files in $(DESTDIR)$(includedir)" + @for i in $(GENERIC_HDRS) ; do \ + echo "Installing $$i" ; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(includedir) ; \ + done; + +#======================================================================== +# Install documentation. Unix manpages should go in the $(mandir) +# directory. +#======================================================================== + +install-doc-x: doc + @mkdir -p $(DESTDIR)$(mandir)/mann + @echo "Installing documentation in $(DESTDIR)$(mandir)" + @for i in $(srcdir)/doc/*.n; do \ + echo "Installing $$i"; \ + rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \ + done + +install-doc: + @echo "No docs to install" + +test: binaries libraries + $(TCLSH) `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS) + +shell: binaries libraries + @$(TCLSH) $(SCRIPT) + +gdb: + $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT) + +depend: + +#======================================================================== +# $($(PACKAGE)_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 $($(PACKAGE)_OBJECTS) objects are created and linked into the final +# library. In most cases these object files will correspond to the +# source files above. +#======================================================================== + +$($(PACKAGE)_LIB_FILE): $($(PACKAGE)_OBJECTS) + -rm -f $($(PACKAGE)_LIB_FILE) + ${MAKE_LIB} + $(RANLIB) $($(PACKAGE)_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:.... +#======================================================================== + +# I added leading $(srcdir) because autoconf 2.53 strips it off +VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win + +.c.$(OBJEXT): + $(COMPILE) -c `@CYGPATH@ $<` -o $@ + +#======================================================================== +# Create the pkgIndex.tcl file. +# It is usually easiest to let Tcl do this for you with pkg_mkIndex, but +# you may find that you need to customize the package. If so, either +# modify the -hand version, or create a pkgIndex.tcl.in file and have +# the configure script output the pkgIndex.tcl by editing configure.in. +#======================================================================== + +pkgIndex.tcl-x: + ( echo pkg_mkIndex . $($(PACKAGE)_LIB_FILE) \; exit; ) | $(TCLSH) + +pkgIndex.tcl-hand: + (echo 'package ifneeded $(PACKAGE) $(VERSION) \ + [list load [file join $$dir $($(PACKAGE)_LIB_FILE)]]'\ + ) > pkgIndex.tcl + +#======================================================================== +# 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 + + cp -p $(srcdir)/*.[ch] $(DIST_DIR)/ + + 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: + @mkdir -p $(DESTDIR)$(pkglibdir) + @list='$(lib_BINARIES)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \ + echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \ + $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \ + 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='$(RUNTIME_SOURCES)'; for p in $$list; do \ + if test -f $(srcdir)/library/$$p; then \ + echo " Install $$p $(DESTDIR)$(pkglibdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/library/$$p $(DESTDIR)$(pkglibdir)/$$p; \ + fi; \ + done + +#======================================================================== +# 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: + @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='$(RUNTIME_SOURCES)'; for p in $$list; do \ + 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: diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..0b05739 --- /dev/null +++ b/aclocal.m4 @@ -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 +# diff --git a/configure b/configure new file mode 100644 index 0000000..63a3898 --- /dev/null +++ b/configure @@ -0,0 +1,7052 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.56. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +# 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 Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="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="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# 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` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="generic/tkTreeCtrl.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CONFIGDIR PACKAGE VERSION treectrl_LIB_FILE CYGPATH EXEEXT TCL_VERSION TCL_BIN_DIR TCL_SRC_DIR TCL_LIB_FILE TCL_LIB_FLAG TCL_LIB_SPEC TCL_STUB_LIB_FILE TCL_STUB_LIB_FLAG TCL_STUB_LIB_SPEC TCL_LIBS TCL_DEFS TCL_EXTRA_CFLAGS TCL_LD_FLAGS TCL_SHLIB_LD_LIBS TK_VERSION TK_BIN_DIR TK_SRC_DIR TK_LIB_FILE TK_LIB_FLAG TK_LIB_SPEC TK_STUB_LIB_FILE TK_STUB_LIB_FLAG TK_STUB_LIB_SPEC TK_LIBS TK_XINCLUDES CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE RANLIB ac_ct_RANLIB TCL_TOP_DIR_NATIVE TCL_GENERIC_DIR_NATIVE TCL_UNIX_DIR_NATIVE TCL_WIN_DIR_NATIVE TCL_BMAP_DIR_NATIVE TCL_TOOL_DIR_NATIVE TCL_PLATFORM_DIR_NATIVE TCL_INCLUDES TK_TOP_DIR_NATIVE TK_UNIX_DIR_NATIVE TK_WIN_DIR_NATIVE TK_GENERIC_DIR_NATIVE TK_XLIB_DIR_NATIVE TK_PLATFORM_DIR_NATIVE TK_INCLUDES CLEANFILES EXTRA_SOURCES TCL_THREADS SHARED_BUILD AR CPP EGREP DL_LIBS CFLAGS_DEBUG CFLAGS_OPTIMIZE CFLAGS_WARNING EXTRA_CFLAGS STLIB_LD SHLIB_LD SHLIB_CFLAGS SHLIB_LDFLAGS SHLIB_LD_LIBS LDFLAGS_DEBUG LDFLAGS_OPTIMIZE TCL_DBGX CFLAGS_DEFAULT LDFLAGS_DEFAULT MAKE_LIB MAKE_SHARED_LIB MAKE_STATIC_LIB MAKE_STUB_LIB TCLSH_PROG WISH_PROG LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# 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. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +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 + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -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 | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$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 ;; + + -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 ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + 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 ;; + + -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_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=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 ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + 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'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +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 + 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 + + +# 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 its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + 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 + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# 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 this package 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 \`..'] + +_ACEOF + + cat <<_ACEOF +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] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --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] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --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 --enable-shared + --enable-64bit enable 64bit support (where applicable) + --enable-64bit-vis enable 64bit Sparc VIS support + --disable-load disallow dynamic loading and "load" command + --enable-symbols build with debugging symbols --disable-symbols + +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-tk directory containing tk configuration (tkConfig.sh) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + 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. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style 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 + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +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 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.56. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +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` +hostinfo = `(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=. + echo "PATH: $as_dir" +done + +} >&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_sep= +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=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$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 + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export 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: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +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, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core core.* *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >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 + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +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. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +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 `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; 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,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +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 + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`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. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +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_aux_dir= +for ac_dir in tclconfig $srcdir/tclconfig; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in tclconfig $srcdir/tclconfig" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in tclconfig $srcdir/tclconfig" >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +CONFIGDIR=${srcdir}/tclconfig + + +#---------------------------------------------------------------------- +# __CHANGE__ +# Set your package name and version numbers here. The NODOT_VERSION is +# required for constructing the library name on systems that don't like +# dots in library names (Windows). The VERSION variable is used on the +# other systems. +#---------------------------------------------------------------------- + +PACKAGE=treectrl + +MAJOR_VERSION=1 +MINOR_VERSION=0 +PATCHLEVEL= + +VERSION=${MAJOR_VERSION}.${MINOR_VERSION}${PATCHLEVEL} +NODOT_VERSION=${MAJOR_VERSION}${MINOR_VERSION} + + + +# This package name must be replaced statically for AC_SUBST to work + +# Substitute stub_LIB_FILE if your package creates a stub library too. +#AC_SUBST(samplestub_LIB_FILE) + +#-------------------------------------------------------------------- +# We put this here so that you can compile with -DVERSION="1.2" to +# encode the package version directly into the source files. +#-------------------------------------------------------------------- + +eval cat >>confdefs.h <<_ACEOF +#define VERSION "${VERSION}" +_ACEOF + + +#-------------------------------------------------------------------- +# Call TEA_INIT as the first TEA_ macro to set up initial vars. +# This will define a ${TEA_PLATFORM} variable == "unix" or "windows". +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking for correct TEA configuration" >&5 +echo $ECHO_N "checking for correct TEA configuration... $ECHO_C" >&6 + if test x"${PACKAGE}" = x ; then + { { echo "$as_me:$LINENO: error: +The PACKAGE variable must be defined by your TEA configure.in" >&5 +echo "$as_me: error: +The PACKAGE variable must be defined by your TEA configure.in" >&2;} + { (exit 1); exit 1; }; } + fi + echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + TEA_INITED=ok + case "`uname -s`" in + *win32*|*WIN32*|*CYGWIN_NT*|*CYGWIN_9*|*CYGWIN_ME*|*MINGW32_*) + # Extract the first word of "cygpath", so it can be a program name with args. +set dummy cygpath; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CYGPATH+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CYGPATH="cygpath -w" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" +fi +fi +CYGPATH=$ac_cv_prog_CYGPATH +if test -n "$CYGPATH"; then + echo "$as_me:$LINENO: result: $CYGPATH" >&5 +echo "${ECHO_T}$CYGPATH" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + EXEEXT=".exe" + TEA_PLATFORM="windows" + ;; + *) + CYGPATH=echo + EXEEXT="" + TEA_PLATFORM="unix" + ;; + esac + + + + + +#-------------------------------------------------------------------- +# Load the tclConfig.sh file +#-------------------------------------------------------------------- + + + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + { { echo "$as_me:$LINENO: error: Must call TEA INIT before PATH_TCLCONFIG" >&5 +echo "$as_me: error: Must call TEA INIT before PATH_TCLCONFIG" >&2;} + { (exit 1); exit 1; }; } + fi + # + # 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 or --without-tcl was given. +if test "${with_tcl+set}" = set; then + withval="$with_tcl" + with_tclconfig=${withval} +fi; + echo "$as_me:$LINENO: checking for Tcl configuration" >&5 +echo $ECHO_N "checking for Tcl configuration... $ECHO_C" >&6 + if test "${ac_cv_c_tclconfig+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + # First check to see if --with-tcl was specified. + if test x"${with_tclconfig}" != x ; then + if test -f "${with_tclconfig}/tclConfig.sh" ; then + ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)` + else + { { echo "$as_me:$LINENO: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&5 +echo "$as_me: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&2;} + { (exit 1); exit 1; }; } + 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]* 2>/dev/null` \ + ../../tcl \ + `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \ + ../../../tcl \ + `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do + if test -f "$i/unix/tclConfig.sh" ; then + ac_cv_c_tclconfig=`(cd $i/unix; 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 ${exec_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` \ + ; 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]* 2>/dev/null` ; do + 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" + { echo "$as_me:$LINENO: WARNING: \"Cannot find Tcl configuration definitions\"" >&5 +echo "$as_me: WARNING: \"Cannot find Tcl configuration definitions\"" >&2;} + exit 0 + else + no_tcl= + TCL_BIN_DIR=${ac_cv_c_tclconfig} + echo "$as_me:$LINENO: result: found $TCL_BIN_DIR/tclConfig.sh" >&5 +echo "${ECHO_T}found $TCL_BIN_DIR/tclConfig.sh" >&6 + fi + fi + + + echo "$as_me:$LINENO: checking for existence of $TCL_BIN_DIR/tclConfig.sh" >&5 +echo $ECHO_N "checking for existence of $TCL_BIN_DIR/tclConfig.sh... $ECHO_C" >&6 + + if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then + echo "$as_me:$LINENO: result: loading" >&5 +echo "${ECHO_T}loading" >&6 + . $TCL_BIN_DIR/tclConfig.sh + else + echo "$as_me:$LINENO: result: file not found" >&5 +echo "${ECHO_T}file not found" >&6 + fi + + # + # 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} + fi + + # + # eval is required to do the TCL_DBGX substitution + # + + eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" + eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" + eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" + + eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" + eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" + eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" + + + + + + + + + + + + + + #AC_SUBST(TCL_DBGX) + + + + + + #AC_SUBST(TCL_BUILD_LIB_SPEC) + #AC_SUBST(TCL_BUILD_STUB_LIB_SPEC) + + +#-------------------------------------------------------------------- +# Load the tkConfig.sh file if necessary (Tk extension) +#-------------------------------------------------------------------- + + + # + # 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 + +# Check whether --with-tk or --without-tk was given. +if test "${with_tk+set}" = set; then + withval="$with_tk" + with_tkconfig=${withval} +fi; + echo "$as_me:$LINENO: checking for Tk configuration" >&5 +echo $ECHO_N "checking for Tk configuration... $ECHO_C" >&6 + if test "${ac_cv_c_tkconfig+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + # First check to see if --with-tkconfig was specified. + if test x"${with_tkconfig}" != x ; then + if test -f "${with_tkconfig}/tkConfig.sh" ; then + ac_cv_c_tkconfig=`(cd ${with_tkconfig}; pwd)` + else + { { echo "$as_me:$LINENO: error: ${with_tkconfig} directory doesn't contain tkConfig.sh" >&5 +echo "$as_me: error: ${with_tkconfig} directory doesn't contain tkConfig.sh" >&2;} + { (exit 1); exit 1; }; } + 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]* 2>/dev/null` \ + ../../tk \ + `ls -dr ../../tk[8-9].[0-9]* 2>/dev/null` \ + ../../../tk \ + `ls -dr ../../../tk[8-9].[0-9]* 2>/dev/null` ; do + if test -f "$i/unix/tkConfig.sh" ; then + ac_cv_c_tkconfig=`(cd $i/unix; 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 ${exec_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` \ + ; 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]* 2>/dev/null` ; do + if test -f "$i/unix/tkConfig.sh" ; then + ac_cv_c_tkconfig=`(cd $i/unix; pwd)` + break + fi + done + fi + +fi + + if test x"${ac_cv_c_tkconfig}" = x ; then + TK_BIN_DIR="# no Tk configs found" + { echo "$as_me:$LINENO: WARNING: \"Cannot find Tk configuration definitions\"" >&5 +echo "$as_me: WARNING: \"Cannot find Tk configuration definitions\"" >&2;} + exit 0 + else + no_tk= + TK_BIN_DIR=${ac_cv_c_tkconfig} + echo "$as_me:$LINENO: result: found $TK_BIN_DIR/tkConfig.sh" >&5 +echo "${ECHO_T}found $TK_BIN_DIR/tkConfig.sh" >&6 + fi + fi + + + + echo "$as_me:$LINENO: checking for existence of ${TK_BIN_DIR}/tkConfig.sh" >&5 +echo $ECHO_N "checking for existence of ${TK_BIN_DIR}/tkConfig.sh... $ECHO_C" >&6 + + if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then + echo "$as_me:$LINENO: result: loading" >&5 +echo "${ECHO_T}loading" >&6 + . $TK_BIN_DIR/tkConfig.sh + else + echo "$as_me:$LINENO: result: could not find ${TK_BIN_DIR}/tkConfig.sh" >&5 +echo "${ECHO_T}could not find ${TK_BIN_DIR}/tkConfig.sh" >&6 + fi + + # + # 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} + fi + + # + # eval is required to do the TK_DBGX substitution + # + + eval "TK_LIB_FILE=\"${TK_LIB_FILE}\"" + eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\"" + eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\"" + + eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\"" + eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\"" + eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\"" + + + + + + + + + + + + + + + + + +#----------------------------------------------------------------------- +# Handle the --prefix=... option by defaulting to what Tcl gave. +# Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER. +#----------------------------------------------------------------------- + + + # Should be AC_MSG_NOTICE, but that requires autoconf 2.50 + if test "${prefix}" = "NONE"; then + prefix_default=yes + if test x"${TCL_PREFIX}" != x; then + { echo "$as_me:$LINENO: WARNING: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&5 +echo "$as_me: WARNING: --prefix defaulting to TCL_PREFIX ${TCL_PREFIX}" >&2;} + prefix=${TCL_PREFIX} + else + prefix=/usr/local + fi + fi + if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" ; then + if test x"${TCL_EXEC_PREFIX}" != x; then + { echo "$as_me:$LINENO: WARNING: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&5 +echo "$as_me: WARNING: --exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}" >&2;} + exec_prefix=${TCL_EXEC_PREFIX} + else + 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. +#----------------------------------------------------------------------- + + + + # 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$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" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +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 + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out 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. +echo "$as_me:$LINENO: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; 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 | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$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 +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* 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; +} +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 +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +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 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 + echo "$as_me:$LINENO: checking if the compiler understands -pipe" >&5 +echo $ECHO_N "checking if the compiler understands -pipe... $ECHO_C" >&6 + OLDCC="$CC" + CC="$CC -pipe" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +CC="$OLDCC" + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest.$ac_objext conftest.$ac_ext + fi + + # 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" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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/* | \ + /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 $as_executable_p "$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 + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +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. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$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' + + + #-------------------------------------------------------------------- + # Checks to see if the make program sets the $MAKE variable. + #-------------------------------------------------------------------- + + echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + #-------------------------------------------------------------------- + # Determines the correct binary file extension (.o, .obj, .exe etc.) + #-------------------------------------------------------------------- + + + + + +#-------------------------------------------------------------------- +# __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 + + echo "$as_me:$LINENO: checking for Tcl private include files" >&5 +echo $ECHO_N "checking for Tcl private include files... $ECHO_C" >&6 + + if test "${TEA_PLATFORM}" = "windows"; then + TCL_TOP_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}`\" + TCL_GENERIC_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/generic`\" + TCL_UNIX_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/unix`\" + TCL_WIN_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/win`\" + TCL_BMAP_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/bitmaps`\" + TCL_TOOL_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/tools`\" + TCL_COMPAT_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/compat`\" + TCL_PLATFORM_DIR_NATIVE=${TCL_WIN_DIR_NATIVE} + else + TCL_TOP_DIR_NATIVE='$(TCL_SRC_DIR)' + TCL_GENERIC_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/generic' + TCL_UNIX_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/unix' + TCL_WIN_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/win' + TCL_BMAP_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/bitmaps' + TCL_TOOL_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/tools' + TCL_COMPAT_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/compat' + TCL_PLATFORM_DIR_NATIVE=${TCL_UNIX_DIR_NATIVE} + fi + + + + + + + + + + TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}" + + echo "$as_me:$LINENO: result: Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}" >&5 +echo "${ECHO_T}Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}" >&6 + + +#TEA_PUBLIC_TK_HEADERS + + echo "$as_me:$LINENO: checking for Tk private include files" >&5 +echo $ECHO_N "checking for Tk private include files... $ECHO_C" >&6 + + if test "${TEA_PLATFORM}" = "windows"; then + TK_TOP_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}`\" + TK_UNIX_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/unix`\" + TK_WIN_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/win`\" + TK_GENERIC_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/generic`\" + TK_XLIB_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/xlib`\" + TK_PLATFORM_DIR_NATIVE=${TK_WIN_DIR_NATIVE} + + TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE} -I${TK_XLIB_DIR_NATIVE}" + else + TK_TOP_DIR_NATIVE='$(TK_SRC_DIR)' + TK_GENERIC_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/generic' + TK_UNIX_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/unix' + TK_WIN_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/win' + TK_PLATFORM_DIR_NATIVE=${TK_UNIX_DIR_NATIVE} + + TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}" + fi + + + + + + + + + + echo "$as_me:$LINENO: result: Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}" >&5 +echo "${ECHO_T}Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}" >&6 + + +#-------------------------------------------------------------------- +# __CHANGE__ +# A few miscellaneous platform-specific items: +# +# Define a special symbol for Windows (BUILD_sample in this case) so +# that we create the export library with the dll. See sha1.h on how +# to use this. +# +# Windows creates a few extra files that need to be cleaned up. +# You can add more files to clean if your extension creates any extra +# files. +# +# Define any extra compiler flags in the PACKAGE_CFLAGS variable. +# These will be appended to the current set of compiler flags for +# your system. +#-------------------------------------------------------------------- + +if test "${TEA_PLATFORM}" = "windows" ; then + cat >>confdefs.h <<\_ACEOF +#define BUILD_treectrl 1 +_ACEOF + + CLEANFILES="pkgIndex.tcl *.lib *.dll *.exp *.ilk *.pdb vc*.pch" + EXTRA_SOURCES='$(WIN_SOURCES)' +else + CLEANFILES="pkgIndex.tcl" + EXTRA_SOURCES='$(UNIX_SOURCES)' +fi + + + +#-------------------------------------------------------------------- +# Check whether --enable-threads or --disable-threads was given. +#-------------------------------------------------------------------- + + + # Check whether --enable-threads or --disable-threads was given. +if test "${enable_threads+set}" = set; then + enableval="$enable_threads" + tcl_ok=$enableval +else + tcl_ok= +fi; + + if test "$tcl_ok" = "yes"; then + TCL_THREADS=1 + + if test "${TEA_PLATFORM}" != "windows" ; then + # We are always OK on Windows, so check what this platform wants. + cat >>confdefs.h <<\_ACEOF +#define USE_THREAD_ALLOC 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _REENTRANT 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _THREAD_SAFE 1 +_ACEOF + + echo "$as_me:$LINENO: checking for pthread_mutex_init in -lpthread" >&5 +echo $ECHO_N "checking for pthread_mutex_init in -lpthread... $ECHO_C" >&6 +if test "${ac_cv_lib_pthread_pthread_mutex_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_mutex_init (); +int +main () +{ +pthread_mutex_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthread_pthread_mutex_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pthread_pthread_mutex_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_mutex_init" >&5 +echo "${ECHO_T}$ac_cv_lib_pthread_pthread_mutex_init" >&6 +if test $ac_cv_lib_pthread_pthread_mutex_init = 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] + echo "$as_me:$LINENO: checking for __pthread_mutex_init in -lpthread" >&5 +echo $ECHO_N "checking for __pthread_mutex_init in -lpthread... $ECHO_C" >&6 +if test "${ac_cv_lib_pthread___pthread_mutex_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char __pthread_mutex_init (); +int +main () +{ +__pthread_mutex_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthread___pthread_mutex_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pthread___pthread_mutex_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pthread___pthread_mutex_init" >&5 +echo "${ECHO_T}$ac_cv_lib_pthread___pthread_mutex_init" >&6 +if test $ac_cv_lib_pthread___pthread_mutex_init = 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 + echo "$as_me:$LINENO: checking for pthread_mutex_init in -lpthreads" >&5 +echo $ECHO_N "checking for pthread_mutex_init in -lpthreads... $ECHO_C" >&6 +if test "${ac_cv_lib_pthreads_pthread_mutex_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthreads $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_mutex_init (); +int +main () +{ +pthread_mutex_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthreads_pthread_mutex_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pthreads_pthread_mutex_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pthreads_pthread_mutex_init" >&5 +echo "${ECHO_T}$ac_cv_lib_pthreads_pthread_mutex_init" >&6 +if test $ac_cv_lib_pthreads_pthread_mutex_init = yes; then + tcl_ok=yes +else + tcl_ok=no +fi + + if test "$tcl_ok" = "yes"; then + # The space is needed + THREADS_LIBS=" -lpthreads" + else + echo "$as_me:$LINENO: checking for pthread_mutex_init in -lc" >&5 +echo $ECHO_N "checking for pthread_mutex_init in -lc... $ECHO_C" >&6 +if test "${ac_cv_lib_c_pthread_mutex_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_mutex_init (); +int +main () +{ +pthread_mutex_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_c_pthread_mutex_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_c_pthread_mutex_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_c_pthread_mutex_init" >&5 +echo "${ECHO_T}$ac_cv_lib_c_pthread_mutex_init" >&6 +if test $ac_cv_lib_c_pthread_mutex_init = yes; then + tcl_ok=yes +else + tcl_ok=no +fi + + if test "$tcl_ok" = "no"; then + echo "$as_me:$LINENO: checking for pthread_mutex_init in -lc_r" >&5 +echo $ECHO_N "checking for pthread_mutex_init in -lc_r... $ECHO_C" >&6 +if test "${ac_cv_lib_c_r_pthread_mutex_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc_r $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_mutex_init (); +int +main () +{ +pthread_mutex_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_c_r_pthread_mutex_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_c_r_pthread_mutex_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_c_r_pthread_mutex_init" >&5 +echo "${ECHO_T}$ac_cv_lib_c_r_pthread_mutex_init" >&6 +if test $ac_cv_lib_c_r_pthread_mutex_init = 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 + { echo "$as_me:$LINENO: WARNING: \"Don t know how to find pthread lib on your system - thread support disabled\"" >&5 +echo "$as_me: WARNING: \"Don t know how to find pthread lib on your system - thread support disabled\"" >&2;} + fi + fi + fi + fi + + # Does the pthread-implementation provide + # 'pthread_attr_setstacksize' ? + +for ac_func in pthread_attr_setstacksize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + fi + else + TCL_THREADS=0 + fi + # Do checking message here to not mess up interleaved configure output + echo "$as_me:$LINENO: checking for building with threads" >&5 +echo $ECHO_N "checking for building with threads... $ECHO_C" >&6 + if test "${TCL_THREADS}" = "1"; then + cat >>confdefs.h <<\_ACEOF +#define TCL_THREADS 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no (default)" >&5 +echo "${ECHO_T}no (default)" >&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 + { echo "$as_me:$LINENO: WARNING: + Building ${PACKAGE} without threads enabled, but building against a Tcl + that IS thread-enabled." >&5 +echo "$as_me: WARNING: + Building ${PACKAGE} without threads enabled, but building against a Tcl + that IS thread-enabled." >&2;} + fi + ;; + *) + if test "${TCL_THREADS}" = "1"; then + { echo "$as_me:$LINENO: WARNING: + --enable-threads requested, but attempting building against a Tcl + that is NOT thread-enabled." >&5 +echo "$as_me: WARNING: + --enable-threads requested, but attempting building against a Tcl + that is NOT thread-enabled." >&2;} + fi + ;; + esac + + + +#-------------------------------------------------------------------- +# The statement below defines a collection of symbols related to +# building as a shared library instead of a static library. +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking how to build libraries" >&5 +echo $ECHO_N "checking how to build libraries... $ECHO_C" >&6 + # Check whether --enable-shared or --disable-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 + echo "$as_me:$LINENO: result: shared" >&5 +echo "${ECHO_T}shared" >&6 + SHARED_BUILD=1 + else + echo "$as_me:$LINENO: result: static" >&5 +echo "${ECHO_T}static" >&6 + SHARED_BUILD=0 + cat >>confdefs.h <<\_ACEOF +#define STATIC_BUILD 1 +_ACEOF + + 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. +#-------------------------------------------------------------------- + +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 +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&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 + echo $ECHO_N "(cached) $ECHO_C" >&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. + # 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # 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 +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$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. + # 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # 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 + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +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 + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f 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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_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 >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#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)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +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=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + { { echo "$as_me:$LINENO: error: Must call TEA INIT before CONFIG_CFLAGS" >&5 +echo "$as_me: error: Must call TEA INIT before CONFIG_CFLAGS" >&2;} + { (exit 1); exit 1; }; } + fi + + # Step 0: Enable 64 bit support? + + echo "$as_me:$LINENO: checking if 64bit support is enabled" >&5 +echo $ECHO_N "checking if 64bit support is enabled... $ECHO_C" >&6 + # Check whether --enable-64bit or --disable-64bit was given. +if test "${enable_64bit+set}" = set; then + enableval="$enable_64bit" + do64bit=$enableval +else + do64bit=no +fi; + echo "$as_me:$LINENO: result: $do64bit" >&5 +echo "${ECHO_T}$do64bit" >&6 + + # Step 0.b: Enable Solaris 64 bit VIS support? + + echo "$as_me:$LINENO: checking if 64bit Sparc VIS support is requested" >&5 +echo $ECHO_N "checking if 64bit Sparc VIS support is requested... $ECHO_C" >&6 + # Check whether --enable-64bit-vis or --disable-64bit-vis was given. +if test "${enable_64bit_vis+set}" = set; then + enableval="$enable_64bit_vis" + do64bitVIS=$enableval +else + do64bitVIS=no +fi; + echo "$as_me:$LINENO: result: $do64bitVIS" >&5 +echo "${ECHO_T}$do64bitVIS" >&6 + + if test "$do64bitVIS" = "yes"; then + # Force 64bit on with VIS + do64bit=yes + fi + + # Step 1: set the variable "system" to hold the name and version number + # for the system. This can usually be done via the "uname" command, but + # there are a few systems, like Next, where this doesn't work. + + echo "$as_me:$LINENO: checking system version (for dynamic loading)" >&5 +echo $ECHO_N "checking system version (for dynamic loading)... $ECHO_C" >&6 + if test -f /usr/lib/NextStep/software_version; then + system=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version` + else + system=`uname -s`-`uname -r` + if test "$?" -ne 0 ; then + echo "$as_me:$LINENO: result: unknown (can't find uname command)" >&5 +echo "${ECHO_T}unknown (can't find uname command)" >&6 + system=unknown + else + # Special check for weird MP-RAS system (uname returns weird + # results, and the version is kept in special file). + + if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then + system=MP-RAS-`awk '{print }' /etc/.relid'` + fi + if test "`uname -s`" = "AIX" ; then + system=AIX-`uname -v`.`uname -r` + fi + if test "${TEA_PLATFORM}" = "windows" ; then + system=windows + fi + echo "$as_me:$LINENO: result: $system" >&5 +echo "${ECHO_T}$system" >&6 + fi + fi + + # Step 2: check for existence of -ldl library. This is needed because + # Linux can use either -ldl or -ldld for dynamic loading. + + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + have_dl=yes +else + have_dl=no +fi + + + # Step 3: set configuration options based on system name and version. + + do64bit_ok=no + EXTRA_CFLAGS="" + TCL_EXPORT_FILE_SUFFIX="" + UNSHARED_LIB_SUFFIX="" + TCL_TRIM_DOTS='`echo ${VERSION} | tr -d .`' + ECHO_VERSION='`echo ${VERSION}`' + TCL_LIB_VERSIONS_OK=ok + CFLAGS_DEBUG=-g + CFLAGS_OPTIMIZE=-O + if test "$GCC" = "yes" ; then + CFLAGS_WARNING="-Wall -Wconversion -Wno-implicit-int" + else + CFLAGS_WARNING="" + fi + TCL_NEEDS_EXP_FILE=0 + TCL_BUILD_EXP_FILE="" + TCL_EXP_FILE="" + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + echo "$as_me:$LINENO: result: $AR" >&5 +echo "${ECHO_T}$AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STLIB_LD='${AR} cr' + case $system in + 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. + if test "$do64bit" = "yes" ; then + if test "x${MSSDK}x" = "xx" ; then + MSSDK="C:/Progra~1/Microsoft SDK" + fi + # In order to work in the tortured autoconf environment, + # we need to ensure that this path has no spaces + MSSDK=`cygpath -w -s "$MSSDK" | sed -e 's!\\\!/!g'` + if test ! -d "${MSSDK}/bin/win64" ; then + { echo "$as_me:$LINENO: WARNING: \"could not find 64-bit SDK to enable 64bit mode\"" >&5 +echo "$as_me: WARNING: \"could not find 64-bit SDK to enable 64bit mode\"" >&2;} + do64bit="no" + else + do64bit_ok="yes" + fi + fi + + if test "${SHARED_BUILD}" = "0" ; then + runtime=-MT + else + runtime=-MD + fi + + if test "$do64bit" = "yes" ; then + # All this magic is necessary for the Win64 SDK RC1 - hobbs + export CC="${MSSDK}/Bin/Win64/cl.exe \ + -I${MSSDK}/Include/prerelease -I${MSSDK}/Include/Win64/crt \ + -I${MSSDK}/Include" + export RC="${MSSDK}/bin/rc.exe" + export lflags="-MACHINE:IA64 -LIBPATH:${MSSDK}/Lib/IA64 \ + -LIBPATH:${MSSDK}/Lib/Prerelease/IA64" + export STLIB_LD="${MSSDK}/bin/win64/lib.exe -nologo ${lflags}" + export LINKBIN="${MSSDK}/bin/win64/link.exe ${lflags}" + CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" + CFLAGS_OPTIMIZE="-nologo -O2 -Gs -W2 ${runtime}" + else + RC="rc" + STLIB_LD="lib -nologo" + LINKBIN="link -link50compat" + CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" + CFLAGS_OPTIMIZE="-nologo -O2 -Gs -GD -W2 ${runtime}" + fi + + if test "$MINGW32" = "yes"; then + # mingw gcc mode + CFLAGS_DEBUG="-g" + CFLAGS_OPTIMIZE="-O2" + SHLIB_LD="gcc -shared" + STLIB_LD='${AR} cr' + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" + LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" + else + SHLIB_LD="${LINKBIN} -dll -nologo" + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.lib' + EXTRA_CFLAGS="-YX" + # For information on what debugtype is most useful, see: + # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp + # This essentially turns it all on. + LDFLAGS_DEBUG="-debug:full -debugtype:both -warn:2" + LDFLAGS_OPTIMIZE="-release" + LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" + LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" + PATHTYPE=-w + fi + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".dll" + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.dll' + + TCL_LIB_VERSIONS_OK=nodots + # Bogus to avoid getting this turned off + DL_OBJS="tclLoadNone.obj" + ;; + AIX-*) + if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes" ; then + # AIX requires the _r compiler when gcc isn't being used + if test "${CC}" != "cc_r" ; then + CC=${CC}_r + fi + echo "$as_me:$LINENO: result: Using $CC for compiling with threads" >&5 +echo "${ECHO_T}Using $CC for compiling with threads" >&6 + fi + LIBS="$LIBS -lc" + SHLIB_CFLAGS="" + SHLIB_SUFFIX=".so" + SHLIB_LD_LIBS='${LIBS}' + 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" + # AIX-5 has dl* in libc.so + DL_LIBS="" + if test "$GCC" = "yes" ; then + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + LD_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' + fi + else + SHLIB_LD="${TCL_SRC_DIR}/unix/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" + DL_LIBS="-ldl" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + TCL_NEEDS_EXP_FILE=1 + TCL_EXPORT_FILE_SUFFIX='${VERSION}\$\{DBGX\}.exp' + fi + DL_OBJS="tclLoadDl.o" + LDFLAGS="" + + # AIX v<=4.1 has some different flags than 4.2+ + if test "$system" = "AIX-4.1" -o "`uname -v`" -lt "4" ; then + LIBOBJS="$LIBOBJS tclLoadAix.o" + DL_LIBS="-lld" + fi + + # On AIX <=v4 systems, libbsd.a has to be linked in to support + # non-blocking file IO. This library has to be linked in after + # the MATH_LIBS or it breaks the pow() function. The way to + # insure proper sequencing, is to add it to the tail of MATH_LIBS. + # This library also supplies gettimeofday. + # + # AIX does not have a timezone field in struct tm. When the AIX + # bsd library is used, the timezone global and the gettimeofday + # methods are to be avoided for timezone deduction instead, we + # deduce the timezone by comparing the localtime result on a + # known GMT value. + + echo "$as_me:$LINENO: checking for gettimeofday in -lbsd" >&5 +echo $ECHO_N "checking for gettimeofday in -lbsd... $ECHO_C" >&6 +if test "${ac_cv_lib_bsd_gettimeofday+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gettimeofday (); +int +main () +{ +gettimeofday (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_bsd_gettimeofday=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_bsd_gettimeofday=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gettimeofday" >&5 +echo "${ECHO_T}$ac_cv_lib_bsd_gettimeofday" >&6 +if test $ac_cv_lib_bsd_gettimeofday = yes; then + libbsd=yes +else + libbsd=no +fi + + if test $libbsd = yes; then + MATH_LIBS="$MATH_LIBS -lbsd" + cat >>confdefs.h <<\_ACEOF +#define USE_DELTA_FOR_TZ 1 +_ACEOF + + fi + + # Check to enable 64-bit flags for compiler/linker on AIX 4+ + if test "$do64bit" = "yes" -a "`uname -v`" -gt "3" ; then + if test "$GCC" = "yes" ; then + { echo "$as_me:$LINENO: WARNING: \"64bit mode not supported with GCC on $system\"" >&5 +echo "$as_me: WARNING: \"64bit mode not supported with GCC on $system\"" >&2;} + else + do64bit_ok=yes + EXTRA_CFLAGS="-q64" + LDFLAGS="-q64" + RANLIB="${RANLIB} -X64" + AR="${AR} -X64" + SHLIB_LDFLAGS="-b64" + fi + fi + ;; + BSD/OS-2.1*|BSD/OS-3*) + SHLIB_CFLAGS="" + SHLIB_LD="shlicc -r" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + BSD/OS-4.*) + SHLIB_CFLAGS="-export-dynamic -fPIC" + SHLIB_LD="cc -shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-export-dynamic" + LD_SEARCH_FLAGS="" + ;; + dgux*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + HP-UX-*.11.*) + # Use updated header definitions where possible + cat >>confdefs.h <<\_ACEOF +#define _XOPEN_SOURCE_EXTENDED 1 +_ACEOF + + + SHLIB_SUFFIX=".sl" + echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 +echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +int +main () +{ +shl_load (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_shl_load=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 +if test $ac_cv_lib_dld_shl_load = yes; then + tcl_ok=yes +else + tcl_ok=no +fi + + if test "$tcl_ok" = yes; then + SHLIB_CFLAGS="+z" + SHLIB_LD="ld -b" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadShl.o" + DL_LIBS="-ldld" + LDFLAGS="-Wl,-E" + LD_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' + fi + + # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc + #EXTRA_CFLAGS="+DAportable" + + # Check to enable 64-bit flags for compiler/linker + if test "$do64bit" = "yes" ; then + if test "$GCC" = "yes" ; then + hpux_arch=`gcc -dumpmachine` + case $hpux_arch in + hppa64*) + # 64-bit gcc in use. Fix flags for GNU ld. + do64bit_ok=yes + SHLIB_LD="gcc -shared" + SHLIB_LD_LIBS="" + LD_SEARCH_FLAGS='' + ;; + *) + { echo "$as_me:$LINENO: WARNING: \"64bit mode not supported with GCC on $system\"" >&5 +echo "$as_me: WARNING: \"64bit mode not supported with GCC on $system\"" >&2;} + ;; + esac + else + do64bit_ok=yes + EXTRA_CFLAGS="+DA2.0W" + LDFLAGS="+DA2.0W $LDFLAGS" + fi + fi + ;; + HP-UX-*.08.*|HP-UX-*.09.*|HP-UX-*.10.*) + SHLIB_SUFFIX=".sl" + echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 +echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +int +main () +{ +shl_load (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_shl_load=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 +if test $ac_cv_lib_dld_shl_load = yes; then + tcl_ok=yes +else + tcl_ok=no +fi + + if test "$tcl_ok" = yes; then + SHLIB_CFLAGS="+z" + SHLIB_LD="ld -b" + SHLIB_LD_LIBS="" + DL_OBJS="tclLoadShl.o" + DL_LIBS="-ldld" + LDFLAGS="-Wl,-E" + LD_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' + fi + ;; + IRIX-4.*) + SHLIB_CFLAGS="-G 0" + SHLIB_SUFFIX=".a" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + SHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}.a' + ;; + IRIX-5.*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + EXTRA_CFLAGS="" + LDFLAGS="" + ;; + IRIX-6.*|IRIX64-6.5*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -n32 -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + if test "$GCC" = "yes" ; then + EXTRA_CFLAGS="-mabi=n32" + LDFLAGS="-mabi=n32" + else + case $system in + IRIX-6.3) + # Use to build 6.2 compatible binaries on 6.3. + EXTRA_CFLAGS="-n32 -D_OLD_TERMIOS" + ;; + *) + EXTRA_CFLAGS="-n32" + ;; + esac + LDFLAGS="-n32" + fi + ;; + IRIX64-6.*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -n32 -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + + # Check to enable 64-bit flags for compiler/linker + + if test "$do64bit" = "yes" ; then + if test "$GCC" = "yes" ; then + { echo "$as_me:$LINENO: WARNING: 64bit mode not supported by gcc" >&5 +echo "$as_me: WARNING: 64bit mode not supported by gcc" >&2;} + else + do64bit_ok=yes + SHLIB_LD="ld -64 -shared -rdata_shared" + EXTRA_CFLAGS="-64" + LDFLAGS="-64" + fi + fi + ;; + Linux*) + SHLIB_CFLAGS="-fPIC" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + + # egcs-2.91.66 on Redhat Linux 6.0 generates lots of warnings + # when you inline the string and math operations. Turn this off to + # get rid of the warnings. + + CFLAGS_OPTIMIZE="${CFLAGS_OPTIMIZE} -D__NO_STRING_INLINES -D__NO_MATH_INLINES" + + if test "$have_dl" = yes; then + SHLIB_LD="${CC} -shared" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-rdynamic" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + else + if test "${ac_cv_header_dld_h+set}" = set; then + echo "$as_me:$LINENO: checking for dld.h" >&5 +echo $ECHO_N "checking for dld.h... $ECHO_C" >&6 +if test "${ac_cv_header_dld_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dld_h" >&5 +echo "${ECHO_T}$ac_cv_header_dld_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking dld.h usability" >&5 +echo $ECHO_N "checking dld.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking dld.h presence" >&5 +echo $ECHO_N "checking dld.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: dld.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: dld.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dld.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: dld.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: dld.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: dld.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dld.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for dld.h" >&5 +echo $ECHO_N "checking for dld.h... $ECHO_C" >&6 +if test "${ac_cv_header_dld_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_dld_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dld_h" >&5 +echo "${ECHO_T}$ac_cv_header_dld_h" >&6 + +fi +if test $ac_cv_header_dld_h = yes; then + + SHLIB_LD="ld -shared" + DL_OBJS="tclLoadDld.o" + DL_LIBS="-ldld" + LDFLAGS="" + LD_SEARCH_FLAGS="" +fi + + + fi + if test "`uname -m`" = "alpha" ; then + EXTRA_CFLAGS="-mieee" + 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"${LIBOBJS}" != x ; then + EXTRA_CFLAGS="${EXTRA_CFLAGS} -fno-inline" + fi + + ;; + GNU*) + SHLIB_CFLAGS="-fPIC" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + + if test "$have_dl" = yes; then + SHLIB_LD="${CC} -shared" + DL_OBJS="" + DL_LIBS="-ldl" + LDFLAGS="-rdynamic" + LD_SEARCH_FLAGS="" + else + if test "${ac_cv_header_dld_h+set}" = set; then + echo "$as_me:$LINENO: checking for dld.h" >&5 +echo $ECHO_N "checking for dld.h... $ECHO_C" >&6 +if test "${ac_cv_header_dld_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dld_h" >&5 +echo "${ECHO_T}$ac_cv_header_dld_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking dld.h usability" >&5 +echo $ECHO_N "checking dld.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking dld.h presence" >&5 +echo $ECHO_N "checking dld.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: dld.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: dld.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dld.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: dld.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: dld.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: dld.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: dld.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dld.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for dld.h" >&5 +echo $ECHO_N "checking for dld.h... $ECHO_C" >&6 +if test "${ac_cv_header_dld_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_dld_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dld_h" >&5 +echo "${ECHO_T}$ac_cv_header_dld_h" >&6 + +fi +if test $ac_cv_header_dld_h = yes; then + + SHLIB_LD="ld -shared" + DL_OBJS="" + DL_LIBS="-ldld" + LDFLAGS="" + LD_SEARCH_FLAGS="" +fi + + + fi + if test "`uname -m`" = "alpha" ; then + EXTRA_CFLAGS="-mieee" + fi + ;; + MP-RAS-02*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + MP-RAS-*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-Wl,-Bexport" + LD_SEARCH_FLAGS="" + ;; + NetBSD-*|FreeBSD-[1-2].*|OpenBSD-*) + # Not available on all versions: check for include file. + if test "${ac_cv_header_dlfcn_h+set}" = set; then + echo "$as_me:$LINENO: checking for dlfcn.h" >&5 +echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6 +if test "${ac_cv_header_dlfcn_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5 +echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking dlfcn.h usability" >&5 +echo $ECHO_N "checking dlfcn.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking dlfcn.h presence" >&5 +echo $ECHO_N "checking dlfcn.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: dlfcn.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: dlfcn.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: dlfcn.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: dlfcn.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for dlfcn.h" >&5 +echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6 +if test "${ac_cv_header_dlfcn_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_dlfcn_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5 +echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6 + +fi +if test $ac_cv_header_dlfcn_h = yes; then + + # NetBSD/SPARC needs -fPIC, -fpic will not do. + SHLIB_CFLAGS="-fPIC" + SHLIB_LD="ld -Bshareable -x" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + echo "$as_me:$LINENO: checking for ELF" >&5 +echo $ECHO_N "checking for ELF... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#ifdef __ELF__ + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so' +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1.0' + +fi +rm -f conftest* + + +else + + SHLIB_CFLAGS="" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".a" + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + +fi + + + + # FreeBSD doesn't handle version numbers with dots. + + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + TCL_LIB_VERSIONS_OK=nodots + ;; + FreeBSD-*) + # FreeBSD 3.* and greater have ELF. + SHLIB_CFLAGS="-fPIC" + SHLIB_LD="ld -Bshareable -x" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="-export-dynamic" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + if test "${TCL_THREADS}" = "1" ; then + # The -pthread needs to go in the CFLAGS, not LIBS + LIBS=`echo $LIBS | sed s/-pthread//` + EXTRA_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}\$\{DBGX\}.a' + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so' + TCL_LIB_VERSIONS_OK=nodots + ;; + esac + ;; + Rhapsody-*|Darwin-*) + SHLIB_CFLAGS="-fno-common" + SHLIB_LD="cc -dynamiclib \${LDFLAGS}" + TCL_SHLIB_LD_EXTRAS="-compatibility_version ${TCL_MAJOR_VERSION} -current_version \${VERSION} -install_name \${LIB_RUNTIME_DIR}/\${TCL_LIB_FILE} -prebind -seg1addr 0xa000000" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".dylib" + DL_OBJS="tclLoadDyld.o" + DL_LIBS="" + LDFLAGS="-prebind" + LD_SEARCH_FLAGS="" + CFLAGS_OPTIMIZE="-O3" + EXTRA_CFLAGS="-arch ppc -pipe" + ;; + NEXTSTEP-*) + SHLIB_CFLAGS="" + SHLIB_LD="cc -nostdlib -r" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadNext.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + OS/390-*) + CFLAGS_OPTIMIZE="" # Optimizer is buggy + cat >>confdefs.h <<\_ACEOF +#define _OE_SOCKETS 1 +_ACEOF + # needed in sys/socket.h + ;; + OSF1-1.0|OSF1-1.1|OSF1-1.2) + # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1 + SHLIB_CFLAGS="" + # Hack: make package name same as library name + SHLIB_LD='ld -R -export :' + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadOSF.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + OSF1-1.*) + # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2 + SHLIB_CFLAGS="-fPIC" + if test "$SHARED_BUILD" = "1" ; then + SHLIB_LD="ld -shared" + else + SHLIB_LD="ld -non_shared" + fi + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + 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_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' + if test "$GCC" != "yes" ; then + EXTRA_CFLAGS="-DHAVE_TZSET -std1" + fi + # see pthread_intro(3) for pthread support on osf1, k.furukawa + if test "${TCL_THREADS}" = "1" ; then + EXTRA_CFLAGS="${EXTRA_CFLAGS} -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" + EXTRA_CFLAGS="${EXTRA_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 + EXTRA_CFLAGS="${EXTRA_CFLAGS} -pthread" + 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" + DL_OBJS="tclLoadDl.o" + # dlopen is in -lc on QNX + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + RISCos-*) + SHLIB_CFLAGS="-G 0" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".a" + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + ;; + SCO_SV-3.2*) + # Note, dlopen is available only on SCO 3.2.5 and greater. However, + # this test works, since "uname -s" was non-standard in 3.2.4 and + # below. + if test "$GCC" = "yes" ; then + SHLIB_CFLAGS="-fPIC -melf" + LDFLAGS="-melf -Wl,-Bexport" + else + SHLIB_CFLAGS="-Kpic -belf" + LDFLAGS="-belf -Wl,-Bexport" + fi + SHLIB_LD="ld -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS="" + ;; + SINIX*5.4*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + SunOS-4*) + SHLIB_CFLAGS="-PIC" + SHLIB_LD="ld" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + + # SunOS can't handle version numbers with dots in them in library + # specs, like -ltcl7.5, so use -ltcl75 instead. Also, it + # requires an extra version number at the end of .so file names. + # So, the library has to have a name like libtcl75.so.1.0 + + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1.0' + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + TCL_LIB_VERSIONS_OK=nodots + ;; + SunOS-5.[0-6]*) + + # Note: If _REENTRANT isn't defined, then Solaris + # won't define thread-safe library routines. + + cat >>confdefs.h <<\_ACEOF +#define _REENTRANT 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _POSIX_PTHREAD_SEMANTICS 1 +_ACEOF + + + SHLIB_CFLAGS="-KPIC" + + # Note: need the LIBS below, otherwise Tk won't find Tcl's + # symbols when dynamically loaded into tclsh. + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + if test "$GCC" = "yes" ; then + SHLIB_LD="$CC -shared" + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + SHLIB_LD="/usr/ccs/bin/ld -G -z text" + LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' + fi + ;; + SunOS-5*) + + # Note: If _REENTRANT isn't defined, then Solaris + # won't define thread-safe library routines. + + cat >>confdefs.h <<\_ACEOF +#define _REENTRANT 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _POSIX_PTHREAD_SEMANTICS 1 +_ACEOF + + + SHLIB_CFLAGS="-KPIC" + LDFLAGS="" + + # 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 + { echo "$as_me:$LINENO: WARNING: \"64bit mode not supported with GCC on $system\"" >&5 +echo "$as_me: WARNING: \"64bit mode not supported with GCC on $system\"" >&2;} + else + do64bit_ok=yes + if test "$do64bitVIS" = "yes" ; then + EXTRA_CFLAGS="-xarch=v9a" + LDFLAGS="-xarch=v9a" + else + EXTRA_CFLAGS="-xarch=v9" + LDFLAGS="-xarch=v9" + fi + fi + else + { echo "$as_me:$LINENO: WARNING: \"64bit mode only supported sparcv9 system\"" >&5 +echo "$as_me: WARNING: \"64bit mode only supported sparcv9 system\"" >&2;} + fi + fi + + # Note: need the LIBS below, otherwise Tk won't find Tcl's + # symbols when dynamically loaded into tclsh. + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + if test "$GCC" = "yes" ; then + SHLIB_LD="$CC -shared" + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + SHLIB_LD="/usr/ccs/bin/ld -G -z text" + LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' + fi + ;; + ULTRIX-4.*) + SHLIB_CFLAGS="-G 0" + SHLIB_SUFFIX=".a" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + if test "$GCC" != "yes" ; then + EXTRA_CFLAGS="-DHAVE_TZSET -std1" + fi + ;; + UNIX_SV* | UnixWare-5*) + SHLIB_CFLAGS="-KPIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers + # that don't grok the -Bexport option. Test that it does. + hold_ldflags=$LDFLAGS + echo "$as_me:$LINENO: checking for ld accepts -Bexport flag" >&5 +echo $ECHO_N "checking for ld accepts -Bexport flag... $ECHO_C" >&6 + LDFLAGS="${LDFLAGS} -Wl,-Bexport" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + found=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +found=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$hold_ldflags + echo "$as_me:$LINENO: result: $found" >&5 +echo "${ECHO_T}$found" >&6 + if test $found = yes; then + LDFLAGS="-Wl,-Bexport" + else + LDFLAGS="" + fi + LD_SEARCH_FLAGS="" + ;; + esac + + if test "$do64bit" = "yes" -a "$do64bit_ok" = "no" ; then + { echo "$as_me:$LINENO: WARNING: \"64bit support being disabled -- don\'t know magic for this platform\"" >&5 +echo "$as_me: WARNING: \"64bit support being disabled -- don\'t know magic for this platform\"" >&2;} + fi + + # Step 4: If pseudo-static linking is in use (see K. B. Kenny, "Dynamic + # Loading for Tcl -- What Became of It?". Proc. 2nd Tcl/Tk Workshop, + # New Orleans, LA, Computerized Processes Unlimited, 1994), then we need + # to determine which of several header files defines the a.out file + # format (a.out.h, sys/exec.h, or sys/exec_aout.h). At present, we + # support only a file format that is more or less version-7-compatible. + # In particular, + # - a.out files must begin with `struct exec'. + # - the N_TXTOFF on the `struct exec' must compute the seek address + # of the text segment + # - The `struct exec' must contain a_magic, a_text, a_data, a_bss + # and a_entry fields. + # The following compilation should succeed if and only if either sys/exec.h + # or a.out.h is usable for the purpose. + # + # Note that the modified COFF format used on MIPS Ultrix 4.x is usable; the + # `struct exec' includes a second header that contains information that + # duplicates the v7 fields that are needed. + + if test "x$DL_OBJS" = "xtclLoadAout.o" ; then + echo "$as_me:$LINENO: checking sys/exec.h" >&5 +echo $ECHO_N "checking sys/exec.h... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_magic == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_ok=usable +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_ok=unusable +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: result: $tcl_ok" >&5 +echo "${ECHO_T}$tcl_ok" >&6 + if test $tcl_ok = usable; then + cat >>confdefs.h <<\_ACEOF +#define USE_SYS_EXEC_H 1 +_ACEOF + + else + echo "$as_me:$LINENO: checking a.out.h" >&5 +echo $ECHO_N "checking a.out.h... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_magic == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_ok=usable +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_ok=unusable +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: result: $tcl_ok" >&5 +echo "${ECHO_T}$tcl_ok" >&6 + if test $tcl_ok = usable; then + cat >>confdefs.h <<\_ACEOF +#define USE_A_OUT_H 1 +_ACEOF + + else + echo "$as_me:$LINENO: checking sys/exec_aout.h" >&5 +echo $ECHO_N "checking sys/exec_aout.h... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_midmag == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_ok=usable +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_ok=unusable +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: result: $tcl_ok" >&5 +echo "${ECHO_T}$tcl_ok" >&6 + if test $tcl_ok = usable; then + cat >>confdefs.h <<\_ACEOF +#define USE_SYS_EXEC_AOUT_H 1 +_ACEOF + + else + DL_OBJS="" + fi + fi + fi + fi + + # Step 5: disable dynamic loading if requested via a command-line switch. + + # Check whether --enable-load or --disable-load was given. +if test "${enable_load+set}" = set; then + enableval="$enable_load" + tcl_ok=$enableval +else + tcl_ok=yes +fi; + if test "$tcl_ok" = "no"; then + DL_OBJS="" + fi + + if test "x$DL_OBJS" != "x" ; then + BUILD_DLTEST="\$(DLTEST_TARGETS)" + else + echo "Can't figure out how to do dynamic loading or shared libraries" + echo "on this system." + SHLIB_CFLAGS="" + SHLIB_LD="" + SHLIB_SUFFIX="" + DL_OBJS="tclLoadNone.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + BUILD_DLTEST="" + fi + + # 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 "$DL_OBJS" != "tclLoadNone.o" ; then + if test "$GCC" = "yes" ; then + case $system in + AIX-*) + ;; + BSD/OS*) + ;; + IRIX*) + ;; + NetBSD-*|FreeBSD-*|OpenBSD-*) + ;; + Rhapsody-*|Darwin-*) + ;; + RISCos-*) + ;; + SCO_SV-3.2*) + ;; + ULTRIX-4.*) + ;; + windows) + if test "$MINGW32" != "yes"; then + SHLIB_CFLAGS="-fPIC" + fi + ;; + *) + SHLIB_CFLAGS="-fPIC" + ;; + esac + fi + fi + + if test "$SHARED_LIB_SUFFIX" = "" ; then + SHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}${SHLIB_SUFFIX}' + fi + if test "$UNSHARED_LIB_SUFFIX" = "" ; then + UNSHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}.a' + fi + + + + + + + + SHLIB_LDFLAGS='$(LDFLAGS_DEFAULT)' + + + + + + + + + +#-------------------------------------------------------------------- +# Set the default compiler switches based on the --enable-symbols option. +#-------------------------------------------------------------------- + + + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + { { echo "$as_me:$LINENO: error: Must call TEA INIT before ENABLE_SYMBOLS" >&5 +echo "$as_me: error: Must call TEA INIT before ENABLE_SYMBOLS" >&2;} + { (exit 1); exit 1; }; } + fi + + if test "${TEA_PLATFORM}" = "windows" ; then + tcl_dbgx=d + else + tcl_dbgx=g + fi + + echo "$as_me:$LINENO: checking for build with symbols" >&5 +echo $ECHO_N "checking for build with symbols... $ECHO_C" >&6 + # Check whether --enable-symbols or --disable-symbols was given. +if test "${enable_symbols+set}" = set; then + enableval="$enable_symbols" + tcl_ok=$enableval +else + tcl_ok=no +fi; + if test "$tcl_ok" = "no"; then + CFLAGS_DEFAULT='$(CFLAGS_OPTIMIZE)' + LDFLAGS_DEFAULT='$(LDFLAGS_OPTIMIZE)' + DBGX="" + TCL_DBGX="" + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + else + CFLAGS_DEFAULT='$(CFLAGS_DEBUG)' + LDFLAGS_DEFAULT='$(LDFLAGS_DEBUG)' + DBGX=${tcl_dbgx} + TCL_DBGX=${tcl_dbgx} + if test "$tcl_ok" = "yes"; then + echo "$as_me:$LINENO: result: yes (standard debugging)" >&5 +echo "${ECHO_T}yes (standard debugging)" >&6 + fi + fi + + + + + + if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then + cat >>confdefs.h <<\_ACEOF +#define TCL_MEM_DEBUG 1 +_ACEOF + + fi + + if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then + if test "$tcl_ok" = "all"; then + echo "$as_me:$LINENO: result: enabled symbols mem debugging" >&5 +echo "${ECHO_T}enabled symbols mem debugging" >&6 + else + echo "$as_me:$LINENO: result: enabled $tcl_ok debugging" >&5 +echo "${ECHO_T}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. +#-------------------------------------------------------------------- + +cat >>confdefs.h <<\_ACEOF +#define USE_TCL_STUBS 1 +_ACEOF + +cat >>confdefs.h <<\_ACEOF +#define USE_TK_STUBS 1 +_ACEOF + + +#-------------------------------------------------------------------- +# 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:\$@ \$(\$(PACKAGE)_OBJECTS)" + MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LDFLAGS} \${SHLIB_LD_LIBS} \$(LDFLAGS) -out:\$@ \$(\$(PACKAGE)_OBJECTS)" + MAKE_STUB_LIB="\${STLIB_LD} -out:\$@ \$(\$(PACKAGE)stub_OBJECTS)" + else + MAKE_STATIC_LIB="\${STLIB_LD} \$@ \$(\$(PACKAGE)_OBJECTS)" + MAKE_SHARED_LIB="\${SHLIB_LD} -o \$@ \$(\$(PACKAGE)_OBJECTS) \${SHLIB_LDFLAGS} \${SHLIB_LD_LIBS}" + MAKE_STUB_LIB="\${STLIB_LD} \$@ \$(\$(PACKAGE)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 the ${DBGX} in the suffix is + # substituted. + #-------------------------------------------------------------------- + + 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 "${PACKAGE}_LIB_FILE=${PACKAGE}${SHARED_LIB_SUFFIX}" + RANLIB=: + else + eval eval "${PACKAGE}_LIB_FILE=${PACKAGE}${UNSHARED_LIB_SUFFIX}" + fi + # Some packages build there own stubs libraries + eval eval "${PACKAGE}stub_LIB_FILE=${PACKAGE}stub${UNSHARED_LIB_SUFFIX}" + else + 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 "${PACKAGE}_LIB_FILE=lib${PACKAGE}${SHARED_LIB_SUFFIX}" + RANLIB=: + else + eval eval "${PACKAGE}_LIB_FILE=lib${PACKAGE}${UNSHARED_LIB_SUFFIX}" + fi + # Some packages build there own stubs libraries + eval eval "${PACKAGE}stub_LIB_FILE=lib${PACKAGE}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 + + + + + + + +#-------------------------------------------------------------------- +# __CHANGE__ +# Add platform libs to LIBS or SHLIB_LD_LIBS as necessary. +#-------------------------------------------------------------------- + +#LIBS="${LIBS} -lsuperfly" +LIBS="${LIBS} ${TK_LIBS}" + +#-------------------------------------------------------------------- +# Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl +# file during the install process. Don't run the TCLSH_PROG through +# ${CYGPATH} because it's being used directly by make. +# Require that we use a tclsh shell version 8.2 or later since earlier +# versions have bugs in the pkg_mkIndex routine. +# Add WISH as well if this is a Tk extension. +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking for tclsh" >&5 +echo $ECHO_N "checking for tclsh... $ECHO_C" >&6 + + if test "${ac_cv_path_tclsh+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + search_path=`echo ${TCL_BIN_DIR}:${TCL_BIN_DIR}/../bin:${exec_prefix}/bin:${prefix}/bin:${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/tclsh[8-9]*${EXEEXT} 2> /dev/null` \ + `ls -r $dir/tclsh*${EXEEXT} 2> /dev/null` ; do + if test x"$ac_cv_path_tclsh" = x ; then + if test -f "$j" ; then + ac_cv_path_tclsh=$j + break + fi + fi + done + done + +fi + + + if test -f "$ac_cv_path_tclsh" ; then + TCLSH_PROG=$ac_cv_path_tclsh + echo "$as_me:$LINENO: result: $TCLSH_PROG" >&5 +echo "${ECHO_T}$TCLSH_PROG" >&6 + else + { { echo "$as_me:$LINENO: error: No tclsh found in PATH: $search_path" >&5 +echo "$as_me: error: No tclsh found in PATH: $search_path" >&2;} + { (exit 1); exit 1; }; } + fi + + + + echo "$as_me:$LINENO: checking for wish" >&5 +echo $ECHO_N "checking for wish... $ECHO_C" >&6 + + if test "${ac_cv_path_wish+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + search_path=`echo ${TK_BIN_DIR}:${TK_BIN_DIR}/../bin:${TCL_BIN_DIR}:${TCL_BIN_DIR}/../bin:${exec_prefix}/bin:${prefix}/bin:${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/wish[8-9]*${EXEEXT} 2> /dev/null` \ + `ls -r $dir/wish*${EXEEXT} 2> /dev/null` ; do + if test x"$ac_cv_path_wish" = x ; then + if test -f "$j" ; then + ac_cv_path_wish=$j + break + fi + fi + done + done + +fi + + + if test -f "$ac_cv_path_wish" ; then + WISH_PROG=$ac_cv_path_wish + echo "$as_me:$LINENO: result: $WISH_PROG" >&5 +echo "${ECHO_T}$WISH_PROG" >&6 + else + { { echo "$as_me:$LINENO: error: No wish found in PATH: $search_path" >&5 +echo "$as_me: error: No wish found in PATH: $search_path" >&2;} + { (exit 1); exit 1; }; } + fi + + + +#-------------------------------------------------------------------- +# 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, don't put newlines in cache variables' values. +# 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. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *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 \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!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" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + 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}' + +# 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 + +# 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 we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +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_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $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} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="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="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.56. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet 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 ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.56, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +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 + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + 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 +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "pkgIndex.tcl" ) CONFIG_FILES="$CONFIG_FILES pkgIndex.tcl" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + 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 to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CONFIGDIR@,$CONFIGDIR,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@treectrl_LIB_FILE@,$treectrl_LIB_FILE,;t t +s,@CYGPATH@,$CYGPATH,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@TCL_VERSION@,$TCL_VERSION,;t t +s,@TCL_BIN_DIR@,$TCL_BIN_DIR,;t t +s,@TCL_SRC_DIR@,$TCL_SRC_DIR,;t t +s,@TCL_LIB_FILE@,$TCL_LIB_FILE,;t t +s,@TCL_LIB_FLAG@,$TCL_LIB_FLAG,;t t +s,@TCL_LIB_SPEC@,$TCL_LIB_SPEC,;t t +s,@TCL_STUB_LIB_FILE@,$TCL_STUB_LIB_FILE,;t t +s,@TCL_STUB_LIB_FLAG@,$TCL_STUB_LIB_FLAG,;t t +s,@TCL_STUB_LIB_SPEC@,$TCL_STUB_LIB_SPEC,;t t +s,@TCL_LIBS@,$TCL_LIBS,;t t +s,@TCL_DEFS@,$TCL_DEFS,;t t +s,@TCL_EXTRA_CFLAGS@,$TCL_EXTRA_CFLAGS,;t t +s,@TCL_LD_FLAGS@,$TCL_LD_FLAGS,;t t +s,@TCL_SHLIB_LD_LIBS@,$TCL_SHLIB_LD_LIBS,;t t +s,@TK_VERSION@,$TK_VERSION,;t t +s,@TK_BIN_DIR@,$TK_BIN_DIR,;t t +s,@TK_SRC_DIR@,$TK_SRC_DIR,;t t +s,@TK_LIB_FILE@,$TK_LIB_FILE,;t t +s,@TK_LIB_FLAG@,$TK_LIB_FLAG,;t t +s,@TK_LIB_SPEC@,$TK_LIB_SPEC,;t t +s,@TK_STUB_LIB_FILE@,$TK_STUB_LIB_FILE,;t t +s,@TK_STUB_LIB_FLAG@,$TK_STUB_LIB_FLAG,;t t +s,@TK_STUB_LIB_SPEC@,$TK_STUB_LIB_SPEC,;t t +s,@TK_LIBS@,$TK_LIBS,;t t +s,@TK_XINCLUDES@,$TK_XINCLUDES,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@TCL_TOP_DIR_NATIVE@,$TCL_TOP_DIR_NATIVE,;t t +s,@TCL_GENERIC_DIR_NATIVE@,$TCL_GENERIC_DIR_NATIVE,;t t +s,@TCL_UNIX_DIR_NATIVE@,$TCL_UNIX_DIR_NATIVE,;t t +s,@TCL_WIN_DIR_NATIVE@,$TCL_WIN_DIR_NATIVE,;t t +s,@TCL_BMAP_DIR_NATIVE@,$TCL_BMAP_DIR_NATIVE,;t t +s,@TCL_TOOL_DIR_NATIVE@,$TCL_TOOL_DIR_NATIVE,;t t +s,@TCL_PLATFORM_DIR_NATIVE@,$TCL_PLATFORM_DIR_NATIVE,;t t +s,@TCL_INCLUDES@,$TCL_INCLUDES,;t t +s,@TK_TOP_DIR_NATIVE@,$TK_TOP_DIR_NATIVE,;t t +s,@TK_UNIX_DIR_NATIVE@,$TK_UNIX_DIR_NATIVE,;t t +s,@TK_WIN_DIR_NATIVE@,$TK_WIN_DIR_NATIVE,;t t +s,@TK_GENERIC_DIR_NATIVE@,$TK_GENERIC_DIR_NATIVE,;t t +s,@TK_XLIB_DIR_NATIVE@,$TK_XLIB_DIR_NATIVE,;t t +s,@TK_PLATFORM_DIR_NATIVE@,$TK_PLATFORM_DIR_NATIVE,;t t +s,@TK_INCLUDES@,$TK_INCLUDES,;t t +s,@CLEANFILES@,$CLEANFILES,;t t +s,@EXTRA_SOURCES@,$EXTRA_SOURCES,;t t +s,@TCL_THREADS@,$TCL_THREADS,;t t +s,@SHARED_BUILD@,$SHARED_BUILD,;t t +s,@AR@,$AR,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@DL_LIBS@,$DL_LIBS,;t t +s,@CFLAGS_DEBUG@,$CFLAGS_DEBUG,;t t +s,@CFLAGS_OPTIMIZE@,$CFLAGS_OPTIMIZE,;t t +s,@CFLAGS_WARNING@,$CFLAGS_WARNING,;t t +s,@EXTRA_CFLAGS@,$EXTRA_CFLAGS,;t t +s,@STLIB_LD@,$STLIB_LD,;t t +s,@SHLIB_LD@,$SHLIB_LD,;t t +s,@SHLIB_CFLAGS@,$SHLIB_CFLAGS,;t t +s,@SHLIB_LDFLAGS@,$SHLIB_LDFLAGS,;t t +s,@SHLIB_LD_LIBS@,$SHLIB_LD_LIBS,;t t +s,@LDFLAGS_DEBUG@,$LDFLAGS_DEBUG,;t t +s,@LDFLAGS_OPTIMIZE@,$LDFLAGS_OPTIMIZE,;t t +s,@TCL_DBGX@,$TCL_DBGX,;t t +s,@CFLAGS_DEFAULT@,$CFLAGS_DEFAULT,;t t +s,@LDFLAGS_DEFAULT@,$LDFLAGS_DEFAULT,;t t +s,@MAKE_LIB@,$MAKE_LIB,;t t +s,@MAKE_SHARED_LIB@,$MAKE_SHARED_LIB,;t t +s,@MAKE_STATIC_LIB@,$MAKE_STATIC_LIB,;t t +s,@MAKE_STUB_LIB@,$MAKE_STUB_LIB,;t t +s,@TCLSH_PROG@,$TCLSH_PROG,;t t +s,@WISH_PROG@,$WISH_PROG,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # 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. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;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,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# 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 || { (exit 1); exit 1; } +fi + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..4e72f88 --- /dev/null +++ b/configure.ac @@ -0,0 +1,210 @@ +#!/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.ac,v 1.1 2002/12/17 05:04:18 treectrl Exp $ + +#----------------------------------------------------------------------- +# Sample configure.in for Tcl Extensions. The only places you should +# need to modify this file are marked by the string __CHANGE__ +#----------------------------------------------------------------------- + +#----------------------------------------------------------------------- +# __CHANGE__ +# This very first macro is used to verify that the configure script can +# find the sources. The argument to AC_INIT should be a unique filename +# for this package, and can be a relative path, such as: +# +# AC_INIT(generic/tcl.h) +#----------------------------------------------------------------------- + +AC_INIT(generic/tkTreeCtrl.h) + +AC_CONFIG_AUX_DIR(tclconfig) +CONFIGDIR=${srcdir}/tclconfig +AC_SUBST(CONFIGDIR) + +#---------------------------------------------------------------------- +# __CHANGE__ +# Set your package name and version numbers here. The NODOT_VERSION is +# required for constructing the library name on systems that don't like +# dots in library names (Windows). The VERSION variable is used on the +# other systems. +#---------------------------------------------------------------------- + +PACKAGE=treectrl + +MAJOR_VERSION=1 +MINOR_VERSION=0 +PATCHLEVEL= + +VERSION=${MAJOR_VERSION}.${MINOR_VERSION}${PATCHLEVEL} +NODOT_VERSION=${MAJOR_VERSION}${MINOR_VERSION} + +AC_SUBST(PACKAGE) +AC_SUBST(VERSION) +# This package name must be replaced statically for AC_SUBST to work +AC_SUBST(treectrl_LIB_FILE) +# Substitute stub_LIB_FILE if your package creates a stub library too. +#AC_SUBST(samplestub_LIB_FILE) + +#-------------------------------------------------------------------- +# We put this here so that you can compile with -DVERSION="1.2" to +# encode the package version directly into the source files. +#-------------------------------------------------------------------- + +eval AC_DEFINE_UNQUOTED(VERSION, "${VERSION}") + +#-------------------------------------------------------------------- +# Call TEA_INIT as the first TEA_ macro to set up initial vars. +# This will define a ${TEA_PLATFORM} variable == "unix" or "windows". +#-------------------------------------------------------------------- + +TEA_INIT + +#-------------------------------------------------------------------- +# 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__ +# 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 + +#-------------------------------------------------------------------- +# __CHANGE__ +# A few miscellaneous platform-specific items: +# +# Define a special symbol for Windows (BUILD_sample in this case) so +# that we create the export library with the dll. See sha1.h on how +# to use this. +# +# Windows creates a few extra files that need to be cleaned up. +# You can add more files to clean if your extension creates any extra +# files. +# +# Define any extra compiler flags in the PACKAGE_CFLAGS variable. +# These will be appended to the current set of compiler flags for +# your system. +#-------------------------------------------------------------------- + +if test "${TEA_PLATFORM}" = "windows" ; then + AC_DEFINE(BUILD_treectrl) + CLEANFILES="pkgIndex.tcl *.lib *.dll *.exp *.ilk *.pdb vc*.pch" + EXTRA_SOURCES='$(WIN_SOURCES)' +else + CLEANFILES="pkgIndex.tcl" + EXTRA_SOURCES='$(UNIX_SOURCES)' +fi +AC_SUBST(CLEANFILES) +AC_SUBST(EXTRA_SOURCES) + +#-------------------------------------------------------------------- +# Check whether --enable-threads or --disable-threads was given. +#-------------------------------------------------------------------- + +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) +AC_DEFINE(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 + +#-------------------------------------------------------------------- +# __CHANGE__ +# Add platform libs to LIBS or SHLIB_LD_LIBS as necessary. +#-------------------------------------------------------------------- + +#LIBS="${LIBS} -lsuperfly" +LIBS="${LIBS} ${TK_LIBS}" + +#-------------------------------------------------------------------- +# Find tclsh so that we can run pkg_mkIndex to generate the pkgIndex.tcl +# file during the install process. Don't run the TCLSH_PROG through +# ${CYGPATH} because it's being used directly by make. +# Require that we use a tclsh shell version 8.2 or later since earlier +# versions have bugs in the pkg_mkIndex routine. +# Add WISH as well if this is a Tk extension. +#-------------------------------------------------------------------- + +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]) diff --git a/demo.tcl b/demo.tcl new file mode 100644 index 0000000..d9c0f77 --- /dev/null +++ b/demo.tcl @@ -0,0 +1,935 @@ +#! D:/Programming/Tcl-8.4.1/bin/wish84.exe + +# Get full pathname to this file +set ScriptDir [file normalize [file dirname [info script]]] + +# Command to create a full pathname in this file's directory +proc Path {args} { + return [eval file join [list $::ScriptDir] $args] +} + +# Create some photo images on demand +proc InitPics {args} { + foreach pattern $args { + if {[lsearch [image names] $pattern] == -1} { + foreach file [glob -directory [Path pics] $pattern.gif] { + set imageName [file root [file tail $file]] + # I created an image called "file", which clobbered the + # original Tcl command "file". Then I got confused. + if {[llength [info commands $imageName]]} { + error "don't want to create image called \"$imageName\"" + } + image create photo $imageName -file $file + + # Hack -- Create a "selected" version too + image create photo ${imageName}Sel + ${imageName}Sel copy $imageName + imagetint ${imageName}Sel $::SystemHighlight 128 + } + } + } + return +} + +if {[catch { + package require dbwin 1.0 +}]} { + proc dbwin s {puts -nonewline $s} +} +if {[file exists libtreectrl1.0[info sharedlibextension]} { + load libtreectrl1.0[info sharedlibextension] +} else { + load Build/treectrl[info sharedlibextension] +} + +# Default TreeCtrl bindings +source [Path library treectrl.tcl] + +# Other useful bindings +source [Path library filelist-bindings.tcl] + +# Demo sources +foreach file [glob -directory [Path demos] *.tcl] { + source $file +} + +# Get default colors +set w [listbox .listbox] +set SystemButtonFace [$w cget -highlightbackground] +set SystemHighlight [$w cget -selectbackground] +set SystemHighlightText [$w cget -selectforeground] +destroy $w + +proc MakeMenuBar {} { + set m [menu .menubar] + . configure -menu $m + set m2 [menu $m.mFile -tearoff no] + if {$::tcl_platform(platform) ne "unix"} { + console eval {.console conf -height 8} + $m2 add command -label "Console" -command { + if {[console eval {winfo ismapped .}]} { + console hide + } else { + console show + } + } + } + $m2 add command -label "View Source" -command ToggleSourceWindow + $m2 add command -label Quit -command exit + $m add cascade -label File -menu $m2 + return +} + +proc MakeSourceWindow {} { + set w [toplevel .source] + set f [frame $w.f -borderwidth 0] + if {$::tcl_platform(platform) eq "unix"} { + set font {Courier 16} + } else { + set font {Courier 9} + } + text $f.t -font $font -tabs [font measure $font 1234] -wrap none \ + -yscrollcommand "$f.sv set" -xscrollcommand "$f.sh set" + scrollbar $f.sv -orient vertical -command "$f.t yview" + scrollbar $f.sh -orient horizontal -command "$f.t xview" + pack $f -expand yes -fill both + grid columnconfigure $f 0 -weight 1 + grid rowconfigure $f 0 -weight 1 + grid configure $f.t -row 0 -column 0 -sticky news + grid configure $f.sh -row 1 -column 0 -sticky we + grid configure $f.sv -row 0 -column 1 -sticky ns + + wm protocol $w WM_DELETE_WINDOW "wm withdraw $w" + wm geom $w -0+0 + wm withdraw $w + + return +} +proc ShowSource {file} { + wm title .source "Demo Source: $file" + set path [Path demos $file] + set t .source.f.t + set chan [open $path] + $t delete 1.0 end + $t insert end [read $chan] + $t mark set insert 1.0 + close $chan + return +} +proc ToggleSourceWindow {} { + set w .source + if {[winfo ismapped $w]} { + wm withdraw $w + } else { + wm deiconify $w + } + return +} + +MakeSourceWindow +MakeMenuBar + +proc TreePlusScrollbarsInAFrame {f h v} { + frame $f -borderwidth 1 -relief sunken + if {$::tcl_platform(platform) eq "unix"} { + set font {Helvetica 16} + } else { + # There is a bug on my Win98 box with Tk_MeasureChars() and + # MS Sans Serif 8. + set font {{MS Sans} 8} + } + treectrl $f.t -highlightthickness 0 -borderwidth 0 -font $font + $f.t configure -xscrollincrement 20 + $f.t debug configure -enable no -display no + if {$h} { + scrollbar $f.sh -orient horizontal -command "$f.t xview" +# $f.t configure -xscrollcommand "$f.sh set" + $f.t notify bind $f.sh { %W set %l %u } + bind $f.sh "focus $f.t" + } + if {$v} { + scrollbar $f.sv -orient vertical -command "$f.t yview" +# $f.t configure -yscrollcommand "$f.sv set" + $f.t notify bind $f.sv { %W set %l %u } + bind $f.sv "focus $f.t" + } + grid columnconfigure $f 0 -weight 1 + grid rowconfigure $f 0 -weight 1 + grid configure $f.t -row 0 -column 0 -sticky news + if {$h} { + grid configure $f.sh -row 1 -column 0 -sticky we + } + if {$v} { + grid configure $f.sv -row 0 -column 1 -sticky ns + } + return +} + +proc MakeMainWindow {} { + + wm title . "TkTreeCtrl Demo" + wm geometry . +0+0 + + panedwindow .pw2 -orient horizontal -borderwidth 0 + panedwindow .pw1 -orient vertical -borderwidth 0 + + # Tree + scrollbar: demos + TreePlusScrollbarsInAFrame .f1 0 1 + .f1.t configure -showbuttons no -showlines no -showroot no -height 100 + .f1.t column configure 0 -text "List of Demos" -expand yes -button no + + # Tree + scrollbar: styles + elements in list + TreePlusScrollbarsInAFrame .f4 0 1 + .f4.t configure -showroot no -height 140 + .f4.t column configure 0 -text "Elements and Styles" -expand yes -button no + + # Tree + scrollbar: styles + elements in selected item + TreePlusScrollbarsInAFrame .f3 0 1 + .f3.t configure -showroot no + .f3.t column configure 0 -text "Styles in Item" -expand yes -button no + + .pw1 add .f1 .f4 .f3 -height 150 + + # Frame on right + frame .f2 + + # Tree + scrollbars + TreePlusScrollbarsInAFrame .f2.f1 1 1 + .f2.f1.t configure -indent 19 + .f2.f1.t debug configure -enable no -display yes -erasecolor pink -displaydelay 30 + + # Give it a big border to debug drawing + .f2.f1.t configure -borderwidth 6 -relief ridge -highlightthickness 3 + + grid columnconfigure .f2 0 -weight 1 + grid rowconfigure .f2 0 -weight 1 + grid configure .f2.f1 -row 0 -column 0 -sticky news -pady 0 + + .pw2 add .pw1 -width 200 + .pw2 add .f2 -width 450 + + pack .pw2 -expand yes -fill both + + ### + .f2.f1.t notify install event Header + .f2.f1.t notify install detail Header invoke + + .f2.f1.t notify install event Drag + .f2.f1.t notify install detail Drag begin + .f2.f1.t notify install detail Drag end + .f2.f1.t notify install detail Drag receive + + .f2.f1.t notify install event Edit + .f2.f1.t notify install detail Edit accept + ### + + return +} + +proc MakeListPopup {} { + set m [menu .f2.f1.t.mTree -tearoff no] + + set m2 [menu $m.mCollapse -tearoff no] + $m add cascade -label Collapse -menu $m2 + + set m2 [menu $m.mExpand -tearoff no] + $m add cascade -label Expand -menu $m2 + + set m2 [menu $m.mDebug -tearoff no] + $m2 add checkbutton -label Data -variable Popup(debug,data) \ + -command {.f2.f1.t debug configure -data $Popup(debug,data)} + $m2 add checkbutton -label Display -variable Popup(debug,display) \ + -command {.f2.f1.t debug configure -display $Popup(debug,display)} + $m2 add checkbutton -label Enable -variable Popup(debug,enable) \ + -command {.f2.f1.t debug configure -enable $Popup(debug,enable)} + $m add cascade -label Debug -menu $m2 + + set m2 [menu $m.mBuffer -tearoff no] + $m2 add radiobutton -label "none" -variable Popup(doublebuffer) -value none \ + -command {.f2.f1.t configure -doublebuffer $Popup(doublebuffer)} + $m2 add radiobutton -label "item" -variable Popup(doublebuffer) -value item \ + -command {.f2.f1.t configure -doublebuffer $Popup(doublebuffer)} + $m2 add radiobutton -label "window" -variable Popup(doublebuffer) -value window \ + -command {.f2.f1.t configure -doublebuffer $Popup(doublebuffer)} + $m add cascade -label Buffering -menu $m2 + + set m2 [menu $m.mLineStyle -tearoff no] + $m2 add radiobutton -label "dot" -variable Popup(linestyle) -value dot \ + -command {.f2.f1.t configure -linestyle $Popup(linestyle)} + $m2 add radiobutton -label "solid" -variable Popup(linestyle) -value solid \ + -command {.f2.f1.t configure -linestyle $Popup(linestyle)} + $m add cascade -label "Line style" -menu $m2 + + set m2 [menu $m.mOrient -tearoff no] + $m2 add radiobutton -label "Horizontal" -variable Popup(orient) -value horizontal \ + -command {.f2.f1.t configure -orient $Popup(orient)} + $m2 add radiobutton -label "Vertical" -variable Popup(orient) -value vertical \ + -command {.f2.f1.t configure -orient $Popup(orient)} + $m add cascade -label Orient -menu $m2 + + set m2 [menu $m.mSelectMode -tearoff no] + foreach mode [list browse extended multiple single] { + $m2 add radiobutton -label $mode -variable Popup(selectmode) -value $mode \ + -command {.f2.f1.t configure -selectmode $Popup(selectmode)} + } + $m add cascade -label Selectmode -menu $m2 + + set m2 [menu $m.mShow -tearoff no] + $m2 add checkbutton -label "Buttons" -variable Popup(showbuttons) \ + -command {.f2.f1.t configure -showbuttons $Popup(showbuttons)} + $m2 add checkbutton -label "Header" -variable Popup(showheader) \ + -command {.f2.f1.t configure -showheader $Popup(showheader)} + $m2 add checkbutton -label "Lines" -variable Popup(showlines) \ + -command {.f2.f1.t configure -showlines $Popup(showlines)} + $m2 add checkbutton -label "Root" -variable Popup(showroot) \ + -command {.f2.f1.t configure -showroot $Popup(showroot)} + $m2 add checkbutton -label "Root Button" -variable Popup(showrootbutton) \ + -command {.f2.f1.t configure -showrootbutton $Popup(showrootbutton)} + $m add cascade -label Show -menu $m2 + + set m2 [menu $m.mVisible -tearoff no] + $m add cascade -label Visible -menu $m2 + return +} + +proc MakeHeaderPopup {} { + set m [menu .f2.f1.t.mHeader -tearoff no] + + set m2 [menu $m.mArrow -tearoff no] + $m add cascade -label Arrow -menu $m2 + $m2 add radiobutton -label "None" -variable Popup(arrow) -value none -command {.f2.f1.t column configure $Popup(column) -arrow none} + $m2 add radiobutton -label "Up" -variable Popup(arrow) -value up -command {.f2.f1.t column configure $Popup(column) -arrow up} + $m2 add radiobutton -label "Down" -variable Popup(arrow) -value down -command {.f2.f1.t column configure $Popup(column) -arrow down} + $m2 add separator + $m2 add radiobutton -label "Side Left" -variable Popup(arrow,side) -value left -command {.f2.f1.t column configure $Popup(column) -arrowside left} + $m2 add radiobutton -label "Side Right" -variable Popup(arrow,side) -value right -command {.f2.f1.t column configure $Popup(column) -arrowside right} + $m2 add separator + $m2 add radiobutton -label "Gravity Left" -variable Popup(arrow,gravity) -value left -command {.f2.f1.t column configure $Popup(column) -arrowgravity left} + $m2 add radiobutton -label "Gravity Right" -variable Popup(arrow,gravity) -value right -command {.f2.f1.t column configure $Popup(column) -arrowgravity right} + + $m add checkbutton -label "Expand" -variable Popup(expand) -command {.f2.f1.t column configure $Popup(column) -expand $Popup(expand)} + + set m2 [menu $m.mJustify -tearoff no] + $m add cascade -label Justify -menu $m2 + $m2 add radiobutton -label "Left" -variable Popup(justify) -value left -command {.f2.f1.t column configure $Popup(column) -justify left} + $m2 add radiobutton -label "Center" -variable Popup(justify) -value center -command {.f2.f1.t column configure $Popup(column) -justify center} + $m2 add radiobutton -label "Right" -variable Popup(justify) -value right -command {.f2.f1.t column configure $Popup(column) -justify right} + return +} + +MakeMainWindow +MakeListPopup +MakeHeaderPopup + +bind .f2.f1.t { + set id [%W identify %x %y] + if {$id ne ""} { + if {[lindex $id 0] eq "header"} { + set Popup(column) [lindex $id 1] + set Popup(arrow) [%W column cget $Popup(column) -arrow] + set Popup(arrow,side) [%W column cget $Popup(column) -arrowside] + set Popup(arrow,gravity) [%W column cget $Popup(column) -arrowgravity] + set Popup(expand) [%W column cget $Popup(column) -expand] + set Popup(justify) [%W column cget $Popup(column) -justify] + tk_popup %W.mHeader %X %Y + return + } + } + set m %W.mTree.mCollapse + $m delete 0 end + $m add command -label "All" -command {%W collapse all} + if {$id ne ""} { + if {[lindex $id 0] eq "item"} { + set item [lindex $id 1] + $m add command -label "Item $item" -command "%W collapse $item" + $m add command -label "Item $item (recurse)" -command "%W collapse -recurse $item" + } + } + set m %W.mTree.mExpand + $m delete 0 end + $m add command -label "All" -command {%W expand all} + if {$id ne ""} { + if {[lindex $id 0] eq "item"} { + set item [lindex $id 1] + $m add command -label "Item $item" -command "%W expand $item" + $m add command -label "Item $item (recurse)" -command "%W expand -recurse $item" + } + } + foreach option {data display enable} { + set Popup(debug,$option) [%W debug cget -$option] + } + set Popup(doublebuffer) [%W cget -doublebuffer] + set Popup(linestyle) [%W cget -linestyle] + set Popup(orient) [%W cget -orient] + set Popup(selectmode) [%W cget -selectmode] + set Popup(showbuttons) [%W cget -showbuttons] + set Popup(showheader) [%W cget -showheader] + set Popup(showlines) [%W cget -showlines] + set Popup(showroot) [%W cget -showroot] + set Popup(showrootbutton) [%W cget -showrootbutton] + set m %W.mTree.mVisible + $m delete 0 end + for {set i 0} {$i < [%W numcolumns]} {incr i} { + set Popup(visible,$i) [%W column cget $i -visible] + $m add checkbutton -label "Column $i \"[%W column cget $i -text]\" \[[%W column cget $i -image]\]" -variable Popup(visible,$i) \ + -command "%W column configure $i -visible \$Popup(visible,$i)" + } + tk_popup %W.mTree %X %Y +} + +bind TreeCtrlXXX { + focus %W + set id [%W identify %x %y] + puts "identify: $id" + if {$id ne "" && [lindex $id 0] eq "item"} { + foreach {where item arg1 arg2} $id {} + if {$arg1 eq "button"} { + %W toggle $item + return + } elseif {$arg1 eq "line"} { + %W toggle $arg2 + return + } + } + TreeCtrl::ButtonPress1 %W %x %y +} + +# +# List of demos +# +proc InitDemoList {} { + global DemoCmd + global DemoFile + + set t .f1.t + $t element create e1 text -fill [list $::SystemHighlightText {selected focus}] + $t element create e2 rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] \ + -showfocus yes + $t style create s1 + $t style elements s1 {e2 e1} + # Tk listbox has linespace + 1 height + $t style layout s1 e2 -union [list e1] -ipadw 2 -ipade 2 -ipads 1 -iexpand e + + # "Picture Catalog" DemoPictureCatalog + # "Picture Catalog 2" DemoPictureCatalog2 + # "Folder Contents (Vertical)" DemoExplorerFilesV + foreach {label command file} [list \ + "Random $::RandomN Items" DemoRandom random.tcl \ + "Random $::RandomN Items, Button Images" DemoRandom2 random.tcl \ + "Outlook Express (Folders)" DemoOutlookFolders outlook-folders.tcl \ + "Outlook Express (Newsgroup)" DemoOutlookNewsgroup outlook-newgroup.tcl \ + "Explorer (Details)" DemoExplorerDetails explorer.tcl \ + "Explorer (List)" DemoExplorerList explorer.tcl \ + "Explorer (Large icons)" DemoExplorerLargeIcons explorer.tcl \ + "Explorer (Small icons)" DemoExplorerSmallIcons explorer.tcl \ + "Internet Options" DemoInternetOptions www-options.tcl \ + "Help Contents" DemoHelpContents help.tcl \ + "Layout" DemoLayout layout.tcl \ + "MailWasher" DemoMailWasher mailwasher.tcl \ + "Bitmaps" DemoBitmaps bitmaps.tcl \ + "iMovie" DemoIMovie imovie.tcl \ + ] { + set item [$t item create] + $t item lastchild root $item + $t item style set $item 0 s1 + $t item text $item 0 $label + set DemoCmd($item) $command + set DemoFile($item) $file + } + $t yview moveto 0.0 + return +} + +InitDemoList + +proc ClicksToSeconds {clicks} { + return [format "%.2g" [expr {$clicks / 1000000.0}]] +} + +proc DemoSet {cmd file} { + DemoClear + set clicks [clock clicks] + uplevel #0 $cmd + set clicks [expr {[clock clicks] - $clicks}] + puts "set list in [ClicksToSeconds $clicks] seconds ($clicks clicks)" + .f2.f1.t xview moveto 0 + .f2.f1.t yview moveto 0 + update + DisplayStylesInList + ShowSource $file +} + +.f1.t notify bind .f1.t { + if {%c == 1} { + set selection [%T selection get] + set item [lindex $selection 0] + DemoSet $DemoCmd($item) $DemoFile($item) + } +} + +proc DisplayStylesInList {} { + + set T .f2.f1.t + set t .f4.t + + # Create elements and styles the first time this is called + if {[llength [$t style names]] == 0} { + $t element create e1 text -fill [list $::SystemHighlightText {selected focus}] + $t element create e2 text -fill [list $::SystemHighlightText {selected focus} "" {selected !focus} blue {}] + $t element create e3 rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] \ + -showfocus yes + + $t style create s1 + $t style elements s1 {e3 e1} + $t style layout s1 e3 -union [list e1] -ipadw 1 -ipade 1 -ipadn 0 -ipads 1 + + $t style create s2 + $t style elements s2 {e3 e1 e2} + $t style layout s2 e1 -pade 4 + $t style layout s2 e3 -union [list e1 e2] -ipade 1 -ipadw 1 -ipadn 0 -ipads 1 + } + + # Clear the list + $t item delete all + + # One item for each element in the demo list + foreach elem [lsort [$T element names]] { + set item [$t item create] + $t collapse $item + $t item hasbutton $item yes + $t item style set $item 0 s1 + $t item text $item 0 "Element $elem ([$T element type $elem])" + + # One item for each configuration option for this element + foreach list [$T element configure $elem] { + foreach {name x y default current} $list {} + set item2 [$t item create] + if {[string equal $default $current]} { + $t item style set $item2 0 s1 + $t item complex $item2 [list [list e1 -text [list $name $current]]] + } else { + $t item style set $item2 0 s2 + $t item complex $item2 [list [list e1 -text $name] [list e2 -text [list $current]]] + } + $t item lastchild $item $item2 + } + $t item lastchild root $item + } + + # One item for each style in the demo list + foreach style [lsort [$T style names]] { + set item [$t item create] + $t collapse $item + $t item hasbutton $item yes + $t item style set $item 0 s1 + $t item text $item 0 "Style $style" + + # One item for each element in the style + foreach elem [$T style elements $style] { + set item2 [$t item create] + $t collapse $item2 + $t item hasbutton $item2 yes + $t item style set $item2 0 s1 + $t item text $item2 0 "Element $elem ([$T element type $elem])" + + # One item for each layout option for this element in this style + foreach {option value} [$T style layout $style $elem] { + set item3 [$t item create] + $t item hasbutton $item3 no + $t item style set $item3 0 s1 + $t item text $item3 0 [list $option $value] + $t item lastchild $item2 $item3 + } + $t item lastchild $item $item2 + } + $t item lastchild root $item + } + + $t xview moveto 0 + $t yview moveto 0 + return +} + +proc DisplayStylesInItem {item} { + + set T .f2.f1.t + set t .f3.t + $t column configure 0 -text "Styles in item [$T index $item]" + + # Create elements and styles the first time this is called + if {[llength [$t style names]] == 0} { + $t element create e1 text -fill [list $::SystemHighlightText {selected focus}] + $t element create e2 text -fill [list $::SystemHighlightText {selected focus} "" {selected !focus} blue {}] + $t element create e3 rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] \ + -showfocus yes + + $t style create s1 + $t style elements s1 {e3 e1} + $t style layout s1 e3 -union [list e1] -ipade 1 -ipadw 2 -ipadn 0 -ipads 1 + + $t style create s2 + $t style elements s2 {e3 e1 e2} + $t style layout s2 e1 -pade 4 + $t style layout s2 e3 -union [list e1 e2] -ipadw 1 -ipade 1 -ipadn 0 -ipads 1 + } + + # Clear the list + $t item delete all + + # One item for each item-column + set column 0 + foreach style [$T item style set $item] { + set item2 [$t item create] + $t collapse $item2 + $t item style set $item2 0 s1 + $t item element configure $item2 0 e1 -text "Column $column: Style $style" + set button 0 + + # One item for each element in this style + if {[string length $style]} { + foreach elem [$T item style elements $item $column] { + set button 1 + set item3 [$t item create] + $t collapse $item3 + $t item hasbutton $item3 yes + $t item style set $item3 0 s1 + $t item element configure $item3 0 e1 -text "Element $elem ([$T element type $elem])" + + # One item for each configuration option in this element + foreach list [$T item element configure $item $column $elem] { + foreach {name x y default current} $list {} + set item4 [$t item create] + set masterDefault [$T element cget $elem $name] + set sameAsMaster [string equal $masterDefault $current] + if {!$sameAsMaster && ![string length $current]} { + set sameAsMaster 1 + set current $masterDefault + } + + if {$sameAsMaster} { + $t item style set $item4 0 s1 + $t item complex $item4 [list [list e1 -text "$name [list $current]"]] + } else { + $t item style set $item4 0 s2 + $t item complex $item4 [list [list e1 -text $name] [list e2 -text [list $current]]] + } + $t item lastchild $item3 $item4 + } + $t item lastchild $item2 $item3 + } + if {$button} { + $t item hasbutton $item2 yes + } + } + $t item lastchild root $item2 + incr column + } + $t xview moveto 0 + $t yview moveto 0 + + return +} + +# When one item is selected in the demo list, display the styles in that item. +# See DemoClear for why the tag "DontDelete" is used +.f2.f1.t notify bind DontDelete { + if {%c == 1} { + set selection [%T selection get] + DisplayStylesInItem [lindex $selection 0] + } +} + +proc DemoClear {} { + + set T .f2.f1.t + + # Clear the demo list + $T item delete all + + # Clear all bindings on the demo list added by the previous demo. + # This is why DontDelete it used for the binding. + foreach pattern [$T notify bind $T] { + $T notify bind $T $pattern {} + } + + # Clear all run-time states + foreach state [$T state names] { + $T state undefine $state + } + + # Clear the styles-in-item list + .f3.t item delete all + + # Delete columns in demo list + while {[$T numcolumns]} { + $T column delete 0 + } + + # Delete all styles in demo list + eval $T style delete [$T style names] + + # Delete all elements in demo list + eval $T element delete [$T element names] + + $T item hasbutton root no + $T expand root + + # Restore some happy defaults to the demo list + $T configure -orient vertical -wrap "" -xscrollincrement 0 \ + -yscrollincrement 0 -itemheight 0 -showheader yes \ + -background white -scrollmargin 0 -xscrolldelay 50 -yscrolldelay 50 \ + -openbuttonimage "" -closedbuttonimage "" -backgroundmode row + + # Restore default bindings to the demo list + bindtags $T [list $T TreeCtrl [winfo toplevel $T] all] + + catch {destroy $T.entry} + catch {destroy $T.text} + + return +} + +# +# Demo: Picture catalog +# +proc DemoPictureCatalog {} { + + set T .f2.f1.t + + $T configure -showroot no -showbuttons no -showlines no \ + -selectmode multiple -orient horizontal -wrap window \ + -yscrollincrement 50 -showheader no + + $T element create elemTxt text -fill {SystemHighlightText {selected focus}} + $T element create elemSelTxt rect -fill {SystemHighlight {selected focus}} + $T element create elemSelImg rect -outline {SystemHighlight {selected focus}} \ + -outlinewidth 4 + $T element create elemImg rect -fill gray -width 80 -height 120 + + set S [$T style create STYLE -orient vertical] + $T style elements $S {elemSelImg elemImg elemSelTxt elemTxt} + $T style layout $S elemSelImg -union elemImg -ipadn 6 -ipads 6 -ipadw 6 -ipade 6 + $T style layout $S elemSelTxt -union elemTxt + $T style layout $S elemImg -pads 6 + + for {set i 1} {$i <= 10} {incr i} { + set I [$T item create] + $T item style set $I 0 $S + $T item text $I 0 "Picture #$i" + $T item lastchild root $I + } + + return +} + +# +# Demo: Picture catalog +# +proc DemoPictureCatalog2 {} { + + set T .f2.f1.t + + $T configure -showroot no -showbuttons no -showlines no \ + -selectmode multiple -orient horizontal -wrap window \ + -yscrollincrement 50 -showheader no + + $T element create elemTxt text -fill {SystemHighlightText {selected focus}} \ + -justify left -wrap word -lines 2 + $T element create elemSelTxt rect -fill {SystemHighlight {selected focus}} + $T element create elemSelImg rect -outline {SystemHighlight {selected focus}} \ + -outlinewidth 4 + $T element create elemImg rect -fill gray + + set S [$T style create STYLE -orient vertical] + $T style elements $S {elemSelImg elemImg elemSelTxt elemTxt} + $T style layout $S elemSelImg -union elemImg \ + -ipadn 6 -ipads 6 -ipadw 6 -ipade 6 + $T style layout $S elemSelTxt -union elemTxt + $T style layout $S elemImg -pads 6 + $T style layout $S elemImg -expand n + $T style layout $S elemTxt -expand s + + for {set i 1} {$i <= 10} {incr i} { + set I [$T item create] + $T item style set $I 0 $S + $T item text $I 0 "This is\nPicture\n#$i" + $T item element configure $I 0 elemImg -width [expr int(20 + rand() * 80)] \ + -height [expr int(20 + rand() * 120)] + $T item lastchild root $I + } + + return +} + + + + +proc CursorWindow {} { + set w .cursors + if {[winfo exists $w]} { + destroy $w + } + toplevel $w + set c [canvas $w.canvas -background white -width [expr {50 * 10}] \ + -highlightthickness 0 -borderwidth 0] + pack $c -expand yes -fill both + set cursors { + X_cursor + arrow + based_arrow_down + based_arrow_up + boat + bogosity + bottom_left_corner + bottom_right_corner + bottom_side + bottom_tee + box_spiral + center_ptr + circle + clock + coffee_mug + cross + cross_reverse + crosshair + diamond_cross + dot + dotbox + double_arrow + draft_large + draft_small + draped_box + exchange + fleur + gobbler + gumby + hand1 + hand2 + heart + icon + iron_cross + left_ptr + left_side + left_tee + leftbutton + ll_angle + lr_angle + man + middlebutton + mouse + pencil + pirate + plus + question_arrow + right_ptr + right_side + right_tee + rightbutton + rtl_logo + sailboat + sb_down_arrow + sb_h_double_arrow + sb_left_arrow + sb_right_arrow + sb_up_arrow + sb_v_double_arrow + shuttle + sizing + spider + spraycan + star + target + tcross + top_left_arrow + top_left_corner + top_right_corner + top_side + top_tee + trek + ul_angle + umbrella + ur_angle + watch + xterm + } + set col 0 + set row 0 + foreach cursor $cursors { + set x [expr {$col * 50}] + set y [expr {$row * 40}] + $c create rectangle $x $y [expr {$x + 50}] [expr {$y + 40}] \ + -fill gray90 -outline black -width 2 -tags $cursor.rect + $c create text [expr {$x + 50 / 2}] [expr {$y + 4}] -text $cursor \ + -anchor n -width 42 -tags $cursor.text + if {[incr col] == 10} { + set col 0 + incr row + } + $c bind $cursor.rect " + $c configure -cursor $cursor + $c itemconfigure $cursor.rect -fill linen + " + $c bind $cursor.rect " + $c configure -cursor {} + $c itemconfigure $cursor.rect -fill gray90 + " + $c bind $cursor.text " + $c configure -cursor $cursor + " + $c bind $cursor.text " + $c configure -cursor {} + " + } + $c configure -height [expr {($row + 1) * 40}] + return +} + +proc compare {i1 i2} { + if {$i1 < $i2} { return -1 } + if {$i1 == $i2} { return 0 } + return 1 +} + +# A little screen magnifier for X11 +if {$::tcl_platform(platform) eq "unix"} { + +set Loupe(zoom) 3 +set Loupe(x) 0 +set Loupe(y) 0 +set Loupe(auto) 1 + +proc LoupeAfter {} { + + global Loupe + + set x [winfo pointerx .] + set y [winfo pointery .] + if {$Loupe(auto) || ($Loupe(x) != $x) || ($Loupe(y) != $y)} { + set w [image width $Loupe(image)] + set h [image height $Loupe(image)] + loupe $Loupe(image) $x $y $w $h $::Loupe(zoom) + set Loupe(x) $x + set Loupe(y) $y + } + after $Loupe(delay) LoupeAfter + return +} + +proc MakeLoupeWindow {} { + + global Loupe + + set w [toplevel .loupe] + wm geometry $w -0+0 + image create photo ImageLoupe -width 150 -height 150 + pack [label $w.label -image ImageLoupe] + set Loupe(image) ImageLoupe + set Loupe(delay) 500 + after $Loupe(delay) LoupeAfter + return +} +MakeLoupeWindow + +# unix +} + diff --git a/demos/bitmaps.tcl b/demos/bitmaps.tcl new file mode 100644 index 0000000..36917af --- /dev/null +++ b/demos/bitmaps.tcl @@ -0,0 +1,55 @@ +# +# Demo: Bitmaps +# +proc DemoBitmaps {} { + + set T .f2.f1.t + + $T configure -showroot no -showbuttons no -showlines no \ + -selectmode browse -orient horizontal -wrap "5 items" \ + -showheader no + + $T column configure 0 -itembackground {gray90 {}} + + $T element create elemTxt text -fill [list $::SystemHighlightText {selected focus}] + $T element create elemSelTxt rect -fill [list $::SystemHighlight {selected focus}] \ + -showfocus yes + $T element create elemSelBmp rect -outline [list $::SystemHighlight {selected focus}] \ + -outlinewidth 4 + $T element create elemBmp bitmap \ + -foreground [list $::SystemHighlight {selected focus}] \ + -background linen \ + -bitmap {question {selected}} + + set S [$T style create STYLE -orient vertical] + $T style elements $S {elemSelBmp elemBmp elemSelTxt elemTxt} + $T style layout $S elemSelBmp -union elemBmp \ + -ipadn 6 -ipads 6 -ipadw 6 -ipade 6 + $T style layout $S elemBmp -pads 6 -expand we + $T style layout $S elemSelTxt -union elemTxt -ipadw 2 -ipade 2 + $T style layout $S elemTxt -expand we + + set bitmapNames [list error gray75 gray50 gray25 gray12 hourglass info \ + questhead question warning] + + foreach name $bitmapNames { + set I [$T item create] + $T item style set $I 0 $S + $T item text $I 0 $name + $T item element configure $I 0 elemBmp -bitmap $name + $T item lastchild root $I + } + + foreach name $bitmapNames { + set I [$T item create] + $T item style set $I 0 $S + $T item text $I 0 $name + $T item element configure $I 0 elemBmp -bitmap $name \ + -foreground [list $::SystemHighlight {selected focus} brown {}] \ + -background {"" {}} + $T item lastchild root $I + } + + return +} + diff --git a/demos/explorer.tcl b/demos/explorer.tcl new file mode 100644 index 0000000..ab388f7 --- /dev/null +++ b/demos/explorer.tcl @@ -0,0 +1,367 @@ +set Dir [file dirname [Path]] + +proc DemoExplorerAux {scriptDir scriptFile} { + + set T .f2.f1.t + + set clicks [clock clicks] + set globDirs [glob -nocomplain -types d -dir $::Dir *] + set clickGlobDirs [expr {[clock clicks] - $clicks}] + + set clicks [clock clicks] + set list [lsort -dictionary $globDirs] + set clickSortDirs [expr {[clock clicks] - $clicks}] + + set clicks [clock clicks] + foreach file $list $scriptDir + set clickAddDirs [expr {[clock clicks] - $clicks}] + + set clicks [clock clicks] + set globFiles [glob -nocomplain -types f -dir $::Dir *] + set clickGlobFiles [expr {[clock clicks] - $clicks}] + + set clicks [clock clicks] + set list [lsort -dictionary $globFiles] + set clickSortFiles [expr {[clock clicks] - $clicks}] + + set clicks [clock clicks] + foreach file $list $scriptFile + set clickAddFiles [expr {[clock clicks] - $clicks}] + + set gd [ClicksToSeconds $clickGlobDirs] + set sd [ClicksToSeconds $clickSortDirs] + set ad [ClicksToSeconds $clickAddDirs] + set gf [ClicksToSeconds $clickGlobFiles] + set sf [ClicksToSeconds $clickSortFiles] + set af [ClicksToSeconds $clickAddFiles] + puts "dirs([llength $globDirs]) glob/sort/add $gd/$sd/$ad files([llength $globFiles]) glob/sort/add $gf/$sf/$af" + + set ::TreeCtrl::Priv(DirCnt,$T) [llength $globDirs] + + return +} + +# +# Demo: explorer files +# +proc DemoExplorerDetails {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode extended -xscrollincrement 20 \ + -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50" + + InitPics small-* + + $T column configure 0 -text Name -tag name -width 200 \ + -arrow up -arrowpadw 6 -arrowpade 6 + $T column configure 1 -text Size -tag size -justify right -width 60 \ + -arrowside left -arrowgravity right + $T column configure 2 -text Type -tag type -width 120 + $T column configure 3 -text Modified -tag modified -width 120 + + $T element create e1 image -image {small-folderSel {selected} small-folder {}} + $T element create e2 text -fill [list $::SystemHighlightText {selected focus}] \ + -lines 1 + $T element create txtType text -lines 1 + $T element create txtSize text -datatype integer -format "%dKB" -lines 1 + $T element create txtDate text -datatype time -format "%d/%m/%y %I:%M %p" -lines 1 + $T element create e4 rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -showfocus yes + + # image + text + set S [$T style create styName -orient horizontal] + $T style elements $S {e4 e1 e2} + $T style layout $S e1 -expand ns + $T style layout $S e2 -padw 2 -squeeze x -expand ns + $T style layout $S e4 -union [list e2] -iexpand ns -ipadw 2 -ipade 2 + + # column 1: text + set S [$T style create stySize] + $T style elements $S txtSize + $T style layout $S txtSize -padw 6 -pade 6 -squeeze x -expand ns + + # column 2: text + set S [$T style create styType] + $T style elements $S txtType + $T style layout $S txtType -padw 6 -pade 6 -squeeze x -expand ns + + # column 3: text + set S [$T style create styDate] + $T style elements $S txtDate + $T style layout $S txtDate -padw 6 -pade 6 -squeeze x -expand ns + + set ::TreeCtrl::Priv(edit,$T) {e2} + set ::TreeCtrl::Priv(sensitive,$T) {e1 e2} + set ::TreeCtrl::Priv(dragimage,$T) { + {0 styName e1 e2} + } + + $T notify bind $T { + %T item text %I 0 %t + } + + set scriptDir { + set item [$T item create] + $T item style set $item 0 styName 2 styType 3 styDate + $T item complex $item \ + [list [list e2 -text [file tail $file]]] \ + [list] \ + [list [list txtType -text "Folder"]] \ + [list [list txtDate -data [file mtime $file]]] + $T item lastchild root $item + } + + set scriptFile { + set item [$T item create] + $T item style set $item 0 styName 1 stySize 2 styType 3 styDate + switch [file extension $file] { + .dll { set img small-dll } + .exe { set img small-exe } + .txt { set img small-txt } + default { set img small-file } + } + set type [string toupper [file extension $file]] + if {$type ne ""} { + set type "[string range $type 1 end] " + } + append type "File" + $T item complex $item \ + [list [list e1 -image [list ${img}Sel {selected} $img {}]] [list e2 -text [file tail $file]]] \ + [list [list txtSize -data [expr {[file size $file] / 1024 + 1}]]] \ + [list [list txtType -text $type]] \ + [list [list txtDate -data [file mtime $file]]] + $T item lastchild root $item + } + + DemoExplorerAux $scriptDir $scriptFile + + set ::SortColumn 0 + $T notify bind $T { ExplorerHeaderInvoke %T %C } + + bindtags $T [list $T TreeCtrlFileList TreeCtrl [winfo toplevel $T] all] + + return +} + +proc ExplorerHeaderInvoke {T C} { + global SortColumn + if {$C == $SortColumn} { + if {[$T column cget $SortColumn -arrow] eq "down"} { + set order -increasing + set arrow up + } else { + set order -decreasing + set arrow down + } + } else { + if {[$T column cget $SortColumn -arrow] eq "down"} { + set order -decreasing + set arrow down + } else { + set order -increasing + set arrow up + } + $T column configure $SortColumn -arrow none + set SortColumn $C + } + $T column configure $C -arrow $arrow + set dirCount $::TreeCtrl::Priv(DirCnt,$T) + set lastDir [expr {$dirCount - 1}] + switch [$T column cget $C -tag] { + name { + if {$dirCount} { + $T item sort root $order -last "root child $lastDir" -column $C -dictionary + } + if {$dirCount < [$T numitems] - 1} { + $T item sort root $order -first "root child $dirCount" -column $C -dictionary + } + } + size { + if {$dirCount < [$T numitems] - 1} { + $T item sort root $order -first "root child $dirCount" -column $C -integer -column name -dictionary + } + } + type { + if {$dirCount < [$T numitems] - 1} { + $T item sort root $order -first "root child $dirCount" -column $C -dictionary -column name -dictionary + } + } + modified { + if {$dirCount} { + $T item sort root $order -last "root child $dirCount prevsibling" -column $C -integer -column name -dictionary + } + if {$dirCount < [$T numitems] - 1} { + $T item sort root $order -first "root child $dirCount" -column $C -integer -column name -dictionary + } + } + } + return +} + +proc DemoExplorerLargeIcons {} { + + set T .f2.f1.t + + # Item height is 32 for icon, 4 padding, 3 lines of text + set itemHeight [expr {32 + 4 + [font metrics [$T cget -font] -linespace] * 3}] + + $T configure -showroot no -showbuttons no -showlines no \ + -selectmode extended -wrap window -orient horizontal \ + -itemheight $itemHeight -showheader no \ + -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50" + + InitPics big-* + + $T column configure 0 -width 75 + + $T element create elemImg image -image {big-folderSel {selected} big-folder {}} + $T element create elemTxt text -fill [list $::SystemHighlightText {selected focus}] \ + -justify center -lines 1 -width 71 -wrap word + $T element create elemSel rect -fill [list $::SystemHighlight {selected focus} gray {selected}] -showfocus yes + + # image + text + set S [$T style create STYLE -orient vertical] + $T style elements $S {elemSel elemImg elemTxt} + $T style layout $S elemImg -expand we + $T style layout $S elemTxt -padn 4 -padw 2 -pade 2 -squeeze x -expand we + $T style layout $S elemSel -union [list elemTxt] + + set ::TreeCtrl::Priv(edit,$T) {elemTxt} + set ::TreeCtrl::Priv(sensitive,$T) {elemImg elemTxt} + set ::TreeCtrl::Priv(dragimage,$T) { + {0 STYLE elemImg elemTxt} + } + + $T notify bind $T { + %T item text %I 0 %t + } + + set scriptDir { + set item [$T item create] + $T item style set $item 0 STYLE + $T item text $item 0 [file tail $file] + $T item lastchild root $item + } + + set scriptFile { + set item [$T item create] + $T item style set $item 0 STYLE + switch [file extension $file] { + .dll { set img big-dll } + .exe { set img big-exe } + .txt { set img big-txt } + default { set img big-file } + } + set type [string toupper [file extension $file]] + if {$type ne ""} { + set type "[string range $type 1 end] " + } + append type "File" + $T item complex $item \ + [list [list elemImg -image [list ${img}Sel {selected} $img {}]] [list elemTxt -text [file tail $file]]] + $T item lastchild root $item + } + + DemoExplorerAux $scriptDir $scriptFile + + $T activate [$T index "root firstchild"] + + $T notify bind $T { + %T item element configure %p 0 elemTxt -lines {} + %T item element configure %c 0 elemTxt -lines 3 + } + + bindtags $T [list $T TreeCtrlFileList TreeCtrl [winfo toplevel $T] all] + + return +} + +# Tree is horizontal, wrapping occurs at right edge of window, each item +# is as wide as the smallest needed multiple of 110 pixels +proc DemoExplorerSmallIcons {} { + set T .f2.f1.t + DemoExplorerList + $T configure -orient horizontal -xscrollincrement 0 + $T column configure 0 -width {} -stepwidth 110 -widthhack no +} + +# Tree is vertical, wrapping occurs at bottom of window, each range has the +# same width (as wide as the longest item), xscrollincrement is by range +proc DemoExplorerList {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode extended -wrap window -showheader no \ + -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50" + + InitPics small-* + + $T column configure 0 -widthhack yes + + $T element create elemImg image -image {small-folderSel {selected} small-folder {}} + $T element create elemTxt text -fill [list $::SystemHighlightText {selected focus}] \ + -lines 1 + $T element create elemSel rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -showfocus yes + + # image + text + set S [$T style create STYLE] + $T style elements $S {elemSel elemImg elemTxt} + $T style layout $S elemImg -expand ns + $T style layout $S elemTxt -squeeze x -expand ns -padw 2 + $T style layout $S elemSel -union [list elemTxt] -iexpand ns -ipadw 2 -ipade 2 + + set ::TreeCtrl::Priv(edit,$T) {elemTxt} + set ::TreeCtrl::Priv(sensitive,$T) {elemImg elemTxt} + set ::TreeCtrl::Priv(dragimage,$T) { + {0 STYLE elemImg elemTxt} + } + + $T notify bind $T { + %T item text %I 0 %t + } + + set scriptDir { + set item [$T item create] + $T item style set $item 0 STYLE + $T item text $item 0 [file tail $file] + $T item lastchild root $item + } + + set scriptFile { + set item [$T item create] + $T item style set $item 0 STYLE + switch [file extension $file] { + .dll { set img small-dll } + .exe { set img small-exe } + .txt { set img small-txt } + default { set img small-file } + } + set type [string toupper [file extension $file]] + if {$type ne ""} { + set type "[string range $type 1 end] " + } + append type "File" + $T item complex $item \ + [list [list elemImg -image [list ${img}Sel {selected} $img {}]] [list elemTxt -text [file tail $file]]] + $T item lastchild root $item + } + + DemoExplorerAux $scriptDir $scriptFile + + $T activate [$T item firstchild root] + + bindtags $T [list $T TreeCtrlFileList TreeCtrl [winfo toplevel $T] all] + + return +} + diff --git a/demos/help.tcl b/demos/help.tcl new file mode 100644 index 0000000..fc67589 --- /dev/null +++ b/demos/help.tcl @@ -0,0 +1,347 @@ +# +# Demo: Help contents +# +proc DemoHelpContents {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode browse + + InitPics help-* + + $T column configure 0 -text "Help Contents" + + # Define a new item state + $T state define mouseover + + $T element create e1 image -image help-page + $T element create e2 image -image {help-book-open {open} help-book-closed {}} + $T element create e3 text -fill [list $::SystemHighlightText {selected focus} blue {mouseover}] \ + -font [list "[$T cget -font] underline" {mouseover}] + $T element create e4 rect -fill [list $::SystemHighlight {selected focus}] -showfocus yes + + # book + set S [$T style create s1] + $T style elements $S {e4 e1 e3} + $T style layout $S e1 -pade 4 -expand ns + $T style layout $S e3 -expand ns + $T style layout $S e4 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + # page + set S [$T style create s2] + $T style elements $S {e4 e2 e3} + $T style layout $S e2 -pade 4 -expand ns + $T style layout $S e3 -expand ns + $T style layout $S e4 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + set parentList [list root {} {} {} {} {} {}] + set parent root + foreach {depth style text} { + 0 s1 "Welcome to Help" + 0 s2 "Introducing Windows 98" + 1 s2 "How to Use Help" + 2 s1 "Find a topic" + 2 s1 "Get more out of help" + 1 s2 "Register Your Software" + 2 s1 "Registering Windows 98 online" + 1 s2 "What's New in Windows 98" + 2 s1 "Innovative, easy-to-use features" + 2 s1 "Improved reliability" + 2 s1 "A faster operating system" + 2 s1 "True Web integration" + 2 s1 "More entertaining and fun" + 1 s2 "If You're New to Windows 98" + 2 s2 "Tips for Macintosh Users" + 3 s1 "Why does the mouse have two buttons?" + } { + set item [$T item create] + $T item style set $item 0 $style + $T item element configure $item 0 e3 -text $text + $T collapse $item + $T item lastchild [lindex $parentList $depth] $item + incr depth + set parentList [lreplace $parentList $depth $depth $item] + } + + bind TreeCtrlHelp { + TreeCtrl::DoubleButton1 %W %x %y + } + bind TreeCtrlHelp { + TreeCtrl::HelpButton1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpLeave1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpRelease1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion %W %x %y + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion %W %x %y + } + bind TreeCtrlHelp { + if {[llength [%W selection get]] == 1} { + %W toggle [%W selection get] + } + break + } + + set ::TreeCtrl::Priv(help,prev) "" + bindtags $T [list $T TreeCtrlHelp TreeCtrl [winfo toplevel $T] all] + + return +} + +# This is an alternate implementation that does not define a new item state +# to change the appearance of the item under the cursor. +proc DemoHelpContents2 {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode browse + + InitPics help-* + + $T column configure 0 -text "Help Contents" + + $T element create e1 image -image help-page + $T element create e2 image -image {help-book-open {open} help-book-closed {}} + $T element create e3 text -fill [list $::SystemHighlightText {selected focus}] + $T element create e4 rect -fill [list $::SystemHighlight {selected focus}] -showfocus yes + $T element create e5 text -fill [list $::SystemHighlightText {selected focus} blue {}] \ + -font "[$T cget -font] underline" + + # book + set S [$T style create s1] + $T style elements $S {e4 e1 e3} + $T style layout $S e1 -pade 4 -expand ns + $T style layout $S e3 -expand ns + $T style layout $S e4 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + # page + set S [$T style create s2] + $T style elements $S {e4 e2 e3} + $T style layout $S e2 -pade 4 -expand ns + $T style layout $S e3 -expand ns + $T style layout $S e4 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + # book (focus) + set S [$T style create s1.f] + $T style elements $S {e4 e1 e5} + $T style layout $S e1 -pade 4 -expand ns + $T style layout $S e5 -expand ns + $T style layout $S e4 -union [list e5] -iexpand ns -ipadw 1 -ipade 2 + + # page (focus) + set S [$T style create s2.f] + $T style elements $S {e4 e2 e5} + $T style layout $S e2 -pade 4 -expand ns + $T style layout $S e5 -expand ns + $T style layout $S e4 -union [list e5] -iexpand ns -ipadw 1 -ipade 2 + + set parentList [list root {} {} {} {} {} {}] + set parent root + foreach {depth style text} { + 0 s1 "Welcome to Help" + 0 s2 "Introducing Windows 98" + 1 s2 "How to Use Help" + 2 s1 "Find a topic" + 2 s1 "Get more out of help" + 1 s2 "Register Your Software" + 2 s1 "Registering Windows 98 online" + 1 s2 "What's New in Windows 98" + 2 s1 "Innovative, easy-to-use features" + 2 s1 "Improved reliability" + 2 s1 "A faster operating system" + 2 s1 "True Web integration" + 2 s1 "More entertaining and fun" + 1 s2 "If You're New to Windows 98" + 2 s2 "Tips for Macintosh Users" + 3 s1 "Why does the mouse have two buttons?" + } { + set item [$T item create] + $T item style set $item 0 $style + $T item element configure $item 0 e3 -text $text + $T collapse $item + $T item lastchild [lindex $parentList $depth] $item + incr depth + set parentList [lreplace $parentList $depth $depth $item] + } + + bind TreeCtrlHelp { + TreeCtrl::DoubleButton1 %W %x %y + } + bind TreeCtrlHelp { + TreeCtrl::HelpButton1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpLeave1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpRelease1 %W %x %y + break + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion2 %W %x %y + } + bind TreeCtrlHelp { + TreeCtrl::HelpMotion2 %W %x %y + } + bind TreeCtrlHelp { + if {[llength [%W selection get]] == 1} { + %W toggle [%W selection get] + } + break + } + + set ::TreeCtrl::Priv(help,prev) "" + bindtags $T [list $T TreeCtrlHelp TreeCtrl [winfo toplevel $T] all] + + return +} + +proc TreeCtrl::HelpButton1 {w x y} { + variable Priv + focus $w + set id [$w identify $x $y] + set Priv(buttonMode) "" + if {[lindex $id 0] eq "header"} { + ButtonPress1 $w $x $y + } elseif {[lindex $id 0] eq "item"} { + set item [lindex $id 1] + # didn't click an element + if {[llength $id] != 6} return + if {[$w selection includes $item]} { + $w toggle $item + return + } + if {[llength [$w selection get]} { + set item2 [$w selection get] + $w collapse $item2 + foreach item2 [$w item ancestors $item2] { + if {[$w compare $item != $item2]} { + $w collapse $item2 + } + } + } + $w selection modify $item all + $w activate $item + eval $w expand [$w item ancestors $item] + $w toggle $item + } + return +} +proc TreeCtrl::HelpMotion1 {w x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $w $x $y + } + } + return +} +proc TreeCtrl::HelpLeave1 {w x y} { + variable Priv + # This is called when I do ButtonPress-1 on Unix for some reason, + # and buttonMode is undefined. + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + "header" { + $w column configure $Priv(column) -sunken no + } + } + return +} +proc TreeCtrl::HelpRelease1 {w x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Release1 $w $x $y + } + } + set Priv(buttonMode) "" + return +} + +proc TreeCtrl::HelpMotion {w x y} { + variable Priv + set id [$w identify $x $y] + if {$id eq ""} { + } elseif {[lindex $id 0] eq "header"} { + } elseif {[lindex $id 0] eq "item"} { + set item [lindex $id 1] + if {[llength $id] == 6} { + if {$item ne $Priv(help,prev)} { + if {$Priv(help,prev) ne ""} { + $w item state $Priv(help,prev) !mouseover + } + $w item state $item mouseover + set Priv(help,prev) $item + } + return + } + } + if {$Priv(help,prev) ne ""} { + $w item state $Priv(help,prev) !mouseover + set Priv(help,prev) "" + } + return +} + +# Alternate implementation doesn't rely on mouseover state +proc TreeCtrl::HelpMotion2 {w x y} { + variable Priv + set id [$w identify $x $y] + if {[lindex $id 0] eq "header"} { + } elseif {$id ne ""} { + set item [lindex $id 1] + if {[llength $id] == 6} { + if {$item ne $Priv(help,prev)} { + if {$Priv(help,prev) ne ""} { + set style [$w item style set $Priv(help,prev) 0] + set style [string trim $style .f] + $w item style map $Priv(help,prev) 0 $style {e5 e3} + } + set style [$w item style set $item 0] + $w item style map $item 0 $style.f {e3 e5} + set Priv(help,prev) $item + } + return + } + } + if {$Priv(help,prev) ne ""} { + set style [$w item style set $Priv(help,prev) 0] + set style [string trim $style .f] + $w item style map $Priv(help,prev) 0 $style {e5 e3} + set Priv(help,prev) "" + } + return +} + diff --git a/demos/imovie.tcl b/demos/imovie.tcl new file mode 100644 index 0000000..7540dfe --- /dev/null +++ b/demos/imovie.tcl @@ -0,0 +1,111 @@ +# +# Demo: iMovie +# +proc DemoIMovie {} { + + set T .f2.f1.t + + $T configure -showroot no -showbuttons no -showlines no \ + -selectmode browse -orient horizontal -wrap window \ + -showheader no -background #dcdcdc + + InitPics imovie-* + + if {$::tcl_platform(platform) eq "unix"} { + set font1 {Helvetica 12} + set font2 {Helvetica 14} + } else { + set font1 {Helvetica 8} + set font2 {Helvetica 10} + } + + $T element create elemTime text -font [list $font1] + $T element create elemName text -font [list $font2] -lines 1 -width 80 + $T element create elemRect rect -fill {#ffdc5a {selected} white {}} \ + -outline #827878 -outlinewidth 1 + $T element create elemImg image + $T element create elemShadow rect -outline gray -outlinewidth 1 -open wn + + set S [$T style create STYLE -orient vertical] + $T style elements $S {elemShadow elemRect elemTime elemImg elemName} + $T style layout $S elemShadow -detach yes -pade 2 -pads 2 -padn 1 -padw 1 -iexpand es + $T style layout $S elemTime -padw 2 + $T style layout $S elemImg -pads 1 + $T style layout $S elemName -expand we -ipads 2 -pade 3 -squeeze x + $T style layout $S elemRect -union {elemTime elemImg elemName} \ + -ipadw 6 -ipade 6 -pade 3 -pads 3 + +for {set i 0} {$i < 5} {incr i} { + foreach {time name image} { + 15:20 "Clip 1" imovie-01 + 19:18 "Clip 2" imovie-02 + 07:20 "Clip 3" imovie-03 + 07:20 "Clip 4" imovie-04 + 07:20 "Clip 5" imovie-05 + 07:20 "Clip 6" imovie-06 + 07:20 "Clip 7" imovie-07 + } { + set I [$T item create] + $T item style set $I 0 $S + $T item element configure $I 0 elemTime -text $time + $T item element configure $I 0 elemImg -image $image + $T item element configure $I 0 elemName -text $name + $T item lastchild root $I + } +} + + $T notify bind $T { + %T item element configure %I %C %E -text %t + } + + bind iMovie { + iMovieButton1 %W %x %y + } + + bindtags $T [list $T iMovie TreeCtrl [winfo toplevel $T] all] + + return +} + +proc iMovieButton1 {T x y} { + focus $T + set id [$T identify $x $y] + + # Click outside any item + if {$id eq ""} { + + # Click in header + } elseif {[lindex $id 0] eq "header"} { + ::TreeCtrl::ButtonPress1 $T $x $y + + # Click in item + } elseif {[lindex $id 0] eq "item"} { + ::TreeCtrl::ButtonPress1 $T $x $y + update + foreach {where item arg1 arg2 arg3 arg4} $id {} + switch $arg1 { + column { + set I [lindex $id 1] + if {[llength $id] == 6} { + set E [lindex $id end] + if {$E eq "elemName"} { + set exists [winfo exists $T.entry] + ::TreeCtrl::EntryOpen $T $I 0 $E + if {!$exists} { + $T.entry configure -borderwidth 0 -justify center \ + -background #ffdc5a + scan [$T item bbox $I 0 $E] "%d %d %d %d" x1 y1 x2 y2 + place $T.entry -y [expr {$y1 - 1}] + } + $T.entry selection clear + scan [$T item bbox $I] "%d %d %d %d" x1 y1 x2 y2 + place $T.entry -x [expr {$x1 + 1}] -width [expr {$x2 - $x1 - 5}] +puts @[expr {$x - ($x1 + 1)}] + $T.entry icursor [$T.entry index @[expr {$x - ($x1 + 1)}]] + } + } + } + } + } + return -code break +} diff --git a/demos/layout.tcl b/demos/layout.tcl new file mode 100644 index 0000000..504cd26 --- /dev/null +++ b/demos/layout.tcl @@ -0,0 +1,98 @@ +# +# Demo: Layout +# +proc DemoLayout {} { + + set T .f2.f1.t + + $T configure -showroot yes -showrootbutton yes -showbuttons yes \ + -showlines yes -itemheight 0 -selectmode browse + + $T column configure 0 -text Layout + + $T element create e1 rect -width 30 -height 30 -fill gray20 + $T element create e2 rect -width 30 -height 30 -fill gray40 \ + -outline blue -outlinewidth 3 + $T element create e3 rect -fill gray60 + $T element create e4 rect -fill [list $::SystemHighlight {selected focus} gray80 {}] \ + -showfocus yes + $T element create e5 rect -fill "{sky blue}" -width 20 -height 20 + $T element create e6 rect -fill "{sky blue}" -width 30 -height 16 + $T element create e7 rect -fill "{sky blue}" -width 30 -height 16 + + set S [$T style create s1] + $T style elements $S {e4 e3 e1 e2 e5 e6 e7} + $T style layout $S e1 -padw 28 -pade 4 -padn 4 -pads 4 + $T style layout $S e2 -expand es -pade 38 + $T style layout $S e3 -union [list e1 e2] -ipadw 4 -ipade 4 -ipadn 4 -ipads 4 -padn 2 -pads 2 + $T style layout $S e4 -detach yes -iexpand es + $T style layout $S e5 -detach yes -padw 2 -padn 2 -pads 2 -iexpand s + $T style layout $S e6 -detach yes -expand ws -pade 2 -padn 2 + $T style layout $S e7 -detach yes -expand wn -pade 2 -pads 2 + + $T item style set root 0 $S + $T item hasbutton root yes + + set item [$T item create] + $T item hasbutton $item no + $T item style set $item 0 $S + $T item lastchild root $item + + ### + + set S [$T style create s2] + $T style elements $S {e4 e3 e1} + $T style layout $S e1 -padw 8 -pade 8 -padn 8 -pads 8 -iexpand e + $T style layout $S e3 -union e1 -ipadw 20 -ipade 4 -ipadn 4 -ipads 12 + $T style layout $S e4 -detach yes -iexpand es + + set I [$T item create] + $T item hasbutton $I yes + $T item style set $I 0 $S + $T item lastchild root $I + + set I2 [$T item create] + $T item hasbutton $I2 no + $T item style set $I2 0 $S + $T item lastchild $I $I2 + + ### + + set S [$T style create s3] + $T style elements $S {e4 e3 e1 e5 e6} + $T style layout $S e4 -union {e1 e6} -ipadw 8 -ipadn 8 -ipade 8 + $T style layout $S e3 -union {e1 e5} -ipadw 4 -ipadn 4 -ipade 4 -ipads 4 + $T style layout $S e5 -ipads 20 + + set I [$T item create] + $T item hasbutton $I yes + $T item style set $I 0 $S + $T item lastchild root $I + + set I2 [$T item create] + $T item hasbutton $I2 no + $T item style set $I2 0 $S + $T item lastchild $I $I2 + + ### + + $T element create eb border -background $::SystemButtonFace \ + -relief {sunken {selected} raised {}} -thickness 2 -filled yes + $T element create et text + + set S [$T style create s4] + $T style elements $S {eb et} + $T style layout $S eb -union et -ipadw 2 -ipadn 2 -ipade 2 -ipads 2 + $T style layout $S et -squeeze x + +for {set i 0} {$i < 2} {incr i} { + set I [$T item create] + $T item hasbutton $I no + $T item style set $I 0 $S + $T item text $I 0 "Here is a text element surrounded by a border element\nResize the column to watch me wrap" + $T item lastchild root $I +} + + return +} + diff --git a/demos/mailwasher.tcl b/demos/mailwasher.tcl new file mode 100644 index 0000000..d97dcbb --- /dev/null +++ b/demos/mailwasher.tcl @@ -0,0 +1,183 @@ +# +# Demo: MailWasher +# +proc DemoMailWasher {} { + + set T .f2.f1.t + + InitPics *checked + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showrootbutton no -showbuttons no \ + -showlines no -itemheight $height -selectmode browse \ + -xscrollincrement 1 + + set pad 4 + $T column configure 0 -text Delete -textpadw $pad -textpade $pad -tag delete + $T column configure 1 -text Bounce -textpadw $pad -textpade $pad -tag bounce + $T column configure 2 -text Status -width 80 -textpadw $pad -textpade $pad -tag status + $T column configure 3 -text Size -width 40 -textpadw $pad -textpade $pad -justify right -tag size + $T column configure 4 -text From -width 140 -textpadw $pad -textpade $pad -tag from + $T column configure 5 -text Subject -width 240 -textpadw $pad -textpade $pad -tag subject + $T column configure 6 -text Received -textpadw $pad -textpade $pad -arrow up -arrowpadw 4 -tag received + $T column configure 7 -text Attachments -textpadw $pad -textpade $pad -tag attachments + + $T element create border rect -open nw -outline gray -outlinewidth 1 \ + -fill [list $::SystemHighlight {selected}] + $T element create imgOff image -image unchecked + $T element create imgOn image -image checked + $T element create txtAny text \ + -fill [list $::SystemHighlightText {selected}] -lines 1 + $T element create txtNone text -text "none" \ + -fill [list $::SystemHighlightText {selected}] -lines 1 + $T element create txtYes text -text "yes" \ + -fill [list $::SystemHighlightText {selected}] -lines 1 + $T element create txtNormal text -text "Normal" \ + -fill [list $::SystemHighlightText {selected} #006800 {}] -lines 1 + $T element create txtPossSpam text -text "Possible Spam" \ + -fill [list $::SystemHighlightText {selected} #787800 {}] -lines 1 + $T element create txtProbSpam text -text "Probably Spam" \ + -fill [list $::SystemHighlightText {selected} #FF9000 {}] -lines 1 + $T element create txtBlacklist text -text "Blacklisted" \ + -fill [list $::SystemHighlightText {selected} #FF5800 {}] -lines 1 + + foreach name {Off On} { + set S [$T style create sty$name] + $T style elements $S [list border img$name] + $T style layout $S border -detach yes -iexpand es + $T style layout $S img$name -expand wnes + } + + set pad 4 + + foreach name {Any None Yes Normal PossSpam ProbSpam Blacklist} { + set S [$T style create sty$name] + $T style elements $S [list border txt$name] + $T style layout $S border -detach yes -iexpand es + $T style layout $S txt$name -padw $pad -pade $pad -squeeze x -expand ns + } +for {set i 0} {$i < 1} {incr i} { + foreach {from subject} { + baldy@spammer.com "Your hair is thinning" + flat@spammer.com "Your breasts are too small" + tiny@spammer.com "Your penis is too small" + dumbass@spammer.com "You are not very smart" + bankrobber@spammer.com "You need more money" + loser@spammer.com "You need better friends" + gossip@spammer.com "Find out what your coworkers think about you" + whoami@spammer.com "Find out what you think about yourself" + downsized@spammer.com "You need a better job" + poorhouse@spammer.com "Your mortgage is a joke" + spam4ever@spammer.com "You need more spam" + } { + set item [$T item create] + set status [lindex [list styNormal styPossSpam styProbSpam styBlacklist] [expr int(rand() * 4)]] + set delete [lindex [list styOn styOff] [expr int(rand() * 2)]] + set bounce [lindex [list styOn styOff] [expr int(rand() * 2)]] + set attachments [lindex [list styNone styYes] [expr int(rand() * 2)]] + $T item style set $item 0 $delete 1 $bounce 2 $status 3 styAny \ + 4 styAny 5 styAny 6 styAny 7 $attachments + set bytes [expr {512 + int(rand() * 1024 * 12)}] + set size [expr {$bytes / 1024 + 1}]KB + set seconds [expr {[clock seconds] - int(rand() * 100000)}] + set received [clock format $seconds -format "%d/%m/%y %I:%M %p"] + $T item text $item 3 $size 4 $from 5 $subject 6 $received + $T item lastchild root $item + } +} + if 0 { + $T notify bind MailWasher { + %T item style set %I %C styOff + } + $T notify bind MailWasher { + %T item style set %I %C styOn + } + } + + set ::SortColumn 6 + $T notify bind $T { + if {%C == $SortColumn} { + if {[%T column cget $SortColumn -arrow] eq "down"} { + set order -increasing + set arrow up + } else { + set order -decreasing + set arrow down + } + } else { + if {[%T column cget $SortColumn -arrow] eq "down"} { + set order -decreasing + set arrow down + } else { + set order -increasing + set arrow up + } + %T column configure $SortColumn -arrow none + set SortColumn %C + } + %T column configure %C -arrow $arrow + switch [%T column cget %C -tag] { + bounce - + delete { + %T item sort root $order -column %C -command [list CompareOnOff %T %C] -column subject -dictionary + } + status { + %T item sort root $order -column %C -dictionary + } + from { + %T item sort root $order -column %C -dictionary -column subject -dictionary + } + subject { + %T item sort root $order -column %C -dictionary + } + size { + %T item sort root $order -column %C -dictionary -column subject -dictionary + } + received { + %T item sort root $order -column %C -dictionary -column subject -dictionary + } + attachments { + %T item sort root $order -column %C -dictionary -column subject -dictionary + } + } + } + + bind MailWasher { + set id [%W identify %x %y] + if {$id eq ""} { + } elseif {[lindex $id 0] eq "header"} { + } else { + foreach {what item where arg1 arg2 arg3} $id {} + if {$where eq "column"} { + set tag [%W column cget $arg1 -tag] + if {$tag eq "delete" || $tag eq "bounce"} { + set style [%W item style set $item $arg1] + if {$style eq "styOn"} { + set style styOff + } else { + set style styOn + } + %W item style set $item $arg1 $style + DisplayStylesInItem $item +# return -code break + } + } + } + } + + bindtags $T [list $T MailWasher TreeCtrl [winfo toplevel $T] all] + + return +} + +proc CompareOnOff {T C item1 item2} { + set s1 [$T item style set $item1 $C] + set s2 [$T item style set $item2 $C] + if {$s1 eq $s2} { return 0 } + if {$s1 eq "styOff"} { return -1 } + return 1 +} + diff --git a/demos/outlook-folders.tcl b/demos/outlook-folders.tcl new file mode 100644 index 0000000..4eac92f --- /dev/null +++ b/demos/outlook-folders.tcl @@ -0,0 +1,106 @@ +# +# Demo: Outlook Express folder list +# +proc DemoOutlookFolders {} { + + InitPics outlook-* + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -itemheight $height -selectmode browse \ + -showroot yes -showrootbutton no -showbuttons yes -showlines yes + + $T column configure 0 -text Folders + + $T element create e1 image + $T element create e2 text -fill [list $::SystemHighlightText {selected focus}] \ + -lines 1 + $T element create e3 text -fill [list $::SystemHighlightText {selected focus}] \ + -font [list "[$T cget -font] bold"] -lines 1 + $T element create e4 text -fill blue + $T element create e5 image -image outlook-folder + $T element create e6 rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] \ + -showfocus yes + + # image + text + set S [$T style create s1] + $T style elements $S {e6 e1 e2} + $T style layout $S e1 -expand ns + $T style layout $S e2 -padw 4 -expand ns -squeeze x + $T style layout $S e6 -union [list e2] -iexpand ns -ipadw 2 -ipade 2 + + # image + text + text + set S [$T style create s2] + $T style elements $S {e6 e1 e3 e4} + $T style layout $S e1 -expand ns + $T style layout $S e3 -padw 4 -pade 4 -expand ns -squeeze x + $T style layout $S e4 -expand ns + $T style layout $S e6 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + # folder + text + set S [$T style create s3] + $T style elements $S {e6 e5 e2} + $T style layout $S e5 -expand ns + $T style layout $S e2 -padw 4 -expand ns -squeeze x + $T style layout $S e6 -union [list e2] -iexpand ns -ipadw 2 -ipade 2 + + # folder + text + text + set S [$T style create s4] + $T style elements $S {e6 e5 e3 e4} + $T style layout $S e5 -expand ns + $T style layout $S e3 -padw 4 -pade 4 -expand ns -squeeze x + $T style layout $S e4 -expand ns + $T style layout $S e6 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + $T item style set root 0 s1 + $T item complex root [list [list e1 -image outlook-main] [list e2 -text "Outlook Express"]] + + set parentList [list root {} {} {} {} {} {}] + set parent root + foreach {depth img text button unread} { + 0 local "Local Folders" yes 0 + 1 inbox Inbox no 5 + 1 outbox Outbox no 0 + 1 sent "Sent Items" no 0 + 1 deleted "Deleted Items" no 50 + 1 draft Drafts no 0 + 1 folder "Messages to Dad" no 0 + 1 folder "Messages to Sis" no 0 + 1 folder "Messages to Me" yes 0 + 2 folder "2001" no 0 + 2 folder "2000" no 0 + 2 folder "1999" no 0 + 0 server "news.gmane.org" yes 0 + 1 group "gmane.comp.lang.lua.general" no 498 + } { + set item [$T item create] + $T item hasbutton $item $button + if {[string equal $img folder]} { + if {$unread} { + $T item style set $item 0 s4 + $T item complex $item [list [list e3 -text $text] [list e4 -text "($unread)"]] + } else { + $T item style set $item 0 s3 + $T item complex $item [list [list e2 -text $text]] + } + } else { + if {$unread} { + $T item style set $item 0 s2 + $T item complex $item [list [list e1 -image outlook-$img] [list e3 -text $text] [list e4 -text "($unread)"]] + } else { + $T item style set $item 0 s1 + $T item complex $item [list [list e1 -image outlook-$img] [list e2 -text $text]] + } + } + $T item lastchild [lindex $parentList $depth] $item + incr depth + set parentList [lreplace $parentList $depth $depth $item] + } + + return +} + diff --git a/demos/outlook-newgroup.tcl b/demos/outlook-newgroup.tcl new file mode 100644 index 0000000..1a18e91 --- /dev/null +++ b/demos/outlook-newgroup.tcl @@ -0,0 +1,382 @@ +# +# Demo: Outlook Express newsgroup messages +# +proc DemoOutlookNewsgroup {} { + + global Message + + InitPics outlook-* + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -itemheight $height -selectmode browse \ + -showroot no -showrootbutton no -showbuttons yes -showlines no + + $T column configure 0 -image outlook-clip -tag clip + $T column configure 1 -image outlook-arrow -tag arrow + $T column configure 2 -image outlook-watch -tag watch + $T column configure 3 -text Subject -width 250 -tag subject + $T column configure 4 -text From -width 150 -tag from + $T column configure 5 -text Sent -width 150 -tag sent + $T column configure 6 -text Size -width 60 -justify right -tag size + + # Would be nice if I could specify a column -tag too + $T configure -treecolumn 3 + + # State for a read message + $T state define read + + # State for a message with unread descendants + $T state define unread + + $T element create elemImg image -image { + outlook-read-2Sel {selected read unread !open} + outlook-read-2 {read unread !open} + outlook-readSel {selected read} + outlook-read {read} + outlook-unreadSel {selected} + outlook-unread {} + } + $T element create elemTxt text -fill [list $::SystemHighlightText {selected focus}] \ + -font [list "[$T cget -font] bold" {read unread !open} "[$T cget -font] bold" {!read}] -lines 1 + $T element create sel.e rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open e -showfocus yes + $T element create sel.w rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open w -showfocus yes + $T element create sel.we rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open we -showfocus yes + + # Image + text + set S [$T style create s1] + $T style elements $S {sel.e elemImg elemTxt} + $T style layout $S elemImg -expand ns + $T style layout $S elemTxt -padw 2 -pade 6 -squeeze x -expand ns + $T style layout $S sel.e -union [list elemTxt] -iexpand nes -ipadw 2 + + # Text + set S [$T style create s2.we] + $T style elements $S {sel.we elemTxt} + $T style layout $S elemTxt -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.we -detach yes -iexpand es + + # Text + set S [$T style create s2.w] + $T style elements $S {sel.w elemTxt} + $T style layout $S elemTxt -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.w -detach yes -iexpand es + + set msgCnt 100 + + set thread 0 + set Message(count,0) 0 + for {set i 1} {$i < $msgCnt} {incr i} { + $T item create + while 1 { + set j [expr {int(rand() * $i)}] + if {$j == 0} break + if {[$T depth $j] == 5} continue + if {$Message(count,$Message(thread,$j)) == 15} continue + break + } + $T item lastchild $j $i + + set Message(read,$i) [expr rand() * 2 > 1] + if {$j == 0} { + set Message(thread,$i) [incr thread] + set Message(seconds,$i) [expr {[clock seconds] - int(rand() * 500000)}] + set Message(seconds2,$i) $Message(seconds,$i) + set Message(count,$thread) 1 + } else { + set Message(thread,$i) $Message(thread,$j) + set Message(seconds,$i) [expr {$Message(seconds2,$j) + int(rand() * 10000)}] + set Message(seconds2,$i) $Message(seconds,$i) + set Message(seconds2,$j) $Message(seconds,$i) + incr Message(count,$Message(thread,$j)) + } + } + + for {set i 1} {$i < $msgCnt} {incr i} { + set subject "This is thread number $Message(thread,$i)" + set from somebody@somewhere.net + set sent [clock format $Message(seconds,$i) -format "%d/%m/%y %I:%M %p"] + set size [expr {1 + int(rand() * 10)}]KB + + # This message has been read + if {$Message(read,$i)} { + $T item state $i read + } + + # This message has unread descendants + if {[AnyUnreadDescendants $T $i]} { + $T item state $i unread + } + + if {[$T item numchildren $i]} { + $T item hasbutton $i yes + + # Collapse some messages + if {rand() * 2 > 1} { + $T collapse $i + } + } + + $T item style set $i 3 s1 4 s2.we 5 s2.we 6 s2.w + $T item text $i 3 $subject 4 $from 5 $sent 6 $size + } + + # Do something when the selection changes + $T notify bind $T { + + # One item is selected + if {[%T selection count] == 1} { + if {[info exists Message(afterId)]} { + after cancel $Message(afterId) + } + set Message(afterId,item) [lindex [%T selection get] 0] + set Message(afterId) [after 500 MessageReadDelayed] + } + } + + return +} + +proc MessageReadDelayed {} { + + global Message + + set T .f2.f1.t + + unset Message(afterId) + set I $Message(afterId,item) + if {![$T selection includes $I]} return + + # This message is not read + if {!$Message(read,$I)} { + + # Read the message + $T item state $I read + set Message(read,$I) 1 + + # Check ancestors (except root) + foreach I2 [lrange [$T item ancestors $I] 0 end-1] { + + # This ancestor has no more unread descendants + if {![AnyUnreadDescendants $T $I2]} { + $T item state $I2 !unread + } + } + } +} + +# Alternate implementation which does not rely on run-time states +proc DemoOutlookNewsgroup2 {} { + + global Message + + InitPics outlook-* + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -itemheight $height -selectmode browse \ + -showroot no -showrootbutton no -showbuttons yes -showlines no + + $T column configure 0 -image outlook-clip -tag clip + $T column configure 1 -image outlook-arrow -tag arrow + $T column configure 2 -image outlook-watch -tag watch + $T column configure 3 -text Subject -width 250 -tag subject + $T column configure 4 -text From -width 150 -tag from + $T column configure 5 -text Sent -width 150 -tag sent + $T column configure 6 -text Size -width 60 -justify right -tag size + + $T configure -treecolumn 3 + + $T element create image.unread image -image outlook-unread + $T element create image.read image -image outlook-read + $T element create image.read2 image -image outlook-read-2 + $T element create text.read text -fill [list $::SystemHighlightText {selected focus}] \ + -lines 1 + $T element create text.unread text -fill [list $::SystemHighlightText {selected focus}] \ + -font [list "[$T cget -font] bold"] -lines 1 + $T element create sel.e rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open e -showfocus yes + $T element create sel.w rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open w -showfocus yes + $T element create sel.we rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open we -showfocus yes + + # Image + text + set S [$T style create unread] + $T style elements $S {sel.e image.unread text.unread} + $T style layout $S image.unread -expand ns + $T style layout $S text.unread -padw 2 -pade 6 -squeeze x -expand ns + $T style layout $S sel.e -union [list text.unread] -iexpand nes -ipadw 2 + + # Image + text + set S [$T style create read] + $T style elements $S {sel.e image.read text.read} + $T style layout $S image.read -expand ns + $T style layout $S text.read -padw 2 -pade 6 -squeeze x -expand ns + $T style layout $S sel.e -union [list text.read] -iexpand nes -ipadw 2 + + # Image + text + set S [$T style create read2] + $T style elements $S {sel.e image.read2 text.unread} + $T style layout $S image.read2 -expand ns + $T style layout $S text.unread -padw 2 -pade 6 -squeeze x -expand ns + $T style layout $S sel.e -union [list text.unread] -iexpand nes -ipadw 2 + + # Text + set S [$T style create unread.we] + $T style elements $S {sel.we text.unread} + $T style layout $S text.unread -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.we -detach yes -iexpand es + + # Text + set S [$T style create read.we] + $T style elements $S {sel.we text.read} + $T style layout $S text.read -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.we -detach yes -iexpand es + + # Text + set S [$T style create unread.w] + $T style elements $S {sel.w text.unread} + $T style layout $S text.unread -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.w -detach yes -iexpand es + + # Text + set S [$T style create read.w] + $T style elements $S {sel.w text.read} + $T style layout $S text.read -padw 6 -pade 6 -squeeze x -expand ns + $T style layout $S sel.w -detach yes -iexpand es + + set msgCnt 100 + + set thread 0 + set Message(count,0) 0 + for {set i 1} {$i < $msgCnt} {incr i} { + $T item create + while 1 { + set j [expr {int(rand() * $i)}] + if {$j == 0} break + if {[$T depth $j] == 5} continue + if {$Message(count,$Message(thread,$j)) == 15} continue + break + } + $T item lastchild $j $i + + set Message(read,$i) [expr rand() * 2 > 1] + if {$j == 0} { + set Message(thread,$i) [incr thread] + set Message(seconds,$i) [expr {[clock seconds] - int(rand() * 500000)}] + set Message(seconds2,$i) $Message(seconds,$i) + set Message(count,$thread) 1 + } else { + set Message(thread,$i) $Message(thread,$j) + set Message(seconds,$i) [expr {$Message(seconds2,$j) + int(rand() * 10000)}] + set Message(seconds2,$i) $Message(seconds,$i) + set Message(seconds2,$j) $Message(seconds,$i) + incr Message(count,$Message(thread,$j)) + } + } + + for {set i 1} {$i < $msgCnt} {incr i} { + set subject "This is thread number $Message(thread,$i)" + set from somebody@somewhere.net + set sent [clock format $Message(seconds,$i) -format "%d/%m/%y %I:%M %p"] + set size [expr {1 + int(rand() * 10)}]KB + if {$Message(read,$i)} { + set style read + set style2 read + } else { + set style unread + set style2 unread + } + $T item style set $i 3 $style 4 $style2.we 5 $style2.we 6 $style2.w + $T item text $i 3 $subject 4 $from 5 $sent 6 $size + if {[$T item numchildren $i]} { + $T item hasbutton $i yes + } + } + + $T notify bind $T { + if {[%T selection count] == 1} { + set I [lindex [%T selection get] 0] + if {!$Message(read,$I)} { + if {[%T item isopen $I] || ![AnyUnreadDescendants %T $I]} { + # unread ->read + %T item style map $I subject read {text.unread text.read} + %T item style map $I from read.we {text.unread text.read} + %T item style map $I sent read.we {text.unread text.read} + %T item style map $I size read.w {text.unread text.read} + } else { + # unread -> read2 + %T item style map $I subject read2 {text.unread text.unread} + } + set Message(read,$I) 1 + DisplayStylesInItem $I + } + } + } + + $T notify bind $T { + if {$Message(read,%I) && [AnyUnreadDescendants %T %I]} { + # read2 -> read + %T item style map %I subject read {text.unread text.read} + # unread -> read + %T item style map %I from read.we {text.unread text.read} + %T item style map %I sent read.we {text.unread text.read} + %T item style map %I size read.w {text.unread text.read} + } + } + + $T notify bind $T { + if {$Message(read,%I) && [AnyUnreadDescendants %T %I]} { + # read -> read2 + %T item style map %I subject read2 {text.read text.unread} + # read -> unread + %T item style map %I from unread.we {text.read text.unread} + %T item style map %I sent unread.we {text.read text.unread} + %T item style map %I size unread.w {text.read text.unread} + } + } + + for {set i 1} {$i < $msgCnt} {incr i} { + if {rand() * 2 > 1} { + if {[$T item numchildren $i]} { + $T collapse $i + } + } + } + + return +} +proc AnyUnreadDescendants {T I} { + + global Message + + set itemList [$T item firstchild $I] + while {[llength $itemList]} { + # Pop + set item [lindex $itemList end] + set itemList [lrange $itemList 0 end-1] + + if {!$Message(read,$item)} { + return 1 + } + + set item2 [$T item nextsibling $item] + if {$item2 ne ""} { + # Push + lappend itemList $item2 + } + set item2 [$T item firstchild $item] + if {$item2 ne ""} { + # Push + lappend itemList $item2 + } + } + + return 0 +} diff --git a/demos/random.tcl b/demos/random.tcl new file mode 100644 index 0000000..be9e9ba --- /dev/null +++ b/demos/random.tcl @@ -0,0 +1,475 @@ +set RandomN 500 + +# +# Demo: random N items +# +proc DemoRandom {} { + + set T .f2.f1.t + + InitPics folder-* small-* + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -itemheight $height -selectmode extended \ + -showroot yes -showrootbutton yes -showbuttons yes -showlines yes \ + -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50" + + $T column configure 0 -expand yes -text Item -itembackground {#e0e8f0 {}} + $T column configure 1 -text Parent -justify center -itembackground {gray90 {}} + $T column configure 2 -text Depth -justify center -itembackground {linen {}} + + $T element create e1 image -image {folder-open {open} folder-closed {}} + $T element create e2 image -image small-file + $T element create e3 text \ + -fill [list $::SystemHighlightText {selected focus}] + $T element create e4 text -fill blue + $T element create e6 text + $T element create e5 rect -showfocus yes \ + -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] + + $T style create s1 + $T style elements s1 {e5 e1 e3 e4} + $T style layout s1 e1 -pade 4 -expand ns + $T style layout s1 e3 -pade 4 -expand ns + $T style layout s1 e4 -pade 6 -expand ns + $T style layout s1 e5 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + $T style create s2 + $T style elements s2 {e5 e2 e3} + $T style layout s2 e2 -pade 4 -expand ns + $T style layout s2 e3 -pade 6 -expand ns + $T style layout s2 e5 -union [list e3] -iexpand ns -ipadw 2 -ipade 2 + + $T style create s3 + $T style elements s3 {e6} + $T style layout s3 e6 -padw 6 -pade 6 -expand ns + + set ::TreeCtrl::Priv(sensitive,$T) {e5 e1 e2 e3} + set ::TreeCtrl::Priv(dragimage,$T) { + {0 s1 e1 e3} + {0 s2 e2 e3} + } + + set clicks [clock clicks] + for {set i 1} {$i < $::RandomN} {incr i} { + $T item create + while 1 { + set j [expr {int(rand() * $i)}] + if {[$T depth $j] < 5} break + } + if {rand() * 2 > 1} { + $T collapse $i + } + if {rand() * 2 > 1} { + $T item lastchild $j $i + } else { + $T item firstchild $j $i + } + } + puts "created $::RandomN-1 items in [expr [clock clicks] - $clicks] clicks" + set clicks [clock clicks] + for {set i 0} {$i < $::RandomN} {incr i} { + set numChildren [$T item numchildren $i] + if {$numChildren} { + $T item hasbutton $i yes + $T item style set $i 0 s1 1 s3 2 s3 + $T item complex $i \ + [list [list e3 -text "Item $i"] [list e4 -text "($numChildren)"]] \ + [list [list e6 -text "[$T item parent $i]"]] \ + [list [list e6 -text "[$T depth $i]"]] + } else { + $T item style set $i 1 s3 2 s3 0 s2 + $T item complex $i \ + [list [list e3 -text "Item $i"]] \ + [list [list e6 -text "[$T item parent $i]"]] \ + [list [list e6 -text "[$T depth $i]"]] + } + } + puts "configured $::RandomN items in [expr [clock clicks] - $clicks] clicks" + + bind TreeCtrlRandom { + TreeCtrl::DoubleButton1 %W %x %y + } + bind TreeCtrlRandom { + set TreeCtrl::Priv(selectMode) toggle + TreeCtrl::RandomButton1 %W %x %y + break + } + bind TreeCtrlRandom { + set TreeCtrl::Priv(selectMode) add + TreeCtrl::RandomButton1 %W %x %y + break + } + bind TreeCtrlRandom { + set TreeCtrl::Priv(selectMode) set + TreeCtrl::RandomButton1 %W %x %y + break + } + bind TreeCtrlRandom { + TreeCtrl::RandomMotion1 %W %x %y + break + } + bind TreeCtrlRandom { + TreeCtrl::RandomLeave1 %W %x %y + break + } + bind TreeCtrlRandom { + TreeCtrl::RandomRelease1 %W %x %y + break + } + + bindtags $T [list $T TreeCtrlRandom TreeCtrl [winfo toplevel $T] all] + + return +} + +proc TreeCtrl::RandomButton1 {T x y} { + variable Priv + focus $T + set id [$T identify $x $y] + puts $id + set Priv(buttonMode) "" + + # Click outside any item + if {$id eq ""} { + $T selection clear + + # Click in header + } elseif {[lindex $id 0] eq "header"} { + ButtonPress1 $T $x $y + + # Click in item + } else { + foreach {where item arg1 arg2 arg3 arg4} $id {} + switch $arg1 { + button { + $T toggle $item + } + line { + $T toggle $arg2 + } + column { + set ok 0 + # Clicked an element + if {[llength $id] == 6} { + set E [lindex $id 5] + if {[lsearch -exact $Priv(sensitive,$T) $E] != -1} { + set ok 1 + } + } + if {!$ok} { + $T selection clear + return + } + + set Priv(drag,motion) 0 + set Priv(drag,x) [$T canvasx $x] + set Priv(drag,y) [$T canvasy $y] + set Priv(drop) "" + + if {$Priv(selectMode) eq "add"} { + BeginExtend $T $item + } elseif {$Priv(selectMode) eq "toggle"} { + BeginToggle $T $item + } elseif {![$T selection includes $item]} { + BeginSelect $T $item + } + $T activate $item + + if {[$T selection includes $item]} { + set Priv(buttonMode) drag + } + } + } + } + return +} +proc TreeCtrl::RandomMotion1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $T $x $y + } + "drag" { + RandomAutoScanCheck $T $x $y + RandomMotion $T $x $y + } + } + return +} +proc TreeCtrl::RandomMotion {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $T $x $y + } + "drag" { + # Detect initial mouse movement + if {!$Priv(drag,motion)} { + set Priv(selection) [$T selection get] + set Priv(drop) "" + $T dragimage clear + # For each selected item, add 2nd and 3rd elements of + # column zero to the dragimage + foreach I $Priv(selection) { + foreach list $Priv(dragimage,$T) { + set C [lindex $list 0] + set S [lindex $list 1] + if {[$T item style set $I $C] eq $S} { + eval $T dragimage add $I $C [lrange $list 2 end] + } + } + } + set Priv(drag,motion) 1 + } + + # Find the item under the cursor + set cursor X_cursor + set drop "" + set id [$T identify $x $y] + set ok 0 + if {($id ne "") && ([lindex $id 0] eq "item") && ([llength $id] == 6)} { + set E [lindex $id 5] + if {[lsearch -exact $Priv(sensitive,$T) $E] != -1} { + set ok 1 + } + } + if {$ok} { + set item [lindex $id 1] + set column [lindex $id 3] + if {$column == 0} { + # If the item is not in the pre-drag selection + # (i.e. not being dragged) see if we can drop on it + if {[lsearch -exact $Priv(selection) $item] == -1} { + set drop $item + # We can drop if dragged item isn't an ancestor + foreach item2 $Priv(selection) { + if {[$T item isancestor $item2 $item]} { + set drop "" + break + } + } + if {$drop ne ""} { + scan [$T item bbox $drop] "%d %d %d %d" x1 y1 x2 y2 + if {$y < $y1 + 3} { + set cursor top_side + set Priv(drop,pos) prevsibling + } elseif {$y >= $y2 - 3} { + set cursor bottom_side + set Priv(drop,pos) nextsibling + } else { + set cursor "" + set Priv(drop,pos) lastchild + } + } + } + } + } + + if {[$T cget -cursor] ne $cursor} { + $T configure -cursor $cursor + } + + # Select the item under the cursor (if any) and deselect + # the previous drop-item (if any) + $T selection modify $drop $Priv(drop) + set Priv(drop) $drop + + # Show the dragimage in its new position + set x [expr {[$T canvasx $x] - $Priv(drag,x)}] + set y [expr {[$T canvasy $y] - $Priv(drag,y)}] + $T dragimage offset $x $y + $T dragimage visible yes + } + } + return +} +proc TreeCtrl::RandomLeave1 {T x y} { + variable Priv + # This is called when I do ButtonPress-1 on Unix for some reason, + # and buttonMode is undefined. + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + "header" { + Leave1 $T $x $y + } + } + return +} +proc TreeCtrl::RandomRelease1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Release1 $T $x $y + } + "drag" { + AutoScanCancel $T + $T dragimage visible no + $T selection modify {} $Priv(drop) + $T configure -cursor "" + if {$Priv(drop) ne ""} { + RandomDrop $T $Priv(drop) $Priv(selection) $Priv(drop,pos) + } + } + } + set Priv(buttonMode) "" + return +} + +proc RandomDrop {T target source pos} { + set parentList {} + switch -- $pos { + lastchild { set parent $target } + prevsibling { set parent [$T item parent $target] } + nextsibling { set parent [$T item parent $target] } + } + foreach item $source { + + # Ignore any item whose ancestor is also selected + set ignore 0 + foreach ancestor [$T item ancestors $item] { + if {[lsearch -exact $source $ancestor] != -1} { + set ignore 1 + break + } + } + if {$ignore} continue + + # Update the old parent of this moved item later + if {[lsearch -exact $parentList $item] == -1} { + lappend parentList [$T item parent $item] + } + + # Add to target + $T item $pos $target $item + + # Update text: parent + $T item element configure $item 1 e6 -text $parent + + # Update text: depth + $T item element configure $item 2 e6 -text [$T depth $item] + + # Recursively update text: depth + set itemList [$T item firstchild $item] + while {[llength $itemList]} { + # Pop + set item [lindex $itemList end] + set itemList [lrange $itemList 0 end-1] + + $T item element configure $item 2 e6 -text [$T depth $item] + + set item2 [$T item nextsibling $item] + if {$item2 ne ""} { + # Push + lappend itemList $item2 + } + set item2 [$T item firstchild $item] + if {$item2 ne ""} { + # Push + lappend itemList $item2 + } + } + } + + # Update items that lost some children + foreach item $parentList { + set numChildren [$T item numchildren $item] + if {$numChildren == 0} { + $T item hasbutton $item no + $T item style map $item 0 s2 {e3 e3} + } else { + $T item element configure $item 0 e4 -text "($numChildren)" + } + } + + # Update the target that gained some children + if {[$T item style set $parent 0] ne "s1"} { + $T item hasbutton $parent yes + $T item style map $parent 0 s1 {e3 e3} + } + set numChildren [$T item numchildren $parent] + $T item element configure $parent 0 e4 -text "($numChildren)" + return +} + +# Same as TreeCtrl::AutoScanCheck, but calls RandomMotion and +# RandomAutoScanCheckAux +proc TreeCtrl::RandomAutoScanCheck {T x y} { + variable Priv + scan [$T contentbox] "%d %d %d %d" x1 y1 x2 y2 + set margin [winfo pixels $T [$T cget -scrollmargin]] + if {($x < $x1 + $margin) || ($x >= $x2 - $margin) || + ($y < $y1 + $margin) || ($y >= $y2 - $margin)} { + if {![info exists Priv(autoscan,afterId,$T)]} { + if {$y >= $y2 - $margin} { + $T yview scroll 1 units + set delay [$T cget -yscrolldelay] + } elseif {$y < $y1 + $margin} { + $T yview scroll -1 units + set delay [$T cget -yscrolldelay] + } elseif {$x >= $x2 - $margin} { + $T xview scroll 1 units + set delay [$T cget -xscrolldelay] + } elseif {$x < $x1 + $margin} { + $T xview scroll -1 units + set delay [$T cget -xscrolldelay] + } + set count [scan $delay "%d %d" d1 d2] + if {[info exists Priv(autoscan,scanning,$T)]} { + if {$count == 2} { + set delay $d2 + } + } else { + if {$count == 2} { + set delay $d1 + } + set Priv(autoscan,scanning,$T) 1 + } + switch $Priv(buttonMode) { + "drag" - + "marquee" { + RandomMotion $T $x $y + } + } + set Priv(autoscan,afterId,$T) [after $delay [list TreeCtrl::RandomAutoScanCheckAux $T]] + } + return + } + AutoScanCancel $T + return +} + +proc ::TreeCtrl::RandomAutoScanCheckAux {T} { + variable Priv + unset Priv(autoscan,afterId,$T) + set x [winfo pointerx $T] + set y [winfo pointery $T] + set x [expr {$x - [winfo rootx $T]}] + set y [expr {$y - [winfo rooty $T]}] + RandomAutoScanCheck $T $x $y + return +} + +# +# Demo: random N items, button images +# +proc DemoRandom2 {} { + + set T .f2.f1.t + + DemoRandom + + InitPics mac-* + + $T configure -openbuttonimage mac-collapse -closedbuttonimage mac-expand \ + -showlines no + + return +} + diff --git a/demos/www-options.tcl b/demos/www-options.tcl new file mode 100644 index 0000000..d141f65 --- /dev/null +++ b/demos/www-options.tcl @@ -0,0 +1,299 @@ +proc DemoInternetOptions {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode browse + + InitPics internet-* + + $T column configure 0 -text "Internet Options" + + $T state define check + $T state define radio + $T state define on + + $T element create e1 image -image { + internet-check-on {check on} + internet-check-off {check} + internet-radio-on {radio on} + internet-radio-off {radio} + } + $T element create e2 text -fill [list $::SystemHighlightText {selected focus}] + $T element create e3 rect -fill [list $::SystemHighlight {selected focus}] -showfocus yes + + set S [$T style create s1] + $T style elements $S {e3 e1 e2} + $T style layout $S e1 -pade 4 -expand ns + $T style layout $S e2 -expand ns + $T style layout $S e3 -union [list e2] -iexpand ns -ipadw 2 -ipade 2 + + set parentList [list root {} {} {} {} {} {}] + set parent root + foreach {depth setting text option group} { + 0 print "Printing" "" "" + 1 off "Print background colors and images" "o1" "" + 0 search "Search from Address bar" "" "" + 1 search "When searching" "" "" + 2 off "Display results, and go to the most likely sites" "o2" "r1" + 2 off "Do not search from the Address bar" "o3" "r1" + 2 off "Just display the results in the main window" "o4" "r1" + 2 on "Just go to the most likely site" "o5" "r1" + 0 security "Security" "" "" + 1 on "Check for publisher's certificate revocation" "o5" "" + 1 off "Check for server certificate revocation (requires restart)" "o6" "" + } { + set item [$T item create] + $T item style set $item 0 s1 + $T item element configure $item 0 e2 -text $text + set ::Option(option,$item) $option + set ::Option(group,$item) $group + if {($setting eq "on") || ($setting eq "off")} { + set ::Option(setting,$item) $setting + if {$group eq ""} { + $T item state $item check + if {$setting eq "on"} { + $T item state $item on + } + } else { + if {$setting eq "on"} { + set ::Option(current,$group) $item + $T item state $item on + } + $T item state $item radio + } + } else { + $T item element configure $item 0 e1 -image internet-$setting + } + $T item lastchild [lindex $parentList $depth] $item + incr depth + set parentList [lreplace $parentList $depth $depth $item] + } + + bind TreeCtrlOption { + TreeCtrl::DoubleButton1 %W %x %y + } + bind TreeCtrlOption { + TreeCtrl::OptionButton1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionMotion1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionLeave1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionRelease1 %W %x %y + break + } + + bindtags $T [list $T TreeCtrlOption TreeCtrl [winfo toplevel $T] all] + + return +} +proc TreeCtrl::OptionButton1 {T x y} { + variable Priv + focus $T + set id [$T identify $x $y] + if {[lindex $id 0] eq "header"} { + ButtonPress1 $T $x $y + } elseif {$id eq ""} { + set Priv(buttonMode) "" + } else { + set Priv(buttonMode) "" + set item [lindex $id 1] + $T selection modify $item all + $T activate $item + if {$::Option(option,$item) eq ""} return + set group $::Option(group,$item) + # a checkbutton + if {$group eq ""} { + if {$::Option(setting,$item) eq "on"} { + set setting off + $T item state $item !on + } else { + set setting on + $T item state $item on + } + set ::Option(setting,$item) $setting + # a radiobutton + } else { + set current $::Option(current,$group) + if {$current eq $item} return + $T item state $current !on + $T item state $item on + set ::Option(setting,$item) on + set ::Option(current,$group) $item + } + } + return +} + + +# Alternate implementation that doesn't rely on run-time styles +proc DemoInternetOptions_2 {} { + + set T .f2.f1.t + + set height [font metrics [$T cget -font] -linespace] + if {$height < 18} { + set height 18 + } + $T configure -showroot no -showbuttons no -showlines no -itemheight $height \ + -selectmode browse + + InitPics internet-* + + $T column configure 0 -text "Internet Options" + + $T element create e1 image + $T element create e2 text -fill [list $::SystemHighlightText {selected focus}] + $T element create e3 rect -fill [list $::SystemHighlight {selected focus}] -showfocus yes + + set S [$T style create s1] + $T style elements $S {e3 e1 e2} + $T style layout $S e1 -pade 4 -expand ns + $T style layout $S e2 -expand ns + $T style layout $S e3 -union [list e2] -iexpand ns -ipadw 2 -ipade 2 + + set parentList [list root {} {} {} {} {} {}] + set parent root + foreach {depth setting text option group} { + 0 print "Printing" "" "" + 1 off "Print background colors and images" "o1" "" + 0 search "Search from Address bar" "" "" + 1 search "When searching" "" "" + 2 off "Display results, and go to the most likely sites" "o2" "r1" + 2 off "Do not search from the Address bar" "o3" "r1" + 2 off "Just display the results in the main window" "o4" "r1" + 2 on "Just go to the most likely site" "o5" "r1" + 0 security "Security" "" "" + 1 on "Check for publisher's certificate revocation" "o5" "" + 1 off "Check for server certificate revocation (requires restart)" "o6" "" + } { + set item [$T item create] + $T item style set $item 0 s1 + $T item element configure $item 0 e2 -text $text + set ::Option(option,$item) $option + set ::Option(group,$item) $group + if {$setting eq "on" || $setting eq "off"} { + set ::Option(setting,$item) $setting + if {$group eq ""} { + set img internet-check-$setting + $T item element configure $item 0 e1 -image $img + } else { + if {$setting eq "on"} { + set ::Option(current,$group) $item + } + set img internet-radio-$setting + $T item element configure $item 0 e1 -image $img + } + } else { + $T item element configure $item 0 e1 -image internet-$setting + } + $T item lastchild [lindex $parentList $depth] $item + incr depth + set parentList [lreplace $parentList $depth $depth $item] + } + + bind TreeCtrlOption { + TreeCtrl::DoubleButton1 %W %x %y + } + bind TreeCtrlOption { + TreeCtrl::OptionButton1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionMotion1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionLeave1 %W %x %y + break + } + bind TreeCtrlOption { + TreeCtrl::OptionRelease1 %W %x %y + break + } + + bindtags $T [list $T TreeCtrlOption TreeCtrl [winfo toplevel $T] all] + + return +} +proc TreeCtrl::OptionButton1_2 {T x y} { + variable Priv + focus $T + set id [$T identify $x $y] + if {[lindex $id 0] eq "header"} { + ButtonPress1 $T $x $y + } elseif {$id eq ""} { + set Priv(buttonMode) "" + } else { + set Priv(buttonMode) "" + set item [lindex $id 1] + $T selection modify $item all + $T activate $item + if {$::Option(option,$item) eq ""} return + set group $::Option(group,$item) + # a checkbutton + if {$group eq ""} { + if {$::Option(setting,$item) eq "on"} { + set setting off + } else { + set setting on + } + $T item element configure $item 0 e1 -image internet-check-$setting + set ::Option(setting,$item) $setting + # a radiobutton + } else { + set current $::Option(current,$group) + if {$current eq $item} return + $T item element configure $current 0 e1 -image internet-radio-off + $T item element configure $item 0 e1 -image internet-radio-on + set ::Option(setting,$item) on + set ::Option(current,$group) $item + } + } + return +} +proc TreeCtrl::OptionMotion1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $T $x $y + } + } + return +} +proc TreeCtrl::OptionLeave1 {T x y} { + variable Priv + # This is called when I do ButtonPress-1 on Unix for some reason, + # and buttonMode is undefined. + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + "header" { + $T column configure $Priv(column) -sunken no + } + } + return +} +proc TreeCtrl::OptionRelease1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Release1 $T $x $y + } + } + set Priv(buttonMode) "" + return +} + diff --git a/doc/manual.txt b/doc/manual.txt new file mode 100644 index 0000000..1c546cc --- /dev/null +++ b/doc/manual.txt @@ -0,0 +1,415 @@ +C -- column +E -- element +I -- item +S -- style +T -- treectrl + +========== + +T activate I + +T canvasx $x + +T canvasy $y + +T cget $option + +T collapse I ... + +T column XXX + T column bbox C + + T column cget C $option + + T column configure C + T column configure C $option + T column configure C $option $value ... + -arrow + -arrowside + -arrowgravity + -arrowpadw + -arrowpade + -bitmap + -border + -borderwidth + -button + -expand + -font + -image + -imagepade + -imagepadw + -itembackground + -justify + -minwidth + -relief + -stepwidth + -sunken + -tag + -text + -textcolor + -textpade + -textpadw + -width + -visible + -widthhack + + T column delete C + + T column width C + +T compare I $op I + $op "<", "<=", "==", ">=", ">", "!=" + +T configure +T configure $option +T configure $option $value ... + -background + -backgroundmode + -borderwidth + -buttoncolor + -buttonsize + -buttonthickness + -closedbuttonbitmap + -closedbuttonimage + -columnproxy + -cursor + -doublebuffer + -font + -foreground + -height + -highlightbackground + -highlightcolor + -highlightthickness + -indent + -itemheight + -linecolor + -linestyle + -linethickness + -openbuttonbitmap + -openbuttonimage + -orient + -relief + -scrollmargin + -selectmode + -showbuttons + -showheader + -showlines + -showroot + -showrootbutton + -takefocus + -treecolumn + -width + -wrap + -xscrollcommand + -xscrolldelay + -xscrollincrement + -yscrollcommand + -yscrolldelay + -yscrollincrement + +T contentbox + +T debug XXX + T debug cget $option + + T debug configure + T debug configure $option + T debug configure $option $value ... + -displaydelay + -data + -display + -enable + -erasecolor + + T debug dinfo + + T debug scroll + +T depth +T depth I + +T dragimage XXX + T dragimage add I + T dragimage add I C + T dragimage add I C E ... + + T dragimage cget $option + + T dragimage clear + + T dragimage configure + T dragimage configure $option + T dragimage configure $option $value ... + + T dragimage offset + T dragimage offset $x $y + + T dragimage visible + T dragimage visible $boolean + +T element XXX + T element cget E $option + + T element configure E + T element configure E $option + T element configure E $option $value ... + + --- bitmap --- + -background + -bitmap + -foreground + + --- border --- + -background + -filled + -height + -relief + -thickness + -width + + --- image --- + -height + -image + -width + + --- rect --- + -fill + -height + -open + -outline + -outlinewidth + -showfocus + -width + + --- text --- + -data + -datatype + -format + -fill + -font + -justify + -lines + -text + -width + -wrap + + T element create $name $type $option $value ... + + T element delete E ... + + T element names + + T element type E + +T expand I ... + +T identify $x $y + +T index I + +T item XXX + T item ancestors I + + T item bbox I + + T item children I + + T item complex I {{E $option $value ...} ...} {{E $option $value ...} ...} + + T item create + + T item delete I + T item delete I I + + T item dump I + + T item element XXX + T item element actual I C E $option + + T item element cget I C E $option + + T item element configure I C E + T item element configure I C E $option + T item element configure I C E $option $value ... + + T item hasbutton I + T item hasbutton I $boolean + + T item firstchild I + T item firstchild I I + + T item lastchild I + T item lastchild I I + + T item nextsibling I + T item nextsibling I I + + T item numchildren I + + T item index I + + T item isancestor I I + + T item parent I + + T item prevsibling I + T item prevsibling I I + + T item remove I + + T item rnc I + + T item sort I $option $value ... + + T item state I + T item state I $state ... + + T item style XXX + T item style elements I C + + T item style map I C S {E E ...} + + T item style set I + T item style set I C + T item style set I C S ... + + T item text I C + T item text I C $text + +T marquee XXX + T marquee anchor + T marquee anchor $x $y + + T marquee cget $option + + T marquee configure + T marquee configure $option + T marquee configure $option $value ... + + T marquee coords + T marquee coords $x $y $x $y + + T marquee corner + T marquee corner $x $y + + T marquee identify + + T marquee visible + T marquee visible $boolean + +T notify XXX + T notify bind + T notify bind $object + T notify bind $object $pattern + T notify bind $object $pattern $script + + T notify configure $object $pattern $option $value ... + -active + + T notify detailnames $eventName + + T notify eventnames + + T notify generate $pattern -$char $value ... + + T notify install event $eventName + + T notify install detail $eventName $detailName + T notify install detail $eventName $detailName $percentsCmd + + T notify linkage $eventName + T notify linkage $eventName $detailName + + T notify uninstall event $eventName + + T notify uninstall detail $eventName $detailName + +T numcolumns + +T numitems + +T orphans + +T range I I + +T see I + +T selection XXX + T selection add I + T selection add I I + + T selection anchor + T selection anchor I + + T selection clear + T selection clear I + T selection clear I I + + T selection count + + T selection get + + T selection includes I + + T selection modify $select $deselect + +T state XXX + T state define $stateName + + T state undefine $stateName + + T state names + +T style XXX + T style cget S $option + + T style configure S + T style configure S $option + T style configure S $option $value ... + -orient + + T style create $name $option $value ... + + T style delete S ... + + T style elements S + T style elements S {E ...} + + T style layout S E + T style layout S E $option $value ... + -padw + -padn + -pade + -pads + -ipadw + -ipadn + -ipade + -ipads + -expand + -iexpand + -detach + -squeeze + -union + + T style names + +T toggle I ... + +T xview XXX + T xview + + T xview moveto $fraction + + T xview scroll $number pages + + T xview scroll $number units + +T yview XXX + T yview + + T yview moveto $fraction + + T yview scroll $number pages + + T yview scroll $number units + diff --git a/generic/qebind.c b/generic/qebind.c new file mode 100644 index 0000000..dbfba8c --- /dev/null +++ b/generic/qebind.c @@ -0,0 +1,1992 @@ +/* File: qebind.c */ + +/* Purpose: implements quasi-events */ + +/* + * A general purpose module that allows a program to send event-like + * messages to scripts, and to bind Tcl commands to those quasi-events. + * Each event has it's own detail field and other fields, and this + * module performs %-substitution on bound scripts just like regular + * Tk binding model. + * + * To use it first call QE_BindInit() to initialize the package. + * Then call QE_InstallEvent() for each new event you wish to define. + * For events with details, call QE_InstallDetail() to register each + * detail associated with a specific event type. Then create a + * binding table, which records all binding commands defined by your + * scripts, with QE_CreateBindingTable(). QE_BindCmd() is + * called to associate a Tcl script with a given event for a particular + * object. The objects that commands are bound to can be a Tk widget or any + * string, just like the usual "bind" command. Bindings are not automatically + * deleted when a widget is destroyed. + */ + +#include +#include +#include +#include +#include "qebind.h" +#include "dbwin.h" + +int debug_bindings = 0; + +/* + * Allow bindings to be deactivated. + */ +#define BIND_ACTIVE 1 + +#define ALLOW_INSTALL 1 + +typedef struct BindValue { + int type; /* Type of event, etc) */ + int detail; /* Misc. other information, or 0 for none */ + ClientData object; + char *command; + int specific; /* For less-specific events (detail=0), this is 1 + * if a more-specific event (detail>0) exists. */ + struct BindValue *nextValue; /* list of BindValues matching event */ +#if BIND_ACTIVE + int active; /* 1 if binding is "active", 0 otherwise */ +#endif /* BIND_ACTIVE */ +} BindValue; + +typedef struct Pattern { + int type; /* Type of event */ + int detail; /* Misc. other information, or 0 for none */ +} Pattern; + +typedef struct PatternTableKey { + int type; /* Type of event */ + int detail; /* Misc. other information, or 0 for none */ +} PatternTableKey; + +typedef struct ObjectTableKey { + int type; /* Type of event */ + int detail; /* Misc. other information, or 0 for none */ + ClientData object; /* Object info */ +} ObjectTableKey; + +typedef struct Detail { + Tk_Uid name; /* Name of detail */ + int code; /* Detail code */ + struct EventInfo *event; /* Associated event */ + QE_ExpandProc expandProc; /* Callback to expand % in scripts */ +#if ALLOW_INSTALL + int dynamic; + char *command; /* Tcl command to expand percents, or NULL */ +#endif + struct Detail *next; /* List of Details for event */ +} Detail; + +typedef struct EventInfo { + char *name; /* Name of event */ + int type; /* Type of event */ + QE_ExpandProc expandProc; /* Callback to expand % in scripts */ + Detail *detailList; /* List of Details */ + int nextDetailId; /* Next unique Detail.code */ +#if ALLOW_INSTALL + int dynamic; /* Created by QE_InstallCmd() */ +#endif + struct EventInfo *next; /* List of all EventInfos */ +} EventInfo; + +typedef struct BindingTable { + Tcl_Interp *interp; + Tcl_HashTable patternTable; /* Key: PatternTableKey, Value: (BindValue *) */ + Tcl_HashTable objectTable; /* Key: ObjectTableKey, Value: (BindValue *) */ + Tcl_HashTable eventTableByName; /* Key: string, Value: EventInfo */ + Tcl_HashTable eventTableByType; /* Key: int, Value: EventInfo */ + Tcl_HashTable detailTableByType; /* Key: PatternTableKey, Value: Detail */ + EventInfo *eventList; /* List of all EventInfos */ + int nextEventId; /* Next unique EventInfo.type */ +} BindingTable; + +static void ExpandPercents(BindingTable *bindPtr, ClientData object, char *command, + QE_Event *eventPtr, QE_ExpandProc expandProc, Tcl_DString *result); +static int ParseEventDescription(BindingTable *bindPtr, char *eventPattern, + Pattern *patPtr, EventInfo **eventInfoPtr, Detail **detailPtr); +static int FindSequence(BindingTable *bindPtr, ClientData object, + char *eventString, int create, int *created, BindValue **result); +#if ALLOW_INSTALL +static void Percents_Install(QE_ExpandArgs *args); +#endif +static int DeleteBinding(BindingTable *bindPtr, BindValue *valuePtr); +static EventInfo *FindEvent(BindingTable *bindPtr, int eventType); + +static int initialized = 0; + +int QE_BindInit(Tcl_Interp *interp) +{ + if (initialized) + return TCL_OK; + + initialized = 1; + + return TCL_OK; +} + +int QE_InstallEvent(QE_BindingTable bindingTable, char *name, QE_ExpandProc expandProc) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + EventInfo *eiPtr; + int isNew; + int type; + + hPtr = Tcl_CreateHashEntry(&bindPtr->eventTableByName, name, &isNew); + if (!isNew) + { + Tcl_AppendResult(bindPtr->interp, "event \"", + name, "\" already exists", NULL); + return 0; + } + + type = bindPtr->nextEventId++; + + eiPtr = (EventInfo *) Tcl_Alloc(sizeof(EventInfo)); + eiPtr->name = Tcl_Alloc(strlen(name) + 1); + strcpy(eiPtr->name, name); + eiPtr->type = type; + eiPtr->expandProc = expandProc; + eiPtr->detailList = NULL; + eiPtr->nextDetailId = 1; +#ifdef ALLOW_INSTALL + eiPtr->dynamic = 0; +#endif + + Tcl_SetHashValue(hPtr, (ClientData) eiPtr); + + hPtr = Tcl_CreateHashEntry(&bindPtr->eventTableByType, (char *) type, &isNew); + Tcl_SetHashValue(hPtr, (ClientData) eiPtr); + + /* List of EventInfos */ + eiPtr->next = bindPtr->eventList; + bindPtr->eventList = eiPtr; + + return type; +} + +int QE_InstallDetail(QE_BindingTable bindingTable, char *name, int eventType, QE_ExpandProc expandProc) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + Detail *dPtr; + EventInfo *eiPtr; + PatternTableKey key; + int isNew; + int code; + + /* Find the event this detail goes with */ + eiPtr = FindEvent(bindPtr, eventType); + if (eiPtr == NULL) + return 0; + + /* Verify the detail is not already defined for this event */ + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dPtr->next) + { + if (strcmp(dPtr->name, name) == 0) + { + Tcl_AppendResult(bindPtr->interp, + "detail \"", name, "\" already exists for event \"", + eiPtr->name, "\"", NULL); + return 0; + } + } + + code = eiPtr->nextDetailId++; + + /* New Detail for detailTable */ + dPtr = (Detail *) Tcl_Alloc(sizeof(Detail)); + dPtr->name = Tk_GetUid(name); + dPtr->code = code; + dPtr->event = eiPtr; + dPtr->expandProc = expandProc; +#if ALLOW_INSTALL + dPtr->dynamic = 0; + dPtr->command = NULL; +#endif + + /* Entry to find detail by event type and detail code */ + key.type = eventType; + key.detail = code; + hPtr = Tcl_CreateHashEntry(&bindPtr->detailTableByType, (char *) &key, &isNew); + Tcl_SetHashValue(hPtr, (ClientData) dPtr); + + /* List of Details */ + dPtr->next = eiPtr->detailList; + eiPtr->detailList = dPtr; + + return code; +} + +static void DeleteEvent(BindingTable *bindPtr, EventInfo *eiPtr) +{ + EventInfo *eiPrev; + Detail *dPtr, *dNext; + + /* Free Details */ + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dNext) + { + dNext = dPtr->next; +#ifdef ALLOW_INSTALL + if (dPtr->command != NULL) + Tcl_Free(dPtr->command); +#endif + memset((char *) dPtr, 0xAA, sizeof(Detail)); + Tcl_Free((char *) dPtr); + } + + if (bindPtr->eventList == eiPtr) + bindPtr->eventList = eiPtr->next; + else + { + for (eiPrev = bindPtr->eventList; + eiPrev->next != eiPtr; + eiPrev = eiPrev->next) + { + } + eiPrev->next = eiPtr->next; + } + + /* Free EventInfo */ + Tcl_Free(eiPtr->name); + memset((char *) eiPtr, 0xAA, sizeof(EventInfo)); + Tcl_Free((char *) eiPtr); +} + +int QE_UninstallEvent(QE_BindingTable bindingTable, int eventType) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + EventInfo *eiPtr; + BindValue *valuePtr, **valueList; + Tcl_DString dString; + int i, count = 0; + + /* Find the event */ + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByType, (char *) eventType); + if (hPtr == NULL) + return TCL_ERROR; + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); + + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eiPtr->name); + Tcl_DeleteHashEntry(hPtr); + + Tcl_DStringInit(&dString); + + /* Find all bindings to this event for any object */ + hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); + while (hPtr != NULL) + { + valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + while (valuePtr != NULL) + { + if (valuePtr->type == eiPtr->type) + { + Tcl_DStringAppend(&dString, (char *) &valuePtr, sizeof(valuePtr)); + count++; + } + valuePtr = valuePtr->nextValue; + } + hPtr = Tcl_NextHashEntry(&search); + } + + valueList = (BindValue **) Tcl_DStringValue(&dString); + for (i = 0; i < count; i++) + DeleteBinding(bindPtr, valueList[i]); + + Tcl_DStringFree(&dString); + + DeleteEvent(bindPtr, eiPtr); + + return TCL_OK; +} + +int QE_UninstallDetail(QE_BindingTable bindingTable, int eventType, int detail) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + PatternTableKey key; + Tcl_HashEntry *hPtr; + Detail *dPtr = NULL, *dPrev; + EventInfo *eiPtr; + + /* Find the event */ + eiPtr = FindEvent(bindPtr, eventType); + if (eiPtr == NULL) + return TCL_ERROR; + + if (eiPtr->detailList == NULL) + return TCL_ERROR; + + /* Delete all bindings on this event/detail for all objects */ + while (1) + { + key.type = eventType; + key.detail = detail; + hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); + if (hPtr == NULL) + break; + DeleteBinding(bindPtr, (BindValue *) Tcl_GetHashValue(hPtr)); + } + + if (eiPtr->detailList->code == detail) + { + dPtr = eiPtr->detailList; + eiPtr->detailList = eiPtr->detailList->next; + } + else + { + for (dPrev = eiPtr->detailList; + dPrev != NULL; + dPrev = dPrev->next) + { + if ((dPrev->next != NULL) && (dPrev->next->code == detail)) + { + dPtr = dPrev->next; + dPrev->next = dPtr->next; + break; + } + } + if (dPtr == NULL) + return TCL_ERROR; + } + +#ifdef ALLOW_INSTALL + if (dPtr->command != NULL) + Tcl_Free(dPtr->command); +#endif + memset((char *) dPtr, 0xAA, sizeof(Detail)); + Tcl_Free((char *) dPtr); + + key.type = eventType; + key.detail = detail; + hPtr = Tcl_FindHashEntry(&bindPtr->detailTableByType, (char *) &key); + Tcl_DeleteHashEntry(hPtr); + + return TCL_OK; +} + +static EventInfo *FindEvent(BindingTable *bindPtr, int eventType) +{ + Tcl_HashEntry *hPtr; + + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByType, (char *) eventType); + if (hPtr == NULL) return NULL; + return (EventInfo *) Tcl_GetHashValue(hPtr); +} + +static Detail *FindDetail(BindingTable *bindPtr, int eventType, int code) +{ + PatternTableKey key; + Tcl_HashEntry *hPtr; + + key.type = eventType; + key.detail = code; + hPtr = Tcl_FindHashEntry(&bindPtr->detailTableByType, (char *) &key); + if (hPtr == NULL) return NULL; + return (Detail *) Tcl_GetHashValue(hPtr); +} + +QE_BindingTable QE_CreateBindingTable(Tcl_Interp *interp) +{ + BindingTable *bindPtr; + + bindPtr = (BindingTable *) Tcl_Alloc(sizeof(BindingTable)); + bindPtr->interp = interp; + Tcl_InitHashTable(&bindPtr->patternTable, + sizeof(PatternTableKey) / sizeof(int)); + Tcl_InitHashTable(&bindPtr->objectTable, + sizeof(ObjectTableKey) / sizeof(int)); + Tcl_InitHashTable(&bindPtr->eventTableByName, TCL_STRING_KEYS); + Tcl_InitHashTable(&bindPtr->eventTableByType, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&bindPtr->detailTableByType, + sizeof(PatternTableKey) / sizeof(int)); + bindPtr->nextEventId = 1; + bindPtr->eventList = NULL; + + return (QE_BindingTable) bindPtr; +} + +void QE_DeleteBindingTable(QE_BindingTable bindingTable) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + EventInfo *eiPtr, *eiNext; + Detail *dPtr, *dNext; + + hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); + while (hPtr != NULL) + { + BindValue *valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + while (valuePtr != NULL) + { + BindValue *nextValue = valuePtr->nextValue; + Tcl_Free((char *) valuePtr->command); + memset((char *) valuePtr, 0xAA, sizeof(BindValue)); + Tcl_Free((char *) valuePtr); + valuePtr = nextValue; + } + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(&bindPtr->patternTable); + Tcl_DeleteHashTable(&bindPtr->objectTable); + + for (eiPtr = bindPtr->eventList; + eiPtr != NULL; + eiPtr = eiNext) + { + eiNext = eiPtr->next; + + /* Free Detail */ + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dNext) + { + dNext = dPtr->next; +#ifdef ALLOW_INSTALL + if (dPtr->command != NULL) + Tcl_Free(dPtr->command); +#endif + memset((char *) dPtr, 0xAA, sizeof(Detail)); + Tcl_Free((char *) dPtr); + } + + /* Free EventInfo */ + Tcl_Free(eiPtr->name); + memset((char *) eiPtr, 0xAA, sizeof(EventInfo)); + Tcl_Free((char *) eiPtr); + } + + Tcl_DeleteHashTable(&bindPtr->eventTableByName); + Tcl_DeleteHashTable(&bindPtr->eventTableByType); + Tcl_DeleteHashTable(&bindPtr->detailTableByType); + + memset((char *) bindPtr, 0xAA, sizeof(BindingTable)); + Tcl_Free((char *) bindPtr); +} + +int QE_CreateBinding(QE_BindingTable bindingTable, ClientData object, + char *eventString, char *command, int append) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + BindValue *valuePtr; + int isNew, length; + char *cmdOld, *cmdNew; + + if (FindSequence(bindPtr, object, eventString, 1, &isNew, &valuePtr) != TCL_OK) + return TCL_ERROR; + + /* created a new objectTable entry */ + if (isNew) + { + Tcl_HashEntry *hPtr; + PatternTableKey key; + + key.type = valuePtr->type; + key.detail = valuePtr->detail; + hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, + &isNew); + + /* + * A patternTable entry exists for each different type/detail. + * The entry points to a BindValue which is the head of the list + * of BindValue's with this same type/detail, but for different + * objects. + */ + if (!isNew) + { + valuePtr->nextValue = (BindValue *) Tcl_GetHashValue(hPtr); + } + Tcl_SetHashValue(hPtr, (ClientData) valuePtr); + } + + cmdOld = valuePtr->command; + + /* Append given command to any existing command */ + if (append && cmdOld) + { + length = strlen(cmdOld) + strlen(command) + 2; + cmdNew = Tcl_Alloc((unsigned) length); + (void) sprintf(cmdNew, "%s\n%s", cmdOld, command); + } + /* Copy the given command */ + else + { + cmdNew = (char *) Tcl_Alloc((unsigned) strlen(command) + 1); + (void) strcpy(cmdNew, command); + } + + /* Free the old command, if any */ + if (cmdOld) Tcl_Free(cmdOld); + + /* Save command associated with this binding */ + valuePtr->command = cmdNew; + + return TCL_OK; +} + +int QE_DeleteBinding(QE_BindingTable bindingTable, ClientData object, + char *eventString) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + BindValue *valuePtr; + + if (FindSequence(bindPtr, object, eventString, 0, NULL, &valuePtr) != TCL_OK) + return TCL_ERROR; + if (valuePtr == NULL) + { + Tcl_ResetResult(bindPtr->interp); + return TCL_OK; + } + DeleteBinding(bindPtr, valuePtr); + return TCL_OK; +} + +static int DeleteBinding(BindingTable *bindPtr, BindValue *valuePtr) +{ + Tcl_HashEntry *hPtr; + BindValue *listPtr; + ObjectTableKey keyObj; + PatternTableKey keyPat; + + /* Delete the objectTable entry */ + keyObj.type = valuePtr->type; + keyObj.detail = valuePtr->detail; + keyObj.object = valuePtr->object; + hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) &keyObj); + if (hPtr == NULL) return TCL_ERROR; /* fatal error */ + Tcl_DeleteHashEntry(hPtr); + + /* Find the patternTable entry for this type/detail */ + keyPat.type = valuePtr->type; + keyPat.detail = valuePtr->detail; + hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &keyPat); + if (hPtr == NULL) return TCL_ERROR; /* fatal error */ + + /* + * Get the patternTable value. This is the head of a list of + * BindValue's that match the type/detail, but for different + * objects; + */ + listPtr = (BindValue *) Tcl_GetHashValue(hPtr); + + /* The deleted BindValue is the first */ + if (listPtr == valuePtr) + { + /* The deleted BindValue was the only one in the list */ + if (valuePtr->nextValue == NULL) + { + if (debug_bindings) + dbwin("QE_DeleteBinding: Deleted pattern type=%d detail=%d\n", + valuePtr->type, valuePtr->detail); + + Tcl_DeleteHashEntry(hPtr); + } + /* The next BindValue is the new head of the list */ + else + { + Tcl_SetHashValue(hPtr, valuePtr->nextValue); + } + } + /* Look for the deleted BindValue in the list, and remove it */ + else + { + while (1) + { + if (listPtr->nextValue == NULL) return TCL_ERROR; /* fatal */ + if (listPtr->nextValue == valuePtr) + { + if (debug_bindings) + dbwin("QE_DeleteBinding: Unlinked binding type=%d detail=%d\n", + valuePtr->type, valuePtr->detail); + + listPtr->nextValue = valuePtr->nextValue; + break; + } + listPtr = listPtr->nextValue; + } + } + + Tcl_Free((char *) valuePtr->command); + memset((char *) valuePtr, 0xAA, sizeof(BindValue)); + Tcl_Free((char *) valuePtr); + + return TCL_OK; +} + +int QE_GetAllObjects(QE_BindingTable bindingTable) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tcl_DString dString; + ClientData *objectList; + int i, count = 0; + Tcl_Obj *listObj; + + Tcl_DStringInit(&dString); + hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); + while (hPtr != NULL) + { + BindValue *valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + while (valuePtr != NULL) + { + objectList = (ClientData *) Tcl_DStringValue(&dString); + for (i = 0; i < count; i++) + { + if (objectList[i] == valuePtr->object) + break; + } + if (i >= count) + { + Tcl_DStringAppend(&dString, (char *) &valuePtr->object, + sizeof(ClientData)); + count++; + } + valuePtr = valuePtr->nextValue; + } + hPtr = Tcl_NextHashEntry(&search); + } + if (count > 0) + { + listObj = Tcl_NewListObj(0, NULL); + objectList = (ClientData *) Tcl_DStringValue(&dString); + for (i = 0; i < count; i++) + { + Tcl_ListObjAppendElement(bindPtr->interp, listObj, + Tcl_NewStringObj((char *) objectList[i], -1)); + } + Tcl_SetObjResult(bindPtr->interp, listObj); + } + Tcl_DStringFree(&dString); + + return TCL_OK; +} + +int QE_GetBinding(QE_BindingTable bindingTable, ClientData object, + char *eventString) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + BindValue *valuePtr; + + if (FindSequence(bindPtr, object, eventString, 0, NULL, &valuePtr) != TCL_OK) + return TCL_ERROR; + if (valuePtr == NULL) + return TCL_OK; + Tcl_SetObjResult(bindPtr->interp, Tcl_NewStringObj(valuePtr->command, -1)); + return TCL_OK; +} + +static void GetPatternString(BindingTable *bindPtr, BindValue *bindValue, Tcl_DString *dString) +{ + EventInfo *eiPtr; + + eiPtr = FindEvent(bindPtr, bindValue->type); + if (eiPtr != NULL) + { + Tcl_DStringAppend(dString, "<", 1); + Tcl_DStringAppend(dString, eiPtr->name, -1); + if (bindValue->detail) + { + Detail *detail = FindDetail(bindPtr, bindValue->type, bindValue->detail); + if (detail != NULL) + { + Tcl_DStringAppend(dString, "-", 1); + Tcl_DStringAppend(dString, detail->name, -1); + } + } + Tcl_DStringAppend(dString, ">", 1); + } +} + +int QE_GetAllBindings(QE_BindingTable bindingTable, ClientData object) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tcl_DString dString; + + Tcl_DStringInit(&dString); + hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search); + while (hPtr != NULL) + { + BindValue *valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + while (valuePtr != NULL) + { + if (valuePtr->object == object) + { + Tcl_DStringSetLength(&dString, 0); + GetPatternString(bindPtr, valuePtr, &dString); + Tcl_AppendElement(bindPtr->interp, Tcl_DStringValue(&dString)); + } + valuePtr = valuePtr->nextValue; + } + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_DStringFree(&dString); + + return TCL_OK; +} + +int QE_GetEventNames(QE_BindingTable bindingTable) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + EventInfo *eiPtr; + + for (eiPtr = bindPtr->eventList; + eiPtr != NULL; + eiPtr = eiPtr->next) + { + Tcl_AppendElement(bindPtr->interp, eiPtr->name); + } + + return TCL_OK; +} + +int QE_GetDetailNames(QE_BindingTable bindingTable, char *eventName) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_HashEntry *hPtr; + EventInfo *eiPtr; + Detail *dPtr; + + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, "unknown event \"", eventName, + "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dPtr->next) + { + Tcl_AppendElement(bindPtr->interp, dPtr->name); + } + + return TCL_OK; +} + +static void ExpandPercents(BindingTable *bindPtr, ClientData object, + char *command, QE_Event *eventPtr, QE_ExpandProc expandProc, + Tcl_DString *result) +{ + char *string; + QE_ExpandArgs expandArgs; + +#if 0 + Tcl_DStringSetLength(result, 0); + if (debug_bindings) + dbwin("ExpandPercents on '%s' name=%s type=%d detail=%d expand=%lu\n", + object, eiPtr->name, eiPtr->type, eventPtr->detail, eiPtr->expand); +#endif + expandArgs.bindingTable = (QE_BindingTable) bindPtr; + expandArgs.object = object; + expandArgs.event = eventPtr->type; + expandArgs.detail = eventPtr->detail; + expandArgs.result = result; + expandArgs.clientData = eventPtr->clientData; + + while (1) + { + for (string = command; (*string != 0) && (*string != '%'); string++) + { + /* Empty loop body. */ + } + if (string != command) + { + Tcl_DStringAppend(result, command, string - command); + command = string; + } + if (*command == 0) + { + break; + } + + /* Expand % here */ + expandArgs.which = command[1]; + (*expandProc)(&expandArgs); + + command += 2; + } +} + +#if 1 + +static void BindEvent(BindingTable *bindPtr, QE_Event *eventPtr, int wantDetail, + EventInfo *eiPtr, Detail *dPtr) +{ + Tcl_HashEntry *hPtr; + BindValue *valuePtr; + ObjectTableKey keyObj; + PatternTableKey key; + Tcl_DString scripts, savedResult; + int code; + char *p, *end; + + /* Find the first BindValue for this event */ + key.type = eventPtr->type; + key.detail = wantDetail ? eventPtr->detail : 0; + hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); + if (hPtr == NULL) + return; + + /* Collect all scripts, with % expanded, separated by null characters. + * Do it this way because anything could happen while evaluating, including + * uninstalling events/details, even the interpreter being deleted. */ + Tcl_DStringInit(&scripts); + + for (valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + valuePtr; valuePtr = valuePtr->nextValue) + { + if (wantDetail && valuePtr->detail) + { + keyObj.type = key.type; + keyObj.detail = 0; + keyObj.object = valuePtr->object; + hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) &keyObj); + if (hPtr != NULL) + { + BindValue *value2Ptr; + value2Ptr = (BindValue *) Tcl_GetHashValue(hPtr); + value2Ptr->specific = 1; + } + } + + /* + * If a binding for a more-specific event exists for this object + * and event-type, and this is a binding for a less-specific + * event, then skip this binding, since the binding for the + * more-specific event was already invoked. + */ + else if (!wantDetail && valuePtr->specific) + { + if (debug_bindings) + dbwin("QE_BindEvent: Skipping less-specific event type=%d object='%s'\n", + valuePtr->type, valuePtr->object); + + valuePtr->specific = 0; + continue; + } + +#if BIND_ACTIVE + /* This binding isn't active */ + if (valuePtr->active == 0) + continue; +#endif /* BIND_ACTIVE */ + +#if ALLOW_INSTALL + /* + * Call a Tcl script to expand the percents. + */ + if (dPtr && (dPtr->command != NULL)) + { + ClientData oldClientData = eventPtr->clientData; + + eventPtr->clientData = (ClientData) dPtr; + ExpandPercents(bindPtr, valuePtr->object, valuePtr->command, + eventPtr, Percents_Install, &scripts); + eventPtr->clientData = oldClientData; + } + else +#endif /* ALLOW_INSTALL */ + { + QE_ExpandProc expandProc = + ((dPtr != NULL) && (dPtr->expandProc != NULL)) ? + dPtr->expandProc : eiPtr->expandProc; + + ExpandPercents(bindPtr, valuePtr->object, valuePtr->command, + eventPtr, expandProc, &scripts); + } + + /* Separate each script by '\0' */ + Tcl_DStringAppend(&scripts, "", 1); + } + + /* Nothing to do. No need to call Tcl_DStringFree(&scripts) */ + if (Tcl_DStringLength(&scripts) == 0) + return; + + /* + * As in Tk bindings, we expect that bindings may be invoked + * in the middle of Tcl commands. So we preserve the current + * interpreter result and restore it later. + */ + Tcl_DStringInit(&savedResult); + Tcl_DStringGetResult(bindPtr->interp, &savedResult); + + p = Tcl_DStringValue(&scripts); + end = p + Tcl_DStringLength(&scripts); + + while (p < end) + { + code = Tcl_GlobalEval(bindPtr->interp, p); + p += strlen(p); + p++; + + if (code != TCL_OK) + { +#if 0 + if (code == TCL_CONTINUE) + { + /* Nothing */ + } + else if (code == TCL_BREAK) + { + break; + } + else +#endif + { + Tcl_AddErrorInfo(bindPtr->interp, + "\n (command bound to quasi-event)"); + Tcl_BackgroundError(bindPtr->interp); + break; + } + } + } + + Tcl_DStringFree(&scripts); + + /* Restore the interpreter result */ + Tcl_DStringResult(bindPtr->interp, &savedResult); +} + +#else /* not 1 */ + +static void BindEvent(BindingTable *bindPtr, QE_Event *eventPtr, int wantDetail, + EventInfo *eiPtr, Detail *dPtr) +{ + Tcl_HashEntry *hPtr; + BindValue *valuePtr; + ObjectTableKey keyObj; + PatternTableKey key; + Tcl_DString command, savedResult; + int code; + + /* Find the first BindValue for this event */ + key.type = eventPtr->type; + key.detail = wantDetail ? eventPtr->detail : 0; + hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); + if (hPtr == NULL) return; + + Tcl_DStringInit(&command); + + /* + * As in Tk bindings, we expect that bindings may be invoked + * in the middle of Tcl commands. So we preserve the current + * interpreter result and restore it later. + */ + Tcl_DStringInit(&savedResult); + Tcl_DStringGetResult(bindPtr->interp, &savedResult); + + for (valuePtr = (BindValue *) Tcl_GetHashValue(hPtr); + valuePtr; valuePtr = valuePtr->nextValue) + { + if (wantDetail && valuePtr->detail) + { + keyObj.type = key.type; + keyObj.detail = 0; + keyObj.object = valuePtr->object; + hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) &keyObj); + if (hPtr != NULL) + { + BindValue *value2Ptr; + value2Ptr = (BindValue *) Tcl_GetHashValue(hPtr); + value2Ptr->specific = 1; + } + } + + /* + * If a binding for a more-specific event exists for this object + * and event-type, and this is a binding for a less-specific + * event, then skip this binding, since the binding for the + * more-specific event was already invoked. + */ + else if (!wantDetail && valuePtr->specific) + { + if (debug_bindings) + dbwin("QE_BindEvent: Skipping less-specific event type=%d object='%s'\n", + valuePtr->type, valuePtr->object); + + valuePtr->specific = 0; + continue; + } + +#if BIND_ACTIVE + /* This binding isn't active */ + if (valuePtr->active == 0) continue; +#endif /* BIND_ACTIVE */ + +#if ALLOW_INSTALL + /* + * Call a Tcl script to expand the percents. + */ + if (dPtr && (dPtr->command != NULL)) + { + ClientData oldClientData = eventPtr->clientData; + + eventPtr->clientData = (ClientData) dPtr; + ExpandPercents(bindPtr, valuePtr->object, valuePtr->command, + eventPtr, Percents_Install, &command); + eventPtr->clientData = oldClientData; + } + else +#endif /* ALLOW_INSTALL */ + { + QE_ExpandProc expandProc = + ((dPtr != NULL) && (dPtr->expandProc != NULL)) ? + dPtr->expandProc : eiPtr->expandProc; + + ExpandPercents(bindPtr, valuePtr->object, valuePtr->command, + eventPtr, expandProc, &command); + } + code = Tcl_EvalEx(bindPtr->interp, Tcl_DStringValue(&command), + Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); + + if (code != TCL_OK) + { +#if 0 + if (code == TCL_CONTINUE) + { + /* Nothing */ + } + else if (code == TCL_BREAK) + { + break; + } + else +#endif + { + Tcl_AddErrorInfo(bindPtr->interp, + "\n (command bound to quasi-event)"); + Tcl_BackgroundError(bindPtr->interp); + break; + } + } + } + + /* Restore the interpreter result */ + Tcl_DStringResult(bindPtr->interp, &savedResult); + + Tcl_DStringFree(&command); +} + +#endif /* 0 */ + +int QE_BindEvent(QE_BindingTable bindingTable, QE_Event *eventPtr) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Detail *dPtr = NULL; + EventInfo *eiPtr; + + /* Find the event */ + eiPtr = FindEvent(bindPtr, eventPtr->type); + if (eiPtr == NULL) + return TCL_OK; + + /* Find the detail */ + if (eventPtr->detail) + { + dPtr = FindDetail(bindPtr, eventPtr->type, eventPtr->detail); + if (dPtr == NULL) + return TCL_OK; + } + + BindEvent(bindPtr, eventPtr, 1, eiPtr, dPtr); + if (eventPtr->detail) + BindEvent(bindPtr, eventPtr, 0, eiPtr, dPtr); + + return TCL_OK; +} + +/* + * 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)) + +static char *GetField(char *p, char *copy, int size) +{ + int ch = *p; + + while ((ch != '\0') && !isspace(ch) && (ch != '>') + && (ch != '-') && (size > 1)) + { + *copy = ch; + p++; + copy++; + size--; + ch = *p; + } + *copy = '\0'; + + while ((*p == '-') || isspace(UCHAR(*p))) + { + p++; + } + return p; +} + +#define FIELD_SIZE 48 + +static int ParseEventDescription(BindingTable *bindPtr, char *eventString, + Pattern *patPtr, EventInfo **eventInfoPtr, Detail **detailPtr) +{ + Tcl_Interp *interp = bindPtr->interp; + Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); + char *p; + Tcl_HashEntry *hPtr; + char field[FIELD_SIZE]; + EventInfo *eiPtr; + Detail *dPtr; + + if (eventInfoPtr) *eventInfoPtr = NULL; + if (detailPtr) *detailPtr = NULL; + + p = eventString; + + patPtr->type = -1; + patPtr->detail = 0; + + /* First char must by opening < */ + if (*p != '<') + { + Tcl_AppendResult(interp, "missing \"<\" in event pattern \"", + eventString, "\"", (char *) NULL); + return TCL_ERROR; + } + p++; + + /* Event name (required)*/ + p = GetField(p, field, FIELD_SIZE); + + if (debug_bindings) + dbwin("GetField='%s'\n", field); + + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, field); + if (hPtr == NULL) + { + Tcl_AppendStringsToObj(resultPtr, "unknown event \"", + field, "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + patPtr->type = eiPtr->type; + if (eventInfoPtr) *eventInfoPtr = eiPtr; + + /* Terminating > */ + if (*p == '>') + return TCL_OK; + + /* Detail name (optional) */ + p = GetField(p, field, FIELD_SIZE); + + if (debug_bindings) + dbwin("GetField='%s'\n", field); + + if (*field != '\0') + { + /* Find detail for the matching event */ + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dPtr->next) + { + if (strcmp(dPtr->name, field) == 0) + break; + } + if (dPtr == NULL) + { + Tcl_AppendStringsToObj(resultPtr, "unknown detail \"", + field, "\" for event \"", eiPtr->name, "\"", NULL); + return TCL_ERROR; + } + patPtr->detail = dPtr->code; + if (detailPtr) *detailPtr = dPtr; + } + + /* Terminating > */ + if (*p != '>') + { + Tcl_AppendResult(interp, "missing \">\" in event pattern \"", + eventString, "\"", (char *) NULL); + return TCL_ERROR; + } + + return TCL_OK; +} + +static int FindSequence(BindingTable *bindPtr, ClientData object, + char *eventString, int create, int *created, BindValue **result) +{ + Tcl_HashEntry *hPtr; + Pattern pats; + ObjectTableKey key; + BindValue *valuePtr; + int isNew; + + if (debug_bindings) + dbwin("FindSequence object='%s' pattern='%s'...\n", object, + eventString); + + if (created) (*created) = 0; + + /* Event description -> Pattern */ + if (ParseEventDescription(bindPtr, eventString, &pats, NULL, NULL) != TCL_OK) + return TCL_ERROR; + + /* type + detail + object -> BindValue */ + key.type = pats.type; + key.detail = pats.detail; + key.object = object; + if (create) + { + hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) &key, &isNew); + + if (isNew) + { + if (debug_bindings) + dbwin("New BindValue for '%s' type=%d detail=%d\n", object, + pats.type, pats.detail); + + valuePtr = (BindValue *) Tcl_Alloc(sizeof(BindValue)); + valuePtr->type = pats.type; + valuePtr->detail = pats.detail; + valuePtr->object = object; + valuePtr->command = NULL; + valuePtr->specific = 0; + valuePtr->nextValue = NULL; +#if BIND_ACTIVE + /* This binding is active */ + valuePtr->active = 1; +#endif /* BIND_ACTIVE */ + Tcl_SetHashValue(hPtr, (ClientData) valuePtr); + } + + if (created) (*created) = isNew; + (*result) = (BindValue *) Tcl_GetHashValue(hPtr); + return TCL_OK; + } + + /* Look for existing objectTable entry */ + hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) &key); + if (hPtr == NULL) + { + (*result) = NULL; + return TCL_OK; + } + (*result) = (BindValue *) Tcl_GetHashValue(hPtr); + return TCL_OK; +} + +void QE_ExpandDouble(double number, Tcl_DString *result) +{ + char numStorage[TCL_DOUBLE_SPACE]; + + Tcl_PrintDouble((Tcl_Interp *) NULL, number, numStorage); + Tcl_DStringAppend(result, numStorage, -1); +/* QE_ExpandString(numStorage, result); */ +} + +void QE_ExpandNumber(long number, Tcl_DString *result) +{ + char numStorage[TCL_INTEGER_SPACE]; + + /* TclFormatInt() */ + (void) sprintf(numStorage, "%ld", number); + Tcl_DStringAppend(result, numStorage, -1); +/* QE_ExpandString(numStorage, result); */ +} + +void QE_ExpandString(char *string, Tcl_DString *result) +{ + int length, spaceNeeded, cvtFlags; + + spaceNeeded = Tcl_ScanElement(string, &cvtFlags); + length = Tcl_DStringLength(result); + Tcl_DStringSetLength(result, length + spaceNeeded); + spaceNeeded = Tcl_ConvertElement(string, + Tcl_DStringValue(result) + length, + cvtFlags | TCL_DONT_USE_BRACES); + Tcl_DStringSetLength(result, length + spaceNeeded); +} + +void QE_ExpandUnknown(char which, Tcl_DString *result) +{ + char string[2]; + + (void) sprintf(string, "%c", which); + QE_ExpandString(string, result); +} + +void QE_ExpandEvent(QE_BindingTable bindingTable, int eventType, Tcl_DString *result) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + EventInfo *eiPtr = FindEvent(bindPtr, eventType); + + if (eiPtr != NULL) + QE_ExpandString((char *) eiPtr->name, result); + else + QE_ExpandString("unknown", result); +} + +void QE_ExpandDetail(QE_BindingTable bindingTable, int event, int detail, Tcl_DString *result) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Detail *detailPtr = FindDetail(bindPtr, event, detail); + + if (detailPtr != NULL) + QE_ExpandString((char *) detailPtr->name, result); + else + QE_ExpandString("unknown", result); +} + +int QE_BindCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tk_Window tkwin = Tk_MainWindow(bindPtr->interp); + ClientData object; + char *string; + + if ((objc - objOffset < 1) || (objc - objOffset > 4)) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset, objv, + "?object? ?pattern? ?script?"); + return TCL_ERROR; + } + + if (objc - objOffset == 1) + { + QE_GetAllObjects(bindingTable); + return TCL_OK; + } + + string = Tcl_GetString(objv[objOffset + 1]); + + if (string[0] == '.') + { + Tk_Window tkwin2; + tkwin2 = Tk_NameToWindow(bindPtr->interp, string, tkwin); + if (tkwin2 == NULL) + { + return TCL_ERROR; + } + object = (ClientData) Tk_PathName(tkwin2); + } + else + { + object = (ClientData) Tk_GetUid(string); + } + + if (objc - objOffset == 4) + { + int append = 0; + char *sequence = Tcl_GetString(objv[objOffset + 2]); + char *script = Tcl_GetString(objv[objOffset + 3]); + + if (script[0] == 0) + { + return QE_DeleteBinding(bindingTable, object, sequence); + } + if (script[0] == '+') + { + script++; + append = 1; + } + return QE_CreateBinding(bindingTable, object, sequence, script, + append); + } + else if (objc - objOffset == 3) + { + char *sequence = Tcl_GetString(objv[objOffset + 2]); + + return QE_GetBinding(bindingTable, object, sequence); + } + else + { + QE_GetAllBindings(bindingTable, object); + } + + return TCL_OK; +} + +/* + * qegenerate -- Generate events from scripts. + * Usage: qegenerate pattern ?field value ...? + * Desciption: Scripts can generate "fake" quasi-events by providing + * a quasi-event pattern and option field/value pairs. + */ + +typedef struct GenerateField { + char which; + char *string; +} GenerateField; + +static GenerateField generateField[10]; +static int generateCount; + +/* Perform %-substitution using args passed to QE_GenerateCmd() */ +static void Percents_Generate(QE_ExpandArgs *args) +{ + int i; + + for (i = 0; i < generateCount; i++) + { + if (args->which == generateField[i].which) + { + QE_ExpandString(generateField[i].string, args->result); + return; + } + } + + switch (args->which) + { + case 'd': /* detail */ + QE_ExpandDetail(args->bindingTable, args->event, args->detail, + args->result); + break; + + case 'e': /* event */ + QE_ExpandEvent(args->bindingTable, args->event, args->result); + break; + + case 'W': /* object */ + QE_ExpandString((char *) args->object, args->result); + break; + + default: /* unknown */ + QE_ExpandUnknown(args->which, args->result); + break; + } +} + +int +QE_GenerateCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + QE_Event fakeEvent; + QE_ExpandProc oldExpandProc; + Tcl_Obj *CONST *objPtr; + EventInfo *eiPtr; + Detail *dPtr; + GenerateField *fieldPtr; + char *p, *t; + Pattern pats; + int result; + + if (objc - objOffset < 2) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset, objv, "pattern ?field value ...?"); + return TCL_ERROR; + } + + p = Tcl_GetStringFromObj(objv[objOffset + 1], NULL); + if (ParseEventDescription(bindPtr, p, &pats, &eiPtr, &dPtr) != TCL_OK) + return TCL_ERROR; + + /* Can't generate an event without a detail*/ + if ((dPtr == NULL) && (eiPtr->detailList != NULL)) + { + Tcl_AppendResult(bindPtr->interp, "cannot generate \"", p, "\": missing detail", + (char *) NULL); + return TCL_ERROR; + } + + fakeEvent.type = pats.type; + fakeEvent.detail = pats.detail; + fakeEvent.clientData = (ClientData) dPtr; + + objPtr = objv + objOffset + 2; + objc -= objOffset + 2; + + fieldPtr = &generateField[0]; + generateCount = 0; + + while (objc > 1) + { + int length; + t = Tcl_GetStringFromObj(objPtr[0], &length); + if ((length != 2) || (t[0] != '-')) + { + Tcl_AppendResult(bindPtr->interp, "invalid percent char \"", t, + "\"", NULL); + return TCL_ERROR; + } + fieldPtr->which = t[1]; + fieldPtr->string = Tcl_GetStringFromObj(objPtr[1], NULL); + fieldPtr++; + generateCount++; + objPtr += 2; + objc -= 2; + } + + if (objc != 0) + { + Tcl_WrongNumArgs(bindPtr->interp, 2, objv, "pattern ?field value ...?"); + return TCL_ERROR; + } + + /* + * XXX Hack -- Swap in our own %-substitution routine. Percents_Generate() + * uses the values the caller passed us. + */ + if ((dPtr != NULL) && (dPtr->expandProc != NULL)) + { + oldExpandProc = dPtr->expandProc; + dPtr->expandProc = Percents_Generate; + } + else + { + oldExpandProc = eiPtr->expandProc; + eiPtr->expandProc = Percents_Generate; + } + + result = QE_BindEvent(bindingTable, &fakeEvent); + + if ((dPtr != NULL) && (dPtr->expandProc != NULL)) + dPtr->expandProc = oldExpandProc; + else + eiPtr->expandProc = oldExpandProc; + + return result; +} + +#if BIND_ACTIVE + +/* qeconfigure $win -active no */ + +int +QE_ConfigureCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + Tcl_Interp *interp = bindPtr->interp; + Tk_Window tkwin = Tk_MainWindow(interp); + static CONST char *configSwitch[] = {"-active", NULL}; + Tcl_Obj *CONST *objPtr; + BindValue *valuePtr; + char *t, *eventString; + int index; + ClientData object; + + if (objc - objOffset < 3) + { + Tcl_WrongNumArgs(interp, objOffset, objv, "window pattern ?option? ?value? ?option value ...?"); + return TCL_ERROR; + } + + t = Tcl_GetStringFromObj(objv[objOffset + 1], NULL); + eventString = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + + if (t[0] == '.') + { + Tk_Window tkwin2; + tkwin2 = Tk_NameToWindow(interp, t, tkwin); + if (tkwin2 == NULL) + { + return TCL_ERROR; + } + object = (ClientData) Tk_PathName(tkwin2); + } + else + { + object = (ClientData) Tk_GetUid(t); + } + + if (FindSequence(bindPtr, object, eventString, 0, NULL, &valuePtr) != TCL_OK) + return TCL_ERROR; + + objPtr = objv + objOffset + 3; + objc -= objOffset + 3; + + if (objc == 0) + { + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-active", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewBooleanObj(valuePtr->active)); + Tcl_SetObjResult(interp, listObj); + return TCL_OK; + } + + while (objc > 1) + { + if (Tcl_GetIndexFromObj(interp, objPtr[0], configSwitch, + "option", 0, &index) != TCL_OK) + { + return TCL_ERROR; + } + switch (index) + { + case 0: /* -active */ + if (Tcl_GetBooleanFromObj(interp, objPtr[1], &valuePtr->active) + != TCL_OK) + { + return TCL_ERROR; + } + break; + } + objPtr += 2; + objc -= 2; + } + + return TCL_OK; +} + +#endif /* BIND_ACTIVE */ + +#if ALLOW_INSTALL + +#if 0 /* comment */ + +qeinstall detail show_icons 500 QEExpandCmd_Setting + +proc QEExpandCmd_Setting {char object event detail} { + + switch -- $char { + c { + return [Setting $detail] + } + d { + return $detail + } + W { + return $object + } + default { + return $char + } + } +} + +#endif /* comment */ + +/* Perform %-substitution by calling a Tcl command */ +static void Percents_Install(QE_ExpandArgs *args) +{ + BindingTable *bindPtr = (BindingTable *) args->bindingTable; + Tcl_Interp *interp = bindPtr->interp; + Detail *dPtr = (Detail *) args->clientData; + Tcl_DString command; + Tcl_SavedResult state; + + if (dPtr->command != NULL) + { + Tcl_DStringInit(&command); + Tcl_DStringAppend(&command, dPtr->command, -1); + Tcl_DStringAppend(&command, " ", 1); + Tcl_DStringAppend(&command, &args->which, 1); + Tcl_DStringAppend(&command, " ", 1); + Tcl_DStringAppend(&command, (char *) args->object, -1); + Tcl_DStringAppend(&command, " ", 1); + Tcl_DStringAppend(&command, dPtr->event->name, -1); + Tcl_DStringAppend(&command, " ", 1); + Tcl_DStringAppend(&command, dPtr->name, -1); + + Tcl_SaveResult(interp, &state); + if (Tcl_EvalEx(interp, Tcl_DStringValue(&command), + Tcl_DStringLength(&command), TCL_EVAL_GLOBAL) == TCL_OK) + { + QE_ExpandString(Tcl_GetStringFromObj(Tcl_GetObjResult(interp), + NULL), args->result); + } + else + { + Tcl_AddErrorInfo(interp, "\n (expanding percents)"); + Tcl_BackgroundError(interp); + } + Tcl_RestoreResult(interp, &state); + + Tcl_DStringFree(&command); + } +} + +int QE_InstallCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + static CONST char *commandOption[] = {"detail", "event", NULL}; + int index; + + if (objc - objOffset < 2) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 1, objv, "option arg ..."); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(bindPtr->interp, objv[objOffset + 1], + commandOption, "option", 0, &index) != TCL_OK) + { + return TCL_ERROR; + } + switch (index) + { + case 0: /* detail */ + { + char *eventName, *detailName, *command; + int id, length; + Detail *dPtr; + EventInfo *eiPtr; + Tcl_HashEntry *hPtr; + + if ((objc - objOffset < 4) || (objc - objOffset > 5)) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 2, objv, + "event detail ?percentsCommand?"); + return TCL_ERROR; + } + + /* Find the event type */ + eventName = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, "unknown event \"", + eventName, "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + /* Get the detail name */ + detailName = Tcl_GetStringFromObj(objv[objOffset + 3], NULL); + + /* Define the new detail */ + id = QE_InstallDetail(bindingTable, detailName, eiPtr->type, NULL); + if (id == 0) + return TCL_ERROR; + + /* Get the detail we just defined */ + dPtr = FindDetail(bindPtr, eiPtr->type, id); + if (dPtr == NULL) + return TCL_ERROR; + dPtr->dynamic = 1; + + if (objc - objOffset == 4) + break; + + /* Set the Tcl command for this detail */ + command = Tcl_GetStringFromObj(objv[objOffset + 4], &length); + if (length) + { + dPtr->command = Tcl_Alloc(length + 1); + (void) strcpy(dPtr->command, command); + } + break; + } + + case 1: /* event */ + { + char *eventName; + int id; + EventInfo *eiPtr; + Tcl_HashEntry *hPtr; + + if (objc - objOffset != 3) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 2, objv, + "name"); + return TCL_ERROR; + } + + eventName = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + + id = QE_InstallEvent(bindingTable, eventName, NULL); + if (id == 0) + return TCL_ERROR; + + /* Find the event we just installed */ + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + return TCL_ERROR; + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + /* Mark as installed-by-script */ + eiPtr->dynamic = 1; + break; + } + } + + return TCL_OK; +} + +int QE_UninstallCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + static CONST char *commandOption[] = {"detail", "event", NULL}; + int index; + + if (objc - objOffset < 2) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 1, objv, "option arg ..."); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(bindPtr->interp, objv[objOffset + 1], + commandOption, "option", 0, &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case 0: /* detail */ + { + char *eventName, *detailName; + Detail *dPtr; + EventInfo *eiPtr; + Tcl_HashEntry *hPtr; + + if (objc - objOffset != 4) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 2, objv, + "event detail"); + return TCL_ERROR; + } + + /* Find the event type */ + eventName = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, "unknown event \"", + eventName, "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + /* Get the detail name */ + detailName = Tcl_GetStringFromObj(objv[objOffset + 3], NULL); + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dPtr->next) + { + if (strcmp(dPtr->name, detailName) == 0) + break; + } + if (dPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, + "unknown detail \"", detailName, "\" for event \"", + eiPtr->name, "\"", NULL); + return TCL_ERROR; + } + + if (!dPtr->dynamic) + { + Tcl_AppendResult(bindPtr->interp, + "can't uninstall static detail \"", detailName, "\"", NULL); + return TCL_ERROR; + } + + return QE_UninstallDetail(bindingTable, eiPtr->type, dPtr->code); + } + + case 1: /* event */ + { + Tcl_HashEntry *hPtr; + EventInfo *eiPtr; + char *eventName; + + if (objc - objOffset != 3) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 2, objv, + "name"); + return TCL_ERROR; + } + + /* Find the event type */ + eventName = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, "unknown event \"", + eventName, "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + if (!eiPtr->dynamic) + { + Tcl_AppendResult(bindPtr->interp, + "can't uninstall static event \"", eventName, "\"", NULL); + return TCL_ERROR; + } + + return QE_UninstallEvent(bindingTable, eiPtr->type); + } + } + + return TCL_OK; +} + +int QE_LinkageCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]) +{ + BindingTable *bindPtr = (BindingTable *) bindingTable; + char *eventName, *detailName; + Detail *dPtr; + EventInfo *eiPtr; + Tcl_HashEntry *hPtr; + + if (objc - objOffset < 2 || objc - objOffset > 3) + { + Tcl_WrongNumArgs(bindPtr->interp, objOffset + 1, objv, "event ?detail?"); + return TCL_ERROR; + } + + /* Find the event type */ + eventName = Tcl_GetStringFromObj(objv[objOffset + 1], NULL); + hPtr = Tcl_FindHashEntry(&bindPtr->eventTableByName, eventName); + if (hPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, "unknown event \"", + eventName, "\"", NULL); + return TCL_ERROR; + } + eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr); + + if (objc - objOffset == 2) + { + Tcl_SetResult(bindPtr->interp, eiPtr->dynamic ? "dynamic" : "static", + TCL_STATIC); + return TCL_OK; + } + + /* Get the detail name */ + detailName = Tcl_GetStringFromObj(objv[objOffset + 2], NULL); + for (dPtr = eiPtr->detailList; + dPtr != NULL; + dPtr = dPtr->next) + { + if (strcmp(dPtr->name, detailName) == 0) + break; + } + if (dPtr == NULL) + { + Tcl_AppendResult(bindPtr->interp, + "unknown detail \"", detailName, "\" for event \"", + eiPtr->name, "\"", NULL); + return TCL_ERROR; + } + + Tcl_SetResult(bindPtr->interp, dPtr->dynamic ? "dynamic" : "static", + TCL_STATIC); + + return TCL_OK; +} + +#endif /* ALLOW_INSTALL */ + diff --git a/generic/qebind.h b/generic/qebind.h new file mode 100644 index 0000000..eb5799d --- /dev/null +++ b/generic/qebind.h @@ -0,0 +1,67 @@ +/* File: qebind.c */ + +/* Purpose: public interface to quasi-event package */ + +#ifndef INCLUDED_QEBIND_H +#define INCLUDED_QEBIND_H + +typedef struct QE_BindingTable_ *QE_BindingTable; + +/* Pass to QE_BindEvent */ +typedef struct QE_Event { + int type; + int detail; + ClientData clientData; +} QE_Event; + +typedef struct QE_ExpandArgs { + QE_BindingTable bindingTable; + char which; + ClientData object; + Tcl_DString *result; + int event; + int detail; + ClientData clientData; +} QE_ExpandArgs; + +typedef void (*QE_ExpandProc)(QE_ExpandArgs *args); +extern QE_BindingTable bindingTable; +extern int debug_bindings; + +extern int QE_BindInit(Tcl_Interp *interp); +extern QE_BindingTable QE_CreateBindingTable(Tcl_Interp *interp); +extern void QE_DeleteBindingTable(QE_BindingTable bindingTable); +extern int QE_InstallEvent(QE_BindingTable bindingTable, char *name, QE_ExpandProc expand); +extern int QE_InstallDetail(QE_BindingTable bindingTable, char *name, int eventType, QE_ExpandProc expand); +extern int QE_CreateBinding(QE_BindingTable bindingTable, + ClientData object, char *eventString, char *command, int append); +extern int QE_DeleteBinding(QE_BindingTable bindingTable, + ClientData object, char *eventString); +extern int QE_GetBinding(QE_BindingTable bindingTable, + ClientData object, char *eventString); +extern int QE_GetAllBindings(QE_BindingTable bindingTable, + ClientData object); +extern int QE_GetEventNames(QE_BindingTable bindingTable); +extern int QE_GetDetailNames(QE_BindingTable bindingTable, char *eventName); +extern int QE_BindEvent(QE_BindingTable bindingTable, QE_Event *eventPtr); +extern void QE_ExpandDouble(double number, Tcl_DString *result); +extern void QE_ExpandNumber(long number, Tcl_DString *result); +extern void QE_ExpandString(char *string, Tcl_DString *result); +extern void QE_ExpandEvent(QE_BindingTable bindingTable, int eventType, Tcl_DString *result); +extern void QE_ExpandDetail(QE_BindingTable bindingTable, int event, int detail, Tcl_DString *result); +extern void QE_ExpandUnknown(char which, Tcl_DString *result); +extern int QE_BindCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); +extern int QE_ConfigureCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); +extern int QE_GenerateCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); +extern int QE_InstallCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); +extern int QE_UninstallCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); +extern int QE_LinkageCmd(QE_BindingTable bindingTable, int objOffset, int objc, + Tcl_Obj *CONST objv[]); + +#endif /* INCLUDED_QEBIND_H */ + diff --git a/generic/tkTreeColumn.c b/generic/tkTreeColumn.c new file mode 100644 index 0000000..6d43e1f --- /dev/null +++ b/generic/tkTreeColumn.c @@ -0,0 +1,1547 @@ +#include "tkTreeCtrl.h" + +typedef struct Column Column; + +struct Column +{ + char *text; /* -text */ + int width; /* -width */ + Tcl_Obj *widthObj; /* -width */ + int minWidth; /* -minwidth */ + Tcl_Obj *minWidthObj; /* -minwidth */ + int stepWidth; /* -stepwidth */ + Tcl_Obj *stepWidthObj; /* -stepwidth */ + int widthHack; /* -widthhack */ + Tk_Font tkfont; /* -font */ + Tk_Justify justify; /* -justify */ + Tk_3DBorder border; /* -border */ + int borderWidth; /* -borderwidth */ + int relief; /* -relief */ + XColor *textColor; /* -textcolor */ + int expand; /* -expand */ + int visible; /* -visible */ + char *tag; /* -tag */ + char *imageString; /* -image */ + Pixmap bitmap; /* -bitmap */ + int sunken; /* -sunken */ + Tcl_Obj *itemBgObj; /* -itembackground */ + int button; /* -button */ + + TreeCtrl *tree; + int index; /* column number */ + int textLen; + int textWidth; + Tk_Image image; + int neededWidth; /* calculated from borders + text + arrow */ + int neededHeight; /* calculated from borders + text */ + int useWidth; /* -width, -minwidth, or required+expansion */ + int widthOfItems; /* width of all TreeItemColumns */ + int textPad[2]; /* padding for text */ + int imagePad[2]; /* padding for image */ + int itemBgCount; + XColor **itemBgColor; + GC bitmapGC; + +#define ARROW_NONE 0 +#define ARROW_UP 1 +#define ARROW_DOWN 2 + int arrow; + +#define SIDE_LEFT 0 +#define SIDE_RIGHT 1 + int arrowSide; + int arrowGravity; + + int arrowPad[2]; + Column *next; +}; + +static char *arrowST[] = { "none", "up", "down", (char *) NULL }; +static char *arrowSideST[] = { "left", "right", (char *) NULL }; + +#define COLU_CONF_IMAGE 0x0001 +#define COLU_CONF_NWIDTH 0x0002 /* neededWidth */ +#define COLU_CONF_NHEIGHT 0x0004 /* neededHeight */ +#define COLU_CONF_TWIDTH 0x0008 /* totalWidth */ +#define COLU_CONF_ITEMBG 0x0010 +#define COLU_CONF_DISPLAY 0x0040 +#define COLU_CONF_JUSTIFY 0x0080 +#define COLU_CONF_TAG 0x0100 +#define COLU_CONF_TEXT 0x0200 +#define COLU_CONF_BITMAP 0x0400 +#define COLU_CONF_RANGES 0x0800 + +static Tk_OptionSpec columnSpecs[] = { + {TK_OPTION_STRING_TABLE, "-arrow", (char *) NULL, (char *) NULL, + "none", -1, Tk_Offset(Column, arrow), + 0, (ClientData) arrowST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING_TABLE, "-arrowside", (char *) NULL, (char *) NULL, + "right", -1, Tk_Offset(Column, arrowSide), + 0, (ClientData) arrowSideST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING_TABLE, "-arrowgravity", (char *) NULL, (char *) NULL, + "left", -1, Tk_Offset(Column, arrowGravity), + 0, (ClientData) arrowSideST, COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-arrowpadw", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, arrowPad[SIDE_LEFT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-arrowpade", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, arrowPad[SIDE_RIGHT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(Column, bitmap), + TK_OPTION_NULL_OK, (ClientData) NULL, + COLU_CONF_BITMAP | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, + DEF_BUTTON_BG_COLOR, -1, Tk_Offset(Column, border), + 0, (ClientData) DEF_BUTTON_BG_MONO, COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-borderwidth", (char *) NULL, (char *) NULL, + "2", -1, Tk_Offset(Column, borderWidth), + 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_BOOLEAN, "-button", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(Column, button), + 0, (ClientData) NULL, 0}, + {TK_OPTION_BOOLEAN, "-expand", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(Column, expand), + 0, (ClientData) NULL, COLU_CONF_TWIDTH}, + {TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(Column, tkfont), + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(Column, imageString), + TK_OPTION_NULL_OK, (ClientData) NULL, + COLU_CONF_IMAGE | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-imagepade", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, imagePad[SIDE_RIGHT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-imagepadw", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, imagePad[SIDE_LEFT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING, "-itembackground", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, itemBgObj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_ITEMBG}, + {TK_OPTION_JUSTIFY, "-justify", (char *) NULL, (char *) NULL, + "left", -1, Tk_Offset(Column, justify), + 0, (ClientData) NULL, COLU_CONF_DISPLAY | COLU_CONF_JUSTIFY}, + {TK_OPTION_PIXELS, "-minwidth", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, minWidthObj), + Tk_Offset(Column, minWidth), + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, + {TK_OPTION_RELIEF, "-relief", (char *) NULL, (char *) NULL, + "raised", -1, Tk_Offset(Column, relief), + 0, (ClientData) NULL, COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-stepwidth", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, stepWidthObj), + Tk_Offset(Column, stepWidth), + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_RANGES}, + {TK_OPTION_BOOLEAN, "-sunken", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(Column, sunken), + 0, (ClientData) NULL, COLU_CONF_DISPLAY}, + {TK_OPTION_STRING, "-tag", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(Column, tag), + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TAG}, + {TK_OPTION_STRING, "-text", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(Column, text), + TK_OPTION_NULL_OK, (ClientData) NULL, + COLU_CONF_TEXT | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_COLOR, "-textcolor", (char *) NULL, (char *) NULL, + DEF_BUTTON_FG, -1, Tk_Offset(Column, textColor), + 0, (ClientData) NULL, COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-textpade", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, textPad[SIDE_RIGHT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-textpadw", (char *) NULL, (char *) NULL, + "6", -1, Tk_Offset(Column, textPad[SIDE_LEFT]), + 0, (ClientData) NULL, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, widthObj), Tk_Offset(Column, width), + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, + {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(Column, visible), + 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_BOOLEAN, "-widthhack", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(Column, widthHack), + 0, (ClientData) NULL, COLU_CONF_RANGES}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +static Tk_OptionTable columnOptionTable = NULL; + +/* Called when Tk_Image is deleted or modified */ +static void ImageChangedProc( + ClientData clientData, + int x, int y, + int width, int height, + int imageWidth, int imageHeight) +{ + /* I would like to know the image was deleted... */ +} + +int Tree_FindColumnByTag(TreeCtrl *tree, Tcl_Obj *obj, TreeColumn *columnPtr, int flags) +{ + Column *walk = (Column *) tree->columns; + char *string = Tcl_GetString(obj); + + if (!strcmp(string, "tail")) + { + if (!(flags & CFO_NOT_TAIL)) + { + (*columnPtr) = tree->columnTail; + return TCL_OK; + } + FormatResult(tree->interp, "can't specify \"tail\" for this command"); + return TCL_ERROR; + } + + while (walk != NULL) + { + if ((walk->tag != NULL) && !strcmp(walk->tag, string)) + { + (*columnPtr) = (TreeColumn) walk; + return TCL_OK; + } + walk = walk->next; + } + FormatResult(tree->interp, "column with tag \"%s\" doesn't exist", + string); + return TCL_ERROR; +} + +int TreeColumn_FromObj(TreeCtrl *tree, Tcl_Obj *obj, TreeColumn *columnPtr, int flags) +{ + int columnIndex; + + if (Tcl_GetIntFromObj(NULL, obj, &columnIndex) == TCL_OK) + { + if (columnIndex < 0 || columnIndex >= tree->columnCount) + { + FormatResult(tree->interp, + "bad column index \"%d\": must be from 0 to %d", + columnIndex, tree->columnCount - 1); + return TCL_ERROR; + } + (*columnPtr) = Tree_FindColumn(tree, columnIndex); + return TCL_OK; + } + + return Tree_FindColumnByTag(tree, obj, columnPtr, flags); +} + +TreeColumn Tree_FindColumn(TreeCtrl *tree, int columnIndex) +{ + Column *column = (Column *) tree->columns; + + while (column != NULL) + { + if (column->index == columnIndex) + break; + column = column->next; + } + return (TreeColumn) column; +} + +TreeColumn TreeColumn_Next(TreeColumn column_) +{ + return (TreeColumn) ((Column *) column_)->next; +} + +static void Column_FreeColors(XColor **colors, int count) +{ + int i; + + if (colors == NULL) + return; + for (i = 0; i < count; i++) + if (colors[i] != NULL) + Tk_FreeColor(colors[i]); + wipefree((char *) colors, sizeof(XColor *) * count); +} + +static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = column->tree; + Column saved, *walk; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + int mask; + XGCValues gcValues; + unsigned long gcMask; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) column, columnOptionTable, + objc, objv, tree->tkwin, &savedOptions, &mask) != TCL_OK) + { + mask = 0; + continue; + } + + /* Verify -tag is unique */ + if ((mask & COLU_CONF_TAG) && (column->tag != NULL)) + { + walk = (Column *) tree->columns; + if (!strcmp(column->tag, "tail")) + { + FormatResult(tree->interp, "column tag \"%s\" is not unique", + column->tag); + continue; + } + while (walk != NULL) + { + if ((walk != column) && (walk->tag != NULL) && !strcmp(walk->tag, column->tag)) + { + FormatResult(tree->interp, "column tag \"%s\" is not unique", + column->tag); + break; + } + walk = walk->next; + } + if (walk != NULL) + continue; + } + + if (mask & COLU_CONF_IMAGE) + { + saved.image = column->image; + column->image = NULL; + if (column->imageString != NULL) + { + column->image = Tk_GetImage(tree->interp, tree->tkwin, + column->imageString, ImageChangedProc, + (ClientData) column); + if (column->image == NULL) + continue; + } + } + + if (mask & COLU_CONF_ITEMBG) + { + saved.itemBgColor = column->itemBgColor; + saved.itemBgCount = column->itemBgCount; + column->itemBgColor = NULL; + column->itemBgCount = 0; + if (column->itemBgObj != NULL) + { + int i, length, listObjc; + Tcl_Obj **listObjv; + XColor **colors; + + if (Tcl_ListObjGetElements(tree->interp, column->itemBgObj, + &listObjc, &listObjv) != TCL_OK) + continue; + colors = (XColor **) ckalloc(sizeof(XColor *) * listObjc); + for (i = 0; i < listObjc; i++) + colors[i] = NULL; + for (i = 0; i < listObjc; i++) + { + /* Can specify "" for tree background */ + (void) Tcl_GetStringFromObj(listObjv[i], &length); + if (length != 0) + { + colors[i] = Tk_AllocColorFromObj(tree->interp, + tree->tkwin, listObjv[i]); + if (colors[i] == NULL) + break; + } + } + if (i < listObjc) + { + Column_FreeColors(colors, listObjc); + continue; + } + column->itemBgColor = colors; + column->itemBgCount = listObjc; + } + } + + if (mask & COLU_CONF_IMAGE) + { + if (saved.image != NULL) + Tk_FreeImage(saved.image); + } + if (mask & COLU_CONF_ITEMBG) + { + Column_FreeColors(saved.itemBgColor, saved.itemBgCount); + + /* Set max -itembackground */ + tree->columnBgCnt = 0; + walk = (Column *) tree->columns; + while (walk != NULL) + { + if (walk->itemBgCount > tree->columnBgCnt) + tree->columnBgCnt = walk->itemBgCount; + walk = walk->next; + } + } + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (mask & COLU_CONF_IMAGE) + { + if (column->image != NULL) + Tk_FreeImage(column->image); + column->image = saved.image; + } + if (mask & COLU_CONF_ITEMBG) + { + Column_FreeColors(column->itemBgColor, column->itemBgCount); + column->itemBgColor = saved.itemBgColor; + column->itemBgCount = saved.itemBgCount; + } + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + if (mask & COLU_CONF_TEXT) + { + column->textLen = column->text ? strlen(column->text) : 0; + if (column->textLen) + { + Tk_Font tkfont = column->tkfont ? column->tkfont : tree->tkfont; + column->textWidth = Tk_TextWidth(tkfont, column->text, column->textLen); + } + else + column->textWidth = 0; + } + + if (mask & COLU_CONF_BITMAP) + { + if (column->bitmapGC != None) + { + Tk_FreeGC(tree->display, column->bitmapGC); + column->bitmapGC = None; + } + if (column->bitmap != None) + { + gcValues.clip_mask = column->bitmap; + gcValues.graphics_exposures = False; + gcMask = GCClipMask | GCGraphicsExposures; + column->bitmapGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + } + + if (mask & COLU_CONF_ITEMBG) + Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_OUT_OF_DATE); + + if (mask & COLU_CONF_NWIDTH) + column->neededWidth = -1; + if (mask & COLU_CONF_NHEIGHT) + { + column->neededHeight = -1; + tree->headerHeight = -1; + } + + if (mask & COLU_CONF_JUSTIFY) + Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_OUT_OF_DATE); + + /* -stepwidth and -widthHack */ + if (mask & COLU_CONF_RANGES) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + /* Redraw everything */ + if (mask & (COLU_CONF_TWIDTH | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT)) + { + tree->widthOfColumns = -1; + Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH | DINFO_DRAW_HEADER); + } + + /* Redraw header only */ + else if (mask & COLU_CONF_DISPLAY) + { + Tree_DInfoChanged(tree, DINFO_DRAW_HEADER); + } + + return TCL_OK; +} + +static Column *Column_Alloc(TreeCtrl *tree) +{ + Column *column; + + if (columnOptionTable == NULL) + columnOptionTable = Tk_CreateOptionTable(tree->interp, columnSpecs); + + column = (Column *) ckalloc(sizeof(Column)); + memset(column, '\0', sizeof(Column)); + column->tree = tree; + if (Tk_InitOptions(tree->interp, (char *) column, columnOptionTable, + tree->tkwin) != TCL_OK) + { + WFREE(column, Column); + return NULL; + } +#if 0 + if (Tk_SetOptions(header->tree->interp, (char *) column, + columnOptionTable, 0, + NULL, header->tree->tkwin, &savedOptions, + (int *) NULL) != TCL_OK) + { + WFREE(column, Column); + return NULL; + } +#endif + column->neededWidth = column->neededHeight = -1; + tree->headerHeight = tree->widthOfColumns = -1; + tree->columnCount++; + return column; +} + +TreeColumn Tree_CreateColumn(TreeCtrl *tree, int columnIndex, int *isNew) +{ + Column *column = (Column *) tree->columns; + int i; + + if (isNew != NULL) + (*isNew) = FALSE; + if (column == NULL) + { + column = Column_Alloc(tree); + column->index = 0; + tree->columns = (TreeColumn) column; + if (isNew != NULL) + (*isNew) = TRUE; + } + for (i = 0; i < columnIndex; i++) + { + if (column->next == NULL) + { + column->next = Column_Alloc(tree); + column->next->index = i + 1; + if (isNew != NULL) + (*isNew) = TRUE; + } + column = column->next; + } + return (TreeColumn) column; +} + +static Column *Column_Free(Column *column) +{ + TreeCtrl *tree = column->tree; + Column *next = column->next; + + Column_FreeColors(column->itemBgColor, column->itemBgCount); + if (column->bitmapGC != None) + Tk_FreeGC(tree->display, column->bitmapGC); + if (column->image != NULL) + Tk_FreeImage(column->image); + Tk_FreeConfigOptions((char *) column, columnOptionTable, tree->tkwin); + WFREE(column, Column); + tree->columnCount--; + return next; +} + +int TreeColumn_FixedWidth(TreeColumn column_) +{ + return ((Column *) column_)->widthObj ? ((Column *) column_)->width : -1; +} + +int TreeColumn_MinWidth(TreeColumn column_) +{ + return ((Column *) column_)->minWidthObj ? ((Column *) column_)->minWidth : -1; +} + +int TreeColumn_StepWidth(TreeColumn column_) +{ + return ((Column *) column_)->stepWidthObj ? ((Column *) column_)->stepWidth : -1; +} + +int TreeColumn_NeededWidth(TreeColumn column_) +{ + Column *column = (Column *) column_; + int i, widthList[3], padList[4], n = 0; + int arrowWidth = 0; + + if (column->neededWidth >= 0) + return column->neededWidth; + + for (i = 0; i < 3; i++) widthList[i] = 0; + for (i = 0; i < 4; i++) padList[i] = 0; + + if (column->arrow != ARROW_NONE) + { + arrowWidth = Tree_HeaderHeight(column->tree) / 2; + if (!(arrowWidth & 1)) + arrowWidth--; + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) + { + widthList[n] = arrowWidth; + padList[n] = column->arrowPad[SIDE_LEFT]; + padList[n + 1] = column->arrowPad[SIDE_RIGHT]; + n++; + } + if ((column->image != NULL) || (column->bitmap != None)) + { + int imgWidth, imgHeight; + if (column->image != NULL) + Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); + else + Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); + padList[n] = MAX(column->imagePad[SIDE_LEFT], padList[n]); + padList[n + 1] = column->imagePad[SIDE_RIGHT]; + widthList[n] = imgWidth; + n++; + } + if (column->textLen > 0) + { + padList[n] = MAX(column->textPad[SIDE_LEFT], padList[n]); + padList[n + 1] = column->textPad[SIDE_RIGHT]; + widthList[n] = column->textWidth; + n++; + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) + { + widthList[n] = arrowWidth; + padList[n] = column->arrowPad[SIDE_LEFT]; + padList[n + 1] = column->arrowPad[SIDE_RIGHT]; + n++; + } + + column->neededWidth = 0; + for (i = 0; i < n; i++) + column->neededWidth += widthList[i] + padList[i]; + column->neededWidth += padList[n]; + + return column->neededWidth; +} + +int TreeColumn_NeededHeight(TreeColumn column_) +{ + Column *column = (Column *) column_; + + if (column->neededHeight >= 0) + return column->neededHeight; + + column->neededHeight = 0; + if ((column->image != NULL) || (column->bitmap != None)) + { + int imgWidth, imgHeight; + if (column->image != NULL) + Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); + else + Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); + column->neededHeight = MAX(column->neededHeight, imgHeight); + } + if (column->text != NULL) + { + Tk_Font tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; + Tk_FontMetrics fm; + Tk_GetFontMetrics(tkfont, &fm); + column->neededHeight = MAX(column->neededHeight, fm.linespace); + } + column->neededHeight += column->borderWidth * 2; + + return column->neededHeight; +} + +int TreeColumn_UseWidth(TreeColumn column_) +{ + /* Update layout if needed */ + (void) Tree_WidthOfColumns(((Column *) column_)->tree); + + return ((Column *) column_)->useWidth; +} + +void TreeColumn_SetUseWidth(TreeColumn column_, int width) +{ + ((Column *) column_)->useWidth = width; +} + +Tk_Justify TreeColumn_Justify(TreeColumn column_) +{ + return ((Column *) column_)->justify; +} + +int TreeColumn_WidthHack(TreeColumn column_) +{ + return ((Column *) column_)->widthHack; +} + +GC TreeColumn_BackgroundGC(TreeColumn column_, int index) +{ + Column *column = (Column *) column_; + XColor *color; + + if ((index < 0) || (column->itemBgCount == 0)) + return None; + color = column->itemBgColor[index % column->itemBgCount]; + if (color == NULL) + return None; + return Tk_GCForColor(color, Tk_WindowId(column->tree->tkwin)); +} + +int TreeColumn_Visible(TreeColumn column_) +{ + return ((Column *) column_)->visible; +} + +int TreeColumn_Index(TreeColumn column_) +{ + return ((Column *) column_)->index; +} + +int TreeColumnCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "bbox", "cget", "configure", "delete", + "neededwidth", "width", (char *) NULL }; + enum { COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE, + COMMAND_NEEDEDWIDTH, COMMAND_WIDTH }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_BBOX: + { + int x = 0; + Column *column, *walk; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex"); + return TCL_ERROR; + } + if (TreeColumn_FromObj(tree, objv[3], (TreeColumn *) &column, 0) != TCL_OK) + return TCL_ERROR; + if (!tree->showHeader || !column->visible) + break; + /* Update layout */ + Tree_WidthOfColumns(tree); + walk = (Column *) tree->columns; + while (walk != column) + { + if (column->visible) + x += walk->useWidth; + walk = walk->next; + } + FormatResult(interp, "%d %d %d %d", + x - tree->xOrigin, + tree->inset, + x - tree->xOrigin + column->useWidth, + tree->inset + Tree_HeaderHeight(tree)); + break; + } + case COMMAND_CGET: + { + TreeColumn column; + Tcl_Obj *resultObjPtr; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex option"); + return TCL_ERROR; + } + if (TreeColumn_FromObj(tree, objv[3], &column, 0) != TCL_OK) + return TCL_ERROR; + resultObjPtr = Tk_GetOptionValue(interp, (char *) column, + columnOptionTable, objv[4], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + case COMMAND_CONFIGURE: + { + int columnIndex; + Column *column; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex ?option? ?value?"); + return TCL_ERROR; + } + if (Tcl_GetIntFromObj(NULL, objv[3], &columnIndex) == TCL_OK) + { + if (columnIndex < 0) + return TCL_ERROR; + column = (Column *) Tree_CreateColumn(tree, columnIndex, NULL); + if (column == NULL) + return TCL_ERROR; + } + else if (Tree_FindColumnByTag(tree, objv[3], (TreeColumn *) &column, 0) != TCL_OK) + return TCL_ERROR; + if (objc <= 5) + { + Tcl_Obj *resultObjPtr; + resultObjPtr = Tk_GetOptionInfo(interp, (char *) column, + columnOptionTable, + (objc == 4) ? (Tcl_Obj *) NULL : objv[4], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + return Column_Config(column, objc - 4, objv + 4); + } + + case COMMAND_DELETE: + { + int columnIndex; + Column *column, *prev; + TreeItem item; + TreeItemColumn itemColumn; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex"); + return TCL_ERROR; + } + if (TreeColumn_FromObj(tree, objv[3], (TreeColumn *) &column, + CFO_NOT_TAIL) != TCL_OK) + return TCL_ERROR; + columnIndex = column->index; + if (columnIndex > 0) + { + prev = (Column *) Tree_FindColumn(tree, columnIndex - 1); + prev->next = column->next; + } + else + { + tree->columns = (TreeColumn) column->next; + } + Column_Free(column); + + if (columnIndex == tree->columnTree) + tree->columnTree = 0; + + /* Delete all TreeItemColumns */ + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + itemColumn = TreeItem_FindColumn(tree, item, columnIndex); + if (itemColumn != NULL) + TreeItem_RemoveColumn(tree, item, itemColumn); + hPtr = Tcl_NextHashEntry(&search); + } + + /* Renumber columns */ + column = (Column *) tree->columns; + columnIndex = 0; + while (column != NULL) + { + column->index = columnIndex++; + column = column->next; + } + + tree->widthOfColumns = tree->headerHeight = -1; + Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH); + break; + } + + case COMMAND_WIDTH: + { + Column *column; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex"); + return TCL_ERROR; + } + if (TreeColumn_FromObj(tree, objv[3], (TreeColumn *) &column, 0) != TCL_OK) + return TCL_ERROR; + /* Update layout if needed */ + (void) Tree_TotalWidth(tree); + Tcl_SetObjResult(interp, Tcl_NewIntObj(column->useWidth)); + break; + } + + case COMMAND_NEEDEDWIDTH: + { + Column *column; + int width; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "columnIndex"); + return TCL_ERROR; + } + if (TreeColumn_FromObj(tree, objv[3], (TreeColumn *) &column, 0) != TCL_OK) + return TCL_ERROR; + /* Update layout if needed */ + (void) Tree_TotalWidth(tree); + width = TreeColumn_WidthOfItems((TreeColumn) column); + width = MAX(width, TreeColumn_NeededWidth((TreeColumn) column)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(width)); + break; + } + } + + return TCL_OK; +} + +struct Layout +{ + Tk_Font tkfont; + Tk_FontMetrics fm; + int width; /* Provided by caller */ + int height; /* Provided by caller */ + int textLeft; + int textWidth; + int bytesThatFit; + int imageLeft; + int imageWidth; + int arrowLeft; + int arrowWidth; + int arrowHeight; +}; + +static void Column_Layout(Column *column, struct Layout *layout) +{ + int i, leftList[3], widthList[3], padList[4], n = 0; + int iArrow = -1, iImage = -1, iText = -1; + int left, right; + + for (i = 0; i < 3; i++) leftList[i] = 0; + for (i = 0; i < 3; i++) widthList[i] = 0; + for (i = 0; i < 4; i++) padList[i] = 0; + + if (column->arrow != ARROW_NONE) + { + layout->arrowWidth = Tree_HeaderHeight(column->tree) / 2; + if (!(layout->arrowWidth & 1)) + layout->arrowWidth--; + layout->arrowHeight = layout->arrowWidth; + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) + { + widthList[n] = layout->arrowWidth; + padList[n] = column->arrowPad[SIDE_LEFT]; + padList[n + 1] = column->arrowPad[SIDE_RIGHT]; + iArrow = n++; + } + if ((column->image != NULL) || (column->bitmap != None)) + { + int imgWidth, imgHeight; + if (column->image != NULL) + Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); + else + Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); + padList[n] = MAX(column->imagePad[SIDE_LEFT], padList[n]); + padList[n + 1] = column->imagePad[SIDE_RIGHT]; + widthList[n] = imgWidth; + layout->imageWidth = imgWidth; + iImage = n++; + } + if (column->textLen > 0) + { + layout->tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; + Tk_GetFontMetrics(layout->tkfont, &layout->fm); + layout->bytesThatFit = 0; + if (layout->width >= TreeColumn_NeededWidth((TreeColumn) column)) + { + padList[n] = MAX(column->textPad[SIDE_LEFT], padList[n]); + padList[n + 1] = column->textPad[SIDE_RIGHT]; + widthList[n] = column->textWidth; + iText = n++; + layout->bytesThatFit = column->textLen; + layout->textWidth = column->textWidth; + } + else + { + int width = column->neededWidth - layout->width; + if (width < column->textWidth) + { + width = column->textWidth - width; + layout->bytesThatFit = Ellipsis(layout->tkfont, column->text, + column->textLen, &width, "..."); + padList[n] = MAX(column->textPad[SIDE_LEFT], padList[n]); + padList[n + 1] = column->textPad[SIDE_RIGHT]; + widthList[n] = width; + iText = n++; + layout->textWidth = width; + } + } + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) + { + widthList[n] = layout->arrowWidth; + padList[n] = column->arrowPad[SIDE_LEFT]; + padList[n + 1] = column->arrowPad[SIDE_RIGHT]; + iArrow = n++; + } + + if (n == 0) + return; + + if (iText != -1) + { + switch (column->justify) + { + case TK_JUSTIFY_LEFT: + leftList[iText] = 0; + break; + case TK_JUSTIFY_RIGHT: + leftList[iText] = layout->width; + break; + case TK_JUSTIFY_CENTER: + if (iImage == -1) + leftList[iText] = (layout->width - widthList[iText]) / 2; + else + leftList[iText] = (layout->width - widthList[iImage] - + padList[iText] - widthList[iText]) / 2 + widthList[iImage] + + padList[iText]; + break; + } + } + + if (iImage != -1) + { + switch (column->justify) + { + case TK_JUSTIFY_LEFT: + leftList[iImage] = 0; + break; + case TK_JUSTIFY_RIGHT: + leftList[iImage] = layout->width; + break; + case TK_JUSTIFY_CENTER: + if (iText == -1) + leftList[iImage] = (layout->width - widthList[iImage]) / 2; + else + leftList[iImage] = (layout->width - widthList[iImage] - + padList[iText] - widthList[iText]) / 2; + break; + } + } + + if (iArrow == -1) + goto finish; + + switch (column->justify) + { + case TK_JUSTIFY_LEFT: + switch (column->arrowSide) + { + case SIDE_LEFT: + leftList[iArrow] = 0; + break; + case SIDE_RIGHT: + switch (column->arrowGravity) + { + case SIDE_LEFT: + leftList[iArrow] = 0; + break; + case SIDE_RIGHT: + leftList[iArrow] = layout->width; + break; + } + break; + } + break; + case TK_JUSTIFY_RIGHT: + switch (column->arrowSide) + { + case SIDE_LEFT: + switch (column->arrowGravity) + { + case SIDE_LEFT: + leftList[iArrow] = 0; + break; + case SIDE_RIGHT: + leftList[iArrow] = layout->width; + break; + } + break; + case SIDE_RIGHT: + leftList[iArrow] = layout->width; + break; + } + break; + case TK_JUSTIFY_CENTER: + switch (column->arrowSide) + { + case SIDE_LEFT: + switch (column->arrowGravity) + { + case SIDE_LEFT: + leftList[iArrow] = 0; + break; + case SIDE_RIGHT: + if (n == 3) + leftList[iArrow] = + (layout->width - widthList[1] - padList[2] - + widthList[2]) / 2 - padList[1] - widthList[0]; + else if (n == 2) + leftList[iArrow] = + (layout->width - widthList[1]) / 2 - + padList[1] - widthList[0]; + else + leftList[iArrow] = + (layout->width - widthList[0]) / 2; + break; + } + break; + case SIDE_RIGHT: + switch (column->arrowGravity) + { + case SIDE_LEFT: + if (n == 3) + leftList[iArrow] = + (layout->width - widthList[0] - padList[1] - + widthList[1]) / 2 + widthList[0] + padList[1] + + widthList[1] + padList[2]; + else if (n == 2) + leftList[iArrow] = + (layout->width - widthList[0]) / 2 + + widthList[0] + padList[1]; + else + leftList[iArrow] = + (layout->width - widthList[0]) / 2; + break; + case SIDE_RIGHT: + leftList[iArrow] = layout->width; + break; + } + break; + } + break; + } + +finish: + right = layout->width - padList[n]; + for (i = n - 1; i >= 0; i--) + { + if (leftList[i] + widthList[i] > right) + leftList[i] = right - widthList[i]; + right -= widthList[i] + padList[i]; + } + left = padList[0]; + for (i = 0; i < n; i++) + { + if (leftList[i] < left) + leftList[i] = left; + + if (i == iArrow) + layout->arrowLeft = leftList[i]; + else if (i == iText) + layout->textLeft = leftList[i]; + else if (i == iImage) + layout->imageLeft = leftList[i]; + + left += widthList[i] + padList[i + 1]; + } +} + +void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) +{ + Column *column = (Column *) column_; + TreeCtrl *tree = column->tree; + int height = tree->headerHeight; + struct Layout layout; + int width = column->useWidth; + int relief = column->sunken ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED; + + layout.width = width; + layout.height = height; + Column_Layout(column, &layout); + + Tk_Fill3DRectangle(tree->tkwin, drawable, column->border, + x, y, width, height, 0, TK_RELIEF_FLAT /* column->borderWidth, relief */); + + if (column->image != NULL) + { + int imgW, imgH; + Tk_SizeOfImage(column->image, &imgW, &imgH); + Tk_RedrawImage(column->image, 0, 0, imgW, imgH, + drawable, x + layout.imageLeft + column->sunken, + y + (height - imgH) / 2 + column->sunken); + } + else if (column->bitmap != None) + { + int imgW, imgH, bx, by; + + Tk_SizeOfBitmap(tree->display, column->bitmap, &imgW, &imgH); + bx = x + layout.imageLeft + column->sunken; + by = y + (height - imgH) / 2 + column->sunken; + XSetClipOrigin(tree->display, column->bitmapGC, bx, by); + XCopyPlane(tree->display, column->bitmap, drawable, column->bitmapGC, + 0, 0, (unsigned int) imgW, (unsigned int) imgH, + bx, by, 1); + XSetClipOrigin(tree->display, column->bitmapGC, 0, 0); + } + + if ((column->text != NULL) && (layout.bytesThatFit > 0)) + { + XGCValues gcValues; + GC gc; + unsigned long mask; + char staticStr[256], *text = staticStr; + int textLen = column->textLen; + char *ellipsis = "..."; + int ellipsisLen = strlen(ellipsis); + + if (textLen + ellipsisLen > sizeof(staticStr)) + text = ckalloc(textLen + ellipsisLen); + memcpy(text, column->text, textLen); + if (layout.bytesThatFit != textLen) + { + textLen = abs(layout.bytesThatFit); + if (layout.bytesThatFit > 0) + { + memcpy(text + layout.bytesThatFit, ellipsis, ellipsisLen); + textLen += ellipsisLen; + } + } + + gcValues.font = Tk_FontId(layout.tkfont); + gcValues.foreground = column->textColor->pixel; + gcValues.graphics_exposures = False; + mask = GCFont | GCForeground | GCGraphicsExposures; + gc = Tk_GetGC(tree->tkwin, mask, &gcValues); + Tk_DrawChars(tree->display, drawable, gc, + layout.tkfont, text, textLen, + x + layout.textLeft + column->sunken, + y + (height - layout.fm.linespace) / 2 + layout.fm.ascent + column->sunken); + Tk_FreeGC(tree->display, gc); + if (text != staticStr) + ckfree(text); + } + + if (column->arrow != ARROW_NONE) + { + int arrowWidth = layout.arrowWidth; + int arrowHeight = layout.arrowHeight; + int arrowTop = y + (height - arrowHeight) / 2; + int arrowBottom = arrowTop + arrowHeight; + XPoint points[5]; + int color1 = 0, color2 = 0; + int i; + + switch (column->arrow) + { + case ARROW_UP: + points[0].x = x + layout.arrowLeft; + points[0].y = arrowBottom - 1; + points[1].x = x + layout.arrowLeft + arrowWidth / 2; + points[1].y = arrowTop - 1; + color1 = TK_3D_DARK_GC; + points[4].x = x + layout.arrowLeft + arrowWidth / 2; + points[4].y = arrowTop - 1; + points[3].x = x + layout.arrowLeft + arrowWidth - 1; + points[3].y = arrowBottom - 1; + points[2].x = x + layout.arrowLeft; + points[2].y = arrowBottom - 1; + color2 = TK_3D_LIGHT_GC; + break; + case ARROW_DOWN: + points[0].x = x + layout.arrowLeft + arrowWidth - 1; + points[0].y = arrowTop; + points[1].x = x + layout.arrowLeft + arrowWidth / 2; + points[1].y = arrowBottom; + color1 = TK_3D_LIGHT_GC; + points[2].x = x + layout.arrowLeft + arrowWidth - 1; + points[2].y = arrowTop; + points[3].x = x + layout.arrowLeft; + points[3].y = arrowTop; + points[4].x = x + layout.arrowLeft + arrowWidth / 2; + points[4].y = arrowBottom; + color2 = TK_3D_DARK_GC; + break; + } + for (i = 0; i < 5; i++) + { + points[i].x += column->sunken; + points[i].y += column->sunken; + } + XDrawLines(tree->display, drawable, + Tk_3DBorderGC(tree->tkwin, column->border, color2), + points + 2, 3, CoordModeOrigin); + XDrawLines(tree->display, drawable, + Tk_3DBorderGC(tree->tkwin, column->border, color1), + points, 2, CoordModeOrigin); + } + + Tk_Draw3DRectangle(tree->tkwin, drawable, column->border, + x, y, width, height, column->borderWidth, relief); +} + +void Tree_DrawHeader(TreeCtrl *tree, Drawable drawable, int x, int y) +{ + Column *column = (Column *) tree->columns; + Tk_Window tkwin = tree->tkwin; + int minX, maxX, width, height; + Drawable pixmap; + + /* Update layout if needed */ + (void) Tree_HeaderHeight(tree); + (void) Tree_WidthOfColumns(tree); + + minX = tree->inset; + maxX = Tk_Width(tkwin) - tree->inset; + + if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), + Tk_Width(tkwin), tree->inset + tree->headerHeight, Tk_Depth(tkwin)); + else + pixmap = drawable; + + while (column != NULL) + { + if (column->visible) + { + if ((x < maxX) && (x + column->useWidth > minX)) + TreeColumn_Draw((TreeColumn) column, pixmap, x, y); + x += column->useWidth; + } + column = column->next; + } + + /* Draw "tail" column */ + if (x < maxX) + { + column = (Column *) tree->columnTail; + width = maxX - x + column->borderWidth; + height = tree->headerHeight; + Tk_Fill3DRectangle(tkwin, pixmap, column->border, + x, y, width, height, column->borderWidth, column->relief); + Tk_Draw3DRectangle(tkwin, pixmap, column->border, + x, y, width, height, column->borderWidth, column->relief); + } + + if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) + { + XCopyArea(tree->display, pixmap, drawable, + tree->copyGC, minX, y, + maxX - minX, tree->headerHeight, + tree->inset, y); + + Tk_FreePixmap(tree->display, pixmap); + } +} + +/* Calculate the maximum needed width of all ReallyVisible TreeItemColumns */ +int TreeColumn_WidthOfItems(TreeColumn column_) +{ + Column *column = (Column *) column_; + TreeCtrl *tree = column->tree; + TreeItem item; + TreeItemColumn itemColumn; + int width; + + if (column->widthOfItems >= 0) + return column->widthOfItems; + + column->widthOfItems = 0; + item = tree->root; + if (!TreeItem_ReallyVisible(tree, item)) + item = TreeItem_NextVisible(tree, item); + while (item != NULL) + { + itemColumn = TreeItem_FindColumn(tree, item, column->index); + if (itemColumn != NULL) + { + width = TreeItemColumn_NeededWidth(tree, item, itemColumn); + if (column->index == tree->columnTree) + width += TreeItem_Indent(tree, item); + column->widthOfItems = MAX(column->widthOfItems, width); + } + item = TreeItem_NextVisible(tree, item); + } + + return column->widthOfItems; +} + +/* Set useWidth for all columns */ +void Tree_LayoutColumns(TreeCtrl *tree) +{ + Column *column = (Column *) tree->columns; + int width, visWidth, totalWidth = 0; + int numExpand = 0; + + while (column != NULL) + { + if (column->visible) + { + if (column->widthObj != NULL) + width = column->width; + else + { + width = TreeColumn_WidthOfItems((TreeColumn) column); + width = MAX(width, TreeColumn_NeededWidth((TreeColumn) column)); + width = MAX(width, TreeColumn_MinWidth((TreeColumn) column)); + if (column->expand) + numExpand++; + } + column->useWidth = width; + totalWidth += width; + } + else + column->useWidth = 0; + column = column->next; + } + + visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + if ((visWidth > totalWidth) && (numExpand > 0)) + { + int extraWidth = (visWidth - totalWidth) / numExpand; + int fudge = (visWidth - totalWidth) - extraWidth * numExpand; + int seen = 0; + + column = (Column *) tree->columns; + while (column != NULL) + { + if (column->visible && column->expand && (column->widthObj == NULL)) + { + column->useWidth += extraWidth; + if (++seen == numExpand) + { + column->useWidth += fudge; + break; + } + } + column = column->next; + } + } +} + +void Tree_InvalidateColumnWidth(TreeCtrl *tree, int columnIndex) +{ + Column *column; + + if (columnIndex == -1) + { + column = (Column *) tree->columns; + while (column != NULL) + { + column->widthOfItems = -1; + column = column->next; + } + } + else + { + column = (Column *) Tree_FindColumn(tree, columnIndex); + if (column != NULL) + column->widthOfItems = -1; + } + tree->widthOfColumns = -1; + Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH); +} + +void TreeColumn_TreeChanged(TreeCtrl *tree, int flagT) +{ + Column *column; + + /* Column widths are invalidated elsewhere */ + if (flagT & TREE_CONF_FONT) + { + column = (Column *) tree->columns; + while (column != NULL) + { + if ((column->tkfont == NULL) && (column->textLen > 0)) + { + column->textWidth = Tk_TextWidth(tree->tkfont, column->text, column->textLen); + column->neededHeight = -1; + } + column = column->next; + } + tree->headerHeight = -1; + } +} + +int Tree_HeaderHeight(TreeCtrl *tree) +{ + Column *column; + int height; + + if (!tree->showHeader) + return 0; + + if (tree->headerHeight >= 0) + return tree->headerHeight; + + height = 0; + column = (Column *) tree->columns; + while (column != NULL) + { + if (column->visible) + height = MAX(height, TreeColumn_NeededHeight((TreeColumn) column)); + column = column->next; + } + return tree->headerHeight = height; +} + +int Tree_WidthOfColumns(TreeCtrl *tree) +{ + Column *column; + int width; + + if (tree->widthOfColumns >= 0) + return tree->widthOfColumns; + + Tree_LayoutColumns(tree); + + tree->columnTreeLeft = 0; + tree->columnTreeVis = FALSE; + tree->columnVis = NULL; + tree->columnCountVis = 0; + width = 0; + column = (Column *) tree->columns; + while (column != NULL) + { + if (column->visible) + { + if (tree->columnVis == NULL) + tree->columnVis = (TreeColumn) column; + tree->columnCountVis++; + if (column->index == tree->columnTree) + { + tree->columnTreeLeft = width; + tree->columnTreeVis = TRUE; + } + width += column->useWidth; + } + column = column->next; + } + + return tree->widthOfColumns = width; +} + +void Tree_InitColumns(TreeCtrl *tree) +{ + tree->columnTail = (TreeColumn) Column_Alloc(tree); + tree->columnCount = 0; + ((Column *) tree->columnTail)->index = -1; +} + +void Tree_FreeColumns(TreeCtrl *tree) +{ + Column *column = (Column *) tree->columns; + + while (column != NULL) + column = Column_Free(column); + + Column_Free((Column *) tree->columnTail); + tree->columnCount = 0; +} + diff --git a/generic/tkTreeCtrl.c b/generic/tkTreeCtrl.c new file mode 100644 index 0000000..5791998 --- /dev/null +++ b/generic/tkTreeCtrl.c @@ -0,0 +1,2988 @@ +#include "tkTreeCtrl.h" + +static CONST char *bgModeST[] = { "column", "index", "row", "visindex", (char *) NULL }; +static CONST char *doubleBufferST[] = { "none", "item", "window", (char *) NULL }; +static CONST char *lineStyleST[] = { "dot", "solid", (char *) NULL }; +static CONST char *orientStringTable[] = { "horizontal", "vertical", (char *) NULL }; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_BORDER, "-background", "background", "Background", + "white", -1, Tk_Offset(TreeCtrl, border), 0, + (ClientData) "white", TREE_CONF_REDISPLAY}, + {TK_OPTION_STRING_TABLE, "-backgroundmode", (char *) NULL, (char *) NULL, + "row", -1, Tk_Offset(TreeCtrl, backgroundMode), + 0, (ClientData) bgModeST, TREE_CONF_REDISPLAY}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + DEF_LISTBOX_BORDER_WIDTH, -1, Tk_Offset(TreeCtrl, borderWidth), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_COLOR, "-buttoncolor", (char *) NULL, (char *) NULL, + "#808080", -1, Tk_Offset(TreeCtrl, buttonColor), + 0, (ClientData) NULL, TREE_CONF_BUTTON | TREE_CONF_REDISPLAY}, + {TK_OPTION_PIXELS, "-buttonsize", (char *) NULL, (char *) NULL, + "9", Tk_Offset(TreeCtrl, buttonSizeObj), + Tk_Offset(TreeCtrl, buttonSize), + 0, (ClientData) NULL, TREE_CONF_BUTTON | TREE_CONF_RELAYOUT}, + {TK_OPTION_PIXELS, "-buttonthickness", (char *) NULL, (char *) NULL, + "1", Tk_Offset(TreeCtrl, buttonThicknessObj), + Tk_Offset(TreeCtrl, buttonThickness), + 0, (ClientData) NULL, TREE_CONF_BUTTON | TREE_CONF_REDISPLAY}, + {TK_OPTION_BITMAP, "-closedbuttonbitmap", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, closedButtonBitmap), + TK_OPTION_NULL_OK, (ClientData) NULL, + TREE_CONF_BUTTON | TREE_CONF_BUTBMP_CLOSED | TREE_CONF_RELAYOUT}, + {TK_OPTION_STRING, "-closedbuttonimage", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, closedButtonString), + TK_OPTION_NULL_OK, (ClientData) NULL, + TREE_CONF_BUTTON | TREE_CONF_BUTIMG_CLOSED | TREE_CONF_RELAYOUT}, + {TK_OPTION_PIXELS, "-columnproxy", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(TreeCtrl, columnProxy.xObj), + Tk_Offset(TreeCtrl, columnProxy.x), + TK_OPTION_NULL_OK, (ClientData) NULL, TREE_CONF_PROXY}, + {TK_OPTION_CURSOR, "-cursor", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, cursor), + TK_OPTION_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_STRING_TABLE, "-doublebuffer", (char *) NULL, (char *) NULL, + "item", -1, Tk_Offset(TreeCtrl, doubleBuffer), + 0, (ClientData) doubleBufferST, TREE_CONF_REDISPLAY}, + {TK_OPTION_FONT, "-font", "font", "Font", + DEF_LISTBOX_FONT, Tk_Offset(TreeCtrl, fontObj), + Tk_Offset(TreeCtrl, tkfont), + 0, (ClientData) NULL, TREE_CONF_FONT | TREE_CONF_RELAYOUT}, + {TK_OPTION_COLOR, "-foreground", (char *) NULL, (char *) NULL, + DEF_LISTBOX_FG, Tk_Offset(TreeCtrl, fgObj), Tk_Offset(TreeCtrl, fgColorPtr), + 0, (ClientData) NULL, TREE_CONF_FG | TREE_CONF_REDISPLAY}, + {TK_OPTION_PIXELS, "-height", "height", "Height", + "200", -1, Tk_Offset(TreeCtrl, height), + 0, (ClientData) NULL, 0}, + {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", + "HighlightBackground", DEF_LISTBOX_HIGHLIGHT_BG, -1, + Tk_Offset(TreeCtrl, highlightBgColorPtr), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", + DEF_LISTBOX_HIGHLIGHT, -1, Tk_Offset(TreeCtrl, highlightColorPtr), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", + "HighlightThickness", DEF_LISTBOX_HIGHLIGHT_WIDTH, -1, + Tk_Offset(TreeCtrl, highlightWidth), + 0, (ClientData) NULL, 0}, + {TK_OPTION_PIXELS, "-indent", (char *) NULL, (char *) NULL, + "19", -1, Tk_Offset(TreeCtrl, indent), + 0, (ClientData) NULL, TREE_CONF_INDENT | TREE_CONF_RELAYOUT}, + {TK_OPTION_PIXELS, "-itemheight", "itemHeight", "ItemHeight", + "0", -1, Tk_Offset(TreeCtrl, itemHeight), + 0, (ClientData) NULL, TREE_CONF_ITEMHEIGHT | TREE_CONF_RELAYOUT}, +#if 0 + {TK_OPTION_PIXELS, "-itempadw", (char *) NULL, (char *) NULL, + "0", + Tk_Offset(TreeCtrl, itemPadObj[LEFT]), + Tk_Offset(TreeCtrl, itemPad[LEFT]), + TK_CONFIG_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_PIXELS, "-itempadn", (char *) NULL, (char *) NULL, + "0", + Tk_Offset(TreeCtrl, itemPadObj[TOP]), + Tk_Offset(TreeCtrl, itemPad[TOP]), + TK_CONFIG_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_PIXELS, "-itempade", (char *) NULL, (char *) NULL, + "0", + Tk_Offset(TreeCtrl, itemPadObj[RIGHT]), + Tk_Offset(TreeCtrl, itemPad[RIGHT]), + TK_CONFIG_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_PIXELS, "-itempads", (char *) NULL, (char *) NULL, + "0", + Tk_Offset(TreeCtrl, itemPadObj[BOTTOM]), + Tk_Offset(TreeCtrl, itemPad[BOTTOM]), + TK_CONFIG_NULL_OK, (ClientData) NULL, 0}, +#endif + {TK_OPTION_COLOR, "-linecolor", (char *) NULL, (char *) NULL, + "#808080", -1, Tk_Offset(TreeCtrl, lineColor), + 0, (ClientData) NULL, TREE_CONF_LINE | TREE_CONF_REDISPLAY}, + {TK_OPTION_STRING_TABLE, "-linestyle", (char *) NULL, (char *) NULL, + "dot", -1, Tk_Offset(TreeCtrl, lineStyle), + 0, (ClientData) lineStyleST, TREE_CONF_LINE | TREE_CONF_REDISPLAY}, + {TK_OPTION_PIXELS, "-linethickness", (char *) NULL, (char *) NULL, + "1", Tk_Offset(TreeCtrl, lineThicknessObj), + Tk_Offset(TreeCtrl, lineThickness), + 0, (ClientData) NULL, TREE_CONF_LINE | TREE_CONF_REDISPLAY}, + {TK_OPTION_BITMAP, "-openbuttonbitmap", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, openButtonBitmap), + TK_OPTION_NULL_OK, (ClientData) NULL, + TREE_CONF_BUTTON | TREE_CONF_BUTBMP_OPEN | TREE_CONF_RELAYOUT}, + {TK_OPTION_STRING, "-openbuttonimage", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, openButtonString), + TK_OPTION_NULL_OK, (ClientData) NULL, + TREE_CONF_BUTTON | TREE_CONF_BUTIMG_OPEN | TREE_CONF_RELAYOUT}, + {TK_OPTION_STRING_TABLE, "-orient", (char *) NULL, (char *) NULL, + "vertical", -1, Tk_Offset(TreeCtrl, vertical), + 0, (ClientData) orientStringTable, TREE_CONF_RELAYOUT}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "sunken", -1, Tk_Offset(TreeCtrl, relief), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_STRING, "-scrollmargin", (char *) NULL, (char *) NULL, + "0", Tk_Offset(TreeCtrl, scrollMargin), -1, + 0, (ClientData) NULL, 0}, + {TK_OPTION_STRING, "-selectmode", "selectMode", "SelectMode", + DEF_LISTBOX_SELECT_MODE, -1, Tk_Offset(TreeCtrl, selectMode), + TK_OPTION_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_BOOLEAN, "-showbuttons", "showButtons", + "ShowButtons", "1", -1, Tk_Offset(TreeCtrl, showButtons), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_BOOLEAN, "-showheader", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(TreeCtrl, showHeader), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_BOOLEAN, "-showlines", "showLines", + "ShowLines", "1", -1, Tk_Offset(TreeCtrl, showLines), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_BOOLEAN, "-showroot", "showRoot", + "ShowRoot", "1", -1, Tk_Offset(TreeCtrl, showRoot), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_BOOLEAN, "-showrootbutton", "showRootButton", + "ShowRootButton", "0", -1, Tk_Offset(TreeCtrl, showRootButton), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", + DEF_LISTBOX_TAKE_FOCUS, -1, Tk_Offset(TreeCtrl, takeFocus), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_INT, "-treecolumn", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(TreeCtrl, columnTree), + 0, (ClientData) NULL, TREE_CONF_RELAYOUT}, + {TK_OPTION_PIXELS, "-width", "width", "Width", + "200", -1, Tk_Offset(TreeCtrl, width), + 0, (ClientData) NULL, 0}, + {TK_OPTION_STRING, "-wrap", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(TreeCtrl, wrapObj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, + TREE_CONF_WRAP | TREE_CONF_RELAYOUT}, + {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", + (char *) NULL, -1, Tk_Offset(TreeCtrl, xScrollCmd), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_STRING, "-xscrolldelay", (char *) NULL, (char *) NULL, + "50", Tk_Offset(TreeCtrl, xScrollDelay), -1, + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_PIXELS, "-xscrollincrement", "xScrollIncrement", "ScrollIncrement", + "0", -1, Tk_Offset(TreeCtrl, xScrollIncrement), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", + (char *) NULL, -1, Tk_Offset(TreeCtrl, yScrollCmd), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_STRING, "-yscrolldelay", (char *) NULL, (char *) NULL, + "50", Tk_Offset(TreeCtrl, yScrollDelay), -1, + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_PIXELS, "-yscrollincrement", "yScrollIncrement", "ScrollIncrement", + "0", -1, Tk_Offset(TreeCtrl, yScrollIncrement), + 0, (ClientData) NULL, TREE_CONF_REDISPLAY}, + {TK_OPTION_END} +}; + +static Tk_OptionSpec debugSpecs[] = { + {TK_OPTION_INT, "-displaydelay", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(TreeCtrl, debug.displayDelay), + 0, (ClientData) NULL, 0}, + {TK_OPTION_BOOLEAN, "-data", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(TreeCtrl, debug.data), + 0, (ClientData) NULL, 0}, + {TK_OPTION_BOOLEAN, "-display", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(TreeCtrl, debug.display), + 0, (ClientData) NULL, 0}, + {TK_OPTION_BOOLEAN, "-enable", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(TreeCtrl, debug.enable), + 0, (ClientData) NULL, 0}, + {TK_OPTION_COLOR, "-erasecolor", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(TreeCtrl, debug.eraseColor), + TK_OPTION_NULL_OK, (ClientData) NULL, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +static int TreeWidgetCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +static int TreeConfigure(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[], int createFlag); +static void TreeEventProc(ClientData clientData, XEvent * eventPtr); +static void TreeDestroy(char *memPtr); +static void TreeCmdDeletedProc(ClientData clientData); +static void TreeWorldChanged(ClientData instanceData); +static void TreeComputeGeometry(TreeCtrl *tree); +static int TreeStateCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +static int TreeSelectionCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +static int TreeXviewCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +static int TreeYviewCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +static int TreeDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]); + +static Tk_ClassProcs treectrlClass = { + sizeof(Tk_ClassProcs), /* size */ + TreeWorldChanged, /* worldChangedProc. */ + NULL, /* createProc. */ + NULL /* modalProc. */ +}; + +static int TreeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree; + Tk_Window tkwin; + Tk_OptionTable optionTable; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); + return TCL_ERROR; + } + + tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), + Tcl_GetStringFromObj(objv[1], NULL), (char *) NULL); + if (tkwin == NULL) + { + return TCL_ERROR; + } + + optionTable = Tk_CreateOptionTable(interp, optionSpecs); + + tree = (TreeCtrl *) ckalloc(sizeof(TreeCtrl)); + memset(tree, '\0', sizeof(TreeCtrl)); + tree->tkwin = tkwin; + tree->display = Tk_Display(tkwin); + tree->interp = interp; + tree->widgetCmd = Tcl_CreateObjCommand(interp, + Tk_PathName(tree->tkwin), TreeWidgetCmd, + (ClientData) tree, TreeCmdDeletedProc); + tree->optionTable = optionTable; + tree->relief = TK_RELIEF_SUNKEN; + tree->prevWidth = Tk_Width(tkwin); + tree->prevHeight = Tk_Height(tkwin); + tree->updateIndex = 1; + + tree->stateNames[0] = "open"; + tree->stateNames[1] = "selected"; + tree->stateNames[2] = "enabled"; + tree->stateNames[3] = "active"; + tree->stateNames[4] = "focus"; + + /* Do this before Tree_InitColumns() which does Tk_InitOptions(), which + * calls Tk_GetOption() which relies on the window class */ + Tk_SetClass(tkwin, "TreeCtrl"); + Tk_SetClassProcs(tkwin, &treectrlClass, (ClientData) tree); + + tree->debug.optionTable = Tk_CreateOptionTable(interp, debugSpecs); + (void) Tk_InitOptions(interp, (char *) tree, tree->debug.optionTable, + tkwin); + + Tcl_InitHashTable(&tree->itemHash, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&tree->elementHash, TCL_STRING_KEYS); + Tcl_InitHashTable(&tree->styleHash, TCL_STRING_KEYS); + Tcl_InitHashTable(&tree->imageHash, TCL_STRING_KEYS); + + Tree_InitColumns(tree); + + tree->root = TreeItem_AllocRoot(tree); + tree->activeItem = tree->root; /* always non-null */ + tree->anchorItem = tree->root; /* always non-null */ + + TreeNotify_Init(tree); + TreeMarquee_Init(tree); + TreeDragImage_Init(tree); + TreeDInfo_Init(tree); + + Tk_CreateEventHandler(tree->tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + TreeEventProc, (ClientData) tree); + + /* Must do this on Unix because Tk_GCForColor() uses + * Tk_WindowId(tree->tkwin) */ + Tk_MakeWindowExist(tree->tkwin); + + if (Tk_InitOptions(interp, (char *) tree, optionTable, tkwin) != TCL_OK) + { + Tk_DestroyWindow(tree->tkwin); + WFREE(tree, TreeCtrl); + return TCL_ERROR; + } + + if (TreeConfigure(interp, tree, objc - 2, objv + 2, TRUE) != TCL_OK) + { + Tk_DestroyWindow(tree->tkwin); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tree->tkwin), -1)); + return TCL_OK; +} + +#define W2Cx(x) ((x) + tree->xOrigin) +#define C2Wx(x) ((x) - tree->xOrigin) +#define C2Ox(x) ((x) - tree->inset) + +#define W2Cy(y) ((y) + tree->yOrigin) +#define C2Wy(y) ((y) - tree->yOrigin) +#define C2Oy(y) ((y) - tree->inset - Tree_HeaderHeight(tree)) + +static int TreeWidgetCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + int result = TCL_OK; + static CONST char *commandName[] = { "activate", "canvasx", "canvasy", + "cget", "collapse", + "column", "compare", "configure", "contentbox", "debug", "depth", "dragimage", + "element", "expand", "identify", "index", "item", + "marquee", "notify", "numcolumns", "numitems", "orphans", + "range", "see", "selection", "state", "style", + "toggle", "xview", "yview", (char *) NULL }; + enum { + COMMAND_ACTIVATE, COMMAND_CANVASX, COMMAND_CANVASY, COMMAND_CGET, + COMMAND_COLLAPSE, COMMAND_COLUMN, COMMAND_COMPARE, COMMAND_CONFIGURE, + COMMAND_CONTENTBOX, COMMAND_DEBUG, COMMAND_DEPTH, + COMMAND_DRAGIMAGE, COMMAND_ELEMENT, COMMAND_EXPAND,COMMAND_IDENTIFY, + COMMAND_INDEX, COMMAND_ITEM, COMMAND_MARQUEE, COMMAND_NOTIFY, + COMMAND_NUMCOLUMNS, COMMAND_NUMITEMS, COMMAND_ORPHANS, COMMAND_RANGE, + COMMAND_SEE, COMMAND_SELECTION, COMMAND_STATE, COMMAND_STYLE, + COMMAND_TOGGLE, COMMAND_XVIEW, COMMAND_YVIEW + }; + Tcl_Obj *resultObjPtr; + int index; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 1, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[1], commandName, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + Tcl_Preserve((ClientData) tree); + + switch (index) + { + case COMMAND_ACTIVATE: + { + TreeItem item; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "item"); + goto error; + } + if (TreeItem_FromObj(tree, objv[2], &item, 0) != TCL_OK) + { + goto error; + } + if (item != tree->activeItem) + { + TreeNotify_ActiveItem(tree, tree->activeItem, item); + TreeItem_ChangeState(tree, tree->activeItem, STATE_ACTIVE, 0); + tree->activeItem = item; + TreeItem_ChangeState(tree, tree->activeItem, 0, STATE_ACTIVE); + } + break; + } + + case COMMAND_CANVASX: + { + int x; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "x"); + goto error; + } + if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, Tcl_NewIntObj(x + tree->xOrigin)); + break; + } + + case COMMAND_CANVASY: + { + int y; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "y"); + goto error; + } + if (Tcl_GetIntFromObj(interp, objv[2], &y) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, Tcl_NewIntObj(y + tree->yOrigin)); + break; + } + + case COMMAND_CGET: + { + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "option"); + goto error; + } + resultObjPtr = Tk_GetOptionValue(interp, (char *) tree, + tree->optionTable, objv[2], tree->tkwin); + if (resultObjPtr == NULL) + { + result = TCL_ERROR; + } + else + { + Tcl_SetObjResult(interp, resultObjPtr); + } + break; + } + + case COMMAND_CONFIGURE: + { + resultObjPtr = NULL; + if (objc <= 3) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) tree, + tree->optionTable, + (objc == 2) ? (Tcl_Obj *) NULL : objv[2], + tree->tkwin); + if (resultObjPtr == NULL) + { + result = TCL_ERROR; + } + else + { + Tcl_SetObjResult(interp, resultObjPtr); + } + } + else + { + result = TreeConfigure(interp, tree, objc - 2, objv + 2, FALSE); + } + break; + } + + case COMMAND_CONTENTBOX: + { + if (objc != 2) + { + Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL); + goto error; + } + FormatResult(interp, "%d %d %d %d", + tree->inset, tree->inset + Tree_HeaderHeight(tree), + Tk_Width(tree->tkwin) - tree->inset, + Tk_Height(tree->tkwin) - tree->inset); + break; + } + + case COMMAND_COLLAPSE: + case COMMAND_EXPAND: + case COMMAND_TOGGLE: + { + char *s; + int i; + int recurse = 0; + TreeItem item; + + if (objc < 2) + { + Tcl_WrongNumArgs(interp, 2, objv, "options ?item...?"); + goto error; + } + if (objc == 2) + break; + s = Tcl_GetString(objv[2]); + if (!strcmp(s, "-recurse")) + { + recurse = 1; + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "options ?item...?"); + goto error; + } + } + for (i = 2 + recurse; i < objc; i++) + { + int oldRecurse = recurse; + if (TreeItem_FromObj(tree, objv[i], &item, IFO_ALLOK) != TCL_OK) + goto error; + if (item == ITEM_ALL) + { + item = tree->root; + recurse = 1; + } + switch (index) + { + case COMMAND_COLLAPSE: + TreeItem_OpenClose(tree, item, 0, recurse); + break; + case COMMAND_EXPAND: + TreeItem_OpenClose(tree, item, 1, recurse); + break; + case COMMAND_TOGGLE: + TreeItem_OpenClose(tree, item, -1, recurse); + break; + } + recurse = oldRecurse; + } +#if 0 + if (tree->debug.enable) + Tree_Debug(tree); +#endif + break; + } + + case COMMAND_COLUMN: + { + result = TreeColumnCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_COMPARE: + { + TreeItem item1, item2; + static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL }; + int op, compare = 0, index1, index2; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 2, objv, "item1 op item2"); + goto error; + } + if (TreeItem_FromObj(tree, objv[2], &item1, 0) != TCL_OK) + goto error; + if (Tcl_GetIndexFromObj(interp, objv[3], opName, "comparison operator", 0, + &op) != TCL_OK) + goto error; + if (TreeItem_FromObj(tree, objv[4], &item2, 0) != TCL_OK) + goto error; + if (TreeItem_RootAncestor(tree, item1) != + TreeItem_RootAncestor(tree, item2)) + { + FormatResult(interp, + "item %d and item %d don't share a common ancestor", + TreeItem_GetID(tree, item1), TreeItem_GetID(tree, item2)); + goto error; + } + TreeItem_ToIndex(tree, item1, &index1, NULL); + TreeItem_ToIndex(tree, item2, &index2, NULL); + switch (op) + { + case 0: compare = index1 < index2; break; + case 1: compare = index1 <= index2; break; + case 2: compare = index1 == index2; break; + case 3: compare = index1 >= index2; break; + case 4: compare = index1 > index2; break; + case 5: compare = index1 != index2; break; + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare)); + break; + } + + case COMMAND_DEBUG: + { + result = TreeDebugCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_DEPTH: + { + TreeItem item; + + if (objc == 3) + { + if (TreeItem_FromObj(tree, objv[2], &item, 0) != TCL_OK) + goto error; + Tcl_SetObjResult(interp, + Tcl_NewIntObj(TreeItem_GetDepth(tree, item) + 1)); + break; + } + if (tree->updateIndex) + Tree_UpdateItemIndex(tree); + Tcl_SetObjResult(interp, Tcl_NewIntObj(tree->depth + 1)); + break; + } + + case COMMAND_DRAGIMAGE: + { + result = DragImageCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_ELEMENT: + { + result = TreeElementCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_IDENTIFY: + { + int x, y, depth; + TreeItem item; + char buf[64]; +/* + set id [$tree identify $x $y] + "item I column C" : mouse is in column C of item I + "item I column C elem E" : mouse is in element E in column C of item I + "item I button" : mouse is in button-area of item I + "item I line J" : mouse is near line coming from item J + "header C ?left|right?" : mouse is in header column C + "" : mouse is not in any item +*/ + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 2, objv, "x y"); + goto error; + } + if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) + goto error; + if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) + goto error; + + /* Point in header */ + if ((y >= tree->inset) && (y < tree->inset + Tree_HeaderHeight(tree))) + { + int left = 0 - tree->xOrigin, columnIndex = 0; + TreeColumn column = tree->columns; + + while (column != NULL) + { + int width = TreeColumn_UseWidth(column); + if ((x >= left) && (x < left + width)) + { + sprintf(buf, "header %d", columnIndex); + if (x < left + 4) + sprintf(buf + strlen(buf), " left"); + else if (x >= left + width - 4) + sprintf(buf + strlen(buf), " right"); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + goto done; + } + left += width; + columnIndex++; + column = TreeColumn_Next(column); + } + strcpy(buf, "header tail"); + if (x < left + 4) + sprintf(buf + strlen(buf), " left"); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + break; + } + + item = Tree_ItemUnderPoint(tree, &x, &y, FALSE); + if (item == NULL) + break; + + sprintf(buf, "item %d", TreeItem_GetID(tree, item)); /* TreeItem_ToObj() */ + depth = TreeItem_GetDepth(tree, item); + if (tree->showRoot || tree->showButtons || tree->showLines) + depth++; + if (tree->showRoot && tree->showButtons && tree->showRootButton) + depth++; + if (item == tree->root) + depth = (tree->showButtons && tree->showRootButton) ? 1 : 0; + + /* Point is in a line or button */ + if ((x >= tree->columnTreeLeft) && (x < tree->columnTreeLeft + depth * tree->useIndent)) + { + int column = (x - tree->columnTreeLeft) / tree->useIndent + 1; + if (column == depth) + { + if (tree->showButtons && TreeItem_GetButton(tree, item)) + sprintf(buf + strlen(buf), " button"); + } + else if (tree->showLines) + { + do { + item = TreeItem_GetParent(tree, item); + } while (++column < depth); + if (TreeItem_GetNextSibling(tree, item) != NULL) + sprintf(buf + strlen(buf), " line %d", TreeItem_GetID(tree, item)); /* TreeItem_ToObj() */ + } + } + + else if (tree->columnCountVis == 1) + { + char *elem; + + sprintf(buf + strlen(buf), " column %d", + TreeColumn_Index(tree->columnVis)); + elem = TreeItem_Identify(tree, item, x, y); + if (elem != NULL) + sprintf(buf + strlen(buf), " elem %s", elem); + } + + /* Point is in a TreeItemColumn */ + else + { + int left = 0; + TreeColumn treeColumn; + TreeItemColumn itemColumn; + + treeColumn = tree->columns; + itemColumn = TreeItem_GetFirstColumn(tree, item); + while (itemColumn != NULL) + { + int width = TreeColumn_UseWidth(treeColumn); + if ((x >= left) && (x < left + width)) + { + char *elem; + sprintf(buf + strlen(buf), " column %d", + TreeColumn_Index(treeColumn)); + elem = TreeItem_Identify(tree, item, x, y); + if (elem != NULL) + sprintf(buf + strlen(buf), " elem %s", elem); + break; + } + left += width; + treeColumn = TreeColumn_Next(treeColumn); + itemColumn = TreeItemColumn_GetNext(tree, itemColumn); + } + } + Tcl_SetResult(interp, buf, TCL_VOLATILE); + break; + } + + case COMMAND_INDEX: + { + TreeItem item; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "item"); + goto error; + } + if (TreeItem_FromObj(tree, objv[2], &item, IFO_NULLOK) != TCL_OK) + { + goto error; + } + if (item != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item)); + break; + } + + case COMMAND_ITEM: + { + result = TreeItemCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_MARQUEE: + { + result = TreeMarqueeCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_NOTIFY: + { + result = TreeNotifyCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_NUMCOLUMNS: + { + Tcl_SetObjResult(interp, Tcl_NewIntObj(tree->columnCount)); + break; + } + + case COMMAND_NUMITEMS: + { + Tcl_SetObjResult(interp, Tcl_NewIntObj(tree->itemCount)); + break; + } + + case COMMAND_ORPHANS: + { + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tcl_Obj *listObj; + TreeItem item; + + /* Pretty slow. Could keep a hash table of orphans */ + listObj = Tcl_NewListObj(0, NULL); + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if ((item != tree->root) && + (TreeItem_GetParent(tree, item) == NULL)) + { + Tcl_ListObjAppendElement(interp, listObj, + TreeItem_ToObj(tree, item)); + } + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_SetObjResult(interp, listObj); + break; + } + + case COMMAND_RANGE: + { + TreeItem item, itemFirst, itemLast; + int indexFirst, indexLast; + Tcl_Obj *listObj; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 2, objv, "first last"); + goto error; + } + if (TreeItem_FromObj(tree, objv[2], &itemFirst, 0) != TCL_OK) + goto error; + if (TreeItem_FromObj(tree, objv[3], &itemLast, 0) != TCL_OK) + goto error; + if (TreeItem_RootAncestor(tree, itemFirst) != + TreeItem_RootAncestor(tree, itemLast)) + { + FormatResult(interp, + "item %d and item %d don't share a common ancestor", + TreeItem_GetID(tree, itemFirst), TreeItem_GetID(tree, itemLast)); + goto error; + } + TreeItem_ToIndex(tree, itemFirst, &indexFirst, NULL); + TreeItem_ToIndex(tree, itemLast, &indexLast, NULL); + if (indexFirst > indexLast) + { + item = itemFirst; + itemFirst = itemLast; + itemLast = item; + } + listObj = Tcl_NewListObj(0, NULL); + item = itemFirst; + while (item != NULL) + { + Tcl_ListObjAppendElement(interp, listObj, + TreeItem_ToObj(tree, item)); + if (item == itemLast) + break; + item = TreeItem_Next(tree, item); + } + Tcl_SetObjResult(interp, listObj); + break; + } + + case COMMAND_SEE: + { + TreeItem item; + int x, y, w, h; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int xOrigin = tree->xOrigin; + int yOrigin = tree->yOrigin; + int minX = tree->inset; + int minY = topInset; + int maxX = minX + visWidth; + int maxY = minY + visHeight; + int index, offset; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "item"); + goto error; + } + if (TreeItem_FromObj(tree, objv[2], &item, 0) != TCL_OK) + goto error; + if (!TreeItem_ReallyVisible(tree, item)) + break; + + /* Canvas coords */ + Tree_ItemBbox(tree, item, &x, &y, &w, &h); + + if ((C2Wx(x) > maxX) || (C2Wx(x + w) <= minX) || (w <= visWidth)) + { + if ((C2Wx(x) < minX) || (w > visWidth)) + { + index = Increment_FindX(tree, x); + offset = Increment_ToOffsetX(tree, index); + xOrigin = C2Ox(offset); + } + else if (C2Wx(x + w) > maxX) + { + index = Increment_FindX(tree, x + w - visWidth); + offset = Increment_ToOffsetX(tree, index); + if (offset < x + w - visWidth) + { + index++; + offset = Increment_ToOffsetX(tree, index); + } + xOrigin = C2Ox(offset); + } + } + + if ((C2Wy(y) > maxY) || (C2Wy(y + h) <= minY) || (h <= visHeight)) + { + if ((C2Wy(y) < minY) || (h > visHeight)) + { + index = Increment_FindY(tree, y); + offset = Increment_ToOffsetY(tree, index); + yOrigin = C2Oy(offset); + } + else if (C2Wy(y + h) > maxY) + { + index = Increment_FindY(tree, y + h - visHeight); + offset = Increment_ToOffsetY(tree, index); + if (offset < y + h - visHeight) + { + index++; + offset = Increment_ToOffsetY(tree, index); + } + yOrigin = C2Oy(offset); + } + } + + Tree_SetOriginX(tree, xOrigin); + Tree_SetOriginY(tree, yOrigin); + break; + } + + case COMMAND_SELECTION: + { + result = TreeSelectionCmd(interp, tree, objc, objv); + break; + } + + case COMMAND_STATE: + { + result = TreeStateCmd(tree, objc, objv); + break; + } + + case COMMAND_STYLE: + { + result = TreeStyleCmd(clientData, interp, objc, objv); + break; + } + + case COMMAND_XVIEW: + { + result = TreeXviewCmd(interp, tree, objc, objv); + break; + } + + case COMMAND_YVIEW: + { + result = TreeYviewCmd(interp, tree, objc, objv); + break; + } + } +done: + Tcl_Release((ClientData) tree); + return result; + +error: + Tcl_Release((ClientData) tree); + return TCL_ERROR; +} + +static int TreeConfigure(Tcl_Interp *interp, TreeCtrl *tree, int objc, + Tcl_Obj *CONST objv[], int createFlag) +{ + int error; + Tcl_Obj *errorResult = NULL; + TreeCtrl saved; + Tk_SavedOptions savedOptions; + int doubleBuffer = 1; + int oldShowRoot = tree->showRoot; + int mask; + XGCValues gcValues; + unsigned long gcMask; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(interp, (char *) tree, tree->optionTable, objc, + objv, tree->tkwin, &savedOptions, &mask) != TCL_OK) + { + mask = 0; + continue; + } + + if (mask & TREE_CONF_BUTIMG_CLOSED) + { + saved.closedButtonImage = tree->closedButtonImage; + tree->closedButtonImage = NULL; + if (tree->closedButtonString != NULL) + { + Tk_Image image = Tree_GetImage(tree, tree->closedButtonString); + if (image == NULL) + continue; + tree->closedButtonImage = image; + } + } + + if (mask & TREE_CONF_BUTIMG_OPEN) + { + saved.openButtonImage = tree->openButtonImage; + tree->openButtonImage = NULL; + if (tree->openButtonString != NULL) + { + Tk_Image image = Tree_GetImage(tree, tree->openButtonString); + if (image == NULL) + continue; + tree->openButtonImage = image; + } + } + + /* Parse -wrap string into wrapMode and wrapArg */ + if (mask & TREE_CONF_WRAP) + { + int listObjc; + Tcl_Obj **listObjv; + + saved.wrapMode = tree->wrapMode; + saved.wrapArg = tree->wrapArg; + + if (tree->wrapObj == NULL) + { + tree->wrapMode = TREE_WRAP_NONE; + tree->wrapArg = 0; + } + else + { + int len0, len1; + char *s0, *s1, ch0, ch1; + + if ((Tcl_ListObjGetElements(interp, tree->wrapObj, &listObjc, + &listObjv) != TCL_OK) || (listObjc > 2)) + { +badWrap: + FormatResult(interp, "bad wrap \"%s\"", + Tcl_GetString(tree->wrapObj)); + continue; + } + if (listObjc == 1) + { + s0 = Tcl_GetStringFromObj(listObjv[0], &len0); + ch0 = s0[0]; + if ((ch0 == 'w') && !strncmp(s0, "window", len0)) + { + tree->wrapMode = TREE_WRAP_WINDOW; + tree->wrapArg = 0; + } + else + goto badWrap; + } + else + { + s1 = Tcl_GetStringFromObj(listObjv[1], &len1); + ch1 = s1[0]; + if ((ch1 == 'i') && !strncmp(s1, "items", len1)) + { + int n; + if ((Tcl_GetIntFromObj(interp, listObjv[0], &n) != TCL_OK) || + (n < 0)) + { + goto badWrap; + } + tree->wrapMode = TREE_WRAP_ITEMS; + tree->wrapArg = n; + } + else if ((ch1 == 'p') && !strncmp(s1, "pixels", len1)) + { + int n; + if (Tk_GetPixelsFromObj(interp, tree->tkwin, listObjv[0], &n) + != TCL_OK) + { + goto badWrap; + } + tree->wrapMode = TREE_WRAP_PIXELS; + tree->wrapArg = n; + } + else + goto badWrap; + } + } + } + + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (mask & TREE_CONF_BUTIMG_CLOSED) + { + tree->closedButtonImage = saved.closedButtonImage; + } + + if (mask & TREE_CONF_BUTIMG_OPEN) + { + tree->openButtonImage = saved.openButtonImage; + } + + if (mask & TREE_CONF_WRAP) + { + tree->wrapMode = saved.wrapMode; + tree->wrapArg = saved.wrapArg; + } + + Tcl_SetObjResult(interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + Tk_SetWindowBackground(tree->tkwin, + Tk_3DBorderColor(tree->border)->pixel); + + if (createFlag) + mask |= TREE_CONF_FONT; + + if (mask & (TREE_CONF_FONT | TREE_CONF_FG)) + { + gcValues.font = Tk_FontId(tree->tkfont); + gcValues.foreground = tree->fgColorPtr->pixel; + gcValues.graphics_exposures = False; + gcMask = GCForeground | GCFont | GCGraphicsExposures; + if (tree->textGC != None) + Tk_FreeGC(tree->display, tree->textGC); + tree->textGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + + if ((tree->copyGC == None) && (doubleBuffer)) + { + gcValues.function = GXcopy; + gcValues.graphics_exposures = False; + gcMask = GCFunction | GCGraphicsExposures; + tree->copyGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + + if (createFlag) + { + mask |= TREE_CONF_BUTTON; + tree->closedButtonWidth = tree->buttonSize; + tree->closedButtonHeight = tree->buttonSize; + } + + if (mask & TREE_CONF_BUTTON) + { + if (tree->buttonGC != None) + Tk_FreeGC(tree->display, tree->buttonGC); + gcValues.foreground = tree->buttonColor->pixel; + gcValues.line_width = tree->buttonThickness; + gcMask = GCForeground | GCLineWidth; + tree->buttonGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + + if (createFlag) + mask |= TREE_CONF_LINE; + + if (mask & TREE_CONF_LINE) + { + if (tree->lineGC != None) + Tk_FreeGC(tree->display, tree->lineGC); + gcValues.foreground = tree->lineColor->pixel; + gcValues.line_width = tree->lineThickness; + gcMask = GCForeground | GCLineWidth; + tree->lineGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + + if (mask & TREE_CONF_PROXY) + { + TreeColumnProxy_Undisplay(tree); + TreeColumnProxy_Display(tree); + } + + if (mask & TREE_CONF_BUTBMP_CLOSED) + { + if (tree->buttonClosedGC != None) + { + Tk_FreeGC(tree->display, tree->buttonClosedGC); + tree->buttonClosedGC = None; + } + if (tree->closedButtonBitmap != None) + { + gcValues.clip_mask = tree->closedButtonBitmap; + gcValues.graphics_exposures = False; + gcMask = GCClipMask | GCGraphicsExposures; + tree->buttonClosedGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + } + } + + if (mask & TREE_CONF_BUTBMP_OPEN) + { + if (tree->buttonOpenGC != None) + { + Tk_FreeGC(tree->display, tree->buttonOpenGC); + tree->buttonOpenGC = None; + } + if (tree->openButtonBitmap != None) + { + gcValues.clip_mask = tree->openButtonBitmap; + gcValues.graphics_exposures = False; + gcMask = GCClipMask | GCGraphicsExposures; + tree->buttonOpenGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + + Tk_SizeOfBitmap(tree->display, tree->openButtonBitmap, + &tree->openButtonWidth, &tree->openButtonHeight); + } + } + + if (mask & (TREE_CONF_BUTIMG_CLOSED | TREE_CONF_BUTBMP_CLOSED)) + { + if (tree->closedButtonImage != NULL) + { + Tk_SizeOfImage(tree->closedButtonImage, + &tree->closedButtonWidth, &tree->closedButtonHeight); + } + else if (tree->closedButtonBitmap != None) + { + Tk_SizeOfBitmap(tree->display, tree->closedButtonBitmap, + &tree->closedButtonWidth, &tree->closedButtonHeight); + } + else + { + tree->closedButtonWidth = tree->buttonSize; + tree->closedButtonHeight = tree->buttonSize; + } + } + + if (mask & (TREE_CONF_BUTIMG_OPEN | TREE_CONF_BUTBMP_OPEN)) + { + if (tree->openButtonImage != NULL) + { + Tk_SizeOfImage(tree->openButtonImage, + &tree->openButtonWidth, &tree->openButtonHeight); + } + else if (tree->openButtonBitmap != None) + { + Tk_SizeOfBitmap(tree->display, tree->openButtonBitmap, + &tree->openButtonWidth, &tree->openButtonHeight); + } + else + { + tree->openButtonWidth = tree->buttonSize; + tree->openButtonHeight = tree->buttonSize; + } + } + + tree->useIndent = MAX(tree->closedButtonWidth, tree->openButtonWidth); + tree->useIndent = MAX(tree->indent, tree->useIndent); + + if (tree->highlightWidth < 0) + tree->highlightWidth = 0; + tree->inset = tree->highlightWidth + tree->borderWidth; + + if ((tree->columnTree < 0) || (tree->columnTree >= tree->columnCount)) + tree->columnTree = 0; + + if (oldShowRoot != tree->showRoot) + { + TreeItem_InvalidateHeight(tree, tree->root); + tree->updateIndex = 1; + } + + TreeStyle_TreeChanged(tree, mask); + TreeColumn_TreeChanged(tree, mask); + + if (mask & TREE_CONF_RELAYOUT) + { + TreeComputeGeometry(tree); + Tree_InvalidateColumnWidth(tree, -1); + Tree_RelayoutWindow(tree); + } + + if (mask & TREE_CONF_REDISPLAY) + Tree_RelayoutWindow(tree); + + return TCL_OK; +} + +static void TreeWorldChanged(ClientData instanceData) +{ + TreeCtrl *tree = (TreeCtrl *) instanceData; + XGCValues gcValues; + unsigned long gcMask; + + gcValues.font = Tk_FontId(tree->tkfont); + gcValues.foreground = tree->fgColorPtr->pixel; + gcValues.graphics_exposures = False; + gcMask = GCForeground | GCFont | GCGraphicsExposures; + if (tree->textGC != None) + Tk_FreeGC(tree->display, tree->textGC); + tree->textGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + + TreeComputeGeometry(tree); + Tree_InvalidateColumnWidth(tree, -1); + Tree_RelayoutWindow(tree); +} + +static void TreeEventProc(ClientData clientData, XEvent *eventPtr) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + + if (eventPtr->type == Expose) + { + int x = eventPtr->xexpose.x; + int y = eventPtr->xexpose.y; + Tree_RedrawArea(tree, x, y, + x + eventPtr->xexpose.width, + y + eventPtr->xexpose.height); + } + else if (eventPtr->type == ConfigureNotify) + { + if ((tree->prevWidth != Tk_Width(tree->tkwin)) || + (tree->prevHeight != Tk_Height(tree->tkwin))) + { + tree->widthOfColumns = -1; + Tree_RelayoutWindow(tree); + tree->prevWidth = Tk_Width(tree->tkwin); + tree->prevHeight = Tk_Height(tree->tkwin); + } + } + else if (eventPtr->type == FocusIn) + { + if (eventPtr->xfocus.detail != NotifyInferior) + Tree_FocusChanged(tree, 1); + } + else if (eventPtr->type == FocusOut) + { + if (eventPtr->xfocus.detail != NotifyInferior) + Tree_FocusChanged(tree, 0); + } + else if (eventPtr->type == DestroyNotify) + { + if (!tree->deleted) + { + tree->deleted = 1; + Tcl_DeleteCommandFromToken(tree->interp, tree->widgetCmd); + Tcl_EventuallyFree((ClientData) tree, TreeDestroy); + } + } +} + +static void TreeCmdDeletedProc(ClientData clientData) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + + if (!tree->deleted) + { + Tk_DestroyWindow(tree->tkwin); + } +} + +static void TreeDestroy(char *memPtr) +{ + TreeCtrl *tree = (TreeCtrl *) memPtr; + TreeItem item; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tk_Image image; + int i; + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + TreeItem_FreeResources(tree, item); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(&tree->itemHash); + + hPtr = Tcl_FirstHashEntry(&tree->imageHash, &search); + while (hPtr != NULL) + { + image = (Tk_Image) Tcl_GetHashValue(hPtr); + Tk_FreeImage(image); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(&tree->imageHash); + + TreeStyle_Free(tree); + + TreeDragImage_Free(tree->dragImage); + TreeMarquee_Free(tree->marquee); + TreeDInfo_Free(tree); + + if (tree->copyGC != None) + Tk_FreeGC(tree->display, tree->copyGC); + if (tree->textGC != None) + Tk_FreeGC(tree->display, tree->textGC); + if (tree->buttonGC != None) + Tk_FreeGC(tree->display, tree->buttonGC); + if (tree->lineGC != None) + Tk_FreeGC(tree->display, tree->lineGC); + if (tree->buttonClosedGC != None) + Tk_FreeGC(tree->display, tree->buttonClosedGC); + if (tree->buttonOpenGC != None) + Tk_FreeGC(tree->display, tree->buttonOpenGC); + + Tree_FreeColumns(tree); + + QE_DeleteBindingTable(tree->bindingTable); + + for (i = STATE_USER - 1; i < 32; i++) + if (tree->stateNames[i] != NULL) + ckfree(tree->stateNames[i]); + + Tk_FreeConfigOptions((char *) tree, tree->debug.optionTable, + tree->tkwin); + + Tk_FreeConfigOptions((char *) tree, tree->optionTable, tree->tkwin); + + WFREE(tree, TreeCtrl); +} + +void Tree_UpdateScrollbarX(TreeCtrl *tree) +{ + Tcl_Interp *interp = tree->interp; + int result; + double fractions[2]; + char buffer[TCL_DOUBLE_SPACE * 2]; + char *xScrollCmd; + + Tree_GetScrollFractionsX(tree, fractions); + TreeNotify_Scroll(tree, fractions, FALSE); + + if (tree->xScrollCmd == NULL) + return; + + Tcl_Preserve((ClientData) interp); + Tcl_Preserve((ClientData) tree); + + xScrollCmd = tree->xScrollCmd; + Tcl_Preserve((ClientData) xScrollCmd); + sprintf(buffer, "%g %g", fractions[0], fractions[1]); + result = Tcl_VarEval(interp, xScrollCmd, " ", buffer, (char *) NULL); + if (result != TCL_OK) + Tcl_BackgroundError(interp); + Tcl_ResetResult(interp); + Tcl_Release((ClientData) xScrollCmd); + + Tcl_Release((ClientData) tree); + Tcl_Release((ClientData) interp); +} + +void Tree_UpdateScrollbarY(TreeCtrl *tree) +{ + Tcl_Interp *interp = tree->interp; + int result; + double fractions[2]; + char buffer[TCL_DOUBLE_SPACE * 2]; + char *yScrollCmd; + + Tree_GetScrollFractionsY(tree, fractions); + TreeNotify_Scroll(tree, fractions, TRUE); + + if (tree->yScrollCmd == NULL) + return; + + Tcl_Preserve((ClientData) interp); + Tcl_Preserve((ClientData) tree); + + yScrollCmd = tree->yScrollCmd; + Tcl_Preserve((ClientData) yScrollCmd); + sprintf(buffer, "%g %g", fractions[0], fractions[1]); + result = Tcl_VarEval(interp, yScrollCmd, " ", buffer, (char *) NULL); + if (result != TCL_OK) + Tcl_BackgroundError(interp); + Tcl_ResetResult(interp); + Tcl_Release((ClientData) yScrollCmd); + + Tcl_Release((ClientData) tree); + Tcl_Release((ClientData) interp); +} + +static void TreeComputeGeometry(TreeCtrl *tree) +{ + Tk_SetInternalBorder(tree->tkwin, tree->inset); + Tk_GeometryRequest(tree->tkwin, tree->width + tree->inset * 2, + tree->height + tree->inset * 2); +} + +void Tree_AddItem(TreeCtrl *tree, TreeItem item) +{ + Tcl_HashEntry *hPtr; + int id, isNew; + + id = TreeItem_SetID(tree, item, tree->nextItemId++); + hPtr = Tcl_CreateHashEntry(&tree->itemHash, (char *) id, &isNew); + Tcl_SetHashValue(hPtr, item); + tree->itemCount++; +} + +void Tree_RemoveItem(TreeCtrl *tree, TreeItem item) +{ + Tcl_HashEntry *hPtr; + + if (TreeItem_GetSelected(tree, item)) + Tree_RemoveFromSelection(tree, item); + + hPtr = Tcl_FindHashEntry(&tree->itemHash, + (char *) TreeItem_GetID(tree, item)); + Tcl_DeleteHashEntry(hPtr); + tree->itemCount--; + if (tree->itemCount == 1) + tree->nextItemId = TreeItem_GetID(tree, tree->root) + 1; +} + +/* Called when Tk_Image is deleted or modified */ +static void ImageChangedProc( + ClientData clientData, + int x, int y, + int width, int height, + int imageWidth, int imageHeight) +{ + /* I would like to know the image was deleted... */ +} + +Tk_Image Tree_GetImage(TreeCtrl *tree, char *imageName) +{ + Tcl_HashEntry *hPtr; + Tk_Image image; + int isNew; + + hPtr = Tcl_CreateHashEntry(&tree->imageHash, imageName, &isNew); + if (isNew) + { + image = Tk_GetImage(tree->interp, tree->tkwin, imageName, + ImageChangedProc, (ClientData) tree); + if (image == NULL) + { + Tcl_DeleteHashEntry(hPtr); + return NULL; + } + Tcl_SetHashValue(hPtr, image); + } + return (Tk_Image) Tcl_GetHashValue(hPtr); +} + +static int TreeStateCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Interp *interp = tree->interp; + static CONST char *commandName[] = { "define", "undefine", "names", + (char *) NULL }; + enum { + COMMAND_DEFINE, COMMAND_UNDEFINE, COMMAND_NAMES + }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandName, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_DEFINE: + { + char *string; + int i, length, slot = -1; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "stateName"); + return TCL_ERROR; + } + string = Tcl_GetStringFromObj(objv[3], &length); + for (i = 0; i < 32; i++) + { + if (tree->stateNames[i] == NULL) + { + if (slot == -1) + slot = i; + continue; + } + if (strcmp(tree->stateNames[i], string) == 0) + { + FormatResult(interp, "state \"%s\" already defined", string); + return TCL_ERROR; + } + } + if (slot == -1) + { + FormatResult(interp, "cannot define any more states"); + return TCL_ERROR; + } + tree->stateNames[slot] = ckalloc(length + 1); + strcpy(tree->stateNames[slot], string); + break; + } + + case COMMAND_NAMES: + { + Tcl_Obj *listObj; + int i; + + if (tree->stateNames == NULL) + break; + listObj = Tcl_NewListObj(0, NULL); + for (i = STATE_USER - 1; i < 32; i++) + { + if (tree->stateNames[i] != NULL) + Tcl_ListObjAppendElement(interp, listObj, + Tcl_NewStringObj(tree->stateNames[i], -1)); + } + Tcl_SetObjResult(interp, listObj); + break; + } + + case COMMAND_UNDEFINE: + { + char *string; + int i, length; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "stateName"); + return TCL_ERROR; + } + string = Tcl_GetStringFromObj(objv[3], &length); + for (i = 0; i < 32; i++) + { + if (tree->stateNames[i] == NULL) + continue; + if (strcmp(tree->stateNames[i], string) == 0) + break; + } + if (i < STATE_USER - 1) + { + FormatResult(interp, "state \"%s\" cannot be undefined", + string); + return TCL_ERROR; + } + if (i == 32) + { + FormatResult(interp, "unknown state \"%s\"", string); + return TCL_ERROR; + } + TreeStyle_UndefineState(tree, 1L << i); + ckfree(tree->stateNames[i]); + tree->stateNames[i] = NULL; + break; + } + } + + return TCL_OK; +} + +void Tree_AddToSelection(TreeCtrl *tree, TreeItem item) +{ + if (TreeItem_GetSelected(tree, item)) + panic("Tree_AddToSelection: item %d already selected", + TreeItem_GetID(tree, item)); + TreeItem_ChangeState(tree, item, 0, STATE_SELECTED); + tree->selectCount++; +} + +void Tree_RemoveFromSelection(TreeCtrl *tree, TreeItem item) +{ + if (!TreeItem_GetSelected(tree, item)) + panic("Tree_RemoveFromSelection: item %d isn't selected", + TreeItem_GetID(tree, item)); + TreeItem_ChangeState(tree, item, STATE_SELECTED, 0); + tree->selectCount--; +} + +static int TreeSelectionCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + static CONST char *commandName[] = { "add", "anchor", "clear", "count", + "get", "includes", "modify", NULL }; + enum { + COMMAND_ADD, COMMAND_ANCHOR, COMMAND_CLEAR, COMMAND_COUNT, + COMMAND_GET, COMMAND_INCLUDES, COMMAND_MODIFY + }; + int index; + TreeItem item, itemFirst, itemLast; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandName, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_ADD: + { + int indexFirst, indexLast, count; + TreeItem staticItems[20], *items = staticItems; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + if (objc < 4 || objc > 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "first ?last?"); + return TCL_ERROR; + } + if (TreeItem_FromObj(tree, objv[3], &itemFirst, IFO_ALLOK) != TCL_OK) + return TCL_ERROR; + itemLast = NULL; + if (objc == 5) + { + if (TreeItem_FromObj(tree, objv[4], &itemLast, IFO_ALLOK) != TCL_OK) + return TCL_ERROR; + if (TreeItem_RootAncestor(tree, itemFirst) != + TreeItem_RootAncestor(tree, itemLast)) + { + FormatResult(interp, + "item %d and item %d don't share a common ancestor", + TreeItem_GetID(tree, itemFirst), + TreeItem_GetID(tree, itemLast)); + return TCL_ERROR; + } + } + if ((itemFirst == ITEM_ALL) || (itemLast == ITEM_ALL)) + { + count = tree->itemCount - tree->selectCount; + if (count + 1 > sizeof(staticItems) / sizeof(staticItems[0])) + items = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + + /* Include detached items */ + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (!TreeItem_GetSelected(tree, item)) + { + Tree_AddToSelection(tree, item); + items[count++] = item; + } + hPtr = Tcl_NextHashEntry(&search); + } + goto doneADD; + } + if (objc == 4) + { + itemLast = itemFirst; + count = 1; + } + else + { + TreeItem_ToIndex(tree, itemFirst, &indexFirst, NULL); + TreeItem_ToIndex(tree, itemLast, &indexLast, NULL); + if (indexFirst > indexLast) + { + item = itemFirst; + itemFirst = itemLast; + itemLast = item; + + index = indexFirst; + indexFirst = indexLast; + indexLast = index; + } + count = indexLast - indexFirst + 1; + } + if (count + 1 > sizeof(staticItems) / sizeof(staticItems[0])) + items = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + item = itemFirst; + while (item != NULL) + { + if (!TreeItem_GetSelected(tree, item)) + { + Tree_AddToSelection(tree, item); + items[count++] = item; + } + if (item == itemLast) + break; + item = TreeItem_Next(tree, item); + } +doneADD: + if (count) + { + items[count] = NULL; + TreeNotify_Selection(tree, items, NULL); + } + if (items != staticItems) + ckfree((char *) items); + break; + } + + case COMMAND_ANCHOR: + { + if (objc != 3 && objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "?index?"); + return TCL_ERROR; + } + if (objc == 4) + { + if (TreeItem_FromObj(tree, objv[3], &item, 0) != TCL_OK) + { + return TCL_ERROR; + } + tree->anchorItem = item; + } + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, tree->anchorItem)); + break; + } + + case COMMAND_CLEAR: + { + int indexFirst, indexLast, count; + TreeItem staticItems[20], *items = staticItems; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + if (objc > 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "?first? ?last?"); + return TCL_ERROR; + } + itemFirst = itemLast = NULL; + if (objc >= 4) + { + if (TreeItem_FromObj(tree, objv[3], &itemFirst, IFO_ALLOK) != TCL_OK) + return TCL_ERROR; + } + if (objc == 5) + { + if (TreeItem_FromObj(tree, objv[4], &itemLast, IFO_ALLOK) != TCL_OK) + return TCL_ERROR; + if (TreeItem_RootAncestor(tree, itemFirst) != + TreeItem_RootAncestor(tree, itemLast)) + { + FormatResult(interp, + "item %d and item %d don't share a common ancestor", + TreeItem_GetID(tree, itemFirst), + TreeItem_GetID(tree, itemLast)); + return TCL_ERROR; + } + } + if ((objc == 3) || (itemFirst == ITEM_ALL) || (itemLast == ITEM_ALL)) + { + count = tree->selectCount; + if (count + 1 > sizeof(staticItems) / sizeof(staticItems[0])) + items = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + + /* Include detached items */ + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (TreeItem_GetSelected(tree, item)) + { + Tree_RemoveFromSelection(tree, item); + items[count++] = item; + } + hPtr = Tcl_NextHashEntry(&search); + } + goto doneCLEAR; + } + if (objc == 4) + { + itemLast = itemFirst; + count = 1; + } + else + { + TreeItem_ToIndex(tree, itemFirst, &indexFirst, NULL); + TreeItem_ToIndex(tree, itemLast, &indexLast, NULL); + if (indexFirst > indexLast) + { + item = itemFirst; + itemFirst = itemLast; + itemLast = item; + + index = indexFirst; + indexFirst = indexLast; + indexLast = index; + } + count = indexLast - indexFirst + 1; + } + if (count + 1 > sizeof(staticItems) / sizeof(staticItems[0])) + items = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + item = itemFirst; + while (item != NULL) + { + if (TreeItem_GetSelected(tree, item)) + { + Tree_RemoveFromSelection(tree, item); + items[count++] = item; + } + if (item == itemLast) + break; + item = TreeItem_Next(tree, item); + } +doneCLEAR: + if (count) + { + items[count] = NULL; + TreeNotify_Selection(tree, NULL, items); + } + if (items != staticItems) + ckfree((char *) items); + break; + } + + case COMMAND_COUNT: + { + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(tree->selectCount)); + break; + } + + case COMMAND_GET: + { + TreeItem item; + Tcl_Obj *listObj; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); + return TCL_ERROR; + } + if (tree->selectCount < 1) + break; + listObj = Tcl_NewListObj(0, NULL); + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (TreeItem_GetSelected(tree, item)) + { + Tcl_ListObjAppendElement(interp, listObj, + TreeItem_ToObj(tree, item)); + } + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_SetObjResult(interp, listObj); + break; + } + + case COMMAND_INCLUDES: + { + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "index"); + return TCL_ERROR; + } + if (TreeItem_FromObj(tree, objv[3], &item, 0) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + TreeItem_GetSelected(tree, item))); + break; + } + + case COMMAND_MODIFY: + { + int i, j, count, objcS, objcD; + Tcl_Obj **objvS, **objvD; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + TreeItem item; +#define STATIC_SIZE 20 + TreeItem staticItemS[STATIC_SIZE], *itemS = staticItemS; + TreeItem staticItemD[STATIC_SIZE], *itemD = staticItemD; + TreeItem staticNewS[STATIC_SIZE], *newS = staticNewS; + TreeItem staticNewD[STATIC_SIZE], *newD = staticNewD; + int allS = FALSE, allD = FALSE; + int countS, countD; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "select deselect"); + return TCL_ERROR; + } + if (Tcl_ListObjGetElements(interp, objv[3], &objcS, &objvS) != TCL_OK) + return TCL_ERROR; + if (Tcl_ListObjGetElements(interp, objv[4], &objcD, &objvD) != TCL_OK) + return TCL_ERROR; + + /* No change */ + if (!objcS && !objcD) + break; + + /* List of items to select */ + if (objcS > 0) + { + if (objcS > STATIC_SIZE) + itemS = (TreeItem *) ckalloc(sizeof(TreeItem) * objcS); + for (i = 0; i < objcS; i++) + { + if (TreeItem_FromObj(tree, objvS[i], &itemS[i], IFO_ALLOK) != TCL_OK) + { + if (itemS != staticItemS) + ckfree((char *) itemS); + return TCL_ERROR; + } + if (itemS[i] == ITEM_ALL) + { + if (itemS != staticItemS) + ckfree((char *) itemS); + allS = TRUE; + break; + } + } + } + + /* Select all */ + if (allS) + { + count = tree->itemCount - tree->selectCount; + if (count + 1 > STATIC_SIZE) + newS = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + + /* Include detached items */ + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (!TreeItem_GetSelected(tree, item)) + { + Tree_AddToSelection(tree, item); + newS[count++] = item; + } + hPtr = Tcl_NextHashEntry(&search); + } + if (count) + { + newS[count] = NULL; + TreeNotify_Selection(tree, newS, NULL); + } + if (newS != staticNewS) + ckfree((char *) newS); + break; + } + + /* Select some, deselect none */ + if ((objcS > 0) && (objcD == 0)) + { + if (objcS + 1 > STATIC_SIZE) + newS = (TreeItem *) ckalloc(sizeof(TreeItem) * (objcS + 1)); + count = 0; + for (i = 0; i < objcS; i++) + { + item = itemS[i]; + if (TreeItem_GetSelected(tree, item)) + continue; + /* Add unique item to newly-selected list */ + for (j = 0; j < count; j++) + if (newS[j] == item) + break; + if (j == count) + { + Tree_AddToSelection(tree, item); + newS[count++] = item; + } + } + if (count) + { + newS[count] = NULL; + TreeNotify_Selection(tree, newS, NULL); + } + if (newS != staticNewS) + ckfree((char *) newS); + if (itemS != staticItemS) + ckfree((char *) itemS); + break; + } + + /* Nothing to deselect */ + if (objcD == 0) + break; + + /* List of items to deselect */ + if (objcD > STATIC_SIZE) + itemD = (TreeItem *) ckalloc(sizeof(TreeItem) * objcD); + for (i = 0; i < objcD; i++) + { + if (TreeItem_FromObj(tree, objvD[i], &itemD[i], IFO_ALLOK) != TCL_OK) + { + if (itemS != staticItemS) + ckfree((char *) itemS); + if (itemD != staticItemD) + ckfree((char *) itemD); + return TCL_ERROR; + } + if (itemD[i] == ITEM_ALL) + { + allD = TRUE; + break; + } + } + + /* Select none, Deselect all */ + if ((objcS == 0) && allD) + { + count = tree->selectCount; + if (count + 1 > STATIC_SIZE) + newD = (TreeItem *) ckalloc(sizeof(TreeItem) * (count + 1)); + count = 0; + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (TreeItem_GetSelected(tree, item)) + { + Tree_RemoveFromSelection(tree, item); + newD[count++] = item; + } + hPtr = Tcl_NextHashEntry(&search); + } + if (count) + { + newD[count] = NULL; + TreeNotify_Selection(tree, NULL, newD); + } + if (newD != staticNewD) + ckfree((char *) newD); + break; + } + + /* Select none, deselect some */ + if ((objcS == 0) && !allD) + { + if (objcD + 1 > STATIC_SIZE) + newD = (TreeItem *) ckalloc(sizeof(TreeItem) * (objcD + 1)); + count = 0; + for (i = 0; i < objcD; i++) + { + item = itemD[i]; + if (!TreeItem_GetSelected(tree, item)) + continue; + /* Add unique item to newly-deselected list */ + for (j = 0; j < count; j++) + if (newD[j] == item) + break; + if (j == count) + { + Tree_RemoveFromSelection(tree, item); + newD[count++] = item; + } + } + if (count) + { + newD[count] = NULL; + TreeNotify_Selection(tree, NULL, newD); + } + if (newD != staticNewD) + ckfree((char *) newD); + if (itemD != staticItemD) + ckfree((char *) itemD); + break; + } + + /* Select some, deselect some */ + if ((objcS > 0) && !allD) + { + if (objcS + 1 > STATIC_SIZE) + newS = (TreeItem *) ckalloc(sizeof(TreeItem) * (objcS + 1)); + if (objcD + 1 > STATIC_SIZE) + newD = (TreeItem *) ckalloc(sizeof(TreeItem) * (objcD + 1)); + countS = 0; + for (i = 0; i < objcS; i++) + { + item = itemS[i]; + if (TreeItem_GetSelected(tree, item)) + continue; + /* Add unique item to newly-selected list */ + for (j = 0; j < countS; j++) + if (newS[j] == item) + break; + if (j == countS) + { + Tree_AddToSelection(tree, item); + newS[countS++] = item; + } + } + countD = 0; + for (i = 0; i < objcD; i++) + { + item = itemD[i]; + if (!TreeItem_GetSelected(tree, item)) + continue; + /* Don't deselect an item in the selected list */ + for (j = 0; j < objcS; j++) + if (item == itemS[j]) + break; + if (j != objcS) + continue; + /* Add unique item to newly-deselected list */ + for (j = 0; j < countD; j++) + if (newD[j] == item) + break; + if (j == countD) + { + Tree_RemoveFromSelection(tree, item); + newD[countD++] = item; + } + } + if (countS || countD) + { + newS[countS] = NULL; + newD[countD] = NULL; + TreeNotify_Selection(tree, newS, newD); + } + if (newS != staticNewS) + ckfree((char *) newS); + if (itemS != staticItemS) + ckfree((char *) itemS); + if (newD != staticNewD) + ckfree((char *) newD); + if (itemD != staticItemD) + ckfree((char *) itemD); + break; + } + + /* Select some, deselect all */ + countD = tree->selectCount; + if (countD + 1 > STATIC_SIZE) + newD = (TreeItem *) ckalloc(sizeof(TreeItem) * (countD + 1)); + countD = 0; + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + if (TreeItem_GetSelected(tree, item)) + { + /* Don't deselect an item in the selected list */ + for (j = 0; j < objcS; j++) + if (item == itemS[j]) + break; + if (j == objcS) + { + Tree_RemoveFromSelection(tree, item); + newD[countD++] = item; + } + } + hPtr = Tcl_NextHashEntry(&search); + } + if (objcS + 1 > STATIC_SIZE) + newS = (TreeItem *) ckalloc(sizeof(TreeItem) * (objcS + 1)); + countS = 0; + for (i = 0; i < objcS; i++) + { + item = itemS[i]; + if (TreeItem_GetSelected(tree, item)) + continue; + /* Add unique item to newly-selected list */ + for (j = 0; j < countS; j++) + if (newS[j] == item) + break; + if (j == countS) + { + Tree_AddToSelection(tree, item); + newS[countS++] = item; + } + } + if (countS || countD) + { + newS[countS] = NULL; + newD[countD] = NULL; + TreeNotify_Selection(tree, newS, newD); + } + if (newS != staticNewS) + ckfree((char *) newS); + if (itemS != staticItemS) + ckfree((char *) itemS); + if (newD != staticNewD) + ckfree((char *) newD); + if (itemD != staticItemD) + ckfree((char *) itemD); + + break; + } + } + + return TCL_OK; +} + +static int A_XviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Interp *interp = tree->interp; + + if (objc == 2) + { + double fractions[2]; + char buf[TCL_DOUBLE_SPACE * 2]; + + Tree_GetScrollFractionsX(tree, fractions); + sprintf(buf, "%g %g", fractions[0], fractions[1]); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + else + { + int count, index = 0, indexMax, offset, type; + double fraction; + int visWidth = Tk_Width(tree->tkwin) - 2 * tree->inset; + int totWidth = Tree_TotalWidth(tree); + int xIncr = tree->xScrollIncrement; + + if (totWidth <= visWidth) + return TCL_OK; + + if (visWidth > 1) + { + /* Find incrementLeft when scrolled to extreme right */ + indexMax = Increment_FindX(tree, totWidth - visWidth); + offset = Increment_ToOffsetX(tree, indexMax); + if (offset < totWidth - visWidth) + { + indexMax++; + offset = Increment_ToOffsetX(tree, indexMax); + } + + /* Add some fake content to right */ + if (offset + visWidth > totWidth) + totWidth = offset + visWidth; + } + else + { + indexMax = Increment_FindX(tree, totWidth); + visWidth = 1; + } + + type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); + switch (type) + { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + offset = (int) (fraction * totWidth + 0.5); + index = Increment_FindX(tree, offset); + break; + case TK_SCROLL_PAGES: + offset = tree->inset + tree->xOrigin; + offset += (int) (count * visWidth * 0.9); + index = Increment_FindX(tree, offset); + break; + case TK_SCROLL_UNITS: + offset = tree->inset + tree->xOrigin; + index = offset / xIncr; + index += count; + break; + } + + /* Don't scroll too far left */ + if (index < 0) + index = 0; + + /* Don't scroll too far right */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetX(tree, index); + if (offset - tree->inset != tree->xOrigin) + { + tree->xOrigin = offset - tree->inset; + Tree_EventuallyRedraw(tree); + } + } + return TCL_OK; +} + +static int A_YviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Interp *interp = tree->interp; + + if (objc == 2) + { + double fractions[2]; + char buf[TCL_DOUBLE_SPACE * 2]; + + Tree_GetScrollFractionsY(tree, fractions); + sprintf(buf, "%g %g", fractions[0], fractions[1]); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + else + { + int count, index = 0, indexMax, offset, type; + double fraction; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totHeight = Tree_TotalHeight(tree); + int yIncr = tree->yScrollIncrement; + + if (totHeight <= visHeight) + return TCL_OK; + + if (visHeight > 1) + { + /* Find incrementTop when scrolled to bottom */ + indexMax = Increment_FindY(tree, totHeight - visHeight); + offset = Increment_ToOffsetY(tree, indexMax); + if (offset < totHeight - visHeight) + { + indexMax++; + offset = Increment_ToOffsetY(tree, indexMax); + } + + /* Add some fake content to bottom */ + if (offset + visHeight > totHeight) + totHeight = offset + visHeight; + } + else + { + indexMax = Increment_FindY(tree, totHeight); + visHeight = 1; + } + + type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); + switch (type) + { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + offset = (int) (fraction * totHeight + 0.5); + index = Increment_FindY(tree, offset); + break; + case TK_SCROLL_PAGES: + offset = topInset + tree->yOrigin; + offset += (int) (count * visHeight * 0.9); + index = Increment_FindY(tree, offset); + break; + case TK_SCROLL_UNITS: + offset = topInset + tree->yOrigin; + index = offset / yIncr; + index += count; + break; + } + + /* Don't scroll too far left */ + if (index < 0) + index = 0; + + /* Don't scroll too far right */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetY(tree, index); + if (offset - topInset != tree->yOrigin) + { + tree->yOrigin = offset - topInset; + Tree_EventuallyRedraw(tree); + } + } + return TCL_OK; +} + +static int TreeXviewCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ +#ifdef INCREMENTS + if (tree->xScrollIncrement <= 0) + return B_XviewCmd(tree, objc, objv); +#endif + return A_XviewCmd(tree, objc, objv); +} + +static int TreeYviewCmd(Tcl_Interp *interp, TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ +#ifdef INCREMENTS + if (tree->yScrollIncrement <= 0) + return B_YviewCmd(tree, objc, objv); +#endif + return A_YviewCmd(tree, objc, objv); +} + +void Tree_Debug(TreeCtrl *tree) +{ + if (TreeItem_Debug(tree, tree->root) != TCL_OK) + { + dbwin("Tree_Debug: %s\n", Tcl_GetStringResult(tree->interp)); + Tcl_BackgroundError(tree->interp); + } +} + +static int TreeDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "cget", "configure", "dinfo", + "scroll", (char *) NULL }; + enum { COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DINFO, COMMAND_SCROLL }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T debug cget option */ + case COMMAND_CGET: + { + Tcl_Obj *resultObjPtr; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "option"); + return TCL_ERROR; + } + resultObjPtr = Tk_GetOptionValue(interp, (char *) tree, + tree->debug.optionTable, objv[3], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + /* T debug configure ?option? ?value? ?option value ...? */ + case COMMAND_CONFIGURE: + { + Tcl_Obj *resultObjPtr; + Tk_SavedOptions savedOptions; + int mask, result; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); + return TCL_ERROR; + } + if (objc <= 4) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) tree, + tree->debug.optionTable, + (objc == 3) ? (Tcl_Obj *) NULL : objv[3], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + result = Tk_SetOptions(interp, (char *) tree, + tree->debug.optionTable, objc - 3, objv + 3, tree->tkwin, + &savedOptions, &mask); + if (result != TCL_OK) + { + Tk_RestoreSavedOptions(&savedOptions); + return TCL_ERROR; + } + Tk_FreeSavedOptions(&savedOptions); + if (tree->debug.eraseColor != NULL) + { + tree->debug.gcErase = Tk_GCForColor(tree->debug.eraseColor, + Tk_WindowId(tree->tkwin)); + } + break; + } + + case COMMAND_DINFO: + { + extern void DumpDInfo(TreeCtrl *tree); + DumpDInfo(tree); + break; + } + + case COMMAND_SCROLL: + { + int topInset = Tree_HeaderHeight(tree) + tree->inset; + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totHeight = Tree_TotalHeight(tree); + int yIncr = tree->yScrollIncrement; + if (yIncr <= 0) + yIncr = tree->itemHeight; + if (yIncr <= 0) + yIncr = 1; + FormatResult(interp, "visHeight %d totHeight %d visHeight %% yIncr %d totHeight %% yIncr %d", + visHeight, + totHeight, + visHeight % yIncr, + totHeight % yIncr + ); + break; + } + } + + return TCL_OK; +} + +/* +textlayout $font $text + -width pixels + -wrap word|char + -justify left|center|right + -ignoretabs boolean + -ignorenewlines boolean +*/ +int TextLayoutCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + Tk_Font tkfont; + Tk_Window tkwin = Tk_MainWindow(interp); + char *text; + int textLen; + int flags = 0; + Tk_Justify justify = TK_JUSTIFY_LEFT; + Tk_TextLayout layout; + int width = 0, height; + int result = TCL_OK; + int i; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 1, objv, "font text ?options ...?"); + return TCL_ERROR; + } + + tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[1]); + if (tkfont == NULL) + return TCL_ERROR; + text = Tcl_GetStringFromObj(objv[2], &textLen); + + for (i = 3; i < objc; i += 2) + { + static CONST char *optionNames[] = { "-ignoretabs", "-ignorenewlines", + "-justify", "-width", (char *) NULL }; + enum { OPT_IGNORETABS, OPT_IGNORENEWLINES, OPT_JUSTIFY, OPT_WIDTH }; + int index; + + if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option", 0, + &index) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + + if (i + 1 == objc) + { + FormatResult(interp, "missing value for \"%s\" option", + optionNames[index]); + goto done; + } + + switch (index) + { + case OPT_IGNORENEWLINES: + { + int v; + if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &v) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + if (v) + flags |= TK_IGNORE_NEWLINES; + else + flags &= ~TK_IGNORE_NEWLINES; + break; + } + case OPT_IGNORETABS: + { + int v; + if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &v) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + if (v) + flags |= TK_IGNORE_TABS; + else + flags &= ~TK_IGNORE_TABS; + break; + } + case OPT_JUSTIFY: + { + if (Tk_GetJustifyFromObj(interp, objv[i + 1], &justify) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + break; + } + case OPT_WIDTH: + { + if (Tk_GetPixelsFromObj(interp, tkwin, objv[i + 1], &width) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + break; + } + } + } + + layout = Tk_ComputeTextLayout(tkfont, text, textLen, width, justify, flags, + &width, &height); + FormatResult(interp, "%d %d", width, height); + Tk_FreeTextLayout(layout); + +done: + Tk_FreeFont(tkfont); + return result; +} + +int ImageTintCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + char *imageName; + Tk_PhotoHandle photoH; + Tk_PhotoImageBlock photoBlock; + XColor *xColor; + unsigned char *pixelPtr, *photoPix; + int x, y, alpha, imgW, imgH, pitch; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 1, objv, "imageName color alpha"); + return TCL_ERROR; + } + + imageName = Tcl_GetStringFromObj(objv[1], NULL); + photoH = Tk_FindPhoto(interp, imageName); + if (photoH == NULL) + { + Tcl_AppendResult(interp, "image \"", imageName, + "\" doesn't exist or is not a photo image", + (char *) NULL); + return TCL_ERROR; + } + + xColor = Tk_AllocColorFromObj(interp, Tk_MainWindow(interp), objv[2]); + if (xColor == NULL) + return TCL_ERROR; + + if (Tcl_GetIntFromObj(interp, objv[3], &alpha) != TCL_OK) + return TCL_ERROR; + if (alpha < 0) + alpha = 0; + if (alpha > 255) + alpha = 255; + + Tk_PhotoGetImage(photoH, &photoBlock); + photoPix = photoBlock.pixelPtr; + imgW = photoBlock.width; + imgH = photoBlock.height; + pitch = photoBlock.pitch; + + pixelPtr = Tcl_Alloc(imgW * 4); + photoBlock.pixelPtr = pixelPtr; + photoBlock.width = imgW; + photoBlock.height = 1; + photoBlock.pitch = imgW * 4; + photoBlock.pixelSize = 4; + photoBlock.offset[0] = 0; + photoBlock.offset[1] = 1; + photoBlock.offset[2] = 2; + photoBlock.offset[3] = 3; + + for (x = 0; x < imgW; x++) + { + pixelPtr[x * 4 + 0] = ((double) xColor->red / USHRT_MAX) * 255; + pixelPtr[x * 4 + 1] = ((double) xColor->green / USHRT_MAX) * 255; + pixelPtr[x * 4 + 2] = ((double) xColor->blue / USHRT_MAX) * 255; + } + for (y = 0; y < imgH; y++) + { + for (x = 0; x < imgW; x++) + { + if (photoPix[x * 4 + 3]) + pixelPtr[x * 4 + 3] = alpha; + else + pixelPtr[x * 4 + 3] = 0; + } + Tk_PhotoPutBlock(photoH, &photoBlock, 0, y, + imgW, 1, TK_PHOTO_COMPOSITE_OVERLAY); +#if 1 + photoPix += pitch; +#else + { + Tk_PhotoImageBlock photoBlock; + Tk_PhotoGetImage(photoH, &photoBlock); + photoPix = photoBlock.pixelPtr; + photoPix += photoBlock.pitch * (y + 1); + } +#endif + } + Tcl_Free((char *) photoBlock.pixelPtr); + + return TCL_OK; +} + +#ifndef WIN32 + +int LoupeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + Tk_Window tkwin = Tk_MainWindow(interp); + Display *display = Tk_Display(tkwin); + int screenNum = Tk_ScreenNumber(tkwin); + Visual *visual = Tk_Visual(tkwin); + Window rootWindow = RootWindow(display, screenNum); + int displayW = DisplayWidth(display, screenNum); + int displayH = DisplayHeight(display, screenNum); + char *imageName; + Tk_PhotoHandle photoH; + Tk_PhotoImageBlock photoBlock; + unsigned char *pixelPtr; + int x, y, w, h, zoom; + int grabX, grabY, grabW, grabH; + XImage *ximage; + int i, ncolors; + XColor *xcolors; + unsigned long red_shift, green_shift, blue_shift; + + if (objc != 7) + { + Tcl_WrongNumArgs(interp, 1, objv, "imageName x y w h zoom"); + return TCL_ERROR; + } + + imageName = Tcl_GetStringFromObj(objv[1], NULL); + photoH = Tk_FindPhoto(interp, imageName); + if (photoH == NULL) + { + Tcl_AppendResult(interp, "image \"", imageName, + "\" doesn't exist or is not a photo image", + (char *) NULL); + return TCL_ERROR; + } + + if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[4], &w) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[5], &h) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[6], &zoom) != TCL_OK) + return TCL_ERROR; + + grabX = x - w / zoom / 2; + grabY = y - h / zoom / 2; + grabW = w / zoom; + grabH = h / zoom; + if (grabW > displayW) + grabW = displayW; + if (grabH > displayH) + grabH = displayH; + if (grabX < 0) + grabX = 0; + if (grabY < 0) + grabY = 0; + if (grabX + grabW > displayW) + grabX = displayW - grabW; + if (grabY + grabH > displayH) + grabY = displayH - grabH; + ximage = XGetImage(display, rootWindow, + grabX, grabY, grabW, grabH, AllPlanes, ZPixmap); + if (ximage == NULL) + { + FormatResult(interp, "XGetImage() failed"); + return TCL_ERROR; + } + + /* See TkPoscriptImage */ + + ncolors = visual->map_entries; + xcolors = (XColor *) ckalloc(sizeof(XColor) * ncolors); + + red_shift = green_shift = blue_shift = 0; + while ((0x0001 & (ximage->red_mask >> red_shift)) == 0) + red_shift++; + while ((0x0001 & (ximage->green_mask >> green_shift)) == 0) + green_shift++; + while ((0x0001 & (ximage->blue_mask >> blue_shift)) == 0) + blue_shift++; + + if ((visual->class == DirectColor) || (visual->class == TrueColor)) + { + for (i = 0; i < ncolors; i++) + { + xcolors[i].pixel = + ((i << red_shift) & ximage->red_mask) | + ((i << green_shift) & ximage->green_mask) | + ((i << blue_shift) & ximage->blue_mask); + } + } + else + { + for (i = 0; i < ncolors; i++) + xcolors[i].pixel = i; + } + + XQueryColors(display, Tk_Colormap(tkwin), xcolors, ncolors); + + /* XImage -> Tk_Image */ + pixelPtr = Tcl_Alloc(ximage->width * ximage->height * 4); + photoBlock.pixelPtr = pixelPtr; + photoBlock.width = ximage->width; + photoBlock.height = ximage->height; + photoBlock.pitch = ximage->width * 4; + photoBlock.pixelSize = 4; + photoBlock.offset[0] = 0; + photoBlock.offset[1] = 1; + photoBlock.offset[2] = 2; + photoBlock.offset[3] = 3; + + for (y = 0; y < ximage->height; y++) + { + for (x = 0; x < ximage->width; x++) + { + int r, g, b; + unsigned long pixel; + + pixel = XGetPixel(ximage, x, y); + r = (pixel & ximage->red_mask) >> red_shift; + g = (pixel & ximage->green_mask) >> green_shift; + b = (pixel & ximage->blue_mask) >> blue_shift; + r = ((double) xcolors[r].red / USHRT_MAX) * 255; + g = ((double) xcolors[g].green / USHRT_MAX) * 255; + b = ((double) xcolors[b].blue / USHRT_MAX) * 255; + pixelPtr[y * photoBlock.pitch + x * 4 + 0] = r; + pixelPtr[y * photoBlock.pitch + x * 4 + 1] = g; + pixelPtr[y * photoBlock.pitch + x * 4 + 2] = b; + pixelPtr[y * photoBlock.pitch + x * 4 + 3] = 255; + } + } + + Tk_PhotoPutZoomedBlock(photoH, &photoBlock, 0, 0, w, h, + zoom, zoom, 1, 1, TK_PHOTO_COMPOSITE_SET); + + Tcl_Free((char *) pixelPtr); + ckfree((char *) xcolors); + XDestroyImage(ximage); + + return TCL_OK; +} + +#endif /* not WIN32 */ + +DLLEXPORT int Treectrl_Init(Tcl_Interp *interp) +{ +#ifdef USE_TCL_STUBS + if (Tcl_InitStubs(interp, "8.4", 0) == NULL) + { + return TCL_ERROR; + } +#endif +#ifdef USE_TK_STUBS + if (Tk_InitStubs(interp, "8.4", 0) == NULL) + { + return TCL_ERROR; + } +#endif + if (TreeStyle_Init(interp) != TCL_OK) + { + return TCL_ERROR; + } + + /* Hack for editing a text Element */ + Tcl_CreateObjCommand(interp, "textlayout", TextLayoutCmd, NULL, NULL); + + /* Hack for colorizing a image (like Win98 explorer) */ + Tcl_CreateObjCommand(interp, "imagetint", ImageTintCmd, NULL, NULL); +#ifndef WIN32 + /* Screen magnifier to check those dotted lines */ + Tcl_CreateObjCommand(interp, "loupe", LoupeCmd, NULL, NULL); +#endif + Tcl_CreateObjCommand(interp, "treectrl", TreeObjCmd, NULL, NULL); + return Tcl_PkgProvide(interp, "treectrl", "1.0"); +} + diff --git a/generic/tkTreeCtrl.h b/generic/tkTreeCtrl.h new file mode 100644 index 0000000..10dbc3a --- /dev/null +++ b/generic/tkTreeCtrl.h @@ -0,0 +1,488 @@ +#include "tkPort.h" +#include "default.h" +#include "tkInt.h" +#include "qebind.h" +#include "dbwin.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define INCREMENTS + +typedef struct TreeCtrl TreeCtrl; +typedef struct TreeColumn_ *TreeColumn; +typedef struct TreeDInfo_ *TreeDInfo; +typedef struct TreeDragImage_ *TreeDragImage; +typedef struct TreeItem_ *TreeItem; +typedef struct TreeItemColumn_ *TreeItemColumn; +typedef struct TreeItemDInfo_ *TreeItemDInfo; +typedef struct TreeMarquee_ *TreeMarquee; +typedef struct TreeItemRInfo_ *TreeItemRInfo; +typedef struct TreeStyle_ *TreeStyle; + +enum { LEFT, TOP, RIGHT, BOTTOM }; + +struct TreeCtrlDebug +{ + Tk_OptionTable optionTable; + int enable; /* Turn all debugging on/off */ + int data; /* Debug data structures */ + int display; /* Debug display routines */ + int displayDelay; /* Delay between copy/draw operations */ + XColor *eraseColor; /* Erase "invalidated" areas */ + GC gcErase; /* for eraseColor */ +}; + +struct TreeCtrl +{ + /* Standard stuff */ + Tk_Window tkwin; + Display *display; + Tcl_Interp *interp; + Tcl_Command widgetCmd; + Tk_OptionTable optionTable; + + /* Configuration options */ + Tcl_Obj *fgObj; /* -foreground */ + XColor *fgColorPtr; /* -foreground */ + int borderWidth; /* -borderwidth */ + Tk_3DBorder border; /* -background */ + Tk_Cursor cursor; /* Current cursor for window, or None. */ + int relief; /* -relief */ + int highlightWidth; /* -highlightthickness */ + XColor *highlightBgColorPtr; /* -highlightbackground */ + XColor *highlightColorPtr; /* -highlightcolor */ + char *xScrollCmd; /* -xscrollcommand */ + char *yScrollCmd; /* -yscrollcommand */ + Tcl_Obj *xScrollDelay; /* -xscrolldelay: used by scripts */ + Tcl_Obj *yScrollDelay; /* -yscrolldelay: used by scripts */ + int xScrollIncrement; /* -xscrollincrement */ + int yScrollIncrement; /* -yscrollincrement */ + Tcl_Obj *scrollMargin; /* -scrollmargin: used by scripts */ + char *takeFocus; /* -takfocus */ + Tcl_Obj *fontObj; /* -font */ + Tk_Font tkfont; /* -font */ + int showButtons; /* boolean: Draw expand/collapse buttons */ + int showLines; /* boolean: Draw lines connecting parent to child */ + int showRoot; /* boolean: Draw the unique root item */ + int showRootButton; /* boolean: Draw expand/collapse button for root item */ + int showHeader; /* boolean: show column titles */ + int indent; /* pixels: offset of child relative to parent */ + char *selectMode; /* -selectmode: used by scripts only */ + int itemHeight; /* -itemheight: Fixed height for all items (unless overridden) */ + int width; /* -width */ + int height; /* -height */ + int columnTree; /* column where buttons/lines are drawn */ +#define DOUBLEBUFFER_NONE 0 +#define DOUBLEBUFFER_ITEM 1 +#define DOUBLEBUFFER_WINDOW 2 + int doubleBuffer; /* -doublebuffer */ + XColor *buttonColor; /* -buttoncolor */ + Tcl_Obj *buttonSizeObj; /* -buttonSize */ + int buttonSize; /* -buttonsize */ + Tcl_Obj *buttonThicknessObj; /* -buttonthickness */ + int buttonThickness; /* -buttonthickness */ + XColor *lineColor; /* -linecolor */ + Tcl_Obj *lineThicknessObj; /* -linethickness */ + int lineThickness; /* -linethickness */ +#define LINE_STYLE_DOT 0 +#define LINE_STYLE_SOLID 1 + int lineStyle; /* -linestyle */ + int vertical; /* -orient */ + Tcl_Obj *wrapObj; /* -wrap */ + char *openButtonString; /* -openbuttonimage */ + char *closedButtonString; /* -closedbuttonimage */ + Pixmap openButtonBitmap; /* -openbuttonbitmap */ + Pixmap closedButtonBitmap; /* -closedbuttonbitmap */ + int useIndent; /* MAX of open/closed button width and indent */ +#define BG_MODE_COLUMN 0 +#define BG_MODE_INDEX 1 +#define BG_MODE_ROW 2 +#define BG_MODE_VISINDEX 3 + int backgroundMode; /* -backgroundmode */ + int itemPad[4]; /* -itempad w/n/e/s */ + Tcl_Obj *itemPadObj[4]; /* -itempad w/n/e/s */ + + struct TreeCtrlDebug debug; + + /* Other stuff */ + int gotFocus; /* flag */ + int deleted; /* flag */ + int updateIndex; /* flag */ + int inset; /* borderWidth + highlightWidth */ + int xOrigin; /* offset from content x=0 to window x=0 */ + int yOrigin; /* offset from content y=0 to window y=0 */ + GC copyGC; + GC textGC; + GC buttonGC; + GC lineGC; + Tk_Image openButtonImage; /* -openbuttonimage */ + Tk_Image closedButtonImage; /* -closedbuttonimage */ + GC buttonOpenGC; + GC buttonClosedGC; + int closedButtonWidth; + int closedButtonHeight; + int openButtonWidth; + int openButtonHeight; + int prevWidth; + int prevHeight; + int drawableXOrigin; + int drawableYOrigin; + + TreeColumn columns; /* List of columns */ + TreeColumn columnTail; /* Last infinitely-wide column */ + TreeColumn columnVis; /* First visible column */ + int columnCount; /* Number of columns */ + int columnCountVis; /* Number of visible columns */ + int headerHeight; /* Height of column titles */ + int widthOfColumns; /* Sum of column widths */ + int columnTreeLeft; /* left of column where buttons/lines are drawn */ + int columnTreeVis; /* TRUE if columnTree is visible */ + int columnBgCnt; /* Max -itembackground colors */ + + TreeItem root; + TreeItem activeItem; + TreeItem anchorItem; + int nextItemId; + Tcl_HashTable itemHash; /* TreeItem.id -> TreeItem */ + Tcl_HashTable elementHash; /* Element.name -> Element */ + Tcl_HashTable styleHash; /* Style.name -> Style */ + Tcl_HashTable imageHash; /* image name -> Tk_Image */ + int depth; /* max depth of items under root */ + int itemCount; /* Total number of items */ + int itemVisCount; /* Total number of ReallyVisible() items */ + QE_BindingTable bindingTable; + TreeDragImage dragImage; + TreeMarquee marquee; + TreeDInfo dInfo; + int selectCount; /* Number of selected items */ + +#define TREE_WRAP_NONE 0 +#define TREE_WRAP_ITEMS 1 +#define TREE_WRAP_PIXELS 2 +#define TREE_WRAP_WINDOW 3 + int wrapMode; /* TREE_WRAP_xxx */ + int wrapArg; /* Number of items, number of pixels */ + + int totalWidth; /* Max/Sum of all TreeRanges */ + int totalHeight; /* Max/Sum of all TreeRanges */ + + struct { + Tcl_Obj *xObj; + int x; /* Window coords */ + int sx; /* Window coords */ + int onScreen; + } columnProxy; + + char *stateNames[32]; /* Names of static and dynamic item states */ +}; + +#define TREE_CONF_FONT 0x0001 +#define TREE_CONF_ITEMHEIGHT 0x0002 +#define TREE_CONF_INDENT 0x0004 +#define TREE_CONF_WRAP 0x0008 +#define TREE_CONF_BUTIMG_CLOSED 0x0010 +#define TREE_CONF_BUTIMG_OPEN 0x0020 +#define TREE_CONF_BUTBMP_CLOSED 0x0040 +#define TREE_CONF_BUTBMP_OPEN 0x0080 +#define TREE_CONF_RELAYOUT 0x0100 +#define TREE_CONF_REDISPLAY 0x0200 +#define TREE_CONF_FG 0x0400 +#define TREE_CONF_PROXY 0x0800 +#define TREE_CONF_BUTTON 0x1000 +#define TREE_CONF_LINE 0x2000 + +extern void Tree_AddItem(TreeCtrl *tree, TreeItem item); +extern void Tree_RemoveItem(TreeCtrl *tree, TreeItem item); +extern Tk_Image Tree_GetImage(TreeCtrl *tree, char *imageName); +extern void Tree_UpdateScrollbarX(TreeCtrl *tree); +extern void Tree_UpdateScrollbarY(TreeCtrl *tree); +extern void Tree_AddToSelection(TreeCtrl *tree, TreeItem item); +extern void Tree_RemoveFromSelection(TreeCtrl *tree, TreeItem item); + +/* tkTreeItem.c */ + +#define ITEM_ALL ((TreeItem) -1) +#define IFO_ALLOK 0x0001 /* ItemFromObj flag: "all" is acceptable */ +#define IFO_NULLOK 0x0002 /* ItemFromObj flag: can be NULL */ +#define IFO_NOTROOT 0x0004 /* ItemFromObj flag: "root" is forbidden */ +extern int TreeItem_FromObj(TreeCtrl *tree, Tcl_Obj *objPtr, TreeItem *itemPtr, int flags); + +extern void FormatResult(Tcl_Interp *interp, char *fmt, ...); +extern void Tree_Debug(TreeCtrl *tree); + +extern TreeItem TreeItem_Alloc(TreeCtrl *tree); +extern TreeItem TreeItem_AllocRoot(TreeCtrl *tree); +extern int TreeItem_Debug(TreeCtrl *tree, TreeItem item); +extern void TreeItem_OpenClose(TreeCtrl *tree, TreeItem item, int mode, int recurse); + +#define STATE_OPEN 0x0001 +#define STATE_SELECTED 0x0002 +#define STATE_ENABLED 0x0004 +#define STATE_ACTIVE 0x0008 +#define STATE_FOCUS 0x0010 +#define STATE_USER 6 /* first user bit */ +extern int TreeItem_GetState(TreeCtrl *tree, TreeItem item_); + +#define CS_DISPLAY 0x01 +#define CS_LAYOUT 0x02 +extern int TreeItem_ChangeState(TreeCtrl *tree, TreeItem item_, int stateOff, int stateOn); + +extern void TreeItem_Undefine(TreeCtrl *tree, TreeItem item_, int state); + +extern int TreeItem_GetButton(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_SetButton(TreeCtrl *tree, TreeItem item, int hasButton); +extern int TreeItem_GetDepth(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_SetDepth(TreeCtrl *tree, TreeItem item, int depth); +extern int TreeItem_GetID(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_SetID(TreeCtrl *tree, TreeItem item_, int id); +extern int TreeItem_GetOpen(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_SetOpen(TreeCtrl *tree, TreeItem item, int isOpen); +extern int TreeItem_GetSelected(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_SetSelected(TreeCtrl *tree, TreeItem item, int isSelected); +extern TreeItem TreeItem_SetFirstChild(TreeCtrl *tree, TreeItem item, TreeItem firstChild); +extern TreeItem TreeItem_GetFirstChild(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_SetLastChild(TreeCtrl *tree, TreeItem item, TreeItem lastChild); +extern TreeItem TreeItem_GetLastChild(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_SetParent(TreeCtrl *tree, TreeItem item, TreeItem parent); +extern TreeItem TreeItem_GetParent(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_SetNextSibling(TreeCtrl *tree, TreeItem item, TreeItem nextSibling); +extern TreeItem TreeItem_GetNextSibling(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_SetPrevSibling(TreeCtrl *tree, TreeItem item, TreeItem prevSibling); +extern TreeItem TreeItem_GetPrevSibling(TreeCtrl *tree, TreeItem item); +extern void TreeItem_SetDInfo(TreeCtrl *tree, TreeItem item, TreeItemDInfo dInfo); +extern TreeItemDInfo TreeItem_GetDInfo(TreeCtrl *tree, TreeItem item); +extern void TreeItem_SetRInfo(TreeCtrl *tree, TreeItem item, TreeItemRInfo rInfo); +extern TreeItemRInfo TreeItem_GetRInfo(TreeCtrl *tree, TreeItem item); + +extern void TreeItem_AppendChild(TreeCtrl *tree, TreeItem self, TreeItem child); +extern void TreeItem_RemoveFromParent(TreeCtrl *tree, TreeItem self); +extern int TreeItem_Height(TreeCtrl *tree, TreeItem self); +extern int TreeItem_TotalHeight(TreeCtrl *tree, TreeItem self); +extern void TreeItem_InvalidateHeight(TreeCtrl *tree, TreeItem self); +extern void TreeItem_Draw(TreeCtrl *tree, TreeItem self, int x, int y, int width, int height, Drawable drawable, int minX, int maxX, int index); +extern void TreeItem_DrawLines(TreeCtrl *tree, TreeItem self, int x, int y, int width, int height, Drawable drawable); +extern void TreeItem_DrawButton(TreeCtrl *tree, TreeItem self, int x, int y, int width, int height, Drawable drawable); +extern int TreeItem_ReallyVisible(TreeCtrl *tree, TreeItem self); +extern void TreeItem_FreeResources(TreeCtrl *tree, TreeItem self); +extern TreeItem TreeItem_RootAncestor(TreeCtrl *tree, TreeItem item_); +extern int TreeItem_IsAncestor(TreeCtrl *tree, TreeItem item1, TreeItem item2); +extern Tcl_Obj *TreeItem_ToObj(TreeCtrl *tree, TreeItem item); +extern void TreeItem_ToIndex(TreeCtrl *tree, TreeItem item, int *absolute, int *visible); +extern TreeItem TreeItem_Next(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_NextVisible(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_Prev(TreeCtrl *tree, TreeItem item); +extern TreeItem TreeItem_PrevVisible(TreeCtrl *tree, TreeItem item); +extern char *TreeItem_Identify(TreeCtrl *tree, TreeItem item_, int x, int y); +extern void TreeItem_Identify2(TreeCtrl *tree, TreeItem item_, + int x1, int y1, int x2, int y2, Tcl_Obj *listObj); +extern int TreeItem_Indent(TreeCtrl *tree, TreeItem item_); +extern void Tree_UpdateItemIndex(TreeCtrl *tree); +extern int TreeItemCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); + +extern TreeItemColumn TreeItem_GetFirstColumn(TreeCtrl *tree, TreeItem item); +extern TreeItemColumn TreeItemColumn_GetNext(TreeCtrl *tree, TreeItemColumn column); +extern void TreeItemColumn_InvalidateSize(TreeCtrl *tree, TreeItemColumn column); +extern TreeStyle TreeItemColumn_GetStyle(TreeCtrl *tree, TreeItemColumn column); +extern void TreeItemColumn_ForgetStyle(TreeCtrl *tree, TreeItemColumn column_); +extern int TreeItemColumn_NeededWidth(TreeCtrl *tree, TreeItem item_, TreeItemColumn column_); +extern TreeItemColumn TreeItem_FindColumn(TreeCtrl *tree, TreeItem item, int columnIndex); +extern int TreeItem_ColumnFromObj(TreeCtrl *tree, TreeItem item, Tcl_Obj *obj, TreeItemColumn *columnPtr, int *indexPtr); +extern void TreeItem_RemoveColumn(TreeCtrl *tree, TreeItem item_, TreeItemColumn column_); + +typedef struct StyleDrawArgs StyleDrawArgs; +struct StyleDrawArgs +{ + TreeCtrl *tree; + TreeStyle style; + int x; + int y; + int width; + int height; + Drawable drawable; + int state; /* STATE_xxx */ + Tk_Justify justify; +}; + +/* tkTreeStyle.c */ +extern int TreeStyle_Init(Tcl_Interp *interp); +extern int TreeStyle_NeededWidth(TreeCtrl *tree, TreeStyle style_, int state); +extern int TreeStyle_NeededHeight(TreeCtrl *tree, TreeStyle style_, int state); +extern int TreeStyle_UseHeight(StyleDrawArgs *drawArgs); +extern void TreeStyle_Draw(StyleDrawArgs *args); +extern void TreeStyle_FreeResources(TreeCtrl *tree, TreeStyle style_); +extern void TreeStyle_Free(TreeCtrl *tree); +extern int TreeStyle_FromObj(TreeCtrl *tree, Tcl_Obj *obj, TreeStyle *style_); +extern Tcl_Obj *TreeStyle_ToObj(TreeStyle style_); +extern Tcl_Obj *TreeStyle_GetText(TreeCtrl *tree, TreeStyle style_); +extern void TreeStyle_SetText(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *textObj); +extern TreeStyle TreeStyle_NewInstance(TreeCtrl *tree, TreeStyle master); +extern int TreeStyle_ElementActual(TreeCtrl *tree, TreeStyle style_, int state, Tcl_Obj *elemObj, Tcl_Obj *obj); +extern int TreeStyle_ElementCget(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *elemObj, Tcl_Obj *obj); +extern int TreeStyle_ElementConfigure(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *elemObj, int objc, Tcl_Obj **objv); +extern void TreeStyle_ListElements(TreeCtrl *tree, TreeStyle style_); +extern TreeStyle TreeStyle_GetMaster(TreeCtrl *tree, TreeStyle style_); +extern char *TreeStyle_Identify(StyleDrawArgs *drawArgs, int x, int y); +extern void TreeStyle_Identify2(StyleDrawArgs *drawArgs, + int x1, int y1, int x2, int y2, Tcl_Obj *listObj); +extern int TreeStyle_Remap(TreeCtrl *tree, TreeStyle styleFrom_, TreeStyle styleTo_, int objc, Tcl_Obj *CONST objv[]); +extern void TreeStyle_TreeChanged(TreeCtrl *tree, int flagT); +#define SORT_ASCII 0 +#define SORT_DICT 1 +#define SORT_DOUBLE 2 +#define SORT_LONG 3 +#define SORT_COMMAND 4 +extern int TreeStyle_GetSortData(TreeCtrl *tree, TreeStyle style_, int type, long *lv, double *dv, char **sv); +extern int TreeStyle_GetElemRects(StyleDrawArgs *drawArgs, int objc, Tcl_Obj *CONST objv[], XRectangle rects[20]); +extern int TreeElementCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +extern int TreeStyleCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +extern int TreeStyle_ChangeState(TreeCtrl *tree, TreeStyle style_, int state1, int state2); +extern void TreeStyle_UndefineState(TreeCtrl *tree, int state); + +/* tkTreeNotify.c */ +extern int TreeNotify_Init(TreeCtrl *tree); +extern void TreeNotify_OpenClose(TreeCtrl *tree, TreeItem item, int isOpen, int before); +extern void TreeNotify_Selection(TreeCtrl *tree, TreeItem select[], TreeItem deselect[]); +extern int TreeNotifyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +extern void TreeNotify_ActiveItem(TreeCtrl *tree, TreeItem itemOld, TreeItem itemNew); +extern void TreeNotify_Scroll(TreeCtrl *tree, double fractions[2], int vertical); + +/* tkTreeColumn.c */ +extern void Tree_InitColumns(TreeCtrl *tree); +extern TreeColumn Tree_CreateColumn(TreeCtrl *tree, int columnIndex, int *isNew); +extern TreeColumn Tree_FindColumn(TreeCtrl *tree, int columnIndex); +#define CFO_NOT_TAIL 0x01 +extern int Tree_FindColumnByTag(TreeCtrl *tree, Tcl_Obj *obj, TreeColumn *columnPtr, int flags); +extern int TreeColumn_FromObj(TreeCtrl *tree, Tcl_Obj *obj, TreeColumn *columnPtr, int flags); +extern int TreeColumnCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); +extern int TreeColumn_Index(TreeColumn column_); +extern TreeColumn TreeColumn_Next(TreeColumn column_); +extern int TreeColumn_FixedWidth(TreeColumn column_); +extern int TreeColumn_MinWidth(TreeColumn column_); +extern int TreeColumn_StepWidth(TreeColumn column_); +extern int TreeColumn_NeededWidth(TreeColumn column_); +extern int TreeColumn_UseWidth(TreeColumn column_); +extern void TreeColumn_SetUseWidth(TreeColumn column_, int width); +extern Tk_Justify TreeColumn_Justify(TreeColumn column_); +extern int TreeColumn_WidthHack(TreeColumn column_); +extern int TreeColumn_Visible(TreeColumn column_); +extern GC TreeColumn_BackgroundGC(TreeColumn column_, int which); +extern void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y); +extern void Tree_DrawHeader(TreeCtrl *tree, Drawable drawable, int x, int y); +extern int TreeColumn_WidthOfItems(TreeColumn column_); +extern void TreeColumn_InvalidateWidth(TreeColumn column_); +extern void TreeColumn_Init(TreeCtrl *tree); +extern void Tree_FreeColumns(TreeCtrl *tree); +extern void Tree_InvalidateColumnWidth(TreeCtrl *tree, int columnIndex); +extern int Tree_HeaderHeight(TreeCtrl *tree); +extern int Tree_WidthOfColumns(TreeCtrl *tree); +extern void TreeColumn_TreeChanged(TreeCtrl *tree, int flagT); + +/* tkTreeDrag.c */ +extern int TreeDragImage_Init(TreeCtrl *tree); +extern void TreeDragImage_Free(TreeDragImage dragImage_); +extern void TreeDragImage_Display(TreeDragImage dragImage_); +extern void TreeDragImage_Undisplay(TreeDragImage dragImage_); +extern void TreeDragImage_Draw(TreeDragImage dragImage_, Drawable drawable, int x, int y); +extern int DragImageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); + +/* tkTreeMarquee.c */ +extern int TreeMarquee_Init(TreeCtrl *tree); +extern void TreeMarquee_Free(TreeMarquee marquee_); +extern void TreeMarquee_Draw(TreeMarquee marquee_, Drawable drawable, int x, int y); +extern void TreeMarquee_Display(TreeMarquee marquee_); +extern void TreeMarquee_Undisplay(TreeMarquee marquee_); +extern int TreeMarqueeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); + +/* tkTreeDisplay.c */ +extern int Tree_TotalWidth(TreeCtrl *tree); +extern int Tree_TotalHeight(TreeCtrl *tree); +extern TreeItem Tree_ItemUnderPoint(TreeCtrl *tree, int *x, int *y, int nearest); +extern void Tree_FreeItemRInfo(TreeCtrl *tree, TreeItem item); +extern void Tree_ItemBbox(TreeCtrl *tree, TreeItem item, int *x, int *y, int *w, int *h); +extern TreeItem Tree_ItemAbove(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemBelow(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemLeft(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemRight(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemTop(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemBottom(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemLeftMost(TreeCtrl *tree, TreeItem item); +extern TreeItem Tree_ItemRightMost(TreeCtrl *tree, TreeItem item); +extern int Tree_ItemToRNC(TreeCtrl *tree, TreeItem item, int *row, int *col); +extern TreeItem Tree_RNCToItem(TreeCtrl *tree, int row, int col); + +extern void TreeDInfo_Init(TreeCtrl *tree); +extern void TreeDInfo_Free(TreeCtrl *tree); +extern void Tree_EventuallyRedraw(TreeCtrl *tree); +extern void Tree_GetScrollFractionsX(TreeCtrl *tree, double fractions[2]); +extern void Tree_GetScrollFractionsY(TreeCtrl *tree, double fractions[2]); +extern int Increment_FindX(TreeCtrl *tree, int offset); +extern int Increment_FindY(TreeCtrl *tree, int offset); +extern int Increment_ToOffsetX(TreeCtrl *tree, int index); +extern int Increment_ToOffsetY(TreeCtrl *tree, int index); +extern int B_XviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +extern int B_YviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]); +extern void Tree_SetOriginX(TreeCtrl *tree, int xOrigin); +extern void Tree_SetOriginY(TreeCtrl *tree, int yOrigin); +extern void Tree_RelayoutWindow(TreeCtrl *tree); +extern void Tree_FreeItemDInfo(TreeCtrl *tree, TreeItem item1, TreeItem item2); +extern void Tree_InvalidateItemDInfo(TreeCtrl *tree, TreeItem item1, TreeItem item2); +extern void Tree_InvalidateArea(TreeCtrl *tree, int x1, int y1, int x2, int y2); +extern void Tree_InvalidateItemArea(TreeCtrl *tree, int x1, int y1, int x2, int y2); +extern void Tree_InvalidateRegion(TreeCtrl *tree, TkRegion region); +extern void Tree_InvalidateWindow(TreeCtrl *tree); +extern void Tree_RedrawArea(TreeCtrl *tree, int x1, int y1, int x2, int y2); +extern void Tree_RedrawItemArea(TreeCtrl *tree, int x1, int y1, int x2, int y2); +extern void Tree_FocusChanged(TreeCtrl *tree, int gotFocus); +extern TreeItem *Tree_ItemsInArea(TreeCtrl *tree, int minX, int minY, int maxX, int maxY); +extern void TreeColumnProxy_Undisplay(TreeCtrl *tree); +extern void TreeColumnProxy_Display(TreeCtrl *tree); + +#define DINFO_OUT_OF_DATE 0x0001 +#define DINFO_CHECK_COLUMN_WIDTH 0x0002 +#define DINFO_DRAW_HEADER 0x0004 +#define DINFO_SET_ORIGIN_X 0x0008 +#define DINFO_UPDATE_SCROLLBAR_X 0x0010 +#define DINFO_REDRAW_PENDING 0x00020 +#define DINFO_INVALIDATE 0x0040 +#define DINFO_DRAW_HIGHLIGHT 0x0080 +#define DINFO_DRAW_BORDER 0x0100 +#define DINFO_REDO_RANGES 0x0200 +#define DINFO_SET_ORIGIN_Y 0x0400 +#define DINFO_UPDATE_SCROLLBAR_Y 0x0800 +#define DINFO_REDO_INCREMENTS 0x1000 +extern void Tree_DInfoChanged(TreeCtrl *tree, int flags); + +/* tkTreeUtils.c */ +extern void wipefree(char *memPtr, int size); +#define WFREE(p,t) \ + wipefree((char *) p, sizeof(t)) +extern int Ellipsis(Tk_Font tkfont, char *string, int numBytes, int *maxPixels, char *ellipsis); +extern void HDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int x2); +extern void VDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int y2); +extern void DotRect(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height); +extern void DrawActiveOutline(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height, int open); +typedef struct DotState +{ + int stuff[10]; +} DotState; +extern void DotRect_Setup(TreeCtrl *tree, Drawable drawable, DotState *dotState); +extern void DotRect_Draw(DotState *dotState, int x, int y, int width, int height); +extern void DotRect_Restore(DotState *dotState); +typedef struct TextLayout_ *TextLayout; +extern TextLayout TextLayout_Compute(Tk_Font tkfont, CONST char *string, + int numChars, int wrapLength, Tk_Justify justify, int maxLines, int flags); +extern void TextLayout_Free(TextLayout textLayout); +extern void TextLayout_Size(TextLayout textLayout, int *widthPtr, int *heightPtr); +extern void TextLayout_Draw(Display *display, Drawable drawable, GC gc, + TextLayout layout, int x, int y, int firstChar, int lastChar); + diff --git a/generic/tkTreeDisplay.c b/generic/tkTreeDisplay.c new file mode 100644 index 0000000..8056cbf --- /dev/null +++ b/generic/tkTreeDisplay.c @@ -0,0 +1,3618 @@ +#include "tkTreeCtrl.h" + +typedef struct RItem RItem; +typedef struct Range Range; +typedef struct DItem DItem; +typedef struct DInfo DInfo; + +void Range_RedoIfNeeded(TreeCtrl *tree); +int Range_TotalWidth(TreeCtrl *tree, Range *range_); +int Range_TotalHeight(TreeCtrl *tree, Range *range_); +void Range_Redo(TreeCtrl *tree); +Range *Range_UnderPoint(TreeCtrl *tree, int *x_, int *y_, int nearest); +RItem *Range_ItemUnderPoint(TreeCtrl *tree, Range *range, int *x_, int *y_); + +/* One of these per TreeItem */ +struct RItem +{ + TreeItem item; /* The item */ + Range *range; /* Range the item is in */ + int size; /* height or width consumed in Range */ + int offset; /* vertical or horizontal offset in Range */ + int index; /* 0-based index in Range */ +}; + +/* A collection of visible TreeItems */ +struct Range +{ + RItem *first; + RItem *last; + int totalWidth; + int totalHeight; + int index; /* 0-based index in list of Ranges */ + int offset; /* vertical/horizontal offset from canvas top/left */ + Range *prev; + Range *next; +}; + +/* Display information for a TreeItem */ +struct DItem +{ + char magic[4]; /* debug */ + TreeItem item; + int x, y; /* Where it should be drawn, window coords */ + int oldX, oldY; /* Where it was last drawn, window coords */ + int width, height; /* Current width and height */ + int dirty[4]; /* Dirty area in item coords */ + Range *range; /* Range the TreeItem is in */ + int index; /* Used for alternating background colors */ +#define DITEM_DIRTY 0x0001 +#define DITEM_ALL_DIRTY 0x0002 + int flags; + DItem *next; +}; + +/* Display information for a TreeCtrl */ +struct DInfo +{ + GC scrollGC; + int xOrigin; /* Last seen TreeCtrl.xOrigin */ + int yOrigin; /* Last seen TreeCtrl.yOrigin */ + int totalWidth; /* Last seen Tree_TotalWidth() */ + int totalHeight; /* Last seen Tree_TotalHeight() */ + int columnWidth[20]; /* Last seen column widths */ + int headerHeight; /* Last seen TreeCtrl.headerHeight */ + DItem *dItem; /* Head of list for each visible item */ + DItem *dItemLast; /* Temp for UpdateDInfo() */ + Range *rangeFirst; /* Head of Ranges */ + Range *rangeLast; /* Tail of Ranges */ + RItem *rItem; /* Block of RItems for all Ranges */ + int rItemMax; /* size of rItem */ + Range *rangeFirstD; /* First range with valid display info */ + Range *rangeLastD; /* Last range with valid display info */ + int itemHeight; /* Observed max TreeItem height */ + int itemWidth; /* Observed max TreeItem width */ + Pixmap pixmap; /* DOUBLEBUFFER_WINDOW */ + int dirty[4]; /* DOUBLEBUFFER_WINDOW */ + int flags; /* DINFO_XXX */ +#ifdef INCREMENTS + int xScrollIncrement; /* Last seen TreeCtr.xScrollIncrement */ + int yScrollIncrement; /* Last seen TreeCtr.yScrollIncrement */ + int *xScrollIncrements; /* When tree->xScrollIncrement is zero */ + int *yScrollIncrements; /* When tree->yScrollIncrement is zero */ + int xScrollIncrementCount; + int yScrollIncrementCount; + int incrementTop; /* yScrollIncrement[] index of item at top */ + int incrementLeft; /* xScrollIncrement[] index of item at left */ +#endif +}; + +/*========*/ + +void Tree_FreeItemRInfo(TreeCtrl *tree, TreeItem item) +{ + TreeItem_SetRInfo(tree, item, NULL); +} + +static Range *Range_Free(TreeCtrl *tree, Range *range) +{ + Range *next = range->next; + WFREE(range, Range); + return next; +} + +/* + * This routine puts all ReallyVisible() TreeItems into a list of Ranges. + * If tree->wrapMode is TREE_WRAP_NONE there will only be a single Range. + */ +void Range_Redo(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *rangeList = dInfo->rangeFirst; + Range *range, *rangePrev = NULL; + RItem *rItem; + TreeItem item = tree->root; + int fixedWidth = -1, minWidth = -1, stepWidth = -1; + int wrapCount = 0, wrapPixels = 0; + int count, pixels, rItemCount = 0; + int rangeIndex = 0, itemIndex; + +if (tree->debug.enable && tree->debug.display) + dbwin("Range_Redo %s\n", Tk_PathName(tree->tkwin)); + + /* Update column variables */ + (void) Tree_WidthOfColumns(tree); + + dInfo->rangeFirst = NULL; + dInfo->rangeLast = NULL; + if (tree->columnCountVis < 1) + goto freeRanges; + + switch (tree->wrapMode) + { + case TREE_WRAP_NONE: + break; + case TREE_WRAP_ITEMS: + wrapCount = tree->wrapArg; + break; + case TREE_WRAP_PIXELS: + wrapPixels = tree->wrapArg; + break; + case TREE_WRAP_WINDOW: + wrapPixels = tree->vertical ? + Tk_Height(tree->tkwin) - tree->inset * 2 - Tree_HeaderHeight(tree) : + Tk_Width(tree->tkwin) - tree->inset * 2; + if (wrapPixels < 0) + wrapPixels = 0; + break; + } + + if ((wrapPixels > 0) && !tree->vertical) + { + /* More than one item column, so all items have the same width */ + if (tree->columnCountVis > 1) + fixedWidth = Tree_WidthOfColumns(tree); + + /* Single item column, fixed width for all ranges */ + else if (TreeColumn_FixedWidth(tree->columnVis) != -1) + fixedWidth = TreeColumn_FixedWidth(tree->columns); + + /* Single item column, possible minimum width */ + else if (TreeColumn_MinWidth(tree->columnVis) != -1) + minWidth = TreeColumn_MinWidth(tree->columns); + + /* Single item column, each item is a multiple of this width */ + else + stepWidth = TreeColumn_StepWidth(tree->columnVis); + } + + /* Speed up ReallyVisible() and get itemVisCount */ + if (tree->updateIndex) + Tree_UpdateItemIndex(tree); + + if (dInfo->rItemMax < tree->itemVisCount) + { + dInfo->rItem = (RItem *) ckrealloc((char *) dInfo->rItem, + tree->itemVisCount * sizeof(RItem)); + dInfo->rItemMax = tree->itemVisCount; + } + + if (!TreeItem_ReallyVisible(tree, item)) + item = TreeItem_NextVisible(tree, item); + while (item != NULL) + { + if (rangeList == NULL) + range = (Range *) ckalloc(sizeof(Range)); + else + { + range = rangeList; + rangeList = rangeList->next; + } + memset(range, '\0', sizeof(Range)); + range->totalWidth = -1; + range->totalHeight = -1; + range->index = rangeIndex++; + count = 0; + pixels = 0; + itemIndex = 0; + while (1) + { + rItem = dInfo->rItem + rItemCount; + if (rItemCount >= dInfo->rItemMax) + panic("rItemCount > dInfo->rItemMax"); + if (range->first == NULL) + range->first = range->last = rItem; + TreeItem_SetRInfo(tree, item, (TreeItemRInfo) rItem); + rItem->item = item; + rItem->range = range; + rItem->index = itemIndex; + + /* Range must be <= this number of pixels */ + /* Ensure at least one item is in this Range */ + if (wrapPixels > 0) + { + rItem->offset = pixels; + if (tree->vertical) + rItem->size = TreeItem_Height(tree, item); + else + { + if (fixedWidth != -1) + rItem->size = fixedWidth; + else + { + TreeItemColumn itemColumn = + TreeItem_GetFirstColumn(tree, item); + if (itemColumn != NULL) + { + int columnWidth = + TreeItemColumn_NeededWidth(tree, item, itemColumn); + if (0 == tree->columnTree) + columnWidth += TreeItem_Indent(tree, item); + rItem->size = MAX(columnWidth, minWidth); + } + else + rItem->size = MAX(0, minWidth); + } + } + if ((stepWidth != -1) && (rItem->size % stepWidth)) + rItem->size += stepWidth - rItem->size % stepWidth; + /* Too big */ + if (pixels + rItem->size > wrapPixels) + break; + pixels += rItem->size; + } + range->last = rItem; + itemIndex++; + rItemCount++; + if (++count == wrapCount) + break; + item = TreeItem_NextVisible(tree, item); + if (item == NULL) + break; + } + /* Since we needed to calculate the height or width of this range, + * we don't need to do it later in Range_TotalWidth/Height() */ + if (wrapPixels > 0) + { + if (tree->vertical) + range->totalHeight = pixels; + else + range->totalWidth = pixels; + } + + if (rangePrev == NULL) + dInfo->rangeFirst = range; + else + { + range->prev = rangePrev; + rangePrev->next = range; + } + dInfo->rangeLast = range; + rangePrev = range; + item = TreeItem_NextVisible(tree, range->last->item); + } + +freeRanges: + while (rangeList != NULL) + rangeList = Range_Free(tree, rangeList); +} + +int Range_TotalWidth(TreeCtrl *tree, Range *range) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + TreeItem item; + TreeItemColumn itemColumn; + RItem *rItem; + int columnWidth, fixedWidth = -1, minWidth = -1, stepWidth = -1; + int itemWidth; + + if (range->totalWidth >= 0) + return range->totalWidth; + + if (tree->vertical) + { + /* More than one item column, so all ranges have the same width */ + if (tree->columnCountVis > 1) + return range->totalWidth = Tree_WidthOfColumns(tree); + + /* Single item column, fixed width for all ranges */ + if (TreeColumn_FixedWidth(tree->columnVis) != -1) + return range->totalWidth = TreeColumn_FixedWidth(tree->columnVis); + + /* If there is only a single range, then use the column width, + * since it may expand to fill the window */ + if (dInfo->rangeFirst == dInfo->rangeLast) + return range->totalWidth = TreeColumn_UseWidth(tree->columnVis); + + /* Single item column, want all ranges same width */ + if (TreeColumn_WidthHack(tree->columnVis)) + return range->totalWidth = TreeColumn_UseWidth(tree->columnVis); + + /* Possible minimum column width */ + minWidth = TreeColumn_MinWidth(tree->columnVis); + range->totalWidth = MAX(0, minWidth); + + /* Max needed width of items in this range */ + rItem = range->first; + while (1) + { + item = rItem->item; + itemColumn = TreeItem_GetFirstColumn(tree, item); + if (itemColumn != NULL) + { + columnWidth = TreeItemColumn_NeededWidth(tree, item, itemColumn); + if (0 == tree->columnTree) + columnWidth += TreeItem_Indent(tree, item); + if (columnWidth > range->totalWidth) + range->totalWidth = columnWidth; + } + if (rItem == range->last) + break; + rItem++; + } + return range->totalWidth; + } + else + { + /* More than one item column, so all items/ranges have the same width */ + if (tree->columnCountVis > 1) + fixedWidth = Tree_WidthOfColumns(tree); + + /* Single item column, fixed width for all items/ranges */ + else if (TreeColumn_FixedWidth(tree->columnVis) != -1) + fixedWidth = TreeColumn_FixedWidth(tree->columnVis); + + else if (TreeColumn_MinWidth(tree->columnVis) != -1) + minWidth = TreeColumn_MinWidth(tree->columnVis); + + else + stepWidth = TreeColumn_StepWidth(tree->columnVis); + + /* Sum of widths of items in this range */ + range->totalWidth = 0; + rItem = range->first; + while (1) + { + item = rItem->item; + if (fixedWidth != -1) + itemWidth = fixedWidth; + else + { + itemColumn = TreeItem_GetFirstColumn(tree, item); + if (itemColumn != NULL) + { + columnWidth = TreeItemColumn_NeededWidth(tree, item, itemColumn); + if (0 == tree->columnTree) + columnWidth += TreeItem_Indent(tree, item); + itemWidth = MAX(columnWidth, minWidth); + } + else + itemWidth = MAX(0, minWidth); + + if ((stepWidth != -1) && (itemWidth % stepWidth)) + itemWidth += stepWidth - rItem->size % stepWidth; + } + + rItem = (RItem *) TreeItem_GetRInfo(tree, item); + rItem->offset = range->totalWidth; + rItem->size = itemWidth; + + range->totalWidth += itemWidth; + if (rItem == range->last) + break; + rItem++; + } + return range->totalWidth; + } +} + +int Range_TotalHeight(TreeCtrl *tree, Range *range) +{ + TreeItem item; + RItem *rItem; + int itemHeight; + + if (range->totalHeight >= 0) + return range->totalHeight; + + range->totalHeight = 0; + rItem = range->first; + while (1) + { + item = rItem->item; + itemHeight = TreeItem_Height(tree, item); + if (tree->vertical) + { + rItem->offset = range->totalHeight; + rItem->size = itemHeight; + range->totalHeight += itemHeight; + } + else + { + if (itemHeight > range->totalHeight) + range->totalHeight = itemHeight; + } + if (rItem == range->last) + break; + rItem++; + } + return range->totalHeight; +} + +int Tree_TotalWidth(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range = dInfo->rangeFirst; + int rangeWidth; + + Range_RedoIfNeeded(tree); + + if (tree->totalWidth >= 0) + return tree->totalWidth; + + tree->totalWidth = 0; + while (range != NULL) + { + rangeWidth = Range_TotalWidth(tree, range); + if (tree->vertical) + { + range->offset = tree->totalWidth; + tree->totalWidth += rangeWidth; + } + else + { + if (rangeWidth > tree->totalWidth) + tree->totalWidth = rangeWidth; + } + range = range->next; + } + return tree->totalWidth; +} + +int Tree_TotalHeight(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range = dInfo->rangeFirst; + int rangeHeight; + + Range_RedoIfNeeded(tree); + + if (tree->totalHeight >= 0) + return tree->totalHeight; + + tree->totalHeight = 0; + while (range != NULL) + { + rangeHeight = Range_TotalHeight(tree, range); + if (tree->vertical) + { + if (rangeHeight > tree->totalHeight) + tree->totalHeight = rangeHeight; + } + else + { + range->offset = tree->totalHeight; + tree->totalHeight += rangeHeight; + } + range = range->next; + } + return tree->totalHeight; +} + +Range *Range_UnderPoint(TreeCtrl *tree, int *x_, int *y_, int nearest) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range; + int x = *x_, y = *y_; + + Range_RedoIfNeeded(tree); + + range = dInfo->rangeFirst; + + if (nearest) + { + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + + if ((visWidth <= 0) || (visHeight <= 0)) + return NULL; + + /* Keep inside borders and header. Perhaps another flag needed. */ + if (x < tree->inset) + x = tree->inset; + if (x >= Tk_Width(tree->tkwin) - tree->inset) + x = Tk_Width(tree->tkwin) - tree->inset - 1; + if (y < topInset) + y = topInset; + if (y >= Tk_Height(tree->tkwin) - tree->inset) + y = Tk_Height(tree->tkwin) - tree->inset - 1; + } + + /* Window -> canvas */ + x += tree->xOrigin; + y += tree->yOrigin; + + if (nearest) + { + if (x < 0) + x = 0; + if (x >= Tree_TotalWidth(tree)) + x = Tree_TotalWidth(tree) - 1; + if (y < 0) + y = 0; + if (y >= Tree_TotalHeight(tree)) + y = Tree_TotalHeight(tree) - 1; + } + else + { + if (x < 0) + return NULL; + if (x >= Tree_TotalWidth(tree)) + return NULL; + if (y < 0) + return NULL; + if (y >= Tree_TotalHeight(tree)) + return NULL; + } + + if (tree->vertical) + { + while (range != NULL) + { + if ((x >= range->offset) && (x < range->offset + range->totalWidth)) + { + if (nearest || (y < range->totalHeight)) + { + (*x_) = x - range->offset; + (*y_) = MIN(y, range->totalHeight - 1); + return range; + } + return NULL; + } + range = range->next; + } + return NULL; + } + else + { + while (range != NULL) + { + if ((y >= range->offset) && (y < range->offset + range->totalHeight)) + { + if (nearest || (x < range->totalWidth)) + { + (*x_) = MIN(x, range->totalWidth - 1); + (*y_) = y - range->offset; + return range; + } + return NULL; + } + range = range->next; + } + return NULL; + } +} + +RItem *Range_ItemUnderPoint(TreeCtrl *tree, Range *range, int *x_, int *y_) +{ + RItem *rItem; + int x = (*x_), y = (*y_); + int i, l, u; + + /* x and y must be in Range */ + if (x < 0 || x >= range->totalWidth || y < 0 || y >= range->totalHeight) + goto panicNow; + +#if 1 + if (tree->vertical) + { + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem = range->first + i; + if ((y >= rItem->offset) && (y < rItem->offset + rItem->size)) + { + /* Range -> item coords */ + (*x_) = x; + (*y_) = y - rItem->offset; + return rItem; + } + if (y < rItem->offset) + u = i - 1; + else + l = i + 1; + } + } + else + { + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem = range->first + i; + if ((x >= rItem->offset) && (x < rItem->offset + rItem->size)) + { + /* Range -> item coords */ + (*x_) = x - rItem->offset; + (*y_) = y; + return rItem; + } + if (x < rItem->offset) + u = i - 1; + else + l = i + 1; + } + } +#else + if (tree->vertical) + { + rItem = range->first; + while (1) + { + if ((y >= rItem->offset) && (y < rItem->offset + rItem->size)) + { + /* Range -> item coords */ + (*x_) = x; + (*y_) = y - rItem->offset; + return rItem->item; + } + if (rItem == range->last) + break; + rItem++; + } + } + else + { + rItem = range->first; + while (1) + { + if ((x >= rItem->offset) && (x < rItem->offset + rItem->size)) + { + /* Range -> item coords */ + (*x_) = x - rItem->offset; + (*y_) = y; + return rItem->item; + } + if (rItem == range->last) + break; + rItem++; + } + } +#endif +panicNow: + panic("Range_ItemUnderPoint: can't find TreeItem in Range: x %d y %d W %d H %d", + x, y, range->totalWidth, range->totalHeight); + return NULL; +} + +#ifdef INCREMENTS + +static int Increment_AddX(TreeCtrl *tree, int offset, int size) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + + while ((visWidth > 1) && + (offset - dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] + > visWidth)) + { + size = Increment_AddX(tree, + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] + visWidth, + size); + } + if (dInfo->xScrollIncrementCount + 1 > size) + { + size *= 2; + dInfo->xScrollIncrements = (int *) ckrealloc( + (char *) dInfo->xScrollIncrements, size * sizeof(int)); + } + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = offset; + return size; +} + +static int Increment_AddY(TreeCtrl *tree, int offset, int size) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + + while ((visHeight > 1) && + (offset - dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] + > visHeight)) + { + size = Increment_AddY(tree, + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] + visHeight, + size); + } + if (dInfo->yScrollIncrementCount + 1 > size) + { + size *= 2; + dInfo->yScrollIncrements = (int *) ckrealloc( + (char *) dInfo->yScrollIncrements, size * sizeof(int)); + } + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = offset; + return size; +} + +static void Increment_RedoX(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range; + RItem *rItem; + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int totalWidth = Tree_TotalWidth(tree); + int x1, x2, x, y; + int size; + + if (dInfo->rangeFirst == NULL) + return; + + /* First increment is zero */ + size = 10; + dInfo->xScrollIncrements = (int *) ckalloc(size * sizeof(int)); + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = 0; + + x1 = 0; + while (1) + { + x2 = totalWidth; + for (range = dInfo->rangeFirst; + range != NULL; + range = range->next) + { + if (x1 >= range->totalWidth) + continue; + + /* Find RItem whose right side is >= x1 by smallest amount */ + x = x1; + y = 0; + rItem = Range_ItemUnderPoint(tree, range, &x, &y); + if (rItem->offset + rItem->size < x2) + x2 = rItem->offset + rItem->size; + } + if (x2 == totalWidth) + break; + size = Increment_AddX(tree, x2, size); + x1 = x2; + } + if ((visWidth > 1) && (totalWidth - + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] > visWidth)) + { + Increment_AddX(tree, totalWidth, size); + dInfo->xScrollIncrementCount--; + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] = totalWidth - visWidth; + } +} + +static void Increment_RedoY(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range; + RItem *rItem; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totalHeight = Tree_TotalHeight(tree); + int y1, y2, x, y; + int size; + + if (dInfo->rangeFirst == NULL) + return; + + /* First increment is zero */ + size = 10; + dInfo->yScrollIncrements = (int *) ckalloc(size * sizeof(int)); + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = 0; + + y1 = 0; + while (1) + { + y2 = totalHeight; + for (range = dInfo->rangeFirst; + range != NULL; + range = range->next) + { + if (y1 >= range->totalHeight) + continue; + + /* Find RItem whose bottom edge is >= y1 by smallest amount */ + x = 0; + y = y1; + rItem = Range_ItemUnderPoint(tree, range, &x, &y); + if (rItem->offset + rItem->size < y2) + y2 = rItem->offset + rItem->size; + } + if (y2 == totalHeight) + break; + size = Increment_AddY(tree, y2, size); + y1 = y2; + } + if ((visHeight > 1) && (totalHeight - + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] > visHeight)) + { + size = Increment_AddY(tree, totalHeight, size); + dInfo->yScrollIncrementCount--; + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] = totalHeight - visHeight; + } +} + +static void RangesToIncrementsX(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range = dInfo->rangeFirst; + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int totalWidth = Tree_TotalWidth(tree); + int size; + + if (dInfo->rangeFirst == NULL) + return; + + /* First increment is zero */ + size = 10; + dInfo->xScrollIncrements = (int *) ckalloc(size * sizeof(int)); + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount++] = 0; + + range = dInfo->rangeFirst->next; + while (range != NULL) + { + size = Increment_AddX(tree, range->offset, size); + range = range->next; + } + if ((visWidth > 1) && (totalWidth - + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] > visWidth)) + { + size = Increment_AddX(tree, totalWidth, size); + dInfo->xScrollIncrementCount--; + dInfo->xScrollIncrements[dInfo->xScrollIncrementCount - 1] = totalWidth - visWidth; + } +} + +static void RangesToIncrementsY(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range = dInfo->rangeFirst; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totalHeight = Tree_TotalHeight(tree); + int size; + + if (dInfo->rangeFirst == NULL) + return; + + /* First increment is zero */ + size = 10; + dInfo->yScrollIncrements = (int *) ckalloc(size * sizeof(int)); + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount++] = 0; + + range = dInfo->rangeFirst->next; + while (range != NULL) + { + size = Increment_AddY(tree, range->offset, size); + range = range->next; + } + if ((visHeight > 1) && (totalHeight - + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] > visHeight)) + { + size = Increment_AddY(tree, totalHeight, size); + dInfo->yScrollIncrementCount--; + dInfo->yScrollIncrements[dInfo->yScrollIncrementCount - 1] = totalHeight - visHeight; + } +} + +static void Increment_Redo(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + /* Free x */ + if (dInfo->xScrollIncrements != NULL) + ckfree((char *) dInfo->xScrollIncrements); + dInfo->xScrollIncrements = NULL; + dInfo->xScrollIncrementCount = 0; + + /* Free y */ + if (dInfo->yScrollIncrements != NULL) + ckfree((char *) dInfo->yScrollIncrements); + dInfo->yScrollIncrements = NULL; + dInfo->yScrollIncrementCount = 0; + + if (tree->vertical) + { + /* No xScrollIncrement is given. Snap to left edge of a Range */ + if (tree->xScrollIncrement <= 0) + RangesToIncrementsX(tree); + + /* No yScrollIncrement is given. Snap to top edge of a TreeItem */ + if (tree->yScrollIncrement <= 0) + Increment_RedoY(tree); + } + else + { + /* No xScrollIncrement is given. Snap to left edge of a TreeItem */ + if (tree->xScrollIncrement <= 0) + Increment_RedoX(tree); + + /* No yScrollIncrement is given. Snap to top edge of a Range */ + if (tree->yScrollIncrement <= 0) + RangesToIncrementsY(tree); + } +} + +static void Increment_RedoIfNeeded(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + Range_RedoIfNeeded(tree); + + /* Check for x|yScrollIncrement >0 changing to <=0 */ + if (((dInfo->yScrollIncrement > 0) != (tree->yScrollIncrement > 0)) || + ((dInfo->xScrollIncrement > 0) != (tree->xScrollIncrement > 0))) + { + dInfo->yScrollIncrement = tree->yScrollIncrement; + dInfo->xScrollIncrement = tree->xScrollIncrement; + dInfo->flags |= DINFO_REDO_INCREMENTS; + } + if (dInfo->flags & DINFO_REDO_INCREMENTS) + { + Increment_Redo(tree); + dInfo->flags &= ~DINFO_REDO_INCREMENTS; + } +} + +static int B_IncrementFind(int *increments, int count, int offset) +{ + int i, l, u, v; + + if (offset < 0) + offset = 0; + + /* Binary search */ + l = 0; + u = count - 1; + while (l <= u) + { + i = (l + u) / 2; + v = increments[i]; + if ((offset >= v) && ((i == count - 1) || (offset < increments[i + 1]))) + return i; + if (offset < v) + u = i - 1; + else + l = i + 1; + } + panic("B_IncrementFind failed (count %d offset %d)", count, offset); + return -1; +} + +int B_IncrementFindX(TreeCtrl *tree, int offset) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + return B_IncrementFind( + dInfo->xScrollIncrements, + dInfo->xScrollIncrementCount, + offset); +} + +int B_IncrementFindY(TreeCtrl *tree, int offset) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + return B_IncrementFind( + dInfo->yScrollIncrements, + dInfo->yScrollIncrementCount, + offset); +} + +int B_XviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Interp *interp = tree->interp; + DInfo *dInfo = (DInfo *) tree->dInfo; + + if (objc == 2) + { + double fractions[2]; + char buf[TCL_DOUBLE_SPACE * 2]; + + Tree_GetScrollFractionsX(tree, fractions); + sprintf(buf, "%g %g", fractions[0], fractions[1]); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + else + { + int count, index = 0, indexMax, offset, type; + double fraction; + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int totWidth = Tree_TotalWidth(tree); + + if (totWidth <= visWidth) + return TCL_OK; + + if (visWidth > 1) + { + /* Find incrementLeft when scrolled to right */ + indexMax = Increment_FindX(tree, totWidth - visWidth); + offset = Increment_ToOffsetX(tree, indexMax); + if (offset < totWidth - visWidth) + { + indexMax++; + offset = Increment_ToOffsetX(tree, indexMax); + } + + /* Add some fake content to bottom */ + if (offset + visWidth > totWidth) + totWidth = offset + visWidth; + } + else + { + indexMax = Increment_FindX(tree, totWidth); + visWidth = 1; + } + + type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); + switch (type) + { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + offset = (int) (fraction * totWidth + 0.5); + index = Increment_FindX(tree, offset); + break; + case TK_SCROLL_PAGES: + offset = tree->inset + tree->xOrigin; + offset += (int) (count * visWidth * 0.9); + index = Increment_FindX(tree, offset); + if (index == Increment_FindX(tree, tree->inset + tree->xOrigin)) + index++; + break; + case TK_SCROLL_UNITS: + index = dInfo->incrementLeft + count; + break; + } + + /* Don't scroll too far left */ + if (index < 0) + index = 0; + + /* Don't scroll too far right */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetX(tree, index); + if ((index != dInfo->incrementLeft) || (tree->xOrigin != offset - tree->inset)) + { + dInfo->incrementLeft = index; + tree->xOrigin = offset - tree->inset; + Tree_EventuallyRedraw(tree); + } + } + return TCL_OK; +} + +int B_YviewCmd(TreeCtrl *tree, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Interp *interp = tree->interp; + DInfo *dInfo = (DInfo *) tree->dInfo; + + if (objc == 2) + { + double fractions[2]; + char buf[TCL_DOUBLE_SPACE * 2]; + + Tree_GetScrollFractionsY(tree, fractions); + sprintf(buf, "%g %g", fractions[0], fractions[1]); + Tcl_SetResult(interp, buf, TCL_VOLATILE); + } + else + { + int count, index = 0, indexMax, offset, type; + double fraction; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totHeight = Tree_TotalHeight(tree); + + if (totHeight <= visHeight) + return TCL_OK; + + if (visHeight > 1) + { + /* Find incrementTop when scrolled to bottom */ + indexMax = Increment_FindY(tree, totHeight - visHeight); + offset = Increment_ToOffsetY(tree, indexMax); + if (offset < totHeight - visHeight) + { + indexMax++; + offset = Increment_ToOffsetY(tree, indexMax); + } + + /* Add some fake content to bottom */ + if (offset + visHeight > totHeight) + totHeight = offset + visHeight; + } + else + { + indexMax = Increment_FindY(tree, totHeight); + visHeight = 1; + } + + type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count); + switch (type) + { + case TK_SCROLL_ERROR: + return TCL_ERROR; + case TK_SCROLL_MOVETO: + offset = (int) (fraction * totHeight + 0.5); + index = Increment_FindY(tree, offset); + break; + case TK_SCROLL_PAGES: + offset = topInset + tree->yOrigin; + offset += (int) (count * visHeight * 0.9); + index = Increment_FindY(tree, offset); + if (index == Increment_FindY(tree, topInset + tree->yOrigin)) + index++; + break; + case TK_SCROLL_UNITS: + index = dInfo->incrementTop + count; + break; + } + + /* Don't scroll too far up */ + if (index < 0) + index = 0; + + /* Don't scroll too far down */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetY(tree, index); + if ((index != dInfo->incrementTop) || (tree->yOrigin != offset - topInset)) + { + dInfo->incrementTop = index; + tree->yOrigin = offset - topInset; + Tree_EventuallyRedraw(tree); + } + } + return TCL_OK; +} + +#endif /* INCREMENTS */ + +TreeItem Tree_ItemUnderPoint(TreeCtrl *tree, int *x_, int *y_, int nearest) +{ + Range *range; + RItem *rItem; + + range = Range_UnderPoint(tree, x_, y_, nearest); + if (range == NULL) + return NULL; + rItem = Range_ItemUnderPoint(tree, range, x_, y_); + if (rItem != NULL) + return rItem->item; + return NULL; +} + +void Tree_ItemBbox(TreeCtrl *tree, TreeItem item, int *x, int *y, int *w, int *h) +{ + Range *range; + RItem *rItem; + + if (!TreeItem_ReallyVisible(tree, item)) + return; + Range_RedoIfNeeded(tree); + rItem = (RItem *) TreeItem_GetRInfo(tree, item); + range = rItem->range; + if (tree->vertical) + { + (*x) = range->offset; + (*w) = range->totalWidth; + (*y) = rItem->offset; + (*h) = rItem->size; + } + else + { + (*x) = rItem->offset; + (*w) = rItem->size; + (*y) = range->offset; + (*h) = range->totalHeight; + } +} + +TreeItem Tree_ItemLARB(TreeCtrl *tree, TreeItem item, int vertical, int prev) +{ + RItem *rItem, *rItem2; + Range *range; + int i, l, u; + + if (!TreeItem_ReallyVisible(tree, item)) + return NULL; + Range_RedoIfNeeded(tree); + rItem = (RItem *) TreeItem_GetRInfo(tree, item); + if (vertical) + { + if (prev) + { + if (rItem == rItem->range->first) + return NULL; + rItem--; + } + else + { + if (rItem == rItem->range->last) + return NULL; + rItem++; + } + return rItem->item; + } + else + { + /* Find the previous range */ + range = prev ? rItem->range->prev : rItem->range->next; + if (range == NULL) + return NULL; + + /* Find item with same index */ + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem2 = range->first + i; + if (rItem2->index == rItem->index) + return rItem2->item; + if (rItem->index < rItem2->index) + u = i - 1; + else + l = i + 1; + } + } + return NULL; +} + +TreeItem Tree_ItemLeft(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemLARB(tree, item, !tree->vertical, TRUE); +} + +TreeItem Tree_ItemAbove(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemLARB(tree, item, tree->vertical, TRUE); +} + +TreeItem Tree_ItemRight(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemLARB(tree, item, !tree->vertical, FALSE); +} + +TreeItem Tree_ItemBelow(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemLARB(tree, item, tree->vertical, FALSE); +} + +TreeItem Tree_ItemFL(TreeCtrl *tree, TreeItem item, int vertical, int first) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + RItem *rItem, *rItem2; + Range *range; + int i, l, u; + + if (!TreeItem_ReallyVisible(tree, item)) + return NULL; + Range_RedoIfNeeded(tree); + rItem = (RItem *) TreeItem_GetRInfo(tree, item); + if (vertical) + return first ? rItem->range->first->item : rItem->range->last->item; + else + { + /* Find the first/last range */ + range = first ? dInfo->rangeFirst : dInfo->rangeLast; + + /* Check next/prev range until happy */ + while (1) + { + if (range == rItem->range) + return item; + + /* Find item with same index */ + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem2 = range->first + i; + if (rItem2->index == rItem->index) + return rItem2->item; + if (rItem->index < rItem2->index) + u = i - 1; + else + l = i + 1; + } + + range = first ? range->next : range->prev; + } + } + return NULL; +} + +TreeItem Tree_ItemTop(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemFL(tree, item, tree->vertical, TRUE); +} + +TreeItem Tree_ItemBottom(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemFL(tree, item, tree->vertical, FALSE); +} + +TreeItem Tree_ItemLeftMost(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemFL(tree, item, !tree->vertical, TRUE); +} + +TreeItem Tree_ItemRightMost(TreeCtrl *tree, TreeItem item) +{ + return Tree_ItemFL(tree, item, !tree->vertical, FALSE); +} + +int Tree_ItemToRNC(TreeCtrl *tree, TreeItem item, int *row, int *col) +{ + RItem *rItem; + + if (!TreeItem_ReallyVisible(tree, item)) + return TCL_ERROR; + Range_RedoIfNeeded(tree); + rItem = (RItem *) TreeItem_GetRInfo(tree, item); + if (tree->vertical) + { + (*row) = rItem->index; + (*col) = rItem->range->index; + } + else + { + (*row) = rItem->range->index; + (*col) = rItem->index; + } + return TCL_OK; +} + +TreeItem Tree_RNCToItem(TreeCtrl *tree, int row, int col) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range; + RItem *rItem; + int i, l, u; + + Range_RedoIfNeeded(tree); + range = dInfo->rangeFirst; + if (range == NULL) + return NULL; + if (row < 0) + row = 0; + if (col < 0) + col = 0; + if (tree->vertical) + { + if (col > dInfo->rangeLast->index) + col = dInfo->rangeLast->index; + while (range->index != col) + range = range->next; + rItem = range->last; + if (row > rItem->index) + row = rItem->index; + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem = range->first + i; + if (rItem->index == row) + break; + if (row < rItem->index) + u = i - 1; + else + l = i + 1; + } + } + else + { + if (row > dInfo->rangeLast->index) + row = dInfo->rangeLast->index; + while (range->index != row) + range = range->next; + rItem = range->last; + if (col > rItem->index) + col = rItem->index; + /* Binary search */ + l = 0; + u = range->last->index; + while (l <= u) + { + i = (l + u) / 2; + rItem = range->first + i; + if (rItem->index == col) + break; + if (col < rItem->index) + u = i - 1; + else + l = i + 1; + } + } + return rItem->item; +} + +/*=============*/ + +static void DisplayDelay(TreeCtrl *tree) +{ + if (tree->debug.enable && + tree->debug.display && + tree->debug.displayDelay > 0) + Tcl_Sleep(tree->debug.displayDelay); +} + +static DItem *DItem_Alloc(TreeCtrl *tree, RItem *rItem) +{ + DItem *dItem; + + dItem = (DItem *) TreeItem_GetDInfo(tree, rItem->item); + if (dItem != NULL) + panic("tried to allocate duplicate DItem"); + + dItem = (DItem *) ckalloc(sizeof(DItem)); + memset(dItem, '\0', sizeof(DItem)); + strncpy(dItem->magic, "MAGC", 4); + dItem->item = rItem->item; + dItem->flags = DITEM_DIRTY | DITEM_ALL_DIRTY; + TreeItem_SetDInfo(tree, rItem->item, (TreeItemDInfo) dItem); + return dItem; +} + +static DItem *DItem_Unlink(DItem *head, DItem *dItem) +{ + DItem *prev; + + if (head == dItem) + head = dItem->next; + else + { + for (prev = head; + prev->next != dItem; + prev = prev->next) + { + /* nothing */ + } + prev->next = dItem->next; + } + dItem->next = NULL; + return head; +} + +static DItem *DItem_Free(TreeCtrl *tree, DItem *dItem) +{ + DItem *next = dItem->next; + if (strncmp(dItem->magic, "MAGC", 4) != 0) + panic("DItem_Free: dItem.magic != MAGC"); + if (dItem->item != NULL) + TreeItem_SetDInfo(tree, dItem->item, (TreeItemDInfo) NULL); + WFREE(dItem, DItem); + return next; +} + +static void FreeDItems(TreeCtrl *tree, DItem *first, DItem *last, int unlink) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *prev; + + if (unlink) + { + if (dInfo->dItem == first) + dInfo->dItem = last; + else + { + for (prev = dInfo->dItem; + prev->next != first; + prev = prev->next) + { + /* nothing */ + } + prev->next = last; + } + } + while (first != last) + first = DItem_Free(tree, first); +} + +TreeItem *Tree_ItemsInArea(TreeCtrl *tree, int minX, int minY, int maxX, int maxY) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int x, y, rx = 0, ry = 0, ix, iy, dx, dy; + Range *range; + RItem *rItem; + TreeItem *items; + int count = 0, size = 25; + + Range_RedoIfNeeded(tree); + range = dInfo->rangeFirst; + + if (tree->vertical) + { + /* Find the first range which could be in the area horizontally */ + while (range != NULL) + { + if ((range->offset < maxX) && + (range->offset + range->totalWidth >= minX)) + { + rx = range->offset; + ry = 0; + break; + } + range = range->next; + } + } + else + { + /* Find the first range which could be in the area vertically */ + while (range != NULL) + { + if ((range->offset < maxY) && + (range->offset + range->totalHeight >= minY)) + { + rx = 0; + ry = range->offset; + break; + } + range = range->next; + } + } + + if (range == NULL) + return NULL; + + items = (TreeItem *) ckalloc(sizeof(TreeItem) * size); + + while (range != NULL) + { + if ((rx + range->totalWidth > minX) && + (ry + range->totalHeight > minY)) + { + if (tree->vertical) + { + /* Range coords */ + dx = MAX(minX - rx, 0); + dy = minY; + } + else + { + dx = minX; + dy = MAX(minY - ry, 0); + } + ix = dx; + iy = dy; + rItem = Range_ItemUnderPoint(tree, range, &ix, &iy); + + /* Canvas coords of top-left of item */ + x = rx + dx - ix; + y = ry + dy - iy; + + while (1) + { + if (count + 1 > size) + { + size *= 2; + items = (TreeItem *) ckrealloc((char *) items, sizeof(TreeItem) * size); + } + items[count++] = rItem->item; + if (tree->vertical) + { + y += rItem->size; + if (y >= maxY) + break; + } + else + { + x += rItem->size; + if (x >= maxX) + break; + } + if (rItem == range->last) + break; + rItem++; + } + } + if (tree->vertical) + { + if (rx + range->totalWidth >= maxX) + break; + rx += range->totalWidth; + } + else + { + if (ry + range->totalHeight >= maxY) + break; + ry += range->totalHeight; + } + range = range->next; + } + + /* NULL terminator */ + if (count + 1 > size) + { + size += 1; + items = (TreeItem *) ckrealloc((char *) items, sizeof(TreeItem) * size); + } + items[count++] = NULL; + + return items; +} + +static DItem *UpdateDInfoForRange(TreeCtrl *tree, DItem *dItemHead, Range *range, RItem *rItem, int x, int y) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + TreeItem item; + int maxX, maxY; + int index, indexVis; + + maxX = Tk_Width(tree->tkwin) - tree->inset; + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* Top-to-bottom */ + if (tree->vertical) + { + while (1) + { + item = rItem->item; + + /* Update item/style layout. This can be needed when using fixed + * column widths. */ + (void) TreeItem_Height(tree, item); + + TreeItem_ToIndex(tree, item, &index, &indexVis); + switch (tree->backgroundMode) + { + case BG_MODE_INDEX: break; + case BG_MODE_VISINDEX: index = indexVis; break; + case BG_MODE_COLUMN: index = range->index; break; + case BG_MODE_ROW: index = rItem->index; break; + } + + dItem = (DItem *) TreeItem_GetDInfo(tree, item); + + /* Re-use a previously allocated DItem */ + if (dItem != NULL) + { + dItemHead = DItem_Unlink(dItemHead, dItem); + if (dInfo->flags & DINFO_INVALIDATE) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* Range height may have changed due to an item resizing */ + else if (dItem->width != range->totalWidth) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* Items may have alternating background colors. */ + else if ((tree->columnBgCnt > 1) && + ((index % tree->columnBgCnt) != + (dItem->index % tree->columnBgCnt))) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* We don't copy items horizontally to their new position, + * except for horizontal scrolling which moves the whole + * range */ + else if (x != dItem->oldX + (dInfo->xOrigin - tree->xOrigin)) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* If we are displaying dotted lines and the item has moved + * from odd-top to non-odd-top or vice versa, must redraw + * the lines for this item. */ + else if (tree->showLines && + (tree->lineStyle == LINE_STYLE_DOT) && + tree->columnTreeVis && + ((dItem->oldY & 1) != (y & 1))) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + } + + /* Make a new DItem */ + else + { + dItem = DItem_Alloc(tree, rItem); + } + + dItem->x = x; + dItem->y = y; + dItem->width = Range_TotalWidth(tree, range); + dItem->height = rItem->size; + dItem->range = range; + dItem->index = index; + + /* Keep track of the maximum item size */ + if (dItem->width > dInfo->itemWidth) + dInfo->itemWidth = dItem->width; + if (dItem->height > dInfo->itemHeight) + dInfo->itemHeight = dItem->height; + + /* Linked list of DItems */ + if (dInfo->dItem == NULL) + dInfo->dItem = dItem; + else + dInfo->dItemLast->next = dItem; + dInfo->dItemLast = dItem; + + if (rItem == range->last) + break; + + /* Advance to next TreeItem */ + rItem++; + + /* Stop when out of bounds */ + y += dItem->height; + if (y >= maxY) + break; + } + } + + /* Left-to-right */ + else + { + while (1) + { + item = rItem->item; + + /* Update item/style layout. This can be needed when using fixed + * column widths. */ + (void) TreeItem_Height(tree, item); + + TreeItem_ToIndex(tree, item, &index, &indexVis); + switch (tree->backgroundMode) + { + case BG_MODE_INDEX: break; + case BG_MODE_VISINDEX: index = indexVis; break; + case BG_MODE_COLUMN: index = rItem->index; break; + case BG_MODE_ROW: index = range->index; break; + } + + dItem = (DItem *) TreeItem_GetDInfo(tree, item); + + /* Re-use a previously allocated DItem */ + if (dItem != NULL) + { + dItemHead = DItem_Unlink(dItemHead, dItem); + if (dInfo->flags & DINFO_INVALIDATE) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* If another item changed size, it may affect the + * height/width of this range */ + else if (dItem->height != range->totalHeight) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* Items may have alternating background colors. */ + else if ((tree->columnBgCnt > 1) && ((index % tree->columnBgCnt) != + (dItem->index % tree->columnBgCnt))) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* We don't copy items vertically to their new position, + * except for vertical scrolling which moves the whole range */ + else if (y != dItem->oldY + (dInfo->yOrigin - tree->yOrigin)) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + + /* If we are displaying dotted lines and the item has moved + * from odd-top to non-odd-top or vice versa, must redraw + * the lines for this item. */ + else if (tree->showLines && + (tree->lineStyle == LINE_STYLE_DOT) && + tree->columnTreeVis && + ((dItem->oldY & 1) != (y & 1))) + dItem->flags |= DITEM_DIRTY | DITEM_ALL_DIRTY; + } + + /* Make a new DItem */ + else + { + dItem = DItem_Alloc(tree, rItem); + } + + dItem->x = x; + dItem->y = y; + dItem->width = rItem->size; + dItem->height = Range_TotalHeight(tree, range); + dItem->range = range; + dItem->index = index; + + /* Keep track of the maximum item size */ + if (dItem->width > dInfo->itemWidth) + dInfo->itemWidth = dItem->width; + if (dItem->height > dInfo->itemHeight) + dInfo->itemHeight = dItem->height; + + /* Linked list of DItems */ + if (dInfo->dItem == NULL) + dInfo->dItem = dItem; + else + dInfo->dItemLast->next = dItem; + dInfo->dItemLast = dItem; + + if (rItem == range->last) + break; + + /* Advance to next TreeItem */ + rItem++; + + /* Stop when out of bounds */ + x += dItem->width; + if (x >= maxX) + break; + } + } + + return dItemHead; +} + +void Tree_UpdateDInfo(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItemHead = dInfo->dItem; + int x, y, rx = 0, ry = 0, ix, iy, dx, dy; + int minX, minY, maxX, maxY; + Range *range; + RItem *rItem; + +if (tree->debug.enable && tree->debug.display) + dbwin("Tree_UpdateDInfo %s\n", Tk_PathName(tree->tkwin)); + + minX = tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxX = Tk_Width(tree->tkwin) - tree->inset; + maxY = Tk_Height(tree->tkwin) - tree->inset; + + if ((maxX - minX < 1) || (maxY - minY < 1)) + goto done; + + range = dInfo->rangeFirst; + if (tree->vertical) + { + /* Find the first range which could be onscreen horizontally. + * It may not be onscreen if it has less height than other ranges. */ + while (range != NULL) + { + if ((range->offset < maxX + tree->xOrigin) && + (range->offset + range->totalWidth >= minX + tree->xOrigin)) + { + rx = range->offset; + ry = 0; + break; + } + range = range->next; + } + } + else + { + /* Find the first range which could be onscreen vertically. + * It may not be onscreen if it has less width than other ranges. */ + while (range != NULL) + { + if ((range->offset < maxY + tree->yOrigin) && + (range->offset + range->totalHeight >= minY + tree->yOrigin)) + { + rx = 0; + ry = range->offset; + break; + } + range = range->next; + } + } + + dInfo->dItem = dInfo->dItemLast = NULL; + dInfo->rangeFirstD = dInfo->rangeLastD = NULL; + dInfo->itemWidth = dInfo->itemHeight = 0; + + while (range != NULL) + { + if ((rx + range->totalWidth > minX + tree->xOrigin) && + (ry + range->totalHeight > minY + tree->yOrigin)) + { + if (tree->vertical) + { + /* Range coords */ + dx = MAX(minX + tree->xOrigin - rx, 0); + dy = minY + tree->yOrigin; + } + else + { + dx = minX + tree->xOrigin; + dy = MAX(minY + tree->yOrigin - ry, 0); + } + ix = dx; + iy = dy; + rItem = Range_ItemUnderPoint(tree, range, &ix, &iy); + + /* Window coords of top-left of item */ + x = (rx - tree->xOrigin) + dx - ix; + y = (ry - tree->yOrigin) + dy - iy; + dItemHead = UpdateDInfoForRange(tree, dItemHead, range, rItem, x, y); + } + + /* Track this range even if it has no DItems, so we whitespace is + * erased */ + if (dInfo->rangeFirstD == NULL) + dInfo->rangeFirstD = range; + dInfo->rangeLastD = range; + + if (tree->vertical) + { + rx += range->totalWidth; + if (rx >= maxX + tree->xOrigin) + break; + } + else + { + ry += range->totalHeight; + if (ry >= maxY + tree->yOrigin) + break; + } + range = range->next; + } + + if (dInfo->dItemLast != NULL) + dInfo->dItemLast->next = NULL; +done: + while (dItemHead != NULL) + dItemHead = DItem_Free(tree, dItemHead); + + dInfo->flags &= ~DINFO_INVALIDATE; +} + +static void InvalidateDItemX(DItem *dItem, int itemX, int dirtyX, int dirtyWidth) +{ + int x1, x2; + + if (dirtyX <= itemX) + dItem->dirty[LEFT] = 0; + else + { + x1 = dirtyX - itemX; + if (!(dItem->flags & DITEM_DIRTY) || (x1 < dItem->dirty[LEFT])) + dItem->dirty[LEFT] = x1; + } + + if (dirtyX + dirtyWidth >= itemX + dItem->width) + dItem->dirty[RIGHT] = dItem->width; + else + { + x2 = dirtyX + dirtyWidth - itemX; + if (!(dItem->flags & DITEM_DIRTY) || (x2 > dItem->dirty[RIGHT])) + dItem->dirty[RIGHT] = x2; + } +} + +static void InvalidateDItemY(DItem *dItem, int itemY, int dirtyY, int dirtyHeight) +{ + int y1, y2; + + if (dirtyY <= itemY) + dItem->dirty[TOP] = 0; + else + { + y1 = dirtyY - itemY; + if (!(dItem->flags & DITEM_DIRTY) || (y1 < dItem->dirty[TOP])) + dItem->dirty[TOP] = y1; + } + + if (dirtyY + dirtyHeight >= itemY + dItem->height) + dItem->dirty[BOTTOM] = dItem->height; + else + { + y2 = dirtyY + dirtyHeight - itemY; + if (!(dItem->flags & DITEM_DIRTY) || (y2 > dItem->dirty[BOTTOM])) + dItem->dirty[BOTTOM] = y2; + } +} + +void Range_RedoIfNeeded(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + if (dInfo->flags & DINFO_REDO_RANGES) + { + dInfo->rangeFirstD = dInfo->rangeLastD = NULL; + dInfo->flags |= DINFO_OUT_OF_DATE; + Range_Redo(tree); + dInfo->flags &= ~DINFO_REDO_RANGES; + + /* Do this after clearing REDO_RANGES to prevent infinite loop */ + tree->totalWidth = tree->totalHeight = -1; + (void) Tree_TotalWidth(tree); + (void) Tree_TotalHeight(tree); +#ifdef INCREMENTS + dInfo->flags |= DINFO_REDO_INCREMENTS; +#endif + } +} + +static int ScrollVerticalComplex(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem, *dItem2; + Range *range; + TkRegion damageRgn; + int minX, minY, maxX, maxY; + int oldX, oldY, width, height, offset; + int y; + int numCopy = 0; + + minX = tree->inset; + maxX = Tk_Width(tree->tkwin) - tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* Try updating the display by copying items on the screen to their + * new location */ + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + /* Copy an item to its new location unless: + * (a) item display info is invalid + * (b) item is in same location as last draw */ + if ((dItem->flags & DITEM_ALL_DIRTY) || + (dItem->oldY == dItem->y)) + continue; + +numCopy++; + + range = dItem->range; + + /* This item was previously displayed so it only needs to be + * copied to the new location. Copy all such items as one */ + offset = dItem->y - dItem->oldY; + height = dItem->height; + for (dItem2 = dItem->next; + dItem2 != NULL; + dItem2 = dItem2->next) + { + if ((dItem2->range != range) || + (dItem2->flags & DITEM_ALL_DIRTY) || + (dItem2->oldY + offset != dItem2->y)) + break; +numCopy++; + height += dItem2->height; + } + + y = dItem->y; + oldY = dItem->oldY; + + /* Don't copy part of the window border */ + if (oldY < minY) + { + height -= minY - oldY; + oldY = minY; + } + if (oldY + height > maxY) + height = maxY - oldY; + + /* Don't copy over the window border */ + if (oldY + offset < minY) + { + height -= minY - (oldY + offset); + oldY += minY - (oldY + offset); + } + if (oldY + offset + height > maxY) + height = maxY - (oldY + offset); + + oldX = dItem->oldX; + width = dItem->width; + if (oldX < minX) + { + width -= minX - oldX; + oldX = minX; + } + if (oldX + width > maxX) + width = maxX - oldX; + + /* Update oldY of copied items */ + while (1) + { + /* If an item was partially visible, invalidate the exposed area */ + if ((dItem->oldY < minY) && (offset > 0)) + { + InvalidateDItemX(dItem, oldX, oldX, width); + InvalidateDItemY(dItem, dItem->oldY, dItem->oldY, minY - dItem->oldY); + dItem->flags |= DITEM_DIRTY; + } + if ((dItem->oldY + dItem->height > maxY) && (offset < 0)) + { + InvalidateDItemX(dItem, oldX, oldX, width); + InvalidateDItemY(dItem, dItem->oldY, maxY, maxY - dItem->oldY + dItem->height); + dItem->flags |= DITEM_DIRTY; + } + + dItem->oldY = dItem->y; + if (dItem->next == dItem2) + break; + dItem = dItem->next; + } + + /* Invalidate parts of items being copied over */ + for ( ; dItem2 != NULL; dItem2 = dItem2->next) + { + if (dItem2->range != range) + break; + if (!(dItem2->flags & DITEM_ALL_DIRTY) && + (dItem2->oldY + dItem2->height > y) && + (dItem2->oldY < y + height)) + { + InvalidateDItemX(dItem2, oldX, oldX, width); + InvalidateDItemY(dItem2, dItem2->oldY, y, height); + dItem2->flags |= DITEM_DIRTY; + } + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + int dirtyMin, dirtyMax; + XCopyArea(tree->display, dInfo->pixmap, dInfo->pixmap, + tree->copyGC, + oldX, oldY, width, height, + oldX, oldY + offset); + dirtyMin = MAX(oldY, oldY + offset + height); + dirtyMax = MIN(oldY + height, oldY + offset); + Tree_InvalidateArea(tree, oldX, dirtyMin, width, dirtyMax - dirtyMin); + dInfo->dirty[LEFT] = MIN(dInfo->dirty[LEFT], oldX); + dInfo->dirty[TOP] = MIN(dInfo->dirty[TOP], oldY + offset); + dInfo->dirty[RIGHT] = MAX(dInfo->dirty[RIGHT], oldX + width); + dInfo->dirty[BOTTOM] = MAX(dInfo->dirty[BOTTOM], oldY + offset + height); + continue; + } + + /* Copy */ + damageRgn = TkCreateRegion(); + if (TkScrollWindow(tree->tkwin, dInfo->scrollGC, + oldX, oldY, width, height, 0, offset, damageRgn)) + { +DisplayDelay(tree); + Tree_InvalidateRegion(tree, damageRgn); + } + TkDestroyRegion(damageRgn); + } + return numCopy; +} + +static void ScrollHorizontalSimple(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + TkRegion damageRgn; + int minX, minY, maxX, maxY; + int width, offset; + int x; + + minX = tree->inset; + maxX = Tk_Width(tree->tkwin) - tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* Horizontal scrolling */ + if (dInfo->xOrigin != tree->xOrigin) + { + int dirtyMin, dirtyMax; + + offset = dInfo->xOrigin - tree->xOrigin; + width = maxX - minX - abs(offset); + if (width < 0) + width = maxX - minX; + /* Move pixels right */ + if (offset > 0) + { + x = minX; + } + /* Move pixels left */ + else + { + x = maxX - width; + } + + dirtyMin = minX + width; + dirtyMax = maxX - width; + + /* Update oldX */ + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + dItem->oldX = dItem->x; + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + XCopyArea(tree->display, dInfo->pixmap, dInfo->pixmap, + tree->copyGC, + x, minY, width, maxY - minY, + x + offset, minY); + if (offset < 0) + dirtyMax = maxX; + else + dirtyMin = minX; + Tree_InvalidateArea(tree, dirtyMin, minY, dirtyMax, maxY); + return; + } + /* Copy */ + damageRgn = TkCreateRegion(); + if (TkScrollWindow(tree->tkwin, dInfo->scrollGC, + x, minY, width, maxY - minY, offset, 0, damageRgn)) + { + DisplayDelay(tree); + Tree_InvalidateRegion(tree, damageRgn); + } + TkDestroyRegion(damageRgn); +#ifndef WIN32 + if (offset < 0) + dirtyMax = maxX; + else + dirtyMin = minX; +#endif + if (dirtyMin < dirtyMax) + Tree_InvalidateArea(tree, dirtyMin, minY, dirtyMax, maxY); + } +} + +static void ScrollVerticalSimple(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + TkRegion damageRgn; + int minX, minY, maxX, maxY; + int height, offset; + int y; + + minX = tree->inset; + maxX = Tk_Width(tree->tkwin) - tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* Vertical scrolling */ + if (dInfo->yOrigin != tree->yOrigin) + { + int dirtyMin, dirtyMax; + + offset = dInfo->yOrigin - tree->yOrigin; + height = maxY - minY - abs(offset); + if (height < 0) + height = maxY - minY; + /* Move pixels down */ + if (offset > 0) + { + y = minY; + } + /* Move pixels up */ + else + { + y = maxY - height; + } + + dirtyMin = minY + height; + dirtyMax = maxY - height; + + /* Update oldY */ + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + dItem->oldY = dItem->y; + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + XCopyArea(tree->display, dInfo->pixmap, dInfo->pixmap, + tree->copyGC, + minX, y, maxX - minX, height, + minX, y + offset); + if (offset < 0) + dirtyMax = maxY; + else + dirtyMin = minY; + Tree_InvalidateArea(tree, minX, dirtyMin, maxX, dirtyMax); + return; + } + + /* Copy */ + damageRgn = TkCreateRegion(); + if (TkScrollWindow(tree->tkwin, dInfo->scrollGC, + minX, y, maxX - minX, height, 0, offset, damageRgn)) + { +DisplayDelay(tree); + Tree_InvalidateRegion(tree, damageRgn); + } + TkDestroyRegion(damageRgn); +#ifndef WIN32 + if (offset < 0) + dirtyMax = maxY; + else + dirtyMin = minY; +#endif + if (dirtyMin < dirtyMax) + Tree_InvalidateArea(tree, minX, dirtyMin, maxX, dirtyMax); + } +} + +static int ScrollHorizontalComplex(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem, *dItem2; + Range *range; + TkRegion damageRgn; + int minX, minY, maxX, maxY; + int oldX, oldY, width, height, offset; + int x; + int numCopy = 0; + + minX = tree->inset; + maxX = Tk_Width(tree->tkwin) - tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* Try updating the display by copying items on the screen to their + * new location */ + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + /* Copy an item to its new location unless: + * (a) item display info is invalid + * (b) item is in same location as last draw */ + if ((dItem->flags & DITEM_ALL_DIRTY) || + (dItem->oldX == dItem->x)) + continue; + +numCopy++; + + range = dItem->range; + + /* This item was previously displayed so it only needs to be + * copied to the new location. Copy all such items as one */ + offset = dItem->x - dItem->oldX; + width = dItem->width; + for (dItem2 = dItem->next; + dItem2 != NULL; + dItem2 = dItem2->next) + { + if ((dItem2->range != range) || + (dItem2->flags & DITEM_ALL_DIRTY) || + (dItem2->oldX + offset != dItem2->x)) + break; +numCopy++; + width += dItem2->width; + } + + x = dItem->x; + oldX = dItem->oldX; + + /* Don't copy part of the window border */ + if (oldX < minX) + { + width -= minX - oldX; + oldX = minX; + } + if (oldX + width > maxX) + width = maxX - oldX; + + /* Don't copy over the window border */ + if (oldX + offset < minX) + { + width -= minX - (oldX + offset); + oldX += minX - (oldX + offset); + } + if (oldX + offset + width > maxX) + width = maxX - (oldX + offset); + + oldY = dItem->oldY; + height = dItem->height; /* range->totalHeight */ + if (oldY < minY) + { + height -= minY - oldY; + oldY = minY; + } + if (oldY + height > maxY) + height = maxY - oldY; + + /* Update oldX of copied items */ + while (1) + { + /* If an item was partially visible, invalidate the exposed area */ + if ((dItem->oldX < minX) && (offset > 0)) + { + InvalidateDItemX(dItem, dItem->oldX, dItem->oldX, minX - dItem->oldX); + InvalidateDItemY(dItem, oldY, oldY, height); + dItem->flags |= DITEM_DIRTY; + } + if ((dItem->oldX + dItem->width > maxX) && (offset < 0)) + { + InvalidateDItemX(dItem, dItem->oldX, maxX, maxX - dItem->oldX + dItem->width); + InvalidateDItemY(dItem, oldY, oldY, height); + dItem->flags |= DITEM_DIRTY; + } + + dItem->oldX = dItem->x; + if (dItem->next == dItem2) + break; + dItem = dItem->next; + } + + /* Invalidate parts of items being copied over */ + for ( ; dItem2 != NULL; dItem2 = dItem2->next) + { + if (dItem2->range != range) + break; + if (!(dItem2->flags & DITEM_ALL_DIRTY) && + (dItem2->oldX + dItem2->width > x) && + (dItem2->oldX < x + width)) + { + InvalidateDItemX(dItem2, dItem2->oldX, x, width); + InvalidateDItemY(dItem2, oldY, oldY, height); + dItem2->flags |= DITEM_DIRTY; + } + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + XCopyArea(tree->display, dInfo->pixmap, dInfo->pixmap, + tree->copyGC, + oldX, oldY, width, height, + oldX + offset, oldY); + Tree_InvalidateArea(tree, oldX, oldY, width, height); + dInfo->dirty[LEFT] = MIN(dInfo->dirty[LEFT], oldX + offset); + dInfo->dirty[TOP] = MIN(dInfo->dirty[TOP], oldY); + dInfo->dirty[RIGHT] = MAX(dInfo->dirty[RIGHT], oldX + offset + width); + dInfo->dirty[BOTTOM] = MAX(dInfo->dirty[BOTTOM], oldY + height); + continue; + } + + /* Copy */ + damageRgn = TkCreateRegion(); + if (TkScrollWindow(tree->tkwin, dInfo->scrollGC, + oldX, oldY, width, height, offset, 0, damageRgn)) + { +DisplayDelay(tree); + Tree_InvalidateRegion(tree, damageRgn); + } + TkDestroyRegion(damageRgn); + } + return numCopy; +} + +void TreeColumnProxy_Draw(TreeCtrl *tree) +{ + XGCValues gcValues; + unsigned long gcMask; + GC gc; + + gcValues.function = GXinvert; + gcValues.graphics_exposures = False; + gcMask = GCFunction | GCGraphicsExposures; + gc = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + + /* GXinvert doesn't work with XFillRectangle() on Win32 */ +#ifdef WIN32 + XDrawLine(tree->display, Tk_WindowId(tree->tkwin), gc, + tree->columnProxy.sx, + tree->inset, + tree->columnProxy.sx, + Tk_Height(tree->tkwin) - tree->inset); +#else + XFillRectangle(tree->display, Tk_WindowId(tree->tkwin), gc, + tree->columnProxy.sx, + tree->inset, + 1, + Tk_Height(tree->tkwin) - tree->inset * 2); +#endif + + Tk_FreeGC(tree->display, gc); +} + +void TreeColumnProxy_Undisplay(TreeCtrl *tree) +{ + if (tree->columnProxy.onScreen) + { + TreeColumnProxy_Draw(tree); + tree->columnProxy.onScreen = FALSE; + } +} + +void TreeColumnProxy_Display(TreeCtrl *tree) +{ + if (!tree->columnProxy.onScreen && (tree->columnProxy.xObj != NULL)) + { + tree->columnProxy.sx = tree->columnProxy.x; + TreeColumnProxy_Draw(tree); + tree->columnProxy.onScreen = TRUE; + } +} + +void Tree_Display(ClientData clientData) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + Range *range; + Tk_Window tkwin = tree->tkwin; + Drawable drawable = Tk_WindowId(tkwin); + int x, y, minX, minY, maxX, maxY, height, width; + int left, right, top, bottom; + int count; + int numCopy = 0, numDraw = 0; + +if (tree->debug.enable && tree->debug.display && 0) + dbwin("Tree_Display %s\n", Tk_PathName(tree->tkwin)); + + if (tree->deleted) + { + dInfo->flags &= ~(DINFO_REDRAW_PENDING); + return; + } + /* The width of one or more columns *might* have changed */ + if (dInfo->flags & DINFO_CHECK_COLUMN_WIDTH) + { + TreeColumn treeColumn = tree->columns; + int columnIndex = 0; + + while (treeColumn != NULL) + { + if (dInfo->columnWidth[columnIndex] != TreeColumn_UseWidth(treeColumn)) + { + dInfo->columnWidth[columnIndex] = TreeColumn_UseWidth(treeColumn); + dInfo->flags |= + DINFO_INVALIDATE | + DINFO_OUT_OF_DATE | + DINFO_REDO_RANGES | + DINFO_DRAW_HEADER; + } + columnIndex++; + treeColumn = TreeColumn_Next(treeColumn); + } + dInfo->flags &= ~DINFO_CHECK_COLUMN_WIDTH; + } + Range_RedoIfNeeded(tree); +#ifdef INCREMENTS + Increment_RedoIfNeeded(tree); +#endif + if (dInfo->xOrigin != tree->xOrigin) + { + dInfo->flags |= + DINFO_UPDATE_SCROLLBAR_X | + DINFO_OUT_OF_DATE | + DINFO_DRAW_HEADER; + } + if (dInfo->yOrigin != tree->yOrigin) + { + dInfo->flags |= + DINFO_UPDATE_SCROLLBAR_Y | + DINFO_OUT_OF_DATE; + } + if (dInfo->totalWidth != Tree_TotalWidth(tree)) + { + dInfo->totalWidth = Tree_TotalWidth(tree); + dInfo->flags |= + DINFO_SET_ORIGIN_X | + DINFO_UPDATE_SCROLLBAR_X | + DINFO_OUT_OF_DATE; + } + if (dInfo->totalHeight != Tree_TotalHeight(tree)) + { + dInfo->totalHeight = Tree_TotalHeight(tree); + dInfo->flags |= + DINFO_SET_ORIGIN_Y | + DINFO_UPDATE_SCROLLBAR_Y | + DINFO_OUT_OF_DATE; + } + if (dInfo->headerHeight != Tree_HeaderHeight(tree)) + { + dInfo->headerHeight = Tree_HeaderHeight(tree); + dInfo->flags |= + DINFO_OUT_OF_DATE | + DINFO_SET_ORIGIN_Y | + DINFO_UPDATE_SCROLLBAR_Y | + DINFO_DRAW_HEADER; + } + if (dInfo->flags & DINFO_SET_ORIGIN_X) + { + Tree_SetOriginX(tree, tree->xOrigin); + dInfo->flags &= ~DINFO_SET_ORIGIN_X; + } + if (dInfo->flags & DINFO_SET_ORIGIN_Y) + { + Tree_SetOriginY(tree, tree->yOrigin); + dInfo->flags &= ~DINFO_SET_ORIGIN_Y; + } + if (dInfo->flags & DINFO_UPDATE_SCROLLBAR_X) + { + Tree_UpdateScrollbarX(tree); + dInfo->flags &= ~DINFO_UPDATE_SCROLLBAR_X; + } + if (dInfo->flags & DINFO_UPDATE_SCROLLBAR_Y) + { + Tree_UpdateScrollbarY(tree); + dInfo->flags &= ~DINFO_UPDATE_SCROLLBAR_Y; + } + if (tree->deleted || !Tk_IsMapped(tkwin)) + { + dInfo->flags &= ~(DINFO_REDRAW_PENDING); + return; + } + if (dInfo->flags & DINFO_OUT_OF_DATE) + { + Tree_UpdateDInfo(tree); + dInfo->flags &= ~DINFO_OUT_OF_DATE; + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + dInfo->dirty[LEFT] = dInfo->dirty[TOP] = 100000; + dInfo->dirty[RIGHT] = dInfo->dirty[BOTTOM] = -100000; + } + + minX = tree->inset; + maxX = Tk_Width(tree->tkwin) - tree->inset; + minY = tree->inset + Tree_HeaderHeight(tree); + maxY = Tk_Height(tree->tkwin) - tree->inset; + + /* XOR off */ + TreeColumnProxy_Undisplay(tree); + TreeDragImage_Undisplay(tree->dragImage); + TreeMarquee_Undisplay(tree->marquee); + + if (dInfo->flags & DINFO_DRAW_HEADER) + { + if (tree->showHeader) + { + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + Tree_DrawHeader(tree, dInfo->pixmap, 0 - tree->xOrigin, + tree->inset); + dInfo->dirty[LEFT] = minX; + dInfo->dirty[TOP] = tree->inset; + dInfo->dirty[RIGHT] = maxX; + dInfo->dirty[BOTTOM] = minY; + } + else + Tree_DrawHeader(tree, drawable, 0 - tree->xOrigin, tree->inset); + } + dInfo->flags &= ~DINFO_DRAW_HEADER; + } + + if (tree->vertical) + { + numCopy = ScrollVerticalComplex(tree); + ScrollHorizontalSimple(tree); + } + else + { + ScrollVerticalSimple(tree); + numCopy = ScrollHorizontalComplex(tree); + } + + /* If we scrolled, then copy the entire pixmap, plus the header + * if needed. */ + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + if ((dInfo->xOrigin != tree->xOrigin) || + (dInfo->yOrigin != tree->yOrigin)) + { + dInfo->dirty[LEFT] = minX; + /* might include header */ + dInfo->dirty[TOP] = MIN(dInfo->dirty[TOP], minY); + dInfo->dirty[RIGHT] = maxX; + dInfo->dirty[BOTTOM] = maxY; + } + } + + dInfo->xOrigin = tree->xOrigin; + dInfo->yOrigin = tree->yOrigin; + + /* Does this need to be here? */ + dInfo->flags &= ~(DINFO_REDRAW_PENDING); + + /* See if there are any dirty items */ + count = 0; + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + if (dItem->flags & DITEM_DIRTY) + { + count++; + break; + } + } + + /* Display dirty items */ + if (count > 0) + { + Drawable pixmap = drawable; + if (tree->doubleBuffer != DOUBLEBUFFER_NONE) + { + /* Allocate pixmap for largest item */ + width = MIN(maxX - minX, dInfo->itemWidth); + height = MIN(maxY - minY, dInfo->itemHeight); + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), + width, height, Tk_Depth(tkwin)); + } + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + drawable = dInfo->pixmap; + for (dItem = dInfo->dItem; + dItem != NULL; + dItem = dItem->next) + { + if (!(dItem->flags & DITEM_DIRTY)) + continue; + + if (dItem->flags & DITEM_ALL_DIRTY) + { + left = dItem->x; + right = dItem->x + dItem->width; + top = dItem->y; + bottom = dItem->y + dItem->height; + } + else + { + left = dItem->x + dItem->dirty[LEFT]; + right = dItem->x + dItem->dirty[RIGHT]; + top = dItem->y + dItem->dirty[TOP]; + bottom = dItem->y + dItem->dirty[BOTTOM]; + } + if (left < minX) + left = minX; + if (right > maxX) + right = maxX; + if (top < minY) + top = minY; + if (bottom > maxY) + bottom = maxY; + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + dInfo->dirty[LEFT] = MIN(dInfo->dirty[LEFT], left); + dInfo->dirty[TOP] = MIN(dInfo->dirty[TOP], top); + dInfo->dirty[RIGHT] = MAX(dInfo->dirty[RIGHT], right); + dInfo->dirty[BOTTOM] = MAX(dInfo->dirty[BOTTOM], bottom); + } + + if (tree->doubleBuffer != DOUBLEBUFFER_NONE) + { + /* The top-left corner of the drawable is at this + * point in the canvas */ + tree->drawableXOrigin = left + tree->xOrigin; + tree->drawableYOrigin = top + tree->yOrigin; + + TreeItem_Draw(tree, dItem->item, + dItem->x - left, + dItem->y - top, + dItem->width, dItem->height, + pixmap, + 0, right - left, + dItem->index); + if (tree->columnTreeVis && tree->showLines) + { + TreeItem_DrawLines(tree, dItem->item, + dItem->x - left, + dItem->y - top, + dItem->width, dItem->height, + pixmap); + } + if (tree->columnTreeVis && tree->showButtons) + { + TreeItem_DrawButton(tree, dItem->item, + dItem->x - left, + dItem->y - top, + dItem->width, dItem->height, + pixmap); + } + XCopyArea(tree->display, pixmap, drawable, + tree->copyGC, + 0, 0, + right - left, bottom - top, + left, top); + } + /* DOUBLEBUFFER_NONE */ + else + { + /* The top-left corner of the drawable is at this + * point in the canvas */ + tree->drawableXOrigin = tree->xOrigin; + tree->drawableYOrigin = tree->yOrigin; + + TreeItem_Draw(tree, dItem->item, + dItem->x, + dItem->y, + dItem->width, dItem->height, + pixmap, + minX, maxX, + dItem->index); + if (tree->columnTreeVis && tree->showLines) + { + TreeItem_DrawLines(tree, dItem->item, + dItem->x, + dItem->y, + dItem->width, dItem->height, + pixmap); + } + if (tree->columnTreeVis && tree->showButtons) + { + TreeItem_DrawButton(tree, dItem->item, + dItem->x, + dItem->y, + dItem->width, dItem->height, + pixmap); + } + } +DisplayDelay(tree); +numDraw++; + + dItem->oldX = dItem->x; + dItem->oldY = dItem->y; + dItem->flags &= ~(DITEM_DIRTY | DITEM_ALL_DIRTY); + } + if (tree->doubleBuffer != DOUBLEBUFFER_NONE) + Tk_FreePixmap(tree->display, pixmap); + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + drawable = dInfo->pixmap; + +if (tree->debug.enable && tree->debug.display) + dbwin("copy %d draw %d %s\n", numCopy, numDraw, Tk_PathName(tree->tkwin)); + + x = 0 - tree->xOrigin; + y = 0 - tree->yOrigin; + + if (tree->vertical) + { + /* Erase area to right of last Range */ + if (x + Tree_TotalWidth(tree) < maxX) + { + Tk_Fill3DRectangle(tkwin, drawable, tree->border, + x + Tree_TotalWidth(tree), minY, + maxX - (x + Tree_TotalWidth(tree)), + maxY - minY, 0, TK_RELIEF_FLAT); + } + } + else + { + /* Erase area below last Range */ + if (y + Tree_TotalHeight(tree) < maxY) + { + Tk_Fill3DRectangle(tkwin, drawable, tree->border, + tree->inset, + y + Tree_TotalHeight(tree), + Tk_Width(tkwin) - tree->inset * 2, + maxY - (y + Tree_TotalHeight(tree)), + 0, TK_RELIEF_FLAT); + } + } + + for (range = dInfo->rangeFirstD; + range != NULL; + range = range->next) + { + if (tree->vertical) + { + left = MAX(x + range->offset, minX); + right = MIN(x + range->offset + range->totalWidth, maxX); + top = MAX(y + range->totalHeight, minY); + bottom = maxY; + + /* Erase area below Range */ + if (top < bottom) + { + Tk_Fill3DRectangle(tkwin, drawable, tree->border, + left, + top, + right - left, + bottom - top, + 0, TK_RELIEF_FLAT); + } + } + else + { + left = MAX(x + range->totalWidth, minX); + right = maxX; + top = MAX(y + range->offset, minY); + bottom = MIN(y + range->offset + range->totalHeight, maxY); + + /* Erase area to right of Range */ + if (left < right) + { + Tk_Fill3DRectangle(tkwin, drawable, tree->border, + left, top, + right - left, + bottom - top, 0, TK_RELIEF_FLAT); + } + } + if (range == dInfo->rangeLastD) + break; + } + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + drawable = Tk_WindowId(tree->tkwin); + XCopyArea(tree->display, dInfo->pixmap, drawable, + tree->copyGC, + dInfo->dirty[LEFT], dInfo->dirty[TOP], + dInfo->dirty[RIGHT] - dInfo->dirty[LEFT], + dInfo->dirty[BOTTOM] - dInfo->dirty[TOP], + dInfo->dirty[LEFT],dInfo-> dirty[TOP]); + } + + /* XOR on */ + TreeMarquee_Display(tree->marquee); + TreeDragImage_Display(tree->dragImage); + TreeColumnProxy_Display(tree); + + if (tree->doubleBuffer == DOUBLEBUFFER_NONE) + dInfo->flags |= DINFO_DRAW_HIGHLIGHT | DINFO_DRAW_BORDER; + + /* Draw focus rectangle (outside of 3D-border) */ + if ((dInfo->flags & DINFO_DRAW_HIGHLIGHT) && (tree->highlightWidth > 0)) + { + GC fgGC, bgGC; + + bgGC = Tk_GCForColor(tree->highlightBgColorPtr, drawable); + if (tree->gotFocus) + fgGC = Tk_GCForColor(tree->highlightColorPtr, drawable); + else + fgGC = bgGC; + TkpDrawHighlightBorder(tkwin, fgGC, bgGC, tree->highlightWidth, drawable); + dInfo->flags &= ~DINFO_DRAW_HIGHLIGHT; + } + + /* Draw 3D-border (inside of focus rectangle) */ + if ((dInfo->flags & DINFO_DRAW_BORDER) && (tree->borderWidth > 0)) + { + Tk_Draw3DRectangle(tkwin, drawable, tree->border, tree->highlightWidth, + tree->highlightWidth, Tk_Width(tkwin) - tree->highlightWidth * 2, + Tk_Height(tkwin) - tree->highlightWidth * 2, tree->borderWidth, + tree->relief); + dInfo->flags &= ~DINFO_DRAW_BORDER; + } +} + +static int A_IncrementFindX(TreeCtrl *tree, int offset) +{ + int totWidth = Tree_TotalWidth(tree); + int xIncr = tree->xScrollIncrement; + int index, indexMax; + + indexMax = totWidth / xIncr; + if (totWidth % xIncr == 0) + indexMax--; + if (offset < 0) + offset = 0; + index = offset / xIncr; + if (index > indexMax) + index = indexMax; + return index; +} + +static int A_IncrementFindY(TreeCtrl *tree, int offset) +{ + int totHeight = Tree_TotalHeight(tree); + int yIncr = tree->yScrollIncrement; + int index, indexMax; + + indexMax = totHeight / yIncr; + if (totHeight % yIncr == 0) + indexMax--; + if (offset < 0) + offset = 0; + index = offset / yIncr; + if (index > indexMax) + index = indexMax; + return index; +} + +int Increment_FindX(TreeCtrl *tree, int offset) +{ +#ifdef INCREMENTS + if (tree->xScrollIncrement <= 0) + { + Increment_RedoIfNeeded(tree); + return B_IncrementFindX(tree, offset); + } +#endif + return A_IncrementFindX(tree, offset); +} + +int Increment_FindY(TreeCtrl *tree, int offset) +{ +#ifdef INCREMENTS + if (tree->yScrollIncrement <= 0) + { + Increment_RedoIfNeeded(tree); + return B_IncrementFindY(tree, offset); + } +#endif + return A_IncrementFindY(tree, offset); +} + +int Increment_ToOffsetX(TreeCtrl *tree, int index) +{ +#ifdef INCREMENTS + DInfo *dInfo = (DInfo *) tree->dInfo; + + if (tree->xScrollIncrement <= 0) + { + if (index < 0 || index >= dInfo->xScrollIncrementCount) + panic("Increment_ToOffsetX: bad index %d (must be 0-%d)", + index, dInfo->xScrollIncrementCount-1); + return dInfo->xScrollIncrements[index]; + } +#endif + return index * tree->xScrollIncrement; +} + +int Increment_ToOffsetY(TreeCtrl *tree, int index) +{ +#ifdef INCREMENTS + DInfo *dInfo = (DInfo *) tree->dInfo; + + if (tree->yScrollIncrement <= 0) + { + if (index < 0 || index >= dInfo->yScrollIncrementCount) + { + panic("Increment_ToOffsetY: bad index %d (must be 0-%d)\ntotHeight %d visHeight %d", + index, dInfo->yScrollIncrementCount - 1, + Tree_TotalHeight(tree), Tk_Height(tree->tkwin) - Tree_HeaderHeight(tree) - tree->inset); + } + return dInfo->yScrollIncrements[index]; + } +#endif + return index * tree->yScrollIncrement; +} + +static void GetScrollFractions(int screen1, int screen2, int object1, + int object2, double fractions[2]) +{ + double range, f1, f2; + + range = object2 - object1; + if (range <= 0) + { + f1 = 0; + f2 = 1.0; + } + else + { + f1 = (screen1 - object1) / range; + if (f1 < 0) + f1 = 0.0; + f2 = (screen2 - object1) / range; + if (f2 > 1.0) + f2 = 1.0; + if (f2 < f1) + f2 = f1; + } + + fractions[0] = f1; + fractions[1] = f2; +} + +void Tree_GetScrollFractionsX(TreeCtrl *tree, double fractions[2]) +{ + int left = tree->xOrigin + tree->inset; + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int totWidth = Tree_TotalWidth(tree); + int index, offset; + + /* The tree is empty, or everything fits in the window */ + if (totWidth <= visWidth) + { + fractions[0] = 0.0; + fractions[1] = 1.0; + return; + } + + if (visWidth <= 1) + { + GetScrollFractions(left, left + 1, 0, totWidth, fractions); + return; + } + + /* Find incrementLeft when scrolled to extreme right */ + index = Increment_FindX(tree, totWidth - visWidth); + offset = Increment_ToOffsetX(tree, index); + if (offset < totWidth - visWidth) + { + index++; + offset = Increment_ToOffsetX(tree, index); + } + + /* Add some fake content to right */ + if (offset + visWidth > totWidth) + totWidth = offset + visWidth; + + GetScrollFractions(left, left + visWidth, 0, totWidth, fractions); +} + +void Tree_GetScrollFractionsY(TreeCtrl *tree, double fractions[2]) +{ + int topInset = tree->inset + Tree_HeaderHeight(tree); + int top = topInset + tree->yOrigin; /* canvas coords */ + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totHeight = Tree_TotalHeight(tree); + int index, offset; + + /* The tree is empty, or everything fits in the window */ + if (totHeight <= visHeight) + { + fractions[0] = 0.0; + fractions[1] = 1.0; + return; + } + + if (visHeight <= 1) + { + GetScrollFractions(top, top + 1, 0, totHeight, fractions); + return; + } + + /* Find incrementTop when scrolled to bottom */ + index = Increment_FindY(tree, totHeight - visHeight); + offset = Increment_ToOffsetY(tree, index); + if (offset < totHeight - visHeight) + { + index++; + offset = Increment_ToOffsetY(tree, index); + } + + /* Add some fake content to bottom */ + if (offset + visHeight > totHeight) + totHeight = offset + visHeight; + + GetScrollFractions(top, top + visHeight, 0, totHeight, fractions); +} + +void Tree_SetOriginX(TreeCtrl *tree, int xOrigin) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int totWidth = Tree_TotalWidth(tree); + int visWidth = Tk_Width(tree->tkwin) - tree->inset * 2; + int index, indexMax, offset; + + /* The tree is empty, or everything fits in the window */ + if (totWidth <= visWidth) + { + xOrigin = 0 - tree->inset; + if (xOrigin != tree->xOrigin) + { + tree->xOrigin = xOrigin; +#ifdef INCREMENTS + dInfo->incrementLeft = 0; +#endif + Tree_EventuallyRedraw(tree); + } + return; + } + + if (visWidth > 1) + { + /* Find incrementLeft when scrolled to extreme right */ + indexMax = Increment_FindX(tree, totWidth - visWidth); + offset = Increment_ToOffsetX(tree, indexMax); + if (offset < totWidth - visWidth) + { + indexMax++; + offset = Increment_ToOffsetX(tree, indexMax); + } + + /* Add some fake content to right */ + if (offset + visWidth > totWidth) + totWidth = offset + visWidth; + } + else + indexMax = Increment_FindX(tree, totWidth); + + xOrigin += tree->inset; /* origin -> canvas */ + index = Increment_FindX(tree, xOrigin); + + /* Don't scroll too far left */ + if (index < 0) + index = 0; + + /* Don't scroll too far right */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetX(tree, index); + xOrigin = offset - tree->inset; + + if (xOrigin == tree->xOrigin) + return; + + tree->xOrigin = xOrigin; +#ifdef INCREMENTS + dInfo->incrementLeft = index; +#endif + + Tree_EventuallyRedraw(tree); +} + +void Tree_SetOriginY(TreeCtrl *tree, int yOrigin) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int topInset = tree->inset + Tree_HeaderHeight(tree); + int visHeight = Tk_Height(tree->tkwin) - topInset - tree->inset; + int totHeight = Tree_TotalHeight(tree); + int index, indexMax, offset; + + /* The tree is empty, or everything fits in the window */ + if (totHeight <= visHeight) + { + yOrigin = 0 - topInset; + if (yOrigin != tree->yOrigin) + { + tree->yOrigin = yOrigin; +#ifdef INCREMENTS + dInfo->incrementTop = 0; +#endif + Tree_EventuallyRedraw(tree); + } + return; + } + + if (visHeight > 1) + { + /* Find incrementTop when scrolled to bottom */ + indexMax = Increment_FindY(tree, totHeight - visHeight); + offset = Increment_ToOffsetY(tree, indexMax); + if (offset < totHeight - visHeight) + { + indexMax++; + offset = Increment_ToOffsetY(tree, indexMax); + } + + /* Add some fake content to bottom */ + if (offset + visHeight > totHeight) + totHeight = offset + visHeight; + } + else + indexMax = Increment_FindY(tree, totHeight); + + yOrigin += topInset; /* origin -> canvas */ + index = Increment_FindY(tree, yOrigin); + + /* Don't scroll too far left */ + if (index < 0) + index = 0; + + /* Don't scroll too far right */ + if (index > indexMax) + index = indexMax; + + offset = Increment_ToOffsetY(tree, index); + yOrigin = offset - topInset; + if (yOrigin == tree->yOrigin) + return; + + tree->yOrigin = yOrigin; +#ifdef INCREMENTS + dInfo->incrementTop = index; +#endif + + Tree_EventuallyRedraw(tree); +} + +void Tree_EventuallyRedraw(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + if ((dInfo->flags & DINFO_REDRAW_PENDING) || + tree->deleted || + !Tk_IsMapped(tree->tkwin)) + { + return; + } + dInfo->flags |= DINFO_REDRAW_PENDING; + Tcl_DoWhenIdle(Tree_Display, (ClientData) tree); +} + +void Tree_RelayoutWindow(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + FreeDItems(tree, dInfo->dItem, NULL, 0); + dInfo->dItem = NULL; + dInfo->flags |= + DINFO_REDO_RANGES | + DINFO_OUT_OF_DATE | + DINFO_CHECK_COLUMN_WIDTH | + DINFO_SET_ORIGIN_X | + DINFO_SET_ORIGIN_Y | + DINFO_UPDATE_SCROLLBAR_X | + DINFO_UPDATE_SCROLLBAR_Y; + if (tree->highlightWidth > 0) + dInfo->flags |= DINFO_DRAW_HIGHLIGHT; + if (tree->borderWidth > 0) + dInfo->flags |= DINFO_DRAW_BORDER; + dInfo->xOrigin = tree->xOrigin; + dInfo->yOrigin = tree->yOrigin; + + if (tree->doubleBuffer == DOUBLEBUFFER_WINDOW) + { + if (dInfo->pixmap == None) + { + dInfo->pixmap = Tk_GetPixmap(tree->display, + Tk_WindowId(tree->tkwin), + Tk_Width(tree->tkwin), + Tk_Height(tree->tkwin), + Tk_Depth(tree->tkwin)); + } + else if ((tree->prevWidth != Tk_Width(tree->tkwin)) || + (tree->prevHeight != Tk_Height(tree->tkwin))) + { + Tk_FreePixmap(tree->display, dInfo->pixmap); + dInfo->pixmap = Tk_GetPixmap(tree->display, + Tk_WindowId(tree->tkwin), + Tk_Width(tree->tkwin), + Tk_Height(tree->tkwin), + Tk_Depth(tree->tkwin)); + } + } + else if (dInfo->pixmap != None) + { + Tk_FreePixmap(tree->display, dInfo->pixmap); + dInfo->pixmap = None; + } + + Tree_EventuallyRedraw(tree); +} + +void Tree_FocusChanged(TreeCtrl *tree, int gotFocus) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + int redraw = FALSE; + TreeItem item; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + int stateOn, stateOff; + + tree->gotFocus = gotFocus; + + if (gotFocus) + stateOff = 0, stateOn = STATE_FOCUS; + else + stateOff = STATE_FOCUS, stateOn = 0; + + /* Slow. Change state of every item */ + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + TreeItem_ChangeState(tree, item, stateOff, stateOn); + hPtr = Tcl_NextHashEntry(&search); + } + + if (tree->highlightWidth > 0) + { + dInfo->flags |= DINFO_DRAW_HIGHLIGHT; + redraw = TRUE; + } + if (redraw) + Tree_EventuallyRedraw(tree); +} + +void Tree_FreeItemDInfo(TreeCtrl *tree, TreeItem item1, TreeItem item2) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + TreeItem item = item1; + int changed = 0; + + while (item != NULL) + { + dItem = (DItem *) TreeItem_GetDInfo(tree, item); + if (dItem != NULL) + { + FreeDItems(tree, dItem, dItem->next, 1); + changed = 1; + } + if (item == item2 || item2 == NULL) + break; + item = TreeItem_Next(tree, item); + } +changed = 1; + if (changed) + { + dInfo->flags |= DINFO_OUT_OF_DATE; + Tree_EventuallyRedraw(tree); + } +} + +void Tree_InvalidateItemDInfo(TreeCtrl *tree, TreeItem item1, TreeItem item2) +{ +/* DInfo *dInfo = (DInfo *) tree->dInfo; */ + DItem *dItem; + TreeItem item = item1; + int changed = 0; + + while (item != NULL) + { + dItem = (DItem *) TreeItem_GetDInfo(tree, item); + if (dItem != NULL) + { + dItem->flags |= (DITEM_DIRTY | DITEM_ALL_DIRTY); + changed = 1; + } + if (item == item2 || item2 == NULL) + break; + item = TreeItem_Next(tree, item); + } + if (changed) + { +/* dInfo->flags |= DINFO_OUT_OF_DATE; */ + Tree_EventuallyRedraw(tree); + } +} + +void Tree_DInfoChanged(TreeCtrl *tree, int flags) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + + dInfo->flags |= flags; + Tree_EventuallyRedraw(tree); +} + +void Tree_InvalidateArea(TreeCtrl *tree, int x1, int y1, int x2, int y2) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + + if (x1 >= x2 || y1 >= y2) + return; + + if ((y2 > tree->inset) && (y1 < tree->inset + Tree_HeaderHeight(tree))) + dInfo->flags |= DINFO_DRAW_HEADER; + + dItem = dInfo->dItem; + while (dItem != NULL) + { + if (!(dItem->flags & DITEM_ALL_DIRTY) && + (x2 > dItem->x) && (x1 < dItem->x + dItem->width) && + (y2 > dItem->y) && (y1 < dItem->y + dItem->height)) + { + InvalidateDItemX(dItem, dItem->x, x1, x2 - x1); + InvalidateDItemY(dItem, dItem->y, y1, y2 - y1); + dItem->flags |= DITEM_DIRTY; + } + dItem = dItem->next; + } + + /* Could check border and highlight separately */ + if (tree->inset > 0) + { + if ((x1 < tree->inset) || + (y1 < tree->inset) || + (x2 > Tk_Width(tree->tkwin) - tree->inset) || + (y2 > Tk_Height(tree->tkwin) - tree->inset)) + { + if (tree->highlightWidth > 0) + dInfo->flags |= DINFO_DRAW_HIGHLIGHT; + if (tree->borderWidth > 0) + dInfo->flags |= DINFO_DRAW_BORDER; + } + } + +if (tree->debug.enable && tree->debug.display && tree->debug.eraseColor) +{ + XFillRectangle(tree->display, Tk_WindowId(tree->tkwin), + tree->debug.gcErase, x1, y1, x2 - x1, y2 - y1); + DisplayDelay(tree); +} +} + +void Tree_InvalidateRegion(TreeCtrl *tree, TkRegion region) +{ + XRectangle rect; + + TkClipBox(region, &rect); + Tree_InvalidateArea(tree, rect.x, rect.y, rect.x + rect.width, + rect.y + rect.height); +} + +void Tree_InvalidateItemArea(TreeCtrl *tree, int x1, int y1, int x2, int y2) +{ + if (x1 < tree->inset) + x1 = tree->inset; + if (y1 < tree->inset + Tree_HeaderHeight(tree)) + y1 = tree->inset + Tree_HeaderHeight(tree); + if (x2 > Tk_Width(tree->tkwin) - tree->inset) + x2 = Tk_Width(tree->tkwin) - tree->inset; + if (y2 > Tk_Height(tree->tkwin) - tree->inset) + y2 = Tk_Height(tree->tkwin) - tree->inset; + Tree_InvalidateArea(tree, x1, y1, x2, y2); +} + +void Tree_InvalidateWindow(TreeCtrl *tree) +{ + Tree_InvalidateArea(tree, 0, 0, + Tk_Width(tree->tkwin), + Tk_Height(tree->tkwin)); +} + +void Tree_RedrawArea(TreeCtrl *tree, int x1, int y1, int x2, int y2) +{ + Tree_InvalidateArea(tree, x1, y1, x2, y2); + Tree_EventuallyRedraw(tree); +} + +void Tree_RedrawItemArea(TreeCtrl *tree, int x1, int y1, int x2, int y2) +{ + Tree_InvalidateItemArea(tree, x1, y1, x2, y2); + Tree_EventuallyRedraw(tree); +} + +void TreeDInfo_Init(TreeCtrl *tree) +{ + DInfo *dInfo; + XGCValues gcValues; + + dInfo = (DInfo *) ckalloc(sizeof(DInfo)); + memset(dInfo, '\0', sizeof(DInfo)); + gcValues.graphics_exposures = True; + dInfo->scrollGC = Tk_GetGC(tree->tkwin, GCGraphicsExposures, &gcValues); + dInfo->flags = DINFO_OUT_OF_DATE; + tree->dInfo = (TreeDInfo) dInfo; +} + +void TreeDInfo_Free(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + Range *range = dInfo->rangeFirst; + + if (dInfo->rItem != NULL) + ckfree((char *) dInfo->rItem); + FreeDItems(tree, dInfo->dItem, NULL, 0); + while (range != NULL) + range = Range_Free(tree, range); + Tk_FreeGC(tree->display, dInfo->scrollGC); + if (dInfo->flags & DINFO_REDRAW_PENDING) + Tcl_CancelIdleCall(Tree_Display, (ClientData) tree); + if (dInfo->pixmap != None) + Tk_FreePixmap(tree->display, dInfo->pixmap); + if (dInfo->xScrollIncrements != NULL) + ckfree((char *) dInfo->xScrollIncrements); + if (dInfo->yScrollIncrements != NULL) + ckfree((char *) dInfo->yScrollIncrements); + WFREE(dInfo, DInfo); +} + +void DumpDInfo(TreeCtrl *tree) +{ + DInfo *dInfo = (DInfo *) tree->dInfo; + DItem *dItem; + Range *range; + + dbwin("DumpDInfo: itemW,H %d,%d totalW,H %d,%d flags %0x\n", + dInfo->itemWidth, dInfo->itemHeight, + dInfo->totalWidth, dInfo->totalHeight, + dInfo->flags); + dItem = dInfo->dItem; + while (dItem != NULL) + { + if (dItem->item == NULL) + dbwin(" item NULL\n"); + else + dbwin(" item %d x,y,w,h %d,%d,%d,%d dirty %d,%d,%d,%d flags %0X\n", + TreeItem_GetID(tree, dItem->item), + dItem->x, dItem->y, dItem->width, dItem->height, + dItem->dirty[LEFT], dItem->dirty[TOP], + dItem->dirty[RIGHT], dItem->dirty[BOTTOM], + dItem->flags); + dItem = dItem->next; + } + + dbwin(" dInfo.rangeFirstD %p dInfo.rangeLastD %p\n", + dInfo->rangeFirstD, dInfo->rangeLastD); + for (range = dInfo->rangeFirstD; + range != NULL; + range = range->next) + { + dbwin(" Range: totalW,H %d,%d offset %d\n", range->totalWidth, + range->totalHeight, range->offset); + if (range == dInfo->rangeLastD) + break; + } + + dbwin(" dInfo.rangeFirst %p dInfo.rangeLast %p\n", + dInfo->rangeFirst, dInfo->rangeLast); + for (range = dInfo->rangeFirst; + range != NULL; + range = range->next) + { + dbwin(" Range: first %p last %p totalW,H %d,%d offset %d\n", + range->first, range->last, + range->totalWidth, range->totalHeight, range->offset); + } +} + diff --git a/generic/tkTreeDrag.c b/generic/tkTreeDrag.c new file mode 100644 index 0000000..f6cc1e9 --- /dev/null +++ b/generic/tkTreeDrag.c @@ -0,0 +1,436 @@ +#include "tkTreeCtrl.h" + +typedef struct DragElem DragElem; +typedef struct DragImage DragImage; + +struct DragElem +{ + int x, y, width, height; + DragElem *next; +}; + +struct DragImage +{ + TreeCtrl *tree; + int visible; + int x, y; /* offset to draw at in canvas coords */ + int bounds[4]; /* bounds of all DragElems */ + DragElem *elem; + int onScreen; /* TRUE if is displayed */ + int sx, sy; /* Window coords where displayed */ +}; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(DragImage, visible), + 0, (ClientData) NULL, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +static Tk_OptionTable optionTable = NULL; + +static DragElem *DragElem_Alloc(DragImage *dragImage) +{ + DragElem *elem = (DragElem *) ckalloc(sizeof(DragElem)); + DragElem *walk = dragImage->elem; + memset(elem, '\0', sizeof(DragElem)); + if (dragImage->elem == NULL) + dragImage->elem = elem; + else + { + while (walk->next != NULL) + walk = walk->next; + walk->next = elem; + } + return elem; +} + +static DragElem *DragElem_Free(DragImage *dragImage, DragElem *elem) +{ + DragElem *next = elem->next; + WFREE(elem, DragElem); + return next; +} + +int TreeDragImage_Init(TreeCtrl *tree) +{ + DragImage *dragImage; + + if (optionTable == NULL) + optionTable = Tk_CreateOptionTable(tree->interp, optionSpecs); + + dragImage = (DragImage *) ckalloc(sizeof(DragImage)); + memset(dragImage, '\0', sizeof(DragImage)); + dragImage->tree = tree; + if (Tk_InitOptions(tree->interp, (char *) dragImage, optionTable, + tree->tkwin) != TCL_OK) + { + WFREE(dragImage, DragImage); + return TCL_ERROR; + } + tree->dragImage = (TreeDragImage) dragImage; + return TCL_OK; +} + +void TreeDragImage_Free(TreeDragImage dragImage_) +{ + DragImage *dragImage = (DragImage *) dragImage_; + DragElem *elem = dragImage->elem; + + while (elem != NULL) + elem = DragElem_Free(dragImage, elem); + Tk_FreeConfigOptions((char *) dragImage, optionTable, + dragImage->tree->tkwin); + WFREE(dragImage, DragImage); +} + +void TreeDragImage_Display(TreeDragImage dragImage_) +{ + DragImage *dragImage = (DragImage *) dragImage_; + TreeCtrl *tree = dragImage->tree; + + if (!dragImage->onScreen && dragImage->visible) + { + dragImage->sx = 0 - tree->xOrigin; + dragImage->sy = 0 - tree->yOrigin; + TreeDragImage_Draw(dragImage_, Tk_WindowId(tree->tkwin), dragImage->sx, dragImage->sy); + dragImage->onScreen = TRUE; + } +} + +void TreeDragImage_Undisplay(TreeDragImage dragImage_) +{ + DragImage *dragImage = (DragImage *) dragImage_; + TreeCtrl *tree = dragImage->tree; + + if (dragImage->onScreen) + { + TreeDragImage_Draw(dragImage_, Tk_WindowId(tree->tkwin), dragImage->sx, dragImage->sy); + dragImage->onScreen = FALSE; + } +} + +static int DragImage_Config(DragImage *dragImage, int objc, Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = dragImage->tree; + Tk_SavedOptions savedOptions; + int mask, result; + + result = Tk_SetOptions(tree->interp, (char *) dragImage, optionTable, + objc, objv, tree->tkwin, &savedOptions, &mask); + if (result != TCL_OK) + { + Tk_RestoreSavedOptions(&savedOptions); + return TCL_ERROR; + } + Tk_FreeSavedOptions(&savedOptions); +#if 0 + if (mask & CONF_VISIBLE) + { + if (dragImage->visible) + TreeDragImage_Display(); + else + TreeDragImage_Unisplay(); + } +#endif + return TCL_OK; +} + +void TreeDragImage_Draw(TreeDragImage dragImage_, Drawable drawable, int x, int y) +{ + DragImage *dragImage = (DragImage *) dragImage_; + TreeCtrl *tree = dragImage->tree; + DragElem *elem = dragImage->elem; + DotState dotState; + +/* if (!dragImage->visible) + return; */ + if (elem == NULL) + return; + + DotRect_Setup(tree, drawable, &dotState); + + while (elem != NULL) + { + DotRect_Draw(&dotState, + x + dragImage->x + elem->x, + y + dragImage->y + elem->y, + elem->width, elem->height); + elem = elem->next; + } + + DotRect_Restore(&dotState); +} + +int DragImageCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + DragImage *dragImage = (DragImage *) tree->dragImage; + static CONST char *commandNames[] = { "add", "cget", "clear", "configure", + "offset", "visible", (char *) NULL }; + enum { COMMAND_ADD, COMMAND_CGET, COMMAND_CLEAR, COMMAND_CONFIGURE, + COMMAND_OFFSET, COMMAND_VISIBLE }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T dragimage add I ?C? ?E ...? */ + case COMMAND_ADD: + { + XRectangle rects[20]; + TreeItem item; + TreeItemColumn itemColumn; + TreeColumn treeColumn; + int i, count, columnIndex; + int indent, width, totalWidth; + int x, y, w, h; + DragElem *elem; + StyleDrawArgs drawArgs; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "item ?column? ?element ...?"); + return TCL_ERROR; + } + if (TreeItem_FromObj(tree, objv[3], &item, 0) != TCL_OK) + return TCL_ERROR; + + Tree_ItemBbox(tree, item, &x, &y, &w, &h); + + drawArgs.tree = tree; + drawArgs.drawable = None; + drawArgs.state = TreeItem_GetState(tree, item); + drawArgs.y = y; + drawArgs.height = h; + + TreeDragImage_Undisplay(tree->dragImage); + + if (objc > 4) + { + if (TreeItem_ColumnFromObj(tree, item, objv[4], &itemColumn, &columnIndex) != TCL_OK) + { + TreeDragImage_Display(tree->dragImage); + return TCL_ERROR; + } + drawArgs.style = TreeItemColumn_GetStyle(tree, itemColumn); + if (drawArgs.style == NULL) + { + TreeDragImage_Display(tree->dragImage); + break; + } + totalWidth = 0; + treeColumn = tree->columns; + for (i = 0; i < columnIndex; i++) + { + totalWidth += TreeColumn_UseWidth(treeColumn); + treeColumn = TreeColumn_Next(treeColumn); + } + if (TreeColumn_Index(treeColumn) == tree->columnTree) + indent = TreeItem_Indent(tree, item); + else + indent = 0; + drawArgs.x = x + indent + totalWidth; + drawArgs.width = TreeColumn_UseWidth(treeColumn) - indent; + drawArgs.justify = TreeColumn_Justify(treeColumn); + count = TreeStyle_GetElemRects(&drawArgs, objc - 5, objv + 5, rects); + if (count == -1) + { + TreeDragImage_Display(tree->dragImage); + return TCL_ERROR; + } + for (i = 0; i < count; i++) + { + elem = DragElem_Alloc(dragImage); + elem->x = rects[i].x; + elem->y = rects[i].y; + elem->width = rects[i].width; + elem->height = rects[i].height; + } + } + else + { + totalWidth = 0; + treeColumn = tree->columns; + itemColumn = TreeItem_GetFirstColumn(tree, item); + while (itemColumn != NULL) + { + width = TreeColumn_UseWidth(treeColumn); + if (TreeColumn_Index(treeColumn) == tree->columnTree) + indent = TreeItem_Indent(tree, item); + else + indent = 0; + drawArgs.style = TreeItemColumn_GetStyle(tree, itemColumn); + if (drawArgs.style != NULL) + { + drawArgs.x = x + indent + totalWidth; + drawArgs.width = width - indent; + drawArgs.justify = TreeColumn_Justify(treeColumn); + count = TreeStyle_GetElemRects(&drawArgs, 0, NULL, rects); + if (count == -1) + { + TreeDragImage_Display(tree->dragImage); + return TCL_ERROR; + } + for (i = 0; i < count; i++) + { + elem = DragElem_Alloc(dragImage); + elem->x = rects[i].x; + elem->y = rects[i].y; + elem->width = rects[i].width; + elem->height = rects[i].height; + } + } + totalWidth += width; + treeColumn = TreeColumn_Next(treeColumn); + itemColumn = TreeItemColumn_GetNext(tree, itemColumn); + } + } + dragImage->bounds[0] = 100000; + dragImage->bounds[1] = 100000; + dragImage->bounds[2] = -100000; + dragImage->bounds[3] = -100000; + for (elem = dragImage->elem; + elem != NULL; + elem = elem->next) + { + if (elem->x < dragImage->bounds[0]) + dragImage->bounds[0] = elem->x; + if (elem->y < dragImage->bounds[1]) + dragImage->bounds[1] = elem->y; + if (elem->x + elem->width > dragImage->bounds[2]) + dragImage->bounds[2] = elem->x + elem->width; + if (elem->y + elem->height > dragImage->bounds[3]) + dragImage->bounds[3] = elem->y + elem->height; + } + TreeDragImage_Display(tree->dragImage); + break; + } + + /* T dragimage cget option */ + case COMMAND_CGET: + { + Tcl_Obj *resultObjPtr; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "option"); + return TCL_ERROR; + } + resultObjPtr = Tk_GetOptionValue(interp, (char *) dragImage, + optionTable, objv[4], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + /* T dragimage clear */ + case COMMAND_CLEAR: + { + if (dragImage->elem != NULL) + { + DragElem *elem = dragImage->elem; + TreeDragImage_Undisplay(tree->dragImage); +/* if (dragImage->visible) + DragImage_Redraw(dragImage); */ + while (elem != NULL) + elem = DragElem_Free(dragImage, elem); + dragImage->elem = NULL; + } + break; + } + + /* T dragimage configure ?option? ?value? ?option value ...? */ + case COMMAND_CONFIGURE: + { + Tcl_Obj *resultObjPtr; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); + return TCL_ERROR; + } + if (objc <= 4) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) dragImage, + optionTable, + (objc == 3) ? (Tcl_Obj *) NULL : objv[3], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + return DragImage_Config(dragImage, objc - 3, objv + 3); + } + + /* T dragimage offset ?x y? */ + case COMMAND_OFFSET: + { + int x, y; + + if (objc != 3 && objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "?x y?"); + return TCL_ERROR; + } + if (objc == 3) + { + FormatResult(interp, "%d %d", dragImage->x, dragImage->y); + break; + } + if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) + return TCL_ERROR; + TreeDragImage_Undisplay(tree->dragImage); +/* if (dragImage->visible) + DragImage_Redraw(dragImage); */ + dragImage->x = x; + dragImage->y = y; + TreeDragImage_Display(tree->dragImage); + break; + } + + /* T dragimage visible ?boolean? */ + case COMMAND_VISIBLE: + { + int visible; + + if (objc != 3 && objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "?boolean?"); + return TCL_ERROR; + } + if (objc == 4) + { + if (Tcl_GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) + return TCL_ERROR; + if (visible != dragImage->visible) + { + dragImage->visible = visible; + TreeDragImage_Undisplay(tree->dragImage); + TreeDragImage_Display(tree->dragImage); + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(dragImage->visible)); + break; + } + } + + return TCL_OK; +} diff --git a/generic/tkTreeElem.c b/generic/tkTreeElem.c new file mode 100644 index 0000000..2596eb7 --- /dev/null +++ b/generic/tkTreeElem.c @@ -0,0 +1,3436 @@ +#include "tkTreeCtrl.h" +#include "tkTreeElem.h" + +static int ObjectIsEmpty(Tcl_Obj *obj) +{ + int length; + + if (obj == NULL) + return 1; + if (obj->bytes != NULL) + return (obj->length == 0); + Tcl_GetStringFromObj(obj, &length); + return (length == 0); +} + +/* BEGIN custom "boolean" option */ + +/* Just like TK_OPTION_BOOLEAN but supports TK_OPTION_NULL_OK */ +/* Internal value is -1 for no-such-value */ + +static int BooleanSet( + ClientData clientData, + Tcl_Interp *interp, + Tk_Window tkwin, + Tcl_Obj **value, + char *recordPtr, + int internalOffset, + char *saveInternalPtr, + int flags) +{ + int objEmpty; + int new, *internalPtr; + + objEmpty = 0; + + if (internalOffset >= 0) + internalPtr = (int *) (recordPtr + internalOffset); + else + internalPtr = NULL; + + objEmpty = ObjectIsEmpty((*value)); + + if ((flags & TK_OPTION_NULL_OK) && objEmpty) + (*value) = NULL; + else + { + if (Tcl_GetBooleanFromObj(interp, (*value), &new) != TCL_OK) + return TCL_ERROR; + } + if (internalPtr != NULL) + { + if ((*value) == NULL) + new = -1; + *((int *) saveInternalPtr) = *((int *) internalPtr); + *((int *) internalPtr) = new; + } + + return TCL_OK; +} + +static Tcl_Obj *BooleanGet( + ClientData clientData, + Tk_Window tkwin, + char *recordPtr, + int internalOffset) +{ + int value = *(int *) (recordPtr + internalOffset); + if (value == -1) + return NULL; + return Tcl_NewBooleanObj(value); +} + +static void BooleanRestore( + ClientData clientData, + Tk_Window tkwin, + char *internalPtr, + char *saveInternalPtr) +{ + *(int *) internalPtr = *(int *) saveInternalPtr; +} + +static Tk_ObjCustomOption booleanCO = +{ + "boolean", + BooleanSet, + BooleanGet, + BooleanRestore, + NULL, + (ClientData) NULL +}; + +/* END custom "boolean" option */ + +/* BEGIN custom "integer" option */ + +/* Just like TK_OPTION_INT but supports TK_OPTION_NULL_OK and bounds checking */ + +typedef struct IntegerClientData +{ + int min; + int max; + int empty; /* internal form if empty */ + int flags; /* 0x01 - use min, 0x02 - use max */ +} IntegerClientData; + +static int IntegerSet( + ClientData clientData, + Tcl_Interp *interp, + Tk_Window tkwin, + Tcl_Obj **value, + char *recordPtr, + int internalOffset, + char *saveInternalPtr, + int flags) +{ + IntegerClientData *info = (IntegerClientData *) clientData; + int objEmpty; + int new, *internalPtr; + + objEmpty = 0; + + if (internalOffset >= 0) + internalPtr = (int *) (recordPtr + internalOffset); + else + internalPtr = NULL; + + objEmpty = ObjectIsEmpty((*value)); + + if ((flags & TK_OPTION_NULL_OK) && objEmpty) + (*value) = NULL; + else + { + if (Tcl_GetIntFromObj(interp, (*value), &new) != TCL_OK) + return TCL_ERROR; + if ((info->flags & 0x01) && (new < info->min)) + { + FormatResult(interp, + "bad integer value \"%d\": must be >= %d", + new, info->min); + return TCL_ERROR; + } + if ((info->flags & 0x02) && (new > info->max)) + { + FormatResult(interp, + "bad integer value \"%d\": must be <= %d", + new, info->max); + return TCL_ERROR; + } + } + if (internalPtr != NULL) + { + if ((*value) == NULL) + new = info->empty; + *((int *) saveInternalPtr) = *((int *) internalPtr); + *((int *) internalPtr) = new; + } + + return TCL_OK; +} + +static Tcl_Obj *IntegerGet( + ClientData clientData, + Tk_Window tkwin, + char *recordPtr, + int internalOffset) +{ + IntegerClientData *info = (IntegerClientData *) clientData; + int value = *(int *) (recordPtr + internalOffset); + if (value == info->empty) + return NULL; + return Tcl_NewIntObj(value); +} + +static void IntegerRestore( + ClientData clientData, + Tk_Window tkwin, + char *internalPtr, + char *saveInternalPtr) +{ + *(int *) internalPtr = *(int *) saveInternalPtr; +} + +/* END custom "integer" option */ + +/*****/ + +/* BEGIN custom "stringtable" option */ + +/* Just like TK_OPTION_STRING_TABLE but supports TK_OPTION_NULL_OK */ +/* The integer rep is -1 if empty string specified */ + +typedef struct StringTableClientData +{ + CONST char **tablePtr; /* NULL-termintated list of strings */ + CONST char *msg; /* Tcl_GetIndexFromObj() message */ +} StringTableClientData; + +static int StringTableSet( + ClientData clientData, + Tcl_Interp *interp, + Tk_Window tkwin, + Tcl_Obj **value, + char *recordPtr, + int internalOffset, + char *saveInternalPtr, + int flags) +{ + StringTableClientData *info = (StringTableClientData *) clientData; + int objEmpty; + int new, *internalPtr; + + objEmpty = 0; + + if (internalOffset >= 0) + internalPtr = (int *) (recordPtr + internalOffset); + else + internalPtr = NULL; + + objEmpty = ObjectIsEmpty((*value)); + + if ((flags & TK_OPTION_NULL_OK) && objEmpty) + (*value) = NULL; + else + { + if (Tcl_GetIndexFromObj(interp, (*value), info->tablePtr, + info->msg, 0, &new) != TCL_OK) + return TCL_ERROR; + } + if (internalPtr != NULL) + { + if ((*value) == NULL) + new = -1; + *((int *) saveInternalPtr) = *((int *) internalPtr); + *((int *) internalPtr) = new; + } + + return TCL_OK; +} + +static Tcl_Obj *StringTableGet( + ClientData clientData, + Tk_Window tkwin, + char *recordPtr, + int internalOffset) +{ + StringTableClientData *info = (StringTableClientData *) clientData; + int index = *(int *) (recordPtr + internalOffset); + + if (index == -1) + return NULL; + return Tcl_NewStringObj(info->tablePtr[index], -1); +} + +static void StringTableRestore( + ClientData clientData, + Tk_Window tkwin, + char *internalPtr, + char *saveInternalPtr) +{ + *(int *) internalPtr = *(int *) saveInternalPtr; +} + +/* END custom "stringtable" option */ + +/*****/ + +typedef struct PerStateData PerStateData; +typedef struct PerStateInfo PerStateInfo; +typedef struct PerStateType PerStateType; + +/* There is one of these for each XColor, Tk_Font, Tk_Image etc */ +struct PerStateData +{ + int stateOff; + int stateOn; + /* Type-specific fields go here */ +}; + +#define DEBUG_PSI + +struct PerStateInfo +{ +#ifdef DEBUG_PSI + PerStateType *type; +#endif + Tcl_Obj *obj; + int count; + PerStateData *data; +}; + +typedef int (*PerStateType_FromObjProc)(TreeCtrl *, Tcl_Obj *, PerStateData *); +typedef void (*PerStateType_FreeProc)(TreeCtrl *, PerStateData *); + +struct PerStateType +{ + int size; + PerStateType_FromObjProc fromObjProc; + PerStateType_FreeProc freeProc; +}; + +#define MATCH_NONE 0 +#define MATCH_ANY 1 +#define MATCH_PARTIAL 2 +#define MATCH_EXACT 3 + +static int StateFromObj(TreeCtrl *tree, Tcl_Obj *obj, int *stateOff, int *stateOn) +{ + Tcl_Interp *interp = tree->interp; + int i, negate = 0, length, state = 0; + char ch0, *string; + + string = Tcl_GetStringFromObj(obj, &length); + if (length == 0) + goto unknown; + ch0 = string[0]; + if (ch0 == '!') + { + negate = 1; + ++string; + ch0 = string[0]; + } + for (i = 0; i < 32; i++) + { + if (tree->stateNames[i] == NULL) + continue; + if ((ch0 == tree->stateNames[i][0]) && + (strcmp(string, tree->stateNames[i]) == 0)) + { + state = 1L << i; + break; + } + } + if (state == 0) + goto unknown; + + if (negate) + (*stateOff) |= state; + else + (*stateOn) |= state; + return TCL_OK; + +unknown: + FormatResult(interp, "unknown state \"%s\"", string); + return TCL_ERROR; +} + +static void PerStateInfo_Free( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo) +{ + PerStateData *pData = pInfo->data; + int i; + + if (pInfo->data == NULL) + return; +#ifdef DEBUG_PSI + if (pInfo->type != typePtr) panic("PerStateInfo_Free type mismatch"); +#endif + for (i = 0; i < pInfo->count; i++) + { + (*typePtr->freeProc)(tree, pData); + pData = (PerStateData *) (((char *) pData) + typePtr->size); + } + wipefree((char *) pInfo->data, typePtr->size * pInfo->count); + pInfo->data = NULL; + pInfo->count = 0; +} + +static int PerStateInfo_FromObj( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo) +{ + int i, j; + int objc, objc2; + Tcl_Obj **objv, **objv2; + PerStateData *pData; + +#ifdef DEBUG_PSI + pInfo->type = typePtr; +#endif + + PerStateInfo_Free(tree, typePtr, pInfo); + + if (pInfo->obj == NULL) + return TCL_OK; + + if (Tcl_ListObjGetElements(tree->interp, pInfo->obj, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + if (objc == 0) + return TCL_OK; + + if (objc == 1) + { + pData = (PerStateData *) ckalloc(typePtr->size); + pData->stateOff = pData->stateOn = 0; /* all states */ + if ((*typePtr->fromObjProc)(tree, objv[0], pData) != TCL_OK) + { + wipefree((char *) pData, typePtr->size); + return TCL_ERROR; + } + pInfo->data = pData; + pInfo->count = 1; + return TCL_OK; + } + + if ((objc & 1)) + { + FormatResult(tree->interp, "list must have event number of elements"); + return TCL_ERROR; + } + + pData = (PerStateData *) ckalloc(typePtr->size * (objc / 2)); + pInfo->data = pData; + for (i = 0; i < objc; i += 2) + { + if ((*typePtr->fromObjProc)(tree, objv[i], pData) != TCL_OK) + { + PerStateInfo_Free(tree, typePtr, pInfo); + return TCL_ERROR; + } + pInfo->count++; + if (Tcl_ListObjGetElements(tree->interp, objv[i + 1], &objc2, &objv2) != TCL_OK) + { + PerStateInfo_Free(tree, typePtr, pInfo); + return TCL_ERROR; + } + pData->stateOff = pData->stateOn = 0; /* all states */ + for (j = 0; j < objc2; j++) + { + if (StateFromObj(tree, objv2[j], &pData->stateOff, &pData->stateOn) != TCL_OK) + { + PerStateInfo_Free(tree, typePtr, pInfo); + return TCL_ERROR; + } + } + pData = (PerStateData *) (((char *) pData) + typePtr->size); + } + return TCL_OK; +} + +static PerStateData *PerStateInfo_ForState( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateData *pData = pInfo->data; + int stateOff = ~state, stateOn = state; + int i; + +#ifdef DEBUG_PSI + if ((pInfo->data != NULL) && (pInfo->type != typePtr)) panic("PerStateInfo_ForState type mismatch"); +#endif + + for (i = 0; i < pInfo->count; i++) + { + /* Any state */ + if ((pData->stateOff == 0) && + (pData->stateOn == 0)) + { + (*match) = MATCH_ANY; + return pData; + } + + /* Exact match */ + if ((pData->stateOff == stateOff) && + (pData->stateOn == stateOn)) + { + (*match) = MATCH_EXACT; + return pData; + } + + /* Partial match */ + if (((pData->stateOff & stateOff) == pData->stateOff) && + ((pData->stateOn & stateOn) == pData->stateOn)) + { + (*match) = MATCH_PARTIAL; + return pData; + } + + pData = (PerStateData *) (((char *) pData) + typePtr->size); + } + + (*match) = MATCH_NONE; + return NULL; +} + +static Tcl_Obj *PerStateInfo_ObjForState( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateData *pData; + Tcl_Obj *obj; + int i; + +#ifdef DEBUG_PSI + if ((pInfo->data != NULL) && (pInfo->type != typePtr)) panic("PerStateInfo_ForStateObj type mismatch"); +#endif + + pData = PerStateInfo_ForState(tree, typePtr, pInfo, state, match); + if (pData != NULL) + { + i = ((char *) pData - (char *) pInfo->data) / typePtr->size; + Tcl_ListObjIndex(tree->interp, pInfo->obj, i * 2, &obj); + return obj; + } + + return NULL; +} + +static void PerStateInfo_Undefine( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo, + int state) +{ + PerStateData *pData = pInfo->data; + int i, j, numStates, stateOff, stateOn; + Tcl_Obj *configObj = pInfo->obj, *listObj, *stateObj; + +#ifdef DEBUG_PSI + if ((pInfo->data != NULL) && (pInfo->type != typePtr)) panic("PerStateInfo_ForState type mismatch"); +#endif + + for (i = 0; i < pInfo->count; i++) + { + if ((pData->stateOff | pData->stateOn) & state) + { + pData->stateOff &= ~state; + pData->stateOn &= ~state; + if (Tcl_IsShared(configObj)) + { + configObj = Tcl_DuplicateObj(configObj); + Tcl_DecrRefCount(pInfo->obj); + Tcl_IncrRefCount(configObj); + pInfo->obj = configObj; + } + Tcl_ListObjIndex(tree->interp, configObj, i * 2 + 1, &listObj); + if (Tcl_IsShared(listObj)) + { + listObj = Tcl_DuplicateObj(listObj); + Tcl_ListObjReplace(tree->interp, configObj, i * 2 + 1, 1, 1, &listObj); + } + Tcl_ListObjLength(tree->interp, listObj, &numStates); + for (j = 0; j < numStates; ) + { + Tcl_ListObjIndex(tree->interp, listObj, j, &stateObj); + stateOff = stateOn = 0; + StateFromObj(tree, stateObj, &stateOff, &stateOn); + if ((stateOff | stateOn) & state) + { + Tcl_ListObjReplace(tree->interp, listObj, j, 1, 0, NULL); + numStates--; + } + else + j++; + } + /* Given {bitmap {state1 state2 state3}}, we just invalidated + * the string rep of the sublist {state1 ...}, but not + * the parent list */ + Tcl_InvalidateStringRep(configObj); + } + pData = (PerStateData *) (((char *) pData) + typePtr->size); + } +} + +/*****/ + +struct PerStateGC +{ + unsigned long mask; + XGCValues gcValues; + GC gc; + struct PerStateGC *next; +}; + +void PerStateGC_Free(TreeCtrl *tree, struct PerStateGC **pGCPtr) +{ + struct PerStateGC *pGC = (*pGCPtr), *next; + + while (pGC != NULL) + { + next = pGC->next; + Tk_FreeGC(tree->display, pGC->gc); + WFREE(pGC, struct PerStateGC); + pGC = next; + } + (*pGCPtr) = NULL; +} + +GC PerStateGC_Get(TreeCtrl *tree, struct PerStateGC **pGCPtr, unsigned long mask, XGCValues *gcValues) +{ + struct PerStateGC *pGC; + + if ((mask | (GCFont | GCForeground | GCBackground | GCGraphicsExposures)) != + (GCFont | GCForeground | GCBackground | GCGraphicsExposures)) + panic("PerStateGC_Get: unsupported mask"); + + for (pGC = (*pGCPtr); pGC != NULL; pGC = pGC->next) + { + if (mask != pGC->mask) + continue; + if ((mask & GCFont) && + (pGC->gcValues.font != gcValues->font)) + continue; + if ((mask & GCForeground) && + (pGC->gcValues.foreground != gcValues->foreground)) + continue; + if ((mask & GCBackground) && + (pGC->gcValues.background != gcValues->background)) + continue; + if ((mask & GCGraphicsExposures) && + (pGC->gcValues.graphics_exposures != gcValues->graphics_exposures)) + continue; + return pGC->gc; + } + + pGC = (struct PerStateGC *) ckalloc(sizeof(*pGC)); + pGC->gcValues = (*gcValues); + pGC->mask = mask; + pGC->gc = Tk_GetGC(tree->tkwin, mask, gcValues); + pGC->next = (*pGCPtr); + (*pGCPtr) = pGC; + + return pGC->gc; +} + +/*****/ + +typedef struct PerStateDataBitmap PerStateDataBitmap; +struct PerStateDataBitmap +{ + PerStateData header; + Pixmap bitmap; +}; + +static int BitmapFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataBitmap *pBitmap) +{ + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pBitmap->bitmap = None; + } + else + { + pBitmap->bitmap = Tk_AllocBitmapFromObj(tree->interp, tree->tkwin, obj); + if (pBitmap->bitmap == None) + return TCL_ERROR; + } + return TCL_OK; +} + +static void BitmapFree(TreeCtrl *tree, PerStateDataBitmap *pBitmap) +{ + if (pBitmap->bitmap != None) + Tk_FreeBitmap(tree->display, pBitmap->bitmap); +} + +PerStateType pstBitmap = +{ + sizeof(PerStateDataBitmap), + (PerStateType_FromObjProc) BitmapFromObj, + (PerStateType_FreeProc) BitmapFree +}; + +static Pixmap PerStateBitmap_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataBitmap *pData; + + pData = (PerStateDataBitmap *) PerStateInfo_ForState(tree, &pstBitmap, pInfo, state, match); + if (pData != NULL) + return pData->bitmap; + return None; +} + +/*****/ + +typedef struct PerStateDataBorder PerStateDataBorder; +struct PerStateDataBorder +{ + PerStateData header; + Tk_3DBorder border; +}; + +static int BorderFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataBorder *pBorder) +{ + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pBorder->border = NULL; + } + else + { + pBorder->border = Tk_Alloc3DBorderFromObj(tree->interp, tree->tkwin, obj); + if (pBorder->border == NULL) + return TCL_ERROR; + } + return TCL_OK; +} + +static void BorderFree(TreeCtrl *tree, PerStateDataBorder *pBorder) +{ + if (pBorder->border != NULL) + Tk_Free3DBorder(pBorder->border); +} + +PerStateType pstBorder = +{ + sizeof(PerStateDataBorder), + (PerStateType_FromObjProc) BorderFromObj, + (PerStateType_FreeProc) BorderFree +}; + +static Tk_3DBorder PerStateBorder_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataBorder *pData; + + pData = (PerStateDataBorder *) PerStateInfo_ForState(tree, &pstBorder, pInfo, state, match); + if (pData != NULL) + return pData->border; + return NULL; +} + +/*****/ + +typedef struct PerStateDataColor PerStateDataColor; +struct PerStateDataColor +{ + PerStateData header; + XColor *color; +}; + +static int ColorFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataColor *pColor) +{ + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pColor->color = NULL; + } + else + { + pColor->color = Tk_AllocColorFromObj(tree->interp, tree->tkwin, obj); + if (pColor->color == NULL) + return TCL_ERROR; + } + return TCL_OK; +} + +static void ColorFree(TreeCtrl *tree, PerStateDataColor *pColor) +{ + if (pColor->color != NULL) + Tk_FreeColor(pColor->color); +} + +PerStateType pstColor = +{ + sizeof(PerStateDataColor), + (PerStateType_FromObjProc) ColorFromObj, + (PerStateType_FreeProc) ColorFree +}; + +static XColor *PerStateColor_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataColor *pData; + + pData = (PerStateDataColor *) PerStateInfo_ForState(tree, &pstColor, pInfo, state, match); + if (pData != NULL) + return pData->color; + return NULL; +} + +/*****/ + +typedef struct PerStateDataFont PerStateDataFont; +struct PerStateDataFont +{ + PerStateData header; + Tk_Font tkfont; +}; + +static int FontFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataFont *pFont) +{ + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pFont->tkfont = NULL; + } + else + { + pFont->tkfont = Tk_AllocFontFromObj(tree->interp, tree->tkwin, obj); + if (pFont->tkfont == NULL) + return TCL_ERROR; + } + return TCL_OK; +} + +static void FontFree(TreeCtrl *tree, PerStateDataFont *pFont) +{ + if (pFont->tkfont != NULL) + Tk_FreeFont(pFont->tkfont); +} + +PerStateType pstFont = +{ + sizeof(PerStateDataFont), + (PerStateType_FromObjProc) FontFromObj, + (PerStateType_FreeProc) FontFree +}; + +static Tk_Font PerStateFont_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataFont *pData; + + pData = (PerStateDataFont *) PerStateInfo_ForState(tree, &pstFont, pInfo, state, match); + if (pData != NULL) + return pData->tkfont; + return NULL; +} + +/*****/ + +typedef struct PerStateDataImage PerStateDataImage; +struct PerStateDataImage +{ + PerStateData header; + Tk_Image image; + char *string; +}; + +static int ImageFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataImage *pImage) +{ + int length; + char *string; + + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pImage->image = NULL; + pImage->string = NULL; + } + else + { + string = Tcl_GetStringFromObj(obj, &length); + pImage->image = Tree_GetImage(tree, string); + if (pImage->image == NULL) + return TCL_ERROR; + pImage->string = ckalloc(length + 1); + strcpy(pImage->string, string); + } + return TCL_OK; +} + +static void ImageFree(TreeCtrl *tree, PerStateDataImage *pImage) +{ + if (pImage->string != NULL) + ckfree(pImage->string); + /* don't free image */ +} + +PerStateType pstImage = +{ + sizeof(PerStateDataImage), + (PerStateType_FromObjProc) ImageFromObj, + (PerStateType_FreeProc) ImageFree +}; + +static Tk_Image PerStateImage_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataImage *pData; + + pData = (PerStateDataImage *) PerStateInfo_ForState(tree, &pstImage, pInfo, state, match); + if (pData != NULL) + return pData->image; + return NULL; +} + +/*****/ + +typedef struct PerStateDataRelief PerStateDataRelief; +struct PerStateDataRelief +{ + PerStateData header; + int relief; +}; + +static int ReliefFromObj(TreeCtrl *tree, Tcl_Obj *obj, PerStateDataRelief *pRelief) +{ + if (ObjectIsEmpty(obj)) + { + /* Specify empty string to override masterX */ + pRelief->relief = TK_RELIEF_NULL; + } + else + { + if (Tk_GetReliefFromObj(tree->interp, obj, &pRelief->relief) != TCL_OK) + return TCL_ERROR; + } + return TCL_OK; +} + +static void ReliefFree(TreeCtrl *tree, PerStateDataRelief *pRelief) +{ +} + +PerStateType pstRelief = +{ + sizeof(PerStateDataRelief), + (PerStateType_FromObjProc) ReliefFromObj, + (PerStateType_FreeProc) ReliefFree +}; + +static int PerStateRelief_ForState( + TreeCtrl *tree, + PerStateInfo *pInfo, + int state, + int *match) +{ + PerStateDataRelief *pData; + + pData = (PerStateDataRelief *) PerStateInfo_ForState(tree, &pstRelief, pInfo, state, match); + if (pData != NULL) + return pData->relief; + return TK_RELIEF_NULL; +} + +/*****/ + +void PSTSave( + PerStateInfo *pInfo, + PerStateInfo *pSave) +{ +#ifdef DEBUG_PSI + pSave->type = pInfo->type; /* could be NULL */ +#endif + pSave->data = pInfo->data; + pSave->count = pInfo->count; + pInfo->data = NULL; + pInfo->count = 0; +} + +void PSTRestore( + TreeCtrl *tree, + PerStateType *typePtr, + PerStateInfo *pInfo, + PerStateInfo *pSave) +{ + PerStateInfo_Free(tree, typePtr, pInfo); + pInfo->data = pSave->data; + pInfo->count = pSave->count; +} + +/*****/ + +typedef struct ElementBitmap ElementBitmap; + +struct ElementBitmap +{ + Element header; + PerStateInfo bitmap; + PerStateInfo fg; + PerStateInfo bg; +}; + +#define BITMAP_CONF_BITMAP 0x0001 +#define BITMAP_CONF_FG 0x0002 +#define BITMAP_CONF_BG 0x0004 + +static Tk_OptionSpec bitmapOptionSpecs[] = { + {TK_OPTION_STRING, "-background", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBitmap, bg.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_BG}, + {TK_OPTION_STRING, "-bitmap", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBitmap, bitmap.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_BITMAP}, + {TK_OPTION_STRING, "-foreground", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBitmap, fg.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, BITMAP_CONF_FG}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} +}; + +static void DeleteBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBitmap *elemX = (ElementBitmap *) elem; + + PerStateInfo_Free(tree, &pstBitmap, &elemX->bitmap); + PerStateInfo_Free(tree, &pstColor, &elemX->fg); + PerStateInfo_Free(tree, &pstColor, &elemX->bg); +} + +static int WorldChangedBitmap(ElementArgs *args) +{ + int flagM = args->change.flagMaster; + int flagS = args->change.flagSelf; + int mask = 0; + + if ((flagS | flagM) & BITMAP_CONF_BITMAP) + mask |= CS_DISPLAY | CS_LAYOUT; + + if ((flagS | flagM) & (BITMAP_CONF_FG | BITMAP_CONF_BG)) + mask |= CS_DISPLAY; + + return mask; +} + +static int ConfigBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBitmap *elemX = (ElementBitmap *) elem; + ElementBitmap savedX; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) elemX, + elem->typePtr->optionTable, + args->config.objc, args->config.objv, tree->tkwin, + &savedOptions, &args->config.flagSelf) != TCL_OK) + { + args->config.flagSelf = 0; + continue; + } + + if (args->config.flagSelf & BITMAP_CONF_BITMAP) + { + PSTSave(&elemX->bitmap, &savedX.bitmap); + if (PerStateInfo_FromObj(tree, &pstBitmap, &elemX->bitmap) != TCL_OK) + continue; + } + + if (args->config.flagSelf & BITMAP_CONF_FG) + { + PSTSave(&elemX->fg, &savedX.fg); + if (PerStateInfo_FromObj(tree, &pstColor, &elemX->fg) != TCL_OK) + continue; + } + + if (args->config.flagSelf & BITMAP_CONF_BG) + { + PSTSave(&elemX->bg, &savedX.bg); + if (PerStateInfo_FromObj(tree, &pstColor, &elemX->bg) != TCL_OK) + continue; + } + + if (args->config.flagSelf & BITMAP_CONF_BITMAP) + PerStateInfo_Free(tree, &pstBitmap, &savedX.bitmap); + if (args->config.flagSelf & BITMAP_CONF_FG) + PerStateInfo_Free(tree, &pstColor, &savedX.fg); + if (args->config.flagSelf & BITMAP_CONF_BG) + PerStateInfo_Free(tree, &pstColor, &savedX.bg); + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (args->config.flagSelf & BITMAP_CONF_BITMAP) + PSTRestore(tree, &pstBitmap, &elemX->bitmap, &savedX.bitmap); + + if (args->config.flagSelf & BITMAP_CONF_FG) + PSTRestore(tree, &pstColor, &elemX->fg, &savedX.fg); + + if (args->config.flagSelf & BITMAP_CONF_BG) + PSTRestore(tree, &pstColor, &elemX->bg, &savedX.bg); + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + return TCL_OK; +} + +static int CreateBitmap(ElementArgs *args) +{ + return TCL_OK; +} + +static void DisplayBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBitmap *elemX = (ElementBitmap *) elem; + ElementBitmap *masterX = (ElementBitmap *) elem->master; + int state = args->state; + int match, match2; + Pixmap bitmap; + XColor *fg, *bg; + int imgW, imgH; + + bitmap = PerStateBitmap_ForState(tree, &elemX->bitmap, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Pixmap bitmap2 = PerStateBitmap_ForState(tree, &masterX->bitmap, + state, &match2); + if (match2 > match) + bitmap = bitmap2; + } + + fg = PerStateColor_ForState(tree, &elemX->fg, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *fg2 = PerStateColor_ForState(tree, &masterX->fg, + state, &match2); + if (match2 > match) + fg = fg2; + } + + bg = PerStateColor_ForState(tree, &elemX->bg, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *bg2 = PerStateColor_ForState(tree, &masterX->bg, + state, &match2); + if (match2 > match) + bg = bg2; + } + + if (bitmap != None) + { + int bx = args->display.x /* + args->display.pad[LEFT] */; + int by = args->display.y /* + args->display.pad[TOP] */; + int dx = 0, dy = 0; + XGCValues gcValues; + GC gc; + unsigned long mask = 0; + + Tk_SizeOfBitmap(tree->display, bitmap, &imgW, &imgH); + if (imgW < args->display.width) + dx = (args->display.width - imgW) / 2; + else if (imgW > args->display.width) + imgW = args->display.width; + if (imgH < args->display.height) + dy = (args->display.height - imgH) / 2; + else if (imgH > args->display.height) + imgH = args->display.height; + + bx += dx; + by += dy; + + if (fg != NULL) + { + gcValues.foreground = fg->pixel; + mask |= GCForeground; + } + if (bg != NULL) + { + gcValues.background = bg->pixel; + mask |= GCBackground; + } + else + { + gcValues.clip_mask = bitmap; + mask |= GCClipMask; + } + gcValues.graphics_exposures = False; + mask |= GCGraphicsExposures; + gc = Tk_GetGC(tree->tkwin, mask, &gcValues); + XSetClipOrigin(tree->display, gc, bx, by); + XCopyPlane(tree->display, bitmap, args->display.drawable, gc, + 0, 0, (unsigned int) imgW, (unsigned int) imgH, + bx, by, 1); + Tk_FreeGC(tree->display, gc); + } +} + +static void LayoutBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBitmap *elemX = (ElementBitmap *) elem; + ElementBitmap *masterX = (ElementBitmap *) elem->master; + int state = args->state; + int match, match2; + Pixmap bitmap; + + bitmap = PerStateBitmap_ForState(tree, &elemX->bitmap, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Pixmap bitmap2 = PerStateBitmap_ForState(tree, &masterX->bitmap, + state, &match2); + if (match2 > match) + bitmap = bitmap2; + } + + if (bitmap != None) + Tk_SizeOfBitmap(tree->display, bitmap, + &args->layout.width, &args->layout.height); + else + args->layout.width = args->layout.height = 0; +} + +static int StateProcBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBitmap *elemX = (ElementBitmap *) elem; + ElementBitmap *masterX = (ElementBitmap *) elem->master; + int match, match2; + Pixmap bitmap1, bitmap2; + XColor *fg1, *fg2; + XColor *bg1, *bg2; + int mask = 0; + + bitmap1 = PerStateBitmap_ForState(tree, &elemX->bitmap, + args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Pixmap bitmap = PerStateBitmap_ForState(tree, &masterX->bitmap, + args->states.state1, &match2); + if (match2 > match) + bitmap1 = bitmap; + } + + fg1 = PerStateColor_ForState(tree, &elemX->fg, + args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *fg = PerStateColor_ForState(tree, &masterX->fg, + args->states.state1, &match2); + if (match2 > match) + fg1 = fg; + } + + bg1 = PerStateColor_ForState(tree, &elemX->bg, + args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *bg = PerStateColor_ForState(tree, &masterX->bg, + args->states.state1, &match2); + if (match2 > match) + bg1 = bg; + } + + bitmap2 = PerStateBitmap_ForState(tree, &elemX->bitmap, + args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Pixmap bitmap = PerStateBitmap_ForState(tree, &masterX->bitmap, + args->states.state2, &match2); + if (match2 > match) + bitmap2 = bitmap; + } + + fg2 = PerStateColor_ForState(tree, &elemX->fg, + args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *fg = PerStateColor_ForState(tree, &masterX->fg, + args->states.state2, &match2); + if (match2 > match) + fg2 = fg; + } + + bg2 = PerStateColor_ForState(tree, &elemX->bg, + args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *bg = PerStateColor_ForState(tree, &masterX->bg, + args->states.state2, &match2); + if (match2 > match) + bg2 = bg; + } + + if ((fg1 != fg2) || (bg1 != bg2) || (bitmap1 != bitmap2)) + mask |= CS_DISPLAY; + + if (bitmap1 != bitmap2) + { + if ((bitmap1 != None) && (bitmap2 != None)) + { + int w1, h1, w2, h2; + Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1); + Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2); + if ((w1 != w2) || (h1 != h2)) + mask |= CS_LAYOUT; + } + else + mask |= CS_LAYOUT; + } + + return mask; +} + +static void UndefProcBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementBitmap *elemX = (ElementBitmap *) args->elem; + + PerStateInfo_Undefine(tree, &pstBitmap, &elemX->fg, args->state); + PerStateInfo_Undefine(tree, &pstBitmap, &elemX->bg, args->state); + PerStateInfo_Undefine(tree, &pstBitmap, &elemX->bitmap, args->state); +} + +static int ActualProcBitmap(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementBitmap *elemX = (ElementBitmap *) args->elem; + ElementBitmap *masterX = (ElementBitmap *) args->elem->master; + static CONST char *optionName[] = { + "-background", "-bitmap", "-foreground", + (char *) NULL }; + int index, match, matchM; + Tcl_Obj *obj = NULL, *objM; + + if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, + "option", 0, &index) != TCL_OK) + return TCL_ERROR; + + switch (index) + { + case 0: + { + obj = PerStateInfo_ObjForState(tree, &pstColor, &elemX->bg, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstColor, &masterX->bg, args->state, &matchM); + if (matchM > match) + obj = objM; + } + /* When -background isn't specified, GC default (white) is used */ + if (ObjectIsEmpty(obj)) + obj = Tcl_NewStringObj("white", -1); + break; + } + case 1: + { + obj = PerStateInfo_ObjForState(tree, &pstBitmap, &elemX->bitmap, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstBitmap, &masterX->bitmap, args->state, &matchM); + if (matchM > match) + obj = objM; + } + break; + } + case 2: + { + obj = PerStateInfo_ObjForState(tree, &pstColor, &elemX->fg, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstColor, &masterX->fg, args->state, &matchM); + if (matchM > match) + obj = objM; + } + /* When -foreground isn't specified, GC default (black) is used */ + if (ObjectIsEmpty(obj)) + obj = Tcl_NewStringObj("black", -1); + break; + } + } + if (obj != NULL) + Tcl_SetObjResult(tree->interp, obj); + return TCL_OK; +} + +ElementType elemTypeBitmap = { + "bitmap", + sizeof(ElementBitmap), + bitmapOptionSpecs, + NULL, + CreateBitmap, + DeleteBitmap, + ConfigBitmap, + DisplayBitmap, + LayoutBitmap, + WorldChangedBitmap, + StateProcBitmap, + UndefProcBitmap, + ActualProcBitmap +}; + +/*****/ + +typedef struct ElementBorder ElementBorder; + +struct ElementBorder +{ + Element header; /* Must be first */ + PerStateInfo border; + PerStateInfo relief; + int thickness; + Tcl_Obj *thicknessObj; + int width; + Tcl_Obj *widthObj; + int height; + Tcl_Obj *heightObj; + int filled; +}; + +#define BORDER_CONF_BG 0x0001 +#define BORDER_CONF_RELIEF 0x0002 +#define BORDER_CONF_SIZE 0x0004 +#define BORDER_CONF_THICKNESS 0x0008 +#define BORDER_CONF_FILLED 0x0010 + +static Tk_OptionSpec borderOptionSpecs[] = { + {TK_OPTION_STRING, "-background", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBorder, border.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_BG}, + {TK_OPTION_CUSTOM, "-filled", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementBorder, filled), + TK_OPTION_NULL_OK, (ClientData) &booleanCO, BORDER_CONF_FILLED}, + {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBorder, heightObj), + Tk_Offset(ElementBorder, height), + TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_SIZE}, + {TK_OPTION_STRING, "-relief", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBorder, relief.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_RELIEF}, + {TK_OPTION_PIXELS, "-thickness", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBorder, thicknessObj), + Tk_Offset(ElementBorder, thickness), + TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_THICKNESS}, + {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementBorder, widthObj), + Tk_Offset(ElementBorder, width), + TK_OPTION_NULL_OK, (ClientData) NULL, BORDER_CONF_SIZE}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} +}; + +static void DeleteBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + + PerStateInfo_Free(tree, &pstBorder, &elemX->border); + PerStateInfo_Free(tree, &pstRelief, &elemX->relief); +} + +static int WorldChangedBorder(ElementArgs *args) +{ + int flagM = args->change.flagMaster; + int flagS = args->change.flagSelf; + int mask = 0; + + if ((flagS | flagM) & BORDER_CONF_SIZE) + mask |= CS_DISPLAY | CS_LAYOUT; + + if ((flagS | flagM) & (BORDER_CONF_BG | BORDER_CONF_RELIEF | + BORDER_CONF_THICKNESS | BORDER_CONF_FILLED)) + mask |= CS_DISPLAY; + + return mask; +} + +static int ConfigBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + ElementBorder savedX; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) elemX, + elem->typePtr->optionTable, + args->config.objc, args->config.objv, tree->tkwin, + &savedOptions, &args->config.flagSelf) != TCL_OK) + { + args->config.flagSelf = 0; + continue; + } + + if (args->config.flagSelf & BORDER_CONF_BG) + { + PSTSave(&elemX->border, &savedX.border); + if (PerStateInfo_FromObj(tree, &pstBorder, &elemX->border) != TCL_OK) + continue; + } + + if (args->config.flagSelf & BORDER_CONF_RELIEF) + { + PSTSave(&elemX->relief, &savedX.relief); + if (PerStateInfo_FromObj(tree, &pstRelief, &elemX->relief) != TCL_OK) + continue; + } + + if (args->config.flagSelf & BORDER_CONF_BG) + PerStateInfo_Free(tree, &pstBorder, &savedX.border); + if (args->config.flagSelf & BORDER_CONF_RELIEF) + PerStateInfo_Free(tree, &pstRelief, &savedX.relief); + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (args->config.flagSelf & BORDER_CONF_BG) + PSTRestore(tree, &pstBorder, &elemX->border, &savedX.border); + + if (args->config.flagSelf & BORDER_CONF_RELIEF) + PSTRestore(tree, &pstRelief, &elemX->relief, &savedX.relief); + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + return TCL_OK; +} + +static int CreateBorder(ElementArgs *args) +{ + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + + elemX->filled = -1; + return TCL_OK; +} + +static void DisplayBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + ElementBorder *masterX = (ElementBorder *) elem->master; + int state = args->state; + int match, match2; + Tk_3DBorder border; + int relief, filled = FALSE; + int thickness = 0; + + border = PerStateBorder_ForState(tree, &elemX->border, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_3DBorder border2 = PerStateBorder_ForState(tree, &masterX->border, state, &match2); + if (match2 > match) + border = border2; + } + + relief = PerStateRelief_ForState(tree, &elemX->relief, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + int relief2 = PerStateRelief_ForState(tree, &masterX->relief, state, &match2); + if (match2 > match) + relief = relief2; + } + + if (elemX->thicknessObj) + thickness = elemX->thickness; + else if ((masterX != NULL) && (masterX->thicknessObj != NULL)) + thickness = masterX->thickness; + + if (elemX->filled != -1) + filled = elemX->filled; + else if ((masterX != NULL) && (masterX->filled != -1)) + filled = masterX->filled; + + if (border != NULL) + { + if (relief == TK_RELIEF_NULL) + relief = TK_RELIEF_FLAT; + if (filled) + { + Tk_Fill3DRectangle(tree->tkwin, args->display.drawable, border, + args->display.x, + args->display.y, + args->display.width, args->display.height, + thickness, relief); + } + else if (thickness > 0) + { + Tk_Draw3DRectangle(tree->tkwin, args->display.drawable, border, + args->display.x, + args->display.y, + args->display.width, args->display.height, + thickness, relief); + } + } +} + +static void LayoutBorder(ElementArgs *args) +{ + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + ElementBorder *masterX = (ElementBorder *) elem->master; + int width, height; + + if (elemX->widthObj != NULL) + width = elemX->width; + else if ((masterX != NULL) && (masterX->widthObj != NULL)) + width = masterX->width; + else + width = 0; + args->layout.width = width; + + if (elemX->heightObj != NULL) + height = elemX->height; + else if ((masterX != NULL) && (masterX->heightObj != NULL)) + height = masterX->height; + else + height = 0; + args->layout.height = height; +} + +static int StateProcBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementBorder *elemX = (ElementBorder *) elem; + ElementBorder *masterX = (ElementBorder *) elem->master; + int match, match2; + Tk_3DBorder border1, border2; + int relief1, relief2; + int mask = 0; + + border1 = PerStateBorder_ForState(tree, &elemX->border, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_3DBorder border = PerStateBorder_ForState(tree, &masterX->border, args->states.state1, &match2); + if (match2 > match) + border1 = border; + } + + relief1 = PerStateRelief_ForState(tree, &elemX->relief, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + int relief = PerStateRelief_ForState(tree, &masterX->relief, args->states.state1, &match2); + if (match2 > match) + relief1 = relief; + } + + border2 = PerStateBorder_ForState(tree, &elemX->border, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_3DBorder border = PerStateBorder_ForState(tree, &masterX->border, args->states.state2, &match2); + if (match2 > match) + border2 = border; + } + + relief2 = PerStateRelief_ForState(tree, &elemX->relief, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + int relief = PerStateRelief_ForState(tree, &masterX->relief, args->states.state2, &match2); + if (match2 > match) + relief2 = relief; + } + + if ((border1 != border2) || (relief1 != relief2)) + mask |= CS_DISPLAY; + + return mask; +} + +static void UndefProcBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementBorder *elemX = (ElementBorder *) args->elem; + + PerStateInfo_Undefine(tree, &pstBorder, &elemX->border, args->state); + PerStateInfo_Undefine(tree, &pstRelief, &elemX->relief, args->state); +} + +static int ActualProcBorder(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementBorder *elemX = (ElementBorder *) args->elem; + ElementBorder *masterX = (ElementBorder *) args->elem->master; + static CONST char *optionName[] = { + "-background", "-relief", + (char *) NULL }; + int index, match, matchM; + Tcl_Obj *obj = NULL, *objM; + + if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, + "option", 0, &index) != TCL_OK) + return TCL_ERROR; + + switch (index) + { + case 0: + { + obj = PerStateInfo_ObjForState(tree, &pstBorder, &elemX->border, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstBorder, &masterX->border, args->state, &matchM); + if (matchM > match) + obj = objM; + } + break; + } + case 1: + { + obj = PerStateInfo_ObjForState(tree, &pstRelief, &elemX->relief, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstRelief, &masterX->relief, args->state, &matchM); + if (matchM > match) + obj = objM; + } + if (ObjectIsEmpty(obj)) + obj = Tcl_NewStringObj("flat", -1); + break; + } + } + if (obj != NULL) + Tcl_SetObjResult(tree->interp, obj); + return TCL_OK; +} + +ElementType elemTypeBorder = { + "border", + sizeof(ElementBorder), + borderOptionSpecs, + NULL, + CreateBorder, + DeleteBorder, + ConfigBorder, + DisplayBorder, + LayoutBorder, + WorldChangedBorder, + StateProcBorder, + UndefProcBorder, + ActualProcBorder +}; + +/*****/ + +typedef struct ElementImage ElementImage; + +struct ElementImage +{ + Element header; + PerStateInfo image; + int width; + Tcl_Obj *widthObj; + int height; + Tcl_Obj *heightObj; +}; + +#define IMAGE_CONF_IMAGE 0x0001 +#define IMAGE_CONF_SIZE 0x0002 + +static Tk_OptionSpec imageOptionSpecs[] = { + {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementImage, heightObj), + Tk_Offset(ElementImage, height), + TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_SIZE}, + {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementImage, image.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_IMAGE}, + {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementImage, widthObj), + Tk_Offset(ElementImage, width), + TK_OPTION_NULL_OK, (ClientData) NULL, IMAGE_CONF_SIZE}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} +}; + +static void DeleteImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementImage *elemX = (ElementImage *) elem; + + PerStateInfo_Free(tree, &pstImage, &elemX->image); +} + +static int WorldChangedImage(ElementArgs *args) +{ + int flagM = args->change.flagMaster; + int flagS = args->change.flagSelf; + int mask = 0; + + if ((flagS | flagM) & (IMAGE_CONF_IMAGE | IMAGE_CONF_SIZE)) + mask |= CS_DISPLAY | CS_LAYOUT; + + return mask; +} + +static int ConfigImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementImage *elemX = (ElementImage *) elem; + ElementImage savedX; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) elemX, + elem->typePtr->optionTable, + args->config.objc, args->config.objv, tree->tkwin, + &savedOptions, &args->config.flagSelf) != TCL_OK) + { + args->config.flagSelf = 0; + continue; + } + + if (args->config.flagSelf & IMAGE_CONF_IMAGE) + { + PSTSave(&elemX->image, &savedX.image); + if (PerStateInfo_FromObj(tree, &pstImage, &elemX->image) != TCL_OK) + continue; + } + + if (args->config.flagSelf & IMAGE_CONF_IMAGE) + PerStateInfo_Free(tree, &pstImage, &savedX.image); + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (args->config.flagSelf & IMAGE_CONF_IMAGE) + PSTRestore(tree, &pstImage, &elemX->image, &savedX.image); + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + return TCL_OK; +} + +static int CreateImage(ElementArgs *args) +{ + return TCL_OK; +} + +static void DisplayImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementImage *elemX = (ElementImage *) elem; + ElementImage *masterX = (ElementImage *) elem->master; + int state = args->state; + int match, matchM; + Tk_Image image; + int imgW, imgH; + int dx = 0, dy = 0; + + image = PerStateImage_ForState(tree, &elemX->image, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Image imageM = PerStateImage_ForState(tree, &masterX->image, state, &matchM); + if ((image == NULL) || (matchM > match)) + image = imageM; + } + + if (image != NULL) + { + Tk_SizeOfImage(image, &imgW, &imgH); + if (imgW < args->display.width) + dx = (args->display.width - imgW) / 2; + else if (imgW > args->display.width) + imgW = args->display.width; + if (imgH < args->display.height) + dy = (args->display.height - imgH) / 2; + else if (imgH > args->display.height) + imgH = args->display.height; + Tk_RedrawImage(image, 0, 0, imgW, imgH, args->display.drawable, + args->display.x /* + args->display.pad[LEFT] */ + dx, + args->display.y /* + args->display.pad[TOP] */ + dy); + } +} + +static void LayoutImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementImage *elemX = (ElementImage *) elem; + ElementImage *masterX = (ElementImage *) elem->master; + int state = args->state; + int match, match2; + Tk_Image image; + int width = 0, height = 0; + + image = PerStateImage_ForState(tree, &elemX->image, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Image image2 = PerStateImage_ForState(tree, &masterX->image, state, &match2); + if ((image == NULL) || (match2 > match)) + image = image2; + } + + if (image != NULL) + Tk_SizeOfImage(image, &width, &height); + + if (elemX->widthObj != NULL) + width = elemX->width; + else if ((masterX != NULL) && (masterX->widthObj != NULL)) + width = masterX->width; + args->layout.width = width; + + if (elemX->heightObj != NULL) + height = elemX->height; + else if ((masterX != NULL) && (masterX->heightObj != NULL)) + height = masterX->height; + args->layout.height = height; +} + +static int StateProcImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementImage *elemX = (ElementImage *) elem; + ElementImage *masterX = (ElementImage *) elem->master; + int match, match2; + Tk_Image image1, image2; + int mask = 0; + + image1 = PerStateImage_ForState(tree, &elemX->image, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Image image = PerStateImage_ForState(tree, &masterX->image, args->states.state1, &match2); + if ((image1 == NULL) || (match2 > match)) + image1 = image; + } + + image2 = PerStateImage_ForState(tree, &elemX->image, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Image image = PerStateImage_ForState(tree, &masterX->image, args->states.state2, &match2); + if ((image1 == NULL) || (match2 > match)) + image1 = image; + } + + if (image1 != image2) + { + mask |= CS_DISPLAY; + if ((image1 != NULL) && (image2 != NULL)) + { + int w1, h1, w2, h2; + Tk_SizeOfImage(image1, &w1, &h1); + Tk_SizeOfImage(image2, &w2, &h2); + if ((w1 != w2) || (h1 != h2)) + mask |= CS_LAYOUT; + } + else + mask |= CS_LAYOUT; + } + + return mask; +} + +static void UndefProcImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementImage *elemX = (ElementImage *) args->elem; + + PerStateInfo_Undefine(tree, &pstImage, &elemX->image, args->state); +} + +static int ActualProcImage(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementImage *elemX = (ElementImage *) args->elem; + ElementImage *masterX = (ElementImage *) args->elem->master; + static CONST char *optionName[] = { + "-image", + (char *) NULL }; + int index, match, matchM; + Tcl_Obj *obj = NULL, *objM; + + if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, + "option", 0, &index) != TCL_OK) + return TCL_ERROR; + + switch (index) + { + case 0: + { + obj = PerStateInfo_ObjForState(tree, &pstImage, &elemX->image, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstImage, &masterX->image, args->state, &matchM); + if (matchM > match) + obj = objM; + } + break; + } + } + if (obj != NULL) + Tcl_SetObjResult(tree->interp, obj); + return TCL_OK; +} + +ElementType elemTypeImage = { + "image", + sizeof(ElementImage), + imageOptionSpecs, + NULL, + CreateImage, + DeleteImage, + ConfigImage, + DisplayImage, + LayoutImage, + WorldChangedImage, + StateProcImage, + UndefProcImage, + ActualProcImage +}; + +/*****/ + +typedef struct ElementRect ElementRect; + +struct ElementRect +{ + Element header; + int width; + Tcl_Obj *widthObj; + int height; + Tcl_Obj *heightObj; + PerStateInfo fill; + PerStateInfo outline; + int outlineWidth; + Tcl_Obj *outlineWidthObj; + int open; + char *openString; + int showFocus; +}; + +#define RECT_CONF_FILL 0x0001 +#define RECT_CONF_OUTLINE 0x0002 +#define RECT_CONF_OUTWIDTH 0x0004 +#define RECT_CONF_OPEN 0x0008 +#define RECT_CONF_SIZE 0x0010 +#define RECT_CONF_FOCUS 0x0020 + +static Tk_OptionSpec rectOptionSpecs[] = { + {TK_OPTION_STRING, "-fill", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementRect, fill.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_FILL}, + {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementRect, heightObj), + Tk_Offset(ElementRect, height), + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_SIZE}, + {TK_OPTION_STRING, "-open", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementRect, openString), + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OPEN}, + {TK_OPTION_STRING, "-outline", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementRect, outline.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OUTLINE}, + {TK_OPTION_PIXELS, "-outlinewidth", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementRect, outlineWidthObj), + Tk_Offset(ElementRect, outlineWidth), + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_OUTWIDTH}, + {TK_OPTION_CUSTOM, "-showfocus", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementRect, showFocus), + TK_OPTION_NULL_OK, (ClientData) &booleanCO, RECT_CONF_FOCUS}, + {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementRect, widthObj), + Tk_Offset(ElementRect, width), + TK_OPTION_NULL_OK, (ClientData) NULL, RECT_CONF_SIZE}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} +}; + +static void DeleteRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementRect *elemX = (ElementRect *) args->elem; + + PerStateInfo_Free(tree, &pstColor, &elemX->fill); + PerStateInfo_Free(tree, &pstColor, &elemX->outline); +} + +static int WorldChangedRect(ElementArgs *args) +{ + int flagM = args->change.flagMaster; + int flagS = args->change.flagSelf; + int mask = 0; + + if ((flagS | flagM) & (RECT_CONF_SIZE | RECT_CONF_OUTWIDTH)) + mask |= CS_DISPLAY | CS_LAYOUT; + + if ((flagS | flagM) & (RECT_CONF_FILL | RECT_CONF_OUTLINE | + RECT_CONF_OPEN | RECT_CONF_FOCUS)) + mask |= CS_DISPLAY; + + return mask; +} + +static int ConfigRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementRect *elemX = (ElementRect *) elem; + ElementRect savedX; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + int i; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) elemX, + elem->typePtr->optionTable, + args->config.objc, args->config.objv, tree->tkwin, + &savedOptions, &args->config.flagSelf) != TCL_OK) + { + args->config.flagSelf = 0; + continue; + } + + if (args->config.flagSelf & RECT_CONF_FILL) + { + PSTSave(&elemX->fill, &savedX.fill); + if (PerStateInfo_FromObj(tree, &pstColor, &elemX->fill) != TCL_OK) + continue; + } + + if (args->config.flagSelf & RECT_CONF_OUTLINE) + { + PSTSave(&elemX->outline, &savedX.outline); + if (PerStateInfo_FromObj(tree, &pstColor, &elemX->outline) != TCL_OK) + continue; + } + + if (args->config.flagSelf & RECT_CONF_OPEN) + { + savedX.open = elemX->open; + elemX->open = 0; + if (elemX->openString != NULL) + { + for (i = 0; elemX->openString[i]; i++) + { + switch (elemX->openString[i]) + { + case 'w': elemX->open |= 0x01; break; + case 'n': elemX->open |= 0x02; break; + case 'e': elemX->open |= 0x04; break; + case 's': elemX->open |= 0x08; break; + } + } + } + } + + if (args->config.flagSelf & RECT_CONF_FILL) + PerStateInfo_Free(tree, &pstColor, &savedX.fill); + if (args->config.flagSelf & RECT_CONF_OUTLINE) + PerStateInfo_Free(tree, &pstColor, &savedX.outline); + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (args->config.flagSelf & RECT_CONF_FILL) + PSTRestore(tree, &pstColor, &elemX->fill, &savedX.fill); + + if (args->config.flagSelf & RECT_CONF_OUTLINE) + PSTRestore(tree, &pstColor, &elemX->outline, &savedX.outline); + + if (args->config.flagSelf & RECT_CONF_OPEN) + elemX->open = savedX.open; + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + return TCL_OK; +} + +static int CreateRect(ElementArgs *args) +{ + ElementRect *elemX = (ElementRect *) args->elem; + + elemX->showFocus = -1; + return TCL_OK; +} + +static void DisplayRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementRect *elemX = (ElementRect *) elem; + ElementRect *masterX = (ElementRect *) elem->master; + int state = args->state; + int match, match2; + XColor *color, *color2; + int open = 0; + int outlineWidth = 0; + int showFocus = 0; + + if (elemX->outlineWidthObj != NULL) + outlineWidth = elemX->outlineWidth; + else if ((masterX != NULL) && (masterX->outlineWidthObj != NULL)) + outlineWidth = masterX->outlineWidth; + + if (elemX->openString != NULL) + open = elemX->open; + else if ((masterX != NULL) && (masterX->openString != NULL)) + open = masterX->open; + + if (elemX->showFocus != -1) + showFocus = elemX->showFocus; + else if ((masterX != NULL) && (masterX->showFocus != -1)) + showFocus = masterX->showFocus; + + color = PerStateColor_ForState(tree, &elemX->fill, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + color2 = PerStateColor_ForState(tree, &masterX->fill, state, &match2); + if (match2 > match) + color = color2; + } + if (color != NULL) + { + XFillRectangle(tree->display, args->display.drawable, + Tk_GCForColor(color, Tk_WindowId(tree->tkwin)), + args->display.x, args->display.y, + args->display.width, args->display.height); + } + + color = PerStateColor_ForState(tree, &elemX->outline, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + color2 = PerStateColor_ForState(tree, &masterX->outline, state, &match2); + if (match2 > match) + color = color2; + } + if ((color != NULL) && (outlineWidth > 0)) + { + GC gc = Tk_GCForColor(color, Tk_WindowId(tree->tkwin)); + int w1, w2; + + w1 = outlineWidth / 2; + w2 = outlineWidth - w1; +#if 0 + if (open == 0) + { + XDrawRectangle(tree->display, args->display.drawable, gc, + args->display.x + w1, args->display.y + w1, + args->display.width - outlineWidth, + args->display.height - outlineWidth); + } + else +#endif + { + int x = args->display.x; + int y = args->display.y; + int w = args->display.width; + int h = args->display.height; + + if (!(open & 0x01)) + XFillRectangle(tree->display, args->display.drawable, gc, + x, y, outlineWidth, h); + if (!(open & 0x02)) + XFillRectangle(tree->display, args->display.drawable, gc, + x, y, w, outlineWidth); + if (!(open & 0x04)) + XFillRectangle(tree->display, args->display.drawable, gc, + x + w - outlineWidth, y, outlineWidth, h); + if (!(open & 0x08)) + XFillRectangle(tree->display, args->display.drawable, gc, + x, y + h - outlineWidth, w, outlineWidth); + } + } + + if (showFocus && (state & STATE_FOCUS) && (state & STATE_ACTIVE)) + { + DrawActiveOutline(tree, args->display.drawable, + args->display.x, args->display.y, + args->display.width, args->display.height, + open); + } +} + +static void LayoutRect(ElementArgs *args) +{ + Element *elem = args->elem; + ElementRect *elemX = (ElementRect *) elem; + ElementRect *masterX = (ElementRect *) elem->master; + int width, height, outlineWidth; + + if (elemX->outlineWidthObj != NULL) + outlineWidth = elemX->outlineWidth; + else if ((masterX != NULL) && (masterX->outlineWidthObj != NULL)) + outlineWidth = masterX->outlineWidth; + else + outlineWidth = 0; + + if (elemX->widthObj != NULL) + width = elemX->width; + else if ((masterX != NULL) && (masterX->widthObj != NULL)) + width = masterX->width; + else + width = 0; + args->layout.width = MAX(width, outlineWidth * 2); + + if (elemX->heightObj != NULL) + height = elemX->height; + else if ((masterX != NULL) && (masterX->heightObj != NULL)) + height = masterX->height; + else + height = 0; + args->layout.height = MAX(height, outlineWidth * 2); +} + +static int StateProcRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementRect *elemX = (ElementRect *) elem; + ElementRect *masterX = (ElementRect *) elem->master; + int match, match2; + XColor *f1, *f2; + XColor *o1, *o2; + int s1, s2; + int showFocus = 0; + int mask = 0; + + if (elemX->showFocus != -1) + showFocus = elemX->showFocus; + else if ((masterX != NULL) && (masterX->showFocus != -1)) + showFocus = masterX->showFocus; + + f1 = PerStateColor_ForState(tree, &elemX->fill, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *f = PerStateColor_ForState(tree, &masterX->fill, args->states.state1, &match2); + if (match2 > match) + f1 = f; + } + + o1 = PerStateColor_ForState(tree, &elemX->outline, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *o = PerStateColor_ForState(tree, &masterX->outline, args->states.state1, &match2); + if (match2 > match) + o1 = o; + } + + s1 = showFocus && + (args->states.state1 & STATE_FOCUS) && + (args->states.state1 & STATE_ACTIVE); + + f2 = PerStateColor_ForState(tree, &elemX->fill, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *f = PerStateColor_ForState(tree, &masterX->fill, args->states.state2, &match2); + if (match2 > match) + f2 = f; + } + + o2 = PerStateColor_ForState(tree, &elemX->outline, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *o = PerStateColor_ForState(tree, &masterX->outline, args->states.state2, &match2); + if (match2 > match) + o2 = o; + } + + s2 = showFocus && + (args->states.state2 & STATE_FOCUS) && + (args->states.state2 & STATE_ACTIVE); + + if ((f1 != f2) || (o1 != o2) || (s1 != s2)) + mask |= CS_DISPLAY; + + return mask; +} + +static void UndefProcRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementRect *elemX = (ElementRect *) args->elem; + + PerStateInfo_Undefine(tree, &pstColor, &elemX->fill, args->state); + PerStateInfo_Undefine(tree, &pstColor, &elemX->outline, args->state); +} + +static int ActualProcRect(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementRect *elemX = (ElementRect *) args->elem; + ElementRect *masterX = (ElementRect *) args->elem->master; + static CONST char *optionName[] = { + "-fill", "-outline", + (char *) NULL }; + int index, match, matchM; + Tcl_Obj *obj = NULL, *objM; + + if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, + "option", 0, &index) != TCL_OK) + return TCL_ERROR; + + switch (index) + { + case 0: + { + obj = PerStateInfo_ObjForState(tree, &pstColor, &elemX->fill, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstColor, &masterX->fill, args->state, &matchM); + if (matchM > match) + obj = objM; + } + break; + } + case 1: + { + obj = PerStateInfo_ObjForState(tree, &pstColor, &elemX->outline, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstColor, &masterX->outline, args->state, &matchM); + if (matchM > match) + obj = objM; + } + break; + } + } + if (obj != NULL) + Tcl_SetObjResult(tree->interp, obj); + return TCL_OK; +} + +ElementType elemTypeRect = { + "rect", + sizeof(ElementRect), + rectOptionSpecs, + NULL, + CreateRect, + DeleteRect, + ConfigRect, + DisplayRect, + LayoutRect, + WorldChangedRect, + StateProcRect, + UndefProcRect, + ActualProcRect +}; + +/*****/ + +typedef struct ElementText ElementText; + +/* Compile-time option */ +#define USE_TEXT_DATA 1 + +struct ElementText +{ + Element header; + Tcl_Obj *textObj; /* -text */ + char *text; + int textLen; +#ifdef USE_TEXT_DATA + Tcl_Obj *dataObj; +#define TDT_NULL -1 +#define TDT_DOUBLE 0 +#define TDT_INTEGER 1 +#define TDT_LONG 2 +#define TDT_STRING 3 +#define TDT_TIME 4 +#define TEXT_CONF_DATA 0x0001000 /* for Tk_SetOptions() */ + int dataType; /* -datatype */ + Tcl_Obj *formatObj; /* -format */ + int stringRepInvalid; +#endif /* USE_TEXT_DATA */ + PerStateInfo font; + PerStateInfo fill; + struct PerStateGC *gc; +#define TK_JUSTIFY_NULL -1 + int justify; /* -justify */ + int lines; /* -lines */ + Tcl_Obj *widthObj; /* -width */ + int width; /* -width */ +#define TEXT_WRAP_NULL -1 +#define TEXT_WRAP_CHAR 0 +#define TEXT_WRAP_WORD 1 + int wrap; /* -wrap */ + TextLayout layout; + int layoutInvalid; + int layoutWidth; +}; + +/* for Tk_SetOptions() */ +#define TEXT_CONF_FONT 0x00000001 +#define TEXT_CONF_FILL 0x00000002 +#define TEXT_CONF_TEXTOBJ 0x00000010 +#define TEXT_CONF_LAYOUT 0x00000020 + +static CONST char *textDataTypeST[] = { "double", "integer", "long", "string", + "time", (char *) NULL }; +static StringTableClientData textDataTypeCD = +{ + textDataTypeST, + "datatype" +}; +static Tk_ObjCustomOption textDataTypeCO = +{ + "datatype", + StringTableSet, + StringTableGet, + StringTableRestore, + NULL, + (ClientData) &textDataTypeCD +}; + +static CONST char *textJustifyST[] = { "left", "right", "center", (char *) NULL }; +static StringTableClientData textJustifyCD = +{ + textJustifyST, + "justification" +}; +static Tk_ObjCustomOption textJustifyCO = +{ + "justification", + StringTableSet, + StringTableGet, + StringTableRestore, + NULL, + (ClientData) &textJustifyCD +}; + +static IntegerClientData textLinesCD = +{ + 0, /* min */ + 0, /* max (ignored) */ + -1, /* empty */ + 0x01 /* flags: min */ +}; +static Tk_ObjCustomOption textLinesCO = +{ + "integer", + IntegerSet, + IntegerGet, + IntegerRestore, + NULL, + (ClientData) &textLinesCD +}; + +static CONST char *textWrapST[] = { "char", "word", (char *) NULL }; +static StringTableClientData textWrapCD = +{ + textWrapST, + "wrap" +}; +static Tk_ObjCustomOption textWrapCO = +{ + "wrap", + StringTableSet, + StringTableGet, + StringTableRestore, + NULL, + (ClientData) &textWrapCD +}; + +static Tk_OptionSpec textOptionSpecs[] = { +#ifdef USE_TEXT_DATA + {TK_OPTION_STRING, "-data", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, dataObj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_DATA}, + {TK_OPTION_CUSTOM, "-datatype", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementText, dataType), + TK_OPTION_NULL_OK, (ClientData) &textDataTypeCO, TEXT_CONF_DATA}, + {TK_OPTION_STRING, "-format", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, formatObj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_DATA}, +#endif /* USE_TEXT_DATA */ + {TK_OPTION_STRING, "-fill", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, fill.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_FILL}, + {TK_OPTION_STRING, "-font", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, font.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_FONT}, + {TK_OPTION_CUSTOM, "-justify", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementText, justify), + TK_OPTION_NULL_OK, (ClientData) &textJustifyCO, TEXT_CONF_LAYOUT}, + {TK_OPTION_CUSTOM, "-lines", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementText, lines), + TK_OPTION_NULL_OK, (ClientData) &textLinesCO, TEXT_CONF_LAYOUT}, + {TK_OPTION_STRING, "-text", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, textObj), + Tk_Offset(ElementText, text), + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_TEXTOBJ}, + {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(ElementText, widthObj), + Tk_Offset(ElementText, width), + TK_OPTION_NULL_OK, (ClientData) NULL, TEXT_CONF_LAYOUT}, + {TK_OPTION_CUSTOM, "-wrap", (char *) NULL, (char *) NULL, + (char *) NULL, -1, Tk_Offset(ElementText, wrap), + TK_OPTION_NULL_OK, (ClientData) &textWrapCO, TEXT_CONF_LAYOUT}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +static int WorldChangedText(ElementArgs *args) +{ +/* TreeCtrl *tree = args->tree;*/ + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; +/* ElementText *masterX = (ElementText *) elem->master;*/ + int flagT = args->change.flagTree; + int flagM = args->change.flagMaster; + int flagS = args->change.flagSelf; + int mask = 0; + +#ifdef USE_TEXT_DATA + if ((flagS | flagM) & (TEXT_CONF_DATA | TEXT_CONF_TEXTOBJ)) + { + elemX->stringRepInvalid = TRUE; + mask |= CS_DISPLAY | CS_LAYOUT; + } +#endif /* USE_TEXT_DATA */ + + if (elemX->stringRepInvalid || + ((flagS | flagM) & (TEXT_CONF_FONT | TEXT_CONF_LAYOUT)) || + /* Not always needed */ + (flagT & TREE_CONF_FONT)) + { + elemX->layoutInvalid = TRUE; + mask |= CS_DISPLAY | CS_LAYOUT; + } + + if ((flagS | flagM) & TEXT_CONF_FILL) + mask |= CS_DISPLAY; + + return mask; +} + +#ifdef USE_TEXT_DATA + +static void TextUpdateStringRep(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + Tcl_Obj *dataObj, *formatObj, *textObj; + int dataType; + + dataObj = elemX->dataObj; + if ((dataObj == NULL) && (masterX != NULL)) + dataObj = masterX->dataObj; + + dataType = elemX->dataType; + if ((dataType == TDT_NULL) && (masterX != NULL)) + dataType = masterX->dataType; + + formatObj = elemX->formatObj; + if ((formatObj == NULL) && (masterX != NULL)) + formatObj = masterX->formatObj; + + textObj = elemX->textObj; + if ((textObj == NULL) && (masterX != NULL)) + textObj = masterX->textObj; + + if ((elemX->textObj == NULL) && (elemX->text != NULL)) + { + ckfree(elemX->text); + elemX->text = NULL; + } + + /* If -text is specified, -data, -datatype and -format are ignored */ + if (textObj != NULL) + { + (void) Tcl_GetStringFromObj(textObj, &elemX->textLen); + } + + /* Only create a string rep if elemX (not masterX) has dataObj, + dataType or formatObj. */ + else if ((dataObj != NULL) && (dataType != TDT_NULL) && + ((elemX->dataObj != NULL) || + (elemX->dataType != TDT_NULL) || + (elemX->formatObj != NULL))) + { + int i, objc = 0; + Tcl_Obj *objv[5], *resultObj = NULL; + static Tcl_Obj *staticObj[3] = { NULL }; + static Tcl_Obj *staticFormat[4] = { NULL }; + Tcl_ObjCmdProc *clockObjCmd = NULL; + Tcl_ObjCmdProc *formatObjCmd = NULL; + + if (staticFormat[0] == NULL) + { + staticFormat[0] = Tcl_NewStringObj("%g", -1); + staticFormat[1] = Tcl_NewStringObj("%d", -1); + staticFormat[2] = Tcl_NewStringObj("%ld", -1); + staticFormat[3] = Tcl_NewStringObj("%s", -1); + for (i = 0; i < 4; i++) + Tcl_IncrRefCount(staticFormat[i]); + } + if (staticObj[0] == NULL) + { + staticObj[0] = Tcl_NewStringObj("clock", -1); + staticObj[1] = Tcl_NewStringObj("format", -1); + staticObj[2] = Tcl_NewStringObj("-format", -1); + for (i = 0; i < 3; i++) + Tcl_IncrRefCount(staticObj[i]); + } + if (clockObjCmd == NULL) + { + Tcl_CmdInfo cmdInfo; + + if (Tcl_GetCommandInfo(tree->interp, "::clock", &cmdInfo) == 1) + clockObjCmd = cmdInfo.objProc; + } + if (formatObjCmd == NULL) + { + Tcl_CmdInfo cmdInfo; + + if (Tcl_GetCommandInfo(tree->interp, "::format", &cmdInfo) == 1) + formatObjCmd = cmdInfo.objProc; + } + + /* Important to remove any shared result object, otherwise + * calls like Tcl_SetStringObj(Tcl_GetObjResult()) fail. */ + Tcl_ResetResult(tree->interp); + + switch (dataType) + { + case TDT_DOUBLE: + if (formatObjCmd == NULL) + break; + if (formatObj == NULL) formatObj = staticFormat[0]; + objv[objc++] = staticObj[1]; /* format */ + objv[objc++] = formatObj; + objv[objc++] = dataObj; + if (formatObjCmd(NULL, tree->interp, objc, objv) == TCL_OK) + resultObj = Tcl_GetObjResult(tree->interp); + break; + case TDT_INTEGER: + if (formatObjCmd == NULL) + break; + if (formatObj == NULL) formatObj = staticFormat[1]; + objv[objc++] = staticObj[1]; /* format */ + objv[objc++] = formatObj; + objv[objc++] = dataObj; + if (formatObjCmd(NULL, tree->interp, objc, objv) == TCL_OK) + resultObj = Tcl_GetObjResult(tree->interp); + break; + case TDT_LONG: + if (formatObjCmd == NULL) + break; + if (formatObj == NULL) formatObj = staticFormat[2]; + objv[objc++] = staticObj[1]; /* format */ + objv[objc++] = formatObj; + objv[objc++] = dataObj; + if (formatObjCmd(NULL, tree->interp, objc, objv) == TCL_OK) + resultObj = Tcl_GetObjResult(tree->interp); + break; + case TDT_STRING: + if (formatObjCmd == NULL) + break; + if (formatObj == NULL) formatObj = staticFormat[3]; + objv[objc++] = staticObj[1]; /* format */ + objv[objc++] = formatObj; + objv[objc++] = dataObj; + if (formatObjCmd(NULL, tree->interp, objc, objv) == TCL_OK) + resultObj = Tcl_GetObjResult(tree->interp); + break; + case TDT_TIME: + if (clockObjCmd == NULL) + break; + objv[objc++] = staticObj[0]; + objv[objc++] = staticObj[1]; + objv[objc++] = dataObj; + if (formatObj != NULL) + { + objv[objc++] = staticObj[2]; + objv[objc++] = formatObj; + } + if (clockObjCmd(NULL, tree->interp, objc, objv) == TCL_OK) + resultObj = Tcl_GetObjResult(tree->interp); + break; + default: + panic("unknown ElementText dataType"); + break; + } + + if (resultObj != NULL) + { + char *string = Tcl_GetStringFromObj(resultObj, &elemX->textLen); + elemX->text = ckalloc(elemX->textLen); + memcpy(elemX->text, string, elemX->textLen); + } + } +} + +#endif /* USE_TEXT_DATA */ + +static void TextUpdateLayout(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + int state = args->state; + int match, match2; + Tk_Font tkfont, tkfont2; + char *text = NULL; + int textLen = 0; + int justify = TK_JUSTIFY_LEFT; + int lines = 0; + int wrap = TEXT_WRAP_WORD; + int width = 0; + int flags = 0; + int i, multiLine = FALSE; + + if (elemX->layout != NULL) + { +/*if (tree->debug.enable && tree->debug.display) + dbwin("TextUpdateLayout %s: free %p (%s)\n", Tk_PathName(tree->tkwin), elemX, masterX ? "instance" : "master");*/ + TextLayout_Free(elemX->layout); + elemX->layout = NULL; + } + + if (elemX->text != NULL) + { + text = elemX->text; + textLen = elemX->textLen; + } + else if ((masterX != NULL) && (masterX->text != NULL)) + { + text = masterX->text; + textLen = masterX->textLen; + } + if ((text == NULL) || (textLen == 0)) + return; + + for (i = 0; i < textLen; i++) + { + if ((text[i] == '\n') || (text[i] == '\r')) + { + multiLine = TRUE; + break; + } + } + + if (elemX->lines != -1) + lines = elemX->lines; + else if ((masterX != NULL) && (masterX->lines != -1)) + lines = masterX->lines; + + if (args->layout.width != -1) + width = args->layout.width; + else if (elemX->widthObj != NULL) + width = elemX->width; + else if ((masterX != NULL) && (masterX->widthObj != NULL)) + width = masterX->width; + + if ((lines == 1) || (!multiLine && (width == 0))) + return; + + if (elemX->justify != TK_JUSTIFY_NULL) + justify = elemX->justify; + else if ((masterX != NULL) && (masterX->justify != TK_JUSTIFY_NULL)) + justify = masterX->justify; + + if (elemX->wrap != TEXT_WRAP_NULL) + wrap = elemX->wrap; + else if ((masterX != NULL) && (masterX->wrap != TEXT_WRAP_NULL)) + wrap = masterX->wrap; + + tkfont = PerStateFont_ForState(tree, &elemX->font, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + tkfont2 = PerStateFont_ForState(tree, &masterX->font, state, &match2); + if (match2 > match) + tkfont = tkfont2; + } + if (tkfont == NULL) + tkfont = tree->tkfont; + + if (wrap == TEXT_WRAP_WORD) + flags |= TK_WHOLE_WORDS; + + elemX->layout = TextLayout_Compute(tkfont, text, + textLen, width, justify, lines, flags); + +/*if (tree->debug.enable && tree->debug.display) + dbwin("TextUpdateLayout %s: alloc %p (%s)\n", Tk_PathName(tree->tkwin), elemX, masterX ? "instance" : "master");*/ +} + +static void DeleteText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + + if (elemX->gc != NULL) + PerStateGC_Free(tree, &elemX->gc); + PerStateInfo_Free(tree, &pstColor, &elemX->fill); + PerStateInfo_Free(tree, &pstFont, &elemX->font); +#ifdef USE_TEXT_DATA + if ((elemX->textObj == NULL) && (elemX->text != NULL)) + { + ckfree(elemX->text); + elemX->text = NULL; + } +#endif + if (elemX->layout != NULL) + TextLayout_Free(elemX->layout); +} + +static int ConfigText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText savedX; + Tk_SavedOptions savedOptions; + int error; + Tcl_Obj *errorResult = NULL; + + for (error = 0; error <= 1; error++) + { + if (error == 0) + { + if (Tk_SetOptions(tree->interp, (char *) elemX, + elem->typePtr->optionTable, + args->config.objc, args->config.objv, tree->tkwin, + &savedOptions, &args->config.flagSelf) != TCL_OK) + { + args->config.flagSelf = 0; + continue; + } + + if (args->config.flagSelf & TEXT_CONF_FILL) + { + PSTSave(&elemX->fill, &savedX.fill); + if (PerStateInfo_FromObj(tree, &pstColor, &elemX->fill) != TCL_OK) + continue; + } + + if (args->config.flagSelf & TEXT_CONF_FONT) + { + PSTSave(&elemX->font, &savedX.font); + if (PerStateInfo_FromObj(tree, &pstFont, &elemX->font) != TCL_OK) + continue; + } + + if (args->config.flagSelf & TEXT_CONF_FILL) + PerStateInfo_Free(tree, &pstColor, &savedX.fill); + if (args->config.flagSelf & TEXT_CONF_FONT) + PerStateInfo_Free(tree, &pstFont, &savedX.font); + Tk_FreeSavedOptions(&savedOptions); + break; + } + else + { + errorResult = Tcl_GetObjResult(tree->interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + + if (args->config.flagSelf & TEXT_CONF_FILL) + PSTRestore(tree, &pstColor, &elemX->fill, &savedX.fill); + + if (args->config.flagSelf & TEXT_CONF_FONT) + PSTRestore(tree, &pstFont, &elemX->font, &savedX.font); + + Tcl_SetObjResult(tree->interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } + } + + return TCL_OK; +} + +static int CreateText(ElementArgs *args) +{ + ElementText *elemX = (ElementText *) args->elem; + +#ifdef USE_TEXT_DATA + elemX->dataType = TDT_NULL; +#endif + elemX->justify = TK_JUSTIFY_NULL; + elemX->lines = -1; + elemX->wrap = TEXT_WRAP_NULL; + return TCL_OK; +} + +static void DisplayText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + int state = args->state; + int match, match2; + XColor *color, *color2; + char *text = elemX->text; + int textLen = elemX->textLen; + Tk_Font tkfont, tkfont2; + TextLayout layout = NULL; + Tk_FontMetrics fm; + GC gc; + int bytesThatFit, pixelsForText; + char *ellipsis = "..."; + + if ((text == NULL) && (masterX != NULL)) + { + text = masterX->text; + textLen = masterX->textLen; + } + + if (text == NULL) /* always false (or layout sets height/width to zero) */ + return; + + color = PerStateColor_ForState(tree, &elemX->fill, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + color2 = PerStateColor_ForState(tree, &masterX->fill, state, &match2); + if (match2 > match) + color = color2; + } + + tkfont = PerStateFont_ForState(tree, &elemX->font, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + tkfont2 = PerStateFont_ForState(tree, &masterX->font, state, &match2); + if (match2 > match) + tkfont = tkfont2; + } + + /* FIXME: -font {"" {state...}}*/ + if ((color != NULL) || (tkfont != NULL)) + { + XGCValues gcValues; + unsigned long gcMask = 0; + if (color == NULL) + color = tree->fgColorPtr; + gcValues.foreground = color->pixel; + gcMask |= GCForeground; + if (tkfont == NULL) + tkfont = tree->tkfont; + gcValues.font = Tk_FontId(tkfont); + gcMask |= GCFont; + gcValues.graphics_exposures = False; + gcMask |= GCGraphicsExposures; + gc = PerStateGC_Get(tree, (masterX != NULL) ? &masterX->gc : + &elemX->gc, gcMask, &gcValues); + } + else + { + tkfont = tree->tkfont; + gc = tree->textGC; + } + + if (elemX->layout != NULL) + layout = elemX->layout; + else if ((elemX->text == NULL) && (masterX != NULL) && + (masterX->layout != NULL)) + layout = masterX->layout; + + if (layout != NULL) + { + TextLayout_Draw(tree->display, args->display.drawable, gc, + layout, + args->display.x /* + args->display.pad[LEFT] */, + args->display.y /* + args->display.pad[TOP] */, + 0, -1); + return; + } + + Tk_GetFontMetrics(tkfont, &fm); + + pixelsForText = args->display.width /* - args->display.pad[LEFT] - + args->display.pad[RIGHT] */; + bytesThatFit = Ellipsis(tkfont, text, textLen, &pixelsForText, ellipsis); + if (bytesThatFit != textLen) + { + char staticStr[256], *buf = staticStr; + int bufLen = abs(bytesThatFit); + int ellipsisLen = strlen(ellipsis); + + if (bufLen + ellipsisLen > sizeof(staticStr)) + buf = ckalloc(bufLen + ellipsisLen); + memcpy(buf, text, bufLen); + if (bytesThatFit > 0) + { + memcpy(buf + bufLen, ellipsis, ellipsisLen); + bufLen += ellipsisLen; + } + Tk_DrawChars(tree->display, args->display.drawable, gc, + tkfont, buf, bufLen, args->display.x /* + args->display.pad[LEFT] */, + args->display.y /* + args->display.pad[TOP] */ + fm.ascent); + if (buf != staticStr) + ckfree(buf); + } + else + { + Tk_DrawChars(tree->display, args->display.drawable, gc, + tkfont, text, textLen, args->display.x /* + args->display.pad[LEFT] */, + args->display.y /* + args->display.pad[TOP] */ + fm.ascent); + } +} + +static void LayoutText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + int state = args->state; + int match, match2; + char *text = NULL; + int textLen = 0; + Tk_Font tkfont; + Tk_FontMetrics fm; + int width = 0; + TextLayout layout = NULL; + +#ifdef USE_TEXT_DATA + if ((masterX != NULL) /* && (elemX != masterX) */ && masterX->stringRepInvalid) + { + args->elem = (Element *) masterX; + TextUpdateStringRep(args); + args->elem = elem; + masterX->stringRepInvalid = FALSE; + } + if (elemX->stringRepInvalid) + { + TextUpdateStringRep(args); + elemX->stringRepInvalid = FALSE; + } +#endif + + if ((masterX != NULL) /* && (elemX != masterX) */ && + (masterX->layoutInvalid || (masterX->layoutWidth != args->layout.width))) + { + args->elem = (Element *) masterX; + TextUpdateLayout(args); + args->elem = elem; + masterX->layoutInvalid = FALSE; + masterX->layoutWidth = args->layout.width; + } + if (elemX->layoutInvalid || (elemX->layoutWidth != args->layout.width)) + { + TextUpdateLayout(args); + elemX->layoutInvalid = FALSE; + elemX->layoutWidth = args->layout.width; + } + + if (elemX->layout != NULL) + layout = elemX->layout; + else if ((elemX->text == NULL) && (masterX != NULL) && + (masterX->layout != NULL)) + layout = masterX->layout; + + if (layout != NULL) + { + TextLayout_Size(layout, &args->layout.width, &args->layout.height); + return; + } + + if (elemX->text != NULL) + { + text = elemX->text; + textLen = elemX->textLen; + } + else if ((masterX != NULL) && (masterX->text != NULL)) + { + text = masterX->text; + textLen = masterX->textLen; + } + + if (text != NULL) + { + tkfont = PerStateFont_ForState(tree, &elemX->font, state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Font tkfont2 = PerStateFont_ForState(tree, &masterX->font, state, &match2); + if (match2 > match) + tkfont = tkfont2; + } + if (tkfont == NULL) + tkfont = tree->tkfont; + +#if 0 + /* Weird bug with MS Sans Serif 8 bold */ + Tk_MeasureChars(font, text, textLen, -1, 0, &width); + width2 = width; + while (Tk_MeasureChars(font, text, textLen, width2, 0, &width) != textLen) + width2++; + args->layout.width = width2; +#else + args->layout.width = Tk_TextWidth(tkfont, text, textLen); +#endif + if (elemX->widthObj != NULL) + width = elemX->width; + else if ((masterX != NULL) && (masterX->widthObj != NULL)) + width = masterX->width; + if (width && (width < args->layout.width)) + args->layout.width = width; + Tk_GetFontMetrics(tkfont, &fm); + args->layout.height = fm.linespace; /* TODO: multi-line strings */ + return; + } + + args->layout.width = args->layout.height = 0; +} + +int Element_GetSortData(TreeCtrl *tree, Element *elem, int type, long *lv, double *dv, char **sv) +{ + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + Tcl_Obj *dataObj = elemX->dataObj; + int dataType = elemX->dataType; + Tcl_Obj *obj; + + if (dataType == TDT_NULL && masterX != NULL) + dataType = masterX->dataType; + + switch (type) + { + case SORT_ASCII: + case SORT_DICT: + if (dataObj != NULL && dataType != TDT_NULL) + (*sv) = Tcl_GetString(dataObj); + else + (*sv) = elemX->text; + break; + case SORT_DOUBLE: + if (dataObj != NULL && dataType == TDT_DOUBLE) + obj = dataObj; + else + obj = elemX->textObj; + if (obj == NULL) + return TCL_ERROR; + if (Tcl_GetDoubleFromObj(tree->interp, obj, dv) != TCL_OK) + return TCL_ERROR; + break; + case SORT_LONG: + if (dataObj != NULL && dataType != TDT_NULL) + { + if (dataType == TDT_LONG || dataType == TDT_TIME) + { + if (Tcl_GetLongFromObj(tree->interp, dataObj, lv) != TCL_OK) + return TCL_ERROR; + break; + } + if (dataType == TDT_INTEGER) + { + int iv; + if (Tcl_GetIntFromObj(tree->interp, dataObj, &iv) != TCL_OK) + return TCL_ERROR; + (*lv) = iv; + break; + } + } + if (elemX->textObj != NULL) + if (Tcl_GetLongFromObj(tree->interp, elemX->textObj, lv) != TCL_OK) + return TCL_ERROR; + break; + } + return TCL_OK; +} + +static int StateProcText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + Element *elem = args->elem; + ElementText *elemX = (ElementText *) elem; + ElementText *masterX = (ElementText *) elem->master; + int match, match2; + XColor *f1, *f2; + Tk_Font tkfont1, tkfont2; + int mask = 0; + + f1 = PerStateColor_ForState(tree, &elemX->fill, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *f = PerStateColor_ForState(tree, &masterX->fill, args->states.state1, &match2); + if (match2 > match) + f1 = f; + } + + tkfont1 = PerStateFont_ForState(tree, &elemX->font, args->states.state1, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Font tkfont = PerStateFont_ForState(tree, &masterX->font, args->states.state1, &match2); + if (match2 > match) + tkfont1 = tkfont; + } + + f2 = PerStateColor_ForState(tree, &elemX->fill, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + XColor *f = PerStateColor_ForState(tree, &masterX->fill, args->states.state2, &match2); + if (match2 > match) + f2 = f; + } + + tkfont2 = PerStateFont_ForState(tree, &elemX->font, args->states.state2, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + Tk_Font tkfont = PerStateFont_ForState(tree, &masterX->font, args->states.state2, &match2); + if (match2 > match) + tkfont2 = tkfont; + } + + if (tkfont1 != tkfont2) + mask |= CS_DISPLAY | CS_LAYOUT; + + if (f1 != f2) + mask |= CS_DISPLAY; + + return mask; +} + +static void UndefProcText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementText *elemX = (ElementText *) args->elem; + + PerStateInfo_Undefine(tree, &pstColor, &elemX->fill, args->state); + PerStateInfo_Undefine(tree, &pstFont, &elemX->font, args->state); +} + +static int ActualProcText(ElementArgs *args) +{ + TreeCtrl *tree = args->tree; + ElementText *elemX = (ElementText *) args->elem; + ElementText *masterX = (ElementText *) args->elem->master; + static CONST char *optionName[] = { + "-fill", "-font", + (char *) NULL }; + int index, match, matchM; + Tcl_Obj *obj = NULL, *objM; + + if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, + "option", 0, &index) != TCL_OK) + return TCL_ERROR; + + switch (index) + { + case 0: + { + obj = PerStateInfo_ObjForState(tree, &pstColor, &elemX->fill, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstColor, &masterX->fill, args->state, &matchM); + if (matchM > match) + obj = objM; + } + if (ObjectIsEmpty(obj)) + obj = tree->fgObj; + break; + } + case 1: + { + obj = PerStateInfo_ObjForState(tree, &pstFont, &elemX->font, args->state, &match); + if ((match != MATCH_EXACT) && (masterX != NULL)) + { + objM = PerStateInfo_ObjForState(tree, &pstFont, &masterX->font, args->state, &matchM); + if (matchM > match) + obj = objM; + } + if (ObjectIsEmpty(obj)) + obj = tree->fontObj; + break; + } + } + if (obj != NULL) + Tcl_SetObjResult(tree->interp, obj); + return TCL_OK; +} + +ElementType elemTypeText = { + "text", + sizeof(ElementText), + textOptionSpecs, + NULL, + CreateText, + DeleteText, + ConfigText, + DisplayText, + LayoutText, + WorldChangedText, + StateProcText, + UndefProcText, + ActualProcText +}; + +/*****/ + diff --git a/generic/tkTreeElem.h b/generic/tkTreeElem.h new file mode 100644 index 0000000..3e83f1c --- /dev/null +++ b/generic/tkTreeElem.h @@ -0,0 +1,79 @@ +typedef struct ElementType ElementType; +typedef struct Element Element; +typedef struct ElementArgs ElementArgs; + +struct ElementArgs +{ + TreeCtrl *tree; + Element *elem; + int state; + struct { + } create; + struct { + } delete; + struct { + int objc; + Tcl_Obj *CONST *objv; + int flagSelf; + } config; + struct { + int x; + int y; + int width; + int height; + int pad[4]; + Drawable drawable; + } display; + struct { + int width; + int height; + } layout; + struct { + int flagTree; + int flagMaster; + int flagSelf; + } change; + struct { + int state1; + int state2; + } states; + struct { + Tcl_Obj *obj; + } actual; +}; + +struct ElementType +{ + char *name; /* "image", "text" */ + int size; /* size of an Element */ + Tk_OptionSpec *optionSpecs; + Tk_OptionTable optionTable; + int (*createProc)(ElementArgs *args); + void (*deleteProc)(ElementArgs *args); + int (*configProc)(ElementArgs *args); + void (*displayProc)(ElementArgs *args); + void (*layoutProc)(ElementArgs *args); + int (*changeProc)(ElementArgs *args); + int (*stateProc)(ElementArgs *args); + void (*undefProc)(ElementArgs *args); + int (*actualProc)(ElementArgs *args); + ElementType *next; +}; + +/* list of these for each style */ +struct Element +{ + Tk_Uid name; /* "image", "text" etc */ + ElementType *typePtr; + Element *master; /* NULL if this is master */ + /* type-specific data here */ +}; + +extern ElementType elemTypeBitmap; +extern ElementType elemTypeBorder; +extern ElementType elemTypeImage; +extern ElementType elemTypeRect; +extern ElementType elemTypeText; + +extern int Element_GetSortData(TreeCtrl *tree, Element *elem, int type, long *lv, double *dv, char **sv); + diff --git a/generic/tkTreeItem.c b/generic/tkTreeItem.c new file mode 100644 index 0000000..89cdf42 --- /dev/null +++ b/generic/tkTreeItem.c @@ -0,0 +1,3662 @@ +#include "tkTreeCtrl.h" + +typedef struct Column Column; +typedef struct Item Item; + +struct Column +{ + int neededWidth; + int neededHeight; + TreeStyle style; + Column *next; +}; + +struct Item +{ + int id; /* unique id */ + int depth; /* tree depth (-1 for the unique root item) */ + int neededHeight; /* miniumum height of this item (max of all columns) */ + int fixedHeight; /* desired height of this item (zero for no-such-value) */ + int numChildren; + int index; /* "row" in flattened tree */ + int indexVis; /* visible "row" in flattened tree, or -1 if not visible */ + int state; /* STATE_xxx */ + int isVisible; + int hasButton; + Item *parent; + Item *firstChild; + Item *lastChild; + Item *prevSibling; + Item *nextSibling; + TreeItemDInfo dInfo; /* display info, or NULL */ + TreeItemRInfo rInfo; /* range info, or NULL */ + Column *columns; +}; + +#define ISROOT(i) ((i)->depth == -1) + +/*****/ + +static Column *Column_Alloc(void) +{ + Column *column = (Column *) ckalloc(sizeof(Column)); + memset(column, '\0', sizeof(Column)); + return column; +} + +void TreeItemColumn_InvalidateSize(TreeCtrl *tree, TreeItemColumn column_) +{ + Column *column = (Column *) column_; + + column->neededWidth = column->neededHeight = -1; +} + +#if 0 + +int Column_NeededHeight(TreeCtrl *tree, Item *item, Column *self) +{ + int width, height; + + if (self->style != NULL) + TreeStyle_GetNeededSize(tree, self->style, item->state, &width, &height); + else + width = height = 0; + return height; +} + +#endif + +int TreeItemColumn_NeededWidth(TreeCtrl *tree, TreeItem item_, TreeItemColumn column_) +{ + Item *item = (Item *) item_; + Column *self = (Column *) column_; + + if (self->style != NULL) + return TreeStyle_NeededWidth(tree, self->style, item->state); + return 0; +} + +TreeStyle TreeItemColumn_GetStyle(TreeCtrl *tree, TreeItemColumn column) +{ + return ((Column *) column)->style; +} + +void TreeItemColumn_ForgetStyle(TreeCtrl *tree, TreeItemColumn column_) +{ + Column *self = (Column *) column_; + + if (self->style != NULL) + { + TreeStyle_FreeResources(tree, self->style); + self->style = NULL; + self->neededWidth = self->neededHeight = 0; + } +} + +TreeItemColumn TreeItemColumn_GetNext(TreeCtrl *tree, TreeItemColumn column) +{ + return (TreeItemColumn) ((Column *) column)->next; +} + +Column *Column_FreeResources(TreeCtrl *tree, Column *self) +{ + Column *next = self->next; + + if (self->style != NULL) + TreeStyle_FreeResources(tree, self->style); + WFREE(self, Column); + return next; +} + +/*****/ + +static void Item_UpdateIndex(TreeCtrl *tree, Item *item, int *index, int *indexVis) +{ + Item *child; + int parentVis, parentOpen; + + /* Also track max depth */ + if (item->parent != NULL) + item->depth = item->parent->depth + 1; + else + item->depth = 0; + if (item->depth > tree->depth) + tree->depth = item->depth; + + item->index = (*index)++; + item->indexVis = -1; + if (item->parent != NULL) + { + parentOpen = (item->parent->state & STATE_OPEN) != 0; + parentVis = item->parent->indexVis != -1; + if (ISROOT(item->parent) && !tree->showRoot) + { + parentOpen = TRUE; + parentVis = item->parent->isVisible; + } + if (parentVis && parentOpen && item->isVisible) + item->indexVis = (*indexVis)++; + } + child = item->firstChild; + while (child != NULL) + { + Item_UpdateIndex(tree, child, index, indexVis); + child = child->nextSibling; + } +} + +void Tree_UpdateItemIndex(TreeCtrl *tree) +{ + Item *item = (Item *) tree->root; + int index = 1, indexVis = 0; + +if (tree->debug.enable && tree->debug.data) + dbwin("Tree_UpdateItemIndex %s\n", Tk_PathName(tree->tkwin)); + + /* Also track max depth */ + tree->depth = 0; + + item->index = 0; + item->indexVis = -1; + if (tree->showRoot && item->isVisible) + item->indexVis = indexVis++; + item = item->firstChild; + while (item != NULL) + { + Item_UpdateIndex(tree, item, &index, &indexVis); + item = item->nextSibling; + } + tree->itemVisCount = indexVis; + tree->updateIndex = 0; +} + +TreeItem TreeItem_Alloc(TreeCtrl *tree) +{ + Item *item = (Item *) ckalloc(sizeof(Item)); + memset(item, '\0', sizeof(Item)); + item->isVisible = TRUE; + item->state = + STATE_OPEN | + STATE_ENABLED; + if (tree->gotFocus) + item->state |= STATE_FOCUS; + item->indexVis = -1; + Tree_AddItem(tree, (TreeItem) item); + return (TreeItem) item; +} + +TreeItem TreeItem_AllocRoot(TreeCtrl *tree) +{ + Item *item; + + item = (Item *) TreeItem_Alloc(tree); + item->depth = -1; + item->state |= STATE_ACTIVE; + return (TreeItem) item; +} + +TreeItemColumn TreeItem_GetFirstColumn(TreeCtrl *tree, TreeItem item) +{ + return (TreeItemColumn) ((Item *) item)->columns; +} + +int TreeItem_GetState(TreeCtrl *tree, TreeItem item_) +{ + return ((Item *) item_)->state; +} + +int TreeItem_ChangeState(TreeCtrl *tree, TreeItem item_, int stateOff, int stateOn) +{ + Item *item = (Item *) item_; + Column *column; + int columnIndex = 0, state; + int sMask, iMask = 0; + + state = item->state; + state &= ~stateOff; + state |= stateOn; + + if (state == item->state) + return 0; + + column = item->columns; + while (column != NULL) + { + if (column->style != NULL) + { + sMask = TreeStyle_ChangeState(tree, column->style, item->state, state); + if (sMask) + { + if (sMask & CS_LAYOUT) + Tree_InvalidateColumnWidth(tree, columnIndex); + iMask |= sMask; + } + } + columnIndex++; + column = column->next; + } + + /* OPEN -> CLOSED or vice versa */ + if ((stateOff | stateOn) & STATE_OPEN) + { + /* This item has a button */ + if (item->hasButton && tree->showButtons && (!ISROOT(item) || tree->showRootButton)) + { + /* Image or bitmap is used */ + if ((tree->openButtonWidth != tree->closedButtonWidth) || + (tree->openButtonHeight != tree->closedButtonHeight)) + { + iMask |= CS_LAYOUT; + } + iMask |= CS_DISPLAY; + } + } + + if (iMask & CS_LAYOUT) + { + TreeItem_InvalidateHeight(tree, item_); + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + } + if (iMask & CS_DISPLAY) + Tree_InvalidateItemDInfo(tree, item_, NULL); + + item->state = state; + + return iMask; +} + +void TreeItem_Undefine(TreeCtrl *tree, TreeItem item_, int state) +{ + Item *item = (Item *) item_; + item->state &= ~state; +} + +int TreeItem_GetButton(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return item->hasButton; +} + +int TreeItem_SetButton(TreeCtrl *tree, TreeItem item_, int hasButton) +{ + Item *item = (Item *) item_; + return item->hasButton = hasButton; +} + +int TreeItem_GetDepth(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; +#if 0 + if (tree->updateIndex) + { + if (tree->debug.enable && tree->debug.data) dbwin("%s: ", __FUNCTION__); + Tree_UpdateItemIndex(tree); + } +#endif + return item->depth; +} + +int TreeItem_SetDepth(TreeCtrl *tree, TreeItem item_, int depth) +{ + Item *item = (Item *) item_; + return item->depth = depth; +} + +int TreeItem_GetID(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return item->id; +} + +int TreeItem_SetID(TreeCtrl *tree, TreeItem item_, int id) +{ + Item *item = (Item *) item_; + return item->id = id; +} + +int TreeItem_GetOpen(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (item->state & STATE_OPEN) != 0; +} + +int TreeItem_SetOpen(TreeCtrl *tree, TreeItem item_, int isOpen) +{ + Item *item = (Item *) item_; + if (isOpen) + item->state |= STATE_OPEN; + else + item->state &= ~STATE_OPEN; + return isOpen; +} + +int TreeItem_GetSelected(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (item->state & STATE_SELECTED) != 0; +} + +int TreeItem_SetSelected(TreeCtrl *tree, TreeItem item_, int isSelected) +{ + Item *item = (Item *) item_; + if (isSelected) + item->state |= STATE_SELECTED; + else + item->state &= ~STATE_SELECTED; + return isSelected; +} + +TreeItem TreeItem_SetFirstChild(TreeCtrl *tree, TreeItem item_, TreeItem firstChild) +{ + Item *item = (Item *) item_; + return (TreeItem) (item->firstChild = (Item *) firstChild); +} + +TreeItem TreeItem_GetFirstChild(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (TreeItem) item->firstChild; +} + +TreeItem TreeItem_SetLastChild(TreeCtrl *tree, TreeItem item_, TreeItem lastChild) +{ + Item *item = (Item *) item_; + return (TreeItem) (item->lastChild = (Item *) lastChild); +} + +TreeItem TreeItem_GetLastChild(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (TreeItem) item->lastChild; +} + +TreeItem TreeItem_SetParent(TreeCtrl *tree, TreeItem item_, TreeItem parent) +{ + Item *item = (Item *) item_; + return (TreeItem) (item->parent = (Item *) parent); +} + +TreeItem TreeItem_GetParent(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (TreeItem) item->parent; +} + +TreeItem TreeItem_SetNextSibling(TreeCtrl *tree, TreeItem item_, TreeItem nextSibling) +{ + Item *item = (Item *) item_; + return (TreeItem) (item->nextSibling = (Item *) nextSibling); +} + +TreeItem TreeItem_GetNextSibling(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (TreeItem) item->nextSibling; +} + +TreeItem TreeItem_SetPrevSibling(TreeCtrl *tree, TreeItem item_, TreeItem prevSibling) +{ + Item *item = (Item *) item_; + return (TreeItem) (item->prevSibling = (Item *) prevSibling); +} + +TreeItem TreeItem_GetPrevSibling(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return (TreeItem) item->prevSibling; +} + +void TreeItem_SetDInfo(TreeCtrl *tree, TreeItem item_, TreeItemDInfo dInfo) +{ + Item *item = (Item *) item_; + item->dInfo = dInfo; +} + +TreeItemDInfo TreeItem_GetDInfo(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return item->dInfo; +} + +void TreeItem_SetRInfo(TreeCtrl *tree, TreeItem item_, TreeItemRInfo rInfo) +{ + Item *item = (Item *) item_; + item->rInfo = rInfo; +} + +TreeItemRInfo TreeItem_GetRInfo(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + return item->rInfo; +} + +TreeItem TreeItem_Next(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + + if (item->firstChild != NULL) + return (TreeItem) item->firstChild; + if (item->nextSibling != NULL) + return (TreeItem) item->nextSibling; + while (1) + { + item = item->parent; + if (item == NULL) + break; + if (item->nextSibling != NULL) + return (TreeItem) item->nextSibling; + } + return NULL; +} + +TreeItem TreeItem_NextVisible(TreeCtrl *tree, TreeItem item) +{ + item = TreeItem_Next(tree, item); + while (item != NULL) + { + if (TreeItem_ReallyVisible(tree, item)) + return item; + item = TreeItem_Next(tree, item); + } + return NULL; +} + +TreeItem TreeItem_Prev(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + Item *walk; + + if (item->parent == NULL) /* root */ + return NULL; + walk = item->parent; + if (item->prevSibling) + { + walk = item->prevSibling; + while (walk->lastChild != NULL) + walk = walk->lastChild; + } + return (TreeItem) walk; +} + +TreeItem TreeItem_PrevVisible(TreeCtrl *tree, TreeItem item) +{ + item = TreeItem_Prev(tree, item); + while (item != NULL) + { + if (TreeItem_ReallyVisible(tree, item)) + return item; + item = TreeItem_Prev(tree, item); + } + return NULL; +} + +void TreeItem_ToIndex(TreeCtrl *tree, TreeItem item_, int *index, int *indexVis) +{ + Item *item = (Item *) item_; + + if (tree->updateIndex) + { + if (tree->debug.enable && tree->debug.data) dbwin("%s: ", __FUNCTION__); + Tree_UpdateItemIndex(tree); + } + if (index != NULL) (*index) = item->index; + if (indexVis != NULL) (*indexVis) = item->indexVis; +} + +static int IndexFromList(int listIndex, int objc, Tcl_Obj **objv, CONST char **indexNames) +{ + Tcl_Obj *elemPtr; + int index; + + if (listIndex >= objc) + return -1; + elemPtr = objv[listIndex]; + if (Tcl_GetIndexFromObj(NULL, elemPtr, indexNames, NULL, 0, &index) != TCL_OK) + return -1; + return index; +} + +/* +%W index all +%W index "active MODIFIERS" +%W index "anchor MODIFIERS" +%W index "nearest x y MODIFIERS" +%W index "root MODIFIERS" +%W index "first MODIFIERS" +%W index "last MODIFIERS" +%W index "ID MODIFIERS" +MODIFIERS: + above + below + left + right + top + bottom + leftmost + rightmost + next + prev + parent + firstchild + lastchild + child N + nextsibling + prevsibling + sibling N + visible + +Examples: + %W index "first visible firstchild" + %W index "first visible firstchild visible" + %W index "nearest x y nextsibling visible" +*/ + +int TreeItem_FromObj(TreeCtrl *tree, Tcl_Obj *objPtr, TreeItem *itemPtr, int flags) +{ + Tcl_Interp *interp = tree->interp; + int objc; + int index, listIndex, id; + Tcl_HashEntry *hPtr; + Tcl_Obj **objv, *elemPtr; + Item *item = NULL; + static CONST char *indexName[] = { "active", "all", "anchor", "first", "last", + "nearest", "rnc", "root", (char *) NULL }; + enum indexEnum { + INDEX_ACTIVE, INDEX_ALL, INDEX_ANCHOR, INDEX_FIRST, INDEX_LAST, + INDEX_NEAREST, INDEX_RNC, INDEX_ROOT + } ; + static CONST char *modifiers[] = { "above", "below", "bottom", "child", + "firstchild", "lastchild", "left", "leftmost", "next", "nextsibling", + "parent", "prev", "prevsibling", "right", "rightmost", "sibling", "top", + "visible", (char *) NULL }; + enum modEnum { + MOD_ABOVE, MOD_BELOW, MOD_BOTTOM, MOD_CHILD, MOD_FIRSTCHILD, MOD_LASTCHILD, + MOD_LEFT, MOD_LEFTMOST, MOD_NEXT, MOD_NEXTSIBLING, MOD_PARENT, MOD_PREV, MOD_PREVSIBLING, + MOD_RIGHT, MOD_RIGHTMOST, MOD_SIBLING, MOD_TOP, MOD_VISIBLE + }; + static int modArgs[] = { 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1 }; + + if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK) + goto baditem; + if (objc == 0) + goto baditem; + + listIndex = 0; + elemPtr = objv[listIndex++]; + if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index) == TCL_OK) + { + switch ((enum indexEnum) index) + { + case INDEX_ACTIVE: + { + item = (Item *) tree->activeItem; + break; + } + case INDEX_ALL: + { + if (!(flags & IFO_ALLOK)) + { + Tcl_AppendResult(interp, + "can't specify \"all\" for this command", NULL); + return TCL_ERROR; + } + if (objc > 1) + goto baditem; + (*itemPtr) = ITEM_ALL; + return TCL_OK; + } + case INDEX_ANCHOR: + { + item = (Item *) tree->anchorItem; + break; + } + case INDEX_FIRST: + { + item = (Item *) tree->root; + if (IndexFromList(listIndex, objc, objv, modifiers) == MOD_VISIBLE) + { + if (!item->isVisible) + item = NULL; + else if (!tree->showRoot) + item = (Item *) TreeItem_NextVisible(tree, (TreeItem) item); + listIndex++; + } + break; + } + case INDEX_LAST: + { + item = (Item *) tree->root; + while (item->lastChild) + { + item = item->lastChild; + } + if (IndexFromList(listIndex, objc, objv, modifiers) == MOD_VISIBLE) + { + if (!((Item *) tree->root)->isVisible) + item = NULL; /* nothing is visible */ + else if (item == (Item *) tree->root && !tree->showRoot) + item = NULL; /* no item but root, not visible */ + else if (!TreeItem_ReallyVisible(tree, (TreeItem) item)) + item = (Item *) TreeItem_PrevVisible(tree, (TreeItem) item); + listIndex++; + } + break; + } + case INDEX_NEAREST: + { + int x, y; + + if (objc < 3) + goto baditem; + if (Tcl_GetIntFromObj(NULL, objv[listIndex++], &x) != TCL_OK) + goto baditem; + if (Tcl_GetIntFromObj(NULL, objv[listIndex++], &y) != TCL_OK) + goto baditem; + item = (Item *) Tree_ItemUnderPoint(tree, &x, &y, TRUE); + break; + } + case INDEX_RNC: + { + int row, col; + + if (objc < 3) + goto baditem; + if (Tcl_GetIntFromObj(NULL, objv[listIndex++], &row) != TCL_OK) + goto baditem; + if (Tcl_GetIntFromObj(NULL, objv[listIndex++], &col) != TCL_OK) + goto baditem; + item = (Item *) Tree_RNCToItem(tree, row, col); + break; + } + case INDEX_ROOT: + { + if ((flags & IFO_NOTROOT)) + { + Tcl_AppendResult(interp, + "can't specify \"root\" for this command", NULL); + return TCL_ERROR; + } + item = (Item *) tree->root; + break; + } + } + } + else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) + { + hPtr = Tcl_FindHashEntry(&tree->itemHash, (char *) id); + if (!hPtr) + { + if (!(flags & IFO_NULLOK)) + goto noitem; + (*itemPtr) = NULL; + return TCL_OK; + } + item = (Item *) Tcl_GetHashValue(hPtr); + } + else + { + goto baditem; + } + /* This means a valid specification was given, but there is no such item */ + if (item == NULL) + { + if (!(flags & IFO_NULLOK)) + goto noitem; + (*itemPtr) = (TreeItem) item; + return TCL_OK; + } + for (; listIndex < objc; /* nothing */) + { + int nextIsVisible = FALSE; + + elemPtr = objv[listIndex]; + if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0, &index) != TCL_OK) + return TCL_ERROR; + if (objc - listIndex < modArgs[index]) + goto baditem; + if (IndexFromList(listIndex + modArgs[index], objc, objv, modifiers) == MOD_VISIBLE) + nextIsVisible = TRUE; + switch ((enum modEnum) index) + { + case MOD_ABOVE: + { + item = (Item *) Tree_ItemAbove(tree, (TreeItem) item); + break; + } + case MOD_BELOW: + { + item = (Item *) Tree_ItemBelow(tree, (TreeItem) item); + break; + } + case MOD_BOTTOM: + { + item = (Item *) Tree_ItemBottom(tree, (TreeItem) item); + break; + } + case MOD_CHILD: + { + int n; + + if (Tcl_GetIntFromObj(interp, objv[listIndex + 1], &n) != TCL_OK) + return TCL_ERROR; + item = item->firstChild; + if (nextIsVisible) + { + while (item != NULL) + { + if (TreeItem_ReallyVisible(tree, (TreeItem) item)) + if (n-- <= 0) + break; + item = item->nextSibling; + } + } + else + { + while ((n-- > 0) && (item != NULL)) + item = item->nextSibling; + } + break; + } + case MOD_FIRSTCHILD: + { + item = item->firstChild; + if (nextIsVisible) + { + while ((item != NULL) && !TreeItem_ReallyVisible(tree, (TreeItem) item)) + item = item->nextSibling; + } + break; + } + case MOD_LASTCHILD: + { + item = item->lastChild; + if (nextIsVisible) + { + while ((item != NULL) && !TreeItem_ReallyVisible(tree, (TreeItem) item)) + item = item->prevSibling; + } + break; + } + case MOD_LEFT: + { + item = (Item *) Tree_ItemLeft(tree, (TreeItem) item); + break; + } + case MOD_LEFTMOST: + { + item = (Item *) Tree_ItemLeftMost(tree, (TreeItem) item); + break; + } + case MOD_NEXT: + { + if (nextIsVisible) + item = (Item *) TreeItem_NextVisible(tree, (TreeItem) item); + else + item = (Item *) TreeItem_Next(tree, (TreeItem) item); + break; + } + case MOD_NEXTSIBLING: + { + item = item->nextSibling; + if (nextIsVisible) + { + while ((item != NULL) && !TreeItem_ReallyVisible(tree, (TreeItem) item)) { + item = item->nextSibling; + } + } + break; + } + case MOD_PARENT: + { + item = item->parent; + break; + } + case MOD_PREV: + { + if (nextIsVisible) + item = (Item *) TreeItem_PrevVisible(tree, (TreeItem) item); + else + item = (Item *) TreeItem_Prev(tree, (TreeItem) item); + break; + } + case MOD_PREVSIBLING: + { + item = item->prevSibling; + if (nextIsVisible) + { + while ((item != NULL) && !TreeItem_ReallyVisible(tree, (TreeItem) item)) { + item = item->prevSibling; + } + } + break; + } + case MOD_RIGHT: + { + item = (Item *) Tree_ItemRight(tree, (TreeItem) item); + break; + } + case MOD_RIGHTMOST: + { + item = (Item *) Tree_ItemRightMost(tree, (TreeItem) item); + break; + } + case MOD_SIBLING: + { + int n; + + if (Tcl_GetIntFromObj(interp, objv[listIndex + 1], &n) != TCL_OK) + return TCL_ERROR; + item = item->parent; + if (item == NULL) + break; + item = item->firstChild; + if (nextIsVisible) + { + while (item != NULL) + { + if (TreeItem_ReallyVisible(tree, (TreeItem) item)) + if (n-- <= 0) + break; + item = item->nextSibling; + } + } + else + { + while ((n-- > 0) && (item != NULL)) + item = item->nextSibling; + } + break; + } + case MOD_TOP: + { + item = (Item *) Tree_ItemTop(tree, (TreeItem) item); + break; + } + case MOD_VISIBLE: + { + break; + } + } + if (item == NULL) + { + if (!(flags & IFO_NULLOK)) + goto noitem; + (*itemPtr) = (TreeItem) item; + return TCL_OK; + } + listIndex += modArgs[index]; + if (nextIsVisible) + listIndex++; + } + (*itemPtr) = (TreeItem) item; + return TCL_OK; +baditem: + Tcl_AppendResult(interp, "bad item description \"", Tcl_GetString(objPtr), + "\"", NULL); + return TCL_ERROR; +noitem: + Tcl_AppendResult(interp, "item \"", Tcl_GetString(objPtr), + "\" doesn't exist", NULL); + return TCL_ERROR; +} + +static void Item_Toggle(TreeCtrl *tree, Item *item, int stateOff, int stateOn) +{ + int mask; + + mask = TreeItem_ChangeState(tree, (TreeItem) item, stateOff, stateOn); + + if (ISROOT(item) && !tree->showRoot) + return; + +#if 0 + /* Don't affect display if we weren't visible */ + if (!TreeItem_ReallyVisible(tree, (TreeItem) item)) + return; + + /* Invalidate display info for this item, so it is redrawn later. */ + Tree_InvalidateItemDInfo(tree, (TreeItem) item, NULL); +#endif + + if (item->numChildren > 0) + { + /* indexVis needs updating for all items after this one, if we + * have any visible children */ + tree->updateIndex = 1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + /* Hiding/showing children may change the width of any column */ + Tree_InvalidateColumnWidth(tree, -1); + } + + /* If this item was previously onscreen, this call is repetitive. */ + Tree_EventuallyRedraw(tree); +} + +void TreeItem_OpenClose(TreeCtrl *tree, TreeItem item_, int mode, int recurse) +{ + Item *item = (Item *) item_; + Item *child; + int stateOff = 0, stateOn = 0; + + if (mode == -1) + { + if (item->state & STATE_OPEN) + stateOff = STATE_OPEN; + else + stateOn = STATE_OPEN; + } + else if (!mode && (item->state & STATE_OPEN)) + stateOff = STATE_OPEN; + else if (mode && !(item->state & STATE_OPEN)) + stateOn = STATE_OPEN; + + if (stateOff != stateOn) + { + TreeNotify_OpenClose(tree, item_, stateOn, TRUE); + Item_Toggle(tree, item, stateOff, stateOn); + TreeNotify_OpenClose(tree, item_, stateOn, FALSE); + } + if (recurse) + { + for (child = item->firstChild; child != NULL; child = child->nextSibling) + TreeItem_OpenClose(tree, (TreeItem) child, mode, recurse); + } +} + +void TreeItem_Delete(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + + if (TreeItem_ReallyVisible(tree, item_)) + Tree_InvalidateColumnWidth(tree, -1); + + while (self->numChildren > 0) + TreeItem_Delete(tree, (TreeItem) self->firstChild); + + TreeItem_RemoveFromParent(tree, item_); + Tree_RemoveItem(tree, item_); + TreeItem_FreeResources(tree, item_); + if (tree->activeItem == item_) + { + tree->activeItem = tree->root; + TreeItem_ChangeState(tree, tree->activeItem, 0, STATE_ACTIVE); + } + if (tree->anchorItem == item_) + tree->anchorItem = tree->root; + if (tree->debug.enable && tree->debug.data) + Tree_Debug(tree); +} + +void TreeItem_UpdateDepth(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + Item *child; + + if (ISROOT(self)) + return; + if (self->parent != NULL) + self->depth = self->parent->depth + 1; + else + self->depth = 0; + child = self->firstChild; + while (child != NULL) + { + TreeItem_UpdateDepth(tree, (TreeItem) child); + child = child->nextSibling; + } +} + +void TreeItem_AddToParent(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + Item *last; + + /* If this is the new last child, redraw the lines of the previous + * sibling and all of its descendants so the line from the previous + * sibling reaches this item */ + if ((self->prevSibling != NULL) && + (self->nextSibling == NULL) && + tree->showLines) + { + last = self->prevSibling; + while (last->lastChild != NULL) + last = last->lastChild; + Tree_InvalidateItemDInfo(tree, (TreeItem) self->prevSibling, + (TreeItem) last); + } + + tree->updateIndex = 1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + /* Tree_UpdateItemIndex() also recalcs depth, but in one of my demos + * I retrieve item depth during list creation. Since Tree_UpdateItemIndex() + * is slow I will keep depth up-to-date here. */ + TreeItem_UpdateDepth(tree, item_); + + Tree_InvalidateColumnWidth(tree, -1); + if (tree->debug.enable && tree->debug.data) + Tree_Debug(tree); +} + +static void RemoveFromParentAux(TreeCtrl *tree, Item *self, int *index) +{ + Item *child; + + /* Invalidate display info. Don't free it because we may just be + * moving the item to a new parent. FIXME: if it is being moved, + * it might not actually need to be redrawn (just copied) */ + if (self->dInfo != NULL) + Tree_InvalidateItemDInfo(tree, (TreeItem) self, NULL); + + if (self->parent != NULL) + self->depth = self->parent->depth + 1; + else + self->depth = 0; + self->index = (*index)++; + child = self->firstChild; + while (child != NULL) + { + RemoveFromParentAux(tree, child, index); + child = child->nextSibling; + } +} + +void TreeItem_RemoveFromParent(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + Item *parent = self->parent; + Item *last; + int index = 0; + + if (parent == NULL) + return; + + /* If this is the last child, redraw the lines of the previous + * sibling and all of its descendants because the line from + * the previous sibling to us is now gone */ + if ((self->prevSibling != NULL) && + (self->nextSibling == NULL) && + tree->showLines) + { + last = self->prevSibling; + while (last->lastChild != NULL) + last = last->lastChild; + Tree_InvalidateItemDInfo(tree, (TreeItem) self->prevSibling, + (TreeItem) last); + } + + tree->updateIndex = 1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + if (self->prevSibling) + self->prevSibling->nextSibling = self->nextSibling; + if (self->nextSibling) + self->nextSibling->prevSibling = self->prevSibling; + if (parent->firstChild == self) + { + parent->firstChild = self->nextSibling; + if (!parent->firstChild) + parent->lastChild = NULL; + } + if (parent->lastChild == self) + parent->lastChild = self->prevSibling; + self->prevSibling = self->nextSibling = NULL; + self->parent = NULL; + parent->numChildren--; + + /* Update depth, index and indexVis. Index is needed for some operations + * that use a range of items, such as delete. */ + RemoveFromParentAux(tree, self, &index); +} + +void TreeItem_RemoveColumn(TreeCtrl *tree, TreeItem item_, TreeItemColumn column_) +{ + Item *self = (Item *) item_; + Column *column, *prev; + + column = self->columns; + prev = NULL; + while (column != NULL) + { + if (column == (Column *) column_) + { + if (prev != NULL) + prev->next = column->next; + else + self->columns = column->next; + Column_FreeResources(tree, column); + break; + } + prev = column; + column = column->next; + } + if (column == NULL) + panic("TreeItem_RemoveColumn: can't find column"); +} + +void TreeItem_FreeResources(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + Column *column; + + column = self->columns; + while (column != NULL) + column = Column_FreeResources(tree, column); + if (self->dInfo != NULL) + Tree_FreeItemDInfo(tree, item_, NULL); + if (self->rInfo != NULL) + Tree_FreeItemRInfo(tree, item_); + WFREE(self, Item); +} + +int TreeItem_NeededHeight(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + Column *column; + + self->neededHeight = 0; + for (column = self->columns; column != NULL; column = column->next) + { + if (column->style != NULL) + { + self->neededHeight = MAX(self->neededHeight, + TreeStyle_NeededHeight(tree, column->style, self->state)); + } + } + return self->neededHeight; +} + +int TreeItem_UseHeight(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + Column *column = item->columns; + TreeColumn treeColumn = tree->columns; + StyleDrawArgs drawArgs; + int height = 0; + + drawArgs.tree = tree; + drawArgs.state = item->state; + + while (column != NULL) + { + if (TreeColumn_Visible(treeColumn) && (column->style != NULL)) + { + drawArgs.style = column->style; + if (TreeColumn_FixedWidth(treeColumn) != -1) + { + drawArgs.width = TreeColumn_UseWidth(treeColumn); + if (TreeColumn_Index(treeColumn) == tree->columnTree) + drawArgs.width -= TreeItem_Indent(tree, item_); + } + else + drawArgs.width = -1; + height = MAX(height, TreeStyle_UseHeight(&drawArgs)); + } + treeColumn = TreeColumn_Next(treeColumn); + column = column->next; + } + + return height; +} + +int TreeItem_Height(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + int buttonHeight = 0; + int useHeight; + + if (!self->isVisible || (ISROOT(self) && !tree->showRoot)) + return 0; + + /* Update column + style + element sizes */ + useHeight = TreeItem_UseHeight(tree, item_); + + /* Can't have less height than our button */ + if (tree->showButtons && self->hasButton && (!ISROOT(self) || tree->showRootButton)) + { + buttonHeight = (self->state & STATE_OPEN) ? + tree->openButtonHeight : tree->closedButtonHeight; + } + + /* User specified a fixed height for this item */ + if (self->fixedHeight > 0) + return MAX(self->fixedHeight, buttonHeight); + + /* Fixed height of all items */ + if (tree->itemHeight > 0) + return MAX(tree->itemHeight, buttonHeight); + + /* No fixed height specified */ + return MAX(useHeight, buttonHeight); +} + +void TreeItem_InvalidateHeight(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + + if (self->neededHeight < 0) + return; + self->neededHeight = -1; +} + +static Column *Item_CreateColumn(TreeCtrl *tree, Item *self, int columnIndex, int *isNew) +{ + Column *column; + int i; + + if (isNew != NULL) (*isNew) = FALSE; + column = self->columns; + if (column == NULL) + { + column = Column_Alloc(); + column->neededWidth = column->neededHeight = -1; + self->columns = column; + if (isNew != NULL) (*isNew) = TRUE; + } + for (i = 0; i < columnIndex; i++) + { + if (column->next == NULL) + { + column->next = Column_Alloc(); + column->next->neededWidth = column->next->neededHeight = -1; + if (isNew != NULL) (*isNew) = TRUE; + } + column = column->next; + } + + Tree_CreateColumn(tree, columnIndex, NULL); + + return column; +} + +static Column *Item_FindColumn(TreeCtrl *tree, Item *self, int columnIndex) +{ + Column *column; + int i = 0; + + column = self->columns; + if (!column) + return NULL; + while (column != NULL && i < columnIndex) + { + column = column->next; + i++; + } + return column; +} + +static int Item_FindColumnFromObj(TreeCtrl *tree, Item *item, Tcl_Obj *obj, + Column **column, int *indexPtr) +{ + int columnIndex; + + if (Tcl_GetIntFromObj(NULL, obj, &columnIndex) == TCL_OK) + { + if (columnIndex < 0) + { + FormatResult(tree->interp, "bad column index \"%d\": must be > 0", + columnIndex); + return TCL_ERROR; + } + } + else + { + TreeColumn treeColumn; + + if (Tree_FindColumnByTag(tree, obj, &treeColumn, CFO_NOT_TAIL) != TCL_OK) + return TCL_ERROR; + columnIndex = TreeColumn_Index(treeColumn); + } + (*column) = Item_FindColumn(tree, item, columnIndex); + if ((*column) == NULL) + { + FormatResult(tree->interp, "item %d doesn't have column %d", + item->id, columnIndex); + return TCL_ERROR; + } + if (indexPtr != NULL) + (*indexPtr) = columnIndex; + return TCL_OK; +} + +TreeItemColumn TreeItem_FindColumn(TreeCtrl *tree, TreeItem item, int columnIndex) +{ + return (TreeItemColumn) Item_FindColumn(tree, (Item *) item, columnIndex); +} + +int TreeItem_ColumnFromObj(TreeCtrl *tree, TreeItem item, Tcl_Obj *obj, TreeItemColumn *columnPtr, int *indexPtr) +{ + return Item_FindColumnFromObj(tree, (Item *) item, obj, (Column **) columnPtr, indexPtr); +} + +static int Item_CreateColumnFromObj(TreeCtrl *tree, Item *item, Tcl_Obj *obj, Column **column, int *indexPtr) +{ + int columnIndex; + + if (Tcl_GetIntFromObj(NULL, obj, &columnIndex) == TCL_OK) + { + if (columnIndex < 0 || columnIndex >= 20) + { + FormatResult(tree->interp, + "bad column index \"%d\": must be from 0 to 19", + columnIndex); + return TCL_ERROR; + } + } + else + { + TreeColumn treeColumn; + + if (Tree_FindColumnByTag(tree, obj, &treeColumn, CFO_NOT_TAIL) != TCL_OK) + return TCL_ERROR; + columnIndex = TreeColumn_Index(treeColumn); + } + (*column) = Item_CreateColumn(tree, item, columnIndex, NULL); + if (indexPtr != NULL) + (*indexPtr) = columnIndex; + return TCL_OK; +} + +int TreeItem_Indent(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + int indent; + + if (ISROOT(self)) + return (tree->showRoot && tree->showButtons && tree->showRootButton) ? tree->useIndent : 0; + + if (tree->updateIndex) + { + if (tree->debug.enable && tree->debug.data) dbwin("%s: ", __FUNCTION__); + Tree_UpdateItemIndex(tree); + } + + indent = tree->useIndent * self->depth; + if (tree->showRoot || tree->showButtons || tree->showLines) + indent += tree->useIndent; + if (tree->showRoot && tree->showButtons && tree->showRootButton) + indent += tree->useIndent; + return indent; +} + +static void ItemDrawBackground(TreeCtrl *tree, TreeColumn treeColumn, + Item *item, Column *column, Drawable drawable, int x, int y, int width, + int height, int index) +{ + GC gc = None; + + gc = TreeColumn_BackgroundGC(treeColumn, index); + if (gc == None) + gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC); + XFillRectangle(tree->display, drawable, gc, x, y, width, height); +} + +void TreeItem_Draw(TreeCtrl *tree, TreeItem item_, int x, int y, + int width, int height, Drawable drawable, int minX, int maxX, int index) +{ + Item *self = (Item *) item_; + int indent, columnWidth, totalWidth; + Column *column; + StyleDrawArgs drawArgs; + TreeColumn treeColumn; + + drawArgs.tree = tree; + drawArgs.drawable = drawable; + drawArgs.state = self->state; + + totalWidth = 0; + treeColumn = tree->columns; + column = self->columns; + while (treeColumn != NULL) + { + if (!TreeColumn_Visible(treeColumn)) + columnWidth = 0; + else if (tree->columnCountVis == 1) + columnWidth = width; + else + columnWidth = TreeColumn_UseWidth(treeColumn); + if (columnWidth > 0) + { + if (TreeColumn_Index(treeColumn) == tree->columnTree) + { + indent = TreeItem_Indent(tree, item_); +#if 0 + /* This means the tree lines/buttons don't share the item background color */ + if ((x + totalWidth < maxX) && + (x + totalWidth + indent > minX)) + { + GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC); + XFillRectangle(tree->display, drawable, gc, + x + totalWidth, y, indent, height); + } +#endif + } + else + indent = 0; + if ((x /* + indent */ + totalWidth < maxX) && + (x + totalWidth + columnWidth > minX)) + { + ItemDrawBackground(tree, treeColumn, self, column, drawable, + x + totalWidth /* + indent*/ , y, + columnWidth /* - indent */, height, + index); + if ((column != NULL) && (column->style != NULL)) + { + drawArgs.style = column->style; + drawArgs.x = x + indent + totalWidth; + drawArgs.y = y; + drawArgs.width = columnWidth - indent; + drawArgs.height = height; + drawArgs.justify = TreeColumn_Justify(treeColumn); + TreeStyle_Draw(&drawArgs); + } + } + totalWidth += columnWidth; + } + treeColumn = TreeColumn_Next(treeColumn); + if (column != NULL) + column = column->next; + } +} + +void TreeItem_DrawLines(TreeCtrl *tree, TreeItem item_, int x, int y, int width, int height, Drawable drawable) +{ + Item *self = (Item *) item_; + Item *parent; + int indent, left, lineLeft, lineTop; + int hasPrev = FALSE, hasNext = FALSE; + int i, vert = 0; + + indent = TreeItem_Indent(tree, item_); + + /* Left edge of button/line area */ + left = x + tree->columnTreeLeft + indent - tree->useIndent; + + /* Left edge of vertical line */ + lineLeft = left + (tree->useIndent - tree->lineThickness) / 2; + + /* Top edge of horizontal line */ + lineTop = y + (height - tree->lineThickness) / 2; + + /* Vertical line to parent and/or previous/next sibling */ + /* The root may not actually be visible if showRoot is FALSE */ + if ((self->parent != NULL) && + TreeItem_ReallyVisible(tree, (TreeItem) self->parent)) + hasPrev = TRUE; + else if ((self->prevSibling != NULL) && + TreeItem_ReallyVisible(tree, (TreeItem) self->prevSibling)) + hasPrev = TRUE; + if ((self->nextSibling != NULL) && + TreeItem_ReallyVisible(tree, (TreeItem) self->nextSibling)) + hasNext = TRUE; + if (hasPrev || hasNext) + { + int top = y, bottom = y + height; + + if (!hasPrev) + top = lineTop; + if (!hasNext) + bottom = lineTop + tree->lineThickness; + + if (tree->lineStyle == LINE_STYLE_DOT) + { + for (i = 0; i < tree->lineThickness; i++) + VDotLine(tree, drawable, tree->lineGC, + lineLeft + i, + top, + bottom); + } + else + XFillRectangle(tree->display, drawable, tree->lineGC, + lineLeft, + top, + tree->lineThickness, + bottom - top); + + /* Don't overlap horizontal line */ + vert = tree->lineThickness; + } + + /* Horizontal line to self */ + if (!ISROOT(self) || (tree->showRoot && tree->showButtons && tree->showRootButton)) + { + if (tree->lineStyle == LINE_STYLE_DOT) + { + for (i = 0; i < tree->lineThickness; i++) + HDotLine(tree, drawable, tree->lineGC, + lineLeft + vert, + lineTop + i, + x + tree->columnTreeLeft + indent); + } + else + XFillRectangle(tree->display, drawable, tree->lineGC, + lineLeft + vert, + lineTop, + left + tree->useIndent - (lineLeft + vert), + tree->lineThickness); + } + + /* Vertical lines from ancestors to their next siblings */ + for (parent = self->parent; + parent != NULL; + parent = parent->parent) + { + lineLeft -= tree->useIndent; + if (parent->nextSibling != NULL && + TreeItem_ReallyVisible(tree, (TreeItem) parent->nextSibling)) + { + if (tree->lineStyle == LINE_STYLE_DOT) + { + for (i = 0; i < tree->lineThickness; i++) + VDotLine(tree, drawable, tree->lineGC, + lineLeft + i, + y, + y + height); + } + else + XFillRectangle(tree->display, drawable, tree->lineGC, + lineLeft, + y, + tree->lineThickness, + height); + } + } +} + +void TreeItem_DrawButton(TreeCtrl *tree, TreeItem item_, int x, int y, int width, int height, Drawable drawable) +{ + Item *self = (Item *) item_; + int indent, left, lineLeft, lineTop; + Tk_Image image = NULL; + Pixmap bitmap = None; + int imgW, imgH; + int buttonLeft, buttonTop, w1; + + if (!self->hasButton) + return; + if (ISROOT(self) && !tree->showRootButton) + return; + + indent = TreeItem_Indent(tree, item_); + + /* Left edge of button/line area */ + left = x + tree->columnTreeLeft + indent - tree->useIndent; + + if (self->state & STATE_OPEN) + { + imgW = tree->openButtonWidth; + imgH = tree->openButtonHeight; + if (tree->openButtonImage != NULL) + image = tree->openButtonImage; + else if (tree->openButtonBitmap != None) + bitmap = tree->openButtonBitmap; + } + else + { + imgW = tree->closedButtonWidth; + imgH = tree->closedButtonHeight; + if (tree->closedButtonImage != NULL) + image = tree->closedButtonImage; + else if (tree->closedButtonBitmap != None) + bitmap = tree->closedButtonBitmap; + } + if (image != NULL) + { + Tk_RedrawImage(image, 0, 0, imgW, imgH, drawable, + left + (tree->useIndent - imgW) / 2, + y + (height - imgH) / 2); + return; + } + if (bitmap != None) + { + GC gc = (self->state & STATE_OPEN) ? tree->buttonOpenGC : tree->buttonClosedGC; + int bx = left + (tree->useIndent - imgW) / 2; + int by = y + (height - imgH) / 2; + + XSetClipOrigin(tree->display, gc, bx, by); + XCopyPlane(tree->display, bitmap, drawable, gc, + 0, 0, imgW, imgH, + bx, by, 1); + XSetClipOrigin(tree->display, gc, 0, 0); + return; + } + + w1 = tree->buttonThickness / 2; + + /* Left edge of vertical line */ + /* Make sure this matches TreeItem_DrawLines() */ + lineLeft = left + (tree->useIndent - tree->buttonThickness) / 2; + + /* Top edge of horizontal line */ + /* Make sure this matches TreeItem_DrawLines() */ + lineTop = y + (height - tree->buttonThickness) / 2; + + buttonLeft = left + (tree->useIndent - tree->buttonSize) / 2; + buttonTop = y + (height - tree->buttonSize) / 2; + + /* Erase button background */ + XFillRectangle(tree->display, drawable, + Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC), + buttonLeft + tree->buttonThickness, + buttonTop + tree->buttonThickness, + tree->buttonSize - tree->buttonThickness, + tree->buttonSize - tree->buttonThickness); + + /* Draw button outline */ + XDrawRectangle(tree->display, drawable, tree->buttonGC, + buttonLeft + w1, + buttonTop + w1, + tree->buttonSize - tree->buttonThickness, + tree->buttonSize - tree->buttonThickness); + + /* Horizontal '-' */ + XFillRectangle(tree->display, drawable, tree->buttonGC, + buttonLeft + tree->buttonThickness * 2, + lineTop, + tree->buttonSize - tree->buttonThickness * 4, + tree->buttonThickness); + + if (!(self->state & STATE_OPEN)) + { + /* Finish '+' */ + XFillRectangle(tree->display, drawable, tree->buttonGC, + lineLeft, + buttonTop + tree->buttonThickness * 2, + tree->buttonThickness, + tree->buttonSize - tree->buttonThickness * 4); + } +} + +int TreeItem_ReallyVisible(TreeCtrl *tree, TreeItem item_) +{ + Item *self = (Item *) item_; + + if (!tree->updateIndex) + return self->indexVis != -1; + + if (!self->isVisible) + return 0; + if (self->parent == NULL) + return ISROOT(self) ? tree->showRoot : 0; + if (ISROOT(self->parent)) + { + if (!self->parent->isVisible) + return 0; + if (!tree->showRoot) + return 1; + if (!(self->parent->state & STATE_OPEN)) + return 0; + } + if (!self->parent->isVisible || !(self->parent->state & STATE_OPEN)) + return 0; + return TreeItem_ReallyVisible(tree, (TreeItem) self->parent); +} + +TreeItem TreeItem_RootAncestor(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + + while (item->parent != NULL) + item = item->parent; + return (TreeItem) item; +} + +int TreeItem_IsAncestor(TreeCtrl *tree, TreeItem item1, TreeItem item2) +{ + if (item1 == item2) + return 0; + while (item2 && item2 != item1) + item2 = (TreeItem) ((Item *) item2)->parent; + return item2 != NULL; +} + +Tcl_Obj *TreeItem_ToObj(TreeCtrl *tree, TreeItem item_) +{ + return Tcl_NewIntObj(((Item *) item_)->id); +} + +static int ItemElementCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "actual", "cget", "configure", (char *) NULL }; + enum { COMMAND_ACTUAL, COMMAND_CGET, COMMAND_CONFIGURE }; + int index; + int columnIndex; + Column *column; + Item *item; + + if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, + &index) != TCL_OK) + return TCL_ERROR; + + if (TreeItem_FromObj(tree, objv[4], (TreeItem *) &item, 0) != TCL_OK) + return TCL_ERROR; + + if (Item_FindColumnFromObj(tree, item, objv[5], &column, &columnIndex) != TCL_OK) + return TCL_ERROR; + + if (column->style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + item->id, columnIndex); + return TCL_ERROR; + } + + switch (index) + { + /* T item element actual I C E option */ + case COMMAND_ACTUAL: + { + if (objc != 8) + { + Tcl_WrongNumArgs(tree->interp, 4, objv, + "item column element option"); + return TCL_ERROR; + } + return TreeStyle_ElementActual(tree, column->style, item->state, + objv[6], objv[7]); + } + + /* T item element cget I C E option */ + case COMMAND_CGET: + { + if (objc != 8) + { + Tcl_WrongNumArgs(tree->interp, 4, objv, + "item column element option"); + return TCL_ERROR; + } + return TreeStyle_ElementCget(tree, column->style, objv[6], objv[7]); + } + + /* T item element configure I C E ... */ + case COMMAND_CONFIGURE: + { + int result; + result = TreeStyle_ElementConfigure(tree, column->style, objv[6], + objc - 7, (Tcl_Obj **) objv + 7); + if (objc - 7 > 1) + { + column->neededWidth = column->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItem_InvalidateHeight(tree, (TreeItem) item); + Tree_FreeItemDInfo(tree, (TreeItem) item, NULL); + } + return result; + } + } + + return TCL_OK; +} + +static int ItemStyleCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "elements", "map", "set", (char *) NULL }; + enum { COMMAND_ELEMENTS, COMMAND_MAP, COMMAND_SET }; + int index; + Item *item; + + if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + if (TreeItem_FromObj(tree, objv[4], (TreeItem *) &item, 0) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T item style elements I C */ + case COMMAND_ELEMENTS: + { + Column *column; + int columnIndex; + + if (objc != 6) + { + Tcl_WrongNumArgs(interp, 4, objv, "item column"); + return TCL_ERROR; + } + if (Item_FindColumnFromObj(tree, item, objv[5], &column, &columnIndex) != TCL_OK) + { + return TCL_ERROR; + } + if (column->style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + item->id, columnIndex); + return TCL_ERROR; + } + TreeStyle_ListElements(tree, column->style); + break; + } + + /* T item style map I C S map */ + case COMMAND_MAP: + { + TreeStyle style; + Column *column; + int columnIndex; + int objcM; + Tcl_Obj **objvM; + + if (objc != 8) + { + Tcl_WrongNumArgs(interp, 4, objv, "item column style map"); + return TCL_ERROR; + } + if (Item_CreateColumnFromObj(tree, item, objv[5], &column, &columnIndex) != TCL_OK) + return TCL_ERROR; + if (TreeStyle_FromObj(tree, objv[6], &style) != TCL_OK) + return TCL_ERROR; + if (column->style != NULL) + { + if (Tcl_ListObjGetElements(interp, objv[7], &objcM, &objvM) != TCL_OK) + return TCL_ERROR; + if (objcM & 1) + { + FormatResult(interp, "list must contain even number of elements"); + return TCL_ERROR; + } + if (TreeStyle_Remap(tree, column->style, style, objcM, objvM) != TCL_OK) + return TCL_ERROR; + } + else + column->style = TreeStyle_NewInstance(tree, style); + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItem_InvalidateHeight(tree, (TreeItem) item); + Tree_FreeItemDInfo(tree, (TreeItem) item, NULL); + break; + } + + /* T item style set I ?C? ?S? ?C S ...?*/ + case COMMAND_SET: + { + TreeStyle style; + Column *column; + int i, columnIndex, length; + + if (objc < 5) + { + Tcl_WrongNumArgs(interp, 4, objv, "item ?column? ?style? ?column style ...?"); + return TCL_ERROR; + } + if (objc == 5) + { + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + column = item->columns; + while (column != NULL) + { + if (column->style != NULL) + Tcl_ListObjAppendElement(interp, listObj, + TreeStyle_ToObj(column->style)); + else + Tcl_ListObjAppendElement(interp, listObj, + Tcl_NewObj()); + column = column->next; + } + Tcl_SetObjResult(interp, listObj); + break; + } + if (objc == 6) + { + if (Item_FindColumnFromObj(tree, item, objv[5], &column, NULL) != TCL_OK) + return TCL_ERROR; + if (column->style != NULL) + Tcl_SetObjResult(interp, TreeStyle_ToObj(column->style)); + break; + } + if ((objc - 5) & 1) + return TCL_ERROR; + for (i = 5; i < objc; i += 2) + { + if (Item_CreateColumnFromObj(tree, item, objv[i], &column, &columnIndex) != TCL_OK) + return TCL_ERROR; + (void) Tcl_GetStringFromObj(objv[i + 1], &length); + if (length == 0) + { + if (column->style == NULL) + continue; + TreeItemColumn_ForgetStyle(tree, (TreeItemColumn) column); + } + else + { + if (TreeStyle_FromObj(tree, objv[i + 1], &style) != TCL_OK) + return TCL_ERROR; + TreeItemColumn_ForgetStyle(tree, (TreeItemColumn) column); + column->style = TreeStyle_NewInstance(tree, style); + } + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItem_InvalidateHeight(tree, (TreeItem) item); + Tree_FreeItemDInfo(tree, (TreeItem) item, NULL); + } + break; + } + } + + return TCL_OK; +} + +#if 0 +T item sort I + -first I (default firstchild) + -last I (default lastchild) + -command $cmd + -dictionary + -integer + -real + -increasing + -decreasing + -column C (default 0) + -element E (default first "text") +#endif + +/* one per column per SortItem */ +struct SortItem1 +{ + long longValue; + double doubleValue; + char *string; +}; + +/* one per Item */ +struct SortItem +{ + Item *item; + struct SortItem1 *item1; + Tcl_Obj *obj; /* TreeItem_ToObj() */ +}; + +struct +{ + TreeCtrl *tree; + struct SortItem *items; + struct SortItem1 *item1s; /* SortItem.item1 points in here */ + int (*proc[20])(struct SortItem *, struct SortItem *, int); /* one per column */ + int sortBy[20]; /* one per column */ + int column[20]; /* one per column */ + int order[20]; /* one per column */ + Tcl_Obj *command[20]; /* one per column */ + int count; /* max number of columns to compare */ + int result; +} sortData; + +/* from Tcl 8.4.0 */ +static int DictionaryCompare(char *left, char *right) +{ + Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower; + int diff, zeros; + int secondaryDiff = 0; + + while (1) + { + if (isdigit(UCHAR(*right)) /* INTL: digit */ + && isdigit(UCHAR(*left))) + { /* INTL: digit */ + /* + * 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++; + 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. Covert 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; +} + +static int CompareAscii(struct SortItem *a, struct SortItem *b, int n) +{ + return strcmp(a->item1[n].string, b->item1[n].string); +} + +static int CompareDict(struct SortItem *a, struct SortItem *b, int n) +{ + return DictionaryCompare(a->item1[n].string, b->item1[n].string); +} + +static int CompareDouble(struct SortItem *a, struct SortItem *b, int n) +{ + return (a->item1[n].doubleValue < b->item1[n].doubleValue) ? -1 : + ((a->item1[n].doubleValue == b->item1[n].doubleValue) ? 0 : 1); +} + +static int CompareLong(struct SortItem *a, struct SortItem *b, int n) +{ + return (a->item1[n].longValue < b->item1[n].longValue) ? -1 : + ((a->item1[n].longValue == b->item1[n].longValue) ? 0 : 1); +} + +static int CompareCmd(struct SortItem *a, struct SortItem *b, int n) +{ + Tcl_Interp *interp = sortData.tree->interp; + Tcl_Obj **objv, *paramObjv[2]; + int objc, v; + + paramObjv[0] = a->obj; + paramObjv[1] = b->obj; + + Tcl_ListObjLength(interp, sortData.command[n], &objc); + Tcl_ListObjReplace(interp, sortData.command[n], objc - 2, + 2, 2, paramObjv); + Tcl_ListObjGetElements(interp, sortData.command[n], + &objc, &objv); + + sortData.result = Tcl_EvalObjv(interp, objc, objv, 0); + + if (sortData.result != TCL_OK) + { + Tcl_AddErrorInfo(interp, "\n (evaluating item sort -command)"); + return 0; + } + + sortData.result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &v); + if (sortData.result != TCL_OK) + { + Tcl_ResetResult(interp); + Tcl_AppendToObj(Tcl_GetObjResult(interp), + "-command returned non-numeric result", -1); + return 0; + } + + return v; +} + +static int CompareProc(struct SortItem *a, struct SortItem *b) +{ + int i, v; + + for (i = 0; i < sortData.count; i++) + { + v = (*sortData.proc[i])(a, b, i); + + /* -command returned error */ + if (sortData.result != TCL_OK) + return 0; + + if (v != 0) + { + if (i && (sortData.order[i] != sortData.order[0])) + v *= -1; + return v; + } + } + return 0; +} + +/* BEGIN custom quicksort() */ + +static int find_pivot(struct SortItem *left, struct SortItem *right, struct SortItem *pivot) +{ + struct SortItem *a, *b, *c, *p; + int v; + + a = left; + b = (left + (right - left) / 2); + c = right; + + v = CompareProc(a, b); + if (sortData.result != TCL_OK) + return 0; + if (v < 0) { p = a; a = b; b = p; } + + v = CompareProc(a, c); + if (sortData.result != TCL_OK) + return 0; + if (v < 0) { p = a; a = c; c = p; } + + v = CompareProc(b, c); + if (sortData.result != TCL_OK) + return 0; + if (v < 0) { p = b; b = c; c = p; } + + v = CompareProc(a, b); + if (sortData.result != TCL_OK) + return 0; + if (v < 0) + { + (*pivot) = *b; + return 1; + } + + v = CompareProc(b, c); + if (sortData.result != TCL_OK) + return 0; + if (v < 0) + { + (*pivot) = *c; + return 1; + } + + for (p = left + 1; p <= right; p++) + { + int v = CompareProc(p, left); + if (sortData.result != TCL_OK) + return 0; + if (v != 0) + { + (*pivot) = (v < 0) ? *left : *p; + return 1; + } + } + return 0; +} + +static struct SortItem *partition(struct SortItem *left, struct SortItem *right, struct SortItem *pivot) +{ + int v; + + while (left <= right) + { + while (1) + { + v = CompareProc(left, pivot); + if (sortData.result != TCL_OK) + return NULL; + if (v >= 0) + break; + left++; + } + while (1) + { + v = CompareProc(right, pivot); + if (sortData.result != TCL_OK) + return NULL; + if (v < 0) + break; + right--; + } + if (left < right) + { + struct SortItem tmp = *left; + *left = *right; + *right = tmp; + left++; + right--; + } + } + return left; +} + +static void quicksort(struct SortItem *left, struct SortItem *right) +{ + struct SortItem *p, pivot; + + if (sortData.result != TCL_OK) + return; + + if (find_pivot(left, right, &pivot) == 1) + { + p = partition(left, right, &pivot); + if (sortData.result != TCL_OK) + return; + + quicksort(left, p - 1); + if (sortData.result != TCL_OK) + return; + + quicksort(p, right); + } +} + +/* END custom quicksort() */ + +int ItemSortCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + Item *item, *first, *last, *walk, *lastChild; + Column *column; + int i, count, index, indexF = 0, indexL = 0; + int sawColumn = FALSE, sawCmd = FALSE; + static int (*sortProc[5])(struct SortItem *, struct SortItem *, int) = + { CompareAscii, CompareDict, CompareDouble, CompareLong, CompareCmd }; + TreeColumn treeColumn; + int notReally = FALSE; + int result = TCL_OK; + + if (TreeItem_FromObj(tree, objv[3], (TreeItem *) &item, 0) != TCL_OK) + return TCL_ERROR; + + if (item->numChildren <= 1) + return TCL_OK; + + /* Defaults: sort ascii strings in column 0 only */ + sortData.tree = tree; + sortData.count = 1; + sortData.column[0] = 0; + sortData.sortBy[0] = SORT_ASCII; + sortData.order[0] = 1; + sortData.result = TCL_OK; + + first = item->firstChild; + last = item->lastChild; + + for (i = 4; i < objc; ) + { + static CONST char *optionName[] = { "-ascii", "-column", "-command", + "-decreasing", "-dictionary", "-first", "-increasing", "-integer", + "-last", "-notreally", "-real", NULL }; + int numArgs[] = { 1, 2, 2, 1, 1, 2, 1, 1, 2, 1, 1 }; + enum { OPT_ASCII, OPT_COLUMN, OPT_COMMAND, OPT_DECREASING, OPT_DICT, + OPT_FIRST, OPT_INCREASING, OPT_INTEGER, OPT_LAST, + OPT_NOT_REALLY, OPT_REAL }; + + if (Tcl_GetIndexFromObj(interp, objv[i], optionName, "option", 0, + &index) != TCL_OK) + return TCL_ERROR; + if (objc - i < numArgs[index]) + { + FormatResult(interp, "missing value for \"%s\" option", + optionName[index]); + return TCL_ERROR; + } + switch (index) + { + case OPT_ASCII: + sortData.sortBy[sortData.count - 1] = SORT_ASCII; + break; + case OPT_COLUMN: + if (TreeColumn_FromObj(tree, objv[i + 1], &treeColumn, 0) != TCL_OK) + return TCL_ERROR; + /* The first -column we see is the first column we compare */ + if (sawColumn) + { + sortData.count++; + /* Defaults for this column */ + sortData.sortBy[sortData.count - 1] = SORT_ASCII; + sortData.order[sortData.count - 1] = 1; + } + sortData.column[sortData.count - 1] = TreeColumn_Index(treeColumn); + sawColumn = TRUE; + break; + case OPT_COMMAND: + sortData.command[sortData.count - 1] = objv[i + 1]; + sortData.sortBy[sortData.count - 1] = SORT_COMMAND; + sawCmd = TRUE; + break; + case OPT_DECREASING: + sortData.order[sortData.count - 1] = 0; + break; + case OPT_DICT: + sortData.sortBy[sortData.count - 1] = SORT_DICT; + break; + case OPT_FIRST: + if (TreeItem_FromObj(tree, objv[i + 1], (TreeItem *) &first, 0) != TCL_OK) + return TCL_ERROR; + if (first->parent != item) + { + FormatResult(interp, "item %d is not a child of item %d", + first->id, item->id); + return TCL_ERROR; + } + break; + case OPT_INCREASING: + sortData.order[sortData.count - 1] = 1; + break; + case OPT_INTEGER: + sortData.sortBy[sortData.count - 1] = SORT_LONG; + break; + case OPT_LAST: + if (TreeItem_FromObj(tree, objv[i + 1], (TreeItem *) &last, 0) != TCL_OK) + return TCL_ERROR; + if (last->parent != item) + { + FormatResult(interp, "item %d is not a child of item %d", + last->id, item->id); + return TCL_ERROR; + } + break; + case OPT_NOT_REALLY: + notReally = TRUE; + break; + case OPT_REAL: + sortData.sortBy[sortData.count - 1] = SORT_DOUBLE; + break; + } + i += numArgs[index]; + } + + if (first == last) + return TCL_OK; + + for (i = 0; i < sortData.count; i++) + { + sortData.proc[i] = sortProc[sortData.sortBy[i]]; + + if (sortData.sortBy[i] == SORT_COMMAND) + { + Tcl_Obj *obj = Tcl_DuplicateObj(sortData.command[i]); + Tcl_Obj *obj2 = Tcl_NewObj(); + Tcl_IncrRefCount(obj); + if (Tcl_ListObjAppendElement(interp, obj, obj2) != TCL_OK) + { + Tcl_DecrRefCount(obj); + Tcl_IncrRefCount(obj2); + Tcl_DecrRefCount(obj2); + /* FIXME: free other .command[] */ + return TCL_ERROR; + } + (void) Tcl_ListObjAppendElement(interp, obj, obj2); + sortData.command[i] = obj; + } + } + + index = 0; + walk = item->firstChild; + while (walk != NULL) + { + if (walk == first) + indexF = index; + if (walk == last) + indexL = index; + index++; + walk = walk->nextSibling; + } + if (indexF > indexL) + { + walk = last; + last = first; + first = walk; + + index = indexL; + indexL = indexF; + indexF = index; + } + count = indexL - indexF + 1; + + sortData.item1s = (struct SortItem1 *) ckalloc(sizeof(struct SortItem1) * count * sortData.count); + sortData.items = (struct SortItem *) ckalloc(sizeof(struct SortItem) * count); + for (i = 0; i < count; i++) + { + sortData.items[i].item1 = sortData.item1s + i * sortData.count; + sortData.items[i].obj = NULL; + } + + index = 0; + walk = first; + while (walk != last->nextSibling) + { + struct SortItem *sortItem = &sortData.items[index]; + + sortItem->item = walk; + if (sawCmd) + { + Tcl_Obj *obj = TreeItem_ToObj(tree, (TreeItem) walk); + Tcl_IncrRefCount(obj); + sortData.items[index].obj = obj; + } + for (i = 0; i < sortData.count; i++) + { + struct SortItem1 *sortItem1 = sortItem->item1 + i; + + if (sortData.sortBy[i] == SORT_COMMAND) + continue; + + column = Item_FindColumn(tree, walk, sortData.column[i]); + if (column == NULL) + { + FormatResult(interp, "item %d doesn't have column %d", + walk->id, sortData.column[i]); + result = TCL_ERROR; + goto done; + } + if (column->style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + walk->id, sortData.column[i]); + result = TCL_ERROR; + goto done; + } + if (TreeStyle_GetSortData(tree, + column->style, sortData.sortBy[i], + &sortItem1->longValue, + &sortItem1->doubleValue, + &sortItem1->string) != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + } + index++; + walk = walk->nextSibling; + } + + quicksort(sortData.items, sortData.items + count - 1); + + if (sortData.result != TCL_OK) + { + result = TCL_ERROR; + goto done; + } + + if (sawCmd) + Tcl_ResetResult(interp); + + if (notReally) + { + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + Tcl_Obj *itemObj; + + /* Smallest to largest */ + if (sortData.order[0] == 1) + { + for (i = 0; i < count; i++) + { + itemObj = sortData.items[i].obj; + if (itemObj == NULL) + itemObj = TreeItem_ToObj(tree, + (TreeItem) sortData.items[i].item); + Tcl_ListObjAppendElement(interp, listObj, itemObj); + } + } + + /* Largest to smallest */ + else + { + for (i = count - 1; i >= 0; i--) + { + itemObj = sortData.items[i].obj; + if (itemObj == NULL) + itemObj = TreeItem_ToObj(tree, + (TreeItem) sortData.items[i].item); + Tcl_ListObjAppendElement(interp, listObj, itemObj); + } + } + + Tcl_SetObjResult(interp, listObj); + goto done; + } + + first = first->prevSibling; + last = last->nextSibling; + + /* Smallest to largest */ + if (sortData.order[0] == 1) + { + for (i = 0; i < count - 1; i++) + { + sortData.items[i].item->nextSibling = sortData.items[i + 1].item; + sortData.items[i + 1].item->prevSibling = sortData.items[i].item; + } + indexF = 0; + indexL = count - 1; + } + + /* Largest to smallest */ + else + { + for (i = count - 1; i > 0; i--) + { + sortData.items[i].item->nextSibling = sortData.items[i - 1].item; + sortData.items[i - 1].item->prevSibling = sortData.items[i].item; + } + indexF = count - 1; + indexL = 0; + } + + lastChild = item->lastChild; + + sortData.items[indexF].item->prevSibling = first; + if (first) + first->nextSibling = sortData.items[indexF].item; + else + item->firstChild = sortData.items[indexF].item; + + sortData.items[indexL].item->nextSibling = last; + if (last) + last->prevSibling = sortData.items[indexL].item; + else + item->lastChild = sortData.items[indexL].item; + + /* Redraw the lines of the old/new lastchild */ + if ((item->lastChild != lastChild) && tree->showLines) + { + if (lastChild->dInfo != NULL) + Tree_InvalidateItemDInfo(tree, (TreeItem) lastChild, + (TreeItem) NULL); + if (item->lastChild->dInfo != NULL) + Tree_InvalidateItemDInfo(tree, (TreeItem) item->lastChild, + (TreeItem) NULL); + } + + tree->updateIndex = 1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + +done: + for (i = 0; i < count; i++) + if (sortData.items[i].obj != NULL) + Tcl_DecrRefCount(sortData.items[i].obj); + for (i = 0; i < sortData.count; i++) + if (sortData.sortBy[i] == SORT_COMMAND) + Tcl_DecrRefCount(sortData.command[i]); + ckfree((char *) sortData.item1s); + ckfree((char *) sortData.items); + + if (tree->debug.enable && tree->debug.data) + Tree_Debug(tree); + + return result; +} + +int TreeItemCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { + "ancestors", + "children", + "create", + "delete", + "firstchild", + "lastchild", + "nextsibling", + "numchildren", + "parent", + "prevsibling", + "remove", + + "bbox", + "complex", + "dump", + "element", + "hasbutton", + "index", + "isancestor", + "isopen", + "rnc", + "sort", + "state", + "style", + "text", + "visible", + (char *) NULL + }; + enum { + COMMAND_ANCESTORS, + COMMAND_CHILDREN, + COMMAND_CREATE, + COMMAND_DELETE, + COMMAND_FIRSTCHILD, + COMMAND_LASTCHILD, + COMMAND_NEXTSIBLING, + COMMAND_NUMCHILDREN, + COMMAND_PARENT, + COMMAND_PREVSIBLING, + COMMAND_REMOVE, + + COMMAND_BBOX, + COMMAND_COMPLEX, + COMMAND_DUMP, + COMMAND_ELEMENT, + COMMAND_HASBUTTON, + COMMAND_INDEX, + COMMAND_ISANCESTOR, + COMMAND_ISOPEN, + COMMAND_RNC, + COMMAND_SORT, + COMMAND_STATE, + COMMAND_STYLE, + COMMAND_TEXT, + COMMAND_VISIBLE + }; +#define AF_NOTANCESTOR 0x00010000 /* item can't be ancestor of other item */ +#define AF_PARENT 0x00020000 /* item must have a parent */ +#define AF_EQUALOK 0x00040000 /* second item can be same as first */ +#define AF_SAMEROOT 0x00080000 /* both items must be descendants of a common ancestor */ + struct { + int minArgs; + int maxArgs; + int flags; + int flags2; + char *argString; + } argInfo[] = { + { 1, 1, 0, 0, "item" }, /* ancestors */ + { 1, 1, 0, 0, "item" }, /* children */ + { 0, 0, 0, 0, NULL }, /* create */ + { 1, 2, IFO_ALLOK | IFO_NOTROOT, IFO_ALLOK | IFO_NOTROOT | AF_EQUALOK | AF_SAMEROOT, "first ?last?" }, /* delete */ + { 1, 2, 0, IFO_NOTROOT | AF_NOTANCESTOR, "item ?newFirstChild?" }, /* firstchild */ + { 1, 2, 0, IFO_NOTROOT | AF_NOTANCESTOR, "item ?newLastChild?" }, /* lastchild */ + { 1, 2, IFO_NOTROOT | AF_PARENT, IFO_NOTROOT | AF_NOTANCESTOR, "item ?newNextSibling?" }, /* nextsibling */ + { 1, 1, 0, 0, "item" }, /* numchildren */ + { 1, 1, 0, 0, "item" }, /* parent */ + { 1, 2, IFO_NOTROOT | AF_PARENT, IFO_NOTROOT | AF_NOTANCESTOR, "item ?newPrevSibling?" }, /* prevsibling */ + { 1, 1, IFO_NOTROOT, 0, "item" }, /* remove */ + + { 1, 3, 0, 0, "item ?column? ?element?" }, /* bbox */ + { 2, 100000, 0, 0, "item list..." }, /* complex */ + { 1, 1, 0, 0, "item" }, /* dump */ + { 4, 100000, 0, 0, "command item column element ?arg ...?" }, /* element */ + { 1, 2, 0, 0, "item ?boolean?" }, /* hasbutton */ + { 1, 1, 0, 0, "item" }, /* index */ + { 2, 2, 0, AF_EQUALOK, "item item2" }, /* isancestor */ + { 1, 1, 0, 0, "item" }, /* isopen */ + { 1, 1, 0, 0, "item" }, /* rnc */ + { 1, 100000, 0, 0, "item ?option ...?" }, /* sort */ + { 1, 100000, 0, 0, "item ?state ...?" }, /* state */ + { 2, 100000, 0, 0, "command item ?arg ...?" }, /* style */ + { 2, 100000, 0, 0, "item column ?text? ?column text ...?" }, /* text */ + { 1, 2, 0, 0, "item ?boolean?" }, /* visible */ + }; + int index; + int numArgs = objc - 3; + Item *item, *item2 = NULL, *child; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + if ((numArgs < argInfo[index].minArgs) || + (numArgs > argInfo[index].maxArgs)) + { + Tcl_WrongNumArgs(interp, 3, objv, argInfo[index].argString); + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_ELEMENT: + { + return ItemElementCmd(clientData, interp, objc, objv); + } + case COMMAND_SORT: + { + return ItemSortCmd(clientData, interp, objc, objv); + } + case COMMAND_STYLE: + { + return ItemStyleCmd(clientData, interp, objc, objv); + } + } + + if (numArgs >= 1) + { + if (TreeItem_FromObj(tree, objv[3], (TreeItem *) &item, + argInfo[index].flags & 0xFFFF) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T item bbox I ?C? ?E? */ + case COMMAND_BBOX: + { + int x, y, w, h; + int i, columnIndex, indent, totalWidth; + TreeColumn treeColumn; + TreeItemColumn itemColumn; + StyleDrawArgs drawArgs; + TreeItem item_ = (TreeItem) item; + XRectangle rect; + + if (!TreeItem_ReallyVisible(tree, item_)) + return TCL_OK; + Tree_ItemBbox(tree, item_, &x, &y, &w, &h); + if (objc > 4) + { + if (TreeItem_ColumnFromObj(tree, item_, objv[4], + &itemColumn, &columnIndex) != TCL_OK) + return TCL_ERROR; + totalWidth = 0; + treeColumn = tree->columns; + for (i = 0; i < columnIndex; i++) + { + totalWidth += TreeColumn_UseWidth(treeColumn); + treeColumn = TreeColumn_Next(treeColumn); + } + if (columnIndex == tree->columnTree) + indent = TreeItem_Indent(tree, item_); + else + indent = 0; + if (objc == 5) + { + FormatResult(interp, "%d %d %d %d", + x + totalWidth + indent - tree->xOrigin, + y - tree->yOrigin, + x + totalWidth + TreeColumn_UseWidth(treeColumn) - tree->xOrigin, + y + h - tree->yOrigin); + return TCL_OK; + } + drawArgs.style = TreeItemColumn_GetStyle(tree, itemColumn); + if (drawArgs.style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + TreeItem_GetID(tree, item_), columnIndex); + return TCL_ERROR; + } + drawArgs.tree = tree; + drawArgs.drawable = None; + drawArgs.state = TreeItem_GetState(tree, item_); + drawArgs.x = x + indent + totalWidth; + drawArgs.y = y; + drawArgs.width = TreeColumn_UseWidth(treeColumn) - indent; + drawArgs.height = h; + drawArgs.justify = TreeColumn_Justify(treeColumn); + if (TreeStyle_GetElemRects(&drawArgs, objc - 5, objv + 5, + &rect) == -1) + return TCL_ERROR; + x = rect.x; + y = rect.y; + w = rect.width; + h = rect.height; + } + FormatResult(interp, "%d %d %d %d", + x - tree->xOrigin, + y - tree->yOrigin, + x - tree->xOrigin + w, + y - tree->yOrigin + h); + return TCL_OK; + } + case COMMAND_COMPLEX: + { + int i, j, columnIndex; + int objc1, objc2; + Tcl_Obj **objv1, **objv2; + Column *column; + + if (objc > 4) + { + columnIndex = 0; + for (i = 4; i < objc; i++, columnIndex++) + { + column = Item_FindColumn(tree, item, columnIndex); + if (column == NULL) + { + FormatResult(interp, "item %d doesn't have column %d", + item->id, columnIndex); + return TCL_ERROR; + } + /* List of element-configs per column */ + if (Tcl_ListObjGetElements(interp, objv[i], + &objc1, &objv1) != TCL_OK) + return TCL_ERROR; + if (objc1 == 0) + continue; + if (column->style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + item->id, columnIndex); + return TCL_ERROR; + } + for (j = 0; j < objc1; j++) + { + /* elem option value... */ + if (Tcl_ListObjGetElements(interp, objv1[j], + &objc2, &objv2) != TCL_OK) + return TCL_ERROR; + if (objc2 < 3) + { + FormatResult(interp, + "wrong # args: should be \"element option value...\""); + return TCL_ERROR; + } + if (TreeStyle_ElementConfigure(tree, column->style, + objv2[0], objc2 - 1, objv2 + 1) != TCL_OK) + return TCL_ERROR; + } + column->neededWidth = column->neededHeight = -1; + } + Tree_InvalidateColumnWidth(tree, -1); + TreeItem_InvalidateHeight(tree, (TreeItem) item); + Tree_FreeItemDInfo(tree, (TreeItem) item, NULL); + } + return TCL_OK; + } + case COMMAND_DUMP: + { + if (tree->updateIndex) + Tree_UpdateItemIndex(tree); + FormatResult(interp, "index %d indexVis %d neededHeight %d", + item->index, item->indexVis, item->neededHeight); + return TCL_OK; + } + case COMMAND_HASBUTTON: + { + int hasButton; + + if (objc == 5) + { + if (Tcl_GetBooleanFromObj(interp, objv[4], &hasButton) != TCL_OK) + return TCL_ERROR; + if (hasButton != item->hasButton) + { + item->hasButton = hasButton; + Tree_InvalidateItemDInfo(tree, (TreeItem) item, NULL); + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->hasButton)); + return TCL_OK; + } + /* T item state I ?state ...? */ + case COMMAND_STATE: + { + int i, j, negate, stateOn = 0, stateOff = 0; + char *string; + + if (objc == 4) + { + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + for (i = 0; i < 32; i++) + { + if (tree->stateNames[i] == NULL) + continue; + if (item->state & (1L << i)) + { + Tcl_ListObjAppendElement(interp, listObj, + Tcl_NewStringObj(tree->stateNames[i], -1)); + } + } + Tcl_SetObjResult(interp, listObj); + return TCL_OK; + } + for (i = 4; i < objc; i++) + { + negate = 0; + string = Tcl_GetString(objv[i]); + if (string[0] == '!') + negate = 1; + for (j = STATE_USER - 1; j < 32; j++) + { + if (tree->stateNames[j] == NULL) + continue; + if (strcmp(tree->stateNames[j], string + negate) == 0) + break; + } + if (j == 32) + { + FormatResult(interp, "cannot change state \"%s\"", string); + return TCL_ERROR; + } + if (negate) + stateOff |= 1L << j; + else + stateOn |= 1L << j; + } + TreeItem_ChangeState(tree, (TreeItem) item, stateOff, stateOn); + return TCL_OK; + } + case COMMAND_VISIBLE: + { + int visible; + + if (objc == 5) + { + if (Tcl_GetBooleanFromObj(interp, objv[4], &visible) != TCL_OK) + return TCL_ERROR; + if (visible != item->isVisible) + { + item->isVisible = visible; + + /* May change the width of any column */ + Tree_InvalidateColumnWidth(tree, -1); + + /* If this is the last child, redraw the lines of the previous + * sibling and all of its descendants because the line from + * the previous sibling to us is appearing/disappearing */ + if ((item->prevSibling != NULL) && + (item->nextSibling == NULL) && + tree->showLines) + { + Item *last = item->prevSibling; + while (last->lastChild != NULL) + last = last->lastChild; + Tree_InvalidateItemDInfo(tree, + (TreeItem) item->prevSibling, + (TreeItem) last); + } + + tree->updateIndex = 1; + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->isVisible)); + return TCL_OK; + } + case COMMAND_ISOPEN: + { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->state & STATE_OPEN)); + return TCL_OK; + } + case COMMAND_INDEX: + { + FormatResult(interp, "%d %d", item->index, item->indexVis); + return TCL_OK; + } + case COMMAND_RNC: + { + int row,col; + + if (Tree_ItemToRNC(tree, (TreeItem) item, &row, &col) == TCL_OK) + FormatResult(interp, "%d %d", row, col); + return TCL_OK; + } + + /* T item text I C ?text? ?C text ...? */ + case COMMAND_TEXT: + { + Column *column; + Tcl_Obj *textObj; + int i, columnIndex; + + if (objc == 5) + { + if (Item_FindColumnFromObj(tree, item, objv[4], &column, NULL) != TCL_OK) + { + return TCL_ERROR; + } + if (column->style != NULL) + { + textObj = TreeStyle_GetText(tree, column->style); + if (textObj != NULL) + Tcl_SetObjResult(interp, textObj); + } + return TCL_OK; + } + if ((objc - 4) & 1) + { + FormatResult(interp, "wrong # args: should be \"column text column text...\""); + return TCL_ERROR; + } + TreeItem_InvalidateHeight(tree, (TreeItem) item); + Tree_InvalidateItemDInfo(tree, (TreeItem) item, NULL); + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + for (i = 4; i < objc; i += 2) + { + if (Item_CreateColumnFromObj(tree, item, objv[i], &column, &columnIndex) != TCL_OK) + return TCL_ERROR; + if (column->style == NULL) + { + FormatResult(interp, "item %d column %d has no style", + item->id, columnIndex); + return TCL_ERROR; + } + TreeStyle_SetText(tree, column->style, objv[i + 1]); + column->neededWidth = column->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + } + return TCL_OK; + } + } + + if ((argInfo[index].flags & AF_PARENT) && (item->parent == NULL)) + { + FormatResult(interp, "item %d must have a parent", item->id); + return TCL_ERROR; + } + } + if (numArgs >= 2) + { + if (TreeItem_FromObj(tree, objv[4], (TreeItem *) &item2, + argInfo[index].flags2 & 0xFFFF) != TCL_OK) + { + return TCL_ERROR; + } + if (!(argInfo[index].flags2 & AF_EQUALOK) && (item == item2)) + { + FormatResult(interp, "item %d same as second item", item->id); + return TCL_ERROR; + } + if ((argInfo[index].flags2 & AF_PARENT) && (item2->parent == NULL)) + { + FormatResult(interp, "item %d must have a parent", item2->id); + return TCL_ERROR; + } + if ((argInfo[index].flags & AF_NOTANCESTOR) && + TreeItem_IsAncestor(tree, (TreeItem) item, (TreeItem) item2)) + { + FormatResult(interp, "item %d is ancestor of item %d", + item->id, item2->id); + return TCL_ERROR; + } + if ((argInfo[index].flags2 & AF_NOTANCESTOR) && + TreeItem_IsAncestor(tree, (TreeItem) item2, (TreeItem) item)) + { + FormatResult(interp, "item %d is ancestor of item %d", + item2->id, item->id); + return TCL_ERROR; + } + if ((argInfo[index].flags2 & AF_SAMEROOT) && + TreeItem_RootAncestor(tree, (TreeItem) item) != + TreeItem_RootAncestor(tree, (TreeItem) item2)) + { + FormatResult(interp, + "item %d and item %d don't share a common ancestor", + item->id, item2->id); + return TCL_ERROR; + } + } + + switch (index) + { + case COMMAND_ANCESTORS: + { + Tcl_Obj *listObj; + Item *parent = item->parent; + + listObj = Tcl_NewListObj(0, NULL); + while (parent != NULL) + { + Tcl_ListObjAppendElement(interp, listObj, + TreeItem_ToObj(tree, (TreeItem) parent)); + parent = parent->parent; + } + Tcl_SetObjResult(interp, listObj); + break; + } + case COMMAND_CHILDREN: + { + if (item->numChildren != 0) + { + Tcl_Obj *listObj; + + listObj = Tcl_NewListObj(0, NULL); + child = item->firstChild; + while (child != NULL) + { + Tcl_ListObjAppendElement(interp, listObj, + TreeItem_ToObj(tree, (TreeItem) child)); + child = child->nextSibling; + } + Tcl_SetObjResult(interp, listObj); + } + break; + } + case COMMAND_CREATE: + { + item = (Item *) TreeItem_Alloc(tree); + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item)); + break; + } + case COMMAND_DELETE: + { + int index1, index2; + + if (item == (Item *) ITEM_ALL || item2 == (Item *) ITEM_ALL) + { + /* Do it this way so any detached items are deleted */ + while (1) + { + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + item = (Item *) Tcl_GetHashValue(hPtr); + if (item == (Item *) tree->root) + hPtr = Tcl_NextHashEntry(&search); + if (hPtr == NULL) + break; + item = (Item *) Tcl_GetHashValue(hPtr); + TreeItem_Delete(tree, (TreeItem) item); + } + } + else if (item2 != NULL) + { + TreeItem_ToIndex(tree, (TreeItem) item, &index1, NULL); + TreeItem_ToIndex(tree, (TreeItem) item2, &index2, NULL); + if (index1 > index2) + { + Item *swap = item; + item = item2; + item2 = swap; + } + while (1) + { + Item *prev = (Item *) TreeItem_Prev(tree, (TreeItem) item2); + TreeItem_Delete(tree, (TreeItem) item2); + if (item2 == item) + break; + item2 = prev; + } + } + else + { + TreeItem_Delete(tree, (TreeItem) item); + } + break; + } + case COMMAND_FIRSTCHILD: + { + if (item2 != NULL && item2 != item->firstChild) + { + TreeItem_RemoveFromParent(tree, (TreeItem) item2); + item2->nextSibling = item->firstChild; + if (item->firstChild != NULL) + item->firstChild->prevSibling = item2; + else + item->lastChild = item2; + item->firstChild = item2; + item2->parent = item; + item->numChildren++; + TreeItem_AddToParent(tree, (TreeItem) item2); + } + if (item->firstChild != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item->firstChild)); + break; + } + case COMMAND_ISANCESTOR: + { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + TreeItem_IsAncestor(tree, (TreeItem) item, (TreeItem) item2))); + break; + } + case COMMAND_LASTCHILD: + { + if (item2 != NULL && item2 != item->lastChild) + { + TreeItem_RemoveFromParent(tree, (TreeItem) item2); + item2->prevSibling = item->lastChild; + if (item->lastChild != NULL) + item->lastChild->nextSibling = item2; + else + item->firstChild = item2; + item->lastChild = item2; + item2->parent = item; + item->numChildren++; + TreeItem_AddToParent(tree, (TreeItem) item2); + } + if (item->lastChild != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item->lastChild)); + break; + } + case COMMAND_NEXTSIBLING: + { + if (item2 != NULL && item2 != item->nextSibling) + { + TreeItem_RemoveFromParent(tree, (TreeItem) item2); + item2->prevSibling = item; + if (item->nextSibling != NULL) + { + item->nextSibling->prevSibling = item2; + item2->nextSibling = item->nextSibling; + } + else + item->parent->lastChild = item2; + item->nextSibling = item2; + item2->parent = item->parent; + item->parent->numChildren++; + TreeItem_AddToParent(tree, (TreeItem) item2); + } + if (item->nextSibling != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item->nextSibling)); + break; + } + case COMMAND_NUMCHILDREN: + { + Tcl_SetObjResult(interp, Tcl_NewIntObj(item->numChildren)); + break; + } + case COMMAND_PARENT: + { + if (item->parent != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item->parent)); + break; + } + case COMMAND_PREVSIBLING: + { + if (item2 != NULL && item2 != item->prevSibling) + { + TreeItem_RemoveFromParent(tree, (TreeItem) item2); + item2->nextSibling = item; + if (item->prevSibling != NULL) + { + item->prevSibling->nextSibling = item2; + item2->prevSibling = item->prevSibling; + } + else + item->parent->firstChild = item2; + item->prevSibling = item2; + item2->parent = item->parent; + item->parent->numChildren++; + TreeItem_AddToParent(tree, (TreeItem) item2); + } + if (item->prevSibling != NULL) + Tcl_SetObjResult(interp, TreeItem_ToObj(tree, (TreeItem) item->prevSibling)); + break; + } + case COMMAND_REMOVE: + { + if (item->parent == NULL) + break; + TreeItem_RemoveFromParent(tree, (TreeItem) item); + if (tree->debug.enable && tree->debug.data) + Tree_Debug(tree); + Tree_InvalidateColumnWidth(tree, -1); + Tree_FreeItemDInfo(tree, (TreeItem) item, NULL); + break; + } + } + + return TCL_OK; +} + +int TreeItem_Debug(TreeCtrl *tree, TreeItem item_) +{ + Item *item = (Item *) item_; + Item *child; + Tcl_Interp *interp = tree->interp; + int count; + + if (item->parent == item) + { + FormatResult(interp, + "parent of %d is itself", item->id); + return TCL_ERROR; + } + + if (item->parent == NULL) + { + if (item->prevSibling != NULL) + { + FormatResult(interp, + "parent of %d is nil, prevSibling is not nil", + item->id); + return TCL_ERROR; + } + if (item->nextSibling != NULL) + { + FormatResult(interp, + "parent of %d is nil, nextSibling is not nil", + item->id); + return TCL_ERROR; + } + } + + if (item->prevSibling != NULL) + { + if (item->prevSibling == item) + { + FormatResult(interp, + "prevSibling of %d is itself", + item->id); + return TCL_ERROR; + } + if (item->prevSibling->nextSibling != item) + { + FormatResult(interp, + "item%d.prevSibling.nextSibling is not it", + item->id); + return TCL_ERROR; + } + } + + if (item->nextSibling != NULL) + { + if (item->nextSibling == item) + { + FormatResult(interp, + "nextSibling of %d is itself", + item->id); + return TCL_ERROR; + } + if (item->nextSibling->prevSibling != item) + { + FormatResult(interp, + "item%d.nextSibling->prevSibling is not it", + item->id); + return TCL_ERROR; + } + } + + if (item->numChildren < 0) + { + FormatResult(interp, + "numChildren of %d is %d", + item->id, item->numChildren); + return TCL_ERROR; + } + + if (item->numChildren == 0) + { + if (item->firstChild != NULL) + { + FormatResult(interp, + "item%d.numChildren is zero, firstChild is not nil", + item->id); + return TCL_ERROR; + } + if (item->lastChild != NULL) + { + FormatResult(interp, + "item%d.numChildren is zero, lastChild is not nil", + item->id); + return TCL_ERROR; + } + } + + if (item->numChildren > 0) + { + if (item->firstChild == NULL) + { + FormatResult(interp, + "item%d.firstChild is nil", + item->id); + return TCL_ERROR; + } + if (item->firstChild == item) + { + FormatResult(interp, + "item%d.firstChild is itself", + item->id); + return TCL_ERROR; + } + if (item->firstChild->parent != item) + { + FormatResult(interp, + "item%d.firstChild.parent is not it", + item->id); + return TCL_ERROR; + } + if (item->firstChild->prevSibling != NULL) + { + FormatResult(interp, + "item%d.firstChild.prevSibling is not nil", + item->id); + return TCL_ERROR; + } + + if (item->lastChild == NULL) + { + FormatResult(interp, + "item%d.lastChild is nil", + item->id); + return TCL_ERROR; + } + if (item->lastChild == item) + { + FormatResult(interp, + "item%d.lastChild is itself", + item->id); + return TCL_ERROR; + } + if (item->lastChild->parent != item) + { + FormatResult(interp, + "item%d.lastChild.parent is not it", + item->id); + return TCL_ERROR; + } + if (item->lastChild->nextSibling != NULL) + { + FormatResult(interp, + "item%d.lastChild.nextSibling is not nil", + item->id); + return TCL_ERROR; + } + + /* Count number of children */ + count = 0; + child = item->firstChild; + while (child != NULL) + { + count++; + child = child->nextSibling; + } + if (count != item->numChildren) + { + FormatResult(interp, + "item%d.numChildren is %d, but counted %d", + item->id, item->numChildren, count); + return TCL_ERROR; + } + + /* Debug each child recursively */ + child = item->firstChild; + while (child != NULL) + { + if (child->parent != item) + { + FormatResult(interp, + "child->parent of %d is not it", + item->id); + return TCL_ERROR; + } + if (TreeItem_Debug(tree, (TreeItem) child) != TCL_OK) + return TCL_ERROR; + child = child->nextSibling; + } + } + return TCL_OK; +} + +char *TreeItem_Identify(TreeCtrl *tree, TreeItem item_, int x, int y) +{ + Item *self = (Item *) item_; + int left, top, width, height; + int indent, columnWidth, totalWidth; + Column *column; + StyleDrawArgs drawArgs; + TreeColumn treeColumn; + + if (!TreeItem_ReallyVisible(tree, item_)) + return NULL; + + Tree_ItemBbox(tree, item_, &left, &top, &width, &height); +#if 0 + if (y >= Tk_Height(tree->tkwin) || y + height <= 0) + return NULL; +#endif + drawArgs.tree = tree; + drawArgs.drawable = None; + drawArgs.state = self->state; + + totalWidth = 0; + treeColumn = tree->columns; + column = self->columns; + while (column != NULL) + { + if (!TreeColumn_Visible(treeColumn)) + columnWidth = 0; + else if (tree->columnCountVis == 1) + columnWidth = width; + else + columnWidth = TreeColumn_UseWidth(treeColumn); + if (columnWidth > 0) + { + if (TreeColumn_Index(treeColumn) == tree->columnTree) + indent = TreeItem_Indent(tree, item_); + else + indent = 0; + if ((x >= totalWidth + indent) && (x < totalWidth + columnWidth)) + { + if (column->style != NULL) + { + drawArgs.style = column->style; + drawArgs.x = indent + totalWidth; + drawArgs.y = 0; + drawArgs.width = columnWidth - indent; + drawArgs.height = height; + drawArgs.justify = TreeColumn_Justify(treeColumn); + return TreeStyle_Identify(&drawArgs, x, y); + } + return NULL; + } + totalWidth += columnWidth; + } + treeColumn = TreeColumn_Next(treeColumn); + column = column->next; + } + return NULL; +} + +void TreeItem_Identify2(TreeCtrl *tree, TreeItem item_, + int x1, int y1, int x2, int y2, Tcl_Obj *listObj) +{ + Item *self = (Item *) item_; + int indent, columnWidth, totalWidth; + int x, y, w, h; + Column *column; + StyleDrawArgs drawArgs; + TreeColumn treeColumn; + + if (!TreeItem_ReallyVisible(tree, item_)) + return; + + Tree_ItemBbox(tree, item_, &x, &y, &w, &h); + + drawArgs.tree = tree; + drawArgs.drawable = None; + drawArgs.state = self->state; + + totalWidth = 0; + treeColumn = tree->columns; + column = self->columns; + while (column != NULL) + { + if (!TreeColumn_Visible(treeColumn)) + columnWidth = 0; + else if (tree->columnCountVis == 1) + columnWidth = w; + else + columnWidth = TreeColumn_UseWidth(treeColumn); + if (columnWidth > 0) + { + if (TreeColumn_Index(treeColumn) == tree->columnTree) + indent = TreeItem_Indent(tree, item_); + else + indent = 0; + if ((x2 >= x + totalWidth + indent) && (x1 < x + totalWidth + columnWidth)) + { + Tcl_Obj *subListObj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(tree->interp, subListObj, + Tcl_NewIntObj(TreeColumn_Index(treeColumn))); + if (column->style != NULL) + { + drawArgs.style = column->style; + drawArgs.x = x + totalWidth + indent; + drawArgs.y = y; + drawArgs.width = columnWidth - indent; + drawArgs.height = h; + drawArgs.justify = TreeColumn_Justify(treeColumn); + TreeStyle_Identify2(&drawArgs, x1, y1, x2, y2, subListObj); + } + Tcl_ListObjAppendElement(tree->interp, listObj, subListObj); + } + totalWidth += columnWidth; + } + treeColumn = TreeColumn_Next(treeColumn); + column = column->next; + } +} + diff --git a/generic/tkTreeMarquee.c b/generic/tkTreeMarquee.c new file mode 100644 index 0000000..2e2b7ad --- /dev/null +++ b/generic/tkTreeMarquee.c @@ -0,0 +1,364 @@ +#include "tkTreeCtrl.h" + +typedef struct Marquee Marquee; + +struct Marquee +{ + TreeCtrl *tree; + int visible; + int x1, y1, x2, y2; + int onScreen; + int sx, sy; +}; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, + "0", -1, Tk_Offset(Marquee, visible), + 0, (ClientData) NULL, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, 0, 0} +}; + +static Tk_OptionTable optionTable = NULL; + +int TreeMarquee_Init(TreeCtrl *tree) +{ + Marquee *marquee; + + if (optionTable == NULL) + optionTable = Tk_CreateOptionTable(tree->interp, optionSpecs); + + marquee = (Marquee *) ckalloc(sizeof(Marquee)); + memset(marquee, '\0', sizeof(Marquee)); + marquee->tree = tree; + if (Tk_InitOptions(tree->interp, (char *) marquee, optionTable, + tree->tkwin) != TCL_OK) + { + WFREE(marquee, Marquee); + return TCL_ERROR; + } + tree->marquee = (TreeMarquee) marquee; + return TCL_OK; +} + +void TreeMarquee_Free(TreeMarquee marquee_) +{ + Marquee *marquee = (Marquee *) marquee_; + + Tk_FreeConfigOptions((char *) marquee, optionTable, + marquee->tree->tkwin); + WFREE(marquee, Marquee); +} + +void TreeMarquee_Display(TreeMarquee marquee_) +{ + Marquee *marquee = (Marquee *) marquee_; + TreeCtrl *tree = marquee->tree; + + if (!marquee->onScreen && marquee->visible) + { + marquee->sx = 0 - tree->xOrigin; + marquee->sy = 0 - tree->yOrigin; + TreeMarquee_Draw(marquee_, Tk_WindowId(tree->tkwin), marquee->sx, marquee->sy); + marquee->onScreen = TRUE; + } +} + +void TreeMarquee_Undisplay(TreeMarquee marquee_) +{ + Marquee *marquee = (Marquee *) marquee_; + TreeCtrl *tree = marquee->tree; + + if (marquee->onScreen) + { + TreeMarquee_Draw(marquee_, Tk_WindowId(tree->tkwin), marquee->sx, marquee->sy); + marquee->onScreen = FALSE; + } +} + +void TreeMarquee_Draw(TreeMarquee marquee_, Drawable drawable, int x1, int y1) +{ + Marquee *marquee = (Marquee *) marquee_; + TreeCtrl *tree = marquee->tree; + int x, y, w, h; + DotState dotState; + +/* if (!marquee->visible) + return; */ + + x = MIN(marquee->x1, marquee->x2); + w = abs(marquee->x1 - marquee->x2) + 1; + y = MIN(marquee->y1, marquee->y2); + h = abs(marquee->y1 - marquee->y2) + 1; + + DotRect_Setup(tree, drawable, &dotState); + DotRect_Draw(&dotState, x1 + x, y1 + y, w, h); + DotRect_Restore(&dotState); +} + +static int Marquee_Config(Marquee *marquee, int objc, Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = marquee->tree; + Tk_SavedOptions savedOptions; + int mask, result; + + result = Tk_SetOptions(tree->interp, (char *) marquee, optionTable, + objc, objv, tree->tkwin, &savedOptions, &mask); + if (result != TCL_OK) + { + Tk_RestoreSavedOptions(&savedOptions); + return TCL_ERROR; + } + Tk_FreeSavedOptions(&savedOptions); + +/* Marquee_Redraw(marquee); */ + + return TCL_OK; +} + +int TreeMarqueeCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + Marquee *marquee = (Marquee *) tree->marquee; + static CONST char *commandNames[] = { "anchor", "cget", "configure", + "coords", "corner", "identify", "visible", (char *) NULL }; + enum { COMMAND_ANCHOR, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_COORDS, + COMMAND_CORNER, COMMAND_IDENTIFY, COMMAND_VISIBLE }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + /* T marquee anchor ?x y?*/ + case COMMAND_ANCHOR: + { + int x, y; + + if (objc != 3 && objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "?x y?"); + return TCL_ERROR; + } + if (objc == 3) + { + FormatResult(interp, "%d %d", marquee->x1, marquee->y1); + break; + } + if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) + return TCL_ERROR; + if ((x == marquee->x1) && (y == marquee->y1)) + break; + TreeMarquee_Undisplay(tree->marquee); + marquee->x1 = x; + marquee->y1 = y; + TreeMarquee_Display(tree->marquee); + break; + } + + /* T marquee cget option */ + case COMMAND_CGET: + { + Tcl_Obj *resultObjPtr; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "option"); + return TCL_ERROR; + } + resultObjPtr = Tk_GetOptionValue(interp, (char *) marquee, + optionTable, objv[3], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + /* T marquee configure ?option? ?value? ?option value ...? */ + case COMMAND_CONFIGURE: + { + Tcl_Obj *resultObjPtr; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); + return TCL_ERROR; + } + if (objc <= 4) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) marquee, + optionTable, + (objc == 3) ? (Tcl_Obj *) NULL : objv[3], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + return Marquee_Config(marquee, objc - 3, objv + 3); + } + + /* T marquee coords ?x y x y? */ + case COMMAND_COORDS: + { + int x1, y1, x2, y2; + + if (objc != 3 && objc != 7) + { + Tcl_WrongNumArgs(interp, 3, objv, "?x y x y?"); + return TCL_ERROR; + } + if (objc == 3) + { + FormatResult(interp, "%d %d %d %d", marquee->x1, marquee->y1, + marquee->x2, marquee->y2); + break; + } + if (Tcl_GetIntFromObj(interp, objv[3], &x1) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[4], &y1) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[5], &x2) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[6], &y2) != TCL_OK) + return TCL_ERROR; + if (x1 == marquee->x1 && y1 == marquee->y1 && + x2 == marquee->x2 && y2 == marquee->y2) + break; + TreeMarquee_Undisplay(tree->marquee); + marquee->x1 = x1; + marquee->y1 = y1; + marquee->x2 = x2; + marquee->y2 = y2; + TreeMarquee_Display(tree->marquee); + break; + } + + /* T marquee corner ?x y?*/ + case COMMAND_CORNER: + { + int x, y; + + if (objc != 3 && objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "?x y?"); + return TCL_ERROR; + } + if (objc == 3) + { + FormatResult(interp, "%d %d", marquee->x2, marquee->y2); + break; + } + if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) + return TCL_ERROR; + if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) + return TCL_ERROR; + if (x == marquee->x2 && y == marquee->y2) + break; + TreeMarquee_Undisplay(tree->marquee); + marquee->x2 = x; + marquee->y2 = y; + TreeMarquee_Display(tree->marquee); + break; + } + + /* T marquee identify */ + case COMMAND_IDENTIFY: + { + int x1, y1, x2, y2, n = 0; + int totalWidth = Tree_TotalWidth(tree); + int totalHeight = Tree_TotalHeight(tree); + TreeItem *items; + Tcl_Obj *listObj; + + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); + return TCL_ERROR; + } + + x1 = MIN(marquee->x1, marquee->x2); + x2 = MAX(marquee->x1, marquee->x2); + y1 = MIN(marquee->y1, marquee->y2); + y2 = MAX(marquee->y1, marquee->y2); + + if (x2 <= 0) + break; + if (x1 >= totalWidth) + break; + + if (y2 <= 0) + break; + if (y1 >= totalHeight) + break; + + if (x1 < 0) + x1 = 0; + if (x2 > totalWidth) + x2 = totalWidth; + + if (y1 < 0) + y1 = 0; + if (y2 > totalHeight) + y2 = totalHeight; + + items = Tree_ItemsInArea(tree, x1, y1, x2, y2); + if (items == NULL) + break; + + listObj = Tcl_NewListObj(0, NULL); + while (items[n] != NULL) + { + Tcl_Obj *subListObj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, subListObj, + TreeItem_ToObj(tree, items[n])); + TreeItem_Identify2(tree, items[n], x1, y1, x2, y2, subListObj); + Tcl_ListObjAppendElement(interp, listObj, subListObj); + n++; + } + ckfree((char *) items); + Tcl_SetObjResult(interp, listObj); + break; + } + + /* T marquee visible ?boolean? */ + case COMMAND_VISIBLE: + { + int visible; + + if (objc != 3 && objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "?boolean?"); + return TCL_ERROR; + } + if (objc == 4) + { + if (Tcl_GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) + return TCL_ERROR; + if (visible != marquee->visible) + { + marquee->visible = visible; + TreeMarquee_Undisplay(tree->marquee); + TreeMarquee_Display(tree->marquee); + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(marquee->visible)); + break; + } + } + + return TCL_OK; +} diff --git a/generic/tkTreeNotify.c b/generic/tkTreeNotify.c new file mode 100644 index 0000000..aeb1990 --- /dev/null +++ b/generic/tkTreeNotify.c @@ -0,0 +1,389 @@ +#include "tkTreeCtrl.h" + +static int EVENT_EXPAND, + DETAIL_EXPAND_BEFORE, + DETAIL_EXPAND_AFTER; +static int EVENT_COLLAPSE, + DETAIL_COLLAPSE_BEFORE, + DETAIL_COLLAPSE_AFTER; +static int EVENT_SELECTION; +static int EVENT_ACTIVEITEM; +static int EVENT_SCROLL, + DETAIL_SCROLL_X, + DETAIL_SCROLL_Y; + +/* + * %-substitution for any event + */ +static void Percents_Any(QE_ExpandArgs *args) +{ + struct { + TreeCtrl *tree; + } *data = args->clientData; + + switch (args->which) + { + case 'd': /* detail */ + QE_ExpandDetail(args->bindingTable, args->event, args->detail, + args->result); + break; + + case 'e': /* event */ + QE_ExpandEvent(args->bindingTable, args->event, args->result); + break; + + case 'W': /* object */ + QE_ExpandString((char *) args->object, args->result); + break; + + case 'T': /* tree */ + QE_ExpandString(Tk_PathName(data->tree->tkwin), args->result); + break; + + default: + QE_ExpandUnknown(args->which, args->result); + break; + } +} + +static void Percents_Expand(QE_ExpandArgs *args) +{ + struct { + TreeCtrl *tree; + int id; + } *data = args->clientData; + + switch (args->which) + { + case 'I': + QE_ExpandNumber(data->id, args->result); + break; + + default: + Percents_Any(args); + break; + } +} + +static void Percents_Selection(QE_ExpandArgs *args) +{ + struct { + TreeCtrl *tree; + int *select; + int *deselect; + int count; + } *data = args->clientData; + int *items = NULL; + int i = 0; + char string[TCL_INTEGER_SPACE]; + + switch (args->which) + { + case 'c': + QE_ExpandNumber(data->count, args->result); + break; + case 'D': + case 'S': + items = (args->which == 'D') ? data->deselect : data->select; + if (items == NULL) + { + Tcl_DStringAppend(args->result, "{}", 2); + break; + } + Tcl_DStringStartSublist(args->result); + while (items[i] != -1) + { + TclFormatInt(string, items[i]); + Tcl_DStringAppendElement(args->result, string); + i++; + } + Tcl_DStringEndSublist(args->result); + break; + + default: + Percents_Any(args); + break; + } +} + +static void Percents_ActiveItem(QE_ExpandArgs *args) +{ + struct { + TreeCtrl *tree; + int prev; + int current; + } *data = args->clientData; + + switch (args->which) + { + case 'c': + QE_ExpandNumber(data->current, args->result); + break; + + case 'p': + QE_ExpandNumber(data->prev, args->result); + break; + + default: + Percents_Any(args); + break; + } +} + +static void Percents_Scroll(QE_ExpandArgs *args) +{ + struct { + TreeCtrl *tree; + double lower; + double upper; + } *data = args->clientData; + + switch (args->which) + { + case 'l': + QE_ExpandDouble(data->lower, args->result); + break; + + case 'u': + QE_ExpandDouble(data->upper, args->result); + break; + + default: + Percents_Any(args); + break; + } +} + +int TreeNotifyCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandName[] = { "bind", "configure", "detailnames", + "eventnames", "generate", "install", "linkage", "uninstall", + (char *) NULL }; + enum { + COMMAND_BIND, COMMAND_CONFIGURE, COMMAND_DETAILNAMES, + COMMAND_EVENTNAMES, COMMAND_GENERATE, COMMAND_INSTALL, + COMMAND_LINKAGE, COMMAND_UNINSTALL + }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandName, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_BIND: + { + return QE_BindCmd(tree->bindingTable, 2, objc, objv); + } + + case COMMAND_CONFIGURE: + { + return QE_ConfigureCmd(tree->bindingTable, 2, objc, objv); + } + + /* T notify detailnames $eventName */ + case COMMAND_DETAILNAMES: + { + char *eventName; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "eventName"); + return TCL_ERROR; + } + eventName = Tcl_GetString(objv[3]); + return QE_GetDetailNames(tree->bindingTable, eventName); + } + + /* T notify eventnames */ + case COMMAND_EVENTNAMES: + { + if (objc != 3) + { + Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); + return TCL_ERROR; + } + return QE_GetEventNames(tree->bindingTable); + } + + case COMMAND_GENERATE: + { + return QE_GenerateCmd(tree->bindingTable, 2, objc, objv); + } + + case COMMAND_INSTALL: + { + return QE_InstallCmd(tree->bindingTable, 2, objc, objv); + } + + case COMMAND_LINKAGE: + { + return QE_LinkageCmd(tree->bindingTable, 2, objc, objv); + } + + case COMMAND_UNINSTALL: + { + return QE_UninstallCmd(tree->bindingTable, 2, objc, objv); + } + } + + return TCL_OK; +} + +void TreeNotify_OpenClose(TreeCtrl *tree, TreeItem item, int state, int before) +{ + QE_Event event; + struct { + TreeCtrl *tree; /* Must be first*/ + int id; + } data; + + data.tree = tree; + data.id = TreeItem_GetID(tree, item); + + if (state & STATE_OPEN) + { + event.type = EVENT_EXPAND; + event.detail = before ? DETAIL_EXPAND_BEFORE : DETAIL_EXPAND_AFTER; + } + else + { + event.type = EVENT_COLLAPSE; + event.detail = before ? DETAIL_COLLAPSE_BEFORE : DETAIL_COLLAPSE_AFTER; + } + event.clientData = (ClientData) &data; + (void) QE_BindEvent(tree->bindingTable, &event); +} + +void TreeNotify_Selection(TreeCtrl *tree, TreeItem select[], TreeItem deselect[]) +{ + QE_Event event; + struct { + TreeCtrl *tree; /* Must be first*/ + int *select; + int *deselect; + int count; + } data; + int staticS[20], staticD[20]; + int i; + + data.tree = tree; + + if (select == NULL) + data.select = NULL; + else + { + for (i = 0; select[i] != NULL; i++) + /* nothing */; + if (i < sizeof(staticS) / sizeof(staticS[0])) + data.select = staticS; + else + data.select = (int *) ckalloc(sizeof(int) * (i + 1)); + for (i = 0; select[i] != NULL; i++) + data.select[i] = TreeItem_GetID(tree, select[i]); + data.select[i] = -1; + } + + if (deselect == NULL) + data.deselect = NULL; + else + { + for (i = 0; deselect[i] != NULL; i++) + /* nothing */; + if (i < sizeof(staticD) / sizeof(staticD[0])) + data.deselect = staticD; + else + data.deselect = (int *) ckalloc(sizeof(int) * (i + 1)); + for (i = 0; deselect[i] != NULL; i++) + data.deselect[i] = TreeItem_GetID(tree, deselect[i]); + data.deselect[i] = -1; + } + + data.count = tree->selectCount; + + event.type = EVENT_SELECTION; + event.detail = 0; + event.clientData = (ClientData) &data; + + (void) QE_BindEvent(tree->bindingTable, &event); + + if ((select != NULL) && (data.select != staticS)) + ckfree((char *) data.select); + if ((deselect != NULL) && (data.deselect != staticD)) + ckfree((char *) data.deselect); +} + +void TreeNotify_ActiveItem(TreeCtrl *tree, TreeItem itemPrev, TreeItem itemCur) +{ + QE_Event event; + struct { + TreeCtrl *tree; /* Must be first*/ + int prev; + int current; + } data; + + data.tree = tree; + data.prev = TreeItem_GetID(tree, itemPrev); + data.current = TreeItem_GetID(tree, itemCur); + + event.type = EVENT_ACTIVEITEM; + event.detail = 0; + event.clientData = (ClientData) &data; + + (void) QE_BindEvent(tree->bindingTable, &event); +} + +void TreeNotify_Scroll(TreeCtrl *tree, double fractions[2], int vertical) +{ + QE_Event event; + struct { + TreeCtrl *tree; /* Must be first*/ + double lower; + double upper; + } data; + + data.tree = tree; + data.lower = fractions[0]; + data.upper = fractions[1]; + + event.type = EVENT_SCROLL; + event.detail = vertical ? DETAIL_SCROLL_Y : DETAIL_SCROLL_X; + event.clientData = (ClientData) &data; + + (void) QE_BindEvent(tree->bindingTable, &event); +} + +int TreeNotify_Init(TreeCtrl *tree) +{ + tree->bindingTable = QE_CreateBindingTable(tree->interp); + + EVENT_EXPAND = QE_InstallEvent(tree->bindingTable, "Expand", Percents_Expand); + DETAIL_EXPAND_BEFORE = QE_InstallDetail(tree->bindingTable, "before", EVENT_EXPAND, NULL); + DETAIL_EXPAND_AFTER = QE_InstallDetail(tree->bindingTable, "after", EVENT_EXPAND, NULL); + + EVENT_COLLAPSE = QE_InstallEvent(tree->bindingTable, "Collapse", Percents_Expand); + DETAIL_COLLAPSE_BEFORE = QE_InstallDetail(tree->bindingTable, "before", EVENT_COLLAPSE, NULL); + DETAIL_COLLAPSE_AFTER = QE_InstallDetail(tree->bindingTable, "after", EVENT_COLLAPSE, NULL); + + EVENT_SELECTION = QE_InstallEvent(tree->bindingTable, "Selection", Percents_Selection); + + EVENT_ACTIVEITEM = QE_InstallEvent(tree->bindingTable, "ActiveItem", Percents_ActiveItem); + + EVENT_SCROLL = QE_InstallEvent(tree->bindingTable, "Scroll", Percents_Scroll); + DETAIL_SCROLL_X = QE_InstallDetail(tree->bindingTable, "x", EVENT_SCROLL, NULL); + DETAIL_SCROLL_Y = QE_InstallDetail(tree->bindingTable, "y", EVENT_SCROLL, NULL); + + return TCL_OK; +} + diff --git a/generic/tkTreeStyle.c b/generic/tkTreeStyle.c new file mode 100644 index 0000000..08dbee4 --- /dev/null +++ b/generic/tkTreeStyle.c @@ -0,0 +1,3652 @@ +#include "tkTreeCtrl.h" +#include "tkTreeElem.h" + +typedef struct Style Style; +typedef struct ElementLink ElementLink; + +struct Style +{ + Tk_Uid name; + int numElements; + ElementLink *elements; + int neededWidth; + int neededHeight; + int minWidth; + int minHeight; + int layoutWidth; + int layoutHeight; + Style *master; + int vertical; +}; + +#define ELF_eEXPAND_W 0x0001 /* external expansion */ +#define ELF_eEXPAND_N 0x0002 +#define ELF_eEXPAND_E 0x0004 +#define ELF_eEXPAND_S 0x0008 +#define ELF_iEXPAND_W 0x0010 /* internal expansion */ +#define ELF_iEXPAND_N 0x0020 +#define ELF_iEXPAND_E 0x0040 +#define ELF_iEXPAND_S 0x0080 +#define ELF_SQUEEZE_X 0x0100 /* shrink if needed */ +#define ELF_SQUEEZE_Y 0x0200 +#define ELF_DETACH 0x0400 + +#define ELF_eEXPAND_WE (ELF_eEXPAND_W | ELF_eEXPAND_E) +#define ELF_eEXPAND_NS (ELF_eEXPAND_N | ELF_eEXPAND_S) +#define ELF_eEXPAND (ELF_eEXPAND_WE | ELF_eEXPAND_NS) +#define ELF_iEXPAND_WE (ELF_iEXPAND_W | ELF_iEXPAND_E) +#define ELF_iEXPAND_NS (ELF_iEXPAND_N | ELF_iEXPAND_S) +#define ELF_iEXPAND (ELF_iEXPAND_WE | ELF_iEXPAND_NS) +#define ELF_EXPAND_WE (ELF_eEXPAND_WE | ELF_iEXPAND_WE) +#define ELF_EXPAND_NS (ELF_eEXPAND_NS | ELF_iEXPAND_NS) +#define ELF_EXPAND_W (ELF_eEXPAND_W | ELF_iEXPAND_W) +#define ELF_EXPAND_N (ELF_eEXPAND_N | ELF_iEXPAND_N) +#define ELF_EXPAND_E (ELF_eEXPAND_E | ELF_iEXPAND_E) +#define ELF_EXPAND_S (ELF_eEXPAND_S | ELF_iEXPAND_S) + +struct ElementLink +{ + Element *elem; + int neededWidth; + int neededHeight; + int layoutWidth; + int layoutHeight; + int ePad[4]; /* external padding */ + int iPad[4]; /* internal padding */ + int flags; /* ELF_xxx */ + int *onion, onionCount; /* -union option info */ +}; + +static ElementType *elementTypeList = NULL; + +static char *orientStringTable[] = { "horizontal", "vertical", (char *) NULL }; + +static Tk_OptionSpec styleOptionSpecs[] = { + {TK_OPTION_STRING_TABLE, "-orient", (char *) NULL, (char *) NULL, + "horizontal", -1, Tk_Offset(Style, vertical), + 0, (ClientData) orientStringTable, 0}, + {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, + (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} +}; + +static Tk_OptionTable styleOptionTable = NULL; + +struct Layout +{ + ElementLink *eLink; + ElementLink *master; + int useWidth; + int useHeight; + int x; /* left of ePad */ + int y; /* above ePad */ + int eWidth; /* ePad + iPad + useWidth + iPad + ePad */ + int eHeight; /* ePad + iPad + useHeight + iPad + ePad */ + int iWidth; /* iPad + useWidth + iPad */ + int iHeight; /* iPad + useHeight + iPad */ + int ePad[4]; /* external padding */ + int iPad[4]; /* internal padding */ + int uPad[4]; /* padding due to -union */ +}; + +static void Style_DoExpandH(struct Layout *layout, int flags, int width) +{ + int extraWidth; + int *ePad, *iPad; + int eW, eE, iW, iE, eLeft, eRight, iLeft, iRight, eMax, iMax; + + if (!(flags & ELF_EXPAND_WE)) + return; + + extraWidth = width - layout->eWidth; + if (extraWidth <= 0) + return; + + /* External: can expand to left and right */ + eW = 0; + eE = width; + eLeft = layout->x; + eRight = width - (layout->x + layout->eWidth); + eMax = eLeft + eRight; + + /* Internal: can expand to max of ePad[] or uPad[] */ + iW = MAX(layout->ePad[LEFT], layout->uPad[LEFT]); + iE = width - MAX(layout->ePad[RIGHT], layout->uPad[RIGHT]); + iLeft = layout->x + layout->ePad[LEFT] - iW; + iRight = iE - (layout->x + layout->eWidth - layout->ePad[RIGHT]); + iMax = iLeft + iRight; + + ePad = layout->ePad; + iPad = layout->iPad; + + /* Internal expansion */ + if (flags & ELF_iEXPAND_WE) + { + if ((flags & ELF_iEXPAND_WE) == ELF_iEXPAND_WE) + { + iPad[LEFT] += MIN(iMax / 2, iLeft); + layout->x = iW - ePad[LEFT]; + layout->iWidth += iMax; + layout->eWidth += iMax; + iPad[RIGHT] = layout->iWidth - layout->eLink->neededWidth - iPad[LEFT]; + } + else if (flags & ELF_iEXPAND_W) + { + layout->x = iW - ePad[LEFT]; + layout->iWidth += iMax; + layout->eWidth += iMax; + iPad[LEFT] = layout->iWidth - layout->eLink->neededWidth - iPad[RIGHT]; + } + else + { + layout->x = iW - ePad[LEFT]; + layout->iWidth += iMax; + layout->eWidth += iMax; + iPad[RIGHT] = layout->iWidth - layout->eLink->neededWidth - iPad[LEFT]; + } + return; + } + + /* External expansion */ + if (flags & ELF_eEXPAND_WE) + { + if ((flags & ELF_eEXPAND_WE) == ELF_eEXPAND_WE) + { + int amt = extraWidth / 2; + + layout->x = 0; + layout->eWidth = width; + if (ePad[LEFT] + amt + layout->iWidth > iE) + amt -= (ePad[LEFT] + amt + layout->iWidth) - iE; + ePad[LEFT] += amt; + ePad[RIGHT] += extraWidth - amt; + } + else if (flags & ELF_eEXPAND_W) + { + layout->x = 0; + layout->eWidth = iE + ePad[RIGHT]; + ePad[LEFT] = layout->eWidth - layout->iWidth - ePad[RIGHT]; + } + else + { + layout->x = iW - ePad[LEFT]; + layout->eWidth = width - layout->x; + ePad[RIGHT] = layout->eWidth - layout->iWidth - ePad[LEFT]; + } + } +} + +static void Style_DoExpandV(struct Layout *layout, int flags, int height) +{ + int extraHeight; + int *ePad, *iPad; + int eN, eS, iN, iS, eAbove, eBelow, iAbove, iBelow, eMax, iMax; + + if (!(flags & ELF_EXPAND_NS)) + return; + + extraHeight = height - layout->eHeight; + if (extraHeight <= 0) + return; + + ePad = layout->ePad; + iPad = layout->iPad; + + /* External: can expand to top and bottom */ + eN = 0; + eS = height; + eAbove = layout->y; + eBelow = height - (layout->y + layout->eHeight); + eMax = eAbove + eBelow; + + /* Internal: can expand to max of ePad[] or uPad[] */ + iN = MAX(ePad[TOP], layout->uPad[TOP]); + iS = height - MAX(ePad[BOTTOM], layout->uPad[BOTTOM]); + iAbove = layout->y + ePad[TOP] - iN; + iBelow = iS - (layout->y + layout->eHeight - ePad[BOTTOM]); + iMax = iAbove + iBelow; + + /* Internal expansion */ + if (flags & ELF_iEXPAND_NS) + { + if ((flags & ELF_iEXPAND_NS) == ELF_iEXPAND_NS) + { + iPad[TOP] += MIN(iMax / 2, iAbove); + layout->y = iN - ePad[TOP]; + layout->iHeight += iMax; + layout->eHeight += iMax; + iPad[BOTTOM] = layout->iHeight - layout->eLink->neededHeight - iPad[TOP]; + } + else if (flags & ELF_iEXPAND_N) + { + layout->y = iN - ePad[TOP]; + layout->iHeight += iMax; + layout->eHeight += iMax; + iPad[TOP] = layout->iHeight - layout->eLink->neededHeight - iPad[BOTTOM]; + } + else + { + layout->y = iN - ePad[TOP]; + layout->iHeight += iMax; + layout->eHeight += iMax; + iPad[BOTTOM] = layout->iHeight - layout->eLink->neededHeight - iPad[TOP]; + } + return; + } + + /* External expansion */ + if (flags & ELF_eEXPAND_NS) + { + if ((flags & ELF_eEXPAND_NS) == ELF_eEXPAND_NS) + { + int amt = extraHeight / 2; + + layout->y = 0; + layout->eHeight = height; + if (ePad[TOP] + amt + layout->iHeight > iS) + amt -= (ePad[TOP] + amt + layout->iHeight) - iS; + ePad[TOP] += amt; + ePad[BOTTOM] += extraHeight - amt; + } + else if (flags & ELF_eEXPAND_N) + { + layout->y = 0; + layout->eHeight = iS + ePad[BOTTOM]; + ePad[TOP] = layout->eHeight - layout->iHeight - ePad[BOTTOM]; + } + else + { + layout->y = iN - ePad[TOP]; + layout->eHeight = height - layout->y; + ePad[BOTTOM] = layout->eHeight - layout->iHeight - ePad[TOP]; + } + } +} + +static int Style_DoLayoutH(StyleDrawArgs *drawArgs, struct Layout layouts[20]) +{ + Style *style = (Style *) drawArgs->style; + Style *masterStyle = style->master; + ElementLink *eLinks1, *eLinks2, *eLink1, *eLink2; + int x = 0; + int w, e; + int *ePad, *iPad, *uPad; + int numExpandWE = 0; + int numSqueezeX = 0; + int i, j, k, eLinkCount = 0; + + eLinks1 = masterStyle->elements; + eLinks2 = style->elements; + eLinkCount = style->numElements; + + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + /* Width before squeezing */ + layout->useWidth = eLink2->neededWidth; + + /* No -union padding yet */ + for (j = 0; j < 4; j++) + layout->uPad[j] = 0; + + /* Count all non-union, non-detach squeezeable items */ + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + if (eLink1->flags & ELF_SQUEEZE_X) + numSqueezeX++; + } + + /* Calculate the padding around elements surrounded by -union elements */ + for (i = 0; i < eLinkCount; i++) + { + eLink1 = &eLinks1[i]; + + if (eLink1->onion == NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + + for (j = 0; j < eLink1->onionCount; j++) + { + struct Layout *layout = &layouts[eLink1->onion[j]]; + + uPad = layout->uPad; + for (k = 0; k < 4; k++) + uPad[k] = MAX(uPad[k], iPad[k] + ePad[k]); + } + } + + /* Left-to-right layout. Make the width of some elements less than they + * need */ + if (!masterStyle->vertical && + (drawArgs->width < style->neededWidth) && + (numSqueezeX > 0)) + { + int extraWidth = (style->neededWidth - drawArgs->width) / numSqueezeX; + /* Possible extra pixels */ + int fudge = (style->neededWidth - drawArgs->width) - extraWidth * numSqueezeX; + int seen = 0; + + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + if (!(eLink1->flags & ELF_SQUEEZE_X)) + continue; + + /* Subtract extra pixels from right-most element */ + if (++seen == numSqueezeX) + extraWidth += fudge; + + layout->useWidth -= extraWidth; + } + } + + /* Reduce the width of all non-union elements, except for the + * cases handled above. */ + if (drawArgs->width < style->neededWidth) + { + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if (eLink1->onion != NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + if ((eLink1->flags & ELF_SQUEEZE_X) && + ((eLink1->flags & ELF_DETACH) || + masterStyle->vertical)) + { + int width = + MAX(ePad[LEFT], uPad[LEFT]) + + iPad[LEFT] + layout->useWidth + iPad[RIGHT] + + MAX(ePad[RIGHT], uPad[RIGHT]); + if (width > drawArgs->width) + layout->useWidth -= (width - drawArgs->width); + } + } + } + + /* Layout elements left-to-right */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + layout->eLink = eLink2; + layout->master = eLink1; + layout->x = MAX(x, abs(ePad[LEFT] - MAX(ePad[LEFT], uPad[LEFT]))); + layout->iWidth = iPad[LEFT] + layout->useWidth + iPad[RIGHT]; + layout->eWidth = ePad[LEFT] + layout->iWidth + ePad[RIGHT]; + for (j = 0; j < 4; j++) + { + layout->ePad[j] = ePad[j]; + layout->iPad[j] = iPad[j]; + } + + if (!masterStyle->vertical) + x = layout->x + layout->eWidth; + + /* Count number that want to expand */ + if (eLink1->flags & ELF_EXPAND_WE) + numExpandWE++; + } + + /* Left-to-right layout. Expand some elements horizontally if we have + * more space available horizontally than is needed by the Style. */ + if (!masterStyle->vertical && + (drawArgs->width > style->neededWidth) && + (numExpandWE > 0)) + { + int extraWidth = (drawArgs->width - style->neededWidth) / numExpandWE; + /* Possible extra pixels */ + int fudge = (drawArgs->width - style->neededWidth) - extraWidth * numExpandWE; + int eExtra, iExtra, seen = 0; + + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + if (!(eLink1->flags & ELF_EXPAND_WE)) + continue; + + /* Give extra pixels to right-most expander */ + if (++seen == numExpandWE) + extraWidth += fudge; + + /* Shift following elements to the right */ + for (j = i + 1; j < eLinkCount; j++) + if (layouts[j].eLink != NULL) + layouts[j].x += extraWidth; + + ePad = layout->ePad; + iPad = layout->iPad; + + /* External and internal expansion */ + if ((eLink1->flags & ELF_eEXPAND_WE) && (eLink1->flags & ELF_iEXPAND_WE)) + { + eExtra = extraWidth / 2; + iExtra = extraWidth - extraWidth / 2; + } + else + { + eExtra = extraWidth; + iExtra = extraWidth; + } + + /* External expansion */ + if (eLink1->flags & ELF_eEXPAND_WE) + { + if ((eLink1->flags & ELF_eEXPAND_WE) == ELF_eEXPAND_WE) + { + ePad[LEFT] += eExtra / 2; + ePad[RIGHT] += eExtra - eExtra / 2; + } + else if (eLink1->flags & ELF_eEXPAND_W) + ePad[LEFT] += eExtra; + else + ePad[RIGHT] += eExtra; + } + + /* Internal expansion */ + if (eLink1->flags & ELF_iEXPAND_WE) + { + if ((eLink1->flags & ELF_iEXPAND_WE) == ELF_iEXPAND_WE) + { + iPad[LEFT] += iExtra / 2; + iPad[RIGHT] += iExtra - iExtra / 2; + } + else if (eLink1->flags & ELF_iEXPAND_W) + iPad[LEFT] += iExtra; + else + iPad[RIGHT] += iExtra; + layout->iWidth += iExtra; + } + layout->eWidth += extraWidth; + } + } + + /* Top-to-bottom layout. Expand some elements horizontally */ + if (masterStyle->vertical && (numExpandWE > 0)) + { + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + Style_DoExpandH(layout, eLink1->flags, drawArgs->width); + } + } + + /* Now handle column justification */ + if (drawArgs->width > style->neededWidth) + { + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + switch (drawArgs->justify) + { + case TK_JUSTIFY_LEFT: + break; + case TK_JUSTIFY_RIGHT: + layout->x += drawArgs->width - style->neededWidth; + break; + case TK_JUSTIFY_CENTER: + layout->x += (drawArgs->width - style->neededWidth) / 2; + break; + } + } + } + + /* Position and expand -detach elements */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + layout->eLink = eLink2; + layout->master = eLink1; + layout->x = abs(ePad[LEFT] - MAX(ePad[LEFT], uPad[LEFT])); + layout->iWidth = iPad[LEFT] + layout->useWidth + iPad[RIGHT]; + layout->eWidth = ePad[LEFT] + layout->iWidth + ePad[RIGHT]; + for (j = 0; j < 4; j++) + { + layout->ePad[j] = ePad[j]; + layout->iPad[j] = iPad[j]; + } + + Style_DoExpandH(layout, eLink1->flags, drawArgs->width); + } + + /* Now calculate layout of -union elements. */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (eLink1->onion == NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + + w = 10000, e = -10000; + + for (j = 0; j < eLink1->onionCount; j++) + { + struct Layout *layout2 = &layouts[eLink1->onion[j]]; + + w = MIN(w, layout2->x + layout2->ePad[LEFT]); + e = MAX(e, layout2->x + layout2->ePad[LEFT] + layout2->iWidth); + } + + layout->eLink = eLink2; + layout->master = eLink1; + layout->x = w - iPad[LEFT] - ePad[LEFT]; + layout->iWidth = iPad[LEFT] + (e - w) + iPad[RIGHT]; + layout->eWidth = ePad[LEFT] + layout->iWidth + ePad[RIGHT]; + for (j = 0; j < 4; j++) + { + layout->ePad[j] = ePad[j]; + layout->iPad[j] = iPad[j]; + } + } + + /* Expand -union elements if needed: horizontal */ + /* Expansion of "-union" elements is different than non-"-union" elements */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + int extraWidth; + + eLink1 = &eLinks1[i]; + + if ((eLink1->onion == NULL) || !(eLink1->flags & ELF_EXPAND_WE)) + continue; + + if (drawArgs->width - layout->eWidth <= 0) + continue; + + /* External and internal expansion: W */ + extraWidth = layout->x; + if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_W)) + { + if ((eLink1->flags & ELF_EXPAND_W) == ELF_EXPAND_W) + { + int eExtra = extraWidth / 2; + int iExtra = extraWidth - extraWidth / 2; + + /* External expansion */ + layout->ePad[LEFT] += eExtra; + layout->x = 0; + layout->eWidth += extraWidth; + + /* Internal expansion */ + layout->iPad[LEFT] += iExtra; + layout->iWidth += iExtra; + } + + /* External expansion only: W */ + else if (eLink1->flags & ELF_eEXPAND_W) + { + layout->ePad[LEFT] += extraWidth; + layout->x = 0; + layout->eWidth += extraWidth; + } + + /* Internal expansion only: W */ + else + { + layout->iPad[LEFT] += extraWidth; + layout->x = 0; + layout->iWidth += extraWidth; + layout->eWidth += extraWidth; + } + } + + /* External and internal expansion: E */ + extraWidth = drawArgs->width - (layout->x + layout->eWidth); + if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_E)) + { + if ((eLink1->flags & ELF_EXPAND_E) == ELF_EXPAND_E) + { + int eExtra = extraWidth / 2; + int iExtra = extraWidth - extraWidth / 2; + + /* External expansion */ + layout->ePad[RIGHT] += eExtra; + layout->eWidth += extraWidth; /* all the space */ + + /* Internal expansion */ + layout->iPad[RIGHT] += iExtra; + layout->iWidth += iExtra; + } + + /* External expansion only: E */ + else if (eLink1->flags & ELF_eEXPAND_E) + { + layout->ePad[RIGHT] += extraWidth; + layout->eWidth += extraWidth; + } + + /* Internal expansion only: E */ + else + { + layout->iPad[RIGHT] += extraWidth; + layout->iWidth += extraWidth; + layout->eWidth += extraWidth; + } + } + } + + return eLinkCount; +} + +static int Style_DoLayoutV(StyleDrawArgs *drawArgs, struct Layout layouts[20]) +{ + Style *style = (Style *) drawArgs->style; + Style *masterStyle = style->master; + ElementLink *eLinks1, *eLinks2, *eLink1, *eLink2; + int y = 0; + int n, s; + int *ePad, *iPad, *uPad; + int numExpandNS = 0; + int numSqueezeY = 0; + int i, j, eLinkCount = 0; + + eLinks1 = masterStyle->elements; + eLinks2 = style->elements; + eLinkCount = style->numElements; + + for (i = 0; i < eLinkCount; i++) + { + eLink1 = &eLinks1[i]; + + /* Count all non-union, non-detach squeezeable items */ + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + if (eLink1->flags & ELF_SQUEEZE_Y) + numSqueezeY++; + } + + /* Top-top-bottom layout. Make the height of some elements less than they + * need */ + if (masterStyle->vertical && + (drawArgs->height < style->neededHeight) && + (numSqueezeY > 0)) + { + int extraHeight = (style->neededHeight - drawArgs->height) / numSqueezeY; + /* Possible extra pixels */ + int fudge = (style->neededHeight - drawArgs->height) - extraHeight * numSqueezeY; + int seen = 0; + + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + if (!(eLink1->flags & ELF_SQUEEZE_Y)) + continue; + + /* Subtract extra pixels from bottom-most element */ + if (++seen == numSqueezeY) + extraHeight += fudge; + + layout->useHeight -= extraHeight; + } + } + + /* Reduce the height of all non-union elements, except for the + * cases handled above. */ + if (drawArgs->height < style->neededHeight) + { + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if (eLink1->onion != NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + if ((eLink1->flags & ELF_SQUEEZE_Y) && + ((eLink1->flags & ELF_DETACH) || + !masterStyle->vertical)) + { + int height = + MAX(ePad[TOP], uPad[TOP]) + + iPad[TOP] + layout->useHeight + iPad[BOTTOM] + + MAX(ePad[BOTTOM], uPad[BOTTOM]); + if (height > drawArgs->height) + layout->useHeight -= (height - drawArgs->height); + } + } + } + + /* Layout elements top-to-bottom */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + layout->y = MAX(y, abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP]))); + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + + if (masterStyle->vertical) + y = layout->y + layout->eHeight; + + /* Count number that want to expand */ + if (eLink1->flags & ELF_EXPAND_NS) + numExpandNS++; + } + + /* Top-to-bottom layout. Expand some elements vertically if we have + * more space available vertically than is needed by the Style. */ + if (masterStyle->vertical && + (drawArgs->height > style->neededHeight) && + (numExpandNS > 0)) + { + int extraHeight = (drawArgs->height - style->neededHeight) / numExpandNS; + /* Possible extra pixels */ + int fudge = (drawArgs->height - style->neededHeight) - extraHeight * numExpandNS; + int eExtra, iExtra, seen = 0; + + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + if (!(eLink1->flags & ELF_EXPAND_NS)) + continue; + + /* Give extra pixels to bottom-most expander */ + if (++seen == numExpandNS) + extraHeight += fudge; + + /* Shift following elements down */ + for (j = i + 1; j < eLinkCount; j++) + if (layouts[j].eLink != NULL) + layouts[j].y += extraHeight; + + ePad = layout->ePad; + iPad = layout->iPad; + + /* External and internal expansion */ + if ((eLink1->flags & ELF_eEXPAND_NS) && (eLink1->flags & ELF_iEXPAND_NS)) + { + eExtra = extraHeight / 2; + iExtra = extraHeight - extraHeight / 2; + } + else + { + eExtra = extraHeight; + iExtra = extraHeight; + } + + /* External expansion */ + if (eLink1->flags & ELF_eEXPAND_NS) + { + if ((eLink1->flags & ELF_eEXPAND_NS) == ELF_eEXPAND_NS) + { + ePad[TOP] += eExtra / 2; + ePad[BOTTOM] += eExtra - eExtra / 2; + } + else if (eLink1->flags & ELF_eEXPAND_N) + ePad[TOP] += eExtra; + else + ePad[BOTTOM] += eExtra; + } + + /* Internal expansion */ + if (eLink1->flags & ELF_iEXPAND_NS) + { + if ((eLink1->flags & ELF_iEXPAND_NS) == ELF_iEXPAND_NS) + { + iPad[TOP] += iExtra / 2; + iPad[BOTTOM] += iExtra - iExtra / 2; + } + else if (eLink1->flags & ELF_iEXPAND_N) + iPad[TOP] += iExtra; + else + iPad[BOTTOM] += iExtra; + layout->iHeight += iExtra; + } + layout->eHeight += extraHeight; + } + } + + /* Left-to-right layout. Expand some elements vertically */ + if (!masterStyle->vertical && (numExpandNS > 0)) + { + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + + if ((eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + Style_DoExpandV(layout, eLinks1[i].flags, drawArgs->height); + } + } + + /* Position and expand -detach elements */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + layout->y = abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP])); + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + + Style_DoExpandV(layout, eLink1->flags, drawArgs->height); + } + + /* Now calculate layout of -union elements. */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (eLink1->onion == NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + + n = 10000, s = -10000; + + for (j = 0; j < eLink1->onionCount; j++) + { + struct Layout *layout2 = &layouts[eLink1->onion[j]]; + + n = MIN(n, layout2->y + layout2->ePad[TOP]); + s = MAX(s, layout2->y + layout2->ePad[TOP] + layout2->iHeight); + } + + layout->y = n - iPad[TOP] - ePad[TOP]; + layout->iHeight = iPad[TOP] + (s - n) + iPad[BOTTOM]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + } + + /* Expand -union elements if needed: vertical */ + for (i = 0; i < eLinkCount; i++) + { + struct Layout *layout = &layouts[i]; + int extraHeight; + + eLink1 = &eLinks1[i]; + + if ((eLink1->onion == NULL) || !(eLink1->flags & ELF_EXPAND_NS)) + continue; + + if (drawArgs->height - layout->eHeight <= 0) + continue; + + /* External and internal expansion: N */ + extraHeight = layout->y; + if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_N)) + { + if ((eLink1->flags & ELF_EXPAND_N) == ELF_EXPAND_N) + { + int eExtra = extraHeight / 2; + int iExtra = extraHeight - extraHeight / 2; + + /* External expansion */ + layout->ePad[TOP] += eExtra; + layout->y = 0; + layout->eHeight += extraHeight; + + /* Internal expansion */ + layout->iPad[TOP] += iExtra; + layout->iHeight += iExtra; + } + + /* External expansion only: N */ + else if (eLink1->flags & ELF_eEXPAND_N) + { + layout->ePad[TOP] += extraHeight; + layout->y = 0; + layout->eHeight += extraHeight; + } + + /* Internal expansion only: N */ + else + { + layout->iPad[TOP] += extraHeight; + layout->y = 0; + layout->iHeight += extraHeight; + layout->eHeight += extraHeight; + } + } + + /* External and internal expansion: S */ + extraHeight = drawArgs->height - (layout->y + layout->eHeight); + if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_S)) + { + if ((eLink1->flags & ELF_EXPAND_S) == ELF_EXPAND_S) + { + int eExtra = extraHeight / 2; + int iExtra = extraHeight - extraHeight / 2; + + /* External expansion */ + layout->ePad[BOTTOM] += eExtra; + layout->eHeight += extraHeight; /* all the space */ + + /* Internal expansion */ + layout->iPad[BOTTOM] += iExtra; + layout->iHeight += iExtra; + } + + /* External expansion only: S */ + else if (eLink1->flags & ELF_eEXPAND_S) + { + layout->ePad[BOTTOM] += extraHeight; + layout->eHeight += extraHeight; + } + + /* Internal expansion only */ + else + { + layout->iPad[BOTTOM] += extraHeight; + layout->iHeight += extraHeight; + layout->eHeight += extraHeight; + } + } + } + + return eLinkCount; +} + +/* Calculate the height and width of all the Elements */ +static void Layout_Size(int vertical, int numLayouts, struct Layout layouts[20], int *widthPtr, int *heightPtr) +{ + int i, W, N, E, S; + int width = 0, height = 0; + + W = 10000, N = 10000, E = -10000, S = -10000; + + for (i = 0; i < numLayouts; i++) + { + struct Layout *layout = &layouts[i]; + int w, n, e, s; + + w = layout->x + layout->ePad[LEFT] - MAX(layout->ePad[LEFT], layout->uPad[LEFT]); + n = layout->y + layout->ePad[TOP] - MAX(layout->ePad[TOP], layout->uPad[TOP]); + e = layout->x + layout->eWidth - layout->ePad[RIGHT] + MAX(layout->ePad[RIGHT], layout->uPad[RIGHT]); + s = layout->y + layout->eHeight - layout->ePad[BOTTOM] + MAX(layout->ePad[BOTTOM], layout->uPad[BOTTOM]); + + if (vertical) + { + N = MIN(N, n); + S = MAX(S, s); + width = MAX(width, e - w); + } + else + { + W = MIN(W, w); + E = MAX(E, e); + height = MAX(height, s - n); + } + } + + if (vertical) + height = MAX(height, S - N); + else + width = MAX(width, E - W); + + (*widthPtr) = width; + (*heightPtr) = height; +} + +/* */ +void Style_DoLayoutNeededV(StyleDrawArgs *drawArgs, struct Layout layouts[20]) +{ + Style *style = (Style *) drawArgs->style; + Style *masterStyle = style->master; + ElementLink *eLinks1, *eLinks2, *eLink1, *eLink2; + int *ePad, *iPad, *uPad; + int i; + int y = 0; + + eLinks1 = masterStyle->elements; + eLinks2 = style->elements; + + /* Layout elements left-to-right, or top-to-bottom */ + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + /* The size of a -union element is determined by the elements + * it surrounds */ + if (eLink1->onion != NULL) + continue; + + /* -detached items are positioned by themselves */ + if (eLink1->flags & ELF_DETACH) + continue; + + layout->y = MAX(y, abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP]))); + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + + if (masterStyle->vertical) + y = layout->y + layout->eHeight; + } + + /* -detached elements */ + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + layout->y = abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP])); + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + } +} + +/* Arrange all the Elements considering drawArgs.width but not drawArgs.height */ +static void Style_DoLayout2(StyleDrawArgs *drawArgs, struct Layout layouts[20]) +{ + TreeCtrl *tree = drawArgs->tree; + Style *style = (Style *) drawArgs->style; + int state = drawArgs->state; + int i; + + if (style->neededWidth == -1) panic("Style_DoLayout2: style.neededWidth == -1"); + if (style->minWidth > drawArgs->width) panic("Style_DoLayout2: style.minWidth %d > drawArgs.width %d", style->minWidth, drawArgs->width); + + Style_DoLayoutH(drawArgs, layouts); + + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + ElementLink *eLink = &style->elements[i]; + + layout->useHeight = eLink->neededHeight; + + if (eLink->elem->typePtr == &elemTypeText) + { + if (layout->iWidth != eLink->layoutWidth) + { + ElementArgs args; + + args.tree = tree; + args.state = state; + args.elem = eLink->elem; + args.layout.width = layout->iWidth; + (*args.elem->typePtr->layoutProc)(&args); + + eLink->layoutWidth = layout->iWidth; + eLink->layoutHeight = args.layout.height; + } + layout->useHeight = eLink->layoutHeight; + } + } + + Style_DoLayoutNeededV(drawArgs, layouts); +} + +/* Arrange all the Elements considering drawArgs.width and drawArgs.height */ +static void Style_DoLayout(StyleDrawArgs *drawArgs, struct Layout layouts[20], char *function) +{ + TreeCtrl *tree = drawArgs->tree; + Style *style = (Style *) drawArgs->style; + int state = drawArgs->state; + int i; + + if (style->neededWidth == -1) panic("Style_DoLayout(from %s): style.neededWidth == -1", function); + if (style->minWidth > drawArgs->width) panic("Style_DoLayout: style.minWidth %d > drawArgs.width %d", style->minWidth, drawArgs->width); + + Style_DoLayoutH(drawArgs, layouts); + + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + ElementLink *eLink = &style->elements[i]; + + layout->useHeight = eLink->neededHeight; + + if (eLink->elem->typePtr == &elemTypeText) + { + if (layout->iWidth != eLink->layoutWidth) + { + ElementArgs args; + + args.tree = tree; + args.state = state; + args.elem = eLink->elem; + args.layout.width = layout->iWidth; + (*args.elem->typePtr->layoutProc)(&args); + + eLink->layoutWidth = layout->iWidth; + eLink->layoutHeight = args.layout.height; + } + layout->useHeight = eLink->layoutHeight; + } + } + + Style_DoLayoutV(drawArgs, layouts); +} + +/* Arrange Elements to determine the needed height and width of the Style */ +static void Style_NeededSize(TreeCtrl *tree, Style *style, int state, int *widthPtr, int *heightPtr, int squeeze) +{ + Style *masterStyle = style->master; + ElementLink *eLinks1, *eLinks2, *eLink1, *eLink2; + struct Layout layouts[20]; + int *ePad, *iPad, *uPad; + int i, j, k; + int x = 0, y = 0; + + if (style->master != NULL) + { + eLinks1 = masterStyle->elements; + eLinks2 = style->elements; + } + else + { + eLinks1 = masterStyle->elements; + eLinks2 = masterStyle->elements; + } + + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + /* No -union padding yet */ + for (j = 0; j < 4; j++) + layout->uPad[j] = 0; + } + + /* Figure out the padding around elements surrounded by -union elements */ + for (i = 0; i < style->numElements; i++) + { + eLink1 = &eLinks1[i]; + + if (eLink1->onion == NULL) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + + for (j = 0; j < eLink1->onionCount; j++) + { + struct Layout *layout = &layouts[eLink1->onion[j]]; + + uPad = layout->uPad; + for (k = 0; k < 4; k++) + uPad[k] = MAX(uPad[k], iPad[k] + ePad[k]); + } + } + + /* Layout elements left-to-right, or top-to-bottom */ + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + /* The size of a -union element is determined by the elements + * it surrounds */ + if (eLink1->onion != NULL) + { + layout->x = layout->y = layout->eWidth = layout->eHeight = 0; + for (j = 0; j < 4; j++) + layout->ePad[j] = layout->iPad[j] = 0; + continue; + } + + if ((eLink2->neededWidth == -1) || (eLink2->neededHeight == -1)) + { + ElementArgs args; + + args.tree = tree; + args.state = state; + args.elem = eLink2->elem; + args.layout.width = -1; + (*args.elem->typePtr->layoutProc)(&args); + eLink2->neededWidth = args.layout.width; + eLink2->neededHeight = args.layout.height; + + eLink2->layoutWidth = -1; + eLink2->layoutHeight = args.layout.height; + } + + layout->useWidth = eLink2->neededWidth; + layout->useHeight = eLink2->neededHeight; + if (squeeze) + { + if (eLink1->flags & ELF_SQUEEZE_X) + layout->useWidth = 0; + if (eLink1->flags & ELF_SQUEEZE_Y) + layout->useHeight = 0; + } + + /* -detached items are positioned by themselves */ + if (eLink1->flags & ELF_DETACH) + continue; + + layout->eLink = eLink2; + layout->x = MAX(x, abs(ePad[LEFT] - MAX(ePad[LEFT], uPad[LEFT]))); + layout->y = MAX(y, abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP]))); + layout->iWidth = iPad[LEFT] + layout->useWidth + iPad[RIGHT]; + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eWidth = ePad[LEFT] + layout->iWidth + ePad[RIGHT]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + for (j = 0; j < 4; j++) + { + layout->ePad[j] = ePad[j]; + layout->iPad[j] = iPad[j]; + } + + if (masterStyle->vertical) + y = layout->y + layout->eHeight; + else + x = layout->x + layout->eWidth; + } + + /* -detached elements */ + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + eLink1 = &eLinks1[i]; + eLink2 = &eLinks2[i]; + + if (!(eLink1->flags & ELF_DETACH) || (eLink1->onion != NULL)) + continue; + + ePad = eLink1->ePad; + iPad = eLink1->iPad; + uPad = layout->uPad; + + layout->eLink = eLink2; + layout->master = eLink1; + layout->x = abs(ePad[LEFT] - MAX(ePad[LEFT], uPad[LEFT])); + layout->y = abs(ePad[TOP] - MAX(ePad[TOP], uPad[TOP])); + layout->iWidth = iPad[LEFT] + layout->useWidth + iPad[RIGHT]; + layout->iHeight = iPad[TOP] + layout->useHeight + iPad[BOTTOM]; + layout->eWidth = ePad[LEFT] + layout->iWidth + ePad[RIGHT]; + layout->eHeight = ePad[TOP] + layout->iHeight + ePad[BOTTOM]; + for (j = 0; j < 4; j++) + { + layout->ePad[j] = ePad[j]; + layout->iPad[j] = iPad[j]; + } + } + + Layout_Size(masterStyle->vertical, style->numElements, layouts, + widthPtr, heightPtr); + + memset(layouts, 0xAA, sizeof(layouts)); /* debug */ +} + +int TreeStyle_NeededWidth(TreeCtrl *tree, TreeStyle style, int state) +{ + int width, height; + + Style_NeededSize(tree, (Style *) style, state, &width, &height, FALSE); + return width; +} + +int TreeStyle_NeededHeight(TreeCtrl *tree, TreeStyle style, int state) +{ + int width, height; + + Style_NeededSize(tree, (Style *) style, state, &width, &height, FALSE); + return height; +} + +/* Calculate height of Style considering drawArgs.width */ +int TreeStyle_UseHeight(StyleDrawArgs *drawArgs) +{ + TreeCtrl *tree = drawArgs->tree; + Style *style = (Style *) drawArgs->style; + int state = drawArgs->state; + int layoutWidth = drawArgs->width; + int height; + + if (style->neededWidth == -1) + { + Style_NeededSize(tree, style, state, &style->neededWidth, + &style->neededHeight, FALSE); + Style_NeededSize(tree, style, state, &style->minWidth, + &style->minHeight, TRUE); + + style->layoutWidth = -1; + } + + if ((layoutWidth == -1) || + (layoutWidth >= style->neededWidth) || + (style->neededWidth == style->minWidth)) + { + height = style->neededHeight; + } + + else if (layoutWidth <= style->minWidth) + { + height = style->minHeight; + } + + else if (layoutWidth == style->layoutWidth) + { + height = style->layoutHeight; + } + + else + { + struct Layout layouts[20]; + int width; + + Style_DoLayout2(drawArgs, layouts); + + Layout_Size(style->master->vertical, style->numElements, layouts, + &width, &height); + + memset(layouts, 0xAA, sizeof(layouts)); /* debug */ + + style->layoutHeight = height; + } + + return height; +} + +void TreeStyle_Draw(StyleDrawArgs *drawArgs) +{ + Style *style = (Style *) drawArgs->style; + TreeCtrl *tree = drawArgs->tree; + ElementArgs args; + int i; + struct Layout layouts[20]; + int debugDraw = FALSE; + + if (drawArgs->width < style->minWidth) + drawArgs->width = style->minWidth; + if (drawArgs->height < style->minHeight) + drawArgs->height = style->minHeight; + + Style_DoLayout(drawArgs, layouts, __FUNCTION__); + + args.tree = tree; + args.state = drawArgs->state; + args.display.drawable = drawArgs->drawable; + + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + if (debugDraw && layout->master->onion != NULL) + continue; + + if ((layout->iWidth > 0) && (layout->iHeight > 0)) + { + args.elem = layout->eLink->elem; + args.display.x = drawArgs->x + layout->x + layout->ePad[LEFT]; + args.display.y = drawArgs->y + layout->y + layout->ePad[TOP]; + args.display.width = layout->iWidth; + args.display.height = layout->iHeight; + args.display.pad[LEFT] = layout->iPad[LEFT]; + args.display.pad[TOP] = layout->iPad[TOP]; + args.display.pad[RIGHT] = layout->iPad[RIGHT]; + args.display.pad[BOTTOM] = layout->iPad[BOTTOM]; + if (debugDraw) + { + XColor *color[3]; + GC gc[3]; + + if (layout->master->onion != NULL) + { + color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2"); + gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin)); + color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3"); + gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin)); + } + else + { + color[0] = Tk_GetColor(tree->interp, tree->tkwin, "gray50"); + gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin)); + color[1] = Tk_GetColor(tree->interp, tree->tkwin, "gray60"); + gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin)); + color[2] = Tk_GetColor(tree->interp, tree->tkwin, "gray70"); + gc[2] = Tk_GCForColor(color[2], Tk_WindowId(args.tree->tkwin)); + } + + /* external */ + XFillRectangle(tree->display, args.display.drawable, + gc[2], + args.display.x - layout->ePad[LEFT], + args.display.y - layout->ePad[TOP], + layout->eWidth, layout->eHeight); + /* internal */ + XFillRectangle(tree->display, args.display.drawable, + gc[1], + args.display.x, args.display.y, + args.display.width, args.display.height); + /* needed */ + if (!layout->master->onion && !(layout->master->flags & ELF_DETACH)) + XFillRectangle(tree->display, args.display.drawable, + gc[0], + args.display.x + layout->iPad[LEFT], + args.display.y + layout->iPad[TOP], + layout->eLink->neededWidth, layout->eLink->neededHeight); + } + else + (*args.elem->typePtr->displayProc)(&args); + } + } + + if (debugDraw) + for (i = 0; i < style->numElements; i++) + { + struct Layout *layout = &layouts[i]; + + if (layout->master->onion == NULL) + continue; + if (layout->iWidth > 0 && layout->iHeight > 0) + { + args.elem = layout->eLink->elem; + args.display.x = drawArgs->x + layout->x + layout->ePad[LEFT]; + args.display.y = drawArgs->y + layout->y + layout->ePad[TOP]; + args.display.width = layout->iWidth; + args.display.height = layout->iHeight; + args.display.pad[LEFT] = layout->iPad[LEFT]; + args.display.pad[TOP] = layout->iPad[TOP]; + args.display.pad[RIGHT] = layout->iPad[RIGHT]; + args.display.pad[BOTTOM] = layout->iPad[BOTTOM]; + { + XColor *color[3]; + GC gc[3]; + + color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2"); + gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin)); + color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3"); + gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin)); + + /* external */ + XDrawRectangle(tree->display, args.display.drawable, + gc[0], + args.display.x - layout->ePad[LEFT], + args.display.y - layout->ePad[TOP], + layout->eWidth - 1, layout->eHeight - 1); + /* internal */ + XDrawRectangle(tree->display, args.display.drawable, + gc[1], + args.display.x, args.display.y, + args.display.width - 1, args.display.height - 1); + } + } + } + + memset(layouts, 0xAA, sizeof(layouts)); /* debug */ +} + +static void Element_FreeResources(TreeCtrl *tree, Element *elem) +{ + ElementArgs args; + Tcl_HashEntry *hPtr; + + if (elem->master == NULL) + { + hPtr = Tcl_FindHashEntry(&tree->elementHash, elem->name); + Tcl_DeleteHashEntry(hPtr); + } + args.tree = tree; + args.elem = elem; + (*elem->typePtr->deleteProc)(&args); + Tk_FreeConfigOptions((char *) elem, + elem->typePtr->optionTable, + tree->tkwin); + WFREE(elem, Element); +} + +static ElementLink *ElementLink_Init(ElementLink *eLink, Element *elem) +{ + memset(eLink, '\0', sizeof(ElementLink)); + eLink->elem = elem; + return eLink; +} + +static void ElementLink_FreeResources(TreeCtrl *tree, ElementLink *eLink) +{ + if (eLink->elem->master != NULL) + Element_FreeResources(tree, eLink->elem); + if (eLink->onion != NULL) + wipefree((char *) eLink->onion, sizeof(int) * eLink->onionCount); +} + +void TreeStyle_FreeResources(TreeCtrl *tree, TreeStyle style_) +{ + Style *style = (Style *) style_; + int i; + Tcl_HashEntry *hPtr; + + if (style->master == NULL) + { + hPtr = Tcl_FindHashEntry(&tree->styleHash, style->name); + Tcl_DeleteHashEntry(hPtr); + } + if (style->numElements > 0) + { + for (i = 0; i < style->numElements; i++) + ElementLink_FreeResources(tree, &style->elements[i]); + wipefree((char *) style->elements, sizeof(ElementLink) * + style->numElements); + } + WFREE(style, Style); +} + +static ElementLink *Style_FindElem(TreeCtrl *tree, Style *style, Element *master, int *index) +{ + int i; + + for (i = 0; i < style->numElements; i++) + { + if (style->elements[i].elem->name == master->name) + { + if (index != NULL) (*index) = i; + return &style->elements[i]; + } + } + + return NULL; +} + +/* Create an instance Element if it doesn't exist in this Style */ +static ElementLink *Style_CreateElem(TreeCtrl *tree, Style *style, Element *masterElem, int *isNew) +{ + ElementLink *eLink = NULL; + Element *elem; + ElementArgs args; + int i; + + if (masterElem->master != NULL) + panic("Style_CreateElem called with instance Element"); + + if (isNew != NULL) (*isNew) = FALSE; + + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + + if (eLink->elem == masterElem) + { + /* Master Styles use master Elements only */ + if (style->master == NULL) + return eLink; + + /* Allocate instance Element here */ + break; + } + + /* Instance Style already has instance Element */ + if (eLink->elem->name == masterElem->name) + return eLink; + } + + /* Error: Element isn't in the master Style */ + if (i == style->numElements) + return NULL; + + elem = (Element *) ckalloc(masterElem->typePtr->size); + memset(elem, '\0', masterElem->typePtr->size); + elem->typePtr = masterElem->typePtr; + elem->name = masterElem->name; + elem->master = masterElem; + args.tree = tree; + args.elem = elem; + + /* FIXME: free memory if these calls could actually fail */ + if ((*elem->typePtr->createProc)(&args) != TCL_OK) + return NULL; + + if (Tk_InitOptions(tree->interp, (char *) elem, + elem->typePtr->optionTable, tree->tkwin) != TCL_OK) + return NULL; + + args.config.objc = 0; + args.config.flagSelf = 0; + if ((*elem->typePtr->configProc)(&args) != TCL_OK) + return NULL; + + args.change.flagSelf = args.config.flagSelf; + args.change.flagTree = 0; + args.change.flagMaster = 0; + (*elem->typePtr->changeProc)(&args); + + eLink->elem = elem; + if (isNew != NULL) (*isNew) = TRUE; + return eLink; +} + +TreeStyle TreeStyle_NewInstance(TreeCtrl *tree, TreeStyle style_) +{ + Style *style = (Style *) style_; + Style *copy; + ElementLink *eLink; + int i; + + copy = (Style *) ckalloc(sizeof(Style)); + memset(copy, '\0', sizeof(Style)); + copy->name = style->name; + copy->neededWidth = -1; + copy->neededHeight = -1; + copy->master = style; + copy->numElements = style->numElements; + if (style->numElements > 0) + { + copy->elements = (ElementLink *) ckalloc(sizeof(ElementLink) * style->numElements); + memset(copy->elements, '\0', sizeof(ElementLink) * style->numElements); + for (i = 0; i < style->numElements; i++) + { + eLink = ©->elements[i]; + + /* The only fields needed by an instance */ + eLink->elem = style->elements[i].elem; + eLink->neededWidth = -1; + eLink->neededHeight = -1; + } + } + + return (TreeStyle) copy; +} + +static int Element_FromObj(TreeCtrl *tree, Tcl_Obj *obj, Element **elemPtr) +{ + char *name; + Tcl_HashEntry *hPtr; + + name = Tcl_GetString(obj); + hPtr = Tcl_FindHashEntry(&tree->elementHash, name); + if (hPtr == NULL) + { + Tcl_AppendResult(tree->interp, "element \"", name, "\" doesn't exist", + NULL); + return TCL_ERROR; + } + (*elemPtr) = (Element *) Tcl_GetHashValue(hPtr); + return TCL_OK; +} + +int TreeStyle_FromObj(TreeCtrl *tree, Tcl_Obj *obj, TreeStyle *stylePtr) +{ + char *name; + Tcl_HashEntry *hPtr; + + name = Tcl_GetString(obj); + hPtr = Tcl_FindHashEntry(&tree->styleHash, name); + if (hPtr == NULL) + { + Tcl_AppendResult(tree->interp, "style \"", name, "\" doesn't exist", + NULL); + return TCL_ERROR; + } + (*stylePtr) = (TreeStyle) Tcl_GetHashValue(hPtr); + return TCL_OK; +} + +static Tcl_Obj *Element_ToObj(Element *elem) +{ + return Tcl_NewStringObj(elem->name, -1); +} + +Tcl_Obj *TreeStyle_ToObj(TreeStyle style_) +{ + Style *style = (Style *) style_; + + return Tcl_NewStringObj(style->name, -1); +} + +static void Style_Changed(TreeCtrl *tree, Style *masterStyle) +{ + TreeItem item; + TreeItemColumn column; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + int columnIndex, layout; + int updateDInfo = FALSE; + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + column = TreeItem_GetFirstColumn(tree, item); + columnIndex = 0; + layout = FALSE; + while (column != NULL) + { + Style *style = (Style *) TreeItemColumn_GetStyle(tree, column); + if ((style != NULL) && (style->master == masterStyle)) + { + style->neededWidth = style->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItemColumn_InvalidateSize(tree, column); + layout = TRUE; + } + columnIndex++; + column = TreeItemColumn_GetNext(tree, column); + } + if (layout) + { + TreeItem_InvalidateHeight(tree, item); + Tree_InvalidateItemDInfo(tree, item, NULL); + updateDInfo = TRUE; + } + hPtr = Tcl_NextHashEntry(&search); + } + if (updateDInfo) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); +} + +static void Style_ChangeElementsAux(TreeCtrl *tree, Style *style, int count, Element **elemList, int *map) +{ + ElementLink *eLink, *eLinks = NULL; + int i, keep[20]; + + if (count > 0) + eLinks = (ElementLink *) ckalloc(sizeof(ElementLink) * count); + + /* Assume we are discarding all the old ElementLinks */ + for (i = 0; i < style->numElements; i++) + keep[i] = 0; + + for (i = 0; i < count; i++) + { + if (map[i] != -1) + { + eLinks[i] = style->elements[map[i]]; + keep[map[i]] = 1; + } + else + { + eLink = ElementLink_Init(&eLinks[i], elemList[i]); + eLink->neededWidth = eLink->neededHeight = -1; + } + } + + if (style->numElements > 0) + { + /* Free unused ElementLinks */ + for (i = 0; i < style->numElements; i++) + { + if (!keep[i]) + ElementLink_FreeResources(tree, &style->elements[i]); + } + wipefree((char *) style->elements, sizeof(ElementLink) * + style->numElements); + } + + style->elements = eLinks; + style->numElements = count; +} + +static void Style_ChangeElements(TreeCtrl *tree, Style *masterStyle, int count, Element **elemList, int *map) +{ + TreeItem item; + TreeItemColumn column; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + int columnIndex, layout; + int updateDInfo = FALSE; + int i, j, k; + + /* Update -union lists */ + for (i = 0; i < masterStyle->numElements; i++) + { + ElementLink *eLink = &masterStyle->elements[i]; + int keep[20], onionCnt = 0, *onion = NULL; + + if (eLink->onion == NULL) + continue; + + /* Check every Element in this -union */ + for (j = 0; j < eLink->onionCount; j++) + { + ElementLink *eLink2 = &masterStyle->elements[eLink->onion[j]]; + + /* Check the new list of Elements */ + keep[j] = -1; + for (k = 0; k < count; k++) + { + /* This new Element is in the -union */ + if (elemList[k] == eLink2->elem) + { + keep[j] = k; + onionCnt++; + break; + } + } + } + + if (onionCnt > 0) + { + if (onionCnt != eLink->onionCount) + onion = (int *) ckalloc(sizeof(int) * onionCnt); + else + onion = eLink->onion; + k = 0; + for (j = 0; j < eLink->onionCount; j++) + { + if (keep[j] != -1) + onion[k++] = keep[j]; + } + } + if (onionCnt != eLink->onionCount) + { + wipefree((char *) eLink->onion, sizeof(int) * eLink->onionCount); + eLink->onion = onion; + eLink->onionCount = onionCnt; + } + } + + Style_ChangeElementsAux(tree, masterStyle, count, elemList, map); + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + column = TreeItem_GetFirstColumn(tree, item); + columnIndex = 0; + layout = FALSE; + while (column != NULL) + { + Style *style = (Style *) TreeItemColumn_GetStyle(tree, column); + if ((style != NULL) && (style->master == masterStyle)) + { + Style_ChangeElementsAux(tree, style, count, elemList, map); + style->neededWidth = style->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItemColumn_InvalidateSize(tree, column); + layout = TRUE; + } + columnIndex++; + column = TreeItemColumn_GetNext(tree, column); + } + if (layout) + { + TreeItem_InvalidateHeight(tree, item); + Tree_InvalidateItemDInfo(tree, item, NULL); + updateDInfo = TRUE; + } + hPtr = Tcl_NextHashEntry(&search); + } + if (updateDInfo) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); +} + +static void Style_ElemChanged(TreeCtrl *tree, Style *masterStyle, + Element *masterElem, int flagM, int flagT, int csM) +{ + TreeItem item; + TreeItemColumn column; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + ElementLink *eLink; + int i, columnIndex; + ElementArgs args; + int eMask, iMask; + int updateDInfo = FALSE; + + args.tree = tree; + args.change.flagTree = flagT; + args.change.flagMaster = flagM; + args.change.flagSelf = 0; + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + column = TreeItem_GetFirstColumn(tree, item); + columnIndex = 0; + iMask = 0; + while (column != NULL) + { + Style *style = (Style *) TreeItemColumn_GetStyle(tree, column); + if ((style != NULL) && (style->master == masterStyle)) + { + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + if (eLink->elem == masterElem) + { + if (csM & CS_LAYOUT) + eLink->neededWidth = eLink->neededHeight = -1; + break; + } + /* Instance element */ + if (eLink->elem->master == masterElem) + { + args.elem = eLink->elem; + eMask = (*masterElem->typePtr->changeProc)(&args); + if (eMask & CS_LAYOUT) + eLink->neededWidth = eLink->neededHeight = -1; + iMask |= eMask; + break; + } + } + iMask |= csM; + if (iMask & CS_LAYOUT) + { + style->neededWidth = style->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItemColumn_InvalidateSize(tree, column); + } + } + columnIndex++; + column = TreeItemColumn_GetNext(tree, column); + } + if (iMask & CS_LAYOUT) + { + TreeItem_InvalidateHeight(tree, item); + updateDInfo = TRUE; + } + if (iMask & CS_DISPLAY) + Tree_InvalidateItemDInfo(tree, item, NULL); + hPtr = Tcl_NextHashEntry(&search); + } + if (updateDInfo) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); +} + +TreeStyle TreeStyle_GetMaster(TreeCtrl *tree, TreeStyle style_) +{ + return (TreeStyle) ((Style *) style_)->master; +} + +static Tcl_Obj *confTextObj = NULL; + +Tcl_Obj *TreeStyle_GetText(TreeCtrl *tree, TreeStyle style_) +{ + Style *style = (Style *) style_; + ElementLink *eLink; + int i; + + if (confTextObj == NULL) + { + confTextObj = Tcl_NewStringObj("-text", -1); + Tcl_IncrRefCount(confTextObj); + } + + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + if (eLink->elem->typePtr == &elemTypeText) + { + Tcl_Obj *resultObjPtr; + resultObjPtr = Tk_GetOptionValue(tree->interp, + (char *) eLink->elem, eLink->elem->typePtr->optionTable, + confTextObj, tree->tkwin); + return resultObjPtr; + } + } + + return NULL; +} + +void TreeStyle_SetText(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *textObj) +{ + Style *style = (Style *) style_; + Style *masterStyle = style->master; + ElementLink *eLink; + int i; + + if (masterStyle == NULL) + panic("TreeStyle_SetText called with master Style"); + + if (confTextObj == NULL) + { + confTextObj = Tcl_NewStringObj("-text", -1); + Tcl_IncrRefCount(confTextObj); + } + + for (i = 0; i < style->numElements; i++) + { + eLink = &masterStyle->elements[i]; + if (eLink->elem->typePtr == &elemTypeText) + { + Tcl_Obj *objv[2]; + ElementArgs args; + + eLink = Style_CreateElem(tree, style, eLink->elem, NULL); + objv[0] = confTextObj; + objv[1] = textObj; + args.tree = tree; + args.elem = eLink->elem; + args.config.objc = 2; + args.config.objv = objv; + args.config.flagSelf = 0; + (void) (*eLink->elem->typePtr->configProc)(&args); + + args.change.flagSelf = args.config.flagSelf; + args.change.flagTree = 0; + args.change.flagMaster = 0; + (*eLink->elem->typePtr->changeProc)(&args); + + eLink->neededWidth = eLink->neededHeight = -1; + style->neededWidth = style->neededHeight = -1; + return; + } + } +} + +static void Style_Deleted(TreeCtrl *tree, Style *masterStyle) +{ + TreeItem item; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + TreeItemColumn column; + int columnIndex; + + if (masterStyle->master != NULL) + panic("Style_Deleted called with instance Style"); + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + column = TreeItem_GetFirstColumn(tree, item); + columnIndex = 0; + while (column != NULL) + { + Style *style = (Style *) TreeItemColumn_GetStyle(tree, column); + if ((style != NULL) && ((style == masterStyle) || (style->master == masterStyle))) + { + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItemColumn_ForgetStyle(tree, column); + TreeItem_InvalidateHeight(tree, item); + Tree_FreeItemDInfo(tree, item, NULL); + } + columnIndex++; + column = TreeItemColumn_GetNext(tree, column); + } + hPtr = Tcl_NextHashEntry(&search); + } +} + +static void Element_Changed(TreeCtrl *tree, Element *masterElem, int flagM, int flagT, int csM) +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Style *masterStyle; + ElementLink *eLink; + int i; + + hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search); + while (hPtr != NULL) + { + masterStyle = (Style *) Tcl_GetHashValue(hPtr); + for (i = 0; i < masterStyle->numElements; i++) + { + eLink = &masterStyle->elements[i]; + if (eLink->elem == masterElem) + { + Style_ElemChanged(tree, masterStyle, masterElem, flagM, flagT, csM); + break; + } + } + hPtr = Tcl_NextHashEntry(&search); + } +} + +void TreeStyle_TreeChanged(TreeCtrl *tree, int flagT) +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Element *masterElem; + ElementArgs args; + int eMask; + + if (flagT == 0) + return; + + args.tree = tree; + args.change.flagTree = flagT; + args.change.flagMaster = 0; + args.change.flagSelf = 0; + + hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search); + while (hPtr != NULL) + { + masterElem = (Element *) Tcl_GetHashValue(hPtr); + args.elem = masterElem; + eMask = (*masterElem->typePtr->changeProc)(&args); + Element_Changed(tree, masterElem, 0, flagT, eMask); + hPtr = Tcl_NextHashEntry(&search); + } +} + +int TreeStyle_ElementCget(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *elemObj, Tcl_Obj *obj) +{ + Style *style = (Style *) style_; + Tcl_Obj *resultObjPtr = NULL; + Element *elem; + ElementLink *eLink; + + if (Element_FromObj(tree, elemObj, &elem) != TCL_OK) + return TCL_ERROR; + + eLink = Style_FindElem(tree, style, elem, NULL); + if ((eLink != NULL) && (eLink->elem == elem) && (style->master != NULL)) + eLink = NULL; + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + style->name, elem->name); + return TCL_ERROR; + } + + resultObjPtr = Tk_GetOptionValue(tree->interp, (char *) eLink->elem, + eLink->elem->typePtr->optionTable, obj, tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(tree->interp, resultObjPtr); + return TCL_OK; +} + +int TreeStyle_ElementConfigure(TreeCtrl *tree, TreeStyle style_, Tcl_Obj *elemObj, int objc, Tcl_Obj **objv) +{ + Style *style = (Style *) style_; + Element *elem; + ElementLink *eLink; + ElementArgs args; + + if (Element_FromObj(tree, elemObj, &elem) != TCL_OK) + return TCL_ERROR; + + if (objc <= 1) + { + Tcl_Obj *resultObjPtr; + + eLink = Style_FindElem(tree, style, elem, NULL); + if ((eLink != NULL) && (eLink->elem == elem) && (style->master != NULL)) + eLink = NULL; + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + style->name, elem->name); + return TCL_ERROR; + } + + resultObjPtr = Tk_GetOptionInfo(tree->interp, (char *) eLink->elem, + eLink->elem->typePtr->optionTable, + (objc == 0) ? (Tcl_Obj *) NULL : objv[0], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(tree->interp, resultObjPtr); + } + else + { + eLink = Style_CreateElem(tree, style, elem, NULL); + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + style->name, elem->name); + return TCL_ERROR; + } + + /* Do this before configProc(). If eLink was just allocated and an + * error occurs in configProc() it won't be done */ + eLink->neededWidth = eLink->neededHeight = -1; + style->neededWidth = style->neededHeight = -1; + + args.tree = tree; + args.elem = eLink->elem; + args.config.objc = objc; + args.config.objv = objv; + args.config.flagSelf = 0; + if ((*args.elem->typePtr->configProc)(&args) != TCL_OK) + return TCL_ERROR; + + args.change.flagSelf = args.config.flagSelf; + args.change.flagTree = 0; + args.change.flagMaster = 0; + (*elem->typePtr->changeProc)(&args); + } + return TCL_OK; +} + +int TreeStyle_ElementActual(TreeCtrl *tree, TreeStyle style_, int state, Tcl_Obj *elemObj, Tcl_Obj *obj) +{ + Style *style = (Style *) style_; + Element *masterElem; + ElementLink *eLink; + ElementArgs args; + + if (Element_FromObj(tree, elemObj, &masterElem) != TCL_OK) + return TCL_ERROR; + + eLink = Style_FindElem(tree, style, masterElem, NULL); + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + style->name, masterElem->name); + return TCL_ERROR; + } +\ + args.tree = tree; + args.elem = eLink->elem; + args.state = state; + args.actual.obj = obj; + return (*masterElem->typePtr->actualProc)(&args); +} + +static Element *Element_CreateAndConfig(TreeCtrl *tree, ElementType *type, char *name, int objc, Tcl_Obj *CONST objv[]) +{ + Element *elem; + ElementArgs args; + + elem = (Element *) ckalloc(type->size); + memset(elem, '\0', type->size); + elem->name = Tk_GetUid(name); + elem->typePtr = type; + args.tree = tree; + args.elem = elem; + if ((*type->createProc)(&args) != TCL_OK) + return NULL; + + if (Tk_InitOptions(tree->interp, (char *) elem, + type->optionTable, tree->tkwin) != TCL_OK) + { + WFREE(elem, Element); + return NULL; + } + args.config.objc = objc; + args.config.objv = objv; + args.config.flagSelf = 0; + if ((*type->configProc)(&args) != TCL_OK) + { + (*type->deleteProc)(&args); + Tk_FreeConfigOptions((char *) elem, + elem->typePtr->optionTable, + tree->tkwin); + WFREE(elem, Element); + return NULL; + } + + args.change.flagSelf = args.config.flagSelf; + args.change.flagTree = 0; + args.change.flagMaster = 0; + (*type->changeProc)(&args); + + return elem; +} + +int TreeElementCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "cget", "configure", "create", "delete", + "names", "type", (char *) NULL }; + enum { COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE, + COMMAND_NAMES, COMMAND_TYPE }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_CGET: + { + Tcl_Obj *resultObjPtr = NULL; + Element *elem; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "name option"); + return TCL_ERROR; + } + if (Element_FromObj(tree, objv[3], &elem) != TCL_OK) + return TCL_ERROR; + resultObjPtr = Tk_GetOptionValue(interp, (char *) elem, + elem->typePtr->optionTable, objv[4], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + case COMMAND_CONFIGURE: + { + Tcl_Obj *resultObjPtr = NULL; + Element *elem; + int eMask; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value...?"); + return TCL_ERROR; + } + if (Element_FromObj(tree, objv[3], &elem) != TCL_OK) + return TCL_ERROR; + if (objc <= 5) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) elem, + elem->typePtr->optionTable, + (objc == 4) ? (Tcl_Obj *) NULL : objv[4], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + } + else + { + ElementArgs args; + + args.tree = tree; + args.elem = elem; + args.config.objc = objc - 4; + args.config.objv = objv + 4; + args.config.flagSelf = 0; + if ((*elem->typePtr->configProc)(&args) != TCL_OK) + return TCL_ERROR; + + args.change.flagSelf = args.config.flagSelf; + args.change.flagTree = 0; + args.change.flagMaster = 0; + eMask = (*elem->typePtr->changeProc)(&args); + + Element_Changed(tree, elem, args.change.flagSelf, 0, eMask); + } + break; + } + + case COMMAND_CREATE: + { + char *name, *typeStr; + int length; + int isNew; + Element *elem; + ElementType *typePtr, *matchPtr = NULL; + Tcl_HashEntry *hPtr; + + if (objc < 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "name type ?option value...?"); + return TCL_ERROR; + } + name = Tcl_GetStringFromObj(objv[3], &length); + if (!length) + return TCL_ERROR; + hPtr = Tcl_FindHashEntry(&tree->elementHash, name); + if (hPtr != NULL) + { + FormatResult(interp, "element \"%s\" already exists", name); + return TCL_ERROR; + } + typeStr = Tcl_GetStringFromObj(objv[4], &length); + if (!length) + { + FormatResult(interp, + "invalid element type \"\""); + return TCL_ERROR; + } + for (typePtr = elementTypeList; + typePtr != NULL; + typePtr = typePtr->next) + { + if ((typeStr[0] == typePtr->name[0]) && + !strncmp(typeStr, typePtr->name, length)) + { + if (matchPtr != NULL) + { + FormatResult(interp, + "ambiguous element type \"%s\"", + typeStr); + return TCL_ERROR; + } + matchPtr = typePtr; + } + } + if (matchPtr == NULL) + { + FormatResult(interp, "unknown element type \"%s\"", typeStr); + return TCL_ERROR; + } + typePtr = matchPtr; + elem = Element_CreateAndConfig(tree, typePtr, name, objc - 5, objv + 5); + if (elem == NULL) + return TCL_ERROR; + hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew); + Tcl_SetHashValue(hPtr, elem); + Tcl_SetObjResult(interp, Element_ToObj(elem)); + break; + } + + case COMMAND_DELETE: + { + Element *elem; + int i; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "?name ...?"); + return TCL_ERROR; + } + for (i = 3; i < objc; i++) + { + if (Element_FromObj(tree, objv[i], &elem) != TCL_OK) + return TCL_ERROR; + /* TODO: Mass changes to any Style using this Element */ + Element_FreeResources(tree, elem); + } + break; + } + + case COMMAND_NAMES: + { + Tcl_Obj *listObj; + Tcl_HashSearch search; + Tcl_HashEntry *hPtr; + Element *elem; + + listObj = Tcl_NewListObj(0, NULL); + hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search); + while (hPtr != NULL) + { + elem = (Element *) Tcl_GetHashValue(hPtr); + Tcl_ListObjAppendElement(interp, listObj, Element_ToObj(elem)); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_SetObjResult(interp, listObj); + break; + } + + case COMMAND_TYPE: + { + Element *elem; + + if (objc != 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "name"); + return TCL_ERROR; + } + if (Element_FromObj(tree, objv[3], &elem) != TCL_OK) + return TCL_ERROR; + Tcl_SetResult(interp, elem->typePtr->name, TCL_STATIC); /* Tk_Uid */ + break; + } + } + return TCL_OK; +} + +static Style *Style_CreateAndConfig(TreeCtrl *tree, char *name, int objc, Tcl_Obj *CONST objv[]) +{ + Style *style; + + style = (Style *) ckalloc(sizeof(Style)); + memset(style, '\0', sizeof(Style)); + style->name = Tk_GetUid(name); + + if (Tk_InitOptions(tree->interp, (char *) style, + styleOptionTable, tree->tkwin) != TCL_OK) + { + WFREE(style, Style); + return NULL; + } + + if (Tk_SetOptions(tree->interp, (char *) style, + styleOptionTable, objc, objv, tree->tkwin, + NULL, NULL) != TCL_OK) + { + Tk_FreeConfigOptions((char *) style, styleOptionTable, tree->tkwin); + WFREE(style, Style); + return NULL; + } + + return style; +} + +void TreeStyle_ListElements(TreeCtrl *tree, TreeStyle style_) +{ + Style *style = (Style *) style_; + Tcl_Obj *listObj; + ElementLink *eLink; + int i; + + if (style->numElements <= 0) + return; + + listObj = Tcl_NewListObj(0, NULL); + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + if ((style->master != NULL) && (eLink->elem->master == NULL)) + continue; + Tcl_ListObjAppendElement(tree->interp, listObj, + Element_ToObj(eLink->elem)); + } + Tcl_SetObjResult(tree->interp, listObj); +} + +static int StyleLayoutCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + Style *style; + Element *elem; + ElementLink *eLink; + int i, index, pad; + static CONST char *optionNames[] = { "-padw", "-padn", "-pade", + "-pads", "-ipadw", "-ipadn", "-ipade", + "-ipads", "-expand", "-union", "-detach", "-iexpand", + "-squeeze", (char *) NULL }; + enum { OPTION_PADLEFT, OPTION_PADTOP, OPTION_PADRIGHT, + OPTION_PADBOTTOM, OPTION_iPADLEFT, OPTION_iPADTOP, + OPTION_iPADRIGHT, OPTION_iPADBOTTOM, OPTION_EXPAND, + OPTION_UNION, OPTION_DETACH, OPTION_iEXPAND, OPTION_SQUEEZE }; + + if (objc < 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "name element ?option? ?value? ?option value ...?"); + return TCL_ERROR; + } + + if (TreeStyle_FromObj(tree, objv[3], (TreeStyle *) &style) != TCL_OK) + return TCL_ERROR; + + if (Element_FromObj(tree, objv[4], &elem) != TCL_OK) + return TCL_ERROR; + + eLink = Style_FindElem(tree, style, elem, NULL); + if (eLink == NULL) + { + FormatResult(interp, "style %s does not use element %s", + style->name, elem->name); + return TCL_ERROR; + } + + /* T style layout S E */ + if (objc == 5) + { + char flags[4]; + int n; + Tcl_Obj *listObj = Tcl_NewListObj(0, NULL); + Tcl_Obj *unionObj = Tcl_NewListObj(0, NULL); + + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-padw", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->ePad[LEFT])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-padn", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->ePad[TOP])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-pade", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->ePad[RIGHT])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-pads", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->ePad[BOTTOM])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-ipadw", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->iPad[LEFT])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-ipadn", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->iPad[TOP])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-ipade", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->iPad[RIGHT])); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-ipads", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewIntObj(eLink->iPad[BOTTOM])); + + n = 0; + if (eLink->flags & ELF_eEXPAND_W) flags[n++] = 'w'; + if (eLink->flags & ELF_eEXPAND_N) flags[n++] = 'n'; + if (eLink->flags & ELF_eEXPAND_E) flags[n++] = 'e'; + if (eLink->flags & ELF_eEXPAND_S) flags[n++] = 's'; + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-expand", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(flags, n)); + + n = 0; + if (eLink->flags & ELF_iEXPAND_W) flags[n++] = 'w'; + if (eLink->flags & ELF_iEXPAND_N) flags[n++] = 'n'; + if (eLink->flags & ELF_iEXPAND_E) flags[n++] = 'e'; + if (eLink->flags & ELF_iEXPAND_S) flags[n++] = 's'; + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-iexpand", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(flags, n)); + + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-detach", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj((eLink->flags & ELF_DETACH) ? "yes" : "no", -1)); + + n = 0; + if (eLink->flags & ELF_SQUEEZE_X) flags[n++] = 'x'; + if (eLink->flags & ELF_SQUEEZE_Y) flags[n++] = 'y'; + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-squeeze", -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(flags, n)); + + for (i = 0; i < eLink->onionCount; i++) + Tcl_ListObjAppendElement(interp, unionObj, + Element_ToObj(style->elements[eLink->onion[i]].elem)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("-union", -1)); + Tcl_ListObjAppendElement(interp, listObj, unionObj); + + Tcl_SetObjResult(interp, listObj); + return TCL_OK; + } + + /* T style layout S E option */ + if (objc == 6) + { + Tcl_Obj *objPtr = NULL; + + if (Tcl_GetIndexFromObj(interp, objv[5], optionNames, "option", + 0, &index) != TCL_OK) + return TCL_ERROR; + switch (index) + { + case OPTION_PADLEFT: + case OPTION_PADTOP: + case OPTION_PADRIGHT: + case OPTION_PADBOTTOM: + { + objPtr = Tcl_NewIntObj(eLink->ePad[index - OPTION_PADLEFT]); + break; + } + case OPTION_iPADLEFT: + case OPTION_iPADTOP: + case OPTION_iPADRIGHT: + case OPTION_iPADBOTTOM: + { + objPtr = Tcl_NewIntObj(eLink->iPad[index - OPTION_iPADLEFT]); + break; + } + case OPTION_DETACH: + { + objPtr = Tcl_NewBooleanObj(eLink->flags & ELF_DETACH); + break; + } + case OPTION_EXPAND: + { + char flags[4]; + int n = 0; + + if (eLink->flags & ELF_eEXPAND_W) flags[n++] = 'w'; + if (eLink->flags & ELF_eEXPAND_N) flags[n++] = 'n'; + if (eLink->flags & ELF_eEXPAND_E) flags[n++] = 'e'; + if (eLink->flags & ELF_eEXPAND_S) flags[n++] = 's'; + if (n) + objPtr = Tcl_NewStringObj(flags, n); + break; + } + case OPTION_iEXPAND: + { + char flags[4]; + int n = 0; + + if (eLink->flags & ELF_iEXPAND_W) flags[n++] = 'w'; + if (eLink->flags & ELF_iEXPAND_N) flags[n++] = 'n'; + if (eLink->flags & ELF_iEXPAND_E) flags[n++] = 'e'; + if (eLink->flags & ELF_iEXPAND_S) flags[n++] = 's'; + if (n) + objPtr = Tcl_NewStringObj(flags, n); + break; + } + case OPTION_SQUEEZE: + { + char flags[2]; + int n = 0; + + if (eLink->flags & ELF_SQUEEZE_X) flags[n++] = 'x'; + if (eLink->flags & ELF_SQUEEZE_Y) flags[n++] = 'y'; + if (n) + objPtr = Tcl_NewStringObj(flags, n); + break; + } + case OPTION_UNION: + { + int i; + + if (eLink->onionCount == 0) + break; + objPtr = Tcl_NewListObj(0, NULL); + for (i = 0; i < eLink->onionCount; i++) + Tcl_ListObjAppendElement(interp, objPtr, + Element_ToObj(style->elements[eLink->onion[i]].elem)); + break; + } + } + if (objPtr != NULL) + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + + for (i = 5; i < objc; i += 2) + { + if (i + 2 > objc) + { + FormatResult(interp, "value for \"%s\" missing", + Tcl_GetString(objv[i])); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option", + 0, &index) != TCL_OK) + { + return TCL_ERROR; + } + switch (index) + { + case OPTION_PADLEFT: + case OPTION_PADTOP: + case OPTION_PADRIGHT: + case OPTION_PADBOTTOM: + { + if (Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1], &pad) != TCL_OK) + return TCL_ERROR; + eLink->ePad[index - OPTION_PADLEFT] = pad; + break; + } + case OPTION_iPADLEFT: + case OPTION_iPADTOP: + case OPTION_iPADRIGHT: + case OPTION_iPADBOTTOM: + { + if (Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1], &pad) != TCL_OK) + return TCL_ERROR; + eLink->iPad[index - OPTION_iPADLEFT] = pad; + break; + } + case OPTION_DETACH: + { + int detach; + if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &detach) != TCL_OK) + return TCL_ERROR; + if (detach) + eLink->flags |= ELF_DETACH; + else + eLink->flags &= ~ELF_DETACH; + break; + } + case OPTION_EXPAND: + { + char *expand; + int len, k; + expand = Tcl_GetStringFromObj(objv[i + 1], &len); + eLink->flags &= ~ELF_eEXPAND; + for (k = 0; k < len; k++) + { + switch (expand[k]) + { + case 'w': eLink->flags |= ELF_eEXPAND_W; break; + case 'n': eLink->flags |= ELF_eEXPAND_N; break; + case 'e': eLink->flags |= ELF_eEXPAND_E; break; + case 's': eLink->flags |= ELF_eEXPAND_S; break; + } + } + break; + } + case OPTION_iEXPAND: + { + char *expand; + int len, k; + expand = Tcl_GetStringFromObj(objv[i + 1], &len); + eLink->flags &= ~ELF_iEXPAND; + for (k = 0; k < len; k++) + { + switch (expand[k]) + { + case 'w': eLink->flags |= ELF_iEXPAND_W; break; + case 'n': eLink->flags |= ELF_iEXPAND_N; break; + case 'e': eLink->flags |= ELF_iEXPAND_E; break; + case 's': eLink->flags |= ELF_iEXPAND_S; break; + } + } + break; + } + case OPTION_SQUEEZE: + { + char *string; + int len, k; + string = Tcl_GetStringFromObj(objv[i + 1], &len); + eLink->flags &= ~(ELF_SQUEEZE_X | ELF_SQUEEZE_Y); + for (k = 0; k < len; k++) + { + switch (string[k]) + { + case 'x': eLink->flags |= ELF_SQUEEZE_X; break; + case 'y': eLink->flags |= ELF_SQUEEZE_Y; break; + } + } + break; + } + case OPTION_UNION: + { + int objc1; + Tcl_Obj **objv1; + int j, k, n, *onion, count = 0; + + if (Tcl_ListObjGetElements(interp, objv[i + 1], + &objc1, &objv1) != TCL_OK) + return TCL_ERROR; + if (objc1 == 0) + { + if (eLink->onion != NULL) + { + wipefree((char *) eLink->onion, + sizeof(int) * eLink->onionCount); + eLink->onionCount = 0; + eLink->onion = NULL; + } + break; + } + onion = (int *) ckalloc(sizeof(int) * objc1); + for (j = 0; j < objc1; j++) + { + Element *elem2; + ElementLink *eLink2; + + if (Element_FromObj(tree, objv1[j], &elem2) != TCL_OK) + return TCL_ERROR; + + eLink2 = Style_FindElem(tree, style, elem2, &n); + if (eLink2 == NULL) + { + ckfree((char *) onion); + FormatResult(interp, + "style %s does not use element %s", + style->name, elem2->name); + return TCL_ERROR; + } + if (eLink == eLink2) + { + ckfree((char *) onion); + FormatResult(interp, + "element %s can't form union with itself", + elem2->name); + return TCL_ERROR; + } + /* Silently ignore duplicates */ + for (k = 0; k < count; k++) + { + if (onion[k] == n) + break; + } + if (k < count) + continue; + onion[count++] = n; + } + if (eLink->onion != NULL) + wipefree((char *) eLink->onion, + sizeof(int) * eLink->onionCount); + if (count == objc1) + eLink->onion = onion; + else + { + eLink->onion = (int *) ckalloc(sizeof(int) * count); + for (k = 0; k < count; k++) + eLink->onion[k] = onion[k]; + ckfree((char *) onion); + } + eLink->onionCount = count; + break; + } + } + } + Style_Changed(tree, style); + return TCL_OK; +} + +int TreeStyleCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) +{ + TreeCtrl *tree = (TreeCtrl *) clientData; + static CONST char *commandNames[] = { "cget", "configure", "create", "delete", + "elements", "layout", "names", (char *) NULL }; + enum { COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE, + COMMAND_ELEMENTS, COMMAND_LAYOUT, COMMAND_NAMES }; + int index; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg...?"); + return TCL_ERROR; + } + + if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, + &index) != TCL_OK) + { + return TCL_ERROR; + } + + switch (index) + { + case COMMAND_CGET: + { + Tcl_Obj *resultObjPtr; + Style *style; + + if (objc != 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "name option"); + return TCL_ERROR; + } + if (TreeStyle_FromObj(tree, objv[3], (TreeStyle *) &style) != TCL_OK) + return TCL_ERROR; + resultObjPtr = Tk_GetOptionValue(interp, (char *) style, + styleOptionTable, objv[4], tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + break; + } + + case COMMAND_CONFIGURE: + { + Tcl_Obj *resultObjPtr = NULL; + Style *style; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value...?"); + return TCL_ERROR; + } + if (TreeStyle_FromObj(tree, objv[3], (TreeStyle *) &style) != TCL_OK) + return TCL_ERROR; + if (objc <= 5) + { + resultObjPtr = Tk_GetOptionInfo(interp, (char *) style, + styleOptionTable, + (objc == 4) ? (Tcl_Obj *) NULL : objv[4], + tree->tkwin); + if (resultObjPtr == NULL) + return TCL_ERROR; + Tcl_SetObjResult(interp, resultObjPtr); + } + else + { + if (Tk_SetOptions(tree->interp, (char *) style, + styleOptionTable, objc - 4, objv + 4, tree->tkwin, + NULL, NULL) != TCL_OK) + return TCL_ERROR; + Style_Changed(tree, style); + } + break; + } + + case COMMAND_CREATE: + { + char *name; + int len; + Tcl_HashEntry *hPtr; + int isNew; + Style *style; + + if (objc < 4) + { + Tcl_WrongNumArgs(interp, 3, objv, "name ?option value...?"); + return TCL_ERROR; + } + name = Tcl_GetStringFromObj(objv[3], &len); + if (!len) + { + FormatResult(interp, "invalid style name \"\""); + return TCL_ERROR; + } + hPtr = Tcl_FindHashEntry(&tree->styleHash, name); + if (hPtr != NULL) + { + FormatResult(interp, "style \"%s\" already exists", name); + return TCL_ERROR; + } + style = Style_CreateAndConfig(tree, name, objc - 4, objv + 4); + if (style == NULL) + return TCL_ERROR; + hPtr = Tcl_CreateHashEntry(&tree->styleHash, name, &isNew); + Tcl_SetHashValue(hPtr, style); + Tcl_SetObjResult(interp, TreeStyle_ToObj((TreeStyle) style)); + break; + } + + case COMMAND_DELETE: + { + Style *style; + int i; + + if (objc < 3) + { + Tcl_WrongNumArgs(interp, 3, objv, "?name ...?"); + return TCL_ERROR; + } + for (i = 3; i < objc; i++) + { + if (TreeStyle_FromObj(tree, objv[i], (TreeStyle *) &style) != TCL_OK) + return TCL_ERROR; + Style_Deleted(tree, style); + TreeStyle_FreeResources(tree, (TreeStyle) style); + } + break; + } + + /* T style elements S ?{E ...}? */ + case COMMAND_ELEMENTS: + { + Style *style; + Element *elem, **elemList = NULL; + int i, j, count = 0, map[20]; + int listObjc; + Tcl_Obj **listObjv; + + if (objc < 4 || objc > 5) + { + Tcl_WrongNumArgs(interp, 3, objv, "name ?element element...?"); + return TCL_ERROR; + } + if (TreeStyle_FromObj(tree, objv[3], (TreeStyle *) &style) != TCL_OK) + return TCL_ERROR; + if (objc == 5) + { + if (Tcl_ListObjGetElements(interp, objv[4], &listObjc, &listObjv) != TCL_OK) + return TCL_ERROR; + if (listObjc > 0) + elemList = (Element **) ckalloc(sizeof(Element *) * listObjc); + for (i = 0; i < listObjc; i++) + { + if (Element_FromObj(tree, listObjv[i], &elem) != TCL_OK) + { + ckfree((char *) elemList); + return TCL_ERROR; + } + + /* Ignore duplicate elements */ + for (j = 0; j < count; j++) + { + if (elemList[j] == elem) + break; + } + if (j < count) + continue; + + elemList[count++] = elem; + } + + for (i = 0; i < count; i++) + map[i] = -1; + + /* Reassigning Elements to a Style */ + if (style->numElements > 0) + { + /* Check each Element */ + for (i = 0; i < count; i++) + { + /* See if this Element is already used by the Style */ + for (j = 0; j < style->numElements; j++) + { + if (elemList[i] == style->elements[j].elem) + { + /* Preserve it */ + map[i] = j; + break; + } + } + } + } + Style_ChangeElements(tree, style, count, elemList, map); + if (elemList != NULL) + ckfree((char *) elemList); + break; + } + TreeStyle_ListElements(tree, (TreeStyle) style); + break; + } + + /* T style layout S E ?option? ?value? ?option value ...? */ + case COMMAND_LAYOUT: + { + return StyleLayoutCmd(clientData, interp, objc, objv); + } + + case COMMAND_NAMES: + { + Tcl_Obj *listObj; + Tcl_HashSearch search; + Tcl_HashEntry *hPtr; + TreeStyle style; + + listObj = Tcl_NewListObj(0, NULL); + hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search); + while (hPtr != NULL) + { + style = (TreeStyle) Tcl_GetHashValue(hPtr); + Tcl_ListObjAppendElement(interp, listObj, + TreeStyle_ToObj(style)); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_SetObjResult(interp, listObj); + break; + } + } + return TCL_OK; +} + +char *TreeStyle_Identify(StyleDrawArgs *drawArgs, int x, int y) +{ + TreeCtrl *tree = drawArgs->tree; + Style *style = (Style *) drawArgs->style; + int state = drawArgs->state; + ElementLink *eLink; + int i; + struct Layout layouts[20]; + + if (style->neededWidth == -1) + { + Style_NeededSize(tree, style, state, &style->neededWidth, + &style->neededHeight, FALSE); + Style_NeededSize(tree, style, state, &style->minWidth, + &style->minHeight, TRUE); + } + if (drawArgs->width < style->minWidth) + drawArgs->width = style->minWidth; + if (drawArgs->height < style->minHeight) + drawArgs->height = style->minHeight; + + x -= drawArgs->x; + Style_DoLayout(drawArgs, layouts, __FUNCTION__); + + for (i = style->numElements - 1; i >= 0; i--) + { + struct Layout *layout = &layouts[i]; + eLink = layout->eLink; + if ((x >= layout->x + layout->ePad[LEFT]) && (x < layout->x + layout->ePad[LEFT] + layout->iWidth) && + (y >= layout->y + layout->ePad[TOP]) && (y < layout->y + layout->ePad[TOP] + layout->iHeight)) + { + return (char *) eLink->elem->name; + } + } + return NULL; +} + +void TreeStyle_Identify2(StyleDrawArgs *drawArgs, + int x1, int y1, int x2, int y2, Tcl_Obj *listObj) +{ + TreeCtrl *tree = drawArgs->tree; + Style *style = (Style *) drawArgs->style; + int state = drawArgs->state; + ElementLink *eLink; + int i; + struct Layout layouts[20]; + + if (style->neededWidth == -1) + { + Style_NeededSize(tree, style, state, &style->neededWidth, + &style->neededHeight, FALSE); + Style_NeededSize(tree, style, state, &style->minWidth, + &style->minHeight, TRUE); + } + if (drawArgs->width < style->minWidth) + drawArgs->width = style->minWidth; + if (drawArgs->height < style->minHeight) + drawArgs->height = style->minHeight; + + Style_DoLayout(drawArgs, layouts, __FUNCTION__); + + for (i = style->numElements - 1; i >= 0; i--) + { + struct Layout *layout = &layouts[i]; + eLink = layout->eLink; + if ((drawArgs->x + layout->x + layout->ePad[LEFT] < x2) && + (drawArgs->x + layout->x + layout->ePad[LEFT] + layout->iWidth > x1) && + (drawArgs->y + layout->y + layout->ePad[TOP] < y2) && + (drawArgs->y + layout->y + layout->ePad[TOP] + layout->iHeight > y1)) + { + Tcl_ListObjAppendElement(drawArgs->tree->interp, listObj, + Tcl_NewStringObj(eLink->elem->name, -1)); + } + } +} + +int TreeStyle_Remap(TreeCtrl *tree, TreeStyle styleFrom_, TreeStyle styleTo_, int objc, Tcl_Obj *CONST objv[]) +{ + Style *styleFrom = (Style *) styleFrom_; + Style *styleTo = (Style *) styleTo_; + int i, indexFrom, indexTo, map[20]; + ElementLink *eLink; + Element *elemFrom, *elemTo, *elemMap[20]; + + /* Must be instance */ + if ((styleFrom == NULL) || (styleFrom->master == NULL)) + return TCL_ERROR; + + /* Must be master */ + if ((styleTo == NULL) || (styleTo->master != NULL)) + return TCL_ERROR; + + /* Nothing to do */ + if (styleFrom->master == styleTo) + return TCL_OK; + + if (objc & 1) + return TCL_ERROR; + + for (i = 0; i < styleFrom->numElements; i++) + map[i] = -1; + + for (i = 0; i < objc; i += 2) + { + /* Get the old-style element */ + if (Element_FromObj(tree, objv[i], &elemFrom) != TCL_OK) + return TCL_ERROR; + + /* Verify the old style uses the element */ + eLink = Style_FindElem(tree, styleFrom->master, elemFrom, &indexFrom); + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + styleFrom->name, elemFrom->name); + return TCL_ERROR; + } + + /* Get the new-style element */ + if (Element_FromObj(tree, objv[i + 1], &elemTo) != TCL_OK) + return TCL_ERROR; + + /* Verify the new style uses the element */ + eLink = Style_FindElem(tree, styleTo, elemTo, &indexTo); + if (eLink == NULL) + { + FormatResult(tree->interp, "style %s does not use element %s", + styleTo->name, elemTo->name); + return TCL_ERROR; + } + + /* Must be the same type */ + if (elemFrom->typePtr != elemTo->typePtr) + { + FormatResult(tree->interp, "can't map element type %s to %s", + elemFrom->typePtr->name, elemTo->typePtr->name); + return TCL_ERROR; + } + + /* See if the instance style has any info for this element */ + eLink = &styleFrom->elements[indexFrom]; + if (eLink->elem->master != NULL) + { + map[indexFrom] = indexTo; + elemMap[indexFrom] = eLink->elem; + } + } + + for (i = 0; i < styleFrom->numElements; i++) + { + eLink = &styleFrom->elements[i]; + indexTo = map[i]; + + /* Free info for any Elements not being remapped */ + if ((indexTo == -1) && (eLink->elem->master != NULL)) + { + elemFrom = eLink->elem->master; + Element_FreeResources(tree, eLink->elem); + eLink->elem = elemFrom; + } + + /* Remap this Element */ + if (indexTo != -1) + { + elemMap[i]->master = styleTo->elements[indexTo].elem; + elemMap[i]->name = styleTo->elements[indexTo].elem->name; + } + } + + if (styleFrom->numElements != styleTo->numElements) + { + if (styleFrom->numElements > 0) + wipefree((char *) styleFrom->elements, sizeof(ElementLink) * + styleFrom->numElements); + styleFrom->elements = (ElementLink *) ckalloc(sizeof(ElementLink) * + styleTo->numElements); + memset(styleFrom->elements, '\0', sizeof(ElementLink) * styleTo->numElements); + } + for (i = 0; i < styleTo->numElements; i++) + { + styleFrom->elements[i].elem = styleTo->elements[i].elem; + styleFrom->elements[i].neededWidth = -1; + styleFrom->elements[i].neededHeight = -1; + } + for (i = 0; i < styleFrom->numElements; i++) + { + indexTo = map[i]; + if (indexTo != -1) + styleFrom->elements[indexTo].elem = elemMap[i]; + } + styleFrom->name = styleTo->name; + styleFrom->master = styleTo; + styleFrom->neededWidth = styleFrom->neededHeight = -1; + styleFrom->numElements = styleTo->numElements; + + return TCL_OK; +} + +int TreeStyle_GetSortData(TreeCtrl *tree, TreeStyle style_, int type, long *lv, double *dv, char **sv) +{ + Style *style = (Style *) style_; + ElementLink *eLink; + int i; + + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + if (eLink->elem->typePtr == &elemTypeText) + return Element_GetSortData(tree, eLink->elem, type, lv, dv, sv); + } + + return TCL_ERROR; +} + +int TreeStyle_GetElemRects(StyleDrawArgs *drawArgs, int objc, + Tcl_Obj *CONST objv[], XRectangle rects[20]) +{ + Style *style = (Style *) drawArgs->style; + Style *master = style->master ? style->master : style; + int i, j, count = 0; + struct Layout layouts[20]; + Element *elems[20]; + ElementLink *eLink; + + for (j = 0; j < objc; j++) + { + if (Element_FromObj(drawArgs->tree, objv[j], &elems[j]) != TCL_OK) + return -1; + + eLink = Style_FindElem(drawArgs->tree, master, elems[j], NULL); + if (eLink == NULL) + { + FormatResult(drawArgs->tree->interp, + "style %s does not use element %s", + style->name, elems[j]->name); + return -1; + } + } + + if (drawArgs->width < style->minWidth) + drawArgs->width = style->minWidth; + if (drawArgs->height < style->minHeight) + drawArgs->height = style->minHeight; + + Style_DoLayout(drawArgs, layouts, __FUNCTION__); + + for (i = style->numElements - 1; i >= 0; i--) + { + struct Layout *layout = &layouts[i]; + if (objc > 0) + { + for (j = 0; j < objc; j++) + if (elems[j] == layout->eLink->elem || + elems[j] == layout->master->elem) + break; + if (j == objc) + continue; + } + rects[count].x = drawArgs->x + layout->x + layout->ePad[LEFT]; + rects[count].y = drawArgs->y + layout->y + layout->ePad[TOP]; + rects[count].width = layout->iWidth; + rects[count].height = layout->iHeight; + count++; + } + return count; +} + +int TreeStyle_ChangeState(TreeCtrl *tree, TreeStyle style_, int state1, int state2) +{ + Style *style = (Style *) style_; + ElementLink *eLink; + ElementArgs args; + int i, eMask, mask = 0; + + args.tree = tree; + args.states.state1 = state1; + args.states.state2 = state2; + + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + args.elem = eLink->elem; + eMask = (*eLink->elem->typePtr->stateProc)(&args); + if (eMask) + { + if (eMask & CS_LAYOUT) + eLink->neededWidth = eLink->neededHeight = -1; + mask |= eMask; + } + } + + if (mask & CS_LAYOUT) + style->neededWidth = style->neededHeight = -1; + + return mask; +} + +void TreeStyle_UndefineState(TreeCtrl *tree, int state) +{ + TreeItem item; + TreeItemColumn column; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + ElementLink *eLink; + int i, columnIndex; + ElementArgs args; + int eMask, cMask, iMask; + int updateDInfo = FALSE; + + args.tree = tree; + args.state = state; + + hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); + while (hPtr != NULL) + { + item = (TreeItem) Tcl_GetHashValue(hPtr); + column = TreeItem_GetFirstColumn(tree, item); + columnIndex = 0; + iMask = 0; + args.states.state1 = TreeItem_GetState(tree, item); + args.states.state2 = args.states.state1 & ~state; + while (column != NULL) + { + Style *style = (Style *) TreeItemColumn_GetStyle(tree, column); + if (style != NULL) + { + cMask = 0; + for (i = 0; i < style->numElements; i++) + { + eLink = &style->elements[i]; + args.elem = eLink->elem; + eMask = (*args.elem->typePtr->stateProc)(&args); + if (eMask & CS_LAYOUT) + eLink->neededWidth = eLink->neededHeight = -1; + cMask |= eMask; + if (eLink->elem->master == NULL) + (*args.elem->typePtr->undefProc)(&args); + } + if (cMask & CS_LAYOUT) + { + style->neededWidth = style->neededHeight = -1; + Tree_InvalidateColumnWidth(tree, columnIndex); + TreeItemColumn_InvalidateSize(tree, column); + } + iMask |= cMask; + } + columnIndex++; + column = TreeItemColumn_GetNext(tree, column); + } + if (iMask & CS_LAYOUT) + { + TreeItem_InvalidateHeight(tree, item); + updateDInfo = TRUE; + } + if (iMask & CS_DISPLAY) + Tree_InvalidateItemDInfo(tree, item, NULL); + TreeItem_Undefine(tree, item, state); + hPtr = Tcl_NextHashEntry(&search); + } + if (updateDInfo) + Tree_DInfoChanged(tree, DINFO_REDO_RANGES); + + hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search); + while (hPtr != NULL) + { + args.elem = (Element *) Tcl_GetHashValue(hPtr); + (*args.elem->typePtr->undefProc)(&args); + hPtr = Tcl_NextHashEntry(&search); + } +} + +int TreeStyle_Init(Tcl_Interp *interp) +{ + ElementType *typePtr; + + styleOptionTable = Tk_CreateOptionTable(interp, styleOptionSpecs); + + elementTypeList = &elemTypeBitmap; + elemTypeBitmap.next = &elemTypeBorder; + elemTypeBorder.next = &elemTypeImage; + elemTypeImage.next = &elemTypeRect; + elemTypeRect.next = &elemTypeText; + elemTypeText.next = NULL; + + for (typePtr = elementTypeList; + typePtr != NULL; + typePtr = typePtr->next) + { + typePtr->optionTable = Tk_CreateOptionTable(interp, + typePtr->optionSpecs); + } + + return TCL_OK; +} + +void TreeStyle_Free(TreeCtrl *tree) +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Element *elem; + Style *style; + + while (1) + { + hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search); + if (hPtr == NULL) + break; + style = (Style *) Tcl_GetHashValue(hPtr); + TreeStyle_FreeResources(tree, (TreeStyle) style); + } + + while (1) + { + hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search); + if (hPtr == NULL) + break; + elem = (Element *) Tcl_GetHashValue(hPtr); + Element_FreeResources(tree, elem); + } + + Tcl_DeleteHashTable(&tree->elementHash); + Tcl_DeleteHashTable(&tree->styleHash); +} + + diff --git a/generic/tkTreeUtils.c b/generic/tkTreeUtils.c new file mode 100644 index 0000000..9c8b482 --- /dev/null +++ b/generic/tkTreeUtils.c @@ -0,0 +1,877 @@ +#include "tkTreeCtrl.h" +#ifdef WIN32 +#include "tkWinInt.h" +#endif + +void wipefree(char *memPtr, int size) +{ + memset(memPtr, 0xAA, size); + ckfree(memPtr); +} + +void FormatResult(Tcl_Interp *interp, char *fmt, ...) +{ + va_list ap; + char buf[256]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + Tcl_SetResult(interp, buf, TCL_VOLATILE); +} + +int Ellipsis(Tk_Font tkfont, char *string, int numBytes, int *maxPixels, char *ellipsis) +{ + char staticStr[256], *tmpStr = staticStr; + int pixels, pixelsTest, bytesThatFit, bytesTest; + int ellipsisNumBytes = strlen(ellipsis); + + bytesThatFit = Tk_MeasureChars(tkfont, string, numBytes, *maxPixels, 0, + &pixels); + + /* The whole string fits. No ellipsis needed */ + if (bytesThatFit == numBytes) + { + (*maxPixels) = pixels; + return numBytes; + } + + if (bytesThatFit <= 1) + { + (*maxPixels) = pixels; + return -bytesThatFit; + } + + /* Strip off one character at a time, adding ellipsis, until it fits */ + bytesTest = Tcl_UtfPrev(string + bytesThatFit, string) - string; + if (bytesTest + ellipsisNumBytes > sizeof(staticStr)) + tmpStr = ckalloc(bytesTest + ellipsisNumBytes); + memcpy(tmpStr, string, bytesTest); + while (bytesTest > 0) + { + memcpy(tmpStr + bytesTest, ellipsis, ellipsisNumBytes); + numBytes = Tk_MeasureChars(tkfont, tmpStr, + bytesTest + ellipsisNumBytes, + *maxPixels, 0, &pixelsTest); + if (numBytes == bytesTest + ellipsisNumBytes) + { + (*maxPixels) = pixelsTest; + if (tmpStr != staticStr) + ckfree(tmpStr); + return bytesTest; + } + bytesTest = Tcl_UtfPrev(string + bytesTest, string) - string; + } + + /* No single char + ellipsis fits. Return number of chars that fit */ + /* Negative tells caller to not add ellipsis */ + (*maxPixels) = pixels; + if (tmpStr != staticStr) + ckfree(tmpStr); + return -bytesThatFit; +} + +/* Draws a horizontal 1-pixel tall dotted line */ +void HDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int x2) +{ +#ifdef WIN32 + TkWinDCState state; + HDC dc; + HPEN pen, oldPen; + int nw; + int wx = x1 + tree->drawableXOrigin; + int wy = y1 + tree->drawableYOrigin; + + dc = TkWinGetDrawableDC(tree->display, drawable, &state); +/* SetROP2(dc, R2_NOT); */ + + pen = CreatePen(PS_SOLID, 1, gc->foreground); + oldPen = SelectObject(dc, pen); + + nw = !(wx & 1) == !(wy & 1); + for (x1 += !nw; x1 < x2; x1 += 2) + { + MoveToEx(dc, x1, y1, NULL); + LineTo(dc, x1 + 1, y1); + } + + SelectObject(dc, oldPen); + DeleteObject(pen); + + TkWinReleaseDrawableDC(drawable, dc, &state); +#else + int nw; + int wx = x1 + tree->drawableXOrigin; + int wy = y1 + tree->drawableYOrigin; + + nw = !(wx & 1) == !(wy & 1); + for (x1 += !nw; x1 < x2; x1 += 2) + { + XDrawPoint(tree->display, drawable, gc, x1, y1); + } +#endif +} + +/* Draws a vertical 1-pixel wide dotted line */ +void VDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int y2) +{ +#ifdef WIN32 + TkWinDCState state; + HDC dc; + HPEN pen, oldPen; + int nw; + int wx = x1 + tree->drawableXOrigin; + int wy = y1 + tree->drawableYOrigin; + + dc = TkWinGetDrawableDC(tree->display, drawable, &state); +/* SetROP2(dc, R2_NOT); */ + + pen = CreatePen(PS_SOLID, 1, gc->foreground); + oldPen = SelectObject(dc, pen); + + nw = !(wx & 1) == !(wy & 1); + for (y1 += !nw; y1 < y2; y1 += 2) + { + MoveToEx(dc, x1, y1, NULL); + LineTo(dc, x1 + 1, y1); + } + + SelectObject(dc, oldPen); + DeleteObject(pen); + + TkWinReleaseDrawableDC(drawable, dc, &state); +#else + int nw; + int wx = x1 + tree->drawableXOrigin; + int wy = y1 + tree->drawableYOrigin; + + nw = !(wx & 1) == !(wy & 1); + for (y1 += !nw; y1 < y2; y1 += 2) + { + XDrawPoint(tree->display, drawable, gc, x1, y1); + } +#endif +} + +/* Draws 0 or more sides of a rectangle, dot-on dot-off, XOR style */ +void DrawActiveOutline(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height, int open) +{ +#ifdef WIN32 + int wx = x + tree->drawableXOrigin; + int wy = y + tree->drawableYOrigin; + int w = !(open & 0x01); + int n = !(open & 0x02); + int e = !(open & 0x04); + int s = !(open & 0x08); + int nw, ne, sw, se; + int i; + TkWinDCState state; + HDC dc; + + /* Dots on even pixels only */ + nw = !(wx & 1) == !(wy & 1); + ne = !((wx + width - 1) & 1) == !(wy & 1); + sw = !(wx & 1) == !((wy + height - 1) & 1); + se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1); + + dc = TkWinGetDrawableDC(tree->display, drawable, &state); + SetROP2(dc, R2_NOT); + if (w) /* left */ + { + for (i = !nw; i < height; i += 2) + { + MoveToEx(dc, x, y + i, NULL); + LineTo(dc, x + 1, y + i); + } + } + if (n) /* top */ + { + for (i = nw ? w * 2 : 1; i < width; i += 2) + { + MoveToEx(dc, x + i, y, NULL); + LineTo(dc, x + i + 1, y); + } + } + if (e) /* right */ + { + for (i = ne ? n * 2 : 1; i < height; i += 2) + { + MoveToEx(dc, x + width - 1, y + i, NULL); + LineTo(dc, x + width, y + i); + } + } + if (s) /* bottom */ + { + for (i = sw ? w * 2 : 1; i < width - (se && e); i += 2) + { + MoveToEx(dc, x + i, y + height - 1, NULL); + LineTo(dc, x + i + 1, y + height - 1); + } + } + TkWinReleaseDrawableDC(drawable, dc, &state); +#else + int wx = x + tree->drawableXOrigin; + int wy = y + tree->drawableYOrigin; + int w = !(open & 0x01); + int n = !(open & 0x02); + int e = !(open & 0x04); + int s = !(open & 0x08); + int nw, ne, sw, se; + int i; + XGCValues gcValues; + unsigned long gcMask; + GC gc; + + /* Dots on even pixels only */ + nw = !(wx & 1) == !(wy & 1); + ne = !((wx + width - 1) & 1) == !(wy & 1); + sw = !(wx & 1) == !((wy + height - 1) & 1); + se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1); + + gcValues.function = GXinvert; + gcMask = GCFunction; + gc = Tk_GetGC(tree->tkwin, gcMask, &gcValues); + + if (w) /* left */ + { + for (i = !nw; i < height; i += 2) + { + XDrawPoint(tree->display, drawable, gc, x, y + i); + } + } + if (n) /* top */ + { + for (i = nw ? w * 2 : 1; i < width; i += 2) + { + XDrawPoint(tree->display, drawable, gc, x + i, y); + } + } + if (e) /* right */ + { + for (i = ne ? n * 2 : 1; i < height; i += 2) + { + XDrawPoint(tree->display, drawable, gc, x + width - 1, y + i); + } + } + if (s) /* bottom */ + { + for (i = sw ? w * 2 : 1; i < width - (se && e); i += 2) + { + XDrawPoint(tree->display, drawable, gc, x + i, y + height - 1); + } + } + + Tk_FreeGC(tree->display, gc); +#endif +} + +void DotRect(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height) +{ + DrawActiveOutline(tree, drawable, x, y, width, height, 0); +} + +struct DotStatePriv +{ + TreeCtrl *tree; + Drawable drawable; +#ifdef WIN32 + HDC dc; + TkWinDCState dcState; + HRGN rgn; +#else + GC gc; + TkRegion rgn; +#endif +}; + +void DotRect_Setup(TreeCtrl *tree, Drawable drawable, DotState *p) +{ + struct DotStatePriv *dotState = (struct DotStatePriv *) p; +#ifdef WIN32 +#else + XGCValues gcValues; + unsigned long mask; + XRectangle xrect; +#endif + + if (sizeof(*dotState) > sizeof(*p)) + panic("DotRect_Setup: DotState hack is too small"); + + dotState->tree = tree; + dotState->drawable = drawable; +#ifdef WIN32 + dotState->dc = TkWinGetDrawableDC(tree->display, drawable, &dotState->dcState); + + /* XOR drawing */ + SetROP2(dotState->dc, R2_NOT); + + /* Keep drawing inside the contentbox */ + dotState->rgn = CreateRectRgn( + tree->inset, + tree->inset + Tree_HeaderHeight(tree), + Tk_Width(tree->tkwin) - tree->inset, + Tk_Height(tree->tkwin) - tree->inset); + SelectClipRgn(dotState->dc, dotState->rgn); +#else + gcValues.line_style = LineOnOffDash; + gcValues.line_width = 1; + gcValues.dash_offset = 0; + gcValues.dashes = 1; + gcValues.function = GXinvert; + mask = GCLineWidth | GCLineStyle | GCDashList | GCDashOffset | GCFunction; + dotState->gc = Tk_GetGC(tree->tkwin, mask, &gcValues); + + /* Keep drawing inside the contentbox */ + dotState->rgn = TkCreateRegion(); + xrect.x = tree->inset; + xrect.y = tree->inset + Tree_HeaderHeight(tree); + xrect.width = Tk_Width(tree->tkwin) - tree->inset - xrect.x; + xrect.height = Tk_Height(tree->tkwin) - tree->inset - xrect.y; + TkUnionRectWithRegion(&xrect, dotState->rgn, dotState->rgn); + TkSetRegion(tree->display, dotState->gc, dotState->rgn); +#endif +} + +void DotRect_Draw(DotState *p, int x, int y, int width, int height) +{ + struct DotStatePriv *dotState = (struct DotStatePriv *) p; +#ifdef WIN32 +#if 1 + RECT rect; + + rect.left = x; + rect.right = x + width; + rect.top = y; + rect.bottom = y + height; + DrawFocusRect(dotState->dc, &rect); +#else + HDC dc = dotState->dc; + int i; + int wx = x + dotState->tree->drawableXOrigin; + int wy = y + dotState->tree->drawableYOrigin; + int nw, ne, sw, se; + + /* Dots on even pixels only */ + nw = !(wx & 1) == !(wy & 1); + ne = !((wx + width - 1) & 1) == !(wy & 1); + sw = !(wx & 1) == !((wy + height - 1) & 1); + se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1); + + for (i = !nw; i < height; i += 2) + { + MoveToEx(dc, x, y + i, NULL); + LineTo(dc, x + 1, y + i); + } + for (i = nw ? 2 : 1; i < width; i += 2) + { + MoveToEx(dc, x + i, y, NULL); + LineTo(dc, x + i + 1, y); + } + for (i = ne ? 2 : 1; i < height; i += 2) + { + MoveToEx(dc, x + width - 1, y + i, NULL); + LineTo(dc, x + width, y + i); + } + for (i = sw ? 2 : 1; i < width - se; i += 2) + { + MoveToEx(dc, x + i, y + height - 1, NULL); + LineTo(dc, x + i + 1, y + height - 1); + } +#endif +#else + XDrawRectangle(dotState->tree->display, dotState->drawable, dotState->gc, + x, y, width - 1, height - 1); +#endif +} + +void DotRect_Restore(DotState *p) +{ + struct DotStatePriv *dotState = (struct DotStatePriv *) p; +#ifdef WIN32 + SelectClipRgn(dotState->dc, NULL); + DeleteObject(dotState->rgn); + TkWinReleaseDrawableDC(dotState->drawable, dotState->dc, &dotState->dcState); +#else + XSetClipMask(dotState->tree->display, dotState->gc, None); + Tk_FreeGC(dotState->tree->display, dotState->gc); +#endif +} + +/* + * Replacement for Tk_TextLayout stuff. Allows the caller to break lines + * on character boundaries (as well as word boundaries). Allows the caller + * to specify the maximum number of lines to display. Will add ellipsis "..." + * to the end of text that is too long to fit (when max lines specified). + */ + +typedef struct LayoutChunk +{ + CONST char *start; /* Pointer to simple string to be displayed. + * * This is a pointer into the TkTextLayout's + * * string. */ + int numBytes; /* The number of bytes in this chunk. */ + int numChars; /* The number of characters in this chunk. */ + int numDisplayChars; /* The number of characters to display when + * * this chunk is displayed. Can be less than + * * numChars if extra space characters were + * * absorbed by the end of the chunk. This + * * will be < 0 if this is a chunk that is + * * holding a tab or newline. */ + int x, y; /* The origin of the first character in this + * * chunk with respect to the upper-left hand + * * corner of the TextLayout. */ + int totalWidth; /* Width in pixels of this chunk. Used + * * when hit testing the invisible spaces at + * * the end of a chunk. */ + int displayWidth; /* Width in pixels of the displayable + * * characters in this chunk. Can be less than + * * width if extra space characters were + * * absorbed by the end of the chunk. */ + int ellipsis; /* TRUE if adding "..." */ +} LayoutChunk; + +typedef struct LayoutInfo +{ + Tk_Font tkfont; /* The font used when laying out the text. */ + CONST char *string; /* The string that was layed out. */ + int numLines; /* Number of lines */ + int width; /* The maximum width of all lines in the + * * text layout. */ + int height; + int numChunks; /* Number of chunks actually used in + * * following array. */ + LayoutChunk chunks[1]; /* Array of chunks. The actual size will + * * be maxChunks. THIS FIELD MUST BE THE LAST + * * IN THE STRUCTURE. */ +} LayoutInfo; + +static LayoutChunk *NewChunk(LayoutInfo **layoutPtrPtr, int *maxPtr, + CONST char *start, int numBytes, int curX, int newX, int y) +{ + LayoutInfo *layoutPtr; + LayoutChunk *chunkPtr; + int maxChunks, numChars; + size_t s; + + layoutPtr = *layoutPtrPtr; + maxChunks = *maxPtr; + if (layoutPtr->numChunks == maxChunks) + { + maxChunks *= 2; + s = sizeof(LayoutInfo) + ((maxChunks - 1) * sizeof(LayoutChunk)); + layoutPtr = (LayoutInfo *) ckrealloc((char *) layoutPtr, s); + + *layoutPtrPtr = layoutPtr; + *maxPtr = maxChunks; + } + numChars = Tcl_NumUtfChars(start, numBytes); + chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks]; + chunkPtr->start = start; + chunkPtr->numBytes = numBytes; + chunkPtr->numChars = numChars; + chunkPtr->numDisplayChars = numChars; + chunkPtr->x = curX; + chunkPtr->y = y; + chunkPtr->totalWidth = newX - curX; + chunkPtr->displayWidth = newX - curX; + chunkPtr->ellipsis = FALSE; + layoutPtr->numChunks++; + + return chunkPtr; +} + +TextLayout TextLayout_Compute( + Tk_Font tkfont, /* Font that will be used to display text. */ + CONST char *string, /* String whose dimensions are to be + ** computed. */ + int numChars, /* Number of characters to consider from + ** string, or < 0 for strlen(). */ + int wrapLength, /* Longest permissible line length, in + ** pixels. <= 0 means no automatic wrapping: + ** just let lines get as long as needed. */ + Tk_Justify justify, /* How to justify lines. */ + int maxLines, + int flags /* Flag bits OR-ed together. + ** TK_IGNORE_TABS means that tab characters + ** should not be expanded. TK_IGNORE_NEWLINES + ** means that newline characters should not + ** cause a line break. */ + ) +{ + CONST char *start, *end, *special; + int n, y, bytesThisChunk, maxChunks; + int baseline, height, curX, newX, maxWidth; + LayoutInfo *layoutPtr; + LayoutChunk *chunkPtr; + Tk_FontMetrics fm; + Tcl_DString lineBuffer; + int *lineLengths; + int curLine; + int tabWidth = 20; /* FIXME */ + + Tcl_DStringInit(&lineBuffer); + + Tk_GetFontMetrics(tkfont, &fm); + height = fm.ascent + fm.descent; + + if (numChars < 0) + numChars = Tcl_NumUtfChars(string, -1); + if (wrapLength == 0) + wrapLength = -1; + + maxChunks = 1; + + layoutPtr = (LayoutInfo *) ckalloc(sizeof(LayoutInfo) + (maxChunks - + 1) * sizeof(LayoutChunk)); + layoutPtr->tkfont = tkfont; + layoutPtr->string = string; + layoutPtr->numChunks = 0; + layoutPtr->numLines = 0; + + baseline = fm.ascent; + maxWidth = 0; + + curX = 0; + + end = Tcl_UtfAtIndex(string, numChars); + special = string; + + flags &= TK_WHOLE_WORDS | TK_IGNORE_TABS | TK_IGNORE_NEWLINES; + flags |= TK_AT_LEAST_ONE; + for (start = string; start < end;) + { + if (start >= special) + { + for (special = start; special < end; special++) + { + if (!(flags & TK_IGNORE_NEWLINES)) + { + if ((*special == '\n') || (*special == '\r')) + break; + } + if (!(flags & TK_IGNORE_TABS)) + { + if (*special == '\t') + break; + } + } + } + + chunkPtr = NULL; + if (start < special) + { + bytesThisChunk = Tk_MeasureChars(tkfont, start, special - start, + wrapLength - curX, flags, &newX); + newX += curX; + flags &= ~TK_AT_LEAST_ONE; + if (bytesThisChunk > 0) + { + chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, + bytesThisChunk, curX, newX, baseline); + start += bytesThisChunk; + curX = newX; + } + } + + if ((start == special) && (special < end)) + { + chunkPtr = NULL; + if (*special == '\t') + { + newX = curX + tabWidth; + newX -= newX % tabWidth; + NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX, + baseline)->numDisplayChars = -1; + start++; + if ((start < end) && ((wrapLength <= 0) || + (newX <= wrapLength))) + { + curX = newX; + flags &= ~TK_AT_LEAST_ONE; + continue; + } + } + else + { + NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX, + baseline)->numDisplayChars = -1; + start++; + goto wrapLine; + } + } + + while ((start < end) && isspace(UCHAR(*start))) + { + if (!(flags & TK_IGNORE_NEWLINES)) + { + if ((*start == '\n') || (*start == '\r')) + break; + } + if (!(flags & TK_IGNORE_TABS)) + { + if (*start == '\t') + break; + } + start++; + } + if (chunkPtr != NULL) + { + CONST char *end; + + end = chunkPtr->start + chunkPtr->numBytes; + bytesThisChunk = start - end; + if (bytesThisChunk > 0) + { + bytesThisChunk = + Tk_MeasureChars(tkfont, end, bytesThisChunk, -1, 0, + &chunkPtr->totalWidth); + chunkPtr->numBytes += bytesThisChunk; + chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk); + chunkPtr->totalWidth += curX; + } + } + +wrapLine: + flags |= TK_AT_LEAST_ONE; + + if (curX > maxWidth) + maxWidth = curX; + + Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX)); + + curX = 0; + baseline += height; + + if ((maxLines > 0) && (++layoutPtr->numLines >= maxLines)) + break; + } + + if (start >= end) + if ((layoutPtr->numChunks > 0) && !(flags & TK_IGNORE_NEWLINES)) + { + if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') + { + chunkPtr = + NewChunk(&layoutPtr, &maxChunks, start, 0, curX, curX, + baseline); + chunkPtr->numDisplayChars = -1; + Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX)); + baseline += height; + } + } + +#if 1 + /* Fiddle with chunks on the last line to add ellipsis if there is some + * text remaining */ + if ((start < end) && (layoutPtr->numChunks > 0)) + { + chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 1]; + if (wrapLength > 0) + { + y = chunkPtr->y; + for (n = layoutPtr->numChunks - 1; n >= 0; n--) + { + chunkPtr = &layoutPtr->chunks[n]; + + /* Only consider the last line */ + if (chunkPtr->y != y) + break; + + if (chunkPtr->start[0] == '\n') + continue; + + newX = chunkPtr->totalWidth - 1; + bytesThisChunk = Ellipsis(tkfont, (char *) chunkPtr->start, + chunkPtr->numBytes, &newX, "..."); + if (bytesThisChunk > 0) + { + chunkPtr->numBytes = bytesThisChunk; + chunkPtr->numChars = Tcl_NumUtfChars(chunkPtr->start, bytesThisChunk); + chunkPtr->numDisplayChars = chunkPtr->numChars; + chunkPtr->ellipsis = TRUE; + chunkPtr->displayWidth = newX; + chunkPtr->totalWidth = newX; + lineLengths = (int *) Tcl_DStringValue(&lineBuffer); + lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + newX; + if (chunkPtr->x + newX > maxWidth) + maxWidth = chunkPtr->x + newX; + break; + } + } + } + else + { + char staticStr[256], *buf = staticStr; + char *ellipsis = "..."; + int ellipsisLen = strlen(ellipsis); + + if (chunkPtr->start[0] == '\n') + { + if (layoutPtr->numChunks == 1) + goto finish; + if (layoutPtr->chunks[layoutPtr->numChunks - 2].y != chunkPtr->y) + goto finish; + chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 2]; + } + + if (chunkPtr->numBytes + ellipsisLen > sizeof(staticStr)) + buf = ckalloc(chunkPtr->numBytes + ellipsisLen); + memcpy(buf, chunkPtr->start, chunkPtr->numBytes); + memcpy(buf + chunkPtr->numBytes, ellipsis, ellipsisLen); + Tk_MeasureChars(tkfont, buf, + chunkPtr->numBytes + ellipsisLen, -1, 0, + &chunkPtr->displayWidth); + chunkPtr->totalWidth = chunkPtr->displayWidth; + chunkPtr->ellipsis = TRUE; + lineLengths = (int *) Tcl_DStringValue(&lineBuffer); + lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + chunkPtr->displayWidth; + if (chunkPtr->x + chunkPtr->displayWidth > maxWidth) + maxWidth = chunkPtr->x + chunkPtr->displayWidth; + if (buf != staticStr) + ckfree(buf); + } + } +finish: +#endif + + layoutPtr->width = maxWidth; + layoutPtr->height = baseline - fm.ascent; + if (layoutPtr->numChunks == 0) + { + layoutPtr->height = height; + + layoutPtr->numChunks = 1; + layoutPtr->chunks[0].start = string; + layoutPtr->chunks[0].numBytes = 0; + layoutPtr->chunks[0].numChars = 0; + layoutPtr->chunks[0].numDisplayChars = -1; + layoutPtr->chunks[0].x = 0; + layoutPtr->chunks[0].y = fm.ascent; + layoutPtr->chunks[0].totalWidth = 0; + layoutPtr->chunks[0].displayWidth = 0; + } + else + { + curLine = 0; + chunkPtr = layoutPtr->chunks; + y = chunkPtr->y; + lineLengths = (int *) Tcl_DStringValue(&lineBuffer); + for (n = 0; n < layoutPtr->numChunks; n++) + { + int extra; + + if (chunkPtr->y != y) + { + curLine++; + y = chunkPtr->y; + } + extra = maxWidth - lineLengths[curLine]; + if (justify == TK_JUSTIFY_CENTER) + { + chunkPtr->x += extra / 2; + } + else if (justify == TK_JUSTIFY_RIGHT) + { + chunkPtr->x += extra; + } + chunkPtr++; + } + } + + Tcl_DStringFree(&lineBuffer); + + return (TextLayout) layoutPtr; +} + +void TextLayout_Free(TextLayout textLayout) +{ + LayoutInfo *layoutPtr = (LayoutInfo *) textLayout; + + ckfree((char *) layoutPtr); +} + +void TextLayout_Size(TextLayout textLayout, int *widthPtr, int *heightPtr) +{ + LayoutInfo *layoutPtr = (LayoutInfo *) textLayout; + + if (widthPtr != NULL) + (*widthPtr) = layoutPtr->width; + if (heightPtr != NULL) + (*heightPtr) = layoutPtr->height; +} + +void TextLayout_Draw( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context to use for drawing text. */ + TextLayout layout, /* Layout information, from a previous call + * * to Tk_ComputeTextLayout(). */ + int x, int y, /* Upper-left hand corner of rectangle in + * * which to draw (pixels). */ + int firstChar, /* The index of the first character to draw + * * from the given text item. 0 specfies the + * * beginning. */ + int lastChar /* The index just after the last character + * * to draw from the given text item. A number + * * < 0 means to draw all characters. */ +) +{ + LayoutInfo *layoutPtr = (LayoutInfo *) layout; + int i, numDisplayChars, drawX; + CONST char *firstByte; + CONST char *lastByte; + LayoutChunk *chunkPtr; + + if (lastChar < 0) + lastChar = 100000000; + chunkPtr = layoutPtr->chunks; + for (i = 0; i < layoutPtr->numChunks; i++) + { + numDisplayChars = chunkPtr->numDisplayChars; + if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) + { + if (firstChar <= 0) + { + drawX = 0; + firstChar = 0; + firstByte = chunkPtr->start; + } + else + { + firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar); + Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, + firstByte - chunkPtr->start, -1, 0, &drawX); + } + if (lastChar < numDisplayChars) + numDisplayChars = lastChar; + lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars); +#if 1 + if (chunkPtr->ellipsis) + { + char staticStr[256], *buf = staticStr; + char *ellipsis = "..."; + int ellipsisLen = strlen(ellipsis); + + if ((lastByte - firstByte) + ellipsisLen > sizeof(staticStr)) + buf = ckalloc((lastByte - firstByte) + ellipsisLen); + memcpy(buf, firstByte, (lastByte - firstByte)); + memcpy(buf + (lastByte - firstByte), ellipsis, ellipsisLen); + Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, + buf, (lastByte - firstByte) + ellipsisLen, + x + chunkPtr->x + drawX, y + chunkPtr->y); + if (buf != staticStr) + ckfree(buf); + } + else +#endif + Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, + firstByte, lastByte - firstByte, x + chunkPtr->x + drawX, + y + chunkPtr->y); + } + firstChar -= chunkPtr->numChars; + lastChar -= chunkPtr->numChars; + if (lastChar <= 0) + break; + chunkPtr++; + } +} diff --git a/library/filelist-bindings.tcl b/library/filelist-bindings.tcl new file mode 100644 index 0000000..f58ef82 --- /dev/null +++ b/library/filelist-bindings.tcl @@ -0,0 +1,788 @@ +bind TreeCtrlFileList { + TreeCtrl::FileListEditCancel %W + TreeCtrl::DoubleButton1 %W %x %y +} +bind TreeCtrlFileList { + set TreeCtrl::Priv(selectMode) toggle + TreeCtrl::FileListButton1 %W %x %y + break +} +bind TreeCtrlFileList { + set TreeCtrl::Priv(selectMode) add + TreeCtrl::FileListButton1 %W %x %y + break +} +bind TreeCtrlFileList { + set TreeCtrl::Priv(selectMode) set + TreeCtrl::FileListButton1 %W %x %y + break +} +bind TreeCtrlFileList { + TreeCtrl::FileListMotion1 %W %x %y + break +} +bind TreeCtrlFileList { + TreeCtrl::FileListLeave1 %W %x %y + break +} +bind TreeCtrlFileList { + TreeCtrl::FileListRelease1 %W %x %y + break +} + +proc TreeCtrl::FileListButton1 {T x y} { + variable Priv + focus $T + set id [$T identify $x $y] + set marquee 0 + set Priv(buttonMode) "" + FileListEditCancel $T + # Click outside any item + if {$id eq ""} { + set marquee 1 + + # Click in header + } elseif {[lindex $id 0] eq "header"} { + ButtonPress1 $T $x $y + + # Click in item + } else { + foreach {where item arg1 arg2 arg3 arg4} $id {} + switch $arg1 { + button - + line { + ButtonPress1 $T $x $y + } + column { + # Clicked in column zero + if {$arg2 eq 0} { + set ok 0 + # Clicked an element + if {[llength $id] == 6} { + set E [lindex $id 5] + if {[lsearch -exact $Priv(sensitive,$T) $E] != -1} { + set ok 1 + } + } + if {$ok} { + set Priv(drag,motion) 0 + set Priv(drag,x) [$T canvasx $x] + set Priv(drag,y) [$T canvasy $y] + set Priv(drop) "" + set Priv(drag,wasSel) [$T selection includes $item] + set Priv(drag,E) $E + $T activate $item + if {$Priv(selectMode) eq "add"} { + BeginExtend $T $item + } elseif {$Priv(selectMode) eq "toggle"} { + BeginToggle $T $item + } elseif {![$T selection includes $item]} { + BeginSelect $T $item + } + + # Changing the selection might change the list + if {[$T index $item] eq ""} return + + # Click selected item to drag + if {[$T selection includes $item]} { + set Priv(buttonMode) drag + } + # Clicked inside item, but outside elements + } else { + set marquee 1 + } + # Clicked column > 0 + } else { + set marquee 1 + } + } + } + } + if {$marquee} { + set Priv(buttonMode) marquee + if {$Priv(selectMode) ne "set"} { + set Priv(selection) [$T selection get] + } else { + $T selection clear + set Priv(selection) {} + } + MarqueeBegin $T $x $y + } + return +} +proc TreeCtrl::FileListMotion1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $T $x $y + } + "drag" - + "marquee" { + FileListAutoScanCheck $T $x $y + FileListMotion $T $x $y + } + } +} +proc TreeCtrl::FileListMotion {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Motion1 $T $x $y + } + "marquee" { + MarqueeUpdate $T $x $y + set select $Priv(selection) + set deselect {} + + # Check items covered by the marquee + foreach list [$T marque identify] { + set item [lindex $list 0] + + # Check covered columns in this item + foreach sublist [lrange $list 1 end] { + set column [lindex $sublist 0] + set ok 0 + foreach E [lrange $sublist 1 end] { + if {[lsearch -exact $Priv(sensitive,$T) $E] != -1} { + set ok 1 + break + } + } + # Some elements in column zero are covered + if {($column == 0) && $ok} { + + # Toggle selected status + if {$Priv(selectMode) eq "toggle"} { + set i [lsearch -exact $Priv(selection) $item] + if {$i == -1} { + lappend select $item + } else { + set i [lsearch -exact $select $item] + set select [lreplace $select $i $i] + } + } else { + lappend select $item + } + } + break + } + } + $T selection modify $select all + } + "drag" { + # Detect initial mouse movement + if {!$Priv(drag,motion)} { + set Priv(selection) [$T selection get] + set Priv(drop) "" + $T dragimage clear + # For each selected item, add some elements to the dragimage + foreach I $Priv(selection) { + foreach list $Priv(dragimage,$T) { + set C [lindex $list 0] + set S [lindex $list 1] + if {[$T item style set $I $C] eq $S} { + eval $T dragimage add $I $C [lrange $list 2 end] + } + } + } + set Priv(drag,motion) 1 + $T notify generate -T $T + } + + # Find the element under the cursor + set drop "" + set id [$T identify $x $y] + set ok 0 + if {($id ne "") && ([lindex $id 0] eq "item") && ([llength $id] == 6)} { + set E [lindex $id 5] + if {[lsearch -exact $Priv(sensitive,$T) $E] != -1} { + set ok 1 + } + } + if {$ok} { + set item [lindex $id 1] + set column [lindex $id 3] + if {$column == 0} { + # If the item is not in the pre-drag selection + # (i.e. not being dragged) and it is a directory, + # see if we can drop on it + if {[lsearch -exact $Priv(selection) $item] == -1} { + if {[lindex [$T item index $item] 1] < $Priv(DirCnt,$T)} { + set drop $item + # We can drop if dragged item isn't an ancestor + foreach item2 $Priv(selection) { + if {[$T item isancestor $item2 $item]} { + set drop "" + break + } + } + } + } + } + } + + # Select the directory under the cursor (if any) and deselect + # the previous drop-directory (if any) + $T selection modify $drop $Priv(drop) + set Priv(drop) $drop + + # Show the dragimage in its new position + set x [expr {[$T canvasx $x] - $Priv(drag,x)}] + set y [expr {[$T canvasy $y] - $Priv(drag,y)}] + $T dragimage offset $x $y + $T dragimage visible yes + } + } + return +} +proc TreeCtrl::FileListLeave1 {T x y} { + variable Priv +# This gets called when I click the mouse on Unix, and buttonMode is unset +if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + "header" { + Leave1 $T $x $y + } + } + return +} +proc TreeCtrl::FileListRelease1 {T x y} { + variable Priv + switch $Priv(buttonMode) { + "resize" - + "header" { + Release1 $T $x $y + } + "marquee" { + AutoScanCancel $T + MarqueeEnd $T $x $y + } + "drag" { + AutoScanCancel $T + + # Some dragging occurred + if {$Priv(drag,motion)} { + $T dragimage visible no + if {$Priv(drop) ne ""} { + $T selection modify {} $Priv(drop) + $T notify generate -T $T -I $Priv(drop) \ + -l $Priv(selection) + } + $T notify generate -T $T + + } elseif {$Priv(selectMode) eq "toggle"} { + # don't rename + + # Clicked/released a selected item, but didn't drag + } elseif {$Priv(drag,wasSel)} { + set I [$T index active] + set E $Priv(drag,E) + set S [$T item style set $I 0] + if {[lsearch -exact $Priv(edit,$T) $E] != -1} { + FileListEditCancel $T + set Priv(editId,$T) [after 900 [list ::TreeCtrl::FileListEdit $T $I $S $E]] + } + } + } + } + set Priv(buttonMode) "" + return +} +proc TreeCtrl::FileListEdit {T I S E} { + variable Priv + array unset Priv editId,$T + set lines [$T item element cget $I 0 $E -lines] + if {$lines eq ""} { + set lines [$T element cget $E -lines] + } + + # Scroll item into view + $T see $I ; update + + # Multi-line edit + if {$lines ne "1"} { + scan [$T item bbox $I 0] "%d %d %d %d" x1 y1 x2 y2 + set padw [$T style layout $S $E -padw] + set pade [$T style layout $S $E -pade] + TextExpanderOpen $T $I 0 $E [expr {$x2 - $x1 - $padw - $pade}] + + # Single-line edit + } else { + EntryExpanderOpen $T $I 0 $E + } + return +} +proc TreeCtrl::FileListEditCancel {T} { + variable Priv + if {[info exists Priv(editId,$T)]} { + after cancel $Priv(editId,$T) + array unset Priv editId,$T + } + return +} + +# Same as TreeCtrl::AutoScanCheck, but calls FileListMotion and +# FileListAutoScanCheckAux +proc TreeCtrl::FileListAutoScanCheck {T x y} { + variable Priv + scan [$T contentbox] "%d %d %d %d" x1 y1 x2 y2 + set margin [winfo pixels $T [$T cget -scrollmargin]] + if {($x < $x1 + $margin) || ($x >= $x2 - $margin) || + ($y < $y1 + $margin) || ($y >= $y2 - $margin)} { + if {![info exists Priv(autoscan,afterId,$T)]} { + if {$y >= $y2 - $margin} { + $T yview scroll 1 units + set delay [$T cget -yscrolldelay] + } elseif {$y < $y1 + $margin} { + $T yview scroll -1 units + set delay [$T cget -yscrolldelay] + } elseif {$x >= $x2 - $margin} { + $T xview scroll 1 units + set delay [$T cget -xscrolldelay] + } elseif {$x < $x1 + $margin} { + $T xview scroll -1 units + set delay [$T cget -xscrolldelay] + } + set count [scan $delay "%d %d" d1 d2] + if {[info exists Priv(autoscan,scanning,$T)]} { + if {$count == 2} { + set delay $d2 + } + } else { + if {$count == 2} { + set delay $d1 + } + set Priv(autoscan,scanning,$T) 1 + } + switch $Priv(buttonMode) { + "drag" - + "marquee" { + FileListMotion $T $x $y + } + } + set Priv(autoscan,afterId,$T) [after $delay [list TreeCtrl::FileListAutoScanCheckAux $T]] + } + return + } + AutoScanCancel $T + return +} + +proc ::TreeCtrl::FileListAutoScanCheckAux {T} { + variable Priv + unset Priv(autoscan,afterId,$T) + set x [winfo pointerx $T] + set y [winfo pointery $T] + set x [expr {$x - [winfo rootx $T]}] + set y [expr {$y - [winfo rooty $T]}] + FileListAutoScanCheck $T $x $y + return +} + +proc ::TreeCtrl::EntryOpen {T item column element} { + + variable Priv + + set Priv(entry,$T,item) $item + set Priv(entry,$T,column) $column + set Priv(entry,$T,element) $element + set Priv(entry,$T,focus) [focus] + + # Get window coords of the Element + scan [$T item bbox $item $column $element] "%d %d" x y + + # Get the font used by the Element + set font [$T item element actual $item $column $element -font] + + # Get the text used by the Element. Could check master Element too. + set text [$T item element cget $item $column $element -text] + + # Create the Entry widget if needed + if {[winfo exists $T.entry]} { + $T.entry delete 0 end + } else { + entry $T.entry -borderwidth 1 -highlightthickness 0 -relief solid + + # Accept edit when we lose the focus + bind $T.entry { + if {[winfo ismapped %W]} { + TreeCtrl::EntryClose [winfo parent %W] 1 + } + } + + # Accept edit on + bind $T.entry { + TreeCtrl::EntryClose [winfo parent %W] 1 + focus $TreeCtrl::Priv(entry,[winfo parent %W],focus) + } + + # Cancel edit on + bind $T.entry { + TreeCtrl::EntryClose [winfo parent %W] 0 + focus $TreeCtrl::Priv(entry,[winfo parent %W],focus) + } + } + + # Pesky MouseWheel + $T notify bind $T.entry { + TreeCtrl::EntryClose %T 0 + focus $TreeCtrl::Priv(entry,%T,focus) + } + + $T.entry configure -font $font + $T.entry insert end $text + $T.entry selection range 0 end + + set ebw [$T.entry cget -borderwidth] + if 1 { + set ex [expr {$x - $ebw - 1}] + place $T.entry -x $ex -y [expr {$y - $ebw - 1}] \ + -bordermode outside + } else { + set hw [$T cget -highlightthickness] + set bw [$T cget -borderwidth] + set ex [expr {$x - $bw - $hw - $ebw - 1}] + place $T.entry -x $ex -y [expr {$y - $bw - $hw - $ebw - 1}] + } + + # Make the Entry as wide as the text plus "W" but keep it within the + # TreeCtrl borders + set width [font measure $font ${text}W] + set width [expr {$width + ($ebw + 1) * 2}] + scan [$T contentbox] "%d %d %d %d" left top right bottom + if {$ex + $width > $right} { + set width [expr {$right - $ex}] + } + scan [$T item bbox $item $column] "%d %d %d %d" left top right bottom + if {$ex + $width > $right} { + set width [expr {$right - $ex}] + } + place configure $T.entry -width $width + + focus $T.entry + + return +} + +# Like EntryOpen, but Entry widget expands/shrinks during typing +proc ::TreeCtrl::EntryExpanderOpen {T item column element} { + + variable Priv + + set Priv(entry,$T,item) $item + set Priv(entry,$T,column) $column + set Priv(entry,$T,element) $element + set Priv(entry,$T,focus) [focus] + + # Get window coords of the Element + scan [$T item bbox $item $column $element] "%d %d" x y + + # Get the font used by the Element + set font [$T item element actual $item $column $element -font] + + set Priv(entry,$T,font) $font + + # Get the text used by the Element. Could check master Element too. + set text [$T item element cget $item $column $element -text] + + # Create the Entry widget if needed + if {[winfo exists $T.entry]} { + $T.entry delete 0 end + } else { + entry $T.entry -borderwidth 1 -highlightthickness 0 \ + -selectborderwidth 0 -relief solid + + # Accept edit when we lose the focus + bind $T.entry { + if {[winfo ismapped %W]} { + TreeCtrl::EntryClose [winfo parent %W] 1 + } + } + + # Accept edit on + bind $T.entry { + TreeCtrl::EntryClose [winfo parent %W] 1 + focus $TreeCtrl::Priv(entry,[winfo parent %W],focus) + } + + # Cancel edit on + bind $T.entry { + TreeCtrl::EntryClose [winfo parent %W] 0 + focus $TreeCtrl::Priv(entry,[winfo parent %W],focus) + } + + # Resize as user types + bind $T.entry { + after idle TreeCtrl::EntryExpanderKeypress [winfo parent %W] + } + } + + # Pesky MouseWheel + $T notify bind $T.entry { + TreeCtrl::EntryClose %T 0 + focus $TreeCtrl::Priv(entry,%T,focus) + } + + $T.entry configure -font $font -background [$T cget -background] + $T.entry insert end $text + $T.entry selection range 0 end + + set ebw [$T.entry cget -borderwidth] + set ex [expr {$x - $ebw - 1}] + place $T.entry -x $ex -y [expr {$y - $ebw - 1}] \ + -bordermode outside + + # Make the Entry as wide as the text plus "W" but keep it within the + # TreeCtrl borders + set width [font measure $font ${text}W] + set width [expr {$width + ($ebw + 1) * 2}] + scan [$T contentbox] "%d %d %d %d" left top right bottom + if {$ex + $width > $right} { + set width [expr {$right - $ex}] + } + place configure $T.entry -width $width + + focus $T.entry + + return +} + +proc ::TreeCtrl::EntryClose {T accept} { + + variable Priv + + place forget $T.entry + update + + if {$accept} { + $T notify generate -T $T -I $Priv(entry,$T,item) \ + -C $Priv(entry,$T,column) -E $Priv(entry,$T,element) \ + -t [$T.entry get] + } + + $T notify bind $T.entry {} + + return +} + +proc ::TreeCtrl::EntryExpanderKeypress {T} { + + variable Priv + + set font $Priv(entry,$T,font) + set text [$T.entry get] + set ebw [$T.entry cget -borderwidth] + set ex [winfo x $T.entry] + + set width [font measure $font ${text}W] + set width [expr {$width + ($ebw + 1) * 2}] + + scan [$T contentbox] "%d %d %d %d" left top right bottom + if {$ex + $width > $right} { + set width [expr {$right - $ex}] + } + + place configure $T.entry -width $width + + return +} + +proc ::TreeCtrl::TextOpen {T item column element {width 0} {height 0}} { + + variable Priv + + set Priv(text,$T,item) $item + set Priv(text,$T,column) $column + set Priv(text,$T,element) $element + set Priv(text,$T,focus) [focus] + + # Get window coords of the Element + scan [$T item bbox $item $column $element] "%d %d %d %d" x1 y1 x2 y2 + + # Get the font used by the Element + set font [$T item element actual $item $column $element -font] + + # Get the text used by the Element. Could check master Element too. + set text [$T item element cget $item $column $element -text] + + # Create the Text widget if needed + if {[winfo exists $T.text]} { + $T.text delete 1.0 end + } else { + text $T.text -borderwidth 1 -highlightthickness 0 -relief solid + + # Accept edit when we lose the focus + bind $T.text { + if {[winfo ismapped %W]} { + TreeCtrl::TextClose [winfo parent %W] 1 + } + } + + # Accept edit on + bind $T.text { + TreeCtrl::TextClose [winfo parent %W] 1 + focus $TreeCtrl::Priv(text,[winfo parent %W],focus) + break + } + + # Cancel edit on + bind $T.text { + TreeCtrl::TextClose [winfo parent %W] 0 + focus $TreeCtrl::Priv(text,[winfo parent %W],focus) + } + } + + # Pesky MouseWheel + $T notify bind $T.text { + TreeCtrl::TextClose %T 0 + focus $TreeCtrl::Priv(text,%T,focus) + } + + $T.text tag configure TAG -justify [$T element cget $element -justify] + $T.text configure -font $font + $T.text insert end $text + $T.text tag add sel 1.0 end + $T.text tag add TAG 1.0 end + + set tbw [$T.text cget -borderwidth] + set tx [expr {$x1 - $tbw - 1}] + place $T.text -x $tx -y [expr {$y1 - $tbw - 1}] \ + -width [expr {$x2 - $x1 + ($tbw + 1) * 2}] \ + -height [expr {$y2 - $y1 + ($tbw + 1) * 2}] \ + -bordermode outside + + focus $T.text + + return +} + +# Like TextOpen, but Text widget expands/shrinks during typing +proc ::TreeCtrl::TextExpanderOpen {T item column element width} { + + variable Priv + + set Priv(text,$T,item) $item + set Priv(text,$T,column) $column + set Priv(text,$T,element) $element + set Priv(text,$T,focus) [focus] + + # Get window coords of the Element + scan [$T item bbox $item $column $element] "%d %d %d %d" x1 y1 x2 y2 + + set Priv(text,$T,center) [expr {$x1 + ($x2 - $x1) / 2}] + + # Get the font used by the Element + set font [$T item element actual $item $column $element -font] + + # Get the text used by the Element. Could check master Element too. + set text [$T item element cget $item $column $element -text] + + set justify [$T element cget $element -justify] + + # Create the Text widget if needed + if {[winfo exists $T.text]} { + $T.text delete 1.0 end + } else { + text $T.text -borderwidth 1 -highlightthickness 0 \ + -selectborderwidth 0 -relief solid + + # Accept edit when we lose the focus + bind $T.text { + if {[winfo ismapped %W]} { + TreeCtrl::TextClose [winfo parent %W] 1 + } + } + + # Accept edit on + bind $T.text { + TreeCtrl::TextClose [winfo parent %W] 1 + focus $TreeCtrl::Priv(text,[winfo parent %W],focus) + break + } + + # Cancel edit on + bind $T.text { + TreeCtrl::TextClose [winfo parent %W] 0 + focus $TreeCtrl::Priv(text,[winfo parent %W],focus) + } + + # Resize as user types + bind $T.text { + after idle TreeCtrl::TextExpanderKeypress [winfo parent %W] + } + } + + # Pesky MouseWheel + $T notify bind $T.text { + TreeCtrl::TextClose %T 0 + focus $TreeCtrl::Priv(text,%T,focus) + } + + $T.text tag configure TAG -justify $justify + $T.text configure -font $font -background [$T cget -background] + $T.text insert end $text + $T.text tag add sel 1.0 end + $T.text tag add TAG 1.0 end + + set Priv(text,$T,font) $font + set Priv(text,$T,justify) $justify + set Priv(text,$T,width) $width + + scan [textlayout $font $text -justify $justify -width $width] "%d %d" width height + + set tbw [$T.text cget -borderwidth] + set tx [expr {$x1 - $tbw - 1}] + place $T.text -x $tx -y [expr {$y1 - $tbw - 1}] \ + -width [expr {$width + ($tbw + 1) * 2}] \ + -height [expr {$height + ($tbw + 1) * 2}] \ + -bordermode outside + + focus $T.text + + return +} + +proc ::TreeCtrl::TextClose {T accept} { + + variable Priv + + place forget $T.text + update + + if {$accept} { + $T notify generate -T $T -I $Priv(text,$T,item) \ + -C $Priv(text,$T,column) -E $Priv(text,$T,element) \ + -t [$T.text get 1.0 end-1c] + } + + $T notify bind $T.text {} + + return +} + +proc ::TreeCtrl::TextExpanderKeypress {T} { + + variable Priv + + set font $Priv(text,$T,font) + set justify $Priv(text,$T,justify) + set width $Priv(text,$T,width) + set center $Priv(text,$T,center) + + set text [$T.text get 1.0 end-1c] + + scan [textlayout $font $text -justify $justify -width $width] "%d %d" width height + + set tbw [$T.text cget -borderwidth] + place configure $T.text \ + -x [expr {$center - $width / 2 - $tbw - 1}] \ + -width [expr {$width + ($tbw + 1) * 2}] \ + -height [expr {$height + ($tbw + 1) * 2}] + + $T.text tag add TAG 1.0 end + + return +} + diff --git a/library/treectrl.tcl b/library/treectrl.tcl new file mode 100644 index 0000000..c973410 --- /dev/null +++ b/library/treectrl.tcl @@ -0,0 +1,825 @@ +bind TreeCtrl { + TreeCtrl::CursorCheck %W %x %y +} +bind TreeCtrl { + TreeCtrl::CursorCancel %W +} + +bind TreeCtrl { + TreeCtrl::ButtonPress1 %W %x %y +} + +bind TreeCtrl { + TreeCtrl::DoubleButton1 %W %x %y +} + +bind TreeCtrl { + TreeCtrl::Motion1 %W %x %y +} +bind TreeCtrl { + TreeCtrl::Release1 %W %x %y +} +bind TreeCtrl { + set TreeCtrl::Priv(buttonMode) normal + TreeCtrl::BeginExtend %W [%W index {nearest %x %y}] +} +bind TreeCtrl { + set TreeCtrl::Priv(buttonMode) normal + TreeCtrl::BeginToggle %W [%W index {nearest %x %y}] +} +bind TreeCtrl { + TreeCtrl::Leave1 %W %x %y +} +bind TreeCtrl {} + +bind TreeCtrl { + TreeCtrl::SetActiveItem %W [TreeCtrl::UpDown %W -1] +} +bind TreeCtrl { + TreeCtrl::ExtendUpDown %W above +} +bind TreeCtrl { + TreeCtrl::SetActiveItem %W [TreeCtrl::UpDown %W 1] +} +bind TreeCtrl { + TreeCtrl::ExtendUpDown %W below +} +bind TreeCtrl { + TreeCtrl::SetActiveItem %W [TreeCtrl::LeftRight %W -1] +} +bind TreeCtrl { + TreeCtrl::ExtendUpDown %W left +} +bind TreeCtrl { + %W xview scroll -1 pages +} +bind TreeCtrl { + TreeCtrl::SetActiveItem %W [TreeCtrl::LeftRight %W 1] +} +bind TreeCtrl { + TreeCtrl::ExtendUpDown %W right +} +bind TreeCtrl { + %W xview scroll 1 pages +} +bind TreeCtrl { + %W yview scroll -1 pages + %W activate {nearest 0 0} +} +bind TreeCtrl { + %W yview scroll 1 pages + %W activate {nearest 0 0} +} +bind TreeCtrl { + %W xview scroll -1 pages +} +bind TreeCtrl { + %W xview scroll 1 pages +} +bind TreeCtrl { + %W xview moveto 0 +} +bind TreeCtrl { + %W xview moveto 1 +} +bind TreeCtrl { + %W activate {first visible} + %W see active + %W selection modify active all +} +bind TreeCtrl { + TreeCtrl::DataExtend %W 0 +} +bind TreeCtrl { + %W activate {last visible} + %W see active + %W selection modify active all +} +bind TreeCtrl { + TreeCtrl::DataExtend %W [%W index {last visible}] +} +bind TreeCtrl <> { + if {[string equal [selection own -displayof %W] "%W"]} { + clipboard clear -displayof %W + clipboard append -displayof %W [selection get -displayof %W] + } +} +bind TreeCtrl { + TreeCtrl::BeginSelect %W [%W index active] +} +bind TreeCtrl { + TreeCtrl::BeginSelect %W [%W index active] +} +bind TreeCtrl { + TreeCtrl::BeginExtend %W [%W index active] +} +bind TreeCtrl { + TreeCtrl::BeginExtend %W [%W index active] +} +bind TreeCtrl { + TreeCtrl::Cancel %W +} +bind TreeCtrl { + TreeCtrl::SelectAll %W +} +bind TreeCtrl { + if {[string compare [%W cget -selectmode] "browse"]} { + %W selection clear + } +} + +bind TreeCtrl { + %W expand [%W index active] +} +bind TreeCtrl { + %W collapse [%W index active] +} +bind TreeCtrl { + %W toggle [%W index active] +} + + +# Additional Tk bindings that aren't part of the Motif look and feel: + +bind TreeCtrl { + %W scan mark %x %y +} +bind TreeCtrl { + %W scan dragto %x %y +} + +# The MouseWheel will typically only fire on Windows. However, +# someone could use the "event generate" command to produce one +# on other platforms. + +bind TreeCtrl { + %W yview scroll [expr {- (%D / 120) * 4}] units +} + +if {[string equal "unix" $tcl_platform(platform)]} { + # Support for mousewheels on Linux/Unix commonly comes through mapping + # the wheel to the extended buttons. If you have a mousewheel, find + # Linux configuration info at: + # http://www.inria.fr/koala/colas/mouse-wheel-scroll/ + bind TreeCtrl <4> { + if {!$tk_strictMotif} { + %W yview scroll -5 units + } + } + bind TreeCtrl <5> { + if {!$tk_strictMotif} { + %W yview scroll 5 units + } + } +} + +namespace eval ::TreeCtrl { + variable Priv + array set Priv { + prev {} + rnc {} + } +} + +proc ::TreeCtrl::CursorCheck {w x y} { + variable Priv + set id [$w identify $x $y] + if {([llength $id] == 3) && ([lindex $id 0] eq "header")} { + set column [lindex $id 1] + set side [lindex $id 2] + set visCount 0 + for {set i 0} {$i < [$w numcolumns]} {incr i} { + if {[$w column cget $i -visible]} { + lappend visColumns $i + if {$i eq $column} { + set columnIndex $visCount + } + incr visCount + } + } + lappend visColumns tail + if {$column eq "tail"} { + set columnIndex $visCount + } + if {$side eq "left"} { + if {$column eq [lindex $visColumns 0]} { + return + } + set column [lindex $visColumns [expr {$columnIndex - 1}]] + } + if {![info exists Priv(cursor,$w)]} { + set Priv(cursor,$w) [$w cget -cursor] + $w configure -cursor sb_h_double_arrow + if {[info exists Priv(cursor,afterId,$w)]} { + after cancel $Priv(cursor,afterId,$w) + } + set Priv(cursor,afterId,$w) [after 150 [list TreeCtrl::CursorCheckAux $w]] + } + return + } + CursorCancel $w + return +} + +proc ::TreeCtrl::CursorCheckAux {w} { + variable Priv + set x [winfo pointerx $w] + set y [winfo pointery $w] + if {[info exists Priv(cursor,$w)]} { + set x [expr {$x - [winfo rootx $w]}] + set y [expr {$y - [winfo rooty $w]}] + CursorCheck $w $x $y + } + return +} + +proc ::TreeCtrl::CursorCancel {w} { + variable Priv + if {[info exists Priv(cursor,$w)]} { + $w configure -cursor $Priv(cursor,$w) + unset Priv(cursor,$w) + } + if {[info exists Priv(cursor,afterId,$w)]} { + after cancel $Priv(cursor,afterId,$w) + unset Priv(cursor,afterId,$w) + } + return +} + +proc ::TreeCtrl::ButtonPress1 {w x y} { + variable Priv + focus $w + set id [$w identify $x $y] + if {$id eq ""} { + return + } + if {[lindex $id 0] eq "item"} { + foreach {where item arg1 arg2} $id {} + if {$arg1 eq "button"} { + $w toggle $item + return + } elseif {$arg1 eq "line"} { + $w toggle $arg2 + return + } + } + set Priv(buttonMode) "" + if {[lindex $id 0] eq "header"} { + set column [lindex $id 1] + set visCount 0 + for {set i 0} {$i < [$w numcolumns]} {incr i} { + if {[$w column cget $i -visible]} { + lappend visColumns $i + if {$i eq $column} { + set columnIndex $visCount + } + incr visCount + } + } + lappend visColumns tail + if {$column eq "tail"} { + set columnIndex $visCount + } + if {[llength $id] == 3} { + set side [lindex $id 2] + if {$side == "left"} { + if {$column eq [lindex $visColumns 0]} { + return + } + set column [lindex $visColumns [expr {$columnIndex - 1}]] + } + set Priv(buttonMode) resize + set Priv(column) $column + set Priv(x) $x + set Priv(y) $y + set Priv(width) [$w column width $column] + return + } + if {$column eq "tail"} return + if {![$w column cget $column -button]} return + set Priv(buttonMode) header + set Priv(column) $column + $w column configure $column -sunken yes + return + } + set Priv(buttonMode) normal + BeginSelect $w [lindex $id 1] + return +} + +# Double-click between columns to set default column width +proc ::TreeCtrl::DoubleButton1 {w x y} { + set id [$w identify $x $y] + if {$id eq ""} { + return + } + if {[lindex $id 0] eq "header"} { + set column [lindex $id 1] + set visCount 0 + for {set i 0} {$i < [$w numcolumns]} {incr i} { + if {[$w column cget $i -visible]} { + lappend visColumns $i + if {$i eq $column} { + set columnIndex $visCount + } + incr visCount + } + } + lappend visColumns tail + if {$column eq "tail"} { + set columnIndex $visCount + } + if {[llength $id] == 3} { + set side [lindex $id 2] + if {$side == "left"} { + if {$column eq [lindex $visColumns 0]} { + return + } + set column [lindex $visColumns [expr {$columnIndex - 1}]] + } + if {$column eq "tail"} return + $w column configure $column -width "" + } + } + return +} + +proc ::TreeCtrl::Motion1 {w x y} { + variable Priv + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + header { + set id [$w identify $x $y] + if {![string match "header $Priv(column)*" $id]} { + if {[$w column cget $Priv(column) -sunken]} { + $w column configure $Priv(column) -sunken no + } + } else { + if {![$w column cget $Priv(column) -sunken]} { + $w column configure $Priv(column) -sunken yes + } + } + } + normal { + set Priv(x) $x + set Priv(y) $y + Motion $w [$w index [list nearest $x $y]] + AutoScanCheck $w $x $y + } + resize { + set width [expr {$Priv(width) + $x - $Priv(x)}] + set minWidth [$w column cget $Priv(column) -minwidth] + if {$minWidth eq ""} { + set minWidth 0 + } + if {$width < $minWidth} { + set width $minWidth + } + if {$width == 0} { + incr width + } + scan [$w column bbox $Priv(column)] "%d %d %d %d" x1 y1 x2 y2 + # Use "ne" because -columnproxy could be "" + if {($x1 + $width - 1) ne [$w cget -columnproxy]} { + $w configure -columnproxy [expr {$x1 + $width - 1}] + } + } + } + return +} + +proc ::TreeCtrl::Leave1 {w x y} { + variable Priv + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + header { + if {[$w column cget $Priv(column) -sunken]} { + $w column configure $Priv(column) -sunken no + } + } + normal { + } + resize {} + } + return +} + +proc ::TreeCtrl::Release1 {w x y} { + variable Priv + if {![info exists Priv(buttonMode)]} return + switch $Priv(buttonMode) { + header { + if {[$w column cget $Priv(column) -sunken]} { + $w column configure $Priv(column) -sunken no + $w notify generate -T $w -C $Priv(column) + } + } + normal { + AutoScanCancel $w + $w activate [$w index [list nearest $x $y]] + } + resize { + if {[$w cget -columnproxy] ne ""} { + scan [$w column bbox $Priv(column)] "%d %d %d %d" x1 y1 x2 y2 + set width [expr {[$w cget -columnproxy] - $x1 + 1}] + $w configure -columnproxy {} + $w column configure $Priv(column) -width $width + CursorCheck $w $x $y + } + } + } + unset Priv(buttonMode) + return +} + +# ::TreeCtrl::BeginSelect -- +# +# This procedure is typically invoked on button-1 presses. It begins +# the process of making a selection in the listbox. Its exact behavior +# depends on the selection mode currently in effect for the listbox; +# see the Motif documentation for details. +# +# Arguments: +# w - The listbox widget. +# el - The element for the selection operation (typically the +# one under the pointer). Must be in numerical form. + +proc ::TreeCtrl::BeginSelect {w el} { + variable Priv + if {$el eq ""} return + if {[string equal [$w cget -selectmode] "multiple"]} { + if {[$w selection includes $el]} { + $w selection clear $el + } else { + $w selection add $el + } + } else { + $w selection anchor $el + $w selection modify $el all + set Priv(selection) {} + set Priv(prev) $el + } +} + +# ::TreeCtrl::Motion -- +# +# This procedure is called to process mouse motion events while +# button 1 is down. It may move or extend the selection, depending +# on the listbox's selection mode. +# +# Arguments: +# w - The listbox widget. +# el - The element under the pointer (must be a number). + +proc ::TreeCtrl::Motion {w el} { + variable Priv + if {$el eq $Priv(prev)} { + return + } + switch [$w cget -selectmode] { + browse { + $w selection modify $el all + set Priv(prev) $el + } + extended { + set i $Priv(prev) + if {$i eq ""} { + set i $el + $w selection add $el + } + if {[$w selection includes anchor]} { + $w selection clear $i $el + $w selection add anchor $el + } else { + $w selection clear $i $el + $w selection clear anchor $el + } + if {![info exists Priv(selection)]} { + set Priv(selection) [$w selection get] + } + while {[$w compare $i < $el] && [$w compare $i < anchor]} { + if {[lsearch $Priv(selection) $i] >= 0} { + $w selection add $i + } + set i [$w index "$i next visible"] + } + while {[$w compare $i > $el] && [$w compare $i > anchor]} { + if {[lsearch $Priv(selection) $i] >= 0} { + $w selection add $i + } + set i [$w index "$i prev visible"] + } + set Priv(prev) $el + } + } +} + +# ::TreeCtrl::BeginExtend -- +# +# This procedure is typically invoked on shift-button-1 presses. It +# begins the process of extending a selection in the listbox. Its +# exact behavior depends on the selection mode currently in effect +# for the listbox; see the Motif documentation for details. +# +# Arguments: +# w - The listbox widget. +# el - The element for the selection operation (typically the +# one under the pointer). Must be in numerical form. + +proc ::TreeCtrl::BeginExtend {w el} { + if {[string equal [$w cget -selectmode] "extended"]} { + if {[$w selection includes anchor]} { + Motion $w $el + } else { + # No selection yet; simulate the begin-select operation. + BeginSelect $w $el + } + } +} + +# ::TreeCtrl::BeginToggle -- +# +# This procedure is typically invoked on control-button-1 presses. It +# begins the process of toggling a selection in the listbox. Its +# exact behavior depends on the selection mode currently in effect +# for the listbox; see the Motif documentation for details. +# +# Arguments: +# w - The listbox widget. +# el - The element for the selection operation (typically the +# one under the pointer). Must be in numerical form. + +proc ::TreeCtrl::BeginToggle {w el} { + variable Priv + if {[string equal [$w cget -selectmode] "extended"]} { + set Priv(selection) [$w selection get] + set Priv(prev) $el + $w selection anchor $el + if {[$w selection includes $el]} { + $w selection clear $el + } else { + $w selection add $el + } + } +} + +proc ::TreeCtrl::CancelRepeat {} { + variable Priv + if {[info exists Priv(afterId)]} { + after cancel $Priv(afterId) + unset Priv(afterId) + } +} + +proc ::TreeCtrl::AutoScanCheck {w x y} { + variable Priv + scan [$w contentbox] "%d %d %d %d" x1 y1 x2 y2 + set margin [winfo pixels $w [$w cget -scrollmargin]] + if {($x < $x1 + $margin) || ($x >= $x2 - $margin) || + ($y < $y1 + $margin) || ($y >= $y2 - $margin)} { + if {![info exists Priv(autoscan,afterId,$w)]} { + if {$y >= $y2 - $margin} { + $w yview scroll 1 units + set delay [$w cget -yscrolldelay] + } elseif {$y < $y1 + $margin} { + $w yview scroll -1 units + set delay [$w cget -yscrolldelay] + } elseif {$x >= $x2 - $margin} { + $w xview scroll 1 units + set delay [$w cget -xscrolldelay] + } elseif {$x < $x1 + $margin} { + $w xview scroll -1 units + set delay [$w cget -xscrolldelay] + } + set count [scan $delay "%d %d" d1 d2] + if {[info exists Priv(autoscan,scanning,$w)]} { + if {$count == 2} { + set delay $d2 + } + } else { + if {$count == 2} { + set delay $d1 + } + set Priv(autoscan,scanning,$w) 1 + } + Motion $w [$w index "nearest $x $y"] + set Priv(autoscan,afterId,$w) [after $delay [list TreeCtrl::AutoScanCheckAux $w]] + } + return + } + AutoScanCancel $w + return +} + +proc ::TreeCtrl::AutoScanCheckAux {w} { + variable Priv + unset Priv(autoscan,afterId,$w) + set x [winfo pointerx $w] + set y [winfo pointery $w] + set x [expr {$x - [winfo rootx $w]}] + set y [expr {$y - [winfo rooty $w]}] + AutoScanCheck $w $x $y + return +} + +proc ::TreeCtrl::AutoScanCancel {w} { + variable Priv + if {[info exists Priv(autoscan,afterId,$w)]} { + after cancel $Priv(autoscan,afterId,$w) + unset Priv(autoscan,afterId,$w) + } + unset -nocomplain Priv(autoscan,scanning,$w) + return +} + +# ::TreeCtrl::UpDown -- +# +# Moves the location cursor (active element) up or down by one element, +# and changes the selection if we're in browse or extended selection +# mode. +# +# Arguments: +# w - The listbox widget. +# amount - +1 to move down one item, -1 to move back one item. + +proc ::TreeCtrl::UpDown {w n} { + variable Priv + set rnc [$w item rnc active] + # active item isn't visible + if {$rnc eq ""} { + set rnc [$w item rnc first] + if {$rnc eq ""} return + } + scan $rnc "%d %d" row col + set Priv(row) [expr {$row + $n}] + if {$rnc ne $Priv(rnc)} { + set Priv(col) $col + } + set index [$w index "rnc $Priv(row) $Priv(col)"] + if {[$w compare active == $index]} { + set Priv(row) $row + } else { + set Priv(rnc) [$w item rnc $index] + } + return $index +} + +proc ::TreeCtrl::LeftRight {w n} { + variable Priv + set rnc [$w item rnc active] + if {$rnc eq ""} { + set rnc [$w item rnc first] + if {$rnc eq ""} return + } + scan $rnc "%d %d" row col + set Priv(col) [expr {$col + $n}] + if {$rnc ne $Priv(rnc)} { + set Priv(row) $row + } + set index [$w index "rnc $Priv(row) $Priv(col)"] + if {[$w compare active == $index]} { + set Priv(col) $col + } else { + set Priv(rnc) [$w item rnc $index] + } + return $index +} + +proc ::TreeCtrl::SetActiveItem {w index} { + if {$index eq ""} return + $w activate $index + $w see active + $w selection modify active all + switch [$w cget -selectmode] { + extended { + $w selection anchor active + set Priv(prev) [$w index active] + set Priv(selection) {} + } + } +} + +# ::TreeCtrl::ExtendUpDown -- +# +# Does nothing unless we're in extended selection mode; in this +# case it moves the location cursor (active element) up or down by +# one element, and extends the selection to that point. +# +# Arguments: +# w - The listbox widget. +# amount - +1 to move down one item, -1 to move back one item. + +proc ::TreeCtrl::ExtendUpDown {w amount} { + variable Priv + if {[string compare [$w cget -selectmode] "extended"]} { + return + } + set active [$w index active] + if {![info exists Priv(selection)]} { + $w selection add $active + set Priv(selection) [$w selection get] + } + set index [$w index "active $amount"] + if {$index eq ""} return + $w activate $index + $w see active + Motion $w [$w index active] +} + +# ::TreeCtrl::DataExtend +# +# This procedure is called for key-presses such as Shift-KEndData. +# If the selection mode isn't multiple or extend then it does nothing. +# Otherwise it moves the active element to el and, if we're in +# extended mode, extends the selection to that point. +# +# Arguments: +# w - The listbox widget. +# el - An integer element number. + +proc ::TreeCtrl::DataExtend {w el} { + set mode [$w cget -selectmode] + if {[string equal $mode "extended"]} { + $w activate $el + $w see $el + if {[$w selection includes anchor]} { + Motion $w $el + } + } elseif {[string equal $mode "multiple"]} { + $w activate $el + $w see $el + } +} + +# ::TreeCtrl::Cancel +# +# This procedure is invoked to cancel an extended selection in +# progress. If there is an extended selection in progress, it +# restores all of the items between the active one and the anchor +# to their previous selection state. +# +# Arguments: +# w - The listbox widget. + +proc ::TreeCtrl::Cancel w { + variable Priv + if {[string compare [$w cget -selectmode] "extended"]} { + return + } + set first [$w index anchor] + set last $Priv(prev) + if { [string equal $last ""] } { + # Not actually doing any selection right now + return + } + if {[$w compare $first > $last]} { + set tmp $first + set first $last + set last $tmp + } + $w selection clear $first $last + while {[$w compare $first <= $last]} { + if {[lsearch $Priv(selection) $first] >= 0} { + $w selection add $first + } + set first [$w index "$first next visible"] + } +} + +# ::TreeCtrl::SelectAll +# +# This procedure is invoked to handle the "select all" operation. +# For single and browse mode, it just selects the active element. +# Otherwise it selects everything in the widget. +# +# Arguments: +# w - The listbox widget. + +proc ::TreeCtrl::SelectAll w { + set mode [$w cget -selectmode] + if {[string equal $mode "single"] || [string equal $mode "browse"]} { + $w selection modify active all + } else { + $w selection add all + } +} + +proc ::TreeCtrl::MarqueeBegin {w x y} { + set x [$w canvasx $x] + set y [$w canvasy $y] + $w marquee coords $x $y $x $y + $w marquee visible yes + return +} + +proc ::TreeCtrl::MarqueeUpdate {w x y} { + set x [$w canvasx $x] + set y [$w canvasy $y] + $w marquee corner $x $y + return +} +proc ::TreeCtrl::MarqueeEnd {w x y} { + $w marquee visible no + return +} + diff --git a/pics/big-dll.gif b/pics/big-dll.gif new file mode 100644 index 0000000..09170c0 Binary files /dev/null and b/pics/big-dll.gif differ diff --git a/pics/big-exe.gif b/pics/big-exe.gif new file mode 100644 index 0000000..e19aac1 Binary files /dev/null and b/pics/big-exe.gif differ diff --git a/pics/big-file.gif b/pics/big-file.gif new file mode 100644 index 0000000..6c75231 Binary files /dev/null and b/pics/big-file.gif differ diff --git a/pics/big-folder.gif b/pics/big-folder.gif new file mode 100644 index 0000000..186c974 Binary files /dev/null and b/pics/big-folder.gif differ diff --git a/pics/big-txt.gif b/pics/big-txt.gif new file mode 100644 index 0000000..a934925 Binary files /dev/null and b/pics/big-txt.gif differ diff --git a/pics/checked.gif b/pics/checked.gif new file mode 100644 index 0000000..3b9b176 Binary files /dev/null and b/pics/checked.gif differ diff --git a/pics/file.gif b/pics/file.gif new file mode 100644 index 0000000..a64c2a0 Binary files /dev/null and b/pics/file.gif differ diff --git a/pics/folder-closed.gif b/pics/folder-closed.gif new file mode 100644 index 0000000..0a06437 Binary files /dev/null and b/pics/folder-closed.gif differ diff --git a/pics/folder-open.gif b/pics/folder-open.gif new file mode 100644 index 0000000..3fac27f Binary files /dev/null and b/pics/folder-open.gif differ diff --git a/pics/help-book-closed.gif b/pics/help-book-closed.gif new file mode 100644 index 0000000..0a0497b Binary files /dev/null and b/pics/help-book-closed.gif differ diff --git a/pics/help-book-open.gif b/pics/help-book-open.gif new file mode 100644 index 0000000..40656c5 Binary files /dev/null and b/pics/help-book-open.gif differ diff --git a/pics/help-page.gif b/pics/help-page.gif new file mode 100644 index 0000000..e1ce1d7 Binary files /dev/null and b/pics/help-page.gif differ diff --git a/pics/imovie-01.gif b/pics/imovie-01.gif new file mode 100644 index 0000000..5fd9215 Binary files /dev/null and b/pics/imovie-01.gif differ diff --git a/pics/imovie-02.gif b/pics/imovie-02.gif new file mode 100644 index 0000000..3d2d1c1 Binary files /dev/null and b/pics/imovie-02.gif differ diff --git a/pics/imovie-03.gif b/pics/imovie-03.gif new file mode 100644 index 0000000..9fccf12 Binary files /dev/null and b/pics/imovie-03.gif differ diff --git a/pics/imovie-04.gif b/pics/imovie-04.gif new file mode 100644 index 0000000..eff851c Binary files /dev/null and b/pics/imovie-04.gif differ diff --git a/pics/imovie-05.gif b/pics/imovie-05.gif new file mode 100644 index 0000000..ad00c82 Binary files /dev/null and b/pics/imovie-05.gif differ diff --git a/pics/imovie-06.gif b/pics/imovie-06.gif new file mode 100644 index 0000000..238bf16 Binary files /dev/null and b/pics/imovie-06.gif differ diff --git a/pics/imovie-07.gif b/pics/imovie-07.gif new file mode 100644 index 0000000..a9287e1 Binary files /dev/null and b/pics/imovie-07.gif differ diff --git a/pics/internet-check-off.gif b/pics/internet-check-off.gif new file mode 100644 index 0000000..e64866a Binary files /dev/null and b/pics/internet-check-off.gif differ diff --git a/pics/internet-check-on.gif b/pics/internet-check-on.gif new file mode 100644 index 0000000..cf652be Binary files /dev/null and b/pics/internet-check-on.gif differ diff --git a/pics/internet-print.gif b/pics/internet-print.gif new file mode 100644 index 0000000..7ac25b1 Binary files /dev/null and b/pics/internet-print.gif differ diff --git a/pics/internet-radio-off.gif b/pics/internet-radio-off.gif new file mode 100644 index 0000000..90ef629 Binary files /dev/null and b/pics/internet-radio-off.gif differ diff --git a/pics/internet-radio-on.gif b/pics/internet-radio-on.gif new file mode 100644 index 0000000..9de742c Binary files /dev/null and b/pics/internet-radio-on.gif differ diff --git a/pics/internet-search.gif b/pics/internet-search.gif new file mode 100644 index 0000000..1f9a047 Binary files /dev/null and b/pics/internet-search.gif differ diff --git a/pics/internet-security.gif b/pics/internet-security.gif new file mode 100644 index 0000000..86d3943 Binary files /dev/null and b/pics/internet-security.gif differ diff --git a/pics/mac-collapse.gif b/pics/mac-collapse.gif new file mode 100644 index 0000000..81302c8 Binary files /dev/null and b/pics/mac-collapse.gif differ diff --git a/pics/mac-expand.gif b/pics/mac-expand.gif new file mode 100644 index 0000000..0a2cd80 Binary files /dev/null and b/pics/mac-expand.gif differ diff --git a/pics/outlook-arrow.gif b/pics/outlook-arrow.gif new file mode 100644 index 0000000..b1c2afd Binary files /dev/null and b/pics/outlook-arrow.gif differ diff --git a/pics/outlook-clip.gif b/pics/outlook-clip.gif new file mode 100644 index 0000000..8578132 Binary files /dev/null and b/pics/outlook-clip.gif differ diff --git a/pics/outlook-deleted.gif b/pics/outlook-deleted.gif new file mode 100644 index 0000000..7cc8369 Binary files /dev/null and b/pics/outlook-deleted.gif differ diff --git a/pics/outlook-draft.gif b/pics/outlook-draft.gif new file mode 100644 index 0000000..f195850 Binary files /dev/null and b/pics/outlook-draft.gif differ diff --git a/pics/outlook-folder.gif b/pics/outlook-folder.gif new file mode 100644 index 0000000..b3f7335 Binary files /dev/null and b/pics/outlook-folder.gif differ diff --git a/pics/outlook-group.gif b/pics/outlook-group.gif new file mode 100644 index 0000000..29ad9b4 Binary files /dev/null and b/pics/outlook-group.gif differ diff --git a/pics/outlook-inbox.gif b/pics/outlook-inbox.gif new file mode 100644 index 0000000..f41d804 Binary files /dev/null and b/pics/outlook-inbox.gif differ diff --git a/pics/outlook-local.gif b/pics/outlook-local.gif new file mode 100644 index 0000000..0c74970 Binary files /dev/null and b/pics/outlook-local.gif differ diff --git a/pics/outlook-main.gif b/pics/outlook-main.gif new file mode 100644 index 0000000..92325fa Binary files /dev/null and b/pics/outlook-main.gif differ diff --git a/pics/outlook-outbox.gif b/pics/outlook-outbox.gif new file mode 100644 index 0000000..c7e8052 Binary files /dev/null and b/pics/outlook-outbox.gif differ diff --git a/pics/outlook-read-2.gif b/pics/outlook-read-2.gif new file mode 100644 index 0000000..2f15a3a Binary files /dev/null and b/pics/outlook-read-2.gif differ diff --git a/pics/outlook-read.gif b/pics/outlook-read.gif new file mode 100644 index 0000000..a6f9562 Binary files /dev/null and b/pics/outlook-read.gif differ diff --git a/pics/outlook-sent.gif b/pics/outlook-sent.gif new file mode 100644 index 0000000..963b56c Binary files /dev/null and b/pics/outlook-sent.gif differ diff --git a/pics/outlook-server.gif b/pics/outlook-server.gif new file mode 100644 index 0000000..c950845 Binary files /dev/null and b/pics/outlook-server.gif differ diff --git a/pics/outlook-unread.gif b/pics/outlook-unread.gif new file mode 100644 index 0000000..3df4b99 Binary files /dev/null and b/pics/outlook-unread.gif differ diff --git a/pics/outlook-watch.gif b/pics/outlook-watch.gif new file mode 100644 index 0000000..87ec861 Binary files /dev/null and b/pics/outlook-watch.gif differ diff --git a/pics/small-dll.gif b/pics/small-dll.gif new file mode 100644 index 0000000..d8875ec Binary files /dev/null and b/pics/small-dll.gif differ diff --git a/pics/small-exe.gif b/pics/small-exe.gif new file mode 100644 index 0000000..69d30be Binary files /dev/null and b/pics/small-exe.gif differ diff --git a/pics/small-file.gif b/pics/small-file.gif new file mode 100644 index 0000000..f340662 Binary files /dev/null and b/pics/small-file.gif differ diff --git a/pics/small-folder.gif b/pics/small-folder.gif new file mode 100644 index 0000000..ad1b24d Binary files /dev/null and b/pics/small-folder.gif differ diff --git a/pics/small-txt.gif b/pics/small-txt.gif new file mode 100644 index 0000000..cdc7cbf Binary files /dev/null and b/pics/small-txt.gif differ diff --git a/pics/unchecked.gif b/pics/unchecked.gif new file mode 100644 index 0000000..833e482 Binary files /dev/null and b/pics/unchecked.gif differ diff --git a/pkgIndex.tcl.in b/pkgIndex.tcl.in new file mode 100644 index 0000000..f372c53 --- /dev/null +++ b/pkgIndex.tcl.in @@ -0,0 +1,9 @@ +proc ::TreeCtrlLoad {dir} { + uplevel #0 source [file join $dir treectrl.tcl] + uplevel #0 source [file join $dir filelist-bindings.tcl] + tclPkgSetup $dir treectrl @VERSION@ { + {@treectrl_LIB_FILE@ load {treectrl imagetint textlayout}} + } +} +package ifneeded treectrl @VERSION@ [list ::TreeCtrlLoad $dir] + diff --git a/tclconfig/ChangeLog b/tclconfig/ChangeLog new file mode 100644 index 0000000..758276f --- /dev/null +++ b/tclconfig/ChangeLog @@ -0,0 +1,161 @@ +2002-10-15 Jeff Hobbs + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * README.txt: updated to reflect fewer files + +2002-03-06 Jeff Hobbs + + * 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 + + * 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 + + * 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 + + * installFile.tcl: Added support for converting new-style (1.1+) + Cygnus drive paths to Tcl-style. + +2001-01-15 + + * tcl.m4: Added FreeBSD clause. + +2001-01-03 + + * tcl.m4: Fixed typo in SC_LIB_SPEC where it is checking + for exec-prefix. + +2000-12-01 + + * 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 + + * 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)/.. diff --git a/tclconfig/README.txt b/tclconfig/README.txt new file mode 100644 index 0000000..9055a58 --- /dev/null +++ b/tclconfig/README.txt @@ -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 SC_* macros. diff --git a/tclconfig/install-sh b/tclconfig/install-sh new file mode 100644 index 0000000..0ff4b6a --- /dev/null +++ b/tclconfig/install-sh @@ -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 diff --git a/tclconfig/tcl.m4 b/tclconfig/tcl.m4 new file mode 100644 index 0000000..d2421cf --- /dev/null +++ b/tclconfig/tcl.m4 @@ -0,0 +1,3171 @@ +# 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 ActiveState Corporation. +# +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. + +#------------------------------------------------------------------------ +# 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, [ + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + AC_MSG_ERROR([Must call TEA INIT before PATH_TCLCONFIG]) + fi + # + # 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, [ --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 + 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]]* 2>/dev/null` \ + ../../tcl \ + `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../../tcl \ + `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/unix/tclConfig.sh" ; then + ac_cv_c_tclconfig=`(cd $i/unix; 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 ${exec_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` \ + ; 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]]* 2>/dev/null` ; do + 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_WARN("Cannot find Tcl configuration definitions") + exit 0 + 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, [ --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 + 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]]* 2>/dev/null` \ + ../../tk \ + `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../../tk \ + `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/unix/tkConfig.sh" ; then + ac_cv_c_tkconfig=`(cd $i/unix; 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 ${exec_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` \ + ; 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]]* 2>/dev/null` ; do + 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_WARN("Cannot find Tk configuration definitions") + exit 0 + 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([file not found]) + fi + + # + # 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} + fi + + # + # eval is required to do the TCL_DBGX substitution + # + + eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" + eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" + eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" + + eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" + 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_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) + + #AC_SUBST(TCL_DBGX) + AC_SUBST(TCL_LIBS) + AC_SUBST(TCL_DEFS) + AC_SUBST(TCL_EXTRA_CFLAGS) + AC_SUBST(TCL_LD_FLAGS) + AC_SUBST(TCL_SHLIB_LD_LIBS) + #AC_SUBST(TCL_BUILD_LIB_SPEC) + #AC_SUBST(TCL_BUILD_STUB_LIB_SPEC) +]) + +#------------------------------------------------------------------------ +# 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 + + # + # 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} + fi + + # + # eval is required to do the TK_DBGX substitution + # + + eval "TK_LIB_FILE=\"${TK_LIB_FILE}\"" + eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\"" + eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\"" + + eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\"" + eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\"" + eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\"" + + 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) + + AC_SUBST(TK_LIBS) + AC_SUBST(TK_XINCLUDES) +]) + +#------------------------------------------------------------------------ +# 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, + [ --enable-shared build and link with shared libraries [--enable-shared]], + [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) + 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. +# +# 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 +# +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_ENABLE_THREADS, [ + AC_ARG_ENABLE(threads, [ --enable-threads build with threads], + [tcl_ok=$enableval], [tcl_ok=$1]) + + if test "$tcl_ok" = "yes"; then + TCL_THREADS=1 + + if test "${TEA_PLATFORM}" != "windows" ; then + # We are always OK on Windows, so check what this platform wants. + AC_DEFINE(USE_THREAD_ALLOC) + AC_DEFINE(_REENTRANT) + AC_DEFINE(_THREAD_SAFE) + 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("Don t know how to find pthread lib on your system - thread support disabled") + fi + fi + fi + fi + + # Does the pthread-implementation provide + # 'pthread_attr_setstacksize' ? + AC_CHECK_FUNCS(pthread_attr_setstacksize) + 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) + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no (default)]) + 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} without threads enabled, but building against a Tcl + that IS thread-enabled.]) + fi + ;; + *) + if test "${TCL_THREADS}" = "1"; then + AC_MSG_WARN([ + --enable-threads requested, but attempting building against a Tcl + that is NOT thread-enabled.]) + 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 +# +# Requires the following vars to be set: +# CFLAGS_DEBUG +# CFLAGS_OPTIMIZE +# LDFLAGS_DEBUG +# LDFLAGS_OPTIMIZE +# +# 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 Debug library extension +# +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_ENABLE_SYMBOLS, [ + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + AC_MSG_ERROR([Must call TEA INIT before ENABLE_SYMBOLS]) + fi + + if test "${TEA_PLATFORM}" = "windows" ; then + tcl_dbgx=d + else + tcl_dbgx=g + fi + + AC_MSG_CHECKING([for build with symbols]) + AC_ARG_ENABLE(symbols, [ --enable-symbols build with debugging symbols [--disable-symbols]], [tcl_ok=$enableval], [tcl_ok=no]) + if test "$tcl_ok" = "no"; then + CFLAGS_DEFAULT='$(CFLAGS_OPTIMIZE)' + LDFLAGS_DEFAULT='$(LDFLAGS_OPTIMIZE)' + DBGX="" + TCL_DBGX="" + AC_MSG_RESULT([no]) + else + CFLAGS_DEFAULT='$(CFLAGS_DEBUG)' + LDFLAGS_DEFAULT='$(LDFLAGS_DEBUG)' + DBGX=${tcl_dbgx} + TCL_DBGX=${tcl_dbgx} + if test "$tcl_ok" = "yes"; then + AC_MSG_RESULT([yes (standard debugging)]) + fi + fi + + AC_SUBST(TCL_DBGX) + AC_SUBST(CFLAGS_DEFAULT) + AC_SUBST(LDFLAGS_DEFAULT) + + if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then + AC_DEFINE(TCL_MEM_DEBUG) + 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, + [ --enable-langinfo use nl_langinfo if possible to determine + encoding at startup, otherwise use old heuristic], + [langinfo_ok=$enableval], [langinfo_ok=yes]) + + HAVE_LANGINFO=0 + if test "$langinfo_ok" = "yes"; then + if test "$langinfo_ok" = "yes"; then + AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no]) + fi + fi + AC_MSG_CHECKING([whether to use nl_langinfo]) + if test "$langinfo_ok" = "yes"; then + AC_TRY_COMPILE([#include ], + [nl_langinfo(CODESET);],[langinfo_ok=yes],[langinfo_ok=no]) + if test "$langinfo_ok" = "no"; then + langinfo_ok="no (could not compile with nl_langinfo)"; + fi + if test "$langinfo_ok" = "yes"; then + AC_DEFINE(HAVE_LANGINFO) + fi + fi + AC_MSG_RESULT([$langinfo_ok]) +]) + +#-------------------------------------------------------------------- +# 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 the following vars: +# +# DL_OBJS - Name of the object file that implements dynamic +# loading for Tcl on this system. +# DL_LIBS - Library file(s) to include in tclsh and other base +# applications in order for the "load" command to work. +# 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. +# 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 is +# "${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. +# TCL_LIB_FILE - Name of the file that contains the Tcl library, such +# as libtcl7.8.so or libtcl7.8.a. +# TCL_LIB_SUFFIX -Specifies everything that comes after the "libtcl" +# in the 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 the Tcl shared library needs to have +# a .a extension whereas shared objects for loadable +# extensions have a .so extension. Defaults to +# ${VERSION}${SHLIB_SUFFIX}. +# TCL_NEEDS_EXP_FILE - +# 1 means that an export file is needed to link to a +# shared library. +# TCL_EXP_FILE - The name of the installed export / import file which +# should be used to link to the Tcl shared library. +# Empty if Tcl is unshared. +# TCL_BUILD_EXP_FILE - +# The name of the built export / import file which +# should be used to link to the Tcl shared library. +# Empty if Tcl is unshared. +# CFLAGS_DEBUG - +# Flags used when running the compiler in debug mode +# CFLAGS_OPTIMIZE - +# Flags used when running the compiler in optimize mode +# EXTRA_CFLAGS - +# Extra CFLAGS to pass to the compiler +# +# Subst's the following vars: +# DL_LIBS +# CFLAGS_DEBUG +# CFLAGS_OPTIMIZE +# CFLAGS_WARNING +# +# STLIB_LD +# SHLIB_LD +# SHLIB_CFLAGS +# SHLIB_LDFLAGS +# LDFLAGS_DEBUG +# LDFLAGS_OPTIMIZE +#-------------------------------------------------------------------- + +AC_DEFUN(TEA_CONFIG_CFLAGS, [ + if test x"${TEA_INITED}" = x ; then + # Can't refer to exact macro name or it will be substituted + AC_MSG_ERROR([Must call TEA INIT before CONFIG_CFLAGS]) + fi + + # Step 0: Enable 64 bit support? + + AC_MSG_CHECKING([if 64bit support is enabled]) + AC_ARG_ENABLE(64bit,[ --enable-64bit enable 64bit support (where applicable)], [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,[ --enable-64bit-vis enable 64bit Sparc VIS support], [do64bitVIS=$enableval], [do64bitVIS=no]) + AC_MSG_RESULT([$do64bitVIS]) + + if test "$do64bitVIS" = "yes"; then + # Force 64bit on with VIS + do64bit=yes + fi + + # Step 1: set the variable "system" to hold the name and version number + # for the system. This can usually be done via the "uname" command, but + # there are a few systems, like Next, where this doesn't work. + + AC_MSG_CHECKING([system version (for dynamic loading)]) + if test -f /usr/lib/NextStep/software_version; then + system=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version` + else + system=`uname -s`-`uname -r` + if test "$?" -ne 0 ; then + AC_MSG_RESULT([unknown (can't find uname command)]) + system=unknown + else + # Special check for weird MP-RAS system (uname returns weird + # results, and the version is kept in special file). + + if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then + system=MP-RAS-`awk '{print $3}' /etc/.relid'` + fi + if test "`uname -s`" = "AIX" ; then + system=AIX-`uname -v`.`uname -r` + fi + if test "${TEA_PLATFORM}" = "windows" ; then + system=windows + fi + AC_MSG_RESULT([$system]) + fi + fi + + # Step 2: check for existence of -ldl library. This is needed because + # Linux can use either -ldl or -ldld for dynamic loading. + + AC_CHECK_LIB(dl, dlopen, have_dl=yes, have_dl=no) + + # Step 3: set configuration options based on system name and version. + + do64bit_ok=no + EXTRA_CFLAGS="" + TCL_EXPORT_FILE_SUFFIX="" + UNSHARED_LIB_SUFFIX="" + TCL_TRIM_DOTS='`echo ${VERSION} | tr -d .`' + ECHO_VERSION='`echo ${VERSION}`' + TCL_LIB_VERSIONS_OK=ok + CFLAGS_DEBUG=-g + CFLAGS_OPTIMIZE=-O + if test "$GCC" = "yes" ; then + CFLAGS_WARNING="-Wall -Wconversion -Wno-implicit-int" + else + CFLAGS_WARNING="" + fi + TCL_NEEDS_EXP_FILE=0 + TCL_BUILD_EXP_FILE="" + TCL_EXP_FILE="" +dnl FIXME: Replace AC_CHECK_PROG with AC_CHECK_TOOL once cross compiling is fixed. +dnl AC_CHECK_TOOL(AR, ar, :) + AC_CHECK_PROG(AR, ar, ar) + STLIB_LD='${AR} cr' + case $system in + 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. + if test "$do64bit" = "yes" ; then + if test "x${MSSDK}x" = "xx" ; then + MSSDK="C:/Progra~1/Microsoft SDK" + fi + # In order to work in the tortured autoconf environment, + # we need to ensure that this path has no spaces + MSSDK=`cygpath -w -s "$MSSDK" | sed -e 's!\\\!/!g'` + if test ! -d "${MSSDK}/bin/win64" ; then + AC_MSG_WARN("could not find 64-bit SDK to enable 64bit mode") + do64bit="no" + else + do64bit_ok="yes" + fi + fi + + if test "${SHARED_BUILD}" = "0" ; then + runtime=-MT + else + runtime=-MD + fi + + if test "$do64bit" = "yes" ; then + # All this magic is necessary for the Win64 SDK RC1 - hobbs + export CC="${MSSDK}/Bin/Win64/cl.exe \ + -I${MSSDK}/Include/prerelease -I${MSSDK}/Include/Win64/crt \ + -I${MSSDK}/Include" + export RC="${MSSDK}/bin/rc.exe" + export lflags="-MACHINE:IA64 -LIBPATH:${MSSDK}/Lib/IA64 \ + -LIBPATH:${MSSDK}/Lib/Prerelease/IA64" + export STLIB_LD="${MSSDK}/bin/win64/lib.exe -nologo ${lflags}" + export LINKBIN="${MSSDK}/bin/win64/link.exe ${lflags}" + CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d" + CFLAGS_OPTIMIZE="-nologo -O2 -Gs -W2 ${runtime}" + else + RC="rc" + STLIB_LD="lib -nologo" + LINKBIN="link -link50compat" + CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d" + CFLAGS_OPTIMIZE="-nologo -O2 -Gs -GD -W2 ${runtime}" + fi + + if test "$MINGW32" = "yes"; then + # mingw gcc mode + CFLAGS_DEBUG="-g" + CFLAGS_OPTIMIZE="-O2" + SHLIB_LD="gcc -shared" + STLIB_LD='${AR} cr' + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}" + LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}" + else + SHLIB_LD="${LINKBIN} -dll -nologo" + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.lib' + EXTRA_CFLAGS="-YX" + # For information on what debugtype is most useful, see: + # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp + # This essentially turns it all on. + LDFLAGS_DEBUG="-debug:full -debugtype:both -warn:2" + LDFLAGS_OPTIMIZE="-release" + LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" + LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" + PATHTYPE=-w + fi + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".dll" + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.dll' + + TCL_LIB_VERSIONS_OK=nodots + # Bogus to avoid getting this turned off + DL_OBJS="tclLoadNone.obj" + ;; + AIX-*) + if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes" ; then + # AIX requires the _r compiler when gcc isn't being used + if test "${CC}" != "cc_r" ; then + CC=${CC}_r + fi + AC_MSG_RESULT([Using $CC for compiling with threads]) + fi + LIBS="$LIBS -lc" + SHLIB_CFLAGS="" + SHLIB_SUFFIX=".so" + SHLIB_LD_LIBS='${LIBS}' + 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" + # AIX-5 has dl* in libc.so + DL_LIBS="" + if test "$GCC" = "yes" ; then + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + LD_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}' + fi + else + SHLIB_LD="${TCL_SRC_DIR}/unix/ldAix /bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry" + DL_LIBS="-ldl" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + TCL_NEEDS_EXP_FILE=1 + TCL_EXPORT_FILE_SUFFIX='${VERSION}\$\{DBGX\}.exp' + fi + DL_OBJS="tclLoadDl.o" + LDFLAGS="" + + # AIX v<=4.1 has some different flags than 4.2+ + if test "$system" = "AIX-4.1" -o "`uname -v`" -lt "4" ; then + LIBOBJS="$LIBOBJS tclLoadAix.o" + DL_LIBS="-lld" + fi + + # On AIX <=v4 systems, libbsd.a has to be linked in to support + # non-blocking file IO. This library has to be linked in after + # the MATH_LIBS or it breaks the pow() function. The way to + # insure proper sequencing, is to add it to the tail of MATH_LIBS. + # This library also supplies gettimeofday. + # + # AIX does not have a timezone field in struct tm. When the AIX + # bsd library is used, the timezone global and the gettimeofday + # methods are to be avoided for timezone deduction instead, we + # deduce the timezone by comparing the localtime result on a + # known GMT value. + + AC_CHECK_LIB(bsd, gettimeofday, libbsd=yes, libbsd=no) + if test $libbsd = yes; then + MATH_LIBS="$MATH_LIBS -lbsd" + AC_DEFINE(USE_DELTA_FOR_TZ) + fi + + # Check to enable 64-bit flags for compiler/linker on AIX 4+ + if test "$do64bit" = "yes" -a "`uname -v`" -gt "3" ; then + if test "$GCC" = "yes" ; then + AC_MSG_WARN("64bit mode not supported with GCC on $system") + else + do64bit_ok=yes + EXTRA_CFLAGS="-q64" + LDFLAGS="-q64" + RANLIB="${RANLIB} -X64" + AR="${AR} -X64" + SHLIB_LDFLAGS="-b64" + fi + fi + ;; + BSD/OS-2.1*|BSD/OS-3*) + SHLIB_CFLAGS="" + SHLIB_LD="shlicc -r" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + BSD/OS-4.*) + SHLIB_CFLAGS="-export-dynamic -fPIC" + SHLIB_LD="cc -shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-export-dynamic" + LD_SEARCH_FLAGS="" + ;; + dgux*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + HP-UX-*.11.*) + # Use updated header definitions where possible + AC_DEFINE(_XOPEN_SOURCE_EXTENDED) + + SHLIB_SUFFIX=".sl" + AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no) + if test "$tcl_ok" = yes; then + SHLIB_CFLAGS="+z" + SHLIB_LD="ld -b" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadShl.o" + DL_LIBS="-ldld" + LDFLAGS="-Wl,-E" + LD_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' + fi + + # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc + #EXTRA_CFLAGS="+DAportable" + + # Check to enable 64-bit flags for compiler/linker + if test "$do64bit" = "yes" ; then + if test "$GCC" = "yes" ; then + hpux_arch=`gcc -dumpmachine` + case $hpux_arch in + hppa64*) + # 64-bit gcc in use. Fix flags for GNU ld. + do64bit_ok=yes + SHLIB_LD="gcc -shared" + SHLIB_LD_LIBS="" + LD_SEARCH_FLAGS='' + ;; + *) + AC_MSG_WARN("64bit mode not supported with GCC on $system") + ;; + esac + else + do64bit_ok=yes + EXTRA_CFLAGS="+DA2.0W" + LDFLAGS="+DA2.0W $LDFLAGS" + fi + fi + ;; + HP-UX-*.08.*|HP-UX-*.09.*|HP-UX-*.10.*) + SHLIB_SUFFIX=".sl" + AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no) + if test "$tcl_ok" = yes; then + SHLIB_CFLAGS="+z" + SHLIB_LD="ld -b" + SHLIB_LD_LIBS="" + DL_OBJS="tclLoadShl.o" + DL_LIBS="-ldld" + LDFLAGS="-Wl,-E" + LD_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.' + fi + ;; + IRIX-4.*) + SHLIB_CFLAGS="-G 0" + SHLIB_SUFFIX=".a" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + SHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}.a' + ;; + IRIX-5.*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + EXTRA_CFLAGS="" + LDFLAGS="" + ;; + IRIX-6.*|IRIX64-6.5*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -n32 -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + if test "$GCC" = "yes" ; then + EXTRA_CFLAGS="-mabi=n32" + LDFLAGS="-mabi=n32" + else + case $system in + IRIX-6.3) + # Use to build 6.2 compatible binaries on 6.3. + EXTRA_CFLAGS="-n32 -D_OLD_TERMIOS" + ;; + *) + EXTRA_CFLAGS="-n32" + ;; + esac + LDFLAGS="-n32" + fi + ;; + IRIX64-6.*) + SHLIB_CFLAGS="" + SHLIB_LD="ld -n32 -shared -rdata_shared" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + + # Check to enable 64-bit flags for compiler/linker + + if test "$do64bit" = "yes" ; then + if test "$GCC" = "yes" ; then + AC_MSG_WARN([64bit mode not supported by gcc]) + else + do64bit_ok=yes + SHLIB_LD="ld -64 -shared -rdata_shared" + EXTRA_CFLAGS="-64" + LDFLAGS="-64" + fi + fi + ;; + Linux*) + SHLIB_CFLAGS="-fPIC" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + + # egcs-2.91.66 on Redhat Linux 6.0 generates lots of warnings + # when you inline the string and math operations. Turn this off to + # get rid of the warnings. + + CFLAGS_OPTIMIZE="${CFLAGS_OPTIMIZE} -D__NO_STRING_INLINES -D__NO_MATH_INLINES" + + if test "$have_dl" = yes; then + SHLIB_LD="${CC} -shared" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-rdynamic" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + else + AC_CHECK_HEADER(dld.h, [ + SHLIB_LD="ld -shared" + DL_OBJS="tclLoadDld.o" + DL_LIBS="-ldld" + LDFLAGS="" + LD_SEARCH_FLAGS=""]) + fi + if test "`uname -m`" = "alpha" ; then + EXTRA_CFLAGS="-mieee" + 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"${LIBOBJS}" != x ; then + EXTRA_CFLAGS="${EXTRA_CFLAGS} -fno-inline" + fi + + ;; + GNU*) + SHLIB_CFLAGS="-fPIC" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + + if test "$have_dl" = yes; then + SHLIB_LD="${CC} -shared" + DL_OBJS="" + DL_LIBS="-ldl" + LDFLAGS="-rdynamic" + LD_SEARCH_FLAGS="" + else + AC_CHECK_HEADER(dld.h, [ + SHLIB_LD="ld -shared" + DL_OBJS="" + DL_LIBS="-ldld" + LDFLAGS="" + LD_SEARCH_FLAGS=""]) + fi + if test "`uname -m`" = "alpha" ; then + EXTRA_CFLAGS="-mieee" + fi + ;; + MP-RAS-02*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + MP-RAS-*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="-Wl,-Bexport" + LD_SEARCH_FLAGS="" + ;; + NetBSD-*|FreeBSD-[[1-2]].*|OpenBSD-*) + # Not available on all versions: check for include file. + AC_CHECK_HEADER(dlfcn.h, [ + # NetBSD/SPARC needs -fPIC, -fpic will not do. + SHLIB_CFLAGS="-fPIC" + SHLIB_LD="ld -Bshareable -x" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + AC_MSG_CHECKING([for ELF]) + AC_EGREP_CPP(yes, [ +#ifdef __ELF__ + yes +#endif + ], + AC_MSG_RESULT([yes]) + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so', + AC_MSG_RESULT([no]) + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1.0' + ) + ], [ + SHLIB_CFLAGS="" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".a" + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + ]) + + # FreeBSD doesn't handle version numbers with dots. + + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + TCL_LIB_VERSIONS_OK=nodots + ;; + FreeBSD-*) + # FreeBSD 3.* and greater have ELF. + SHLIB_CFLAGS="-fPIC" + SHLIB_LD="ld -Bshareable -x" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="-export-dynamic" + LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}' + if test "${TCL_THREADS}" = "1" ; then + # The -pthread needs to go in the CFLAGS, not LIBS + LIBS=`echo $LIBS | sed s/-pthread//` + EXTRA_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}\$\{DBGX\}.a' + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so' + TCL_LIB_VERSIONS_OK=nodots + ;; + esac + ;; + Rhapsody-*|Darwin-*) + SHLIB_CFLAGS="-fno-common" + SHLIB_LD="cc -dynamiclib \${LDFLAGS}" + TCL_SHLIB_LD_EXTRAS="-compatibility_version ${TCL_MAJOR_VERSION} -current_version \${VERSION} -install_name \${LIB_RUNTIME_DIR}/\${TCL_LIB_FILE} -prebind -seg1addr 0xa000000" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".dylib" + DL_OBJS="tclLoadDyld.o" + DL_LIBS="" + LDFLAGS="-prebind" + LD_SEARCH_FLAGS="" + CFLAGS_OPTIMIZE="-O3" + EXTRA_CFLAGS="-arch ppc -pipe" + ;; + NEXTSTEP-*) + SHLIB_CFLAGS="" + SHLIB_LD="cc -nostdlib -r" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadNext.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + OS/390-*) + CFLAGS_OPTIMIZE="" # Optimizer is buggy + AC_DEFINE(_OE_SOCKETS) # needed in sys/socket.h + ;; + OSF1-1.0|OSF1-1.1|OSF1-1.2) + # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1 + SHLIB_CFLAGS="" + # Hack: make package name same as library name + SHLIB_LD='ld -R -export $@:' + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadOSF.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + OSF1-1.*) + # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2 + SHLIB_CFLAGS="-fPIC" + if test "$SHARED_BUILD" = "1" ; then + SHLIB_LD="ld -shared" + else + SHLIB_LD="ld -non_shared" + fi + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + 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_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}' + if test "$GCC" != "yes" ; then + EXTRA_CFLAGS="-DHAVE_TZSET -std1" + fi + # see pthread_intro(3) for pthread support on osf1, k.furukawa + if test "${TCL_THREADS}" = "1" ; then + EXTRA_CFLAGS="${EXTRA_CFLAGS} -DHAVE_PTHREAD_ATTR_SETSTACKSIZE" + EXTRA_CFLAGS="${EXTRA_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 + EXTRA_CFLAGS="${EXTRA_CFLAGS} -pthread" + 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" + DL_OBJS="tclLoadDl.o" + # dlopen is in -lc on QNX + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + RISCos-*) + SHLIB_CFLAGS="-G 0" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".a" + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + ;; + SCO_SV-3.2*) + # Note, dlopen is available only on SCO 3.2.5 and greater. However, + # this test works, since "uname -s" was non-standard in 3.2.4 and + # below. + if test "$GCC" = "yes" ; then + SHLIB_CFLAGS="-fPIC -melf" + LDFLAGS="-melf -Wl,-Bexport" + else + SHLIB_CFLAGS="-Kpic -belf" + LDFLAGS="-belf -Wl,-Bexport" + fi + SHLIB_LD="ld -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="" + LD_SEARCH_FLAGS="" + ;; + SINIX*5.4*) + SHLIB_CFLAGS="-K PIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS="" + ;; + SunOS-4*) + SHLIB_CFLAGS="-PIC" + SHLIB_LD="ld" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + + # SunOS can't handle version numbers with dots in them in library + # specs, like -ltcl7.5, so use -ltcl75 instead. Also, it + # requires an extra version number at the end of .so file names. + # So, the library has to have a name like libtcl75.so.1.0 + + SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1.0' + UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.a' + TCL_LIB_VERSIONS_OK=nodots + ;; + SunOS-5.[[0-6]]*) + + # Note: If _REENTRANT isn't defined, then Solaris + # won't define thread-safe library routines. + + AC_DEFINE(_REENTRANT) + AC_DEFINE(_POSIX_PTHREAD_SEMANTICS) + + SHLIB_CFLAGS="-KPIC" + + # Note: need the LIBS below, otherwise Tk won't find Tcl's + # symbols when dynamically loaded into tclsh. + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + LDFLAGS="" + if test "$GCC" = "yes" ; then + SHLIB_LD="$CC -shared" + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + SHLIB_LD="/usr/ccs/bin/ld -G -z text" + LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' + fi + ;; + SunOS-5*) + + # Note: If _REENTRANT isn't defined, then Solaris + # won't define thread-safe library routines. + + AC_DEFINE(_REENTRANT) + AC_DEFINE(_POSIX_PTHREAD_SEMANTICS) + + SHLIB_CFLAGS="-KPIC" + LDFLAGS="" + + # 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 + AC_MSG_WARN("64bit mode not supported with GCC on $system") + else + do64bit_ok=yes + if test "$do64bitVIS" = "yes" ; then + EXTRA_CFLAGS="-xarch=v9a" + LDFLAGS="-xarch=v9a" + else + EXTRA_CFLAGS="-xarch=v9" + LDFLAGS="-xarch=v9" + fi + fi + else + AC_MSG_WARN("64bit mode only supported sparcv9 system") + fi + fi + + # Note: need the LIBS below, otherwise Tk won't find Tcl's + # symbols when dynamically loaded into tclsh. + + SHLIB_LD_LIBS='${LIBS}' + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + if test "$GCC" = "yes" ; then + SHLIB_LD="$CC -shared" + LD_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}' + else + SHLIB_LD="/usr/ccs/bin/ld -G -z text" + LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}' + fi + ;; + ULTRIX-4.*) + SHLIB_CFLAGS="-G 0" + SHLIB_SUFFIX=".a" + SHLIB_LD="echo tclLdAout $CC \{$SHLIB_CFLAGS\} | `pwd`/tclsh -r -G 0" + SHLIB_LD_LIBS='${LIBS}' + DL_OBJS="tclLoadAout.o" + DL_LIBS="" + LDFLAGS="-Wl,-D,08000000" + LD_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}' + if test "$GCC" != "yes" ; then + EXTRA_CFLAGS="-DHAVE_TZSET -std1" + fi + ;; + UNIX_SV* | UnixWare-5*) + SHLIB_CFLAGS="-KPIC" + SHLIB_LD="cc -G" + SHLIB_LD_LIBS="" + SHLIB_SUFFIX=".so" + DL_OBJS="tclLoadDl.o" + DL_LIBS="-ldl" + # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers + # that don't grok the -Bexport option. Test that it does. + hold_ldflags=$LDFLAGS + AC_MSG_CHECKING([for ld accepts -Bexport flag]) + LDFLAGS="${LDFLAGS} -Wl,-Bexport" + AC_TRY_LINK(, [int i;], found=yes, found=no) + LDFLAGS=$hold_ldflags + AC_MSG_RESULT([$found]) + if test $found = yes; then + LDFLAGS="-Wl,-Bexport" + else + LDFLAGS="" + fi + LD_SEARCH_FLAGS="" + ;; + esac + + if test "$do64bit" = "yes" -a "$do64bit_ok" = "no" ; then + AC_MSG_WARN("64bit support being disabled -- don\'t know magic for this platform") + fi + + # Step 4: If pseudo-static linking is in use (see K. B. Kenny, "Dynamic + # Loading for Tcl -- What Became of It?". Proc. 2nd Tcl/Tk Workshop, + # New Orleans, LA, Computerized Processes Unlimited, 1994), then we need + # to determine which of several header files defines the a.out file + # format (a.out.h, sys/exec.h, or sys/exec_aout.h). At present, we + # support only a file format that is more or less version-7-compatible. + # In particular, + # - a.out files must begin with `struct exec'. + # - the N_TXTOFF on the `struct exec' must compute the seek address + # of the text segment + # - The `struct exec' must contain a_magic, a_text, a_data, a_bss + # and a_entry fields. + # The following compilation should succeed if and only if either sys/exec.h + # or a.out.h is usable for the purpose. + # + # Note that the modified COFF format used on MIPS Ultrix 4.x is usable; the + # `struct exec' includes a second header that contains information that + # duplicates the v7 fields that are needed. + + if test "x$DL_OBJS" = "xtclLoadAout.o" ; then + AC_MSG_CHECKING([sys/exec.h]) + AC_TRY_COMPILE([#include ],[ + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_magic == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + ], tcl_ok=usable, tcl_ok=unusable) + AC_MSG_RESULT([$tcl_ok]) + if test $tcl_ok = usable; then + AC_DEFINE(USE_SYS_EXEC_H) + else + AC_MSG_CHECKING([a.out.h]) + AC_TRY_COMPILE([#include ],[ + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_magic == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + ], tcl_ok=usable, tcl_ok=unusable) + AC_MSG_RESULT([$tcl_ok]) + if test $tcl_ok = usable; then + AC_DEFINE(USE_A_OUT_H) + else + AC_MSG_CHECKING([sys/exec_aout.h]) + AC_TRY_COMPILE([#include ],[ + struct exec foo; + unsigned long seek; + int flag; +#if defined(__mips) || defined(mips) + seek = N_TXTOFF (foo.ex_f, foo.ex_o); +#else + seek = N_TXTOFF (foo); +#endif + flag = (foo.a_midmag == OMAGIC); + return foo.a_text + foo.a_data + foo.a_bss + foo.a_entry; + ], tcl_ok=usable, tcl_ok=unusable) + AC_MSG_RESULT([$tcl_ok]) + if test $tcl_ok = usable; then + AC_DEFINE(USE_SYS_EXEC_AOUT_H) + else + DL_OBJS="" + fi + fi + fi + fi + + # Step 5: disable dynamic loading if requested via a command-line switch. + + AC_ARG_ENABLE(load, [ --disable-load disallow dynamic loading and "load" command], + [tcl_ok=$enableval], [tcl_ok=yes]) + if test "$tcl_ok" = "no"; then + DL_OBJS="" + fi + + if test "x$DL_OBJS" != "x" ; then + BUILD_DLTEST="\$(DLTEST_TARGETS)" + else + echo "Can't figure out how to do dynamic loading or shared libraries" + echo "on this system." + SHLIB_CFLAGS="" + SHLIB_LD="" + SHLIB_SUFFIX="" + DL_OBJS="tclLoadNone.o" + DL_LIBS="" + LDFLAGS="" + LD_SEARCH_FLAGS="" + BUILD_DLTEST="" + fi + + # 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 "$DL_OBJS" != "tclLoadNone.o" ; then + if test "$GCC" = "yes" ; then + case $system in + AIX-*) + ;; + BSD/OS*) + ;; + IRIX*) + ;; + NetBSD-*|FreeBSD-*|OpenBSD-*) + ;; + Rhapsody-*|Darwin-*) + ;; + RISCos-*) + ;; + SCO_SV-3.2*) + ;; + ULTRIX-4.*) + ;; + windows) + if test "$MINGW32" != "yes"; then + SHLIB_CFLAGS="-fPIC" + fi + ;; + *) + SHLIB_CFLAGS="-fPIC" + ;; + esac + fi + fi + + if test "$SHARED_LIB_SUFFIX" = "" ; then + SHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}${SHLIB_SUFFIX}' + fi + if test "$UNSHARED_LIB_SUFFIX" = "" ; then + UNSHARED_LIB_SUFFIX='${VERSION}\$\{DBGX\}.a' + fi + + AC_SUBST(DL_LIBS) + AC_SUBST(CFLAGS_DEBUG) + AC_SUBST(CFLAGS_OPTIMIZE) + AC_SUBST(CFLAGS_WARNING) + AC_SUBST(EXTRA_CFLAGS) + + SHLIB_LDFLAGS='$(LDFLAGS_DEFAULT)' + AC_SUBST(STLIB_LD) + AC_SUBST(SHLIB_LD) + AC_SUBST(SHLIB_CFLAGS) + AC_SUBST(SHLIB_LDFLAGS) + AC_SUBST(SHLIB_LD_LIBS) + AC_SUBST(LDFLAGS_DEBUG) + AC_SUBST(LDFLAGS_OPTIMIZE) +]) + +#-------------------------------------------------------------------- +# 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_MSG_CHECKING([termios vs. termio vs. sgtty]) + AC_CACHE_VAL(tcl_cv_api_serial, [ + AC_TRY_RUN([ +#include + +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 + +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 + +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=none, tcl_cv_api_serial=none) + fi + if test $tcl_cv_api_serial = no ; then + AC_TRY_RUN([ +#include +#include + +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 +#include + +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 +#include + +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);; + termio) AC_DEFINE(USE_TERMIO);; + sgtty) AC_DEFINE(USE_SGTTY);; + esac + AC_MSG_RESULT([$tcl_cv_api_serial]) +]) + +#-------------------------------------------------------------------- +# TEA_MISSING_POSIX_HEADERS +# +# Supply substitutes for missing POSIX header files. Special +# notes: +# - stdlib.h doesn't define strtol, strtoul, or +# strtod insome 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 +# NO_LIMITS_H +# NO_STDLIB_H +# NO_STRING_H +# NO_SYS_WAIT_H +# NO_DLFCN_H +# HAVE_UNISTD_H +# HAVE_SYS_PARAM_H +# +# HAVE_STRING_H ? +# +#-------------------------------------------------------------------- + +AC_DEFUN(TEA_MISSING_POSIX_HEADERS, [ + AC_MSG_CHECKING([dirent.h]) + AC_TRY_LINK([#include +#include ], [ +#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_ok=yes, tcl_ok=no) + + if test $tcl_ok = no; then + AC_DEFINE(NO_DIRENT_H) + fi + + AC_MSG_RESULT([$tcl_ok]) + AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H)]) + AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H)]) + AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H)]) + AC_CHECK_HEADER(limits.h, , [AC_DEFINE(NO_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) + 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) + fi + + AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H)]) + AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H)]) + + # OS/390 lacks sys/param.h (and doesn't need it, by chance). + + AC_HAVE_HEADERS(unistd.h 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 +# LIBS (appends to) +# +#-------------------------------------------------------------------- + +AC_DEFUN(TEA_PATH_X, [ + if test "${TEA_PLATFORM}" = "unix" ; 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 ], , 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]) + XINCLUDES="# no special path needed" + AC_TRY_CPP([#include ], , XINCLUDES="nope") + if test "$XINCLUDES" = nope; 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" + break + fi + done + fi + else + if test "$x_includes" != ""; then + XINCLUDES=-I$x_includes + else + XINCLUDES="# no special path needed" + fi + fi + if test "$XINCLUDES" = nope; then + AC_MSG_RESULT([could not find any!]) + XINCLUDES="# no include files found" + 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; 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 + if test x"${XLIBSW}" != x ; then + LIBS="${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) + AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O]) + if test -f /usr/lib/NextStep/software_version; then + system=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version` + else + system=`uname -s`-`uname -r` + if test "$?" -ne 0 ; then + system=unknown + else + # Special check for weird MP-RAS system (uname returns weird + # results, and the version is kept in special file). + + if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then + system=MP-RAS-`awk '{print $3}' /etc/.relid'` + fi + if test "`uname -s`" = "AIX" ; then + system=AIX-`uname -v`.`uname -r` + fi + fi + fi + case $system in + # There used to be code here to use FIONBIO under AIX. However, it + # was reported that FIONBIO doesn't work under AIX 3.2.5. Since + # using O_NONBLOCK seems fine under AIX 4.*, I removed the FIONBIO + # code (JO, 5/31/97). + + OSF*) + AC_DEFINE(USE_FIONBIO) + AC_MSG_RESULT([FIONBIO]) + ;; + SunOS-4*) + AC_DEFINE(USE_FIONBIO) + AC_MSG_RESULT([FIONBIO]) + ;; + ULTRIX-4.*) + AC_DEFINE(USE_FIONBIO) + AC_MSG_RESULT([FIONBIO]) + ;; + *) + AC_MSG_RESULT([O_NONBLOCK]) + ;; + esac +]) + +#-------------------------------------------------------------------- +# TEA_TIME_HANLDER +# +# 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_MSG_CHECKING([tm_tzadj in struct tm]) + AC_CACHE_VAL(tcl_cv_member_tm_tzadj, + AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_tzadj;], + tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)) + AC_MSG_RESULT([$tcl_cv_member_tm_tzadj]) + if test $tcl_cv_member_tm_tzadj = yes ; then + AC_DEFINE(HAVE_TM_TZADJ) + fi + + AC_MSG_CHECKING([tm_gmtoff in struct tm]) + AC_CACHE_VAL(tcl_cv_member_tm_gmtoff, + AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_gmtoff;], + tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)) + AC_MSG_RESULT([$tcl_cv_member_tm_gmtoff]) + if test $tcl_cv_member_tm_gmtoff = yes ; then + AC_DEFINE(HAVE_TM_GMTOFF) + fi + + # + # Its important to include time.h in this check, as some systems + # (like convex) have timezone functions, etc. + # + AC_MSG_CHECKING([long timezone variable]) + AC_CACHE_VAL(tcl_cv_var_timezone, + AC_TRY_COMPILE([#include ], + [extern long timezone; + timezone += 1; + exit (0);], + tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)) + AC_MSG_RESULT([$tcl_cv_timezone_long]) + if test $tcl_cv_timezone_long = yes ; then + AC_DEFINE(HAVE_TIMEZONE_VAR) + else + # + # On some systems (eg IRIX 6.2), timezone is a time_t and not a long. + # + AC_MSG_CHECKING([time_t timezone variable]) + AC_CACHE_VAL(tcl_cv_timezone_time, + AC_TRY_COMPILE([#include ], + [extern time_t timezone; + timezone += 1; + exit (0);], + tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)) + AC_MSG_RESULT([$tcl_cv_timezone_time]) + if test $tcl_cv_timezone_time = yes ; then + AC_DEFINE(HAVE_TIMEZONE_VAR) + 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_MSG_CHECKING([for Solaris2.4/Tru64 strtod bugs]) + AC_TRY_RUN([ + extern double strtod(); + int main() + { + char *string = "NaN", *spaceString = " "; + char *term; + double value; + value = strtod(string, &term); + if ((term != string) && (term[-1] == 0)) { + exit(1); + } + value = strtod(spaceString, &term); + if (term == (spaceString+1)) { + exit(1); + } + exit(0); + }], tcl_ok=1, tcl_ok=0, tcl_ok=0) + if test "$tcl_ok" = 1; then + AC_MSG_RESULT([ok]) + else + AC_MSG_RESULT([buggy]) + LIBOBJS="$LIBOBJS fixstrtod.o" + AC_DEFINE(strtod, fixstrtod) + 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 +# 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)) + + #-------------------------------------------------------------------- + # 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"])]) + + # 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 +# +#-------------------------------------------------------------------- + +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) + 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 ], + [char *p = (char *)strtoll; char *q = (char *)strtoull;]) + TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include ], + [struct stat64 buf; int i = stat64("/", &buf);]) + 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,[ + AC_TRY_COMPILE(,[__int64 value = (__int64) 0;], + tcl_cv_type_64bit=__int64,tcl_cv_type_64bit=none + AC_TRY_RUN([#include + int main() {exit(!(sizeof(long long) > sizeof(long)));} + ], tcl_cv_type_64bit="long long"))]) + if test "${tcl_cv_type_64bit}" = none ; then + AC_MSG_RESULT([using long]) + else + AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit}) + AC_MSG_RESULT([${tcl_cv_type_64bit}]) + + # Now check for auxiliary declarations + AC_MSG_CHECKING([for struct dirent64]) + AC_CACHE_VAL(tcl_cv_struct_dirent64,[ + AC_TRY_COMPILE([#include +#include ],[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) + fi + AC_MSG_RESULT([${tcl_cv_struct_dirent64}]) + + AC_MSG_CHECKING([for struct stat64]) + AC_CACHE_VAL(tcl_cv_struct_stat64,[ + AC_TRY_COMPILE([#include ],[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) + fi + AC_MSG_RESULT([${tcl_cv_struct_stat64}]) + + AC_MSG_CHECKING([for off64_t]) + AC_CACHE_VAL(tcl_cv_type_off64_t,[ + AC_TRY_COMPILE([#include ],[off64_t offset; +], + tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)]) + if test "x${tcl_cv_type_off64_t}" = "xyes" ; then + AC_DEFINE(HAVE_TYPE_OFF64_T) + fi + AC_MSG_RESULT([${tcl_cv_type_off64_t}]) + 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_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, [ + AC_MSG_CHECKING([for correct TEA configuration]) + if test x"${PACKAGE}" = x ; then + AC_MSG_ERROR([ +The PACKAGE variable must be defined by your TEA configure.in]) + fi + AC_MSG_RESULT([ok]) + TEA_INITED=ok + case "`uname -s`" in + *win32*|*WIN32*|*CYGWIN_NT*|*CYGWIN_9*|*CYGWIN_ME*|*MINGW32_*) + AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo) + EXEEXT=".exe" + TEA_PLATFORM="windows" + ;; + *) + CYGPATH=echo + EXEEXT="" + TEA_PLATFORM="unix" + ;; + esac + + AC_SUBST(EXEEXT) + AC_SUBST(CYGPATH) +]) + +#------------------------------------------------------------------------ +# 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, [ + # Should be AC_MSG_NOTICE, but that requires autoconf 2.50 + if test "${prefix}" = "NONE"; then + prefix_default=yes + if test x"${TCL_PREFIX}" != x; then + AC_MSG_WARN([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}]) + prefix=${TCL_PREFIX} + else + prefix=/usr/local + fi + fi + if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" ; then + if test x"${TCL_EXEC_PREFIX}" != x; then + AC_MSG_WARN([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}]) + exec_prefix=${TCL_EXEC_PREFIX} + else + exec_prefix=$prefix + fi + fi +]) + +#------------------------------------------------------------------------ +# TEA_PROG_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, [ + # 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 + + #------------------------------------------------------------------------ + # 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_MSG_CHECKING([if the compiler understands -pipe]) + OLDCC="$CC" + CC="$CC -pipe" + AC_TRY_COMPILE(,, AC_MSG_RESULT([yes]), CC="$OLDCC" + AC_MSG_RESULT([no])) + fi + + AC_PROG_INSTALL + + #-------------------------------------------------------------------- + # Checks to see if the make program sets the $MAKE variable. + #-------------------------------------------------------------------- + + AC_PROG_MAKE_SET + + #-------------------------------------------------------------------- + # Find ranlib + #-------------------------------------------------------------------- + + AC_PROG_RANLIB + + #-------------------------------------------------------------------- + # Determines the correct binary file extension (.o, .obj, .exe etc.) + #-------------------------------------------------------------------- + + AC_OBJEXT + AC_EXEEXT +]) + +#------------------------------------------------------------------------ +# 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 +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_MAKE_LIB, [ + if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then + MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(\[$](PACKAGE)_OBJECTS)" + MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LDFLAGS} \${SHLIB_LD_LIBS} \$(LDFLAGS) -out:\[$]@ \$(\[$](PACKAGE)_OBJECTS)" + MAKE_STUB_LIB="\${STLIB_LD} -out:\[$]@ \$(\[$](PACKAGE)stub_OBJECTS)" + else + MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(\[$](PACKAGE)_OBJECTS)" + MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(\[$](PACKAGE)_OBJECTS) \${SHLIB_LDFLAGS} \${SHLIB_LD_LIBS}" + MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(\[$](PACKAGE)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 the ${DBGX} in the suffix is + # substituted. + #-------------------------------------------------------------------- + + 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 "${PACKAGE}_LIB_FILE=${PACKAGE}${SHARED_LIB_SUFFIX}" + RANLIB=: + else + eval eval "${PACKAGE}_LIB_FILE=${PACKAGE}${UNSHARED_LIB_SUFFIX}" + fi + # Some packages build there own stubs libraries + eval eval "${PACKAGE}stub_LIB_FILE=${PACKAGE}stub${UNSHARED_LIB_SUFFIX}" + else + 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 "${PACKAGE}_LIB_FILE=lib${PACKAGE}${SHARED_LIB_SUFFIX}" + RANLIB=: + else + eval eval "${PACKAGE}_LIB_FILE=lib${PACKAGE}${UNSHARED_LIB_SUFFIX}" + fi + # Some packages build there own stubs libraries + eval eval "${PACKAGE}stub_LIB_FILE=lib${PACKAGE}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) +]) + +#------------------------------------------------------------------------ +# 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/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_GENERIC_DIR_NATIVE +# TCL_UNIX_DIR_NATIVE +# TCL_WIN_DIR_NATIVE +# TCL_BMAP_DIR_NATIVE +# TCL_TOOL_DIR_NATIVE +# TCL_PLATFORM_DIR_NATIVE +# TCL_BIN_DIR_NATIVE +# TCL_INCLUDES +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_PRIVATE_TCL_HEADERS, [ + AC_MSG_CHECKING([for Tcl private include files]) + + if test "${TEA_PLATFORM}" = "windows"; then + TCL_TOP_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}`\" + TCL_GENERIC_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/generic`\" + TCL_UNIX_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/unix`\" + TCL_WIN_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/win`\" + TCL_BMAP_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/bitmaps`\" + TCL_TOOL_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/tools`\" + TCL_COMPAT_DIR_NATIVE=\"`${CYGPATH} ${TCL_SRC_DIR}/compat`\" + TCL_PLATFORM_DIR_NATIVE=${TCL_WIN_DIR_NATIVE} + else + TCL_TOP_DIR_NATIVE='$(TCL_SRC_DIR)' + TCL_GENERIC_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/generic' + TCL_UNIX_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/unix' + TCL_WIN_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/win' + TCL_BMAP_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/bitmaps' + TCL_TOOL_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/tools' + TCL_COMPAT_DIR_NATIVE='$(TCL_TOP_DIR_NATIVE)/compat' + TCL_PLATFORM_DIR_NATIVE=${TCL_UNIX_DIR_NATIVE} + fi + + AC_SUBST(TCL_TOP_DIR_NATIVE) + AC_SUBST(TCL_GENERIC_DIR_NATIVE) + AC_SUBST(TCL_UNIX_DIR_NATIVE) + AC_SUBST(TCL_WIN_DIR_NATIVE) + AC_SUBST(TCL_BMAP_DIR_NATIVE) + AC_SUBST(TCL_TOOL_DIR_NATIVE) + AC_SUBST(TCL_PLATFORM_DIR_NATIVE) + + TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}" + AC_SUBST(TCL_INCLUDES) + AC_MSG_RESULT([Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}]) +]) + +#------------------------------------------------------------------------ +# 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 + # Check order: pkg --prefix location, Tcl's --prefix location, + # directory of tclConfig.sh, and Tcl source directory. + # Looking in the source dir is not ideal, but OK. + + eval "temp_includedir=${includedir}" + 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` \ + `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`" + if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then + list="$list /usr/local/include /usr/include" + 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, [ + AC_MSG_CHECKING([for Tk private include files]) + + if test "${TEA_PLATFORM}" = "windows"; then + TK_TOP_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}`\" + TK_UNIX_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/unix`\" + TK_WIN_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/win`\" + TK_GENERIC_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/generic`\" + TK_XLIB_DIR_NATIVE=\"`${CYGPATH} ${TK_SRC_DIR}/xlib`\" + TK_PLATFORM_DIR_NATIVE=${TK_WIN_DIR_NATIVE} + + TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE} -I${TK_XLIB_DIR_NATIVE}" + else + TK_TOP_DIR_NATIVE='$(TK_SRC_DIR)' + TK_GENERIC_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/generic' + TK_UNIX_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/unix' + TK_WIN_DIR_NATIVE='$(TK_TOP_DIR_NATIVE)/win' + TK_PLATFORM_DIR_NATIVE=${TK_UNIX_DIR_NATIVE} + + TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}" + fi + + AC_SUBST(TK_TOP_DIR_NATIVE) + AC_SUBST(TK_UNIX_DIR_NATIVE) + AC_SUBST(TK_WIN_DIR_NATIVE) + AC_SUBST(TK_GENERIC_DIR_NATIVE) + AC_SUBST(TK_XLIB_DIR_NATIVE) + AC_SUBST(TK_PLATFORM_DIR_NATIVE) + + AC_SUBST(TK_INCLUDES) + AC_MSG_RESULT([Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}]) +]) + +#------------------------------------------------------------------------ +# 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 + # Check order: pkg --prefix location, Tcl's --prefix location, + # directory of tclConfig.sh, and Tcl source directory. + # Looking in the source dir is not ideal, but OK. + + eval "temp_includedir=${includedir}" + list="`ls -d ${temp_includedir} 2>/dev/null` \ + `ls -d ${TK_PREFIX}/include 2>/dev/null` \ + `ls -d ${TCL_PREFIX}/include 2>/dev/null` \ + `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null` \ + `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`" + if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then + list="$list /usr/local/include /usr/include" + 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) +]) + +#------------------------------------------------------------------------ +# TEA_PROG_TCLSH +# Locate a tclsh shell in the following directories: +# ${TCL_BIN_DIR} ${TCL_BIN_DIR}/../bin +# ${exec_prefix}/bin ${prefix}/bin +# ${PATH} +# +# Arguments +# none +# +# Results +# Subst's the following values: +# TCLSH_PROG +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_PROG_TCLSH, [ + AC_MSG_CHECKING([for tclsh]) + + AC_CACHE_VAL(ac_cv_path_tclsh, [ + search_path=`echo ${TCL_BIN_DIR}:${TCL_BIN_DIR}/../bin:${exec_prefix}/bin:${prefix}/bin:${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/tclsh[[8-9]]*${EXEEXT} 2> /dev/null` \ + `ls -r $dir/tclsh*${EXEEXT} 2> /dev/null` ; do + if test x"$ac_cv_path_tclsh" = x ; then + if test -f "$j" ; then + ac_cv_path_tclsh=$j + break + fi + fi + done + done + ]) + + if test -f "$ac_cv_path_tclsh" ; then + TCLSH_PROG=$ac_cv_path_tclsh + AC_MSG_RESULT([$TCLSH_PROG]) + else + AC_MSG_ERROR([No tclsh found in PATH: $search_path]) + fi + AC_SUBST(TCLSH_PROG) +]) + +#------------------------------------------------------------------------ +# TEA_PROG_WISH +# Locate a wish shell in the following directories: +# ${TK_BIN_DIR} ${TK_BIN_DIR}/../bin +# ${TCL_BIN_DIR} ${TCL_BIN_DIR}/../bin +# ${exec_prefix}/bin ${prefix}/bin +# ${PATH} +# +# Arguments +# none +# +# Results +# Subst's the following values: +# WISH_PROG +#------------------------------------------------------------------------ + +AC_DEFUN(TEA_PROG_WISH, [ + AC_MSG_CHECKING([for wish]) + + AC_CACHE_VAL(ac_cv_path_wish, [ + search_path=`echo ${TK_BIN_DIR}:${TK_BIN_DIR}/../bin:${TCL_BIN_DIR}:${TCL_BIN_DIR}/../bin:${exec_prefix}/bin:${prefix}/bin:${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/wish[[8-9]]*${EXEEXT} 2> /dev/null` \ + `ls -r $dir/wish*${EXEEXT} 2> /dev/null` ; do + if test x"$ac_cv_path_wish" = x ; then + if test -f "$j" ; then + ac_cv_path_wish=$j + break + fi + fi + done + done + ]) + + if test -f "$ac_cv_path_wish" ; then + WISH_PROG=$ac_cv_path_wish + AC_MSG_RESULT([$WISH_PROG]) + else + AC_MSG_ERROR([No wish found in PATH: $search_path]) + fi + AC_SUBST(WISH_PROG) +]) + +#------------------------------------------------------------------------ +# 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 + 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[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../$1 \ + `ls -dr ../../$1[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../../$1 \ + `ls -dr ../../../$1[[8-9]].[[0-9]]* 2>/dev/null` \ + ${srcdir}/../$1 \ + `ls -dr ${srcdir}/../$1[[8-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 ${exec_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` \ + ; 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} + fi + + AC_SUBST($1_VERSION) + 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) +]) -- cgit v0.12