--- /dev/null
+#
+# This file is a Makefile for curses wish. 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.
+#
+
+# Current Ck version; used in various names.
+
+VERSION = @CK_VERSION@
+
+# What I suppose version of Ck itself, regardless of used Tcl
+
+SRCVERSION=8.1.2
+
+
+#----------------------------------------------------------------
+# Things you can change to personalize the Makefile for your own
+# site (you can make these changes in either Makefile.in or
+# Makefile, but changes to Makefile will get lost if you re-run
+# the configuration script).
+#----------------------------------------------------------------
+
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix). The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# The following definition can be set to non-null for special systems
+# like AFS with replication. It allows the pathnames used for installation
+# to be different than those used for actually reference files at
+# run-time. INSTALL_ROOT is prepended to $prefix and $exec_prefix
+# when installing files.
+INSTALL_ROOT =
+
+# Directory from which applications will reference the library of Tcl
+# scripts (note: you can set the CK_LIBRARY environment variable at
+# run-time to override the compiled-in location):
+CK_LIBRARY = $(prefix)/lib/ck$(VERSION)
+
+# Path name to use when installing library scripts:
+SCRIPT_INSTALL_DIR = $(INSTALL_ROOT)$(CK_LIBRARY)
+
+# Directory in which to install the archive libck*:
+LIB_INSTALL_DIR = $(INSTALL_ROOT)$(exec_prefix)/lib
+
+# Directory in which to install the program cwsh:
+BIN_INSTALL_DIR = $(INSTALL_ROOT)$(exec_prefix)/bin
+
+# Directory from which the program cwsh should be referenced by scripts:
+BIN_DIR = $(exec_prefix)/bin
+
+# Directory in which to install the include file ck.h:
+INCLUDE_INSTALL_DIR = $(INSTALL_ROOT)$(prefix)/include
+
+# Top-level directory for manual entries:
+MAN_INSTALL_DIR = $(INSTALL_ROOT)$(prefix)/man
+
+# Directory in which to install manual entry for cwsh:
+MAN1_INSTALL_DIR = $(MAN_INSTALL_DIR)/man1
+
+# Directory in which to install manual entries for C library
+# procedures:
+MAN3_INSTALL_DIR = $(MAN_INSTALL_DIR)/man3
+
+# Directory in which to install manual entries for the built-in
+# Tcl commands implemented by Ck:
+MANN_INSTALL_DIR = $(MAN_INSTALL_DIR)/mann
+
+# The directory containing the Tcl headers appropriate
+# for this version ("srcdir" will be replaced or has already
+# been replaced by the configure script):
+TCL_VERSION = @TCL_VERSION@
+TCL_DIR = @TCL_DIR@
+
+# The directory containing the Tcl library archive file appropriate
+# for this version:
+TCL_BIN_DIR = @TCL_BIN_DIR@
+
+# A "-I" switch that can be used when compiling to make curses.h
+# accessible (the configure script will try to set this value, and
+# will cause it to be an empty string if the include file is accessible
+# via /usr/include).
+CURSES_INCLUDES = @CURSESINCLUDES@ @USE_NCURSES@
+
+# Linker switch(es) to use to link with the curses library archive (the
+# configure script will try to set this value automatically, but you
+# can override it).
+CURSES_LIB_SWITCHES = @CURSESLIBSW@
+
+# Libraries to use when linking: must include at least Ck, Tcl, curses,
+# and the math library (in that order). The "LIBS" part will be
+# replaced (or has already been replaced) with relevant libraries as
+# determined by the configure script.
+LIBS = @TCL_BUILD_LIB_SPEC@ @LIBS@ $(CURSES_LIB_SWITCHES) @DL_LIBS@ @MATH_LIBS@ -lc
+
+# To change the compiler switches, for example to change from -O
+# to -g, change the following line:
+CFLAGS = -O @TCL_INCLUDE_SPEC@
+
+# To disable ANSI-C procedure prototypes reverse the comment characters
+# on the following lines:
+PROTO_FLAGS =
+#PROTO_FLAGS = -DNO_PROTOTYPE
+
+# To enable memory debugging reverse the comment characters on the following
+# lines. Warning: if you enable memory debugging, you must do it
+# *everywhere*, including all the code that calls Tcl, and you must use
+# ckalloc and ckfree everywhere instead of malloc and free.
+MEM_DEBUG_FLAGS =
+#MEM_DEBUG_FLAGS = -DTCL_MEM_DEBUG
+
+# Some versions of make, like SGI's, use the following variable to
+# determine which shell to use for executing commands:
+SHELL = /bin/sh
+
+# Ck used to let the configure script choose which program to use
+# for installing, but there are just too many different versions of
+# "install" around; better to use the install-sh script that comes
+# with the distribution, which is slower but guaranteed to work.
+
+INSTALL = @srcdir@/install-sh -c
+
+#----------------------------------------------------------------
+# The symbols below provide support for dynamic loading and shared
+# libraries. The values of the symbols are normally set by the
+# configure script. You shouldn't normally need to modify any of
+# these definitions by hand. However, you can reverse the comments
+# on the pairs of lines to force "no dynamic loading or shared
+# libraries".
+#----------------------------------------------------------------
+
+# Additional cc flags needed in order to compile Ck as a shared library.
+# This will be an empty string if Ck isn't configured as a shared library.
+CK_SHLIB_CFLAGS = @CK_SHLIB_CFLAGS@
+#CK_SHLIB_CFLAGS =
+
+# Base command to use for combining object files into a shared
+# library:
+SHLIB_LD = @SHLIB_LD@
+
+# Suffix to use for the name of the shared library. An empty string
+# means we don't know how to use shared libraries on this platform.
+SHLIB_SUFFIX = @SHLIB_SUFFIX@
+#SHLIB_SUFFIX =
+
+# Version string to tack onto the name of shared libraries (after the
+# suffix), if it is needed for -lxxx to work during linking (e.g. on
+# FreeBSD and NetBSD).
+SHLIB_VERSION = @SHLIB_VERSION@
+#SHLIB_VERSION =
+
+# Library file(s) to include in cwsh and other base applications
+# in order for the the "load" command to work (e.g. "-ldl").
+DL_LIBS = @DL_LIBS@
+#DL_LIBS =
+
+# Flags to pass to the compiler when linking object files into
+# an executable cwsh.
+LD_FLAGS = @LD_FLAGS@ @CK_LD_SEARCH_FLAGS@
+#LD_FLAGS =
+
+CK_LIB_FILE = @CK_LIB_FILE@
+#CK_LIB_FILE = libck.a
+
+#----------------------------------------------------------------
+# The information below is modified by the configure script when
+# Makefile is generated from Makefile.in. You shouldn't normally
+# modify any of this stuff by hand.
+#----------------------------------------------------------------
+
+AC_FLAGS = @DEFS@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+RANLIB = @RANLIB@
+SRC_DIR = @srcdir@
+VPATH = @srcdir@
+
+#----------------------------------------------------------------
+# The information below should be usable as is. The configure
+# script won't modify it and you shouldn't need to modify it
+# either.
+#----------------------------------------------------------------
+
+CC = @CC@
+CC_SWITCHES = ${CFLAGS} ${CK_SHLIB_CFLAGS} -I${SRC_DIR} -I${TCL_DIR} \
+ ${CURSES_INCLUDES} ${AC_FLAGS} ${PROTO_FLAGS} ${MEM_DEBUG_FLAGS} \
+ -DCK_LIBRARY=\"${CK_LIBRARY}\"
+
+WIDGOBJS = ckButton.o ckEntry.o ckFrame.o ckListbox.o \
+ ckMenu.o ckMenubutton.o ckMessage.o ckScrollbar.o ckTree.o
+
+TEXTOBJS = ckText.o ckTextBTree.o ckTextDisp.o ckTextIndex.o \
+ ckTextMark.o ckTextTag.o
+
+OBJS = ckBind.o ckBorder.o ckCmds.o ckConfig.o ckEvent.o ckFocus.o \
+ ckGeometry.o ckGet.o ckGrid.o ckMain.o ckOption.o ckPack.o ckPlace.o \
+ ckPreserve.o ckRecorder.o ckUtil.o ckWindow.o tkEvent.o \
+ $(WIDGOBJS) $(TEXTOBJS)
+
+SRCS = ckBind.c ckBorder.c ckCmds.c ckConfig.c ckEvent.c ckFocus.c \
+ ckGeometry.c ckGet.c ckGrid.c ckMain.c ckOption.c ckPack.c ckPlace.c \
+ ckPreserve.c ckRecorder.c ckUtil.c ckWindow.c tkEvent.c \
+ ckButton.c ckEntry.c ckFrame.c ckListbox.c \
+ ckMenu.c ckMenubutton.c ckMessage.c ckScrollbar.o \
+ ckText.c ckTextBTree.c ckTextDisp.c ckTextIndex.c \
+ ckTextMark.c ckTextTag.c ckTree.c \
+ ckAppInit.c
+
+HDRS = default.h ks_names.h ck.h ckPort.h ckText.h
+
+all: cwsh
+
+@CK_LIB_FILE@: $(OBJS)
+ rm -f @CK_LIB_FILE@
+ @MAKE_LIB@
+ $(RANLIB) @CK_LIB_FILE@
+
+cwsh: ckAppInit.o $(CK_LIB_FILE)
+ $(CC) $(LD_FLAGS) ckAppInit.o @CK_BUILD_LIB_SPEC@ $(LIBS) -o cwsh
+
+configInfo: Makefile
+ @rm -f configInfo
+ @echo "# Definitions and libraries needed to build Ck applications" >> configInfo
+ @echo "# (generated by the configure script):" >> configInfo
+ @echo "CK_CC_SWITCHES = ${AC_FLAGS} ${MEM_DEBUG_FLAGS}" >> configInfo
+ @echo "CK_CURSES_INCLUDES = ${CURSES_INCLUDES}" >> configInfo
+ @echo "CK_LIBS = ${CURSES_LIB_SWITCHES} @LIBS@" >> configInfo
+
+install: install-binaries install-libraries
+
+install-binaries: $(CK_LIB_FILE) cwsh
+ @for i in $(LIB_INSTALL_DIR) $(BIN_INSTALL_DIR) $(LIB_INSTALL_DIR)/ck$(CK_VERSION) ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @echo "Installing $(CK_LIB_FILE)"
+ @$(INSTALL_DATA) $(CK_LIB_FILE) $(LIB_INSTALL_DIR)/$(CK_LIB_FILE)
+ @$(INSTALL_DATA) pkgIndex.tcl $(LIB_INSTALL_DIR)/ck$(CK_VERSION)/pkgIndex.tcl
+ @$(RANLIB) $(LIB_INSTALL_DIR)/$(CK_LIB_FILE)
+ chmod 555 $(LIB_INSTALL_DIR)/$(CK_LIB_FILE)
+ @echo "Installing cwsh"
+ @$(INSTALL_PROGRAM) cwsh $(BIN_INSTALL_DIR)/cwsh
+
+install-libraries:
+ @for i in $(INSTALL_ROOT)$(prefix)/lib $(INCLUDE_INSTALL_DIR) \
+ $(SCRIPT_INSTALL_DIR) ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @echo "Installing ck.h"
+ @$(INSTALL_DATA) $(SRC_DIR)/ck.h $(INCLUDE_INSTALL_DIR)
+ for i in $(SRC_DIR)/library/*.tcl $(SRC_DIR)/library/tclIndex \
+ $(SRC_DIR)/ckAppInit.c; do \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR); \
+ done;
+
+install-demos:
+ @for i in $(INSTALL_ROOT)$(prefix)/lib $(SCRIPT_INSTALL_DIR) \
+ $(SCRIPT_INSTALL_DIR)/demos \
+ $(SCRIPT_INSTALL_DIR)/demos/images ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @for i in $(SRC_DIR)/library/demos/*; \
+ do \
+ if [ -f $$i ] ; then \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/demos; \
+ fi; \
+ done;
+ @for i in $(DEMOPROGS); \
+ do \
+ chmod 755 $(SCRIPT_INSTALL_DIR)/demos/$$i; \
+ done;
+ @for i in $(SRC_DIR)/library/demos/images/*; \
+ do \
+ if [ -f $$i ] ; then \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/demos/images; \
+ fi; \
+ done;
+
+install-man:
+ @for i in $(MAN_INSTALL_DIR) $(MAN1_INSTALL_DIR) $(MAN3_INSTALL_DIR) $(MANN_INSTALL_DIR) ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @cd $(SRC_DIR)/doc; for i in *.1; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MAN1_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MAN1_INSTALL_DIR)/$$i; \
+ chmod 444 $(MAN1_INSTALL_DIR)/$$i; \
+ done;
+ @cd $(SRC_DIR)/doc; for i in *.3; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MAN3_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MAN3_INSTALL_DIR)/$$i; \
+ chmod 444 $(MAN3_INSTALL_DIR)/$$i; \
+ done;
+ @cd $(SRC_DIR)/doc; for i in *.n; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MANN_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MANN_INSTALL_DIR)/$$i; \
+ chmod 444 $(MANN_INSTALL_DIR)/$$i; \
+ done;
+
+Makefile: $(SRC_DIR)/Makefile.in
+ $(SHELL) config.status
+
+clean:
+ rm -f *.a *.o core errs *~ ./.#* \#* TAGS *.E a.out errors cwsh \
+ config.info libck*.so libck*.a
+
+distclean: clean
+ rm -f Makefile config.status config.cache config.log config.sub ckConfig.sh
+
+depend:
+ makedepend -- $(CC_SWITCHES) -- $(SRCS)
+
+orig: ../libck-tcl_$(SRCVERSION).orig.tar.gz
+
+../libck-tcl_$(SRCVERSION).orig.tar.gz: distclean
+ tar -C .. -czf $@ --exclude "*/debian*" --exclude "*.3ck" --exclude "*/CVS*" `basename \`pwd\``
+
+deb: orig
+ debuild
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) $<
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
--- /dev/null
+How to compile and install Ck8.0
+--------------------------------
+
+1. Type "./configure". This runs a configuration script made by GNU
+ autoconf, which configures Ck for your system and creates a Makefile.
+ The configure script allows you to customize the configuration to
+ your local needs; for details how to do this, type "./configure --help"
+ or refer to the autoconf documentation (not included here).
+ The following special switches are supported by "configure":
+ --enable-shared If this switch is specified Ck will
+ compile itself as a shared library if
+ configure can figure out how to do this
+ on this platform.
+ --with-tcl Specifies the directory containing the
+ Tcl binaries and Tcl's platform-dependent
+ configuration information. By default the
+ Tcl distribution is assumed to be in
+ "../../tcl8.0".
+
+2. Type "make". This will create a library called "libck.a" or "libck8.0.so"
+ and an interpreter application called "cwsh" that allows you to type
+ Tcl commands interactively or execute scripts.
+
+3. Type "make install" to install Ck's binaries, script files, and man
+ pages in standard places. You'll need write permission on the install
+ directories to do this. If you plan to install the libraries, executables,
+ and script files whitout documentation, use "make install-binaries" and
+ "make install-libraries".
+
+4. Now you should be able to execute "cwsh". However, if you haven't installed
+ Ck then you'll need to set the CK_LIBRARY environment variable to hold the
+ full path name of the "library" subdirectory. If Ck has been built as
+ shared library, you have to set the LD_LIBRARY_PATH to include the directory
+ where "libck8.0.so" resides.
+
+
+So far, Ck8.0 has been successfully tested on various Linux distributions,
+on FreeBSD 3.3 with manually adapted Makefile, and on Windows NT 4.0 with
+a modified PDCURSES library. The Ck8.0 source tree should be able to be
+combined with Tcl7.4, 7.5, 7.6, and 8.0.
+Older version of Ck (which use Tcl7.4 or Tcl7.5) are in use for several
+years on HP-UX, AIX, and DEC Unix.
+
+
+Christian Werner, December 1999
+mailto:Christian.Werner@t-online.de
--- /dev/null
+/*
+ * ck.h --
+ *
+ * Declaration of all curses wish related things.
+ *
+ * Copyright (c) 1989-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995-2001 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#ifndef _CK_H
+#define _CK_H
+
+#ifndef _TCL
+#include <tcl.h>
+#endif
+
+#if (TCL_MAJOR_VERSION < 7)
+#error Tcl major version must be 7 or greater
+#endif
+
+#if (TCL_MAJOR_VERSION >= 8)
+#define CK_MAJOR_VERSION 8
+#if (TCL_MINOR_VERSION == 5)
+#define CK_VERSION "8.5"
+#define CK_MINOR_VERSION 5
+#define CK_USE_UTF 1
+#elif (TCL_MINOR_VERSION == 4)
+#define CK_VERSION "8.4"
+#define CK_MINOR_VERSION 4
+#define CK_USE_UTF 1
+#elif (TCL_MINOR_VERSION == 3)
+#define CK_VERSION "8.3"
+#define CK_MINOR_VERSION 3
+#define CK_USE_UTF 1
+#elif (TCL_MINOR_VERSION == 2)
+#define CK_VERSION "8.2"
+#define CK_MINOR_VERSION 2
+#define CK_USE_UTF 1
+#elif (TCL_MINOR_VERSION == 1)
+#define CK_VERSION "8.1"
+#define CK_MINOR_VERSION 1
+#define CK_USE_UTF 1
+#else
+#define CK_VERSION "8.0"
+#define CK_MINOR_VERSION 0
+#define CK_USE_UTF 0
+#endif
+#else
+
+#define CK_MAJOR_VERSION 4
+#define CK_USE_UTF 0
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+#define CK_VERSION "4.0"
+#define CK_MINOR_VERSION 0
+#else
+#define CK_VERSION "4.1"
+#define CK_MINOR_VERSION 1
+#endif
+#endif
+
+#ifndef RESOURCE_INCLUDED
+
+#ifdef __STDC__
+#include <stddef.h>
+#endif
+
+#ifdef USE_NCURSES
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+
+/*
+ * Keyboard symbols (KeySym)
+ */
+
+typedef int KeySym;
+#define NoSymbol (-2)
+
+/*
+ * Event structures.
+ */
+
+typedef struct {
+ long type;
+ struct CkWindow *winPtr;
+} CkAnyEvent;
+
+typedef struct {
+ long type;
+ struct CkWindow *winPtr;
+ int keycode;
+} CkKeyEvent;
+
+typedef struct {
+ long type;
+ struct CkWindow *winPtr;
+ int button, x, y, rootx, rooty;
+} CkMouseEvent;
+
+typedef struct {
+ long type;
+ struct CkWindow *winPtr;
+} CkWindowEvent;
+
+typedef union {
+ long type;
+ CkAnyEvent any;
+ CkKeyEvent key;
+ CkMouseEvent mouse;
+ CkWindowEvent win;
+} CkEvent;
+
+/*
+ * Event types/masks
+ */
+
+#define CK_EV_KEYPRESS 0x00000001
+#define CK_EV_MOUSE_DOWN 0x00000002
+#define CK_EV_MOUSE_UP 0x00000004
+#define CK_EV_UNMAP 0x00000010
+#define CK_EV_MAP 0x00000020
+#define CK_EV_EXPOSE 0x00000040
+#define CK_EV_DESTROY 0x00000080
+#define CK_EV_FOCUSIN 0x00000100
+#define CK_EV_FOCUSOUT 0x00000200
+#define CK_EV_BARCODE 0x10000000
+#define CK_EV_ALL 0xffffffff
+
+/*
+ * Additional types exported to clients.
+ */
+
+typedef char *Ck_Uid;
+typedef char *Ck_BindingTable;
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+typedef char *Tk_TimerToken;
+#else
+#define Tk_TimerToken Tcl_TimerToken
+#endif
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Additional procedure types defined by curses wish.
+ *
+ *--------------------------------------------------------------
+ */
+
+typedef void (Ck_EventProc) _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+typedef int (Ck_GenericProc) _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+typedef void (Ck_FreeProc) _ANSI_ARGS_((ClientData clientData));
+#else
+#define Ck_FreeProc Tcl_FreeProc
+#endif
+
+typedef void (Tk_FileProc) _ANSI_ARGS_((ClientData clientData, int mask));
+typedef int (Tk_FileProc2) _ANSI_ARGS_((ClientData clientData, int mask,
+ int flags));
+typedef void (Tk_IdleProc) _ANSI_ARGS_((ClientData clientData));
+typedef void (Tk_TimerProc) _ANSI_ARGS_((ClientData clientData));
+
+/*
+ * Each geometry manager (the packer, the placer, etc.) is represented
+ * by a structure of the following form, which indicates procedures
+ * to invoke in the geometry manager to carry out certain functions.
+ */
+
+typedef void (Ck_GeomRequestProc) _ANSI_ARGS_((ClientData clientData,
+ struct CkWindow *winPtr));
+typedef void (Ck_GeomLostSlaveProc) _ANSI_ARGS_((ClientData clientData,
+ struct CkWindow *winPtr));
+
+typedef struct Ck_GeomMgr {
+ char *name; /* Name of the geometry manager (command
+ * used to invoke it, or name of widget
+ * class that allows embedded widgets). */
+ Ck_GeomRequestProc *requestProc;
+ /* Procedure to invoke when a slave's
+ * requested geometry changes. */
+ Ck_GeomLostSlaveProc *lostSlaveProc;
+ /* Procedure to invoke when a slave is
+ * taken away from one geometry manager
+ * by another. NULL means geometry manager
+ * doesn't care when slaves are lost. */
+} Ck_GeomMgr;
+
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+
+/*
+ * Bits to pass to Tk_CreateFileHandler to indicate what sorts
+ * of events are of interest: (must be synced w/ tk.h !!!)
+ */
+
+#define TK_READABLE 1
+#define TK_WRITABLE 2
+#define TK_EXCEPTION 4
+
+/*
+ * Special return value from Tk_FileProc2 procedures indicating that
+ * an event was successfully processed.
+ */
+
+#define TK_FILE_HANDLED -1
+
+/*
+ * Flag values to pass to Tk_DoOneEvent to disable searches
+ * for some kinds of events:
+ */
+
+#define TK_DONT_WAIT 1
+#define TK_X_EVENTS 2
+#define TK_FILE_EVENTS 4
+#define TK_TIMER_EVENTS 8
+#define TK_IDLE_EVENTS 0x10
+#define TK_ALL_EVENTS 0x1e
+
+#else
+
+/*
+ * Flag values to pass to Tk_DoOneEvent to disable searches
+ * for some kinds of events:
+ */
+
+#define TK_DONT_WAIT TCL_DONT_WAIT
+#define TK_X_EVENTS TCL_WINDOW_EVENTS
+#define TK_FILE_EVENTS TCL_FILE_EVENTS
+#define TK_TIMER_EVENTS TCL_TIMER_EVENTS
+#define TK_IDLE_EVENTS TCL_IDLE_EVENTS
+#define TK_ALL_EVENTS TCL_ALL_EVENTS
+
+#endif
+
+/*
+ * One of the following structures exists for each event handler
+ * created by calling Ck_CreateEventHandler. This information
+ * is used by ckEvent.c only.
+ */
+
+typedef struct CkEventHandler {
+ long mask; /* Events for which to invoke proc. */
+ Ck_EventProc *proc; /* Procedure to invoke when an event
+ * in mask occurs. */
+ ClientData clientData; /* Argument to pass to proc. */
+ struct CkEventHandler *nextPtr; /* Next in list of handlers
+ * associated with window (NULL means
+ * end of list). */
+} CkEventHandler;
+
+/*
+ * Ck keeps the following data structure for the main
+ * window (created by a call to Ck_CreateMainWindow). It stores
+ * information that is shared by all of the windows associated
+ * with the application.
+ */
+
+typedef struct CkMainInfo {
+ struct CkWindow *winPtr; /* Pointer to main window. */
+ Tcl_Interp *interp; /* Interpreter associated with application. */
+ Tcl_HashTable nameTable; /* Hash table mapping path names to CkWindow
+ * structs for all windows related to this
+ * main window. Managed by ckWindow.c. */
+ Tcl_HashTable winTable; /* Ditto, for event handling. */
+ struct CkWindow *topLevPtr; /* Anchor for toplevel window list. */
+ struct CkWindow *focusPtr; /* Identifies window that currently has the
+ * focus. NULL means nobody has the focus.
+ * Managed by ckFocus.c. */
+ Ck_BindingTable bindingTable;
+ /* Used in conjunction with "bind" command
+ * to bind events to Tcl commands. */
+ struct ElArray *optionRootPtr;
+ /* Top level of option hierarchy for this
+ * main window. NULL means uninitialized.
+ * Managed by ckOption.c. */
+ int maxWidth, maxHeight; /* Max dimensions of curses screen. */
+ int refreshCount; /* Counts number of calls
+ * to Ck_EventuallyRefresh. */
+ int refreshDelay; /* Delay in milliseconds between updates;
+ * see comment in ckWindow.c. */
+ double lastRefresh; /* Delay computation for updates. */
+ Tk_TimerToken refreshTimer; /* Timer for delayed updates. */
+ ClientData mouseData; /* Value used by mouse handling code. */
+ ClientData barcodeData; /* Value used by bar code handling code. */
+ int flags; /* See definitions below. */
+#if CK_USE_UTF
+ Tcl_Encoding isoEncoding;
+ Tcl_DString isoBuffer;
+#endif
+} CkMainInfo;
+
+#define CK_HAS_COLOR 1
+#define CK_REVERSE_KLUDGE 2
+#define CK_HAS_MOUSE 4
+#define CK_MOUSE_XTERM 8
+#define CK_REFRESH_TIMER 16
+#define CK_HAS_BARCODE 32
+#define CK_NOCLR_ON_EXIT 64
+
+/*
+ * Ck keeps one of the following structures for each window.
+ * This information is (mostly) managed by ckWindow.c.
+ */
+
+typedef struct CkWindow {
+
+ /*
+ * Structural information:
+ */
+
+ WINDOW *window; /* Curses window. NULL means window
+ * hasn't actually been created yet, or it's
+ * been deleted. */
+ struct CkWindow *childList; /* First in list of child windows,
+ * or NULL if no children. */
+ struct CkWindow *lastChildPtr;
+ /* Last in list of child windows, or NULL
+ * if no children. */
+ struct CkWindow *parentPtr; /* Pointer to parent window. */
+ struct CkWindow *nextPtr; /* Next in list of children with
+ * same parent (NULL if end of list). */
+ struct CkWindow *topLevPtr; /* Next toplevel if this is toplevel. */
+ CkMainInfo *mainPtr; /* Information shared by all windows
+ * associated with the main window. */
+
+ /*
+ * Name and type information for the window:
+ */
+
+ char *pathName; /* Path name of window (concatenation
+ * of all names between this window and
+ * its top-level ancestor). This is a
+ * pointer into an entry in mainPtr->nameTable.
+ */
+ Ck_Uid nameUid; /* Name of the window within its parent
+ * (unique within the parent). */
+ Ck_Uid classUid; /* Class of the window. NULL means window
+ * hasn't been given a class yet. */
+
+ /*
+ * Information kept by the event manager (ckEvent.c):
+ */
+
+ CkEventHandler *handlerList;/* First in list of event handlers
+ * declared for this window, or
+ * NULL if none. */
+
+ /*
+ * Information kept by the bind/bindtags mechanism (ckCmds.c):
+ */
+
+ ClientData *tagPtr; /* Points to array of tags used for bindings
+ * on this window. Each tag is a Ck_Uid.
+ * Malloc'ed. NULL means no tags. */
+ int numTags; /* Number of tags at *tagPtr. */
+
+ /*
+ * Information used by ckFocus.c for toplevel windows.
+ */
+
+ struct CkWindow *focusPtr; /* If toplevel, this was the last child
+ * which had the focus. */
+
+ /*
+ * Information used by ckGeometry.c for geometry managers.
+ */
+
+ Ck_GeomMgr *geomMgrPtr; /* Procedure to manage geometry, NULL
+ * means unmanaged. */
+ ClientData geomData; /* Argument for geomProc. */
+ int reqWidth, reqHeight; /* Requested width/height of window. */
+
+ /*
+ * Information used by ckOption.c to manage options for the
+ * window.
+ */
+
+ int optionLevel; /* -1 means no option information is
+ * currently cached for this window.
+ * Otherwise this gives the level in
+ * the option stack at which info is
+ * cached. */
+
+ /*
+ * Geometry and other attributes of window.
+ */
+
+ int x, y; /* Top-left corner with respect to
+ * parent window. */
+ int width, height; /* Width and height of window. */
+ int fg, bg; /* Foreground/background colors. */
+ int attr; /* Video attributes. */
+ int flags; /* Various flag values, see below. */
+
+
+} CkWindow;
+
+/*
+ * Flag values for CkWindow structures are:
+ *
+ * CK_MAPPED: 1 means window is currently mapped,
+ * 0 means unmapped.
+ * CK_BORDER: 1 means the window has a one character
+ * cell wide border around it.
+ * 0 means no border.
+ * CK_TOPLEVEL: 1 means this is a toplevel window.
+ * CK_SHOW_CURSOR: 1 means show the terminal's cursor if
+ * this window has the focus.
+ * CK_RECURSIVE_DESTROY: 1 means a recursive destroy is in
+ * progress, so some cleanup operations
+ * can be omitted.
+ * CK_ALREADY_DEAD: 1 means the window is in the process of
+ * being destroyed already.
+ */
+
+#define CK_MAPPED 1
+#define CK_BORDER 2
+#define CK_TOPLEVEL 4
+#define CK_SHOW_CURSOR 8
+#define CK_RECURSIVE_DESTROY 16
+#define CK_ALREADY_DEAD 32
+#define CK_DONTRESTRICTSIZE 64
+
+/*
+ * Window stacking literals
+ */
+
+#define CK_ABOVE 0
+#define CK_BELOW 1
+
+/*
+ * Window border structure.
+ */
+
+typedef struct {
+ char *name; /* Name of border, malloc'ed. */
+ int gchar[9]; /* ACS chars making up border. */
+} CkBorder;
+
+/*
+ * Enumerated type for describing a point by which to anchor something:
+ */
+
+typedef enum {
+ CK_ANCHOR_N, CK_ANCHOR_NE, CK_ANCHOR_E, CK_ANCHOR_SE,
+ CK_ANCHOR_S, CK_ANCHOR_SW, CK_ANCHOR_W, CK_ANCHOR_NW,
+ CK_ANCHOR_CENTER
+} Ck_Anchor;
+
+/*
+ * Enumerated type for describing a style of justification:
+ */
+
+typedef enum {
+ CK_JUSTIFY_LEFT, CK_JUSTIFY_RIGHT,
+ CK_JUSTIFY_CENTER, CK_JUSTIFY_FILL
+} Ck_Justify;
+
+/*
+ * Result values returned by Ck_GetScrollInfo:
+ */
+
+#define CK_SCROLL_MOVETO 1
+#define CK_SCROLL_PAGES 2
+#define CK_SCROLL_UNITS 3
+#define CK_SCROLL_ERROR 4
+
+/*
+ * Flags passed to CkMeasureChars/CkDisplayChars:
+ */
+
+#define CK_WHOLE_WORDS 1
+#define CK_AT_LEAST_ONE 2
+#define CK_PARTIAL_OK 4
+#define CK_NEWLINES_NOT_SPECIAL 8
+#define CK_IGNORE_TABS 16
+#define CK_FILL_UNTIL_EOL 32
+
+/*
+ * Priority levels to pass to Tk_AddOption:
+ */
+
+#define CK_WIDGET_DEFAULT_PRIO 20
+#define CK_STARTUP_FILE_PRIO 40
+#define CK_USER_DEFAULT_PRIO 60
+#define CK_INTERACTIVE_PRIO 80
+#define CK_MAX_PRIO 100
+
+/*
+ * Structure used to describe application-specific configuration
+ * options: indicates procedures to call to parse an option and
+ * to return a text string describing an option.
+ */
+
+typedef int (Ck_OptionParseProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, CkWindow *winPtr, char *value, char *widgRec,
+ int offset));
+typedef char *(Ck_OptionPrintProc) _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtr));
+
+typedef struct Ck_CustomOption {
+ Ck_OptionParseProc *parseProc; /* Procedure to call to parse an
+ * option and store it in converted
+ * form. */
+ Ck_OptionPrintProc *printProc; /* Procedure to return a printable
+ * string describing an existing
+ * option. */
+ ClientData clientData; /* Arbitrary one-word value used by
+ * option parser: passed to
+ * parseProc and printProc. */
+} Ck_CustomOption;
+
+/*
+ * Structure used to specify information for Ck_ConfigureWidget. Each
+ * structure gives complete information for one option, including
+ * how the option is specified on the command line, where it appears
+ * in the option database, etc.
+ */
+
+typedef struct Ck_ConfigSpec {
+ int type; /* Type of option, such as CK_CONFIG_COLOR;
+ * see definitions below. Last option in
+ * table must have type CK_CONFIG_END. */
+ char *argvName; /* Switch used to specify option in argv.
+ * NULL means this spec is part of a group. */
+ char *dbName; /* Name for option in option database. */
+ char *dbClass; /* Class for option in database. */
+ char *defValue; /* Default value for option if not
+ * specified in command line or database. */
+ int offset; /* Where in widget record to store value;
+ * use Ck_Offset macro to generate values
+ * for this. */
+ int specFlags; /* Any combination of the values defined
+ * below; other bits are used internally
+ * by ckConfig.c. */
+ Ck_CustomOption *customPtr; /* If type is CK_CONFIG_CUSTOM then this is
+ * a pointer to info about how to parse and
+ * print the option. Otherwise it is
+ * irrelevant. */
+} Ck_ConfigSpec;
+
+/*
+ * Type values for Ck_ConfigSpec structures. See the user
+ * documentation for details.
+ */
+
+#define CK_CONFIG_BOOLEAN 1
+#define CK_CONFIG_INT 2
+#define CK_CONFIG_DOUBLE 3
+#define CK_CONFIG_STRING 4
+#define CK_CONFIG_UID 5
+#define CK_CONFIG_COLOR 6
+#define CK_CONFIG_BORDER 7
+#define CK_CONFIG_JUSTIFY 8
+#define CK_CONFIG_ANCHOR 9
+#define CK_CONFIG_SYNONYM 10
+#define CK_CONFIG_WINDOW 11
+#define CK_CONFIG_COORD 12
+#define CK_CONFIG_ATTR 13
+#define CK_CONFIG_CUSTOM 14
+#define CK_CONFIG_END 15
+
+/*
+ * Macro to use to fill in "offset" fields of Ck_ConfigInfos.
+ * Computes number of bytes from beginning of structure to a
+ * given field.
+ */
+
+#ifdef offsetof
+#define Ck_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Ck_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+
+/*
+ * Possible values for flags argument to Ck_ConfigureWidget:
+ */
+
+#define CK_CONFIG_ARGV_ONLY 1
+
+/*
+ * Possible flag values for Ck_ConfigInfo structures. Any bits at
+ * or above CK_CONFIG_USER_BIT may be used by clients for selecting
+ * certain entries. Before changing any values here, coordinate with
+ * tkConfig.c (internal-use-only flags are defined there).
+ */
+
+#define CK_CONFIG_COLOR_ONLY 1
+#define CK_CONFIG_MONO_ONLY 2
+#define CK_CONFIG_NULL_OK 4
+#define CK_CONFIG_DONT_SET_DEFAULT 8
+#define CK_CONFIG_OPTION_SPECIFIED 0x10
+#define CK_CONFIG_USER_BIT 0x100
+
+extern Ck_Uid ckNormalUid;
+extern Ck_Uid ckActiveUid;
+extern Ck_Uid ckDisabledUid;
+
+/*
+ * Internal procedures.
+ */
+
+#if defined(_WIN32) || defined(WIN32)
+# ifdef BUILD_ck
+# undef EXTERN
+# define EXTERN __declspec(dllexport)
+# endif
+#endif
+
+
+EXTERN int CkAllKeyNames _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int CkBarcodeCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN void CkBindEventProc _ANSI_ARGS_((CkWindow *winPtr,
+ CkEvent *eventPtr));
+EXTERN int CkCopyAndGlobalEval _ANSI_ARGS_((Tcl_Interp *interp,
+ char *string));
+EXTERN void CkDisplayChars _ANSI_ARGS_((CkMainInfo *mainPtr,
+ WINDOW *window, char *string,
+ int numChars, int x, int y, int tabOrigin, int flags));
+EXTERN void CkEventDeadWindow _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void CkFreeBindingTags _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN char * CkGetBarcodeData _ANSI_ARGS_((CkMainInfo *mainPtr));
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+EXTERN int CkHandleInput _ANSI_ARGS_((ClientData clientData, int mask,
+ int flags));
+#else
+EXTERN void CkHandleInput _ANSI_ARGS_((ClientData clientData, int mask));
+#endif
+
+EXTERN int CkInitFrame _ANSI_ARGS_((Tcl_Interp *interp, CkWindow *winPtr,
+ int argc, char **argv));
+EXTERN char * CkKeysymToString _ANSI_ARGS_((KeySym keySym, int printControl));
+EXTERN int CkMeasureChars _ANSI_ARGS_((CkMainInfo *mainPtr,
+ char *source, int maxChars,
+ int startX, int maxX, int tabOrigin, int flags,
+ int *nextPtr, int *nextCPtr));
+EXTERN void CkOptionClassChanged _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void CkOptionDeadWindow _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN KeySym CkStringToKeysym _ANSI_ARGS_((char *name));
+EXTERN int CkTermHasKey _ANSI_ARGS_((Tcl_Interp *interp, char *name));
+EXTERN void CkUnderlineChars _ANSI_ARGS_((CkMainInfo *mainPtr,
+ WINDOW *window, char *string,
+ int numChars, int x, int y, int tabOrigin, int flags,
+ int first, int last));
+
+#if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+
+/*
+ * Resource tracking from tkPreserve.c has moved to Tcl version 7.5:
+ */
+
+#define Ck_EventuallyFree Tcl_EventuallyFree
+#define Ck_Preserve Tcl_Preserve
+#define Ck_Release Tcl_Release
+
+#endif
+
+/*
+ * Exported procedures.
+ */
+
+EXTERN void Ck_AddOption _ANSI_ARGS_((CkWindow *winPtr, char *name,
+ char *value, int priority));
+EXTERN void Ck_BindEvent _ANSI_ARGS_((Ck_BindingTable bindingTable,
+ CkEvent *eventPtr, CkWindow *winPtr, int numObjects,
+ ClientData *objectPtr));
+EXTERN void Ck_ClearToBot _ANSI_ARGS_((CkWindow *winPtr, int x, int y));
+EXTERN void Ck_ClearToEol _ANSI_ARGS_((CkWindow *winPtr, int x, int y));
+EXTERN int Ck_ConfigureInfo _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, Ck_ConfigSpec *specs, char *widgRec,
+ char *argvName, int flags));
+EXTERN int Ck_ConfigureValue _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, Ck_ConfigSpec *specs, char *widgRec,
+ char *argvName, int flags));
+EXTERN int Ck_ConfigureWidget _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, Ck_ConfigSpec *specs,
+ int argc, char **argv, char *widgRec, int flags));
+EXTERN int Ck_CreateBinding _ANSI_ARGS_((Tcl_Interp *interp,
+ Ck_BindingTable bindingTable, ClientData object,
+ char *eventString, char *command, int append));
+EXTERN Ck_BindingTable Ck_CreateBindingTable _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void Ck_CreateEventHandler _ANSI_ARGS_((CkWindow *winPtr, long mask,
+ Ck_EventProc *proc, ClientData clientData));
+EXTERN void Ck_CreateGenericHandler _ANSI_ARGS_((Ck_GenericProc *proc,
+ ClientData clientData));
+EXTERN CkWindow *Ck_CreateMainWindow _ANSI_ARGS_((Tcl_Interp *interp,
+ char *className));
+EXTERN CkWindow *Ck_CreateWindow _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *parentPtr, char *name, int toplevel));
+EXTERN CkWindow *Ck_CreateWindowFromPath _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *anywin, char *pathName, int toplevel));
+EXTERN void Ck_DeleteAllBindings _ANSI_ARGS_((Ck_BindingTable bindingTable,
+ ClientData object));
+EXTERN int Ck_DeleteBinding _ANSI_ARGS_((Tcl_Interp *interp,
+ Ck_BindingTable bindingTable, ClientData object,
+ char *eventString));
+EXTERN void Ck_DeleteBindingTable
+ _ANSI_ARGS_((Ck_BindingTable bindingTable));
+EXTERN void Ck_DeleteEventHandler _ANSI_ARGS_((CkWindow *winPtr, long mask,
+ Ck_EventProc *proc, ClientData clientData));
+EXTERN void Ck_DeleteGenericHandler _ANSI_ARGS_((Ck_GenericProc *proc,
+ ClientData clientData));
+EXTERN void Ck_DestroyWindow _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void Ck_DrawBorder _ANSI_ARGS_((CkWindow *winPtr,
+ CkBorder *borderPtr, int x, int y, int width, int height));
+#if ((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+EXTERN void Ck_EventuallyFree _ANSI_ARGS_((ClientData clientData,
+ Ck_FreeProc *freeProc));
+#endif
+EXTERN void Ck_EventuallyRefresh _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void Ck_FreeBorder _ANSI_ARGS_((CkBorder *borderPtr));
+EXTERN void Ck_FreeOptions _ANSI_ARGS_((Ck_ConfigSpec *specs,
+ char *widgrec, int needFlags));
+EXTERN void Ck_GeometryRequest _ANSI_ARGS_((CkWindow *winPtr,
+ int reqWidth, int reqHeight));
+EXTERN void Ck_GetAllBindings _ANSI_ARGS_((Tcl_Interp *interp,
+ Ck_BindingTable bindingTable, ClientData object));
+EXTERN int Ck_GetAnchor _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ Ck_Anchor *anchorPtr));
+EXTERN int Ck_GetAttr _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ int *attrPtr));
+EXTERN char * Ck_GetBinding _ANSI_ARGS_((Tcl_Interp *inter,
+ Ck_BindingTable bindingTable, ClientData object,
+ char *eventString));
+EXTERN CkBorder *Ck_GetBorder _ANSI_ARGS_((Tcl_Interp *interp,
+ char *string));
+EXTERN int Ck_GetColor _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ int *colorPtr));
+EXTERN int Ck_GetCoord _ANSI_ARGS_((Tcl_Interp *interp, CkWindow *winPtr,
+ char *string, int *intPtr));
+EXTERN int Ck_GetEncoding _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Ck_GetGChar _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ int *gchar));
+EXTERN int Ck_GetJustify _ANSI_ARGS_((Tcl_Interp *interp, char *string,
+ Ck_Justify *justifyPtr));
+EXTERN Ck_Uid Ck_GetOption _ANSI_ARGS_((CkWindow *winPtr, char *name,
+ char *class));
+EXTERN int Ck_GetPair _ANSI_ARGS_((CkWindow *winPtr, int fg, int bg));
+EXTERN void Ck_GetRootGeometry _ANSI_ARGS_((CkWindow *winPtr, int *xPtr,
+ int *yPtr, int *widthPtr, int *heightPtr));
+EXTERN int Ck_GetScrollInfo _ANSI_ARGS_((Tcl_Interp *interp,
+ int argc, char **argv, double *dblPtr, int *intPtr));
+EXTERN Ck_Uid Ck_GetUid _ANSI_ARGS_((char *string));
+EXTERN CkWindow *Ck_GetWindowXY _ANSI_ARGS_((CkMainInfo *mainPtr, int *xPtr,
+ int *yPtr, int mode));
+EXTERN void Ck_HandleEvent _ANSI_ARGS_((CkMainInfo *mainPtr,
+ CkEvent *eventPtr));
+EXTERN int Ck_Init _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void Ck_Main _ANSI_ARGS_((int argc, char **argv,
+ int (*appInitProc)()));
+EXTERN void Ck_MainLoop _ANSI_ARGS_((void));
+EXTERN CkWindow *Ck_MainWindow _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void Ck_MaintainGeometry _ANSI_ARGS_((CkWindow *slave,
+ CkWindow *master, int x, int y, int width,
+ int height));
+EXTERN void Ck_MakeWindowExist _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void Ck_ManageGeometry _ANSI_ARGS_((CkWindow *winPtr,
+ Ck_GeomMgr *mgrPtr, ClientData clientData));
+EXTERN void Ck_MapWindow _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN void Ck_MoveWindow _ANSI_ARGS_((CkWindow *winPtr, int x, int y));
+EXTERN char * Ck_NameOfAnchor _ANSI_ARGS_((Ck_Anchor anchor));
+EXTERN char * Ck_NameOfAttr _ANSI_ARGS_((int attr));
+EXTERN char * Ck_NameOfBorder _ANSI_ARGS_((CkBorder *borderPtr));
+EXTERN char * Ck_NameOfColor _ANSI_ARGS_((int color));
+EXTERN char * Ck_NameOfJustify _ANSI_ARGS_((Ck_Justify justify));
+EXTERN CkWindow *Ck_NameToWindow _ANSI_ARGS_((Tcl_Interp *interp,
+ char *pathName, CkWindow *winPtr));
+#if ((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+EXTERN void Ck_Preserve _ANSI_ARGS_((ClientData clientData));
+EXTERN void Ck_Release _ANSI_ARGS_((ClientData clientData));
+#endif
+EXTERN void Ck_ResizeWindow _ANSI_ARGS_((CkWindow *winPtr, int width,
+ int height));
+EXTERN int Ck_RestackWindow _ANSI_ARGS_((CkWindow *winPtr, int aboveBelow,
+ CkWindow *otherPtr));
+EXTERN void Ck_SetClass _ANSI_ARGS_((CkWindow *winPtr, char *className));
+EXTERN int Ck_SetEncoding _ANSI_ARGS_((Tcl_Interp *interp, char *name));
+EXTERN void Ck_SetFocus _ANSI_ARGS_((CkWindow *winPtr));
+EXTERN int Ck_SetGChar _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ int gchar));
+EXTERN void Ck_SetHWCursor _ANSI_ARGS_((CkWindow *winPtr, int newState));
+EXTERN void Ck_SetInternalBorder _ANSI_ARGS_((CkWindow *winPtr,
+ int onoff));
+EXTERN void Ck_SetWindowAttr _ANSI_ARGS_((CkWindow *winPtr, int fg,
+ int bg, int attr));
+EXTERN void Ck_UnmaintainGeometry _ANSI_ARGS_((CkWindow *slave,
+ CkWindow *master));
+EXTERN void Ck_UnmapWindow _ANSI_ARGS_((CkWindow *winPtr));
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+/*
+ * Event handling procedures.
+ */
+
+EXTERN void Tk_BackgroundError _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void Tk_CancelIdleCall _ANSI_ARGS_((Tk_IdleProc *proc,
+ ClientData clientData));
+EXTERN void Tk_CreateFileHandler _ANSI_ARGS_((int fd, int mask,
+ Tk_FileProc *proc, ClientData clientData));
+EXTERN void Tk_CreateFileHandler2 _ANSI_ARGS_((int fd,
+ Tk_FileProc2 *proc, ClientData clientData));
+EXTERN Tk_TimerToken Tk_CreateTimerHandler _ANSI_ARGS_((int milliseconds,
+ Tk_TimerProc *proc, ClientData clientData));
+EXTERN void Tk_DeleteFileHandler _ANSI_ARGS_((int fd));
+EXTERN void Tk_DeleteTimerHandler _ANSI_ARGS_((Tk_TimerToken token));
+EXTERN int Tk_DoOneEvent _ANSI_ARGS_((int flags));
+EXTERN void Tk_DoWhenIdle _ANSI_ARGS_((Tk_IdleProc *proc,
+ ClientData clientData));
+EXTERN void Tk_DoWhenIdle2 _ANSI_ARGS_((Tk_IdleProc *proc,
+ ClientData clientData));
+EXTERN void Tk_Sleep _ANSI_ARGS_((int ms));
+
+#endif
+
+/*
+ * Command procedures.
+ */
+
+EXTERN int Ck_BellCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_BindCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_BindtagsCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_CursesCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_DestroyCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_ExitCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_FocusCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_GridCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_LowerCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_OptionCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_PackCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_PlaceCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_RaiseCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_RecorderCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_TkwaitCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_UpdateCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_WinfoCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+
+EXTERN int Tk_AfterCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Tk_FileeventCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+
+/*
+ * Widget creation procedures.
+ */
+
+EXTERN int Ck_ButtonCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_EntryCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_FrameCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_ListboxCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_MenuCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_MenubuttonCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_MessageCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_ScrollbarCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_TextCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+EXTERN int Ck_TreeCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+
+#endif /* RESOURCE_INCLUDED */
+#endif /* _CK_H */
--- /dev/null
+%define version 8.0
+
+Name: ck
+Version: %{version}
+Release: 7
+Group: Programming/Interpreter
+Summary: Tk-like Curses toolkit on top of Tcl
+Copyright: BSD
+Packager: <chw@ch-werner.de>
+URL: http://www.ch-werner.de/ck
+BuildRoot: /var/tmp/%{name}%{version}
+Source: http://www.ch-werner.de/ck/ck%{version}.tar.gz
+Requires: tcl >= %{version}
+
+%description
+Ck is a (XPG4|n)curses widget set modelled after Tk designed to work
+closely with the tcl scripting language. It allows you to write simple
+programs with full featured console mode UIs. Tcl/Ck applications can
+also be run on Windows platforms in console mode.
+
+%prep
+%setup -n %{name}%{version}
+
+%build
+./configure --prefix=/usr --disable-shared --enable-gcc --with-tcl=/usr/lib
+ckversion=`. ckConfig.sh ; echo $CK_VERSION`
+make CFLAGS="$RPM_OPT_FLAGS" libck${ckversion}.a
+mv libck${ckversion}.a /tmp
+make distclean
+mv /tmp/libck${ckversion}.a .
+./configure --prefix=/usr --enable-shared --enable-gcc --with-tcl=/usr/lib
+make CFLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/bin
+mkdir -p $RPM_BUILD_ROOT/usr/lib
+mkdir -p $RPM_BUILD_ROOT/usr/man/man1
+mkdir -p $RPM_BUILD_ROOT/usr/man/mann
+make INSTALL_ROOT=$RPM_BUILD_ROOT install install-man
+cp -p ckConfig.sh $RPM_BUILD_ROOT/usr/lib
+ckversion=`. ckConfig.sh ; echo $CK_VERSION`
+ln -sf libck${ckversion}.so $RPM_BUILD_ROOT/usr/lib/libck.so
+cp -p libck${ckversion}.a $RPM_BUILD_ROOT/usr/lib
+ln -sf libck${ckversion}.a $RPM_BUILD_ROOT/usr/lib/libck.a
+mv $RPM_BUILD_ROOT/usr/bin/cwsh $RPM_BUILD_ROOT/usr/bin/cwsh${ckversion}
+ln -sf cwsh${ckversion} $RPM_BUILD_ROOT/usr/bin/cwsh
+mkdir -p $RPM_BUILD_ROOT/usr/share/ck-${ckversion}/man
+mv $RPM_BUILD_ROOT/usr/man/mann $RPM_BUILD_ROOT/usr/share/ck-${ckversion}/man
+find $RPM_BUILD_ROOT/usr/share/ck-${ckversion}/man -type f -exec gzip {} \;
+find $RPM_BUILD_ROOT/usr/man -type f -exec gzip {} \;
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%files
+%defattr(-,root,root)
+/usr/bin/*
+/usr/lib/lib*
+/usr/lib/ck*
+/usr/man/man1/*
+/usr/share/ck-*
+
+%changelog
+* Sun Aug 26 2001 <chw@ch-werner.de>
+- environment variables CK_USE_ENCODING and CK_USE_GPM for
+ controlling standard encoding (Tcl >= 8.1) and GPM usage,
+ various fixes for UTF-8 handling and Win32 code pages.
+
+* Tue May 15 2001 <Christian.Werner@t-online.de>
+- fixed initial screen flashing, added -noclear option in exit cmd
+
+* Thu Dec 07 2000 <Christian.Werner@t-online.de>
+- fixes for Tcl versions >= 8.1 (UTF8 handling)
+
+* Fri Nov 24 2000 <Christian.Werner@t-online.de>
+- fixed Tcl version handling in configure
+
+* Wed Sep 20 2000 <Christian.Werner@t-online.de>
+- rebuilt with ckEvent fixes
+
+* Sun Aug 27 2000 <Christian.Werner@t-online.de>
+- repackaged with new Ck distrib
+
+* Fri Aug 25 2000 <Christian.Werner@t-online.de>
+- created
+
--- /dev/null
+/*
+ * ckAppInit.c --
+ *
+ * Provides a default version of the Tcl_AppInit procedure for
+ * use in curses wish.
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ck.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+#ifndef __WIN32__
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Ck_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+ Ck_Main(argc, argv, Tcl_AppInit);
+ return 0; /* Needed only to prevent compiler warning. */
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if (Ck_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+#if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+ Tcl_StaticPackage(interp, "Ck", Ck_Init, (Tcl_PackageInitProc *) NULL);
+#endif
+
+ /*
+ * Call the init procedures for included packages. Each call should
+ * look like this:
+ *
+ * if (Mod_Init(interp) == TCL_ERROR) {
+ * return TCL_ERROR;
+ * }
+ *
+ * where "Mod" is the name of the module.
+ */
+
+ /*
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ */
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+#ifndef CWSHRC
+#ifdef DJGPP
+# define CWSHRC "cwsh.rc"
+#else
+# define CWSHRC ".cwshrc"
+#endif
+#endif
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ tcl_RcFileName = "~/" CWSHRC;
+#else
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/" CWSHRC, TCL_GLOBAL_ONLY);
+#endif
+ return TCL_OK;
+}
+
--- /dev/null
+/*
+ * ckBind.c --
+ *
+ * This file provides procedures that associate Tcl commands
+ * with events or sequences of events.
+ *
+ * Copyright (c) 1989-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * The structure below represents a binding table. A binding table
+ * represents a domain in which event bindings may occur. It includes
+ * a space of objects relative to which events occur (usually windows,
+ * but not always), a history of recent events in the domain, and
+ * a set of mappings that associate particular Tcl commands with sequences
+ * of events in the domain. Multiple binding tables may exist at once,
+ * either because there are multiple applications open, or because there
+ * are multiple domains within an application with separate event
+ * bindings for each (for example, each canvas widget has a separate
+ * binding table for associating events with the items in the canvas).
+ *
+ * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
+ * below 30. To see this, consider a triple mouse button click while
+ * the Shift key is down (and auto-repeating). There may be as many
+ * as 3 auto-repeat events after each mouse button press or release
+ * (see the first large comment block within Ck_BindEvent for more on
+ * this), for a total of 20 events to cover the three button presses
+ * and two intervening releases. If you reduce EVENT_BUFFER_SIZE too
+ * much, shift multi-clicks will be lost.
+ *
+ */
+
+#define EVENT_BUFFER_SIZE 30
+typedef struct BindingTable {
+ CkEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
+ * (higher indices are for more recent
+ * events). */
+ int detailRing[EVENT_BUFFER_SIZE]; /* "Detail" information (keycodes for
+ * each entry in eventRing. */
+ int curEvent; /* Index in eventRing of most recent
+ * event. Newer events have higher
+ * indices. */
+ Tcl_HashTable patternTable; /* Used to map from an event to a list
+ * of patterns that may match that
+ * event. Keys are PatternTableKey
+ * structs, values are (PatSeq *). */
+ Tcl_HashTable objectTable; /* Used to map from an object to a list
+ * of patterns associated with that
+ * object. Keys are ClientData,
+ * values are (PatSeq *). */
+ Tcl_Interp *interp; /* Interpreter in which commands are
+ * executed. */
+} BindingTable;
+
+/*
+ * Structures of the following form are used as keys in the patternTable
+ * for a binding table:
+ */
+
+typedef struct PatternTableKey {
+ ClientData object; /* Identifies object (or class of objects)
+ * relative to which event occurred. For
+ * example, in the widget binding table for
+ * an application this is the path name of
+ * a widget, or a widget class, or "all". */
+ int type; /* Type of event. */
+ int detail; /* Additional information, such as
+ * keycode, or 0 if nothing
+ * additional.*/
+} PatternTableKey;
+
+/*
+ * The following structure defines a pattern, which is matched
+ * against events as part of the process of converting events
+ * into Tcl commands.
+ */
+
+typedef struct Pattern {
+ int eventType; /* Type of event. */
+ int detail; /* Additional information that must
+ * match event. Normally this is 0,
+ * meaning no additional information
+ * must match. For keystrokes this
+ * is the keycode. Keycode 0 means
+ * any keystroke, keycode -1 means
+ * control keystroke. */
+} Pattern;
+
+/*
+ * The structure below defines a pattern sequence, which consists
+ * of one or more patterns. In order to trigger, a pattern
+ * sequence must match the most recent X events (first pattern
+ * to most recent event, next pattern to next event, and so on).
+ */
+
+typedef struct PatSeq {
+ int numPats; /* Number of patterns in sequence
+ * (usually 1). */
+ char *command; /* Command to invoke when this
+ * pattern sequence matches (malloc-ed). */
+ struct PatSeq *nextSeqPtr;
+ /* Next in list of all pattern
+ * sequences that have the same
+ * initial pattern. NULL means
+ * end of list. */
+ Tcl_HashEntry *hPtr; /* Pointer to hash table entry for
+ * the initial pattern. This is the
+ * head of the list of which nextSeqPtr
+ * forms a part. */
+ ClientData object; /* Identifies object with which event is
+ * associated (e.g. window). */
+ struct PatSeq *nextObjPtr;
+ /* Next in list of all pattern
+ * sequences for the same object
+ * (NULL for end of list). Needed to
+ * implement Tk_DeleteAllBindings. */
+ Pattern pats[1]; /* Array of "numPats" patterns. Only
+ * one element is declared here but
+ * in actuality enough space will be
+ * allocated for "numPats" patterns.
+ * To match, pats[0] must match event
+ * n, pats[1] must match event n-1,
+ * etc. */
+} PatSeq;
+
+typedef struct {
+ char *name; /* Name of keysym. */
+ KeySym value; /* Numeric identifier for keysym. */
+ char *tiname; /* Terminfo name of keysym. */
+} KeySymInfo;
+static KeySymInfo keyArray[] = {
+#include "ks_names.h"
+ {(char *) NULL, 0}
+};
+static Tcl_HashTable keySymTable; /* Hashed form of above structure. */
+static Tcl_HashTable revKeySymTable; /* Ditto, reversed. */
+
+static int initialized = 0;
+
+/*
+ * This module also keeps a hash table mapping from event names
+ * to information about those events. The structure, an array
+ * to use to initialize the hash table, and the hash table are
+ * all defined below.
+ */
+
+typedef struct {
+ char *name; /* Name of event. */
+ int type; /* Event type for X, such as
+ * ButtonPress. */
+ int eventMask; /* Mask bits for this event type. */
+} EventInfo;
+
+static EventInfo eventArray[] = {
+ {"Expose", CK_EV_EXPOSE, CK_EV_EXPOSE},
+ {"FocusIn", CK_EV_FOCUSIN, CK_EV_FOCUSIN},
+ {"FocusOut", CK_EV_FOCUSOUT, CK_EV_FOCUSOUT},
+ {"Key", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
+ {"KeyPress", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
+ {"Control", CK_EV_KEYPRESS, CK_EV_KEYPRESS},
+ {"Destroy", CK_EV_DESTROY, CK_EV_DESTROY},
+ {"Map", CK_EV_MAP, CK_EV_MAP},
+ {"Unmap", CK_EV_UNMAP, CK_EV_UNMAP},
+ {"Button", CK_EV_MOUSE_DOWN, CK_EV_MOUSE_DOWN},
+ {"ButtonPress", CK_EV_MOUSE_DOWN, CK_EV_MOUSE_DOWN},
+ {"ButtonRelease", CK_EV_MOUSE_UP, CK_EV_MOUSE_UP},
+ {"BarCode", CK_EV_BARCODE, CK_EV_BARCODE},
+ {(char *) NULL, 0, 0}
+};
+static Tcl_HashTable eventTable;
+
+/*
+ * Prototypes for local procedures defined in this file:
+ */
+
+static void ExpandPercents _ANSI_ARGS_((CkWindow *winPtr,
+ char *before, CkEvent *eventPtr, KeySym keySym,
+ Tcl_DString *dsPtr));
+static PatSeq * FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
+ BindingTable *bindPtr, ClientData object,
+ char *eventString, int create));
+static char * GetField _ANSI_ARGS_((char *p, char *copy, int size));
+static PatSeq * MatchPatterns _ANSI_ARGS_((BindingTable *bindPtr,
+ PatSeq *psPtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_CreateBindingTable --
+ *
+ * Set up a new domain in which event bindings may be created.
+ *
+ * Results:
+ * The return value is a token for the new table, which must
+ * be passed to procedures like Ck_CreateBinding.
+ *
+ * Side effects:
+ * Memory is allocated for the new table.
+ *
+ *--------------------------------------------------------------
+ */
+
+Ck_BindingTable
+Ck_CreateBindingTable(interp)
+ Tcl_Interp *interp; /* Interpreter to associate with the binding
+ * table: commands are executed in this
+ * interpreter. */
+{
+ BindingTable *bindPtr;
+ int i;
+
+ /*
+ * If this is the first time a binding table has been created,
+ * initialize the global data structures.
+ */
+
+ if (!initialized) {
+ Tcl_HashEntry *hPtr;
+ EventInfo *eiPtr;
+ KeySymInfo *kPtr;
+ int dummy;
+
+ Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&revKeySymTable, TCL_ONE_WORD_KEYS);
+ for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
+ hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
+ Tcl_SetHashValue(hPtr, (char *) kPtr);
+ hPtr = Tcl_CreateHashEntry(&revKeySymTable, (char *) kPtr->value,
+ &dummy);
+ Tcl_SetHashValue(hPtr, (char *) kPtr);
+ }
+ Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
+ for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
+ hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
+ Tcl_SetHashValue(hPtr, eiPtr);
+ }
+ initialized = 1;
+ }
+
+ /*
+ * Create and initialize a new binding table.
+ */
+
+ bindPtr = (BindingTable *) ckalloc(sizeof (BindingTable));
+ for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
+ bindPtr->eventRing[i].type = -1;
+ }
+ bindPtr->curEvent = 0;
+ Tcl_InitHashTable(&bindPtr->patternTable,
+ sizeof(PatternTableKey)/sizeof(int));
+ Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
+ bindPtr->interp = interp;
+ return (Ck_BindingTable) bindPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DeleteBindingTable --
+ *
+ * Destroy a binding table and free up all its memory.
+ * The caller should not use bindingTable again after
+ * this procedure returns.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_DeleteBindingTable(bindingTable)
+ Ck_BindingTable bindingTable; /* Token for the binding table to
+ * destroy. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ PatSeq *psPtr, *nextPtr;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+
+ /*
+ * Find and delete all of the patterns associated with the binding
+ * table.
+ */
+
+ for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
+ psPtr != NULL; psPtr = nextPtr) {
+ nextPtr = psPtr->nextSeqPtr;
+ ckfree((char *) psPtr->command);
+ ckfree((char *) psPtr);
+ }
+ }
+
+ /*
+ * Clean up the rest of the information associated with the
+ * binding table.
+ */
+
+ Tcl_DeleteHashTable(&bindPtr->patternTable);
+ Tcl_DeleteHashTable(&bindPtr->objectTable);
+ ckfree((char *) bindPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_CreateBinding --
+ *
+ * Add a binding to a binding table, so that future calls to
+ * Ck_BindEvent may execute the command in the binding.
+ *
+ * Results:
+ * The return value is TCL_ERROR if an error occurred while setting
+ * up the binding. In this case, an error message will be
+ * left in interp->result. If all went well then the return
+ * value is TCL_OK.
+ *
+ * Side effects:
+ * The new binding may cause future calls to Ck_BindEvent to
+ * behave differently than they did previously.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_CreateBinding(interp, bindingTable, object, eventString, command, append)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Ck_BindingTable bindingTable; /* Table in which to create binding. */
+ ClientData object; /* Token for object with which binding
+ * is associated. */
+ char *eventString; /* String describing event sequence
+ * that triggers binding. */
+ char *command; /* Contains Tcl command to execute
+ * when binding triggers. */
+ int append; /* 0 means replace any existing
+ * binding for eventString; 1 means
+ * append to that binding. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ PatSeq *psPtr;
+
+ psPtr = FindSequence(interp, bindPtr, object, eventString, 1);
+ if (psPtr == NULL)
+ return TCL_ERROR;
+ if (append && (psPtr->command != NULL)) {
+ int length;
+ char *new;
+
+ length = strlen(psPtr->command) + strlen(command) + 2;
+ new = (char *) ckalloc((unsigned) length);
+ sprintf(new, "%s\n%s", psPtr->command, command);
+ ckfree((char *) psPtr->command);
+ psPtr->command = new;
+ } else {
+ if (psPtr->command != NULL) {
+ ckfree((char *) psPtr->command);
+ }
+ psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
+ strcpy(psPtr->command, command);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DeleteBinding --
+ *
+ * Remove an event binding from a binding table.
+ *
+ * Results:
+ * The result is a standard Tcl return value. If an error
+ * occurs then interp->result will contain an error message.
+ *
+ * Side effects:
+ * The binding given by object and eventString is removed
+ * from bindingTable.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_DeleteBinding(interp, bindingTable, object, eventString)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Ck_BindingTable bindingTable; /* Table in which to delete binding. */
+ ClientData object; /* Token for object with which binding
+ * is associated. */
+ char *eventString; /* String describing event sequence
+ * that triggers binding. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ register PatSeq *psPtr, *prevPtr;
+ Tcl_HashEntry *hPtr;
+
+ psPtr = FindSequence(interp, bindPtr, object, eventString, 0);
+ if (psPtr == NULL) {
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+ }
+
+ /*
+ * Unlink the binding from the list for its object, then from the
+ * list for its pattern.
+ */
+
+ hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
+ if (hPtr == NULL) {
+ panic("Ck_DeleteBinding couldn't find object table entry");
+ }
+ prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
+ if (prevPtr == psPtr) {
+ Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
+ } else {
+ for ( ; ; prevPtr = prevPtr->nextObjPtr) {
+ if (prevPtr == NULL) {
+ panic("Ck_DeleteBinding couldn't find on object list");
+ }
+ if (prevPtr->nextObjPtr == psPtr) {
+ prevPtr->nextObjPtr = psPtr->nextObjPtr;
+ break;
+ }
+ }
+ }
+ prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
+ if (prevPtr == psPtr) {
+ if (psPtr->nextSeqPtr == NULL) {
+ Tcl_DeleteHashEntry(psPtr->hPtr);
+ } else {
+ Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
+ }
+ } else {
+ for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
+ if (prevPtr == NULL) {
+ panic("Tk_DeleteBinding couldn't find on hash chain");
+ }
+ if (prevPtr->nextSeqPtr == psPtr) {
+ prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
+ break;
+ }
+ }
+ }
+ ckfree((char *) psPtr->command);
+ ckfree((char *) psPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetBinding --
+ *
+ * Return the command associated with a given event string.
+ *
+ * Results:
+ * The return value is a pointer to the command string
+ * associated with eventString for object in the domain
+ * given by bindingTable. If there is no binding for
+ * eventString, or if eventString is improperly formed,
+ * then NULL is returned and an error message is left in
+ * interp->result. The return value is semi-static: it
+ * will persist until the binding is changed or deleted.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+Ck_GetBinding(interp, bindingTable, object, eventString)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Ck_BindingTable bindingTable; /* Table in which to look for
+ * binding. */
+ ClientData object; /* Token for object with which binding
+ * is associated. */
+ char *eventString; /* String describing event sequence
+ * that triggers binding. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ PatSeq *psPtr;
+
+ psPtr = FindSequence(interp, bindPtr, object, eventString, 0);
+ if (psPtr == NULL) {
+ return NULL;
+ }
+ return psPtr->command;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetAllBindings --
+ *
+ * Return a list of event strings for all the bindings
+ * associated with a given object.
+ *
+ * Results:
+ * There is no return value. Interp->result is modified to
+ * hold a Tcl list with one entry for each binding associated
+ * with object in bindingTable. Each entry in the list
+ * contains the event string associated with one binding.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_GetAllBindings(interp, bindingTable, object)
+ Tcl_Interp *interp; /* Interpreter returning result or
+ * error. */
+ Ck_BindingTable bindingTable; /* Table in which to look for
+ * bindings. */
+ ClientData object; /* Token for object. */
+
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ register PatSeq *psPtr;
+ register Pattern *patPtr;
+ Tcl_HashEntry *hPtr;
+ Tcl_DString ds;
+ char c, buffer[10];
+ int patsLeft;
+ register EventInfo *eiPtr;
+
+ hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
+ if (hPtr == NULL) {
+ return;
+ }
+ Tcl_DStringInit(&ds);
+ for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
+ psPtr = psPtr->nextObjPtr) {
+ Tcl_DStringTrunc(&ds, 0);
+
+ /*
+ * For each binding, output information about each of the
+ * patterns in its sequence. The order of the patterns in
+ * the sequence is backwards from the order in which they
+ * must be output.
+ */
+
+ for (patsLeft = psPtr->numPats,
+ patPtr = &psPtr->pats[psPtr->numPats - 1];
+ patsLeft > 0; patsLeft--, patPtr--) {
+
+ /*
+ * Check for button presses.
+ */
+
+ if ((patPtr->eventType == CK_EV_MOUSE_DOWN)
+ && (patPtr->detail != 0)) {
+ sprintf(buffer, "<%d>", patPtr->detail);
+ Tcl_DStringAppend(&ds, buffer, -1);
+ continue;
+ }
+
+ /*
+ * Check for simple case of an ASCII character.
+ */
+
+ if ((patPtr->eventType == CK_EV_KEYPRESS)
+ && (patPtr->detail < 128)
+ && isprint((unsigned char) patPtr->detail)
+ && (patPtr->detail != '<')
+ && (patPtr->detail != ' ')) {
+ c = patPtr->detail;
+ Tcl_DStringAppend(&ds, &c, 1);
+ continue;
+ }
+
+ /*
+ * It's a more general event specification. First check
+ * event type, then keysym or button detail.
+ */
+
+ Tcl_DStringAppend(&ds, "<", 1);
+
+ for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
+ if (eiPtr->type == patPtr->eventType) {
+ if (patPtr->eventType == CK_EV_KEYPRESS &&
+ patPtr->detail == -1) {
+ Tcl_DStringAppend(&ds, "Control", -1);
+ goto endPat;
+ }
+ if (patPtr->eventType == CK_EV_KEYPRESS &&
+ patPtr->detail > 0 && patPtr->detail < 0x20) {
+ char *string;
+
+ string = CkKeysymToString((KeySym) patPtr->detail, 0);
+ if (string == NULL) {
+ sprintf(buffer, "Control-%c",
+ patPtr->detail + 0x40);
+ string = buffer;
+ }
+ Tcl_DStringAppend(&ds, string, -1);
+ goto endPat;
+ }
+ Tcl_DStringAppend(&ds, eiPtr->name, -1);
+ if (patPtr->detail != 0) {
+ Tcl_DStringAppend(&ds, "-", 1);
+ }
+ break;
+ }
+ }
+
+ if (patPtr->detail != 0) {
+ if (patPtr->eventType == CK_EV_KEYPRESS) {
+ char *string;
+
+ string = CkKeysymToString((KeySym) patPtr->detail, 0);
+ if (string != NULL) {
+ Tcl_DStringAppend(&ds, string, -1);
+ }
+ } else {
+ sprintf(buffer, "%d", patPtr->detail);
+ Tcl_DStringAppend(&ds, buffer, -1);
+ }
+ }
+endPat:
+ Tcl_DStringAppend(&ds, ">", 1);
+ }
+ Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
+ }
+ Tcl_DStringFree(&ds);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DeleteAllBindings --
+ *
+ * Remove all bindings associated with a given object in a
+ * given binding table.
+ *
+ * Results:
+ * All bindings associated with object are removed from
+ * bindingTable.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_DeleteAllBindings(bindingTable, object)
+ Ck_BindingTable bindingTable; /* Table in which to delete
+ * bindings. */
+ ClientData object; /* Token for object. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ PatSeq *psPtr, *prevPtr;
+ PatSeq *nextPtr;
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
+ if (hPtr == NULL) {
+ return;
+ }
+ for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
+ psPtr = nextPtr) {
+ nextPtr = psPtr->nextObjPtr;
+
+ /*
+ * Be sure to remove each binding from its hash chain in the
+ * pattern table. If this is the last pattern in the chain,
+ * then delete the hash entry too.
+ */
+
+ prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
+ if (prevPtr == psPtr) {
+ if (psPtr->nextSeqPtr == NULL) {
+ Tcl_DeleteHashEntry(psPtr->hPtr);
+ } else {
+ Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
+ }
+ } else {
+ for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
+ if (prevPtr == NULL) {
+ panic("Ck_DeleteAllBindings couldn't find on hash chain");
+ }
+ if (prevPtr->nextSeqPtr == psPtr) {
+ prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
+ break;
+ }
+ }
+ }
+ ckfree((char *) psPtr->command);
+ ckfree((char *) psPtr);
+ }
+ Tcl_DeleteHashEntry(hPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_BindEvent --
+ *
+ * This procedure is invoked to process an event. The
+ * event is added to those recorded for the binding table.
+ * Then each of the objects at *objectPtr is checked in
+ * order to see if it has a binding that matches the recent
+ * events. If so, that binding is invoked and the rest of
+ * objects are skipped.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command associated with the matching
+ * binding.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_BindEvent(bindingTable, eventPtr, winPtr, numObjects, objectPtr)
+ Ck_BindingTable bindingTable; /* Table in which to look for
+ * bindings. */
+ CkEvent *eventPtr; /* What actually happened. */
+ CkWindow *winPtr; /* Window where event occurred. */
+ int numObjects; /* Number of objects at *objectPtr. */
+ ClientData *objectPtr; /* Array of one or more objects
+ * to check for a matching binding. */
+{
+ BindingTable *bindPtr = (BindingTable *) bindingTable;
+ CkMainInfo *mainPtr;
+ CkEvent *ringPtr;
+ PatSeq *matchPtr;
+ PatternTableKey key;
+ Tcl_HashEntry *hPtr;
+ int detail, code;
+ Tcl_Interp *interp;
+ Tcl_DString scripts, savedResult;
+ char *p, *end;
+
+ /*
+ * Add the new event to the ring of saved events for the
+ * binding table.
+ */
+
+ bindPtr->curEvent++;
+ if (bindPtr->curEvent >= EVENT_BUFFER_SIZE)
+ bindPtr->curEvent = 0;
+ ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
+ memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof (CkEvent));
+ detail = 0;
+ bindPtr->detailRing[bindPtr->curEvent] = 0;
+ if (ringPtr->type == CK_EV_KEYPRESS)
+ detail = ringPtr->key.keycode;
+ else if (ringPtr->type == CK_EV_MOUSE_DOWN ||
+ ringPtr->type == CK_EV_MOUSE_UP)
+ detail = ringPtr->mouse.button;
+ bindPtr->detailRing[bindPtr->curEvent] = detail;
+
+ /*
+ * Loop over all the objects, finding the binding script for each
+ * one. Append all of the binding scripts, with %-sequences expanded,
+ * to "scripts", with null characters separating the scripts for
+ * each object.
+ */
+
+ Tcl_DStringInit(&scripts);
+ for ( ; numObjects > 0; numObjects--, objectPtr++) {
+
+ /*
+ * Match the new event against those recorded in the
+ * pattern table, saving the longest matching pattern.
+ * For events with details (key events) first
+ * look for a binding for the specific key or button.
+ * If none is found, then look for a binding for all
+ * control-keys (detail of -1, if the keycode is a control
+ * character), else look for a binding for all keys
+ * (detail of 0).
+ */
+
+ matchPtr = NULL;
+ key.object = *objectPtr;
+ key.type = ringPtr->type;
+ key.detail = detail;
+ hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
+ if (hPtr != NULL) {
+ matchPtr = MatchPatterns(bindPtr,
+ (PatSeq *) Tcl_GetHashValue(hPtr));
+ }
+ if (ringPtr->type == CK_EV_KEYPRESS && detail > 0 && detail < 0x20 &&
+ matchPtr == NULL) {
+ key.detail = -1;
+ hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
+ if (hPtr != NULL) {
+ matchPtr = MatchPatterns(bindPtr,
+ (PatSeq *) Tcl_GetHashValue(hPtr));
+ }
+ }
+ if (detail != 0 && matchPtr == NULL) {
+ key.detail = 0;
+ hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
+ if (hPtr != NULL) {
+ matchPtr = MatchPatterns(bindPtr,
+ (PatSeq *) Tcl_GetHashValue(hPtr));
+ }
+ }
+
+ if (matchPtr != NULL) {
+ ExpandPercents(winPtr, matchPtr->command, eventPtr,
+ (KeySym) detail, &scripts);
+ Tcl_DStringAppend(&scripts, "", 1);
+ }
+ }
+
+ /*
+ * Now go back through and evaluate the script for each object,
+ * in order, dealing with "break" and "continue" exceptions
+ * appropriately.
+ *
+ * There are two tricks here:
+ * 1. Bindings can be invoked from in the middle of Tcl commands,
+ * where interp->result is significant (for example, a widget
+ * might be deleted because of an error in creating it, so the
+ * result contains an error message that is eventually going to
+ * be returned by the creating command). To preserve the result,
+ * we save it in a dynamic string.
+ * 2. The binding's action can potentially delete the binding,
+ * so bindPtr may not point to anything valid once the action
+ * completes. Thus we have to save bindPtr->interp in a
+ * local variable in order to restore the result.
+ * 3. When the screen changes, must invoke a Tcl script to update
+ * Tcl level information such as tkPriv.
+ */
+
+ mainPtr = winPtr->mainPtr;
+ interp = bindPtr->interp;
+ Tcl_DStringInit(&savedResult);
+ Tcl_DStringGetResult(interp, &savedResult);
+ p = Tcl_DStringValue(&scripts);
+ end = p + Tcl_DStringLength(&scripts);
+ while (p != end) {
+ Tcl_AllowExceptions(interp);
+ code = Tcl_GlobalEval(interp, p);
+ if (code != TCL_OK) {
+ if (code == TCL_CONTINUE) {
+ /*
+ * Do nothing: just go on to the next script.
+ */
+ } else if (code == TCL_BREAK) {
+ break;
+ } else {
+ Tcl_AddErrorInfo(interp, "\n (command bound to event)");
+ Tk_BackgroundError(interp);
+ break;
+ }
+ }
+
+ /*
+ * Skip over the current script and its terminating null character.
+ */
+
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ }
+ Tcl_DStringResult(interp, &savedResult);
+ Tcl_DStringFree(&scripts);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindSequence --
+ *
+ * Find the entry in a binding table that corresponds to a
+ * particular pattern string, and return a pointer to that
+ * entry.
+ *
+ * Results:
+ * The return value is normally a pointer to the PatSeq
+ * in patternTable that corresponds to eventString. If an error
+ * was found while parsing eventString, or if "create" is 0 and
+ * no pattern sequence previously existed, then NULL is returned
+ * and interp->result contains a message describing the problem.
+ * If no pattern sequence previously existed for eventString, then
+ * a new one is created with a NULL command field. In a successful
+ * return, *maskPtr is filled in with a mask of the event types
+ * on which the pattern sequence depends.
+ *
+ * Side effects:
+ * A new pattern sequence may be created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static PatSeq *
+FindSequence(interp, bindPtr, object, eventString, create)
+ Tcl_Interp *interp; /* Interpreter to use for error
+ * reporting. */
+ BindingTable *bindPtr; /* Table to use for lookup. */
+ ClientData object; /* Token for object(s) with which binding
+ * is associated. */
+ char *eventString; /* String description of pattern to
+ * match on. See user documentation
+ * for details. */
+ int create; /* 0 means don't create the entry if
+ * it doesn't already exist. Non-zero
+ * means create. */
+
+{
+ Pattern pats[EVENT_BUFFER_SIZE];
+ int numPats, isCtrl;
+ register char *p;
+ register Pattern *patPtr;
+ register PatSeq *psPtr;
+ register Tcl_HashEntry *hPtr;
+#define FIELD_SIZE 48
+ char field[FIELD_SIZE];
+ int new;
+ size_t sequenceSize;
+ unsigned long eventMask;
+ PatternTableKey key;
+
+ /*
+ *-------------------------------------------------------------
+ * Step 1: parse the pattern string to produce an array
+ * of Patterns. The array is generated backwards, so
+ * that the lowest-indexed pattern corresponds to the last
+ * event that must occur.
+ *-------------------------------------------------------------
+ */
+
+ p = eventString;
+ eventMask = 0;
+ for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
+ numPats < EVENT_BUFFER_SIZE;
+ numPats++, patPtr--) {
+ patPtr->eventType = -1;
+ patPtr->detail = 0;
+ while (isspace((unsigned char) *p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+
+ /*
+ * Handle simple ASCII characters.
+ */
+
+ if (*p != '<') {
+ char string[2];
+
+ patPtr->eventType = CK_EV_KEYPRESS;
+ string[0] = *p;
+ string[1] = 0;
+ patPtr->detail = CkStringToKeysym(string);
+ if (patPtr->detail == NoSymbol) {
+ if (isprint((unsigned char) *p)) {
+ patPtr->detail = *p;
+ } else {
+ sprintf(interp->result,
+ "bad ASCII character 0x%x", (unsigned char) *p);
+ return NULL;
+ }
+ }
+ p++;
+ continue;
+ }
+
+ p++;
+
+ /*
+ * Abbrevated button press event.
+ */
+
+ if (isdigit((unsigned char) *p) && p[1] == '>') {
+ register EventInfo *eiPtr;
+
+ hPtr = Tcl_FindHashEntry(&eventTable, "ButtonPress");
+ eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
+ patPtr->eventType = eiPtr->type;
+ eventMask |= eiPtr->eventMask;
+ patPtr->detail = *p - '0';
+ p += 2;
+ continue;
+ }
+
+ /*
+ * A fancier event description. Must consist of
+ * 1. open angle bracket.
+ * 2. optional event name.
+ * 3. an option keysym name. Either this or
+ * item 2 *must* be present; if both are present
+ * then they are separated by spaces or dashes.
+ * 4. a close angle bracket.
+ */
+
+ isCtrl = 0;
+ p = GetField(p, field, FIELD_SIZE);
+ hPtr = Tcl_FindHashEntry(&eventTable, field);
+ if (hPtr != NULL) {
+ register EventInfo *eiPtr;
+
+ eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
+ patPtr->eventType = eiPtr->type;
+ eventMask |= eiPtr->eventMask;
+ isCtrl = strcmp(eiPtr->name, "Control") == 0;
+ if (isCtrl) {
+ patPtr->detail = -1;
+ }
+ while ((*p == '-') || isspace((unsigned char) *p)) {
+ p++;
+ }
+ p = GetField(p, field, FIELD_SIZE);
+ }
+ if (*field != '\0') {
+ if (patPtr->eventType == CK_EV_MOUSE_DOWN ||
+ patPtr->eventType == CK_EV_MOUSE_UP) {
+ if (!isdigit((unsigned char) *field) && field[1] != '\0') {
+ Tcl_AppendResult(interp, "bad mouse button \"",
+ field, "\"", (char *) NULL);
+ return NULL;
+ }
+ patPtr->detail = field[0] - '0';
+ goto closeAngle;
+ }
+
+ patPtr->detail = CkStringToKeysym(field);
+ if (patPtr->detail == NoSymbol) {
+badKeySym:
+ Tcl_AppendResult(interp, "bad event type or keysym \"",
+ field, "\"", (char *) NULL);
+ return NULL;
+ } else if (patPtr->eventType == CK_EV_KEYPRESS && isCtrl) {
+ patPtr->detail -= 0x40;
+ if (patPtr->detail >= 0x20)
+ patPtr->detail -= 0x20;
+ if (patPtr->detail < 0 || patPtr->detail >= 0x20)
+ goto badKeySym;
+ }
+ if (patPtr->eventType == -1) {
+ patPtr->eventType = CK_EV_KEYPRESS;
+ } else if (patPtr->eventType != CK_EV_KEYPRESS) {
+ Tcl_AppendResult(interp, "specified keysym \"", field,
+ "\" for non-key event", (char *) NULL);
+ return NULL;
+ }
+ } else if (patPtr->eventType == -1) {
+ interp->result = "no event type or keysym";
+ return NULL;
+ }
+ while ((*p == '-') || isspace((unsigned char) *p)) {
+ p++;
+ }
+closeAngle:
+ if (*p != '>') {
+ interp->result = "missing \">\" in binding";
+ return NULL;
+ }
+ p++;
+
+ }
+
+ /*
+ *-------------------------------------------------------------
+ * Step 2: find the sequence in the binding table if it exists,
+ * and add a new sequence to the table if it doesn't.
+ *-------------------------------------------------------------
+ */
+
+ if (numPats == 0) {
+ interp->result = "no events specified in binding";
+ return NULL;
+ }
+ patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
+ key.object = object;
+ key.type = patPtr->eventType;
+ key.detail = patPtr->detail;
+ hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
+ sequenceSize = numPats*sizeof(Pattern);
+ if (!new) {
+ for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
+ psPtr = psPtr->nextSeqPtr) {
+ if ((numPats == psPtr->numPats)
+ && (memcmp((char *) patPtr, (char *) psPtr->pats,
+ sequenceSize) == 0)) {
+ goto done;
+ }
+ }
+ }
+ if (!create) {
+ if (new) {
+ Tcl_DeleteHashEntry(hPtr);
+ }
+ Tcl_AppendResult(interp, "no binding exists for \"",
+ eventString, "\"", (char *) NULL);
+ return NULL;
+ }
+ psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
+ + (numPats-1)*sizeof(Pattern)));
+ psPtr->numPats = numPats;
+ psPtr->command = NULL;
+ psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
+ psPtr->hPtr = hPtr;
+ Tcl_SetHashValue(hPtr, psPtr);
+
+ /*
+ * Link the pattern into the list associated with the object.
+ */
+
+ psPtr->object = object;
+ hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
+ if (new) {
+ psPtr->nextObjPtr = NULL;
+ } else {
+ psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
+ }
+ Tcl_SetHashValue(hPtr, psPtr);
+
+ memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
+
+done:
+ return psPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetField --
+ *
+ * Used to parse pattern descriptions. Copies up to
+ * size characters from p to copy, stopping at end of
+ * string, space, "-", ">", or whenever size is
+ * exceeded.
+ *
+ * Results:
+ * The return value is a pointer to the character just
+ * after the last one copied (usually "-" or space or
+ * ">", but could be anything if size was exceeded).
+ * Also places NULL-terminated string (up to size
+ * character, including NULL), at copy.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+GetField(p, copy, size)
+ register char *p; /* Pointer to part of pattern. */
+ register char *copy; /* Place to copy field. */
+ int size; /* Maximum number of characters to
+ * copy. */
+{
+ while ((*p != '\0') && !isspace((unsigned char) *p) && (*p != '>')
+ && (*p != '-') && (size > 1)) {
+ *copy = *p;
+ p++;
+ copy++;
+ size--;
+ }
+ *copy = '\0';
+ return p;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MatchPatterns --
+ *
+ * Given a list of pattern sequences and a list of
+ * recent events, return a pattern sequence that matches
+ * the event list.
+ *
+ * Results:
+ * The return value is NULL if no pattern matches the
+ * recent events from bindPtr. If one or more patterns
+ * matches, then the longest (or most specific) matching
+ * pattern is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static PatSeq *
+MatchPatterns(bindPtr, psPtr)
+ BindingTable *bindPtr; /* Information about binding table, such
+ * as ring of recent events. */
+ register PatSeq *psPtr; /* List of pattern sequences. */
+{
+ register PatSeq *bestPtr = NULL;
+
+ /*
+ * Iterate over all the pattern sequences.
+ */
+
+ for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
+ register CkEvent *eventPtr;
+ register Pattern *patPtr;
+ CkWindow *winPtr;
+ int *detailPtr;
+ int patCount, ringCount;
+
+ /*
+ * Iterate over all the patterns in a sequence to be
+ * sure that they all match.
+ */
+
+ eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
+ detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
+ winPtr = eventPtr->any.winPtr;
+ patPtr = psPtr->pats;
+ patCount = psPtr->numPats;
+ ringCount = EVENT_BUFFER_SIZE;
+ while (patCount > 0) {
+ if (ringCount <= 0) {
+ goto nextSequence;
+ }
+ if (eventPtr->any.type != patPtr->eventType) {
+ if (patPtr->eventType == CK_EV_KEYPRESS)
+ goto nextEvent;
+ }
+ if (eventPtr->any.winPtr != winPtr)
+ goto nextSequence;
+
+ /*
+ * Note: it's important for the keysym check to go before
+ * the modifier check, so we can ignore unwanted modifier
+ * keys before choking on the modifier check.
+ */
+
+ if ((patPtr->detail != 0) && (patPtr->detail != -1)
+ && (patPtr->detail != *detailPtr))
+ goto nextSequence;
+
+ if ((patPtr->detail == -1) && (*detailPtr >= 0x20))
+ goto nextSequence;
+
+ patPtr++;
+ patCount--;
+ nextEvent:
+ if (eventPtr == bindPtr->eventRing) {
+ eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
+ detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
+ } else {
+ eventPtr--;
+ detailPtr--;
+ }
+ ringCount--;
+ }
+
+ /*
+ * This sequence matches. If we've already got another match,
+ * pick whichever is most specific.
+ */
+
+ if (bestPtr != NULL) {
+ register Pattern *patPtr2;
+ int i;
+
+ if (psPtr->numPats != bestPtr->numPats) {
+ if (bestPtr->numPats > psPtr->numPats) {
+ goto nextSequence;
+ } else {
+ goto newBest;
+ }
+ }
+ for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
+ i < psPtr->numPats; i++, patPtr++, patPtr2++) {
+ if (patPtr->detail != patPtr2->detail) {
+ if (patPtr->detail == -1 && patPtr2->detail == 0) {
+ goto newBest;
+ } else if (patPtr->detail == 0 || patPtr->detail == -1) {
+ goto nextSequence;
+ } else {
+ goto newBest;
+ }
+ }
+ }
+ goto nextSequence; /* Tie goes to newest pattern. */
+ }
+ newBest:
+ bestPtr = psPtr;
+
+ nextSequence: continue;
+ }
+ return bestPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ExpandPercents --
+ *
+ * Given a command and an event, produce a new command
+ * by replacing % constructs in the original command
+ * with information from the X event.
+ *
+ * Results:
+ * The new expanded command is appended to the dynamic string
+ * given by dsPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
+ CkWindow *winPtr; /* Window where event occurred: needed to
+ * get input context. */
+ register char *before; /* Command containing percent
+ * expressions to be replaced. */
+ register CkEvent *eventPtr; /* Event containing information
+ * to be used in % replacements. */
+ KeySym keySym; /* KeySym: only relevant for
+ * CK_EV_KEYPRESS events). */
+ Tcl_DString *dsPtr; /* Dynamic string in which to append
+ * new command. */
+{
+ int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
+ * list element. */
+ int number;
+#define NUM_SIZE 40
+ char *string, *string2;
+ char numStorage[NUM_SIZE+1];
+
+ while (1) {
+ /*
+ * Find everything up to the next % character and append it
+ * to the result string.
+ */
+
+ for (string = before; (*string != 0) && (*string != '%'); string++) {
+ /* Empty loop body. */
+ }
+ if (string != before) {
+ Tcl_DStringAppend(dsPtr, before, string-before);
+ before = string;
+ }
+ if (*before == 0) {
+ break;
+ }
+
+ /*
+ * There's a percent sequence here. Process it.
+ */
+
+ number = 0;
+ string = "??";
+ switch (before[1]) {
+ case 'k':
+ number = eventPtr->key.keycode;
+ goto doNumber;
+ case 'A':
+ if (eventPtr->type == CK_EV_KEYPRESS) {
+ int numChars = 0;
+
+ if ((eventPtr->key.keycode & ~0xff) == 0 &&
+ eventPtr->key.keycode != 0) {
+#if CK_USE_UTF
+ char c = eventPtr->key.keycode;
+ int numc = 0;
+
+ Tcl_ExternalToUtf(NULL, winPtr->mainPtr->isoEncoding,
+ &c, 1, 0, NULL,
+ numStorage + numChars,
+ sizeof (numStorage) - numChars,
+ NULL, &numc, NULL);
+ numChars += numc;
+#else
+ numStorage[numChars++] = eventPtr->key.keycode;
+#endif
+ }
+ numStorage[numChars] = '\0';
+ string = numStorage;
+ } else if (eventPtr->type == CK_EV_BARCODE) {
+ string = CkGetBarcodeData(winPtr->mainPtr);
+ if (string == NULL) {
+ numStorage[0] = '\0';
+ string = numStorage;
+ }
+ }
+ goto doString;
+ case 'K':
+ if (eventPtr->type == CK_EV_KEYPRESS) {
+ char *name;
+
+ name = CkKeysymToString(keySym, 1);
+ if (name != NULL) {
+ string = name;
+ }
+ }
+ goto doString;
+ case 'N':
+ number = (int) keySym;
+ goto doNumber;
+ case 'W':
+ if (Tcl_FindHashEntry(&winPtr->mainPtr->winTable,
+ (char *) eventPtr->any.winPtr) != NULL) {
+ string = eventPtr->any.winPtr->pathName;
+ } else {
+ string = "??";
+ }
+ goto doString;
+ case 'x':
+ if (eventPtr->type == CK_EV_MOUSE_UP ||
+ eventPtr->type == CK_EV_MOUSE_DOWN) {
+ number = eventPtr->mouse.x;
+ }
+ goto doNumber;
+ case 'y':
+ if (eventPtr->type == CK_EV_MOUSE_UP ||
+ eventPtr->type == CK_EV_MOUSE_DOWN) {
+ number = eventPtr->mouse.y;
+ }
+ goto doNumber;
+ case 'b':
+ if (eventPtr->type == CK_EV_MOUSE_UP ||
+ eventPtr->type == CK_EV_MOUSE_DOWN) {
+ number = eventPtr->mouse.button;
+ }
+ goto doNumber;
+ case 'X':
+ if (eventPtr->type == CK_EV_MOUSE_UP ||
+ eventPtr->type == CK_EV_MOUSE_DOWN) {
+ number = eventPtr->mouse.rootx;
+ }
+ goto doNumber;
+ case 'Y':
+ if (eventPtr->type == CK_EV_MOUSE_UP ||
+ eventPtr->type == CK_EV_MOUSE_DOWN) {
+ number = eventPtr->mouse.rooty;
+ }
+ goto doNumber;
+ default:
+ numStorage[0] = before[1];
+ numStorage[1] = '\0';
+ string = numStorage;
+ goto doString;
+ }
+
+ doNumber:
+ sprintf(numStorage, "%d", number);
+ string = numStorage;
+
+ doString:
+ spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
+ string2 = ckalloc(spaceNeeded + 1);
+ spaceNeeded = Tcl_ConvertElement(string, string2,
+ cvtFlags | TCL_DONT_USE_BRACES);
+ Tcl_DStringAppend(dsPtr, string2, -1);
+ ckfree((char *) string2);
+ before += 2;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkStringToKeysym --
+ *
+ * This procedure finds the keysym associated with a given keysym
+ * name.
+ *
+ * Results:
+ * The return value is the keysym that corresponds to name, or
+ * NoSymbol if there is no such keysym.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeySym
+CkStringToKeysym(name)
+ char *name; /* Name of a keysym. */
+{
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&keySymTable, name);
+ if (hPtr != NULL) {
+ return ((KeySymInfo *) Tcl_GetHashValue(hPtr))->value;
+ }
+ return NoSymbol;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkKeysymToString --
+ *
+ * This procedure finds the keysym name associated with a given
+ * keysym.
+ *
+ * Results:
+ * The return value is the keysym name that corresponds to name,
+ * or NoSymbol if there is no name.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+CkKeysymToString(keySym, printControl)
+ KeySym keySym;
+ int printControl;
+{
+ Tcl_HashEntry *hPtr;
+ static char buffer[64];
+
+ hPtr = Tcl_FindHashEntry(&revKeySymTable, (char *) keySym);
+ if (hPtr != NULL) {
+ return ((KeySymInfo *) Tcl_GetHashValue(hPtr))->name;
+ }
+ if (printControl && keySym >= 0x00 && keySym < 0x20) {
+ keySym += 0x40;
+ sprintf(buffer, "Control-%c", keySym);
+ return buffer;
+ }
+ return printControl ? "NoSymbol" : NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTermHasKey --
+ *
+ * This procedure checks if the terminal has a key for given keysym.
+ *
+ * Results:
+ * TCL_OK or TCL_ERROR, a string is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkTermHasKey(interp, name)
+ Tcl_Interp *interp; /* Interpreter used for result. */
+ char *name; /* Name of a keysym. */
+{
+#if !defined(__WIN32__) && !defined(DJGPP)
+ Tcl_HashEntry *hPtr;
+ char *tiname, *tivalue;
+ extern char *tigetstr();
+#endif
+ char buf[8];
+
+ if (strncmp("Control-", name, 8) == 0) {
+ if (sscanf(name, "Control-%7s", buf) != 1 || strlen(buf) != 1)
+ goto error;
+ if (buf[0] < 'A' && buf[0] > 'z')
+ goto error;
+ interp->result = "1";
+ return TCL_OK;
+ }
+#if defined(__WIN32__) || defined(DJGPP)
+ interp->result = "1";
+ return TCL_OK;
+#else
+ hPtr = Tcl_FindHashEntry(&keySymTable, name);
+ if (hPtr != NULL) {
+tifind:
+ tiname = ((KeySymInfo *) Tcl_GetHashValue(hPtr))->tiname;
+ if (tiname == NULL || ((tivalue = tigetstr(tiname)) != NULL &&
+ tivalue != (char *) -1))
+ interp->result = "1";
+ else
+ interp->result = "0";
+ return TCL_OK;
+ }
+ if (strlen(name) == 1) {
+ if (name[0] > 0x01 && name[0] < ' ') {
+ interp->result = "1";
+ return TCL_OK;
+ }
+ hPtr = Tcl_FindHashEntry(&revKeySymTable, (char *)
+ ((int) ((unsigned char) name[0])));
+ if (hPtr != NULL)
+ goto tifind;
+ }
+#endif
+error:
+ Tcl_AppendResult(interp, "invalid key symbol \"", name,
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkAllKeyNames --
+ *
+ * This procedure returns a list of all key names.
+ *
+ * Results:
+ * Always TCL_OK and list in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkAllKeyNames(interp)
+ Tcl_Interp *interp; /* Interpreter used for result. */
+{
+ KeySymInfo *kPtr;
+ int i;
+
+ for (i = 0x01; i < ' '; i++) {
+ unsigned code;
+ char buf[16];
+
+ code = i + 'A' - 1;
+ sprintf(buf, "Control-%c", tolower(code));
+ Tcl_AppendElement(interp, buf);
+ }
+ for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
+ Tcl_AppendElement(interp, kPtr->name);
+ }
+ return TCL_OK;
+}
--- /dev/null
+/*
+ * ckBorder.c --
+ *
+ * Manage borders by using alternate character set.
+ *
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * Variables used in this module.
+ */
+
+static Tcl_HashTable gCharTable; /* Maps gChar names to values. */
+static int initialized = 0; /* gCharTable initialized. */
+
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_GetGChar --
+ *
+ * Return curses ACS character given string.
+ *
+ *------------------------------------------------------------------------
+ */
+
+int
+Ck_GetGChar(interp, name, gchar)
+ Tcl_Interp *interp;
+ char *name;
+ int *gchar;
+{
+ Tcl_HashEntry *hPtr;
+
+ if (!initialized) {
+ int new;
+
+ Tcl_InitHashTable(&gCharTable, TCL_STRING_KEYS);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "ulcorner", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_ULCORNER);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "urcorner", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_URCORNER);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "llcorner", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_LLCORNER);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "lrcorner", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_LRCORNER);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "rtee", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_RTEE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "ltee", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_LTEE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "btee", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_BTEE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "ttee", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_TTEE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "hline", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_HLINE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "vline", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_VLINE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "plus", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_PLUS);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "s1", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_S1);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "s9", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_S9);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "diamond", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_DIAMOND);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "ckboard", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_CKBOARD);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "degree", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_DEGREE);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "plminus", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_PLMINUS);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "bullet", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_BULLET);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "larrow", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_LARROW);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "rarrow", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_RARROW);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "darrow", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_DARROW);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "uarrow", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_UARROW);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "board", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_BOARD);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "lantern", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_LANTERN);
+ hPtr = Tcl_CreateHashEntry(&gCharTable, "block", &new);
+ Tcl_SetHashValue(hPtr, (ClientData) ACS_BLOCK);
+
+ initialized = 1;
+ }
+
+ hPtr = Tcl_FindHashEntry(&gCharTable, name);
+ if (hPtr == NULL) {
+ if (interp != NULL)
+ Tcl_AppendResult(interp,
+ "bad gchar \"", name, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (gchar != NULL)
+ *gchar = (int) Tcl_GetHashValue(hPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_SetGChar --
+ *
+ * Modify ACS mapping.
+ *
+ *------------------------------------------------------------------------
+ */
+
+int
+Ck_SetGChar(interp, name, gchar)
+ Tcl_Interp *interp;
+ char *name;
+ int gchar;
+{
+ Tcl_HashEntry *hPtr;
+
+ if (!initialized)
+ Ck_GetGChar(interp, "ulcorner", NULL);
+ hPtr = Tcl_FindHashEntry(&gCharTable, name);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "bad gchar \"", name, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetHashValue(hPtr, (ClientData) gchar);
+ return TCL_OK;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_GetBorder --
+ *
+ * Create border from string.
+ *
+ *------------------------------------------------------------------------
+ */
+
+CkBorder *
+Ck_GetBorder(interp, string)
+ Tcl_Interp *interp;
+ char *string;
+{
+ int i, largc, bchar[8];
+ char **largv;
+ CkBorder *borderPtr;
+
+ if (Tcl_SplitList(interp, string, &largc, &largv) != TCL_OK)
+ return NULL;
+ if (largc != 1 && largc != 3 && largc != 6 && largc != 8) {
+ ckfree((char *) largv);
+ Tcl_AppendResult(interp, "illegal number of box characters",
+ (char *) NULL);
+ return NULL;
+ }
+ for (i = 0; i < sizeof (bchar) / sizeof (bchar[0]); i++)
+ bchar[i] = ' ';
+ for (i = 0; i < largc; i++) {
+ if (strlen(largv[i]) == 1)
+ bchar[i] = (unsigned char) largv[i][0];
+ else if (Ck_GetGChar(interp, largv[i], &bchar[i]) != TCL_OK) {
+ ckfree((char *) largv);
+ return NULL;
+ }
+ }
+ if (largc == 1) {
+ for (i = 1; i < sizeof (bchar) / sizeof (bchar[0]); i++)
+ bchar[i] = bchar[0];
+ } else if (largc == 3) {
+ bchar[3] = bchar[7] = bchar[2];
+ bchar[2] = bchar[4] = bchar[6] = bchar[0];
+ bchar[5] = bchar[1];
+ } else if (largc == 6) {
+ bchar[6] = bchar[5];
+ bchar[5] = bchar[1];
+ bchar[7] = bchar[3];
+ }
+ ckfree((char *) largv);
+ borderPtr = (CkBorder *) ckalloc(sizeof (CkBorder));
+ memset(borderPtr, 0, sizeof (CkBorder));
+ for (i = 0; i < 8; i++)
+ borderPtr->gchar[i] = bchar[i];
+ borderPtr->name = ckalloc(strlen(string) + 1);
+ strcpy(borderPtr->name, string);
+ return borderPtr;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_FreeBorder --
+ *
+ * Release memory related to border.
+ *
+ *------------------------------------------------------------------------
+ */
+
+void
+Ck_FreeBorder(borderPtr)
+ CkBorder *borderPtr;
+{
+ ckfree(borderPtr->name);
+ ckfree((char *) borderPtr);
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_NameOfBorder --
+ *
+ * Create border from string.
+ *
+ *------------------------------------------------------------------------
+ */
+
+char *
+Ck_NameOfBorder(borderPtr)
+ CkBorder *borderPtr;
+{
+ return borderPtr->name;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_DrawBorder --
+ *
+ * Given window, border and bounding box, draw border.
+ *
+ *------------------------------------------------------------------------
+ */
+
+void
+Ck_DrawBorder(winPtr, borderPtr, x, y, width, height)
+ CkWindow *winPtr;
+ CkBorder *borderPtr;
+ int x, y, width, height;
+{
+ int i, *gchar;
+ WINDOW *w;
+
+ if (winPtr->window == NULL)
+ return;
+ w = winPtr->window;
+ gchar = borderPtr->gchar;
+ if (width < 1 || height < 1)
+ return;
+ if (width == 1) {
+ for (i = y; i < height + y; i++)
+ mvwaddch(w, i, x, gchar[3]);
+ return;
+ }
+ if (height == 1) {
+ for (i = x; i < width + x; i++)
+ mvwaddch(w, y, i, gchar[1]);
+ return;
+ }
+ if (width == 2) {
+ mvwaddch(w, y, x, gchar[0]);
+ mvwaddch(w, y, x + 1, gchar[2]);
+ for (i = y + 1; i < height - 1 + y; i++)
+ mvwaddch(w, i, x, gchar[7]);
+ for (i = y + 1; i < height - 1 + y; i++)
+ mvwaddch(w, i, x + 1, gchar[3]);
+ mvwaddch(w, height - 1 + y, x, gchar[6]);
+ mvwaddch(w, height - 1 + y, x + 1, gchar[4]);
+ return;
+ }
+ if (height == 2) {
+ mvwaddch(w, y, x, gchar[0]);
+ mvwaddch(w, y + 1, x, gchar[6]);
+ for (i = x + 1; i < width - 1 + x; i++)
+ mvwaddch(w, y, i, gchar[1]);
+ for (i = x + 1; i < width - 1 + x; i++)
+ mvwaddch(w, y + 1, i, gchar[5]);
+ mvwaddch(w, y, width - 1 + x, gchar[2]);
+ mvwaddch(w, y + 1, width - 1 + x, gchar[4]);
+ return;
+ }
+ mvwaddch(w, y, x, gchar[0]);
+ for (i = x + 1; i < width - 1 + x; i++)
+ mvwaddch(w, y, i, gchar[1]);
+ mvwaddch(w, y, width - 1 + x, gchar[2]);
+ for (i = y + 1; i < height - 1 + y; i++)
+ mvwaddch(w, i, width - 1 + x, gchar[3]);
+ mvwaddch(w, height - 1 + y, width - 1 + x, gchar[4]);
+ for (i = x + 1; i < width - 1 + x; i++)
+ mvwaddch(w, height - 1 + y, i, gchar[5]);
+ mvwaddch(w, height - 1 + y, x, gchar[6]);
+ for (i = y + 1; i < height - 1 + y; i++)
+ mvwaddch(w, i, x, gchar[7]);
+}
--- /dev/null
+/*
+ * ckButton.c --
+ *
+ * This module implements a collection of button-like
+ * widgets for the Ck toolkit. The widgets implemented
+ * include labels, buttons, check buttons, and radio
+ * buttons.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the button. NULL
+ * means that the window has been destroyed. */
+ Tcl_Interp *interp; /* Interpreter associated with button. */
+ Tcl_Command widgetCmd; /* Token for button's widget command. */
+ int type; /* Type of widget: restricts operations
+ * that may be performed on widget. See
+ * below for possible values. */
+
+ /*
+ * Information about what's in the button.
+ */
+
+ char *text; /* Text to display in button (malloc'ed)
+ * or NULL. */
+ int textLength; /* # of characters in text. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, button displays the contents
+ * of this variable. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ Ck_Uid state; /* State of button for display purposes:
+ * normal, active, or disabled. */
+ int normalFg; /* Foreground color in normal mode. */
+ int normalBg; /* Background color in normal mode. */
+ int normalAttr; /* Attributes in normal mode. */
+ int activeFg; /* Foreground color in active mode. */
+ int activeBg; /* Ditto, background color. */
+ int activeAttr; /* Attributes in active mode. */
+ int disabledBg; /* Background color when disabled. */
+ int disabledFg; /* Foreground color when disabled. */
+ int disabledAttr; /* Attributes when disabled. */
+ int underline; /* Index of underlined character, < 0 if
+ * no underlining. */
+ int underlineFg; /* Foreground for underlined character. */
+ int underlineAttr; /* Attribute for underlined character. */
+ int selectFg; /* Foreground color for selector. */
+ int width, height; /* If > 0, these specify dimensions to request
+ * for window, in characters for text. */
+ Ck_Anchor anchor; /* Where text should be displayed
+ * inside button region. */
+
+ /*
+ * For check and radio buttons, the fields below are used
+ * to manage the variable indicating the button's state.
+ */
+
+ char *selVarName; /* Name of variable used to control selected
+ * state of button. Malloc'ed (if
+ * not NULL). */
+ char *onValue; /* Value to store in variable when
+ * this button is selected. Malloc'ed (if
+ * not NULL). */
+ char *offValue; /* Value to store in variable when this
+ * button isn't selected. Malloc'ed
+ * (if not NULL). Valid only for check
+ * buttons. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *command; /* Command to execute when button is
+ * invoked; valid for buttons only.
+ * If not NULL, it's malloc-ed. */
+ char *takeFocus; /* Tk 4.0 like. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Button;
+
+/*
+ * Possible "type" values for buttons. These are the kinds of
+ * widgets supported by this file. The ordering of the type
+ * numbers is significant: greater means more features and is
+ * used in the code.
+ */
+
+#define TYPE_LABEL 0
+#define TYPE_BUTTON 1
+#define TYPE_CHECK_BUTTON 2
+#define TYPE_RADIO_BUTTON 3
+
+/*
+ * Class names for buttons, indexed by one of the type values above.
+ */
+
+static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
+
+/*
+ * Flag bits for buttons:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * SELECTED: Non-zero means this button is selected,
+ * so special highlight should be drawn.
+ */
+
+#define REDRAW_PENDING 1
+#define SELECTED 2
+
+/*
+ * Mask values used to selectively enable entries in the
+ * configuration specs:
+ */
+
+#define LABEL_MASK CK_CONFIG_USER_BIT
+#define BUTTON_MASK CK_CONFIG_USER_BIT << 1
+#define CHECK_BUTTON_MASK CK_CONFIG_USER_BIT << 2
+#define RADIO_BUTTON_MASK CK_CONFIG_USER_BIT << 3
+#define ALL_MASK (LABEL_MASK | BUTTON_MASK \
+ | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
+
+static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
+ CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_BUTTON_ACTIVE_ATTR_COLOR,
+ Ck_Offset(Button, activeAttr),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_BUTTON_ACTIVE_ATTR_MONO,
+ Ck_Offset(Button, activeAttr),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_BUTTON_ACTIVE_BG_COLOR, Ck_Offset(Button, activeBg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_BUTTON_ACTIVE_BG_MONO, Ck_Offset(Button, activeBg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_BUTTON_ACTIVE_FG_COLOR, Ck_Offset(Button, activeFg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_BUTTON_ACTIVE_FG_MONO, Ck_Offset(Button, activeFg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_BUTTON_ANCHOR, Ck_Offset(Button, anchor), ALL_MASK},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_BUTTON_ATTR, Ck_Offset(Button, normalAttr),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_LABEL_ATTR, Ck_Offset(Button, normalAttr), LABEL_MASK},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_BUTTON_BG_COLOR, Ck_Offset(Button, normalBg),
+ ALL_MASK | CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_BUTTON_BG_MONO, Ck_Offset(Button, normalBg),
+ ALL_MASK | CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, ALL_MASK},
+ {CK_CONFIG_STRING, "-command", "command", "Command",
+ DEF_BUTTON_COMMAND, Ck_Offset(Button, command),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_ATTR, "-disabledattributes", "disabledAttributes",
+ "DisabledAttributes", DEF_BUTTON_DISABLED_ATTR,
+ Ck_Offset(Button, disabledAttr),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_BUTTON_DISABLED_BG_COLOR,
+ Ck_Offset(Button, disabledBg), BUTTON_MASK|CHECK_BUTTON_MASK
+ |RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_BUTTON_DISABLED_BG_MONO,
+ Ck_Offset(Button, disabledBg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
+ Ck_Offset(Button, disabledFg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
+ Ck_Offset(Button, disabledFg),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, ALL_MASK},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BUTTON_FG, Ck_Offset(Button, normalFg), ALL_MASK},
+ {CK_CONFIG_INT, "-height", "height", "Height",
+ DEF_BUTTON_HEIGHT, Ck_Offset(Button, height), ALL_MASK},
+ {CK_CONFIG_STRING, "-offvalue", "offValue", "Value",
+ DEF_BUTTON_OFF_VALUE, Ck_Offset(Button, offValue),
+ CHECK_BUTTON_MASK},
+ {CK_CONFIG_STRING, "-onvalue", "onValue", "Value",
+ DEF_BUTTON_ON_VALUE, Ck_Offset(Button, onValue),
+ CHECK_BUTTON_MASK},
+ {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
+ DEF_BUTTON_SELECT_COLOR, Ck_Offset(Button, selectFg),
+ CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
+ DEF_BUTTON_SELECT_MONO, Ck_Offset(Button, selectFg),
+ CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_UID, "-state", "state", "State",
+ DEF_BUTTON_STATE, Ck_Offset(Button, state),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_BUTTON_TAKE_FOCUS, Ck_Offset(Button, takeFocus),
+ BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_LABEL_TAKE_FOCUS, Ck_Offset(Button, takeFocus),
+ LABEL_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_BUTTON_TEXT, Ck_Offset(Button, text), ALL_MASK},
+ {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_BUTTON_TEXT_VARIABLE, Ck_Offset(Button, textVarName),
+ ALL_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_INT, "-underline", "underline", "Underline",
+ DEF_BUTTON_UNDERLINE, Ck_Offset(Button, underline),
+ ALL_MASK},
+ {CK_CONFIG_ATTR, "-underlineattributes", "underlineAttributes",
+ "UnderlineAttributes", DEF_BUTTON_UNDERLINE_ATTR,
+ Ck_Offset(Button, underlineAttr), ALL_MASK},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_BUTTON_UNDERLINE_FG_COLOR,
+ Ck_Offset(Button, underlineFg), ALL_MASK|CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_BUTTON_UNDERLINE_FG_MONO,
+ Ck_Offset(Button, underlineFg), ALL_MASK|CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_STRING, "-value", "value", "Value",
+ DEF_BUTTON_VALUE, Ck_Offset(Button, onValue),
+ RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-variable", "variable", "Variable",
+ DEF_RADIOBUTTON_VARIABLE, Ck_Offset(Button, selVarName),
+ RADIO_BUTTON_MASK},
+ {CK_CONFIG_STRING, "-variable", "variable", "Variable",
+ DEF_CHECKBUTTON_VARIABLE, Ck_Offset(Button, selVarName),
+ CHECK_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_INT, "-width", "width", "Width",
+ DEF_BUTTON_WIDTH, Ck_Offset(Button, width), ALL_MASK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * String to print out in error messages, identifying options for
+ * widget commands for different types of labels or buttons:
+ */
+
+static char *optionStrings[] = {
+ "cget or configure",
+ "activate, cget, configure, deactivate, flash, or invoke",
+ "activate, cget, configure, deactivate, deselect, flash, invoke, select, or toggle",
+ "activate, cget, configure, deactivate, deselect, flash, invoke, or select"
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ButtonCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
+static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
+ Button *butPtr, int argc, char **argv,
+ int flags));
+static void DestroyButton _ANSI_ARGS_((ClientData clientData));
+static void DisplayButton _ANSI_ARGS_((ClientData clientData));
+static int InvokeButton _ANSI_ARGS_((Button *butPtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ButtonCmd --
+ *
+ * This procedure is invoked to process the "button", "label",
+ * "radiobutton", and "checkbutton" Tcl commands. See the
+ * user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_ButtonCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Button *butPtr;
+ int type;
+ CkWindow *winPtr = (CkWindow *) clientData;
+ CkWindow *new;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ switch (argv[0][0]) {
+ case 'l':
+ type = TYPE_LABEL;
+ break;
+ case 'b':
+ type = TYPE_BUTTON;
+ break;
+ case 'c':
+ type = TYPE_CHECK_BUTTON;
+ break;
+ case 'r':
+ type = TYPE_RADIO_BUTTON;
+ break;
+ default:
+ sprintf(interp->result,
+ "unknown button-creation command \"%.50s\"", argv[0]);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Create the new window.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, winPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the data structure for the button.
+ */
+
+ butPtr = (Button *) ckalloc(sizeof (Button));
+ butPtr->winPtr = new;
+ butPtr->interp = interp;
+ butPtr->widgetCmd = Tcl_CreateCommand(interp,
+ butPtr->winPtr->pathName, ButtonWidgetCmd,
+ (ClientData) butPtr, ButtonCmdDeletedProc);
+ butPtr->type = type;
+ butPtr->text = NULL;
+ butPtr->textLength = 0;
+ butPtr->textVarName = NULL;
+ butPtr->state = ckNormalUid;
+ butPtr->normalFg = 0;
+ butPtr->normalBg = 0;
+ butPtr->normalAttr = 0;
+ butPtr->activeFg = 0;
+ butPtr->activeBg = 0;
+ butPtr->activeAttr = 0;
+ butPtr->disabledFg = 0;
+ butPtr->disabledBg = 0;
+ butPtr->disabledAttr = 0;
+ butPtr->underline = -1;
+ butPtr->underlineFg = 0;
+ butPtr->underlineAttr = 0;
+ butPtr->selectFg = 0;
+ butPtr->width = 0;
+ butPtr->height = 0;
+ butPtr->anchor = CK_ANCHOR_CENTER;
+ butPtr->selVarName = NULL;
+ butPtr->onValue = NULL;
+ butPtr->offValue = NULL;
+ butPtr->command = NULL;
+ butPtr->takeFocus = NULL;
+ butPtr->flags = 0;
+
+ Ck_SetClass(new, classNames[type]);
+ Ck_CreateEventHandler(butPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
+ ButtonEventProc, (ClientData) butPtr);
+ if (ConfigureButton(interp, butPtr, argc-2, argv+2,
+ configFlags[type]) != TCL_OK) {
+ Ck_DestroyWindow(butPtr->winPtr);
+ return TCL_ERROR;
+ }
+
+ interp->result = butPtr->winPtr->pathName;
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ButtonWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about button widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Button *butPtr = (Button *) clientData;
+ int result = TCL_OK;
+ int length;
+ char c;
+
+ if (argc < 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s option [arg arg ...]\"",
+ argv[0]);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) butPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
+ && (butPtr->type != TYPE_LABEL)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s activate\"",
+ argv[0]);
+ goto error;
+ }
+ if (butPtr->state != ckDisabledUid) {
+ butPtr->state = ckActiveUid;
+ goto redisplay;
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, butPtr->winPtr, configSpecs,
+ (char *) butPtr, argv[2], configFlags[butPtr->type]);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, butPtr->winPtr, configSpecs,
+ (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, butPtr->winPtr, configSpecs,
+ (char *) butPtr, argv[2],
+ configFlags[butPtr->type]);
+ } else {
+ result = ConfigureButton(interp, butPtr, argc-2, argv+2,
+ configFlags[butPtr->type] | CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
+ && (length > 2) && (butPtr->type != TYPE_LABEL)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s deactivate\"",
+ argv[0]);
+ goto error;
+ }
+ if (butPtr->state != ckDisabledUid) {
+ butPtr->state = ckNormalUid;
+ goto redisplay;
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
+ && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s deselect\"",
+ argv[0]);
+ goto error;
+ }
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
+ TCL_GLOBAL_ONLY);
+ } else if (butPtr->flags & SELECTED) {
+ Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
+ && (butPtr->type > TYPE_LABEL)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s invoke\"",
+ argv[0]);
+ goto error;
+ }
+ if (butPtr->state != ckDisabledUid) {
+ result = InvokeButton(butPtr);
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
+ && (butPtr->type >= TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s select\"",
+ argv[0]);
+ goto error;
+ }
+ Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
+ } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
+ && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
+ if (argc > 2) {
+ sprintf(interp->result,
+ "wrong # args: should be \"%.50s select\"",
+ argv[0]);
+ goto error;
+ }
+ if (butPtr->flags & SELECTED) {
+ Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
+ } else {
+ Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
+ }
+ } else {
+ sprintf(interp->result,
+ "bad option \"%.50s\": must be %s", argv[1],
+ optionStrings[butPtr->type]);
+ goto error;
+ }
+ Ck_Release((ClientData) butPtr);
+ return result;
+
+ redisplay:
+ if ((butPtr->winPtr->flags & CK_MAPPED) &&
+ !(butPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ Ck_Release((ClientData) butPtr);
+ return TCL_OK;
+
+ error:
+ Ck_Release((ClientData) butPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyButton --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a button at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyButton(clientData)
+ ClientData clientData; /* Info about entry widget. */
+{
+ Button *butPtr = (Button *) clientData;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Tk_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (butPtr->textVarName != NULL) {
+ Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonTextVarProc, (ClientData) butPtr);
+ }
+ if (butPtr->selVarName != NULL) {
+ Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData) butPtr);
+ }
+ Ck_FreeOptions(configSpecs, (char *) butPtr, configFlags[butPtr->type]);
+ ckfree((char *) butPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ButtonCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Button *butPtr = (Button *) clientData;
+ CkWindow *winPtr = butPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ butPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureButton --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a button widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for butPtr; old resources get freed, if there
+ * were any. The button is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureButton(interp, butPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Button *butPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ /*
+ * Eliminate any existing trace on variables monitored by the button.
+ */
+
+ if (butPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonTextVarProc, (ClientData) butPtr);
+ }
+ if (butPtr->selVarName != NULL) {
+ Tcl_UntraceVar(interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData) butPtr);
+ }
+
+ if (Ck_ConfigureWidget(interp, butPtr->winPtr, configSpecs,
+ argc, argv, (char *) butPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * A few options need special processing.
+ */
+
+ if (butPtr->state != ckActiveUid && butPtr->state != ckDisabledUid)
+ butPtr->state = ckNormalUid;
+
+ if (butPtr->type >= TYPE_CHECK_BUTTON) {
+ char *value;
+
+ if (butPtr->selVarName == NULL) {
+ butPtr->selVarName = (char *) ckalloc(
+ strlen((char *) butPtr->winPtr->nameUid) + 1);
+ strcpy(butPtr->selVarName, (char *) butPtr->winPtr->nameUid);
+ }
+ if (butPtr->onValue == NULL) {
+ butPtr->onValue = (char *) ckalloc(
+ strlen((char *) butPtr->winPtr->nameUid) + 1);
+ strcpy(butPtr->onValue, (char *) butPtr->winPtr->nameUid);
+ }
+
+ /*
+ * Select the button if the associated variable has the
+ * appropriate value, initialize the variable if it doesn't
+ * exist, then set a trace on the variable to monitor future
+ * changes to its value.
+ */
+
+ value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
+ butPtr->flags &= ~SELECTED;
+ if (value != NULL) {
+ if (strcmp(value, butPtr->onValue) == 0) {
+ butPtr->flags |= SELECTED;
+ }
+ } else {
+ Tcl_SetVar(interp, butPtr->selVarName,
+ (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
+ TCL_GLOBAL_ONLY);
+ }
+ Tcl_TraceVar(interp, butPtr->selVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonVarProc, (ClientData) butPtr);
+ }
+
+ /*
+ * If the button is to display the value of a variable, then set up
+ * a trace on the variable's value, create the variable if it doesn't
+ * exist, and fetch its current value.
+ */
+
+ if (butPtr->textVarName != NULL) {
+ char *value;
+
+ value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ Tcl_SetVar(interp, butPtr->textVarName,
+ butPtr->text != NULL ? butPtr->text : "",
+ TCL_GLOBAL_ONLY);
+ } else {
+ if (butPtr->text != NULL) {
+ ckfree(butPtr->text);
+ }
+ butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(butPtr->text, value);
+ }
+ Tcl_TraceVar(interp, butPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonTextVarProc, (ClientData) butPtr);
+ }
+
+ ComputeButtonGeometry(butPtr);
+
+ /*
+ * Lastly, arrange for the button to be redisplayed.
+ */
+
+ if ((butPtr->winPtr->flags & CK_MAPPED)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayButton --
+ *
+ * This procedure is invoked to display a button widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to display the button in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Button *butPtr = (Button *) clientData;
+ int x, y, fg, bg, attr, textWidth, charWidth;
+ CkWindow *winPtr = butPtr->winPtr;
+
+ butPtr->flags &= ~REDRAW_PENDING;
+ if ((butPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+
+ if (butPtr->state == ckDisabledUid) {
+ fg = butPtr->disabledFg;
+ bg = butPtr->disabledBg;
+ attr = butPtr->disabledAttr;
+ } else if (butPtr->state == ckActiveUid) {
+ fg = butPtr->activeFg;
+ bg = butPtr->activeBg;
+ attr = butPtr->activeAttr;
+ } else {
+ fg = butPtr->normalFg;
+ bg = butPtr->normalBg;
+ attr = butPtr->normalAttr;
+ }
+
+ /*
+ * Display text for button.
+ */
+
+ if (butPtr->text != NULL)
+ CkMeasureChars(winPtr->mainPtr, butPtr->text, butPtr->textLength,
+ 0, winPtr->width, 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ &textWidth, &charWidth);
+ else
+ textWidth = 0;
+
+ switch (butPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_W: case CK_ANCHOR_SW:
+ x = butPtr->type >= TYPE_CHECK_BUTTON ? 4 : 0;
+ break;
+ case CK_ANCHOR_N: case CK_ANCHOR_CENTER: case CK_ANCHOR_S:
+ x = (winPtr->width - textWidth) / 2;
+ if (butPtr->type >= TYPE_CHECK_BUTTON)
+ x += 2;
+ break;
+ default:
+ x = winPtr->width - textWidth;
+ if (butPtr->type >= TYPE_CHECK_BUTTON && x < 4)
+ x = 4;
+ break;
+ }
+ if (x + textWidth > winPtr->width)
+ textWidth = winPtr->width - x;
+
+ switch (butPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
+ y = 0;
+ break;
+ case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
+ y = (winPtr->height - 1) / 2;
+ break;
+ default:
+ y = winPtr->height - 1;
+ if (y < 0)
+ y = 0;
+ break;
+ }
+
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ Ck_ClearToBot(winPtr, 0, 0);
+ if (butPtr->text != NULL) {
+ CkDisplayChars(winPtr->mainPtr,
+ winPtr->window, butPtr->text, charWidth, x, y,
+ 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
+ if (butPtr->underline >= 0 && butPtr->state == ckNormalUid) {
+ Ck_SetWindowAttr(winPtr, butPtr->underlineFg, bg,
+ butPtr->underlineAttr);
+ CkUnderlineChars(winPtr->mainPtr,
+ winPtr->window, butPtr->text, charWidth, x, y,
+ 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ butPtr->underline, butPtr->underline);
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ }
+ }
+ if (butPtr->type >= TYPE_CHECK_BUTTON) {
+ int gchar;
+
+ mvwaddstr(winPtr->window, y, 0, butPtr->type == TYPE_CHECK_BUTTON ?
+ "[ ]" : "( )");
+ Ck_SetWindowAttr(winPtr, butPtr->selectFg, bg, attr);
+ if (!(butPtr->flags & SELECTED)) {
+ mvwaddch(winPtr->window, y, 1, (unsigned char) ' ');
+ } else if (butPtr->type == TYPE_CHECK_BUTTON) {
+ Ck_GetGChar(butPtr->interp, "diamond", &gchar);
+ mvwaddch(winPtr->window, y, 1, gchar);
+ } else if (butPtr->type == TYPE_RADIO_BUTTON) {
+ Ck_GetGChar(butPtr->interp, "bullet", &gchar);
+ mvwaddch(winPtr->window, y, 1, gchar);
+ }
+ }
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ wmove(winPtr->window, y, (butPtr->type >= TYPE_CHECK_BUTTON) ? 1 : x);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonEventProc --
+ *
+ * This procedure is invoked by the dispatcher for various
+ * events on buttons.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ButtonEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Button *butPtr = (Button *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE || eventPtr->type == CK_EV_MAP) {
+ if ((butPtr->winPtr != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (butPtr->winPtr != NULL) {
+ butPtr->winPtr = NULL;
+ Tcl_DeleteCommand(butPtr->interp,
+ Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
+ }
+ if (butPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
+ }
+ Ck_EventuallyFree((ClientData) butPtr, (Ck_FreeProc *) DestroyButton);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeButtonGeometry --
+ *
+ * After changes in a button's text or bitmap, this procedure
+ * recomputes the button's geometry and passes this information
+ * along to the geometry manager for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The button's window may change size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeButtonGeometry(butPtr)
+ Button *butPtr; /* Button whose geometry may have changed. */
+{
+ int width, height, dummy;
+ CkWindow *winPtr = butPtr->winPtr;
+
+ butPtr->textLength = butPtr->text == NULL ? 0 : strlen(butPtr->text);
+ if (butPtr->height > 0)
+ height = butPtr->height;
+ else
+ height = 1;
+ if (butPtr->width > 0)
+ width = butPtr->width;
+ else
+ CkMeasureChars(winPtr->mainPtr,
+ butPtr->text == NULL ? "" : butPtr->text,
+ butPtr->textLength, 0, 100000, 0,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ &width, &dummy);
+
+ /*
+ * When issuing the geometry request, add extra space for the selector,
+ * if any.
+ */
+
+ if (butPtr->type >= TYPE_CHECK_BUTTON)
+ width += 4;
+
+ Ck_GeometryRequest(butPtr->winPtr, width, height);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeButton --
+ *
+ * This procedure is called to carry out the actions associated
+ * with a button, such as invoking a Tcl command or setting a
+ * variable. This procedure is invoked, for example, when the
+ * button is invoked via the mouse.
+ *
+ * Results:
+ * A standard Tcl return value. Information is also left in
+ * interp->result.
+ *
+ * Side effects:
+ * Depends on the button and its associated command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InvokeButton(butPtr)
+ Button *butPtr; /* Information about button. */
+{
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ if (butPtr->flags & SELECTED) {
+ Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
+ TCL_GLOBAL_ONLY);
+ } else {
+ Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY);
+ }
+ } else if (butPtr->type == TYPE_RADIO_BUTTON) {
+ Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
+ TCL_GLOBAL_ONLY);
+ }
+ if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
+ return CkCopyAndGlobalEval(butPtr->interp, butPtr->command);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonVarProc --
+ *
+ * This procedure is invoked when someone changes the
+ * state variable associated with a radio button. Depending
+ * on the new value of the button's variable, the button
+ * may be selected or deselected.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The button may become selected or deselected.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+ButtonVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ Button *butPtr = (Button *) clientData;
+ char *value;
+
+ /*
+ * If the variable is being unset, then just re-establish the
+ * trace unless the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ butPtr->flags &= ~SELECTED;
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_TraceVar2(interp, name1, name2,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonVarProc, clientData);
+ }
+ goto redisplay;
+ }
+
+ /*
+ * Use the value of the variable to update the selected status of
+ * the button.
+ */
+
+ value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
+ if (strcmp(value, butPtr->onValue) == 0) {
+ if (butPtr->flags & SELECTED) {
+ return (char *) NULL;
+ }
+ butPtr->flags |= SELECTED;
+ } else if (butPtr->flags & SELECTED) {
+ butPtr->flags &= ~SELECTED;
+ } else {
+ return (char *) NULL;
+ }
+
+ redisplay:
+ if ((butPtr->winPtr != NULL) && (butPtr->winPtr->flags & CK_MAPPED)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ButtonTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in a button.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the button will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+ButtonTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ Button *butPtr = (Button *) clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar2(interp, name1, name2,
+ butPtr->text != NULL ? butPtr->text : "",
+ flags & TCL_GLOBAL_ONLY);
+ Tcl_TraceVar2(interp, name1, name2,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ButtonTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+
+ value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (butPtr->text != NULL) {
+ ckfree(butPtr->text);
+ }
+ butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(butPtr->text, value);
+ ComputeButtonGeometry(butPtr);
+
+ if ((butPtr->winPtr != NULL) && (butPtr->winPtr->flags & CK_MAPPED)
+ && !(butPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
+ butPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
--- /dev/null
+/*
+ * ckCmds.c --
+ *
+ * This file contains a collection of Ck-related Tcl commands
+ * that didn't fit in any particular file of the toolkit.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+static char * WaitVariableProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static void WaitVisibilityProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void WaitWindowProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_DestroyCmd --
+ *
+ * This procedure is invoked to process the "destroy" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_DestroyCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ winPtr = Ck_NameToWindow(interp, argv[i], mainPtr);
+ if (winPtr == NULL)
+ return TCL_ERROR;
+ Ck_DestroyWindow(winPtr);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_ExitCmd --
+ *
+ * This procedure is invoked to process the "exit" Tcl command.
+ * See the user documentation for details on what it does.
+ * Note: this command replaces the Tcl "exit" command in order
+ * to properly destroy all windows.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_ExitCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ extern CkMainInfo *ckMainInfo;
+ int index = 1, noclear = 0, value = 0;
+
+ if (argc > 3) {
+badArgs:
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?-noclear? ?returnCode?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc > 1 && strcmp(argv[1], "-noclear") == 0) {
+ index++;
+ noclear++;
+ }
+ if (argc > index &&
+ Tcl_GetInt(interp, argv[index], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (ckMainInfo != NULL) {
+ if (noclear) {
+ ckMainInfo->flags |= CK_NOCLR_ON_EXIT;
+ } else {
+ ckMainInfo->flags &= ~CK_NOCLR_ON_EXIT;
+ }
+ Ck_DestroyWindow((CkWindow *) clientData);
+ }
+ endwin(); /* just in case */
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Exit(value);
+#else
+ exit(value);
+#endif
+ /* NOTREACHED */
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_LowerCmd --
+ *
+ * This procedure is invoked to process the "lower" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_LowerCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *winPtr, *other;
+
+ if ((argc != 2) && (argc != 3)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window ?belowThis?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ winPtr = Ck_NameToWindow(interp, argv[1], mainPtr);
+ if (winPtr == NULL)
+ return TCL_ERROR;
+ if (argc == 2)
+ other = NULL;
+ else {
+ other = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (other == NULL)
+ return TCL_ERROR;
+ }
+ if (Ck_RestackWindow(winPtr, CK_BELOW, other) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't lower \"", argv[1], "\" below \"",
+ argv[2], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_RaiseCmd --
+ *
+ * This procedure is invoked to process the "raise" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_RaiseCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *winPtr, *other;
+
+ if ((argc != 2) && (argc != 3)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window ?aboveThis?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ winPtr = Ck_NameToWindow(interp, argv[1], mainPtr);
+ if (winPtr == NULL)
+ return TCL_ERROR;
+ if (argc == 2)
+ other = NULL;
+ else {
+ other = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (other == NULL)
+ return TCL_ERROR;
+ }
+ if (Ck_RestackWindow(winPtr, CK_ABOVE, other) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't raise \"", argv[1], "\" above \"",
+ argv[2], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_BellCmd --
+ *
+ * This procedure is invoked to process the "bell" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_BellCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ beep();
+ doupdate();
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_UpdateCmd --
+ *
+ * This procedure is invoked to process the "update" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_UpdateCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ int flags;
+
+ if (argc == 1)
+ flags = TK_DONT_WAIT;
+ else if (argc == 2) {
+ if (strncmp(argv[1], "screen", strlen(argv[1])) == 0) {
+ wrefresh(curscr);
+ Ck_EventuallyRefresh(mainPtr);
+ return TCL_OK;
+ }
+ if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
+ Tcl_AppendResult(interp, "bad argument \"", argv[1],
+ "\": must be idletasks or screen", (char *) NULL);
+ return TCL_ERROR;
+ }
+ flags = TK_IDLE_EVENTS;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?idletasks|screen?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Handle all pending events, and repeat over and over
+ * again until all pending events have been handled.
+ */
+
+ while (Tk_DoOneEvent(flags) != 0) {
+ /* Empty loop body */
+ }
+
+ /*
+ * Must clear the interpreter's result because event handlers could
+ * have executed commands.
+ */
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_CursesCmd --
+ *
+ * This procedure is invoked to process the "curses" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_CursesCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr = (CkWindow *) clientData;
+ CkMainInfo *mainPtr = winPtr->mainPtr;
+ int length;
+ char c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'b') && (strncmp(argv[1], "barcode", length) == 0)) {
+ return CkBarcodeCmd(clientData, interp, argc, argv);
+ } else if ((c == 'b') && (strncmp(argv[1], "baudrate", length) == 0)) {
+ char buf[32];
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ sprintf(buf, "%d", baudrate());
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ return TCL_OK;
+ } else if ((c == 'e') && (strncmp(argv[1], "encoding", length) == 0)) {
+ if (argc == 2)
+ return Ck_GetEncoding(interp);
+ else if (argc == 3)
+ return Ck_SetEncoding(interp, argv[2]);
+ else {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], " ?name?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'g') && (strncmp(argv[1], "gchar", length) == 0)) {
+ int gchar;
+
+ if (argc == 3) {
+ if (Ck_GetGChar(interp, argv[2], &gchar) != TCL_OK)
+ return TCL_ERROR;
+ sprintf(interp->result, "%d", gchar);
+ } else if (argc == 4) {
+ if (Tcl_GetInt(interp, argv[3], &gchar) != TCL_OK)
+ return TCL_ERROR;
+ if (Ck_SetGChar(interp, argv[2], gchar) != TCL_OK)
+ return TCL_ERROR;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], " charName ?value?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'h') && (strncmp(argv[1], "haskey", length) == 0)) {
+ if (argc > 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " haskey ?keySym?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 2)
+ return CkAllKeyNames(interp);
+ return CkTermHasKey(interp, argv[2]);
+ } else if ((c == 'p') && (strncmp(argv[1], "purgeinput", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " purgeinput\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ while (getch() != ERR) {
+ /* Empty loop body. */
+ }
+ return TCL_OK;
+ } else if ((c == 'r') && (strncmp(argv[1], "refreshdelay", length) == 0)) {
+ if (argc == 2) {
+ char buf[32];
+
+ sprintf(buf, "%d", mainPtr->refreshDelay);
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ return TCL_OK;
+ } else if (argc == 3) {
+ int delay;
+
+ if (Tcl_GetInt(interp, argv[2], &delay) != TCL_OK)
+ return TCL_ERROR;
+ mainPtr->refreshDelay = delay < 0 ? 0 : delay;
+ return TCL_OK;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], " ?milliseconds?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'r') && (strncmp(argv[1], "reversekludge", length)
+ == 0)) {
+ int onoff;
+
+ if (argc == 2) {
+ interp->result = (mainPtr->flags & CK_REVERSE_KLUDGE) ?
+ "1" : "0";
+ } else if (argc == 3) {
+ if (Tcl_GetBoolean(interp, argv[2], &onoff) != TCL_OK)
+ return TCL_ERROR;
+ mainPtr->flags |= CK_REVERSE_KLUDGE;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], " ?bool?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "screendump", length) == 0)) {
+ Tcl_DString buffer;
+ char *fileName;
+#ifdef HAVE_SCR_DUMP
+ int ret;
+#endif
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], " filename\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ fileName = Tcl_TildeSubst(interp, argv[2], &buffer);
+ if (fileName == NULL) {
+ Tcl_DStringFree(&buffer);
+ return TCL_ERROR;
+ }
+#ifdef HAVE_SCR_DUMP
+ ret = scr_dump(fileName);
+ Tcl_DStringFree(&buffer);
+ if (ret != OK) {
+ interp->result = "screen dump failed";
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+#else
+ interp->result = "screen dump not supported by this curses";
+ return TCL_ERROR;
+#endif
+ } else if ((c == 's') && (strncmp(argv[1], "suspend", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " ", argv[1], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+#if !defined(__WIN32__) && !defined(DJGPP)
+ curs_set(1);
+ endwin();
+#ifdef SIGTSTP
+ kill(getpid(), SIGTSTP);
+#else
+ kill(getpid(), SIGSTOP);
+#endif
+ Ck_EventuallyRefresh(winPtr);
+#endif
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be barcode, baudrate, encoding, gchar, haskey, ",
+ "purgeinput, refreshdelay, reversekludge, screendump or suspend",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_WinfoCmd --
+ *
+ * This procedure is invoked to process the "winfo" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_WinfoCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ int length;
+ char c, *argName;
+ CkWindow *winPtr;
+
+#define SETUP(name) \
+ if (argc != 3) {\
+ argName = name; \
+ goto wrongArgs; \
+ } \
+ winPtr = Ck_NameToWindow(interp, argv[2], mainPtr); \
+ if (winPtr == NULL) { \
+ return TCL_ERROR; \
+ }
+
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "children", length) == 0)
+ && (length >= 2)) {
+ SETUP("children");
+ for (winPtr = winPtr->childList; winPtr != NULL;
+ winPtr = winPtr->nextPtr) {
+ Tcl_AppendElement(interp, winPtr->pathName);
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "containing", length) == 0)
+ && (length >= 2)) {
+ int x, y;
+
+ argName = "containing";
+ if (argc != 4)
+ goto wrongArgs;
+ if (Tcl_GetInt(interp, argv[2], &x) != TCL_OK ||
+ Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ winPtr = Ck_GetWindowXY(mainPtr->mainPtr, &x, &y, 0);
+ if (winPtr != NULL) {
+ interp->result = winPtr->pathName;
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "depth", length) == 0)) {
+ SETUP("depth");
+ interp->result = (winPtr->mainPtr->flags & CK_HAS_COLOR) ? "3" : "1";
+ } else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0)) {
+ if (argc != 3) {
+ argName = "exists";
+ goto wrongArgs;
+ }
+ if (Ck_NameToWindow(interp, argv[2], mainPtr) == NULL) {
+ interp->result = "0";
+ } else {
+ interp->result = "1";
+ }
+ } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)) {
+ SETUP("geometry");
+ sprintf(interp->result, "%dx%d+%d+%d", winPtr->width,
+ winPtr->height, winPtr->x, winPtr->y);
+ } else if ((c == 'h') && (strncmp(argv[1], "height", length) == 0)) {
+ SETUP("height");
+ sprintf(interp->result, "%d", winPtr->height);
+ } else if ((c == 'i') && (strncmp(argv[1], "ismapped", length) == 0)
+ && (length >= 2)) {
+ SETUP("ismapped");
+ interp->result = (winPtr->flags & CK_MAPPED) ? "1" : "0";
+ } else if ((c == 'm') && (strncmp(argv[1], "manager", length) == 0)) {
+ SETUP("manager");
+ if (winPtr->geomMgrPtr != NULL)
+ interp->result = winPtr->geomMgrPtr->name;
+ } else if ((c == 'n') && (strncmp(argv[1], "name", length) == 0)) {
+ SETUP("name");
+ interp->result = (char *) winPtr->nameUid;
+ } else if ((c == 'c') && (strncmp(argv[1], "class", length) == 0)) {
+ SETUP("class");
+ interp->result = (char *) winPtr->classUid;
+ } else if ((c == 'p') && (strncmp(argv[1], "parent", length) == 0)) {
+ SETUP("parent");
+ if (winPtr->parentPtr != NULL)
+ interp->result = winPtr->parentPtr->pathName;
+ } else if ((c == 'r') && (strncmp(argv[1], "reqheight", length) == 0)
+ && (length >= 4)) {
+ SETUP("reqheight");
+ sprintf(interp->result, "%d", winPtr->reqHeight);
+ } else if ((c == 'r') && (strncmp(argv[1], "reqwidth", length) == 0)
+ && (length >= 4)) {
+ SETUP("reqwidth");
+ sprintf(interp->result, "%d", winPtr->reqWidth);
+ } else if ((c == 'r') && (strncmp(argv[1], "rootx", length) == 0)
+ && (length >= 4)) {
+ int x;
+
+ SETUP("rootx");
+ Ck_GetRootGeometry(winPtr, &x, NULL, NULL, NULL);
+ sprintf(interp->result, "%d", x);
+ } else if ((c == 'r') && (strncmp(argv[1], "rooty", length) == 0)
+ && (length >= 4)) {
+ int y;
+
+ SETUP("rooty");
+ Ck_GetRootGeometry(winPtr, NULL, &y, NULL, NULL);
+ sprintf(interp->result, "%d", y);
+ } else if ((c == 's') && (strncmp(argv[1], "screenheight", length) == 0)
+ && (length >= 7)) {
+ SETUP("screenheight");
+ sprintf(interp->result, "%d", winPtr->mainPtr->winPtr->height);
+ } else if ((c == 's') && (strncmp(argv[1], "screenwidth", length) == 0)
+ && (length >= 7)) {
+ SETUP("screenwidth");
+ sprintf(interp->result, "%d", winPtr->mainPtr->winPtr->width);
+ } else if ((c == 't') && (strncmp(argv[1], "toplevel", length) == 0)) {
+ SETUP("toplevel");
+ for (; winPtr != NULL; winPtr = winPtr->parentPtr) {
+ if (winPtr->flags & CK_TOPLEVEL) {
+ interp->result = winPtr->pathName;
+ break;
+ }
+ }
+ } else if ((c == 'w') && (strncmp(argv[1], "width", length) == 0)) {
+ SETUP("width");
+ sprintf(interp->result, "%d", winPtr->width);
+ } else if ((c == 'x') && (argv[1][1] == '\0')) {
+ SETUP("x");
+ sprintf(interp->result, "%d", winPtr->x);
+ } else if ((c == 'y') && (argv[1][1] == '\0')) {
+ SETUP("y");
+ sprintf(interp->result, "%d", winPtr->y);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be children, class, containing, depth ",
+ "exists, geometry, height, ",
+ "ismapped, manager, name, parent, ",
+ "reqheight, reqwidth, rootx, rooty, ",
+ "screenheight, screenwidth, ",
+ "toplevel, width, x, or y", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+
+ wrongArgs:
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " ", argName, " window\"", (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_BindCmd --
+ *
+ * This procedure is invoked to process the "bind" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_BindCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainWin = (CkWindow *) clientData;
+ CkWindow *winPtr;
+ ClientData object;
+
+ if ((argc < 2) || (argc > 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " window ?pattern? ?command?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argv[1][0] == '.') {
+ winPtr = (CkWindow *) Ck_NameToWindow(interp, argv[1], mainWin);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ object = (ClientData) winPtr->pathName;
+ } else {
+ winPtr = (CkWindow *) clientData;
+ object = (ClientData) Ck_GetUid(argv[1]);
+ }
+
+ if (argc == 4) {
+ int append = 0;
+
+ if (argv[3][0] == 0) {
+ return Ck_DeleteBinding(interp, winPtr->mainPtr->bindingTable,
+ object, argv[2]);
+ }
+ if (argv[3][0] == '+') {
+ argv[3]++;
+ append = 1;
+ }
+ if (Ck_CreateBinding(interp, winPtr->mainPtr->bindingTable,
+ object, argv[2], argv[3], append) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if (argc == 3) {
+ char *command;
+
+ command = Ck_GetBinding(interp, winPtr->mainPtr->bindingTable,
+ object, argv[2]);
+ if (command == NULL) {
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+ }
+ interp->result = command;
+ } else {
+ Ck_GetAllBindings(interp, winPtr->mainPtr->bindingTable, object);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBindEventProc --
+ *
+ * This procedure is invoked by Ck_HandleEvent for each event; it
+ * causes any appropriate bindings for that event to be invoked.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on what bindings have been established with the "bind"
+ * command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBindEventProc(winPtr, eventPtr)
+ CkWindow *winPtr; /* Pointer to info about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+#define MAX_OBJS 20
+ ClientData objects[MAX_OBJS], *objPtr;
+ static Ck_Uid allUid = NULL;
+ int i, count;
+ char *p;
+ Tcl_HashEntry *hPtr;
+ CkWindow *topLevPtr;
+
+ if ((winPtr->mainPtr == NULL) || (winPtr->mainPtr->bindingTable == NULL)) {
+ return;
+ }
+
+ objPtr = objects;
+ if (winPtr->numTags != 0) {
+ /*
+ * Make a copy of the tags for the window, replacing window names
+ * with pointers to the pathName from the appropriate window.
+ */
+
+ if (winPtr->numTags > MAX_OBJS) {
+ objPtr = (ClientData *) ckalloc(winPtr->numTags *
+ sizeof (ClientData));
+ }
+ for (i = 0; i < winPtr->numTags; i++) {
+ p = (char *) winPtr->tagPtr[i];
+ if (*p == '.') {
+ hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->nameTable, p);
+ if (hPtr != NULL) {
+ p = ((CkWindow *) Tcl_GetHashValue(hPtr))->pathName;
+ } else {
+ p = NULL;
+ }
+ }
+ objPtr[i] = (ClientData) p;
+ }
+ count = winPtr->numTags;
+ } else {
+ objPtr[0] = (ClientData) winPtr->pathName;
+ objPtr[1] = (ClientData) winPtr->classUid;
+ for (topLevPtr = winPtr; topLevPtr != NULL &&
+ !(topLevPtr->flags & CK_TOPLEVEL);
+ topLevPtr = topLevPtr->parentPtr) {
+ /* Empty loop body. */
+ }
+ if (winPtr != topLevPtr && topLevPtr != NULL) {
+ objPtr[2] = (ClientData) topLevPtr->pathName;
+ count = 4;
+ } else
+ count = 3;
+ if (allUid == NULL) {
+ allUid = Ck_GetUid("all");
+ }
+ objPtr[count - 1] = (ClientData) allUid;
+ }
+ Ck_BindEvent(winPtr->mainPtr->bindingTable, eventPtr, winPtr,
+ count, objPtr);
+ if (objPtr != objects) {
+ ckfree((char *) objPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_BindtagsCmd --
+ *
+ * This procedure is invoked to process the "bindtags" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_BindtagsCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainWin = (CkWindow *) clientData;
+ CkWindow *winPtr, *winPtr2;
+ int i, tagArgc;
+ char *p, **tagArgv;
+
+ if ((argc < 2) || (argc > 3)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " window ?tags?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ winPtr = (CkWindow *) Ck_NameToWindow(interp, argv[1], mainWin);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (argc == 2) {
+ if (winPtr->numTags == 0) {
+ Tcl_AppendElement(interp, winPtr->pathName);
+ Tcl_AppendElement(interp, winPtr->classUid);
+ for (winPtr2 = winPtr; winPtr2 != NULL &&
+ !(winPtr2->flags & CK_TOPLEVEL);
+ winPtr2 = winPtr2->parentPtr) {
+ /* Empty loop body. */
+ }
+ if (winPtr != winPtr2 && winPtr2 != NULL)
+ Tcl_AppendElement(interp, winPtr2->pathName);
+ Tcl_AppendElement(interp, "all");
+ } else {
+ for (i = 0; i < winPtr->numTags; i++) {
+ Tcl_AppendElement(interp, (char *) winPtr->tagPtr[i]);
+ }
+ }
+ return TCL_OK;
+ }
+ if (winPtr->tagPtr != NULL) {
+ CkFreeBindingTags(winPtr);
+ }
+ if (argv[2][0] == 0) {
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, argv[2], &tagArgc, &tagArgv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ winPtr->numTags = tagArgc;
+ winPtr->tagPtr = (ClientData *) ckalloc(tagArgc * sizeof(ClientData));
+ for (i = 0; i < tagArgc; i++) {
+ p = tagArgv[i];
+ if (p[0] == '.') {
+ char *copy;
+
+ /*
+ * Handle names starting with "." specially: store a malloc'ed
+ * string, rather than a Uid; at event time we'll look up the
+ * name in the window table and use the corresponding window,
+ * if there is one.
+ */
+
+ copy = (char *) ckalloc((unsigned) (strlen(p) + 1));
+ strcpy(copy, p);
+ winPtr->tagPtr[i] = (ClientData) copy;
+ } else {
+ winPtr->tagPtr[i] = (ClientData) Ck_GetUid(p);
+ }
+ }
+ ckfree((char *) tagArgv);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkFreeBindingTags --
+ *
+ * This procedure is called to free all of the binding tags
+ * associated with a window; typically it is only invoked where
+ * there are window-specific tags.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Any binding tags for winPtr are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkFreeBindingTags(winPtr)
+ CkWindow *winPtr; /* Window whose tags are to be released. */
+{
+ int i;
+ char *p;
+
+ for (i = 0; i < winPtr->numTags; i++) {
+ p = (char *) (winPtr->tagPtr[i]);
+ if (*p == '.') {
+ /*
+ * Names starting with "." are malloced rather than Uids, so
+ * they have to be freed.
+ */
+
+ ckfree(p);
+ }
+ }
+ ckfree((char *) winPtr->tagPtr);
+ winPtr->numTags = 0;
+ winPtr->tagPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_TkwaitCmd --
+ *
+ * This procedure is invoked to process the "tkwait" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_TkwaitCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ int c, done;
+ size_t length;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " variable|visible|window name\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'v') && (strncmp(argv[1], "variable", length) == 0)
+ && (length >= 2)) {
+ if (Tcl_TraceVar(interp, argv[2],
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ WaitVariableProc, (ClientData) &done) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ done = 0;
+ while (!done) {
+ Tk_DoOneEvent(0);
+ }
+ Tcl_UntraceVar(interp, argv[2],
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ WaitVariableProc, (ClientData) &done);
+ } else if ((c == 'v') && (strncmp(argv[1], "visibility", length) == 0)
+ && (length >= 2)) {
+ CkWindow *winPtr;
+
+ winPtr = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Ck_CreateEventHandler(winPtr,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ WaitVisibilityProc, (ClientData) &done);
+ done = 0;
+ while (!done) {
+ Tk_DoOneEvent(0);
+ }
+ Ck_DeleteEventHandler(winPtr,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ WaitVisibilityProc, (ClientData) &done);
+ } else if ((c == 'w') && (strncmp(argv[1], "window", length) == 0)) {
+ CkWindow *winPtr;
+
+ winPtr = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Ck_CreateEventHandler(winPtr, CK_EV_DESTROY,
+ WaitWindowProc, (ClientData) &done);
+ done = 0;
+ while (!done) {
+ Tk_DoOneEvent(0);
+ }
+ /*
+ * Note: there's no need to delete the event handler. It was
+ * deleted automatically when the window was destroyed.
+ */
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be variable, visibility, or window", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Clear out the interpreter's result, since it may have been set
+ * by event handlers.
+ */
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+static char *
+WaitVariableProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Pointer to integer to set to 1. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ int *donePtr = (int *) clientData;
+
+ *donePtr = 1;
+ return (char *) NULL;
+}
+
+static void
+WaitVisibilityProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to integer to set to 1. */
+ CkEvent *eventPtr; /* Information about event (not used). */
+{
+ int *donePtr = (int *) clientData;
+
+ *donePtr = 1;
+}
+
+static void
+WaitWindowProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to integer to set to 1. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ int *donePtr = (int *) clientData;
+
+ if (eventPtr->type == CK_EV_DESTROY) {
+ *donePtr = 1;
+ }
+}
--- /dev/null
+/*
+ * ckConfig.c --
+ *
+ * This file contains the Ck_ConfigureWidget procedure.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * Values for "flags" field of Ck_ConfigSpec structures. Be sure
+ * to coordinate these values with those defined in ck.h
+ * (CK_CONFIG_*). There must not be overlap!
+ *
+ * INIT - Non-zero means (char *) things have been
+ * converted to Ck_Uid's.
+ */
+
+#define INIT 0x20
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int DoConfig _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, Ck_ConfigSpec *specPtr,
+ Ck_Uid value, int valueIsUid, char *widgRec));
+static Ck_ConfigSpec * FindConfigSpec _ANSI_ARGS_ ((Tcl_Interp *interp,
+ Ck_ConfigSpec *specs, char *argvName,
+ int needFlags, int hateFlags));
+static char * FormatConfigInfo _ANSI_ARGS_ ((Tcl_Interp *interp,
+ CkWindow *winPtr, Ck_ConfigSpec *specPtr,
+ char *widgRec));
+static char * FormatConfigValue _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *tkwin, Ck_ConfigSpec *specPtr,
+ char *widgRec, char *buffer,
+ Tcl_FreeProc **freeProcPtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ConfigureWidget --
+ *
+ * Process command-line options to fill in fields of a
+ * widget record with resources and other parameters.
+ *
+ * Results:
+ * A standard Tcl return value. In case of an error,
+ * interp->result will hold an error message.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information
+ * from argc/argv. Old information in widgRec's fields
+ * gets recycled.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_ConfigureWidget(interp, winPtr, specs, argc, argv, widgRec, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Window containing widget. */
+ Ck_ConfigSpec *specs; /* Describes legal options. */
+ int argc; /* Number of elements in argv. */
+ char **argv; /* Command-line options. */
+ char *widgRec; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. Also,
+ * may have CK_CONFIG_ARGV_ONLY set. */
+{
+ Ck_ConfigSpec *specPtr;
+ Ck_Uid value; /* Value of option from database. */
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's
+ * not considered. */
+
+ needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
+ if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
+ hateFlags = CK_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = CK_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * Pass one: scan through all the option specs, replacing strings
+ * with Ck_Uids (if this hasn't been done already) and clearing
+ * the CK_CONFIG_OPTION_SPECIFIED flags.
+ */
+
+ for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
+ if (!(specPtr->specFlags & INIT) && (specPtr->argvName != NULL)) {
+ if (specPtr->dbName != NULL) {
+ specPtr->dbName = Ck_GetUid(specPtr->dbName);
+ }
+ if (specPtr->dbClass != NULL) {
+ specPtr->dbClass = Ck_GetUid(specPtr->dbClass);
+ }
+ if (specPtr->defValue != NULL) {
+ specPtr->defValue = Ck_GetUid(specPtr->defValue);
+ }
+ }
+ specPtr->specFlags = (specPtr->specFlags & ~CK_CONFIG_OPTION_SPECIFIED)
+ | INIT;
+ }
+
+ /*
+ * Pass two: scan through all of the arguments, processing those
+ * that match entries in the specs.
+ */
+
+ for ( ; argc > 0; argc -= 2, argv += 2) {
+ specPtr = FindConfigSpec(interp, specs, *argv, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Process the entry.
+ */
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "value for \"", *argv,
+ "\" missing", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (DoConfig(interp, winPtr, specPtr, argv[1], 0, widgRec) != TCL_OK) {
+ char msg[100];
+
+ sprintf(msg, "\n (processing \"%.40s\" option)",
+ specPtr->argvName);
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ specPtr->specFlags |= CK_CONFIG_OPTION_SPECIFIED;
+ }
+
+ /*
+ * Pass three: scan through all of the specs again; if no
+ * command-line argument matched a spec, then check for info
+ * in the option database. If there was nothing in the
+ * database, then use the default.
+ */
+
+ if (!(flags & CK_CONFIG_ARGV_ONLY)) {
+ for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
+ if ((specPtr->specFlags & CK_CONFIG_OPTION_SPECIFIED)
+ || (specPtr->argvName == NULL)
+ || (specPtr->type == CK_CONFIG_SYNONYM)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags)
+ || (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ value = NULL;
+ if (specPtr->dbName != NULL) {
+ value = Ck_GetOption(winPtr, specPtr->dbName,
+ specPtr->dbClass);
+ }
+ if (value != NULL) {
+ if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
+ TCL_OK) {
+ char msg[200];
+
+ sprintf(msg, "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "database entry for",
+ specPtr->dbName, winPtr->pathName);
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ } else {
+ value = specPtr->defValue;
+ if ((value != NULL) && !(specPtr->specFlags
+ & CK_CONFIG_DONT_SET_DEFAULT)) {
+ if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
+ TCL_OK) {
+ char msg[200];
+
+ sprintf(msg,
+ "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "default value for",
+ specPtr->dbName, winPtr->pathName);
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * FindConfigSpec --
+ *
+ * Search through a table of configuration specs, looking for
+ * one that matches a given argvName.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry, or NULL
+ * if nothing matched. In that case an error message is left
+ * in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Ck_ConfigSpec *
+FindConfigSpec(interp, specs, argvName, needFlags, hateFlags)
+ Tcl_Interp *interp; /* Used for reporting errors. */
+ Ck_ConfigSpec *specs; /* Pointer to table of configuration
+ * specifications for a widget. */
+ char *argvName; /* Name (suitable for use in a "config"
+ * command) identifying particular option. */
+ int needFlags; /* Flags that must be present in matching
+ * entry. */
+ int hateFlags; /* Flags that must NOT be present in
+ * matching entry. */
+{
+ Ck_ConfigSpec *specPtr;
+ char c; /* First character of current argument. */
+ Ck_ConfigSpec *matchPtr; /* Matching spec, or NULL. */
+ int length;
+
+ c = argvName[1];
+ length = strlen(argvName);
+ matchPtr = NULL;
+ for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
+ if (specPtr->argvName == NULL) {
+ continue;
+ }
+ if ((specPtr->argvName[1] != c)
+ || (strncmp(specPtr->argvName, argvName, length) != 0)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags)
+ || (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ if (specPtr->argvName[length] == 0) {
+ matchPtr = specPtr;
+ goto gotMatch;
+ }
+ if (matchPtr != NULL) {
+ Tcl_AppendResult(interp, "ambiguous option \"", argvName,
+ "\"", (char *) NULL);
+ return (Ck_ConfigSpec *) NULL;
+ }
+ matchPtr = specPtr;
+ }
+
+ if (matchPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown option \"", argvName,
+ "\"", (char *) NULL);
+ return (Ck_ConfigSpec *) NULL;
+ }
+
+ /*
+ * Found a matching entry. If it's a synonym, then find the
+ * entry that it's a synonym for.
+ */
+
+ gotMatch:
+ specPtr = matchPtr;
+ if (specPtr->type == CK_CONFIG_SYNONYM) {
+ for (specPtr = specs; ; specPtr++) {
+ if (specPtr->type == CK_CONFIG_END) {
+ Tcl_AppendResult(interp,
+ "couldn't find synonym for option \"",
+ argvName, "\"", (char *) NULL);
+ return (Ck_ConfigSpec *) NULL;
+ }
+ if ((specPtr->dbName == matchPtr->dbName)
+ && (specPtr->type != CK_CONFIG_SYNONYM)
+ && ((specPtr->specFlags & needFlags) == needFlags)
+ && !(specPtr->specFlags & hateFlags)) {
+ break;
+ }
+ }
+ }
+ return specPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DoConfig --
+ *
+ * This procedure applies a single configuration option
+ * to a widget record.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * WidgRec is modified as indicated by specPtr and value.
+ * The old value is recycled, if that is appropriate for
+ * the value type.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+DoConfig(interp, winPtr, specPtr, value, valueIsUid, widgRec)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Window containing widget. */
+ Ck_ConfigSpec *specPtr; /* Specifier to apply. */
+ char *value; /* Value to use to fill in widgRec. */
+ int valueIsUid; /* Non-zero means value is a Tk_Uid;
+ * zero means it's an ordinary string. */
+ char *widgRec; /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+{
+ char *ptr;
+ Ck_Uid uid;
+ int nullValue;
+
+ nullValue = 0;
+ if ((*value == 0) && (specPtr->specFlags & CK_CONFIG_NULL_OK)) {
+ nullValue = 1;
+ }
+
+ do {
+ ptr = widgRec + specPtr->offset;
+ switch (specPtr->type) {
+ case CK_CONFIG_BOOLEAN:
+ if (Tcl_GetBoolean(interp, value, (int *) ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CK_CONFIG_INT:
+ if (Tcl_GetInt(interp, value, (int *) ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CK_CONFIG_DOUBLE:
+ if (Tcl_GetDouble(interp, value, (double *) ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CK_CONFIG_STRING: {
+ char *old, *new;
+
+ if (nullValue) {
+ new = NULL;
+ } else {
+ new = (char *) ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(new, value);
+ }
+ old = *((char **) ptr);
+ if (old != NULL) {
+ ckfree(old);
+ }
+ *((char **) ptr) = new;
+ break;
+ }
+ case CK_CONFIG_UID:
+ if (nullValue) {
+ *((Ck_Uid *) ptr) = NULL;
+ } else {
+ uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
+ *((Ck_Uid *) ptr) = uid;
+ }
+ break;
+ case CK_CONFIG_COLOR: {
+ int color;
+
+ uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
+ if (Ck_GetColor(interp, (char *) value, &color) != TCL_OK)
+ return TCL_ERROR;
+ *((int *) ptr) = color;
+ break;
+ }
+ case CK_CONFIG_BORDER: {
+ CkBorder *new, *old;
+
+ if (nullValue) {
+ new = NULL;
+ } else {
+ uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
+ new = Ck_GetBorder(interp, uid);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ old = *((CkBorder **) ptr);
+ if (old != NULL) {
+ Ck_FreeBorder(old);
+ }
+ *((CkBorder **) ptr) = new;
+ break;
+ }
+ case CK_CONFIG_JUSTIFY:
+ uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
+ if (Ck_GetJustify(interp, uid, (Ck_Justify *) ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CK_CONFIG_ANCHOR:
+ uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
+ if (Ck_GetAnchor(interp, uid, (Ck_Anchor *) ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CK_CONFIG_COORD:
+ if (Ck_GetCoord(interp, winPtr, value, (int *) ptr) != TCL_OK)
+ return TCL_ERROR;
+ break;
+ case CK_CONFIG_ATTR:
+ if (Ck_GetAttr(interp, value, (int *) ptr) != TCL_OK)
+ return TCL_ERROR;
+ break;
+ case CK_CONFIG_WINDOW: {
+ CkWindow *winPtr2;
+
+ if (nullValue) {
+ winPtr2 = NULL;
+ } else {
+ winPtr2 = Ck_NameToWindow(interp, value, winPtr);
+ if (winPtr2 == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ *((CkWindow **) ptr) = winPtr2;
+ break;
+ }
+ case CK_CONFIG_CUSTOM:
+ if ((*specPtr->customPtr->parseProc)(
+ specPtr->customPtr->clientData, interp, winPtr,
+ value, widgRec, specPtr->offset) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ default: {
+ sprintf(interp->result, "bad config table: unknown type %d",
+ specPtr->type);
+ return TCL_ERROR;
+ }
+ }
+ specPtr++;
+ } while ((specPtr->argvName == NULL) && (specPtr->type != CK_CONFIG_END));
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ConfigureInfo --
+ *
+ * Return information about the configuration options
+ * for a window, and their current values.
+ *
+ * Results:
+ * Always returns TCL_OK. Interp->result will be modified
+ * hold a description of either a single configuration option
+ * available for "widgRec" via "specs", or all the configuration
+ * options available. In the "all" case, the result will
+ * available for "widgRec" via "specs". The result will
+ * be a list, each of whose entries describes one option.
+ * Each entry will itself be a list containing the option's
+ * name for use on command lines, database name, database
+ * class, default value, and current value (empty string
+ * if none). For options that are synonyms, the list will
+ * contain only two values: name and synonym name. If the
+ * "name" argument is non-NULL, then the only information
+ * returned is that for the named argument (i.e. the corresponding
+ * entry in the overall list is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_ConfigureInfo(interp, winPtr, specs, widgRec, argvName, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Window corresponding to widgRec. */
+ Ck_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ char *argvName; /* If non-NULL, indicates a single option
+ * whose info is to be returned. Otherwise
+ * info is returned for all options. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Ck_ConfigSpec *specPtr;
+ int needFlags, hateFlags;
+ char *list;
+ char *leader = "{";
+
+ needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
+ if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
+ hateFlags = CK_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = CK_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * If information is only wanted for a single configuration
+ * spec, then handle that one spec specially.
+ */
+
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ if (argvName != NULL) {
+ specPtr = FindConfigSpec(interp, specs, argvName, needFlags,
+ hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+ interp->result = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
+ interp->freeProc = (Tcl_FreeProc *) free;
+ return TCL_OK;
+ }
+
+ /*
+ * Loop through all the specs, creating a big list with all
+ * their information.
+ */
+
+ for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
+ if ((argvName != NULL) && (specPtr->argvName != argvName)) {
+ continue;
+ }
+ if (((specPtr->specFlags & needFlags) != needFlags)
+ || (specPtr->specFlags & hateFlags)) {
+ continue;
+ }
+ if (specPtr->argvName == NULL) {
+ continue;
+ }
+ list = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
+ Tcl_AppendResult(interp, leader, list, "}", (char *) NULL);
+ ckfree(list);
+ leader = " {";
+ }
+ return TCL_OK;
+}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_ConfigureValue --
+ *
+ * This procedure returns the current value of a configuration
+ * option for a widget.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code (TCL_OK or
+ * TCL_ERROR). Interp->result will be set to hold either the value
+ * of the option given by argvName (if TCL_OK is returned) or
+ * an error message (if TCL_ERROR is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_ConfigureValue(interp, winPtr, specs, widgRec, argvName, flags)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Window corresponding to widgRec. */
+ Ck_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ char *argvName; /* Gives the command-line name for the
+ * option whose value is to be returned. */
+ int flags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Ck_ConfigSpec *specPtr;
+ int needFlags, hateFlags;
+
+ needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
+ if (winPtr->mainPtr->flags & CK_HAS_COLOR)
+ hateFlags = CK_CONFIG_MONO_ONLY;
+ else
+ hateFlags = CK_CONFIG_COLOR_ONLY;
+ specPtr = FindConfigSpec(interp, specs, argvName, needFlags, hateFlags);
+ if (specPtr == NULL) {
+ return TCL_ERROR;
+ }
+ interp->result = FormatConfigValue(interp, winPtr, specPtr, widgRec,
+ interp->result, &interp->freeProc);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * FormatConfigInfo --
+ *
+ * Create a valid Tcl list holding the configuration information
+ * for a single configuration option.
+ *
+ * Results:
+ * A Tcl list, dynamically allocated. The caller is expected to
+ * arrange for this list to be freed eventually.
+ *
+ * Side effects:
+ * Memory is allocated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+FormatConfigInfo(interp, winPtr, specPtr, widgRec)
+ Tcl_Interp *interp; /* Interpreter to use for things
+ * like floating-point precision. */
+ CkWindow *winPtr; /* Window corresponding to widget. */
+ Ck_ConfigSpec *specPtr; /* Pointer to information describing
+ * option. */
+ char *widgRec; /* Pointer to record holding current
+ * values of info for widget. */
+{
+ char *argv[6], *result;
+ char buffer[200];
+ Tcl_FreeProc *freeProc = (Tcl_FreeProc *) NULL;
+
+ argv[0] = specPtr->argvName;
+ argv[1] = specPtr->dbName;
+ argv[2] = specPtr->dbClass;
+ argv[3] = specPtr->defValue;
+ if (specPtr->type == CK_CONFIG_SYNONYM) {
+ return Tcl_Merge(2, argv);
+ }
+ argv[4] = FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer,
+ &freeProc);
+ if (argv[1] == NULL) {
+ argv[1] = "";
+ }
+ if (argv[2] == NULL) {
+ argv[2] = "";
+ }
+ if (argv[3] == NULL) {
+ argv[3] = "";
+ }
+ if (argv[4] == NULL) {
+ argv[4] = "";
+ }
+ result = Tcl_Merge(5, argv);
+ if (freeProc != NULL) {
+ if (freeProc == (Tcl_FreeProc *) free) {
+ ckfree(argv[4]);
+ } else {
+ (*freeProc)(argv[4]);
+ }
+ }
+ return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FormatConfigValue --
+ *
+ * This procedure formats the current value of a configuration
+ * option.
+ *
+ * Results:
+ * The return value is the formatted value of the option given
+ * by specPtr and widgRec. If the value is static, so that it
+ * need not be freed, *freeProcPtr will be set to NULL; otherwise
+ * *freeProcPtr will be set to the address of a procedure to
+ * free the result, and the caller must invoke this procedure
+ * when it is finished with the result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer, freeProcPtr)
+ Tcl_Interp *interp; /* Interpreter for use in real conversions. */
+ CkWindow *winPtr; /* Window corresponding to widget. */
+ Ck_ConfigSpec *specPtr; /* Pointer to information describing option.
+ * Must not point to a synonym option. */
+ char *widgRec; /* Pointer to record holding current
+ * values of info for widget. */
+ char *buffer; /* Static buffer to use for small values.
+ * Must have at least 200 bytes of storage. */
+ Tcl_FreeProc **freeProcPtr; /* Pointer to word to fill in with address
+ * of procedure to free the result, or NULL
+ * if result is static. */
+{
+ char *ptr, *result;
+
+ *freeProcPtr = NULL;
+ ptr = widgRec + specPtr->offset;
+ result = "";
+ switch (specPtr->type) {
+ case CK_CONFIG_BOOLEAN:
+ if (*((int *) ptr) == 0) {
+ result = "0";
+ } else {
+ result = "1";
+ }
+ break;
+ case CK_CONFIG_INT:
+ case CK_CONFIG_COORD:
+ sprintf(buffer, "%d", *((int *) ptr));
+ result = buffer;
+ break;
+ case CK_CONFIG_DOUBLE:
+ Tcl_PrintDouble(interp, *((double *) ptr), buffer);
+ result = buffer;
+ break;
+ case CK_CONFIG_STRING:
+ result = (*(char **) ptr);
+ if (result == NULL)
+ result = "";
+ break;
+ case CK_CONFIG_UID: {
+ Ck_Uid uid = *((Ck_Uid *) ptr);
+ if (uid != NULL) {
+ result = uid;
+ }
+ break;
+ }
+ case CK_CONFIG_COLOR: {
+ result = Ck_NameOfColor(*((int *) ptr));
+ break;
+ }
+ case CK_CONFIG_BORDER: {
+ CkBorder *borderPtr = *((CkBorder **) ptr);
+ if (borderPtr != NULL) {
+ result = Ck_NameOfBorder(borderPtr);
+ }
+ break;
+ }
+ case CK_CONFIG_JUSTIFY:
+ result = Ck_NameOfJustify(*((Ck_Justify *) ptr));
+ break;
+ case CK_CONFIG_ANCHOR:
+ result = Ck_NameOfAnchor(*((Ck_Anchor *) ptr));
+ break;
+ case CK_CONFIG_ATTR:
+ result = Ck_NameOfAttr(*(int *) ptr);
+ *freeProcPtr = (Tcl_FreeProc *) free;
+ break;
+ case CK_CONFIG_WINDOW: {
+ CkWindow *winPtr2;
+
+ winPtr2 = *((CkWindow **) ptr);
+ if (winPtr2 != NULL) {
+ result = winPtr2->pathName;
+ }
+ break;
+ }
+ case CK_CONFIG_CUSTOM:
+ result = (*specPtr->customPtr->printProc)(
+ specPtr->customPtr->clientData, winPtr, widgRec,
+ specPtr->offset, freeProcPtr);
+ break;
+ default:
+ result = "?? unknown type ??";
+ }
+ return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_FreeOptions --
+ *
+ * Free up all resources associated with configuration options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Any resource in widgRec that is controlled by a configuration
+ * option is freed in the appropriate fashion.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_FreeOptions(specs, widgRec, needFlags)
+ Ck_ConfigSpec *specs; /* Describes legal options. */
+ char *widgRec; /* Record whose fields contain current
+ * values for options. */
+ int needFlags; /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Ck_ConfigSpec *specPtr;
+ char *ptr;
+
+ for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
+ if ((specPtr->specFlags & needFlags) != needFlags) {
+ continue;
+ }
+ ptr = widgRec + specPtr->offset;
+ switch (specPtr->type) {
+ case CK_CONFIG_STRING:
+ if (*((char **) ptr) != NULL) {
+ ckfree(*((char **) ptr));
+ *((char **) ptr) = NULL;
+ }
+ break;
+ case CK_CONFIG_BORDER:
+ if (*((CkBorder **) ptr) != NULL) {
+ Ck_FreeBorder(*((CkBorder **) ptr));
+ *((CkBorder **) ptr) = NULL;
+ }
+ break;
+ }
+ }
+}
--- /dev/null
+# ckConfig.sh --
+#
+# This shell script (for sh) is generated automatically by Ck's
+# configure script. It will create shell variables for most of
+# the configuration options discovered by the configure script.
+# This script is intended to be included by the configure scripts
+# for Ck extensions so that they don't have to figure this all
+# out for themselves.
+#
+# The information in this file is specific to a single platform.
+#
+# $Id: ckConfig.sh.in,v 1.1 2006-02-24 18:59:53 vitus Exp $
+
+# Ck's version number.
+CK_VERSION='@CK_VERSION@'
+CK_MAJOR_VERSION='@CK_MAJOR_VERSION@'
+CK_MINOR_VERSION='@CK_MINOR_VERSION@'
+
+# -D flags for use with the C compiler.
+CK_DEFS='@DEFS@'
+
+# The name of the Ck library (may be either a .a file or a shared library):
+CK_LIB_FILE=@CK_LIB_FILE@
+
+# Additional libraries to use when linking Ck.
+CK_LIBS='@CURSESLIBSW@ @DL_LIBS@ @LIBS@ @MATH_LIBS@'
+
+# Top-level directory in which Ck's files are installed.
+CK_PREFIX='@prefix@'
+
+# Top-level directory in which Tcl's platform-specific files (e.g.
+# executables) are installed.
+CK_EXEC_PREFIX='@exec_prefix@'
+
+# -I switch(es) where to find curses include files
+CK_CURSESINCLUDES='@CURSESINCLUDES@ @USE_NCURSES@'
+
+# Linker switch(es) to use when linking with curses
+CK_CURSESLIBSW='@CURSESLIBSW@'
--- /dev/null
+/*
+ * ckEntry.c --
+ *
+ * This module implements entry widgets for the
+ * toolkit. An entry displays a string and allows
+ * the string to be edited.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each entry
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the entry. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with entry. */
+ Tcl_Command widgetCmd; /* Token for entry's widget command. */
+#if CK_USE_UTF
+ int numBytes; /* Number of bytes in string. */
+#endif
+ int numChars; /* Number of non-NULL characters in
+ * string (may be 0). */
+ char *string; /* Pointer to storage for string;
+ * NULL-terminated; malloc-ed. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, entry's string tracks the
+ * contents of this variable and vice versa. */
+ Ck_Uid state; /* Normal or disabled. Entry is read-only
+ * when disabled. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int normalBg; /* Normal background color. */
+ int normalFg; /* Normal foreground color. */
+ int normalAttr; /* Normal video attributes. */
+ int selBg; /* Select background color. */
+ int selFg; /* Select foreground color. */
+ int selAttr; /* Select video attributes. */
+ Ck_Justify justify; /* Justification to use for text within
+ * window. */
+ int leftX; /* X position at which leftIndex is drawn
+ * (varies depending on justify). */
+ int leftIndex; /* Index of left-most character visible in
+ * window. */
+ int tabOrigin; /* Origin for tabs (left edge of string[0]). */
+ int insertPos; /* Index of character before which next
+ * typed character will be inserted. */
+ char *showChar; /* Value of -show option. If non-NULL, first
+ * character is used for displaying all
+ * characters in entry. Malloc'ed. */
+ char *displayString; /* If non-NULL, points to string with same
+ * length as string but whose characters
+ * are all equal to showChar. Malloc'ed. */
+ int prefWidth; /* Preferred width for window. */
+
+ /*
+ * Information about what's selected, if any.
+ */
+
+ int selectFirst; /* Index of first selected character (-1 means
+ * nothing selected. */
+ int selectLast; /* Index of last selected character (-1 means
+ * nothing selected. */
+ int selectAnchor; /* Fixed end of selection (i.e. "select to"
+ * operation will use this as one end of the
+ * selection). */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *scrollCmd; /* Command prefix for communicating with
+ * scrollbar(s). Malloc'ed. NULL means
+ * no command to issue. */
+ int flags; /* Miscellaneous flags; see below for
+ * definitions. */
+} Entry;
+
+/*
+ * Assigned bits of "flags" fields of Entry structures, and what those
+ * bits mean:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has
+ * already been queued to redisplay the entry.
+ * GOT_FOCUS: Non-zero means this window has the input
+ * focus.
+ * UPDATE_SCROLLBAR: Non-zero means scrollbar should be updated
+ * during next redisplay operation.
+ */
+
+#define REDRAW_PENDING 1
+#define GOT_FOCUS 2
+#define UPDATE_SCROLLBAR 4
+
+/*
+ * Information used for argv parsing.
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_ENTRY_ATTR, Ck_Offset(Entry, normalAttr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_ENTRY_BG_COLOR, Ck_Offset(Entry, normalBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_ENTRY_BG_MONO, Ck_Offset(Entry, normalBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_ENTRY_FG, Ck_Offset(Entry, normalFg), 0},
+ {CK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_ENTRY_JUSTIFY, Ck_Offset(Entry, justify), 0},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_ENTRY_SELECT_ATTR_COLOR,
+ Ck_Offset(Entry, selAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_ENTRY_SELECT_ATTR_MONO,
+ Ck_Offset(Entry, selAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_ENTRY_SELECT_BG_COLOR, Ck_Offset(Entry, selBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_ENTRY_SELECT_BG_MONO, Ck_Offset(Entry, selBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_ENTRY_SELECT_FG_COLOR, Ck_Offset(Entry, selFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_ENTRY_SELECT_FG_MONO, Ck_Offset(Entry, selFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_STRING, "-show", "show", "Show",
+ DEF_ENTRY_SHOW, Ck_Offset(Entry, showChar), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_UID, "-state", "state", "State",
+ DEF_ENTRY_STATE, Ck_Offset(Entry, state), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_ENTRY_TAKE_FOCUS, Ck_Offset(Entry, takeFocus), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_ENTRY_TEXT_VARIABLE, Ck_Offset(Entry, textVarName),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_INT, "-width", "width", "Width",
+ DEF_ENTRY_WIDTH, Ck_Offset(Entry, prefWidth), 0},
+ {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ DEF_ENTRY_SCROLL_COMMAND, Ck_Offset(Entry, scrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Flags for GetEntryIndex procedure:
+ */
+
+#define ZERO_OK 1
+#define LAST_PLUS_ONE_OK 2
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
+ Entry *entryPtr, int argc, char **argv,
+ int flags));
+static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
+ int count));
+static void DestroyEntry _ANSI_ARGS_((ClientData clientData));
+static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
+static void EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
+static void EntryEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
+ int gotFocus));
+static void EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
+static void EntryCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
+ char *value));
+static void EntrySelectTo _ANSI_ARGS_((
+ Entry *entryPtr, int index));
+static char * EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
+static void EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
+ double *firstPtr, double *lastPtr));
+static int EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ Entry *entryPtr, char *string, int *indexPtr));
+static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
+ char *string));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_EntryCmd --
+ *
+ * This procedure is invoked to process the "entry" Tcl
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_EntryCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ register Entry *entryPtr;
+ CkWindow *new;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the fields of the structure that won't be initialized
+ * by ConfigureEntry, or that ConfigureEntry requires to be
+ * initialized already (e.g. resource pointers).
+ */
+
+ entryPtr = (Entry *) ckalloc(sizeof (Entry));
+ entryPtr->winPtr = new;
+ entryPtr->interp = interp;
+ entryPtr->widgetCmd = Tcl_CreateCommand(interp,
+ entryPtr->winPtr->pathName, EntryWidgetCmd,
+ (ClientData) entryPtr, EntryCmdDeletedProc);
+#if CK_USE_UTF
+ entryPtr->numBytes = 0;
+#endif
+ entryPtr->numChars = 0;
+ entryPtr->string = (char *) ckalloc(1);
+ entryPtr->string[0] = '\0';
+ entryPtr->textVarName = NULL;
+ entryPtr->state = ckNormalUid;
+ entryPtr->normalBg = 0;
+ entryPtr->normalFg = 0;
+ entryPtr->normalAttr = 0;
+ entryPtr->selBg = 0;
+ entryPtr->selFg = 0;
+ entryPtr->selAttr = 0;
+ entryPtr->justify = CK_JUSTIFY_LEFT;
+ entryPtr->prefWidth = 0;
+ entryPtr->leftIndex = 0;
+ entryPtr->tabOrigin = 0;
+ entryPtr->insertPos = 0;
+ entryPtr->showChar = NULL;
+ entryPtr->displayString = NULL;
+ entryPtr->prefWidth = 1;
+ entryPtr->selectFirst = -1;
+ entryPtr->selectLast = -1;
+ entryPtr->selectAnchor = 0;
+ entryPtr->takeFocus = NULL;
+ entryPtr->scrollCmd = NULL;
+ entryPtr->flags = 0;
+
+ Ck_SetClass(entryPtr->winPtr, "Entry");
+ Ck_CreateEventHandler(entryPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
+ EntryEventProc, (ClientData) entryPtr);
+ if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ interp->result = entryPtr->winPtr->pathName;
+ return TCL_OK;
+
+ error:
+ Ck_DestroyWindow(entryPtr->winPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * EntryWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+EntryWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about entry widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Entry *entryPtr = (Entry *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) entryPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, entryPtr->winPtr, configSpecs,
+ (char *) entryPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, entryPtr->winPtr, configSpecs,
+ (char *) entryPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, entryPtr->winPtr, configSpecs,
+ (char *) entryPtr, argv[2], 0);
+ } else {
+ result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
+ int first, last;
+
+ if ((argc < 3) || (argc > 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " delete firstIndex ?lastIndex?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
+ goto error;
+ }
+ if (argc == 3) {
+ last = first+1;
+ } else {
+ if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
+ goto error;
+ }
+ }
+ if ((last >= first) && (entryPtr->state == ckNormalUid)) {
+ DeleteChars(entryPtr, first, last-first);
+ }
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get\"", (char *) NULL);
+ goto error;
+ }
+ interp->result = entryPtr->string;
+ } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " icursor pos\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
+ != TCL_OK) {
+ goto error;
+ }
+ EventuallyRedraw(entryPtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " index string\"", (char *) NULL);
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
+ goto error;
+ }
+ sprintf(interp->result, "%d", index);
+ } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " insert index text\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
+ goto error;
+ }
+ if (entryPtr->state == ckNormalUid) {
+ InsertChars(entryPtr, index, argv[3]);
+ }
+ } else if ((c == 's') && (length >= 2)
+ && (strncmp(argv[1], "selection", length) == 0)) {
+ int index, index2;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " select option ?index?\"", (char *) NULL);
+ goto error;
+ }
+ length = strlen(argv[2]);
+ c = argv[2][0];
+ if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection clear\"", (char *) NULL);
+ goto error;
+ }
+ if (entryPtr->selectFirst != -1) {
+ entryPtr->selectFirst = entryPtr->selectLast = -1;
+ EventuallyRedraw(entryPtr);
+ }
+ goto done;
+ } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection present\"", (char *) NULL);
+ goto error;
+ }
+ if (entryPtr->selectFirst == -1) {
+ interp->result = "0";
+ } else {
+ interp->result = "1";
+ }
+ goto done;
+ }
+ if (argc >= 4) {
+ if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
+ goto error;
+ }
+ }
+ if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection adjust index\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (entryPtr->selectFirst >= 0) {
+ int half1, half2;
+
+ half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
+ half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
+ if (index < half1) {
+ entryPtr->selectAnchor = entryPtr->selectLast;
+ } else if (index > half2) {
+ entryPtr->selectAnchor = entryPtr->selectFirst;
+ } else {
+ /*
+ * We're at about the halfway point in the selection;
+ * just keep the existing anchor.
+ */
+ }
+ }
+ EntrySelectTo(entryPtr, index);
+ } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection from index\"",
+ (char *) NULL);
+ goto error;
+ }
+ entryPtr->selectAnchor = index;
+ } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection range start end\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
+ goto error;
+ }
+ if (index >= index2) {
+ entryPtr->selectFirst = entryPtr->selectLast = -1;
+ } else {
+ entryPtr->selectFirst = index;
+ entryPtr->selectLast = index2;
+ }
+ EventuallyRedraw(entryPtr);
+ } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection to index\"",
+ (char *) NULL);
+ goto error;
+ }
+ EntrySelectTo(entryPtr, index);
+ } else {
+ Tcl_AppendResult(interp, "bad selection option \"", argv[2],
+ "\": must be adjust, clear, from, present, range, or to",
+ (char *) NULL);
+ goto error;
+ }
+ } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
+ int index, type, count, charsPerPage;
+ double fraction, first, last;
+
+ if (argc == 2) {
+ EntryVisibleRange(entryPtr, &first, &last);
+ sprintf(interp->result, "%g %g", first, last);
+ goto done;
+ } else if (argc == 3) {
+ if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ index = entryPtr->leftIndex;
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ goto error;
+ case CK_SCROLL_MOVETO:
+ index = (int) (fraction * entryPtr->numChars);
+ break;
+ case CK_SCROLL_PAGES:
+ charsPerPage = entryPtr->winPtr->width - 2;
+ if (charsPerPage < 1)
+ charsPerPage = 1;
+ index += charsPerPage*count;
+ break;
+ case CK_SCROLL_UNITS:
+ index += count;
+ break;
+ }
+ }
+ if (index >= entryPtr->numChars) {
+ index = entryPtr->numChars-1;
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ entryPtr->leftIndex = index;
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget, configure, delete, get, ",
+ "icursor, index, insert, selection, or xview",
+ (char *) NULL);
+ goto error;
+ }
+done:
+ Ck_Release((ClientData) entryPtr);
+ return result;
+
+error:
+ Ck_Release((ClientData) entryPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyEntry --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of an entry at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the entry is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyEntry(clientData)
+ ClientData clientData; /* Info about entry widget. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ ckfree(entryPtr->string);
+ if (entryPtr->textVarName != NULL) {
+ Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ EntryTextVarProc, (ClientData) entryPtr);
+ }
+ if (entryPtr->displayString != NULL) {
+ ckfree(entryPtr->displayString);
+ }
+ Ck_FreeOptions(configSpecs, (char *) entryPtr, 0);
+ ckfree((char *) entryPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntryCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+ CkWindow *winPtr = entryPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ entryPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureEntry --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or reconfigure)
+ * an entry widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as colors, border width,
+ * etc. get set for entryPtr; old resources get freed,
+ * if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureEntry(interp, entryPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Entry *entryPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ /*
+ * Eliminate any existing trace on a variable monitored by the entry.
+ */
+
+ if (entryPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, entryPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ EntryTextVarProc, (ClientData) entryPtr);
+ }
+
+ if (Ck_ConfigureWidget(interp, entryPtr->winPtr, configSpecs,
+ argc, argv, (char *) entryPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * If the entry is tied to the value of a variable, then set up
+ * a trace on the variable's value, create the variable if it doesn't
+ * exist, and set the entry's value from the variable's value.
+ */
+
+ if (entryPtr->textVarName != NULL) {
+ char *value;
+
+ value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
+ TCL_GLOBAL_ONLY);
+ } else {
+ EntrySetValue(entryPtr, value);
+ }
+ Tcl_TraceVar(interp, entryPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ EntryTextVarProc, (ClientData) entryPtr);
+ }
+
+ /*
+ * A few other options also need special processing, such as parsing
+ * the geometry and setting the colors.
+ */
+
+ if ((entryPtr->state != ckNormalUid)
+ && (entryPtr->state != ckDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
+ "\": must be normal or disabled", (char *) NULL);
+ entryPtr->state = ckNormalUid;
+ return TCL_ERROR;
+ }
+
+ /*
+ * Recompute the window's geometry and arrange for it to be
+ * redisplayed.
+ */
+
+ EntryComputeGeometry(entryPtr);
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EventuallyRedraw(entryPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayEntry --
+ *
+ * This procedure redraws the contents of an entry window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayEntry(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ register Entry *entryPtr = (Entry *) clientData;
+ CkWindow *winPtr = entryPtr->winPtr;
+ int y, startX, leftIndex, selectFirst, selectLast, insertPos, dummy;
+ char *displayString;
+
+ entryPtr->flags &= ~REDRAW_PENDING;
+ if ((entryPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED))
+ return;
+
+ /*
+ * Update the scrollbar if that's needed.
+ */
+
+ if (entryPtr->flags & UPDATE_SCROLLBAR) {
+ EntryUpdateScrollbar(entryPtr);
+ }
+
+ /*
+ * Compute x-coordinate of the pixel just after last visible
+ * one, plus vertical position of baseline of text.
+ */
+
+ y = winPtr->height / 2;
+
+ if (entryPtr->displayString == NULL) {
+ displayString = entryPtr->string;
+ } else {
+ displayString = entryPtr->displayString;
+ }
+
+ Ck_SetWindowAttr(winPtr, entryPtr->normalFg, entryPtr->normalBg,
+ entryPtr->normalAttr);
+ Ck_ClearToBot(winPtr, 0, 0);
+
+#if CK_USE_UTF
+ leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
+ displayString;
+ selectFirst = Tcl_UtfAtIndex(displayString, entryPtr->selectFirst) -
+ displayString;
+ selectLast = Tcl_UtfAtIndex(displayString, entryPtr->selectLast) -
+ displayString;
+ insertPos = Tcl_UtfAtIndex(displayString, entryPtr->insertPos) -
+ displayString;
+#else
+ leftIndex = entryPtr->leftIndex;
+ selectFirst = entryPtr->selectFirst;
+ selectLast = entryPtr->selectLast;
+ insertPos = entryPtr->insertPos;
+#endif
+
+ CkDisplayChars(winPtr->mainPtr, winPtr->window,
+ displayString + leftIndex,
+ strlen(displayString) - leftIndex,
+ entryPtr->leftX, y, entryPtr->tabOrigin,
+ CK_NEWLINES_NOT_SPECIAL);
+
+ if (entryPtr->selectLast >= entryPtr->leftIndex) {
+ if (entryPtr->selectFirst < entryPtr->leftIndex) {
+ startX = 0;
+ } else {
+ CkMeasureChars(winPtr->mainPtr,
+ displayString + leftIndex,
+ selectFirst - leftIndex, entryPtr->leftX,
+ winPtr->width, entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL,
+ &startX, &dummy);
+ }
+ if (startX < winPtr->width) {
+ Ck_SetWindowAttr(winPtr, entryPtr->selFg, entryPtr->selBg,
+ entryPtr->selAttr);
+ wmove(winPtr->window, y, startX + entryPtr->leftX);
+ CkDisplayChars(winPtr->mainPtr, winPtr->window,
+ displayString + selectFirst,
+ selectLast - selectFirst,
+ entryPtr->leftX + startX, y, entryPtr->tabOrigin,
+ CK_NEWLINES_NOT_SPECIAL);
+ Ck_SetWindowAttr(winPtr, entryPtr->normalFg, entryPtr->normalBg,
+ entryPtr->normalAttr);
+ }
+ }
+
+ CkMeasureChars(winPtr->mainPtr, displayString + leftIndex,
+ insertPos - leftIndex, entryPtr->leftX,
+ winPtr->width, entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL,
+ &startX, &dummy);
+
+ if (startX >= 0 && startX < winPtr->width) {
+ wmove(winPtr->window, y, startX);
+ if (entryPtr->state == ckNormalUid)
+ Ck_SetHWCursor(winPtr, 1);
+ else
+ Ck_SetHWCursor(winPtr, 0);
+ } else {
+ wmove(winPtr->window, y, 0);
+ Ck_SetHWCursor(winPtr, 0);
+ }
+
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryComputeGeometry --
+ *
+ * This procedure is invoked to recompute information about where
+ * in its window an entry's string will be displayed. It also
+ * computes the requested size for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The tabOrigin fields are recomputed for entryPtr,
+ * and leftIndex may be adjusted. Ck_GeometryRequest is called
+ * to register the desired dimensions for the window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntryComputeGeometry(entryPtr)
+ Entry *entryPtr; /* Widget record for entry. */
+{
+ int totalLength, overflow, maxOffScreen;
+ int width, i, rightX, dummy;
+ char *p, *displayString;
+ CkWindow *winPtr = entryPtr->winPtr;
+
+ /*
+ * If we're displaying a special character instead of the value of
+ * the entry, recompute the displayString.
+ */
+
+ if (entryPtr->displayString != NULL) {
+ ckfree(entryPtr->displayString);
+ entryPtr->displayString = NULL;
+ }
+ if (entryPtr->showChar != NULL) {
+#if CK_USE_UTF
+ int ulen;
+
+ entryPtr->displayString = (char *) ckalloc(entryPtr->numChars * 3 + 1);
+ ulen = Tcl_UtfNext(entryPtr->showChar) - entryPtr->showChar;
+ for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
+ i--) {
+ memcpy(p, entryPtr->showChar, ulen);
+ p += ulen;
+ }
+#else
+ entryPtr->displayString = (char *) ckalloc(entryPtr->numChars + 1);
+ for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
+ i--, p++) {
+ *p = entryPtr->showChar[0];
+ }
+#endif
+ *p = 0;
+ displayString = entryPtr->displayString;
+ } else {
+ displayString = entryPtr->string;
+ }
+
+ /*
+ * Recompute where the leftmost character on the display will
+ * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
+ * so that we don't let characters hang off the edge of the
+ * window unless the entire window is full.
+ */
+
+ CkMeasureChars(winPtr->mainPtr, displayString, strlen(displayString),
+ 0, INT_MAX, 0,
+ CK_NEWLINES_NOT_SPECIAL, &totalLength, &dummy);
+ if (entryPtr->insertPos == entryPtr->numChars)
+ totalLength += 1;
+ overflow = totalLength - entryPtr->winPtr->width;
+ if (overflow < 0) {
+ entryPtr->leftIndex = 0;
+ if (entryPtr->justify == CK_JUSTIFY_LEFT) {
+ entryPtr->leftX = 0;
+ } else if (entryPtr->justify == CK_JUSTIFY_RIGHT) {
+ entryPtr->leftX = entryPtr->winPtr->width - totalLength;
+ } else {
+ entryPtr->leftX = (entryPtr->winPtr->width - totalLength) / 2;
+ }
+ entryPtr->tabOrigin = entryPtr->leftX;
+ } else {
+ int leftIndex;
+
+ /*
+ * The whole string can't fit in the window. Compute the
+ * maximum number of characters that may be off-screen to
+ * the left without leaving empty space on the right of the
+ * window, then don't let leftIndex be any greater than that.
+ */
+
+ maxOffScreen = CkMeasureChars(winPtr->mainPtr,
+ displayString, strlen(displayString),
+ 0, overflow, 0, CK_NEWLINES_NOT_SPECIAL|CK_PARTIAL_OK, &rightX,
+ &dummy);
+ if (rightX < overflow) {
+ maxOffScreen += 1;
+ }
+ if (entryPtr->leftIndex > maxOffScreen) {
+ entryPtr->leftIndex = maxOffScreen;
+ }
+#if CK_USE_UTF
+ leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
+ displayString;
+#else
+ leftIndex = entryPtr->leftIndex;
+#endif
+ CkMeasureChars(winPtr->mainPtr, displayString, leftIndex,
+ 0, INT_MAX, 0,
+ CK_NEWLINES_NOT_SPECIAL|CK_PARTIAL_OK, &rightX, &dummy);
+ entryPtr->leftX = 0;
+ entryPtr->tabOrigin = entryPtr->leftX - rightX;
+ }
+
+ if (entryPtr->prefWidth > 0) {
+ width = entryPtr->prefWidth;
+ } else if (totalLength == 0) {
+ width = 1;
+ } else {
+ width = totalLength;
+ }
+ Ck_GeometryRequest(entryPtr->winPtr, width, 1);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertChars --
+ *
+ * Add new characters to an entry widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * New information gets added to entryPtr; it will be redisplayed
+ * soon, but not necessarily immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InsertChars(entryPtr, index, string)
+ register Entry *entryPtr; /* Entry that is to get the new
+ * elements. */
+ int index; /* Add the new elements before this
+ * element. */
+ char *string; /* New characters to add (NULL-terminated
+ * string). */
+{
+ int length, clength;
+ char *new;
+#if CK_USE_UTF
+ int inspos;
+#endif
+
+ length = strlen(string);
+ if (length == 0) {
+ return;
+ }
+#if CK_USE_UTF
+ clength = Tcl_NumUtfChars(string, -1);
+ new = (char *) ckalloc((unsigned) (entryPtr->numBytes + length + 1));
+ inspos = Tcl_UtfAtIndex(entryPtr->string, index) - entryPtr->string;
+ strncpy(new, entryPtr->string, (size_t) inspos);
+ strcpy(new+inspos, string);
+ strcpy(new+inspos+length, entryPtr->string+inspos);
+ ckfree(entryPtr->string);
+ entryPtr->string = new;
+ entryPtr->numChars += clength;
+ entryPtr->numBytes += length;
+#else
+ clength = length;
+ new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
+ strncpy(new, entryPtr->string, (size_t) index);
+ strcpy(new+index, string);
+ strcpy(new+index+length, entryPtr->string+index);
+ ckfree(entryPtr->string);
+ entryPtr->string = new;
+ entryPtr->numChars += length;
+#endif
+
+ /*
+ * Inserting characters invalidates all indexes into the string.
+ * Touch up the indexes so that they still refer to the same
+ * characters (at new positions). When updating the selection
+ * end-points, don't include the new text in the selection unless
+ * it was completely surrounded by the selection.
+ */
+
+ if (entryPtr->selectFirst >= index) {
+ entryPtr->selectFirst += clength;
+ }
+ if (entryPtr->selectLast > index) {
+ entryPtr->selectLast += clength;
+ }
+ if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
+ entryPtr->selectAnchor += clength;
+ }
+ if (entryPtr->leftIndex > index) {
+ entryPtr->leftIndex += clength;
+ }
+ if (entryPtr->insertPos >= index) {
+ entryPtr->insertPos += clength;
+ }
+
+ if (entryPtr->textVarName != NULL) {
+ Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
+ TCL_GLOBAL_ONLY);
+ }
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteChars --
+ *
+ * Remove one or more characters from an entry widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets freed, the entry gets modified and (eventually)
+ * redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteChars(entryPtr, index, count)
+ register Entry *entryPtr; /* Entry widget to modify. */
+ int index; /* Index of first character to delete. */
+ int count; /* How many characters to delete. */
+{
+ char *new;
+#if CK_USE_UTF
+ int delpos, delcount;
+#endif
+
+ if ((index + count) > entryPtr->numChars) {
+ count = entryPtr->numChars - index;
+ }
+ if (count <= 0) {
+ return;
+ }
+
+#if CK_USE_UTF
+ delpos = Tcl_UtfAtIndex(entryPtr->string, index) - entryPtr->string;
+ delcount = Tcl_UtfAtIndex(entryPtr->string + delpos, count) -
+ (entryPtr->string + delpos);
+ new = (char *) ckalloc((unsigned) (entryPtr->numBytes + 1 - delcount));
+ strncpy(new, entryPtr->string, (size_t) delpos);
+ strcpy(new+delpos, entryPtr->string+delpos+delcount);
+ entryPtr->numChars = Tcl_NumUtfChars(new, -1);
+ entryPtr->numBytes = strlen(new);
+#else
+ new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
+ strncpy(new, entryPtr->string, (size_t) index);
+ strcpy(new+index, entryPtr->string+index+count);
+ entryPtr->numChars -= count;
+#endif
+ ckfree(entryPtr->string);
+ entryPtr->string = new;
+
+ /*
+ * Deleting characters results in the remaining characters being
+ * renumbered. Update the various indexes into the string to reflect
+ * this change.
+ */
+
+ if (entryPtr->selectFirst >= index) {
+ if (entryPtr->selectFirst >= (index+count)) {
+ entryPtr->selectFirst -= count;
+ } else {
+ entryPtr->selectFirst = index;
+ }
+ }
+ if (entryPtr->selectLast >= index) {
+ if (entryPtr->selectLast >= (index+count)) {
+ entryPtr->selectLast -= count;
+ } else {
+ entryPtr->selectLast = index;
+ }
+ }
+ if (entryPtr->selectLast <= entryPtr->selectFirst) {
+ entryPtr->selectFirst = entryPtr->selectLast = -1;
+ }
+ if (entryPtr->selectAnchor >= index) {
+ if (entryPtr->selectAnchor >= (index+count)) {
+ entryPtr->selectAnchor -= count;
+ } else {
+ entryPtr->selectAnchor = index;
+ }
+ }
+ if (entryPtr->leftIndex > index) {
+ if (entryPtr->leftIndex >= (index+count)) {
+ entryPtr->leftIndex -= count;
+ } else {
+ entryPtr->leftIndex = index;
+ }
+ }
+ if (entryPtr->insertPos >= index) {
+ if (entryPtr->insertPos >= (index+count)) {
+ entryPtr->insertPos -= count;
+ } else {
+ entryPtr->insertPos = index;
+ }
+ }
+
+ if (entryPtr->textVarName != NULL) {
+ Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
+ TCL_GLOBAL_ONLY);
+ }
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntrySetValue --
+ *
+ * Replace the contents of a text entry with a given value. This
+ * procedure is invoked when updating the entry from the entry's
+ * associated variable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The string displayed in the entry will change. Any selection
+ * in the entry is lost and the insertion point gets set to the
+ * end of the entry. Note: this procedure does *not* update the
+ * entry's associated variable, since that could result in an
+ * infinite loop.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntrySetValue(entryPtr, value)
+ register Entry *entryPtr; /* Entry whose value is to be
+ * changed. */
+ char *value; /* New text to display in entry. */
+{
+ ckfree(entryPtr->string);
+#if CK_USE_UTF
+ entryPtr->numBytes = strlen(value);
+ entryPtr->numChars = Tcl_NumUtfChars(value, -1);
+ entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numBytes + 1));
+#else
+ entryPtr->numChars = strlen(value);
+ entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
+#endif
+ strcpy(entryPtr->string, value);
+ entryPtr->selectFirst = entryPtr->selectLast = -1;
+ entryPtr->leftIndex = 0;
+ entryPtr->insertPos = entryPtr->numChars;
+
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * EntryEventProc --
+ *
+ * This procedure is invoked by the dispatcher for various
+ * events on entryes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EntryEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ Ck_Preserve((ClientData) entryPtr);
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+ Ck_Release((ClientData) entryPtr);
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (entryPtr->winPtr != NULL) {
+ entryPtr->winPtr = NULL;
+ Tcl_DeleteCommand(entryPtr->interp,
+ Tcl_GetCommandName(entryPtr->interp, entryPtr->widgetCmd));
+ }
+ if (entryPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
+ }
+ Ck_EventuallyFree((ClientData) entryPtr, (Ck_FreeProc *) DestroyEntry);
+ } else if (eventPtr->type == CK_EV_FOCUSIN) {
+ EntryFocusProc(entryPtr, 1);
+ } else if (eventPtr->type == CK_EV_FOCUSOUT) {
+ EntryFocusProc(entryPtr, 0);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GetEntryIndex --
+ *
+ * Parse an index into an entry and return either its value
+ * or an error.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the index (into entryPtr) corresponding to
+ * string. The index value is guaranteed to lie between 0 and
+ * the number of characters in the string, inclusive. If an
+ * error occurs then an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+GetEntryIndex(interp, entryPtr, string, indexPtr)
+ Tcl_Interp *interp; /* For error messages. */
+ Entry *entryPtr; /* Entry for which the index is being
+ * specified. */
+ char *string; /* Specifies character in entryPtr. */
+ int *indexPtr; /* Where to store converted index. */
+{
+ size_t length;
+ int dummy;
+ CkWindow *winPtr = entryPtr->winPtr;
+
+ length = strlen(string);
+
+ if (string[0] == 'a') {
+ if (strncmp(string, "anchor", length) == 0) {
+ *indexPtr = entryPtr->selectAnchor;
+ } else {
+ badIndex:
+
+ /*
+ * Some of the paths here leave messages in interp->result,
+ * so we have to clear it out before storing our own message.
+ */
+
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ Tcl_AppendResult(interp, "bad entry index \"", string,
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if (string[0] == 'e') {
+ if (strncmp(string, "end", length) == 0) {
+ *indexPtr = entryPtr->numChars;
+ } else {
+ goto badIndex;
+ }
+ } else if (string[0] == 'i') {
+ if (strncmp(string, "insert", length) == 0) {
+ *indexPtr = entryPtr->insertPos;
+ } else {
+ goto badIndex;
+ }
+ } else if (string[0] == 's') {
+ if (entryPtr->selectFirst == -1) {
+ interp->result = "selection isn't in entry";
+ return TCL_ERROR;
+ }
+ if (length < 5) {
+ goto badIndex;
+ }
+ if (strncmp(string, "sel.first", length) == 0) {
+ *indexPtr = entryPtr->selectFirst;
+ } else if (strncmp(string, "sel.last", length) == 0) {
+ *indexPtr = entryPtr->selectLast;
+ } else {
+ goto badIndex;
+ }
+ } else if (string[0] == '@') {
+ int x, roundUp;
+
+ if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
+ goto badIndex;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ roundUp = 0;
+ if (x >= entryPtr->winPtr->width) {
+ x = entryPtr->winPtr->width - 1;
+ roundUp = 1;
+ }
+ if (entryPtr->numChars == 0) {
+ *indexPtr = 0;
+ } else {
+ char *string = (entryPtr->displayString == NULL) ?
+ entryPtr->string : entryPtr->displayString;
+
+ *indexPtr = CkMeasureChars(winPtr->mainPtr, string, strlen(string),
+ entryPtr->tabOrigin, x,
+ entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL, &dummy, &dummy);
+#if 0
+ *indexPtr = entryPtr->leftX + entryPtr->leftIndex + x;
+#endif
+ }
+ if (*indexPtr >= entryPtr->numChars)
+ *indexPtr = entryPtr->numChars;
+ } else {
+ if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
+ goto badIndex;
+ }
+ if (*indexPtr < 0){
+ *indexPtr = 0;
+ } else if (*indexPtr > entryPtr->numChars) {
+ *indexPtr = entryPtr->numChars;
+ }
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntrySelectTo --
+ *
+ * Modify the selection by moving its un-anchored end. This could
+ * make the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntrySelectTo(entryPtr, index)
+ register Entry *entryPtr; /* Information about widget. */
+ int index; /* Index of element that is to
+ * become the "other" end of the
+ * selection. */
+{
+ int newFirst, newLast;
+
+ /*
+ * Pick new starting and ending points for the selection.
+ */
+
+ if (entryPtr->selectAnchor > entryPtr->numChars) {
+ entryPtr->selectAnchor = entryPtr->numChars;
+ }
+ if (entryPtr->selectAnchor <= index) {
+ newFirst = entryPtr->selectAnchor;
+ newLast = index;
+ } else {
+ newFirst = index;
+ newLast = entryPtr->selectAnchor;
+ if (newLast < 0) {
+ newFirst = newLast = -1;
+ }
+ }
+ if ((entryPtr->selectFirst == newFirst)
+ && (entryPtr->selectLast == newLast)) {
+ return;
+ }
+ entryPtr->selectFirst = newFirst;
+ entryPtr->selectLast = newLast;
+ EventuallyRedraw(entryPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Ensure that an entry is eventually redrawn on the display.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed. Right now we don't do selective
+ * redisplays: the whole window will be redrawn. This doesn't
+ * seem to hurt performance noticeably, but if it does then this
+ * could be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EventuallyRedraw(entryPtr)
+ Entry *entryPtr; /* Information about widget. */
+{
+ if ((entryPtr->winPtr == NULL) || !(entryPtr->winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+
+ /*
+ * Right now we don't do selective redisplays: the whole window
+ * will be redrawn. This doesn't seem to hurt performance noticeably,
+ * but if it does then this could be changed.
+ */
+
+ if (!(entryPtr->flags & REDRAW_PENDING)) {
+ entryPtr->flags |= REDRAW_PENDING;
+ Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryVisibleRange --
+ *
+ * Return information about the range of the entry that is
+ * currently visible.
+ *
+ * Results:
+ * *firstPtr and *lastPtr are modified to hold fractions between
+ * 0 and 1 identifying the range of characters visible in the
+ * entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntryVisibleRange(entryPtr, firstPtr, lastPtr)
+ Entry *entryPtr; /* Information about widget. */
+ double *firstPtr; /* Return position of first visible
+ * character in widget. */
+ double *lastPtr; /* Return position of char just after
+ * last visible one. */
+{
+ char *displayString;
+ int charsInWindow, endX, dummy;
+ CkWindow *winPtr = entryPtr->winPtr;
+
+ if (entryPtr->displayString == NULL) {
+ displayString = entryPtr->string;
+ } else {
+ displayString = entryPtr->displayString;
+ }
+ if (entryPtr->numChars == 0) {
+ *firstPtr = 0.0;
+ *lastPtr = 1.0;
+ } else {
+ int leftIndex, total;
+
+#if CK_USE_UTF
+ leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
+ displayString;
+ total = entryPtr->numBytes - leftIndex;
+#else
+ leftIndex = entryPtr->leftIndex;
+ total = entryPtr->numChars - leftIndex;
+#endif
+ charsInWindow = CkMeasureChars(winPtr->mainPtr,
+ displayString + leftIndex, total, 0,
+ entryPtr->winPtr->width, 0,
+ CK_AT_LEAST_ONE|CK_NEWLINES_NOT_SPECIAL, &endX, &dummy);
+ *firstPtr = ((double) leftIndex)/entryPtr->numChars;
+ *lastPtr = ((double) (leftIndex + charsInWindow))
+ /entryPtr->numChars;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryUpdateScrollbar --
+ *
+ * This procedure is invoked whenever information has changed in
+ * an entry in a way that would invalidate a scrollbar display.
+ * If there is an associated scrollbar, then this procedure updates
+ * it by invoking a Tcl command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Tcl command is invoked, and an additional command may be
+ * invoked to process errors in the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntryUpdateScrollbar(entryPtr)
+ Entry *entryPtr; /* Information about widget. */
+{
+ char args[100];
+ int code;
+ double first, last;
+
+ if (entryPtr->scrollCmd == NULL) {
+ return;
+ }
+
+ EntryVisibleRange(entryPtr, &first, &last);
+ sprintf(args, " %g %g", first, last);
+ code = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
+ (char *) NULL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(entryPtr->interp,
+ "\n (horizontal scrolling command executed by entry)");
+ Tk_BackgroundError(entryPtr->interp);
+ }
+ Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EntryFocusProc --
+ *
+ * This procedure is called whenever the entry gets or loses the
+ * input focus. It's also called whenever the window is reconfigured
+ * while it has the focus.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor gets turned on or off.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EntryFocusProc(entryPtr, gotFocus)
+ Entry *entryPtr; /* Entry that got or lost focus. */
+ int gotFocus; /* 1 means window is getting focus, 0 means
+ * it's losing it. */
+{
+ if (gotFocus)
+ entryPtr->flags |= GOT_FOCUS;
+ else
+ entryPtr->flags &= ~GOT_FOCUS;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * EntryTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in an entry.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the entry will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+EntryTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Not used. */
+ char *name2; /* Not used. */
+ int flags; /* Information about what happened. */
+{
+ register Entry *entryPtr = (Entry *) clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
+ TCL_GLOBAL_ONLY);
+ Tcl_TraceVar(interp, entryPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ EntryTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+
+ /*
+ * Update the entry's text with the value of the variable, unless
+ * the entry already has that value (this happens when the variable
+ * changes value because we changed it because someone typed in
+ * the entry).
+ */
+
+ value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (strcmp(value, entryPtr->string) != 0) {
+ EntrySetValue(entryPtr, value);
+ }
+ return (char *) NULL;
+}
+
+
+
+
--- /dev/null
+/*
+ * ckEvent.c --
+ *
+ * This file provides basic event-managing facilities,
+ * whereby procedure callbacks may be attached to
+ * certain events.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995-1999 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+#ifdef HAVE_GPM
+#include "gpm.h"
+#endif
+
+/*
+ * There's a potential problem if a handler is deleted while it's
+ * current (i.e. its procedure is executing), since Ck_HandleEvent
+ * will need to read the handler's "nextPtr" field when the procedure
+ * returns. To handle this problem, structures of the type below
+ * indicate the next handler to be processed for any (recursively
+ * nested) dispatches in progress. The nextHandler fields get
+ * updated if the handlers pointed to are deleted. Ck_HandleEvent
+ * also needs to know if the entire window gets deleted; the winPtr
+ * field is set to zero if that particular window gets deleted.
+ */
+
+typedef struct InProgress {
+ CkEvent *eventPtr; /* Event currently being handled. */
+ CkWindow *winPtr; /* Window for event. Gets set to NULL if
+ * window is deleted while event is being
+ * handled. */
+ CkEventHandler *nextHandler; /* Next handler in search. */
+ struct InProgress *nextPtr; /* Next higher nested search. */
+} InProgress;
+
+static InProgress *pendingPtr = NULL;
+ /* Topmost search in progress, or
+ * NULL if none. */
+
+/*
+ * For each call to Ck_CreateGenericHandler, an instance of the following
+ * structure will be created. All of the active handlers are linked into a
+ * list.
+ */
+
+typedef struct GenericHandler {
+ Ck_GenericProc *proc; /* Procedure to dispatch on all events. */
+ ClientData clientData; /* Client data to pass to procedure. */
+ int deleteFlag; /* Flag to set when this handler is deleted. */
+ struct GenericHandler *nextPtr;
+ /* Next handler in list of all generic
+ * handlers, or NULL for end of list. */
+} GenericHandler;
+
+static GenericHandler *genericList = NULL;
+ /* First handler in the list, or NULL. */
+static GenericHandler *lastGenericPtr = NULL;
+ /* Last handler in list. */
+
+/*
+ * There's a potential problem if Ck_HandleEvent is entered recursively.
+ * A handler cannot be deleted physically until we have returned from
+ * calling it. Otherwise, we're looking at unallocated memory in advancing to
+ * its `next' entry. We deal with the problem by using the `delete flag' and
+ * deleting handlers only when it's known that there's no handler active.
+ *
+ * The following variable has a non-zero value when a handler is active.
+ */
+
+static int genericHandlersActive = 0;
+
+/*
+ * For barcode readers an instance of the following structure is linked
+ * to mainInfo. The supported DATA LOGIC barcode readers are connected
+ * between PC keyboard and PC keyboard controller and generate a data
+ * packet surrounded by start and end characters. If the start character
+ * is received a timer is started and the following keystrokes are
+ * collected into the buffer until the end character is received or the
+ * timer expires.
+ */
+
+#define DEFAULT_BARCODE_TIMEOUT 1000
+
+typedef struct barcodeData {
+ Tk_TimerToken timer;/* Barcode packet timer. */
+ int pkttime; /* Timeout value. */
+ int startChar; /* Start of barcode packet character. */
+ int endChar; /* End of barcode packet character. */
+ int delivered; /* BarCode event has been delivered. */
+ int index; /* Current index into buffer. */
+#if CK_USE_UTF
+ char buffer[256]; /* Here the barcode packet is assembled. */
+#else
+ char buffer[128]; /* Here the barcode packet is assembled. */
+#endif
+} BarcodeData;
+
+/*
+ * Timeout procedure for reading barcode packet:
+ */
+
+static void BarcodeTimeout _ANSI_ARGS_((ClientData clientData));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_CreateEventHandler --
+ *
+ * Arrange for a given procedure to be invoked whenever
+ * events from a given class occur in a given window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * From now on, whenever an event of the type given by
+ * mask occurs for token and is processed by Ck_HandleEvent,
+ * proc will be called. See the manual entry for details
+ * of the calling sequence and return value for proc.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_CreateEventHandler(winPtr, mask, proc, clientData)
+ CkWindow *winPtr; /* Window in which to create handler. */
+ long mask; /* Events for which proc should be called. */
+ Ck_EventProc *proc; /* Procedure to call for each
+ * selected event */
+ ClientData clientData; /* Arbitrary data to pass to proc. */
+{
+ CkEventHandler *handlerPtr;
+ int found;
+
+ /*
+ * Skim through the list of existing handlers to see if there's
+ * already a handler declared with the same callback and clientData
+ * (if so, just change the mask). If no existing handler matches,
+ * then create a new handler.
+ */
+
+ found = 0;
+ if (winPtr->handlerList == NULL) {
+ handlerPtr = (CkEventHandler *) ckalloc(sizeof (CkEventHandler));
+ winPtr->handlerList = handlerPtr;
+ goto initHandler;
+ } else {
+ for (handlerPtr = winPtr->handlerList; ;
+ handlerPtr = handlerPtr->nextPtr) {
+ if ((handlerPtr->proc == proc)
+ && (handlerPtr->clientData == clientData)) {
+ handlerPtr->mask = mask;
+ found = 1;
+ }
+ if (handlerPtr->nextPtr == NULL) {
+ break;
+ }
+ }
+ }
+
+ /*
+ * Create a new handler if no matching old handler was found.
+ */
+
+ if (!found) {
+ handlerPtr->nextPtr = (CkEventHandler *) ckalloc(
+ sizeof (CkEventHandler));
+ handlerPtr = handlerPtr->nextPtr;
+initHandler:
+ handlerPtr->mask = mask;
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->nextPtr = NULL;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DeleteEventHandler --
+ *
+ * Delete a previously-created handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there existed a handler as described by the
+ * parameters, the handler is deleted so that proc
+ * will not be invoked again.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_DeleteEventHandler(winPtr, mask, proc, clientData)
+ CkWindow *winPtr; /* Same as corresponding arguments passed */
+ long mask; /* previously to Ck_CreateEventHandler. */
+ Ck_EventProc *proc;
+ ClientData clientData;
+{
+ CkEventHandler *handlerPtr;
+ InProgress *ipPtr;
+ CkEventHandler *prevPtr;
+
+ /*
+ * Find the event handler to be deleted, or return
+ * immediately if it doesn't exist.
+ */
+
+ for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
+ prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
+ if (handlerPtr == NULL) {
+ return;
+ }
+ if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
+ && (handlerPtr->clientData == clientData)) {
+ break;
+ }
+ }
+
+ /*
+ * If Ck_HandleEvent is about to process this handler, tell it to
+ * process the next one instead.
+ */
+
+ for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
+ if (ipPtr->nextHandler == handlerPtr) {
+ ipPtr->nextHandler = handlerPtr->nextPtr;
+ }
+ }
+
+ /*
+ * Free resources associated with the handler.
+ */
+
+ if (prevPtr == NULL) {
+ winPtr->handlerList = handlerPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = handlerPtr->nextPtr;
+ }
+ ckfree((char *) handlerPtr);
+}
+\f
+/*--------------------------------------------------------------
+ *
+ * Ck_CreateGenericHandler --
+ *
+ * Register a procedure to be called on each event, regardless
+ * of window. Generic handlers are useful for capturing
+ * events that aren't associated with windows, or events for windows
+ * not managed by Ck.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * From now on, whenever an event is given to Ck_HandleEvent,
+ * invoke proc, giving it clientData and the event as arguments.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_CreateGenericHandler(proc, clientData)
+ Ck_GenericProc *proc; /* Procedure to call on every event. */
+ ClientData clientData; /* One-word value to pass to proc. */
+{
+ GenericHandler *handlerPtr;
+
+ handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
+
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->deleteFlag = 0;
+ handlerPtr->nextPtr = NULL;
+ if (genericList == NULL) {
+ genericList = handlerPtr;
+ } else {
+ lastGenericPtr->nextPtr = handlerPtr;
+ }
+ lastGenericPtr = handlerPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DeleteGenericHandler --
+ *
+ * Delete a previously-created generic handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If there existed a handler as described by the parameters,
+ * that handler is logically deleted so that proc will not be
+ * invoked again. The physical deletion happens in the event
+ * loop in Ck_HandleEvent.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_DeleteGenericHandler(proc, clientData)
+ Ck_GenericProc *proc;
+ ClientData clientData;
+{
+ GenericHandler * handler;
+
+ for (handler = genericList; handler; handler = handler->nextPtr) {
+ if ((handler->proc == proc) && (handler->clientData == clientData)) {
+ handler->deleteFlag = 1;
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_HandleEvent --
+ *
+ * Given an event, invoke all the handlers that have
+ * been registered for the event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the handlers.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_HandleEvent(mainPtr, eventPtr)
+ CkMainInfo *mainPtr;
+ CkEvent *eventPtr; /* Event to dispatch. */
+{
+ CkEventHandler *handlerPtr;
+ GenericHandler *genericPtr;
+ GenericHandler *genPrevPtr;
+ CkWindow *winPtr;
+ InProgress ip;
+
+ /*
+ * Invoke all the generic event handlers (those that are
+ * invoked for all events). If a generic event handler reports that
+ * an event is fully processed, go no further.
+ */
+
+ for (genPrevPtr = NULL, genericPtr = genericList; genericPtr != NULL; ) {
+ if (genericPtr->deleteFlag) {
+ if (!genericHandlersActive) {
+ GenericHandler *tmpPtr;
+
+ /*
+ * This handler needs to be deleted and there are no
+ * calls pending through the handler, so now is a safe
+ * time to delete it.
+ */
+
+ tmpPtr = genericPtr->nextPtr;
+ if (genPrevPtr == NULL) {
+ genericList = tmpPtr;
+ } else {
+ genPrevPtr->nextPtr = tmpPtr;
+ }
+ if (tmpPtr == NULL) {
+ lastGenericPtr = genPrevPtr;
+ }
+ (void) ckfree((char *) genericPtr);
+ genericPtr = tmpPtr;
+ continue;
+ }
+ } else {
+ int done;
+
+ genericHandlersActive++;
+ done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
+ genericHandlersActive--;
+ if (done) {
+ return;
+ }
+ }
+ genPrevPtr = genericPtr;
+ genericPtr = genPrevPtr->nextPtr;
+ }
+
+ if (Tcl_FindHashEntry(&mainPtr->winTable, (char *) eventPtr->any.winPtr)
+ == NULL) {
+ /*
+ * There isn't a CkWindow structure for this window.
+ */
+ return;
+ }
+ winPtr = eventPtr->any.winPtr;
+ ip.eventPtr = eventPtr;
+ ip.winPtr = winPtr;
+ ip.nextHandler = NULL;
+ ip.nextPtr = pendingPtr;
+ pendingPtr = &ip;
+ for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
+ if ((handlerPtr->mask & eventPtr->type) != 0) {
+ ip.nextHandler = handlerPtr->nextPtr;
+ (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
+ handlerPtr = ip.nextHandler;
+ } else {
+ handlerPtr = handlerPtr->nextPtr;
+ }
+ }
+
+ /*
+ * Pass the event to the "bind" command mechanism.
+ */
+
+ CkBindEventProc(winPtr, eventPtr);
+
+ pendingPtr = ip.nextPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkEventDeadWindow --
+ *
+ * This procedure is invoked when it is determined that
+ * a window is dead. It cleans up event-related information
+ * about the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Various things get cleaned up and recycled.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkEventDeadWindow(winPtr)
+ CkWindow *winPtr; /* Information about the window
+ * that is being deleted. */
+{
+ CkEventHandler *handlerPtr;
+ InProgress *ipPtr;
+
+ /*
+ * While deleting all the handlers, be careful to check for
+ * Ck_HandleEvent being about to process one of the deleted
+ * handlers. If it is, tell it to quit (all of the handlers
+ * are being deleted).
+ */
+
+ while (winPtr->handlerList != NULL) {
+ handlerPtr = winPtr->handlerList;
+ winPtr->handlerList = handlerPtr->nextPtr;
+ for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
+ if (ipPtr->nextHandler == handlerPtr) {
+ ipPtr->nextHandler = NULL;
+ }
+ if (ipPtr->winPtr == winPtr) {
+ ipPtr->winPtr = NULL;
+ }
+ }
+ ckfree((char *) handlerPtr);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkHandleInput --
+ *
+ * Process keyboard events from curses.
+ *
+ * Results:
+ * The return value is TK_FILE_HANDLED if the procedure
+ * actually found an event to process. If no event was found
+ * then TK_READABLE is returned.
+ *
+ * Side effects:
+ * The handling of the event could cause additional
+ * side effects.
+ *
+ *--------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+int
+CkHandleInput(clientData, mask, flags)
+ ClientData clientData; /* Pointer to main info. */
+ int mask; /* OR-ed combination of the bits TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION, indicating
+ * current state of file. */
+ int flags; /* Flag bits passed to Tk_DoOneEvent;
+ * contains bits such as TK_DONT_WAIT,
+ * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
+{
+ CkEvent event;
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+ int code;
+ static int buttonpressed = 0;
+ static int errCount = 0;
+
+ if (!(flags & TK_FILE_EVENTS))
+ return 0;
+
+ if (!(mask & TK_READABLE))
+ return TK_READABLE;
+
+ code = getch();
+ if (code == ERR) {
+ if (++errCount > 100) {
+ Tcl_Eval(mainPtr->interp, "exit 99");
+ exit(99); /* just in case */
+ }
+ return TK_READABLE;
+ }
+ errCount = 0;
+
+ /*
+ * Barcode reader handling.
+ */
+
+ if (mainPtr->flags & CK_HAS_BARCODE) {
+ BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
+
+ /*
+ * Here, special handling for nested event loops:
+ * If BarCode event has been delivered already, we must
+ * reset the buffer index in order to get normal Key events.
+ */
+ if (bd->delivered && bd->index >= 0) {
+ bd->delivered = 0;
+ bd->index = -1;
+ }
+
+ if (bd->index >= 0 || code == bd->startChar) {
+ if (code == bd->startChar) {
+ Tk_DeleteTimerHandler(bd->timer);
+ bd->timer = Tk_CreateTimerHandler(bd->pkttime, BarcodeTimeout,
+ (ClientData) mainPtr);
+ bd->index = 0;
+ } else if (code == bd->endChar) {
+ Tk_DeleteTimerHandler(bd->timer);
+ bd->timer = (Tk_TimerToken) NULL;
+ bd->delivered = 1;
+ event.key.type = CK_EV_BARCODE;
+ event.key.winPtr = mainPtr->focusPtr;
+ event.key.keycode = 0;
+ Ck_HandleEvent(mainPtr, &event);
+ /*
+ * Careful, event handler could turn barcode off.
+ * Only reset buffer index if BarCode event delivered
+ * flag is set.
+ */
+ bd = (BarcodeData *) mainPtr->barcodeData;
+ if (bd != NULL && bd->delivered) {
+ bd->delivered = 0;
+ bd->index = -1;
+ }
+ return TK_FILE_HANDLED;
+ } else {
+ /* Leave space for one NUL byte. */
+ if (bd->index < sizeof (bd->buffer) - 1)
+ bd->buffer[bd->index] = code;
+ bd->index++;
+ }
+ return TK_READABLE;
+ }
+ }
+
+#ifdef NCURSES_MOUSE_VERSION
+ /*
+ * ncurses-1.9.8a has builtin mouse support for at least xterm.
+ */
+
+ if (code == KEY_MOUSE) {
+ MEVENT mEvent;
+ int i;
+
+ if (mainPtr->flags & CK_MOUSE_XTERM) {
+ goto getMouse;
+ }
+
+ if (getmouse(&mEvent) == ERR)
+ return TK_FILE_HANDLED;
+
+ for (i = 1; i <= 3; i++) {
+ if (BUTTON_PRESS(mEvent.bstate, i)) {
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+ goto mouseEventNC;
+ } else if (BUTTON_RELEASE(mEvent.bstate, i)) {
+ event.mouse.type = CK_EV_MOUSE_UP;
+mouseEventNC:
+ event.mouse.button = i;
+ event.mouse.x = mEvent.x;
+ event.mouse.y = mEvent.y;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return TK_FILE_HANDLED;
+ }
+ }
+ }
+#endif
+
+#if defined(__WIN32__) || defined(DJGPP)
+ if ((mainPtr->flags & CK_HAS_MOUSE) && code == KEY_MOUSE) {
+ int i;
+
+ request_mouse_pos();
+ for (i = 0; i < 3; i++) {
+ if (Mouse_status.button[i] == BUTTON_PRESSED) {
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+ goto mouseEvt;
+ } else if (Mouse_status.button[i] == BUTTON_RELEASED) {
+ event.mouse.type = CK_EV_MOUSE_UP;
+mouseEvt:
+ event.mouse.button = i + 1;
+ event.mouse.x = Mouse_status.x;
+ event.mouse.y = Mouse_status.y;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return TK_FILE_HANDLED;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Xterm mouse report handling: Although GPM has an xterm module
+ * this is separately done here, since I want to be as independent
+ * as possible from GPM.
+ * It is assumed that the entire mouse report comes in one piece
+ * ie without any delay between the 6 relevant characters.
+ * Only a single button down/up event is generated.
+ */
+
+#if !defined(__WIN32__)&& !defined(DJGPP)
+ if ((mainPtr->flags & CK_MOUSE_XTERM) && (code == 0x1b || code == 0x9b)) {
+ int code2;
+
+ if (code == 0x9b)
+ goto getM;
+ code2 = getch();
+ if (code2 != ERR) {
+ if (code2 == '[')
+ goto getM;
+ ungetch(code2);
+ } else
+ errCount++;
+ goto keyEvent;
+getM:
+ code2 = getch();
+ if (code2 != ERR) {
+ if (code2 == 'M')
+ goto getMouse;
+ ungetch(code2);
+ } else
+ errCount++;
+ goto keyEvent;
+getMouse:
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return TK_READABLE;
+ }
+ event.mouse.button = ((code2 - 0x20) & 0x03) + 1;
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return TK_READABLE;
+ }
+ event.mouse.x = event.mouse.rootx = code2 - 0x20 - 1;
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return TK_READABLE;
+ }
+ event.mouse.y = event.mouse.rooty = code2 - 0x20 - 1;
+ if (event.mouse.button > 3) {
+ event.mouse.button = buttonpressed;
+ buttonpressed = 0;
+ event.mouse.type = CK_EV_MOUSE_UP;
+ goto mouseEvent;
+ } else if (buttonpressed == 0) {
+ buttonpressed = event.mouse.button;
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+mouseEvent:
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return TK_FILE_HANDLED;
+ }
+ return TK_READABLE;
+ }
+#endif
+
+keyEvent:
+ event.key.type = CK_EV_KEYPRESS;
+ event.key.winPtr = mainPtr->focusPtr;
+ event.key.keycode = code;
+ if (event.key.keycode < 0)
+ event.key.keycode &= 0xff;
+ Ck_HandleEvent(mainPtr, &event);
+ return TK_FILE_HANDLED;
+}
+#else
+void
+CkHandleInput(clientData, mask)
+ ClientData clientData; /* Pointer to main info. */
+ int mask; /* OR-ed combination of the bits TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION, indicating
+ * current state of file. */
+{
+ CkEvent event;
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+ int code;
+ static int buttonpressed = 0;
+ static int errCount = 0;
+
+ if (!(mask & TCL_READABLE))
+ return;
+
+ code = getch();
+ if (code == ERR) {
+ if (++errCount > 100) {
+ Tcl_Eval(mainPtr->interp, "exit 99");
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Exit(99); /* just in case */
+#else
+ exit(99); /* just in case */
+#endif
+ }
+ return;
+ }
+ errCount = 0;
+
+ /*
+ * Barcode reader handling.
+ */
+
+ if (mainPtr->flags & CK_HAS_BARCODE) {
+ BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
+
+ /*
+ * Here, special handling for nested event loops:
+ * If BarCode event has been delivered already, we must
+ * reset the buffer index in order to get normal Key events.
+ */
+ if (bd->delivered && bd->index >= 0) {
+ bd->delivered = 0;
+ bd->index = -1;
+ }
+
+ if (bd->index >= 0 || code == bd->startChar) {
+ if (code == bd->startChar) {
+ Tk_DeleteTimerHandler(bd->timer);
+ bd->timer = Tk_CreateTimerHandler(bd->pkttime, BarcodeTimeout,
+ (ClientData) mainPtr);
+ bd->index = 0;
+ } else if (code == bd->endChar) {
+ Tk_DeleteTimerHandler(bd->timer);
+ bd->timer = (Tk_TimerToken) NULL;
+ bd->delivered = 1;
+ event.key.type = CK_EV_BARCODE;
+ event.key.winPtr = mainPtr->focusPtr;
+ event.key.keycode = 0;
+ Ck_HandleEvent(mainPtr, &event);
+ /*
+ * Careful, event handler could turn barcode off.
+ * Only reset buffer index if BarCode event delivered
+ * flag is set.
+ */
+ bd = (BarcodeData *) mainPtr->barcodeData;
+ if (bd != NULL && bd->delivered) {
+ bd->delivered = 0;
+ bd->index = -1;
+ }
+ return;
+ } else {
+ /* Leave space for one NUL byte. */
+ if (bd->index < sizeof (bd->buffer) - 1) {
+#if CK_USE_UTF
+ char c, utfb[8];
+ int numc, i;
+
+ c = code;
+ Tcl_ExternalToUtf(NULL, mainPtr->isoEncoding,
+ &c, 1, 0, NULL, utfb, sizeof (utfb),
+ NULL, &numc, NULL);
+ if (bd->index + numc < sizeof (bd->buffer) - 1) {
+ for (i = 0; i < numc; i++)
+ bd->buffer[bd->index + i] = utfb[i];
+ } else
+ bd->buffer[bd->index] = '\0';
+ bd->index += numc - 1;
+#else
+ bd->buffer[bd->index] = code;
+#endif
+ }
+ bd->index++;
+ }
+ return;
+ }
+ }
+
+#ifdef NCURSES_MOUSE_VERSION
+ /*
+ * ncurses-1.9.8a has builtin mouse support for at least xterm.
+ */
+
+ if (code == KEY_MOUSE) {
+ MEVENT mEvent;
+ int i;
+
+ if (mainPtr->flags & CK_MOUSE_XTERM) {
+ goto getMouse;
+ }
+
+ if (getmouse(&mEvent) == ERR)
+ return;
+
+ for (i = 1; i <= 3; i++) {
+ if (BUTTON_PRESS(mEvent.bstate, i)) {
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+ goto mouseEventNC;
+ } else if (BUTTON_RELEASE(mEvent.bstate, i)) {
+ event.mouse.type = CK_EV_MOUSE_UP;
+mouseEventNC:
+ event.mouse.button = i;
+ event.mouse.rootx = mEvent.x;
+ event.mouse.rooty = mEvent.y;
+ event.mouse.x = mEvent.x;
+ event.mouse.y = mEvent.y;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return;
+ }
+ }
+ }
+#endif
+
+#if defined(__WIN32__) || defined(DJGPP)
+ if ((mainPtr->flags & CK_HAS_MOUSE) && code == KEY_MOUSE) {
+ int i;
+
+ request_mouse_pos();
+ for (i = 0; i < 3; i++) {
+ if (Mouse_status.button[i] == BUTTON_PRESSED) {
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+ goto mouseEvt;
+ } else if (Mouse_status.button[i] == BUTTON_RELEASED) {
+ event.mouse.type = CK_EV_MOUSE_UP;
+mouseEvt:
+ event.mouse.button = i + 1;
+ event.mouse.x = Mouse_status.x;
+ event.mouse.y = Mouse_status.y;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Xterm mouse report handling: Although GPM has an xterm module
+ * this is separately done here, since I want to be as independent
+ * as possible from GPM.
+ * It is assumed that the entire mouse report comes in one piece
+ * ie without any delay between the 6 relevant characters.
+ * Only a single button down/up event is generated.
+ */
+
+#if !defined(__WIN32__) && !defined(DJGPP)
+ if ((mainPtr->flags & CK_MOUSE_XTERM) && (code == 0x1b || code == 0x9b)) {
+ int code2;
+
+ if (code == 0x9b)
+ goto getM;
+ code2 = getch();
+ if (code2 != ERR) {
+ if (code2 == '[')
+ goto getM;
+ ungetch(code2);
+ } else
+ errCount++;
+ goto keyEvent;
+getM:
+ code2 = getch();
+ if (code2 != ERR) {
+ if (code2 == 'M')
+ goto getMouse;
+ ungetch(code2);
+ } else
+ errCount++;
+ goto keyEvent;
+getMouse:
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return;
+ }
+ event.mouse.button = ((code2 - 0x20) & 0x03) + 1;
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return;
+ }
+ event.mouse.x = event.mouse.rootx = code2 - 0x20 - 1;
+ code2 = getch();
+ if (code2 == ERR) {
+ errCount++;
+ return;
+ }
+ event.mouse.y = event.mouse.rooty = code2 - 0x20 - 1;
+ if (event.mouse.button > 3) {
+ event.mouse.button = buttonpressed;
+ buttonpressed = 0;
+ event.mouse.type = CK_EV_MOUSE_UP;
+ goto mouseEvent;
+ } else if (buttonpressed == 0) {
+ buttonpressed = event.mouse.button;
+ event.mouse.type = CK_EV_MOUSE_DOWN;
+mouseEvent:
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return;
+ }
+ return;
+ }
+#endif
+
+keyEvent:
+ event.key.type = CK_EV_KEYPRESS;
+ event.key.winPtr = mainPtr->focusPtr;
+ event.key.keycode = code;
+ if (event.key.keycode < 0)
+ event.key.keycode &= 0xff;
+ Ck_HandleEvent(mainPtr, &event);
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
+\f
+#ifdef HAVE_GPM
+/*
+ *--------------------------------------------------------------
+ *
+ * CkHandleGPMInput --
+ *
+ * Process mouse events from GPM.
+ *
+ * Results:
+ * The return value is TK_FILE_HANDLED if the procedure
+ * actually found an event to process. If no event was found
+ * then TK_READABLE is returned.
+ *
+ * Side effects:
+ * The handling of the event could cause additional
+ * side effects.
+ *
+ *--------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+int
+CkHandleGPMInput(clientData, mask, flags)
+ ClientData clientData; /* Pointer to main info. */
+ int mask; /* OR-ed combination of the bits TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION, indicating
+ * current state of file. */
+ int flags; /* Flag bits passed to Tk_DoOneEvent;
+ * contains bits such as TK_DONT_WAIT,
+ * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
+{
+ Gpm_Event gpmEvent;
+ CkEvent event;
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+ int ret, type;
+
+ if (!(flags & TK_FILE_EVENTS))
+ return 0;
+
+ if (!(mask & TK_READABLE))
+ return TK_READABLE;
+
+ ret = Gpm_GetEvent(&gpmEvent);
+ if (ret == 0) {
+ /*
+ * GPM connection is closed; delete this file handler.
+ */
+
+ Tk_DeleteFileHandler((int) mainPtr->mouseData);
+ mainPtr->mouseData = (ClientData) -1;
+ return 0;
+ } else if (ret == -1)
+ return TK_READABLE;
+
+ GPM_DRAWPOINTER(&gpmEvent);
+ type = gpmEvent.type & (GPM_DOWN | GPM_UP);
+ if (type == GPM_DOWN || type == GPM_UP) {
+ event.mouse.type = type == GPM_DOWN ? CK_EV_MOUSE_DOWN :
+ CK_EV_MOUSE_UP;
+ if (gpmEvent.buttons & GPM_B_LEFT)
+ event.mouse.button = 1;
+ else if (gpmEvent.buttons & GPM_B_MIDDLE)
+ event.mouse.button = 2;
+ else if (gpmEvent.buttons & GPM_B_RIGHT)
+ event.mouse.button = 3;
+ event.mouse.x = event.mouse.rootx = gpmEvent.x - 1;
+ event.mouse.y = event.mouse.rooty = gpmEvent.y - 1;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ return TK_FILE_HANDLED;
+ }
+ return TK_READABLE;
+}
+#else
+void
+CkHandleGPMInput(clientData, mask)
+ ClientData clientData; /* Pointer to main info. */
+ int mask; /* OR-ed combination of the bits TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION, indicating
+ * current state of file. */
+{
+ Gpm_Event gpmEvent;
+ CkEvent event;
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+ int ret, type;
+
+ if (!(mask & TCL_READABLE))
+ return;
+
+ ret = Gpm_GetEvent(&gpmEvent);
+ if (ret == 0) {
+ /*
+ * GPM connection is closed; delete this file handler.
+ */
+#if (TCL_MAJOR_VERSION == 7)
+ Tcl_DeleteFileHandler((Tcl_File) mainPtr->mouseData);
+#else
+ Tcl_DeleteFileHandler((int) mainPtr->mouseData);
+#endif
+ mainPtr->mouseData = (ClientData) 0;
+ return;
+ } else if (ret == -1)
+ return;
+
+ GPM_DRAWPOINTER(&gpmEvent);
+ type = gpmEvent.type & (GPM_DOWN | GPM_UP);
+ if (type == GPM_DOWN || type == GPM_UP) {
+ event.mouse.type = type == GPM_DOWN ? CK_EV_MOUSE_DOWN :
+ CK_EV_MOUSE_UP;
+ if (gpmEvent.buttons & GPM_B_LEFT)
+ event.mouse.button = 1;
+ else if (gpmEvent.buttons & GPM_B_MIDDLE)
+ event.mouse.button = 2;
+ else if (gpmEvent.buttons & GPM_B_RIGHT)
+ event.mouse.button = 3;
+ event.mouse.x = event.mouse.rootx = gpmEvent.x - 1;
+ event.mouse.y = event.mouse.rooty = gpmEvent.y - 1;
+ event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
+ &event.mouse.y, 1);
+ Ck_HandleEvent(mainPtr, &event);
+ }
+}
+#endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
+#endif /* HAVE_GPM */
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MainLoop --
+ *
+ * Call Ck_DoOneEvent over and over again in an infinite
+ * loop as long as there exist any main windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arbitrary; depends on handlers for events.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_MainLoop()
+{
+ extern CkMainInfo *ckMainInfo;
+
+ while (ckMainInfo != NULL) {
+ Tk_DoOneEvent(0);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * BarcodeTimeout --
+ *
+ * Handle timeout while reading barcode packet.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+BarcodeTimeout(clientData)
+ ClientData clientData;
+{
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+ BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
+
+ if (bd != NULL) {
+ bd->index = -1;
+ bd->timer = (Tk_TimerToken) NULL;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkGetBarcodeData --
+ *
+ * Return data collected in barcode packet buffer.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+CkGetBarcodeData(mainPtr)
+ CkMainInfo *mainPtr;
+{
+ BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
+
+ if (bd == NULL || bd->index < 0)
+ return NULL;
+ if (bd->index >= sizeof (bd->buffer) - 1)
+ bd->buffer[sizeof (bd->buffer) - 1] = '\0';
+ else
+ bd->buffer[bd->index] = '\0';
+ return bd->buffer;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkBarcodeCmd --
+ *
+ * Minor command handler to deal with barcode reader.
+ * Called by "curses" Tcl command.
+ *
+ * Results:
+ * TCL_OK or TCL_ERROR.
+ *
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkBarcodeCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkMainInfo *mainPtr = ((CkWindow *) (clientData))->mainPtr;
+ BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
+
+ if (argc == 2) {
+ if (mainPtr->flags & CK_HAS_BARCODE) {
+ char buffer[32];
+
+ sprintf(buffer, "%d %d %d", bd->startChar, bd->endChar,
+ bd->pkttime);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ }
+ return TCL_OK;
+ } else if (argc == 3) {
+ if (strcmp(argv[2], "off") != 0)
+ goto badArgs;
+ if (mainPtr->flags & CK_HAS_BARCODE) {
+ Tk_DeleteTimerHandler(bd->timer);
+ mainPtr->flags &= ~CK_HAS_BARCODE;
+ mainPtr->barcodeData = NULL;
+ ckfree((char *) bd);
+ }
+ return TCL_OK;
+ } else if (argc == 4 || argc == 5) {
+ int start, end, pkttime;
+
+ if (Tcl_GetInt(interp, argv[2], &start) != TCL_OK ||
+ Tcl_GetInt(interp, argv[3], &end) != TCL_OK)
+ return TCL_ERROR;
+ if (argc > 4 && Tcl_GetInt(interp, argv[4], &pkttime) != TCL_OK)
+ return TCL_ERROR;
+ if (!(mainPtr->flags & CK_HAS_BARCODE)) {
+ bd = (BarcodeData *) ckalloc(sizeof (BarcodeData));
+ mainPtr->flags |= CK_HAS_BARCODE;
+ mainPtr->barcodeData = (ClientData) bd;
+ bd->pkttime = DEFAULT_BARCODE_TIMEOUT;
+ bd->timer = (Tk_TimerToken) NULL;
+ bd->delivered = 0;
+ bd->index = -1;
+ }
+ if (argc > 4 && pkttime > 50)
+ bd->pkttime = pkttime;
+ bd->startChar = start;
+ bd->endChar = end;
+ return TCL_OK;
+ } else {
+badArgs:
+ Tcl_AppendResult(interp, "bad or wrong # args: should be \"", argv[0],
+ " barcode ?off?\" or \"",
+ argv[0], " barcode startChar endChar ?timeout?\"", (char *) NULL);
+ }
+ return TCL_ERROR;
+}
--- /dev/null
+/*
+ * ckFocus.c --
+ *
+ * This file contains procedures that manage the input focus.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_FocusCmd --
+ *
+ * This procedure is invoked to process the "focus" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_FocusCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr = (CkWindow *) clientData;
+ CkWindow *newPtr, *focusWinPtr;
+
+ /*
+ * If invoked with no arguments, just return the current focus window.
+ */
+
+ if (argc == 1) {
+ focusWinPtr = winPtr->mainPtr->focusPtr;
+ if (focusWinPtr != NULL)
+ interp->result = focusWinPtr->pathName;
+ return TCL_OK;
+ }
+
+ /*
+ * If invoked with a single argument beginning with "." then focus
+ * on that window.
+ */
+
+ if (argc == 2) {
+ if (argv[1][0] == 0) {
+ return TCL_OK;
+ }
+ if (argv[1][0] == '.') {
+ newPtr = (CkWindow *) Ck_NameToWindow(interp, argv[1], winPtr);
+ if (newPtr == NULL)
+ return TCL_ERROR;
+ if (!(newPtr->flags & CK_ALREADY_DEAD))
+ Ck_SetFocus(newPtr);
+ return TCL_OK;
+ }
+ }
+
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?pathName?\"", (char *) NULL);
+ return TCL_ERROR;
+}
--- /dev/null
+/*
+ * ckFrame.c --
+ *
+ * This module implements "frame" and "toplevel" widgets for the
+ * toolkit. Frames are windows with a background color
+ * and possibly border, but no other attributes.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each
+ * frame that currently exists for this process:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the frame. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with widget.
+ * Used to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for frame's widget command. */
+ CkBorder *borderPtr; /* Structure used to draw border. */
+ int fg, bg; /* Foreground/background colors. */
+ int attr; /* Video attributes. */
+ int width; /* Width to request for window. <= 0 means
+ * don't request any size. */
+ int height; /* Height to request for window. <= 0 means
+ * don't request any size. */
+ char *takeFocus; /* Value of -takefocus option. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Frame;
+
+/*
+ * Flag bits for frames:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ */
+
+#define REDRAW_PENDING 1
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_FRAME_ATTRIB, Ck_Offset(Frame, attr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_FRAME_BG_COLOR, Ck_Offset(Frame, bg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_FRAME_BG_MONO, Ck_Offset(Frame, bg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_FRAME_FG_COLOR, Ck_Offset(Frame, fg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_FRAME_FG_MONO, Ck_Offset(Frame, fg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_BORDER, "-border", "border", "Border",
+ DEF_FRAME_BORDER, Ck_Offset(Frame, borderPtr), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COORD, "-height", "height", "Height",
+ DEF_FRAME_HEIGHT, Ck_Offset(Frame, height), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_FRAME_TAKE_FOCUS, Ck_Offset(Frame, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COORD, "-width", "width", "Width",
+ DEF_FRAME_WIDTH, Ck_Offset(Frame, width), 0},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int ConfigureFrame _ANSI_ARGS_((Tcl_Interp *interp,
+ Frame *framePtr, int argc, char **argv, int flags));
+static void DestroyFrame _ANSI_ARGS_((ClientData clientData));
+static void FrameCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
+static void DisplayFrame _ANSI_ARGS_((ClientData clientData));
+static void FrameEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static int FrameWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_FrameCmd --
+ *
+ * This procedure is invoked to process the "frame" and
+ * "toplevel" Tcl commands. See the user documentation for
+ * details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_FrameCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr = (CkWindow *) clientData;
+ CkWindow *new;
+ char *className;
+ int src, dst, toplevel;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * The code below is a special hack that extracts a few key
+ * options from the argument list now, rather than letting
+ * ConfigureFrame do it. This is necessary because we have
+ * to know the window's class before creating the window.
+ */
+
+ toplevel = *argv[0] == 't';
+ className = NULL;
+ for (src = 2, dst = 2; src < argc; src += 2) {
+ char c;
+
+ c = argv[src][1];
+ if ((c == 'c')
+ && (strncmp(argv[src], "-class", strlen(argv[src])) == 0)) {
+ className = argv[src+1];
+ } else {
+ argv[dst] = argv[src];
+ argv[dst+1] = argv[src+1];
+ dst += 2;
+ }
+ }
+ argc -= src-dst;
+
+ /*
+ * Create the window and initialize our structures and event handlers.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, winPtr, argv[1], toplevel);
+ if (new == NULL)
+ return TCL_ERROR;
+ if (className == NULL) {
+ className = Ck_GetOption(new, "class", "Class");
+ if (className == NULL) {
+ className = (toplevel) ? "Toplevel" : "Frame";
+ }
+ }
+ Ck_SetClass(new, className);
+ return CkInitFrame(interp, new, argc-2, argv+2);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkInitFrame --
+ *
+ * This procedure initializes a frame widget. It's
+ * separate from Ck_FrameCmd so that it can be used for the
+ * main window, which has already been created elsewhere.
+ *
+ * Results:
+ * A standard Tcl completion code.
+ *
+ * Side effects:
+ * A widget record gets allocated, handlers get set up, etc..
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkInitFrame(interp, winPtr, argc, argv)
+ Tcl_Interp *interp; /* Interpreter associated with the
+ * application. */
+ CkWindow *winPtr; /* Window to use for frame or
+ * top-level. Caller must already
+ * have set window's class. */
+ int argc; /* Number of configuration arguments
+ * (not including class command and
+ * window name). */
+ char *argv[]; /* Configuration arguments. */
+{
+ Frame *framePtr;
+
+ framePtr = (Frame *) ckalloc(sizeof (Frame));
+ framePtr->winPtr = winPtr;
+ framePtr->interp = interp;
+ framePtr->widgetCmd = Tcl_CreateCommand(interp,
+ framePtr->winPtr->pathName, FrameWidgetCmd,
+ (ClientData) framePtr, FrameCmdDeletedProc);
+ framePtr->borderPtr = NULL;
+ framePtr->fg = 0;
+ framePtr->bg = 0;
+ framePtr->attr = 0;
+ framePtr->width = 1;
+ framePtr->height = 1;
+ framePtr->takeFocus = NULL;
+ framePtr->flags = 0;
+ Ck_CreateEventHandler(framePtr->winPtr,
+ CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ FrameEventProc, (ClientData) framePtr);
+ if (ConfigureFrame(interp, framePtr, argc, argv, 0) != TCL_OK) {
+ Ck_DestroyWindow(framePtr->winPtr);
+ return TCL_ERROR;
+ }
+ interp->result = framePtr->winPtr->pathName;
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * FrameWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a frame widget. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+FrameWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about frame widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Frame *framePtr = (Frame *) clientData;
+ int result = TCL_OK;
+ int length;
+ char c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) framePtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, framePtr->winPtr, configSpecs,
+ (char *) framePtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, framePtr->winPtr, configSpecs,
+ (char *) framePtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, framePtr->winPtr, configSpecs,
+ (char *) framePtr, argv[2], 0);
+ } else {
+ result = ConfigureFrame(interp, framePtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure", (char *) NULL);
+ goto error;
+ }
+ Ck_Release((ClientData) framePtr);
+ return result;
+
+error:
+ Ck_Release((ClientData) framePtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyFrame --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a frame at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the frame is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyFrame(clientData)
+ ClientData clientData; /* Info about frame widget. */
+{
+ Frame *framePtr = (Frame *) clientData;
+
+ Ck_FreeOptions(configSpecs, (char *) framePtr, 0);
+ ckfree((char *) framePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FrameCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FrameCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Frame *framePtr = (Frame *) clientData;
+ CkWindow *winPtr = framePtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ framePtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureFrame --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or
+ * reconfigure) a frame widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for framePtr; old resources get freed, if there
+ * were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureFrame(interp, framePtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Frame *framePtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ if (Ck_ConfigureWidget(interp, framePtr->winPtr, configSpecs,
+ argc, argv, (char *) framePtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ Ck_SetWindowAttr(framePtr->winPtr, framePtr->fg, framePtr->bg,
+ framePtr->attr);
+ Ck_SetInternalBorder(framePtr->winPtr, framePtr->borderPtr != NULL);
+ if ((framePtr->width > 0) || (framePtr->height > 0))
+ Ck_GeometryRequest(framePtr->winPtr, framePtr->width,
+ framePtr->height);
+ if ((framePtr->winPtr->flags & CK_MAPPED)
+ && !(framePtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayFrame, (ClientData) framePtr);
+ framePtr->flags |= REDRAW_PENDING;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayFrame --
+ *
+ * This procedure is invoked to display a frame widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to display the frame in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayFrame(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Frame *framePtr = (Frame *) clientData;
+ CkWindow *winPtr = framePtr->winPtr;
+
+ framePtr->flags &= ~REDRAW_PENDING;
+ if ((framePtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+ Ck_ClearToBot(winPtr, 0, 0);
+ if (framePtr->borderPtr != NULL)
+ Ck_DrawBorder(winPtr, framePtr->borderPtr, 0, 0,
+ winPtr->width, winPtr->height);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * FrameEventProc --
+ *
+ * This procedure is invoked by the dispatcher on
+ * structure changes to a frame.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+FrameEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Frame *framePtr = (Frame *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE && framePtr->winPtr != NULL &&
+ !(framePtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayFrame, (ClientData) framePtr);
+ framePtr->flags |= REDRAW_PENDING;
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (framePtr->winPtr != NULL) {
+ framePtr->winPtr = NULL;
+ Tcl_DeleteCommand(framePtr->interp,
+ Tcl_GetCommandName(framePtr->interp, framePtr->widgetCmd));
+ }
+ if (framePtr->flags & REDRAW_PENDING)
+ Tk_CancelIdleCall(DisplayFrame, (ClientData) framePtr);
+ Ck_EventuallyFree((ClientData) framePtr, (Ck_FreeProc *) DestroyFrame);
+ }
+}
--- /dev/null
+/*
+ * ckGeometry.c --
+ *
+ * This file contains generic code for geometry management
+ * (stuff that's used by all geometry managers).
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * Data structures of the following type are used by Tk_MaintainGeometry.
+ * For each slave managed by Tk_MaintainGeometry, there is one of these
+ * structures associated with its master.
+ */
+
+typedef struct MaintainSlave {
+ CkWindow *slave; /* The slave window being positioned. */
+ CkWindow *master; /* The master that determines slave's
+ * position; it must be a descendant of
+ * slave's parent. */
+ int x, y; /* Desired position of slave relative to
+ * master. */
+ int width, height; /* Desired dimensions of slave. */
+ struct MaintainSlave *nextPtr;
+ /* Next in list of Maintains associated
+ * with master. */
+} MaintainSlave;
+
+/*
+ * For each window that has been specified as a master to
+ * Tk_MaintainGeometry, there is a structure of the following type:
+ */
+
+typedef struct MaintainMaster {
+ CkWindow *ancestor; /* The lowest ancestor of this window
+ * for which we have *not* created a
+ * StructureNotify handler. May be the
+ * same as the window itself. */
+ int checkScheduled; /* Non-zero means that there is already a
+ * call to MaintainCheckProc scheduled as
+ * an idle handler. */
+ MaintainSlave *slavePtr; /* First in list of all slaves associated
+ * with this master. */
+} MaintainMaster;
+
+/*
+ * Hash table that maps from a master's CkWindow pointer to a list of
+ * Maintains for that master:
+ */
+
+static Tcl_HashTable maintainHashTable;
+
+/*
+ * Has maintainHashTable been initialized yet?
+ */
+
+static int initialized = 0;
+
+/*
+ * Prototypes for static procedures in this file:
+ */
+
+static void MaintainCheckProc _ANSI_ARGS_((ClientData clientData));
+static void MaintainMasterProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void MaintainSlaveProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ManageGeometry --
+ *
+ * Arrange for a particular procedure to manage the geometry
+ * of a given slave window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Proc becomes the new geometry manager for tkwin, replacing
+ * any previous geometry manager. The geometry manager will
+ * be notified (by calling procedures in *mgrPtr) when interesting
+ * things happen in the future. If there was an existing geometry
+ * manager for tkwin different from the new one, it is notified
+ * by calling its lostSlaveProc.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_ManageGeometry(winPtr, mgrPtr, clientData)
+ CkWindow *winPtr; /* Window whose geometry is to
+ * be managed by proc. */
+ Ck_GeomMgr *mgrPtr; /* Static structure describing the
+ * geometry manager. This structure
+ * must never go away. */
+ ClientData clientData; /* Arbitrary one-word argument to
+ * pass to geometry manager procedures. */
+{
+ if ((winPtr->geomMgrPtr != NULL) && (mgrPtr != NULL)
+ && ((winPtr->geomMgrPtr != mgrPtr)
+ || (winPtr->geomData != clientData))
+ && (winPtr->geomMgrPtr->lostSlaveProc != NULL)) {
+ (*winPtr->geomMgrPtr->lostSlaveProc)(winPtr->geomData, winPtr);
+ }
+
+ winPtr->geomMgrPtr = mgrPtr;
+ winPtr->geomData = clientData;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GeometryRequest --
+ *
+ * This procedure is invoked by widget code to indicate
+ * its preferences about the size of a window it manages.
+ * In general, widget code should call this procedure
+ * rather than Ck_ResizeWindow.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The geometry manager for winPtr (if any) is invoked to
+ * handle the request. If possible, it will reconfigure
+ * winPtr and/or other windows to satisfy the request. The
+ * caller gets no indication of success or failure, but it
+ * will get events if the window size was actually
+ * changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_GeometryRequest(winPtr, reqWidth, reqHeight)
+ CkWindow *winPtr; /* Window that geometry information
+ * pertains to. */
+ int reqWidth, reqHeight; /* Minimum desired dimensions for
+ * window, in pixels. */
+{
+ if (reqWidth <= 0) {
+ reqWidth = 1;
+ }
+ if (reqHeight <= 0) {
+ reqHeight = 1;
+ }
+ if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) {
+ return;
+ }
+ winPtr->reqWidth = reqWidth;
+ winPtr->reqHeight = reqHeight;
+ if ((winPtr->geomMgrPtr != NULL)
+ && (winPtr->geomMgrPtr->requestProc != NULL)) {
+ (*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_SetInternalBorder --
+ *
+ * Notify relevant geometry managers that a window has an internal
+ * border of zero or one character cells and that child windows
+ * should not be placed on that border.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The border is recorded for the window, and all geometry
+ * managers of all children are notified so that can re-layout, if
+ * necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_SetInternalBorder(winPtr, onoff)
+ CkWindow *winPtr; /* Window to modify. */
+ int onoff; /* Border flag. */
+{
+ if ((onoff && (winPtr->flags & CK_BORDER)) ||
+ (!onoff && !(winPtr->flags & CK_BORDER)))
+ return;
+ if (onoff)
+ winPtr->flags |= CK_BORDER;
+ else
+ winPtr->flags &= ~CK_BORDER;
+ for (winPtr = winPtr->childList; winPtr != NULL;
+ winPtr = winPtr->nextPtr) {
+ if (winPtr->geomMgrPtr != NULL) {
+ (*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, winPtr);
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_MaintainGeometry --
+ *
+ * This procedure is invoked by geometry managers to handle slaves
+ * whose master's are not their parents. It translates the desired
+ * geometry for the slave into the coordinate system of the parent
+ * and respositions the slave if it isn't already at the right place.
+ * Furthermore, it sets up event handlers so that if the master (or
+ * any of its ancestors up to the slave's parent) is mapped, unmapped,
+ * or moved, then the slave will be adjusted to match.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Event handlers are created and state is allocated to keep track
+ * of slave. Note: if slave was already managed for master by
+ * Tk_MaintainGeometry, then the previous information is replaced
+ * with the new information. The caller must eventually call
+ * Tk_UnmaintainGeometry to eliminate the correspondence (or, the
+ * state is automatically freed when either window is destroyed).
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_MaintainGeometry(slave, master, x, y, width, height)
+ CkWindow *slave; /* Slave for geometry management. */
+ CkWindow *master; /* Master for slave; must be a descendant
+ * of slave's parent. */
+ int x, y; /* Desired position of slave within master. */
+ int width, height; /* Desired dimensions for slave. */
+{
+ Tcl_HashEntry *hPtr;
+ MaintainMaster *masterPtr;
+ register MaintainSlave *slavePtr;
+ int new, map;
+ CkWindow *ancestor, *parent;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there is already a MaintainMaster structure for the master;
+ * if not, then create one.
+ */
+
+ parent = slave->parentPtr;
+ hPtr = Tcl_CreateHashEntry(&maintainHashTable, (char *) master, &new);
+ if (!new) {
+ masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
+ } else {
+ masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster));
+ masterPtr->ancestor = master;
+ masterPtr->checkScheduled = 0;
+ masterPtr->slavePtr = NULL;
+ Tcl_SetHashValue(hPtr, masterPtr);
+ }
+
+ /*
+ * Create a MaintainSlave structure for the slave if there isn't
+ * already one.
+ */
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if (slavePtr->slave == slave) {
+ goto gotSlave;
+ }
+ }
+ slavePtr = (MaintainSlave *) ckalloc(sizeof(MaintainSlave));
+ slavePtr->slave = slave;
+ slavePtr->master = master;
+ slavePtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = slavePtr;
+ Ck_CreateEventHandler(slave,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MaintainSlaveProc, (ClientData) slavePtr);
+
+ /*
+ * Make sure that there are event handlers registered for all
+ * the windows between master and slave's parent (including master
+ * but not slave's parent). There may already be handlers for master
+ * and some of its ancestors (masterPtr->ancestor tells how many).
+ */
+
+ for (ancestor = master; ancestor != parent;
+ ancestor = ancestor->parentPtr) {
+ if (ancestor == masterPtr->ancestor) {
+ Ck_CreateEventHandler(ancestor,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MaintainMasterProc, (ClientData) masterPtr);
+ masterPtr->ancestor = ancestor->parentPtr;
+ }
+ }
+
+ /*
+ * Fill in up-to-date information in the structure, then update the
+ * window if it's not currently in the right place or state.
+ */
+
+ gotSlave:
+ slavePtr->x = x;
+ slavePtr->y = y;
+ slavePtr->width = width;
+ slavePtr->height = height;
+ map = 1;
+ for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
+ if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
+ map = 0;
+ }
+ if (ancestor == parent) {
+ if ((x != slavePtr->slave->x)
+ || (y != slavePtr->slave->y)
+ || (width != slavePtr->slave->width)
+ || (height != slavePtr->slave->height)) {
+ Ck_MoveWindow(slavePtr->slave, x, y);
+ Ck_ResizeWindow(slavePtr->slave, width, height);
+ Ck_RestackWindow(slavePtr->slave, CK_ABOVE, slavePtr->master);
+ }
+ if (map) {
+ Ck_MapWindow(slavePtr->slave);
+ } else {
+ Ck_UnmapWindow(slavePtr->slave);
+ }
+ break;
+ }
+ x += ancestor->x;
+ y += ancestor->y;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_UnmaintainGeometry --
+ *
+ * This procedure cancels a previous Ck_MaintainGeometry call,
+ * so that the relationship between slave and master is no longer
+ * maintained.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The slave is unmapped and state is released, so that slave won't
+ * track master any more. If we weren't previously managing slave
+ * relative to master, then this procedure has no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_UnmaintainGeometry(slave, master)
+ CkWindow *slave; /* Slave for geometry management. */
+ CkWindow *master; /* Master for slave; must be a descendant
+ * of slave's parent. */
+{
+ Tcl_HashEntry *hPtr;
+ MaintainMaster *masterPtr;
+ register MaintainSlave *slavePtr, *prevPtr;
+ CkWindow *ancestor;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ if (!(slave->flags & CK_ALREADY_DEAD)) {
+ Ck_UnmapWindow(slave);
+ }
+ hPtr = Tcl_FindHashEntry(&maintainHashTable, (char *) master);
+ if (hPtr == NULL) {
+ return;
+ }
+ masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
+ slavePtr = masterPtr->slavePtr;
+ if (slavePtr->slave == slave) {
+ masterPtr->slavePtr = slavePtr->nextPtr;
+ } else {
+ for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ;
+ prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) {
+ if (slavePtr == NULL) {
+ return;
+ }
+ if (slavePtr->slave == slave) {
+ prevPtr->nextPtr = slavePtr->nextPtr;
+ break;
+ }
+ }
+ }
+ Ck_DeleteEventHandler(slavePtr->slave,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MaintainSlaveProc, (ClientData) slavePtr);
+ ckfree((char *) slavePtr);
+ if (masterPtr->slavePtr == NULL) {
+ if (masterPtr->ancestor != NULL) {
+ for (ancestor = master; ; ancestor = ancestor->parentPtr) {
+ Ck_DeleteEventHandler(ancestor,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MaintainMasterProc, (ClientData) masterPtr);
+ if (ancestor == masterPtr->ancestor) {
+ break;
+ }
+ }
+ }
+ if (masterPtr->checkScheduled) {
+ Tk_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr);
+ }
+ Tcl_DeleteHashEntry(hPtr);
+ ckfree((char *) masterPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MaintainMasterProc --
+ *
+ * This procedure is invoked by the event dispatcher in
+ * response to StructureNotify events on the master or one
+ * of its ancestors, on behalf of Ck_MaintainGeometry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * It schedules a call to MaintainCheckProc, which will eventually
+ * caused the postions and mapped states to be recalculated for all
+ * the maintained slaves of the master. Or, if the master window is
+ * being deleted then state is cleaned up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MaintainMasterProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to MaintainMaster structure
+ * for the master window. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ MaintainMaster *masterPtr = (MaintainMaster *) clientData;
+ MaintainSlave *slavePtr;
+ int done;
+
+ if ((eventPtr->type == CK_EV_EXPOSE)
+ || (eventPtr->type == CK_EV_MAP)
+ || (eventPtr->type == CK_EV_UNMAP)) {
+ if (!masterPtr->checkScheduled) {
+ masterPtr->checkScheduled = 1;
+ Tk_DoWhenIdle(MaintainCheckProc, (ClientData) masterPtr);
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ /*
+ * Delete all of the state associated with this master, but
+ * be careful not to use masterPtr after the last slave is
+ * deleted, since its memory will have been freed.
+ */
+
+ done = 0;
+ do {
+ slavePtr = masterPtr->slavePtr;
+ if (slavePtr->nextPtr == NULL) {
+ done = 1;
+ }
+ Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
+ } while (!done);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MaintainSlaveProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher in
+ * response to StructureNotify events on a slave being managed
+ * by Tk_MaintainGeometry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the event is a DestroyNotify event then the Maintain state
+ * and event handlers for this slave are deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MaintainSlaveProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to MaintainSlave structure
+ * for master-slave pair. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ MaintainSlave *slavePtr = (MaintainSlave *) clientData;
+
+ if (eventPtr->type == CK_EV_DESTROY) {
+ Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MaintainCheckProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher as an
+ * idle handler, when a master or one of its ancestors has been
+ * reconfigured, mapped, or unmapped. Its job is to scan all of
+ * the slaves for the master and reposition them, map them, or
+ * unmap them as needed to maintain their geometry relative to
+ * the master.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Slaves can get repositioned, mapped, or unmapped.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MaintainCheckProc(clientData)
+ ClientData clientData; /* Pointer to MaintainMaster structure
+ * for the master window. */
+{
+ MaintainMaster *masterPtr = (MaintainMaster *) clientData;
+ MaintainSlave *slavePtr;
+ CkWindow *ancestor, *parent;
+ int x, y, map;
+
+ masterPtr->checkScheduled = 0;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ parent = slavePtr->slave->parentPtr;
+ x = slavePtr->x;
+ y = slavePtr->y;
+ map = 1;
+ for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
+ if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
+ map = 0;
+ }
+ if (ancestor == parent) {
+ if ((x != slavePtr->slave->x)
+ || (y != slavePtr->slave->y)) {
+ Ck_MoveWindow(slavePtr->slave, x, y);
+ }
+ if (map) {
+ Ck_MapWindow(slavePtr->slave);
+ } else {
+ Ck_UnmapWindow(slavePtr->slave);
+ }
+ break;
+ }
+ x += ancestor->x;
+ y += ancestor->y;
+ }
+ }
+}
--- /dev/null
+/*
+ * ckGet.c --
+ *
+ * This file contains a number of "Ck_GetXXX" procedures, which
+ * parse text strings into useful forms for Ck.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+typedef struct {
+ short fg, bg;
+} CPair;
+
+static CPair *cPairs = NULL;
+static int numPairs, newPair;
+
+/*
+ * The hash table below is used to keep track of all the Ck_Uids created
+ * so far.
+ */
+
+static Tcl_HashTable uidTable;
+static int initialized = 0;
+
+static struct {
+ char *name;
+ int value;
+} ctab[] = {
+ { "black", COLOR_BLACK },
+ { "blue", COLOR_BLUE },
+ { "cyan", COLOR_CYAN },
+ { "green", COLOR_GREEN },
+ { "magenta", COLOR_MAGENTA },
+ { "red", COLOR_RED },
+ { "white", COLOR_WHITE },
+ { "yellow", COLOR_YELLOW }
+};
+
+static struct {
+ char *name;
+ int value;
+} atab[] = {
+ { "blink", A_BLINK },
+ { "bold", A_BOLD },
+ { "dim", A_DIM },
+ { "normal", A_NORMAL },
+ { "reverse", A_REVERSE },
+ { "standout", A_STANDOUT },
+ { "underline", A_UNDERLINE }
+};
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_GetUid --
+ *
+ * Given a string, this procedure returns a unique identifier
+ * for the string.
+ *
+ * Results:
+ * This procedure returns a Ck_Uid corresponding to the "string"
+ * argument. The Ck_Uid has a string value identical to string
+ * (strcmp will return 0), but it's guaranteed that any other
+ * calls to this procedure with a string equal to "string" will
+ * return exactly the same result (i.e. can compare Ck_Uid
+ * *values* directly, without having to call strcmp on what they
+ * point to).
+ *
+ * Side effects:
+ * New information may be entered into the identifier table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Ck_Uid
+Ck_GetUid(string)
+ char *string; /* String to convert. */
+{
+ int dummy;
+
+ if (!initialized) {
+ Tcl_InitHashTable(&uidTable, TCL_STRING_KEYS);
+ initialized = 1;
+ }
+ return (Ck_Uid) Tcl_GetHashKey(&uidTable,
+ Tcl_CreateHashEntry(&uidTable, string, &dummy));
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_GetColor --
+ *
+ * Given a color specification, return curses color value.
+ *
+ * Results:
+ * TCL_OK if color found, curses color in *colorPtr.
+ * TCL_ERROR if color not found; interp->result contains an
+ * error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+int
+Ck_GetColor(interp, name, colorPtr)
+ Tcl_Interp *interp;
+ char *name;
+ int *colorPtr;
+{
+ int i, len;
+
+ len = strlen(name);
+ if (len > 0)
+ for (i = 0; i < sizeof (ctab) / sizeof (ctab[0]); i++)
+ if (strncmp(name, ctab[i].name, len) == 0) {
+ if (colorPtr != NULL)
+ *colorPtr = ctab[i].value;
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "bad color \"", name, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_NameOfColor --
+ *
+ * Given a curses color, return its name.
+ *
+ * Results:
+ * String: name of color, or NULL if no valid color.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+char *
+Ck_NameOfColor(color)
+ int color; /* Curses color to get name for */
+{
+ int i;
+
+ for (i = 0; i < sizeof (ctab) / sizeof (ctab[0]); i++)
+ if (ctab[i].value == color)
+ return ctab[i].name;
+ return NULL;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_GetAttr --
+ *
+ * Given an attribute specification, return attribute value.
+ *
+ * Results:
+ * TCL_OK if color found, curses color in *colorPtr.
+ * TCL_ERROR if color not found; interp->result contains an
+ * error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+int
+Ck_GetAttr(interp, name, attrPtr)
+ Tcl_Interp *interp;
+ char *name;
+ int *attrPtr;
+{
+ int i, k, len, largc;
+ char **largv;
+
+ if (Tcl_SplitList(interp, name, &largc, &largv) != TCL_OK)
+ return TCL_ERROR;
+ if (attrPtr != NULL)
+ *attrPtr = A_NORMAL;
+ if (largc > 1 || (largc == 1 && largv[0][0] != '\0')) {
+ for (i = 0; i < largc; i++) {
+ len = strlen(largv[i]);
+ if (len > 0) {
+ for (k = 0; k < sizeof (atab) / sizeof (atab[0]); k++)
+ if (strncmp(largv[i], atab[k].name, len) == 0) {
+ if (attrPtr != NULL)
+ *attrPtr |= atab[k].value;
+ break;
+ }
+ if (k >= sizeof (atab) / sizeof (atab[0])) {
+ Tcl_AppendResult(interp, "bad attribute \"",
+ name, "\"", (char *) NULL);
+ ckfree((char *) largv);
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+ ckfree((char *) largv);
+ return TCL_OK;
+}
+\f
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_NameOfAttr --
+ *
+ * Given an attribute value, return its textual specification.
+ *
+ * Results:
+ * interp->result contains result or message.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+char *
+Ck_NameOfAttr(attr)
+ int attr;
+{
+ int i;
+ char *result;
+ Tcl_DString list;
+
+ Tcl_DStringInit(&list);
+ if (attr == -1 || attr == A_NORMAL)
+ Tcl_DStringAppendElement(&list, "normal");
+ else {
+ for (i = 0; i < sizeof (atab) / sizeof (atab[0]); i++)
+ if (attr & atab[i].value)
+ Tcl_DStringAppendElement(&list, atab[i].name);
+ }
+ result = ckalloc(Tcl_DStringLength(&list) + 1);
+ strcpy(result, Tcl_DStringValue(&list));
+ Tcl_DStringFree(&list);
+ return result;
+}
+/*
+ *------------------------------------------------------------------------
+ *
+ * Ck_GetColorPair --
+ *
+ * Given background/foreground curses colors, a color pair
+ * is allocated and returned.
+ *
+ * Results:
+ * TCL_OK if color found, curses color in *colorPtr.
+ * TCL_ERROR if color not found; interp->result contains an
+ * error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+int
+Ck_GetPair(winPtr, fg, bg)
+ CkWindow *winPtr;
+ int fg, bg;
+{
+ int i;
+
+ if (!(winPtr->mainPtr->flags & CK_HAS_COLOR))
+ return COLOR_PAIR(0);
+ if (cPairs == NULL) {
+ cPairs = (CPair *) ckalloc(sizeof (CPair) * (COLOR_PAIRS + 2));
+ numPairs = 0;
+ newPair = 1;
+ }
+ for (i = 1; i < numPairs; i++)
+ if (cPairs[i].fg == fg && cPairs[i].bg == bg)
+ return COLOR_PAIR(i);
+ i = newPair;
+ cPairs[i].fg = fg;
+ cPairs[i].bg = bg;
+ init_pair((short) i, (short) fg, (short) bg);
+ if (++newPair >= COLOR_PAIRS)
+ newPair = 1;
+ else
+ numPairs = newPair;
+ return COLOR_PAIR(i);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetAnchor --
+ *
+ * Given a string, return the corresponding Ck_Anchor.
+ *
+ * Results:
+ * The return value is a standard Tcl return result. If
+ * TCL_OK is returned, then everything went well and the
+ * position is stored at *anchorPtr; otherwise TCL_ERROR
+ * is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_GetAnchor(interp, string, anchorPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ char *string; /* String describing a direction. */
+ Ck_Anchor *anchorPtr; /* Where to store Ck_Anchor corresponding
+ * to string. */
+{
+ switch (string[0]) {
+ case 'n':
+ if (string[1] == 0) {
+ *anchorPtr = CK_ANCHOR_N;
+ return TCL_OK;
+ } else if ((string[1] == 'e') && (string[2] == 0)) {
+ *anchorPtr = CK_ANCHOR_NE;
+ return TCL_OK;
+ } else if ((string[1] == 'w') && (string[2] == 0)) {
+ *anchorPtr = CK_ANCHOR_NW;
+ return TCL_OK;
+ }
+ goto error;
+ case 's':
+ if (string[1] == 0) {
+ *anchorPtr = CK_ANCHOR_S;
+ return TCL_OK;
+ } else if ((string[1] == 'e') && (string[2] == 0)) {
+ *anchorPtr = CK_ANCHOR_SE;
+ return TCL_OK;
+ } else if ((string[1] == 'w') && (string[2] == 0)) {
+ *anchorPtr = CK_ANCHOR_SW;
+ return TCL_OK;
+ } else {
+ goto error;
+ }
+ case 'e':
+ if (string[1] == 0) {
+ *anchorPtr = CK_ANCHOR_E;
+ return TCL_OK;
+ }
+ goto error;
+ case 'w':
+ if (string[1] == 0) {
+ *anchorPtr = CK_ANCHOR_W;
+ return TCL_OK;
+ }
+ goto error;
+ case 'c':
+ if (strncmp(string, "center", strlen(string)) == 0) {
+ *anchorPtr = CK_ANCHOR_CENTER;
+ return TCL_OK;
+ }
+ goto error;
+ }
+
+ error:
+ Tcl_AppendResult(interp, "bad anchor position \"", string,
+ "\": must be n, ne, e, se, s, sw, w, nw, or center",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_NameOfAnchor --
+ *
+ * Given a Ck_Anchor, return the string that corresponds
+ * to it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+Ck_NameOfAnchor(anchor)
+ Ck_Anchor anchor; /* Anchor for which identifying string
+ * is desired. */
+{
+ switch (anchor) {
+ case CK_ANCHOR_N: return "n";
+ case CK_ANCHOR_NE: return "ne";
+ case CK_ANCHOR_E: return "e";
+ case CK_ANCHOR_SE: return "se";
+ case CK_ANCHOR_S: return "s";
+ case CK_ANCHOR_SW: return "sw";
+ case CK_ANCHOR_W: return "w";
+ case CK_ANCHOR_NW: return "nw";
+ case CK_ANCHOR_CENTER: return "center";
+ }
+ return "unknown anchor position";
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetJustify --
+ *
+ * Given a string, return the corresponding Ck_Justify.
+ *
+ * Results:
+ * The return value is a standard Tcl return result. If
+ * TCL_OK is returned, then everything went well and the
+ * justification is stored at *justifyPtr; otherwise
+ * TCL_ERROR is returned and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_GetJustify(interp, string, justifyPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ char *string; /* String describing a justification style. */
+ Ck_Justify *justifyPtr; /* Where to store Ck_Justify corresponding
+ * to string. */
+{
+ int c, length;
+
+ c = string[0];
+ length = strlen(string);
+
+ if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *justifyPtr = CK_JUSTIFY_LEFT;
+ return TCL_OK;
+ }
+ if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *justifyPtr = CK_JUSTIFY_RIGHT;
+ return TCL_OK;
+ }
+ if ((c == 'c') && (strncmp(string, "center", length) == 0)) {
+ *justifyPtr = CK_JUSTIFY_CENTER;
+ return TCL_OK;
+ }
+ if ((c == 'f') && (strncmp(string, "fill", length) == 0)) {
+ *justifyPtr = CK_JUSTIFY_FILL;
+ return TCL_OK;
+ }
+
+ Tcl_AppendResult(interp, "bad justification \"", string,
+ "\": must be left, right, center, or fill",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_NameOfJustify --
+ *
+ * Given a Ck_Justify, return the string that corresponds
+ * to it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+Ck_NameOfJustify(justify)
+ Ck_Justify justify; /* Justification style for which
+ * identifying string is desired. */
+{
+ switch (justify) {
+ case CK_JUSTIFY_LEFT: return "left";
+ case CK_JUSTIFY_RIGHT: return "right";
+ case CK_JUSTIFY_CENTER: return "center";
+ case CK_JUSTIFY_FILL: return "fill";
+ }
+ return "unknown justification style";
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetCoord --
+ *
+ * Given a string, return the coordinate that corresponds
+ * to it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_GetCoord(interp, winPtr, string, intPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ CkWindow *winPtr; /* Window (not used). */
+ char *string; /* String to convert. */
+ int *intPtr; /* Place to store converted result. */
+{
+ int value;
+
+ if (Tcl_GetInt(interp, string, &value) != TCL_OK)
+ return TCL_ERROR;
+ if (value < 0) {
+ Tcl_AppendResult(interp, "coordinate may not be negative",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ *intPtr = value;
+ return TCL_OK;
+}
--- /dev/null
+/*
+ * ckGrid.c --
+ *
+ * Grid based geometry manager.
+ *
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * LayoutInfo structure. We shouldn't be using hard-wired limits!
+ */
+
+#define MAXGRIDSIZE 128
+#ifndef MAXINT
+# define MAXINT 0x7fff
+#endif
+#define MINWEIGHT 0.0001 /* weight totals < this are considered to be zero */
+
+/*
+ * Special characters to support relative layouts
+ */
+
+#define REL_SKIP 'x' /* skip this column */
+#define REL_HORIZ '-' /* extend previous widget horizontally */
+#define REL_VERT '^' /* extend previous widget verticallly */
+
+/*
+ * structure to hold collected constraints temporarily:
+ * needs to use a "Constrain" thingy
+ */
+
+typedef struct {
+ int width, height; /* number of cells horizontally, vertically */
+ int lastRow; /* last cell with a window in it */
+ int minWidth[MAXGRIDSIZE]; /* largest minWidth in each column */
+ int minHeight[MAXGRIDSIZE]; /* largest minHeight in each row */
+ double weightX[MAXGRIDSIZE];/* largest weight in each column */
+ double weightY[MAXGRIDSIZE];/* largest weight in each row */
+} LayoutInfo;
+
+/* structure for holding row and column constraints */
+
+typedef struct {
+ int used; /* maximum element used */
+ int max; /* maximum element allocated */
+ int *minsize; /* array of minimum column/row sizes */
+ double *weight; /* array of column/row weights */
+} Constrain;
+
+/* For each window that the gridbag cares about (either because
+ * the window is managed by the gridbag or because the window
+ * has slaves that are managed by the gridbag), there is a
+ * structure of the following type:
+ */
+
+typedef struct GridBag {
+ CkWindow *winPtr; /* Pointer to window. NULL means that
+ * the window has been deleted, but the
+ * packet hasn't had a chance to clean up
+ * yet because the structure is still in
+ * use. */
+ struct GridBag *masterPtr; /* Master window within which this window
+ * is managed (NULL means this window
+ * isn't managed by the gridbag). */
+ struct GridBag *nextPtr; /* Next window managed within same
+ * parent. List is priority-ordered:
+ * first on list gets layed out first. */
+ struct GridBag *slavePtr; /* First in list of slaves managed
+ * inside this window (NULL means
+ * no gridbag slaves). */
+
+ int gridColumn, gridRow;
+ int gridWidth, gridHeight;
+
+ int tempX, tempY;
+ int tempWidth, tempHeight;
+
+ double weightX, weightY;
+ int minWidth, minHeight;
+
+ int padX, padY; /* Total additional pixels to leave around the
+ * window (half of this space is left on each
+ * side). This is space *outside* the window:
+ * we'll allocate extra space in frame but
+ * won't enlarge window). */
+ int iPadX, iPadY; /* Total extra pixels to allocate inside the
+ * window (half this amount will appear on
+ * each side). */
+ int startx, starty; /* starting location of layout */
+ int *abortPtr; /* If non-NULL, it means that there is a nested
+ * call to ArrangeGrid already working on
+ * this window. *abortPtr may be set to 1 to
+ * abort that nested call. This happens, for
+ * example, if winPtr or any of its slaves
+ * is deleted. */
+ int flags; /* Miscellaneous flags; see below
+ * for definitions. */
+
+ Constrain row, column; /* column and row constraints */
+
+ int valid;
+ LayoutInfo *layoutCache;
+} GridBag;
+
+/*
+ * Flag values for GridBag structures:
+ *
+ * REQUESTED_RELAYOUT: 1 means a Tk_DoWhenIdle request
+ * has already been made to re-arrange
+ * all the slaves of this window.
+ * STICK_NORTH 1 means this window sticks to the edgth of its
+ * STICK_EAST cavity
+ * STICK_SOUTH
+ * STICK_WEST
+ *
+ * DONT_PROPAGATE: 1 means don't set this window's requested
+ * size. 0 means if this window is a master
+ * then Ck will set its requested size to fit
+ * the needs of its slaves.
+ */
+
+#define STICK_NORTH 1
+#define STICK_EAST 2
+#define STICK_SOUTH 4
+#define STICK_WEST 8
+#define STICK_ALL (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
+
+#define REQUESTED_RELAYOUT 16
+#define DONT_PROPAGATE 32
+
+/*
+ * Hash table used to map from CkWindow pointers to corresponding
+ * GridBag structures:
+ */
+
+static Tcl_HashTable gridBagHashTable;
+
+/*
+ * Have statics in this module been initialized?
+ */
+
+static initialized = 0;
+
+/*
+ * Prototypes for procedures used only in this file:
+ */
+
+static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
+static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, int argc, char *argv[]));
+static void DestroyGridBag _ANSI_ARGS_((char *memPtr));
+static void GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
+static GridBag * GetGridBag _ANSI_ARGS_((CkWindow *winPtr));
+static void GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
+ LayoutInfo *r));
+static void GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
+ LayoutInfo *info, int *minw, int *minh));
+static void GridBagStructureProc _ANSI_ARGS_((
+ ClientData clientData, CkEvent *eventPtr));
+static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+static void GridReqProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+static void GridBagStructureProc _ANSI_ARGS_((
+ ClientData clientData, CkEvent *eventPtr));
+static void StickyToString _ANSI_ARGS_((int flags, char *result));
+static int StringToSticky _ANSI_ARGS_((char *string));
+static void Unlink _ANSI_ARGS_((GridBag *gridPtr));
+
+static Ck_GeomMgr gridMgrType = {
+ "grid", /* name */
+ GridReqProc, /* requestProc */
+ GridLostSlaveProc, /* lostSlaveProc */
+};
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GridCmd --
+ *
+ * This procedure is invoked to process the "grid" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_GridCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr = (CkWindow *) clientData;
+ size_t length;
+ char c;
+
+ if ((argc >= 2) && (argv[1][0] == '.')) {
+ return ConfigureSlaves(interp, winPtr, argc-1, argv+1);
+ }
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
+ CkWindow *master;
+ GridBag *masterPtr;
+ int row, column;
+ int i, x, y;
+ int prevX, prevY;
+ int width, height;
+ double weight;
+ int diff;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "Wrong number of arguments: ",
+ "must be \"",argv[0],
+ " bbox <master> <column> <row>\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(master);
+
+ /* make sure the grid is up to snuff */
+
+ while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
+ Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
+ ArrangeGrid((ClientData) masterPtr);
+ }
+ GetCachedLayoutInfo(masterPtr);
+
+ if (row < 0 || column < 0) {
+ *interp->result = '\0';
+ return TCL_OK;
+ }
+ if (column >= masterPtr->layoutCache->width ||
+ row >= masterPtr->layoutCache->height) {
+ *interp->result = '\0';
+ return TCL_OK;
+ }
+ x = masterPtr->startx;
+ y = masterPtr->starty;
+ GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
+
+ diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
+ for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
+ weight += masterPtr->layoutCache->weightX[i];
+
+ prevX = 0; /* Needed to prevent gcc warning. */
+ for (i=0; i<=column; i++) {
+ int dx = 0;
+ if (weight > MINWEIGHT) {
+ dx = (int)((((double)diff) *
+ masterPtr->layoutCache->weightX[i]) / weight);
+ }
+ prevX = x;
+ x += masterPtr->layoutCache->minWidth[i] + dx;
+ }
+ diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
+ for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
+ weight += masterPtr->layoutCache->weightY[i];
+ }
+ prevY = 0; /* Needed to prevent gcc warning. */
+ for (i=0; i<=row; i++) {
+ int dy = 0;
+ if (weight > MINWEIGHT) {
+ dy = (int)((((double)diff) *
+ masterPtr->layoutCache->weightY[i]) / weight);
+ }
+ prevY = y;
+ y += masterPtr->layoutCache->minHeight[i] + dy;
+ }
+ sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argv[2][0] != '.') {
+ Tcl_AppendResult(interp, "bad argument \"", argv[2],
+ "\": must be name of window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return ConfigureSlaves(interp, winPtr, argc-2, argv+2);
+ } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
+ CkWindow *slave;
+ GridBag *slavePtr;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ slave = Ck_NameToWindow(interp, argv[i], winPtr);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetGridBag(slave);
+ if (slavePtr->masterPtr != NULL) {
+ Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
+ (ClientData) NULL);
+ Unlink(slavePtr);
+ Ck_UnmapWindow(slavePtr->winPtr);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
+ GridBag *slavePtr;
+ CkWindow *slave;
+ char buffer[64];
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " info window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slave = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetGridBag(slave);
+ if (slavePtr->masterPtr == NULL) {
+ interp->result[0] = '\0';
+ return TCL_OK;
+ }
+
+#if 0
+ Tcl_AppendElement(interp, "-in");
+ Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
+#endif
+ sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
+ slavePtr->gridColumn, slavePtr->gridRow,
+ slavePtr->gridWidth, slavePtr->gridHeight);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
+ slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
+ slavePtr->padY/2);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ StickyToString(slavePtr->flags,buffer);
+ Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
+#if 0
+ sprintf(buffer, " -weightx %.2f -weighty %.2f",
+ slavePtr->weightX, slavePtr->weightY);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+#endif
+ } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
+ CkWindow *master;
+ GridBag *masterPtr;
+ int propagate;
+
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " propagate window ?boolean?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(master);
+ if (argc == 3) {
+ interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (propagate) {
+ masterPtr->flags &= ~DONT_PROPAGATE;
+
+ /*
+ * Re-arrange the master to allow new geometry information to
+ * propagate upwards to the master\'s master.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->valid = 0;
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ }
+ } else {
+ masterPtr->flags |= DONT_PROPAGATE;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
+ CkWindow *master;
+ GridBag *masterPtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " size window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL)
+ return TCL_ERROR;
+ masterPtr = GetGridBag(master);
+ GetCachedLayoutInfo(masterPtr);
+
+ sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
+ masterPtr->layoutCache->height);
+ } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
+ CkWindow *master;
+ GridBag *masterPtr, *slavePtr;
+ int i, value;
+ int row = -1, column = -1;
+
+ if (argc < 3 || argc%2 ==0) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " slaves window ?-option value...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ for (i=3; i<argc; i+=2) {
+ if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
+ Tcl_AppendResult(interp, "Invalid args: should be \"",
+ argv[0], " slaves window ?-option value...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_AppendResult(interp, argv[i],
+ " is an invalid value: should NOT be < 0",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (strncmp(argv[i], "-column", length) == 0) {
+ column = value;
+ } else if (strncmp(argv[i], "-row", length) == 0) {
+ row = value;
+ } else {
+ Tcl_AppendResult(interp, argv[i],
+ " is an invalid option: should be \"",
+ "-row, -column\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(master);
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if (column>=0 && (slavePtr->gridColumn > column
+ || slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
+ continue;
+ }
+ if (row>=0 && (slavePtr->gridRow > row ||
+ slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
+ continue;
+ }
+ Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
+ }
+
+ /*
+ * grid columnconfigure <master> <index> -option
+ * grid columnconfigure <master> <index> -option value -option value
+ * grid rowconfigure <master> <index> -option
+ * grid rowconfigure <master> <index> -option value -option value
+ */
+
+ } else if (((c == 'c') &&
+ (strncmp(argv[1], "columnconfigure", length) == 0)) ||
+ ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
+ CkWindow *master;
+ GridBag *masterPtr;
+ Constrain *con;
+ int index, i, size;
+ double weight;
+
+ if (argc != 5 && (argc < 5 || argc%2 == 1)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ", argv[1], " master index ?-option value...?\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(master);
+ con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
+
+ if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (index < 0 || index >= MAXGRIDSIZE) {
+ Tcl_AppendResult(interp, argv[3], " is out of range",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * make sure the row/column constraint array is allocated. This
+ * Should be changed to avoid hard-wired limits. We'll wimp out
+ * for now.
+ */
+
+ if (con->max == 0) {
+ unsigned int size;
+ con->max = MAXGRIDSIZE;
+ con->used = 0;
+
+ size = MAXGRIDSIZE * sizeof(con->minsize[0]);
+ con->minsize = (int *) ckalloc(size);
+ memset(con->minsize, 0, size);
+
+ size = MAXGRIDSIZE * sizeof(con->weight[0]);
+ con->weight = (double *) ckalloc(size);
+ memset(con->weight, 0, size);
+ }
+
+ for (i=4; i<argc; i+=2) {
+ if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
+ Tcl_AppendResult(interp, "Invalid arg: \"",
+ argv[0], "\" expecting -minsize or -weight",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (strncmp(argv[i], "-minsize", length) == 0) {
+ if (argc == 5) {
+ size = con->used <= index ? 0 : con->minsize[index];
+ sprintf(interp->result, "%d", size);
+ } else if (Ck_GetCoord(interp, master, argv[i + 1], &size)
+ != TCL_OK) {
+ return TCL_ERROR;
+ } else {
+ con->minsize[index] = size;
+ if (size > 0 && index >= con->used) {
+ con->used = index+1;
+ } else if (size == 0 && index+1 == con->used) {
+ while (index >= 0 && (con->minsize[index]==0) &&
+ (con->weight[index] == 0.0)) {
+ index--;
+ }
+ con->used = index + 1;
+ }
+ }
+ } else if (strncmp(argv[i], "-weight", length) == 0) {
+ if (argc == 5) {
+ weight = con->used <= index ? 0 : con->weight[index];
+ sprintf(interp->result, "%.2f", weight);
+ } else if (Tcl_GetDouble(interp, argv[i+1], &weight)
+ != TCL_OK) {
+ return TCL_ERROR;
+ } else {
+ con->weight[index] = weight;
+ if (weight > MINWEIGHT && index >= con->used) {
+ con->used = index+1;
+ } else if (weight == 0.0 && index+1 == con->used) {
+ while (index >= 0 && (con->minsize[index]==0) &&
+ (con->weight[index] == 0.0)) {
+ index--;
+ }
+ con->used = index + 1;
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, argv[i],
+ " is an invalid option: should be \"",
+ "-minsize, -weight\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /* if we changed a property, re-arrange the table */
+
+ if (argc != 5) {
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->valid = 0;
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ }
+ }
+ } else if ((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
+ CkWindow *master;
+ GridBag *masterPtr;
+ int x, y, i, j, w, h;
+ int width, height;
+ double weight;
+ int diff;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " location master x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ master = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(master);
+
+ if (Ck_GetCoord(interp, master, argv[3], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Ck_GetCoord(interp, master, argv[4], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /* make sure the grid is up to snuff */
+
+ while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
+ Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
+ ArrangeGrid((ClientData) masterPtr);
+ }
+ GetCachedLayoutInfo(masterPtr);
+ GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
+
+ diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
+ for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
+ weight += masterPtr->layoutCache->weightX[i];
+ }
+ w = masterPtr->startx;
+ if (w > x) {
+ i = -1;
+ } else {
+ for (i = 0; i < masterPtr->layoutCache->width; i++) {
+ int dx = 0;
+ if (weight > MINWEIGHT) {
+ dx = (int)((((double)diff) *
+ masterPtr->layoutCache->weightX[i]) / weight);
+ }
+ w += masterPtr->layoutCache->minWidth[i] + dx;
+ if (w > x) {
+ break;
+ }
+ }
+ }
+
+ diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
+ for (weight = 0.0, j = 0; j < masterPtr->layoutCache->height; j++)
+ weight += masterPtr->layoutCache->weightY[j];
+ h = masterPtr->starty;
+ if (h > y) {
+ j = -1;
+ } else {
+ for (j = 0; j < masterPtr->layoutCache->height; j++) {
+ int dy = 0;
+ if (weight > MINWEIGHT) {
+ dy = (int)((((double)diff) *
+ masterPtr->layoutCache->weightY[j]) / weight);
+ }
+ h += masterPtr->layoutCache->minHeight[j] + dy;
+ if (h > y) {
+ break;
+ }
+ }
+ }
+ sprintf(interp->result, "%d %d", i, j);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be bbox, columnconfigure, configure, forget, ",
+ "info, location, propagate, rowconfigure, size, or slaves",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GridReqProc --
+ *
+ * This procedure is invoked by Ck_GeometryRequest for
+ * windows managed by the gridbag.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for winPtr, and all its managed siblings, to
+ * be re-arranged at the next idle point.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+GridReqProc(clientData, winPtr)
+ ClientData clientData; /* GridBag's information about
+ * window that got new preferred
+ * geometry. */
+ CkWindow *winPtr; /* Other Ck-related information
+ * about the window. */
+{
+ GridBag *gridPtr = (GridBag *) clientData;
+
+ gridPtr = gridPtr->masterPtr;
+ gridPtr->valid = 0;
+ if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GridLostSlaveProc --
+ *
+ * This procedure is invoked by Ck whenever some other geometry
+ * claims control over a slave that used to be managed by us.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets all grid-related information about the slave.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+GridLostSlaveProc(clientData, winPtr)
+ ClientData clientData; /* GridBag structure for slave window that
+ * was stolen away. */
+ CkWindow *winPtr; /* Pointer to the slave window. */
+{
+ GridBag *slavePtr = (GridBag *) clientData;
+
+ if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
+ }
+ Unlink(slavePtr);
+ Ck_UnmapWindow(slavePtr->winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Fill in an instance of the above structure for the current set
+ * of managed children. This requires two passes through the
+ * set of children, first to figure out what cells they occupy
+ * and how many rows and columns there are, and then to distribute
+ * the weights and min sizes amoung the rows/columns.
+ *
+ * This also caches the minsizes for all the children when they are
+ * first encountered.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+GetLayoutInfo(masterPtr, r)
+ GridBag *masterPtr;
+ LayoutInfo *r;
+{
+ GridBag *slavePtr;
+ int i, k, px, py, pixels_diff, nextSize;
+ double weight_diff, weight;
+ int curX, curY, curWidth, curHeight, curRow, curCol;
+ int xMax[MAXGRIDSIZE];
+ int yMax[MAXGRIDSIZE];
+
+ /*
+ * Pass #1
+ *
+ * Figure out the dimensions of the layout grid.
+ */
+
+ r->width = r->height = 0;
+ curRow = curCol = -1;
+ memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
+ memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ curX = slavePtr->gridColumn;
+ curY = slavePtr->gridRow;
+ curWidth = slavePtr->gridWidth;
+ curHeight = slavePtr->gridHeight;
+
+ /* Adjust the grid width and height */
+ for (px = curX + curWidth; r->width < px; r->width++) {
+ /* Null body. */
+ }
+ for (py = curY + curHeight; r->height < py; r->height++) {
+ /* Null body. */
+ }
+
+ /* Adjust the xMax and yMax arrays */
+ for (i = curX; i < (curX + curWidth); i++) {
+ yMax[i] = py;
+ }
+ for (i = curY; i < (curY + curHeight); i++) {
+ xMax[i] = px;
+ }
+
+ /* Cache the current slave's size. */
+ slavePtr->minWidth = slavePtr->winPtr->reqWidth;
+ slavePtr->minHeight = slavePtr->winPtr->reqHeight;
+ }
+
+ /*
+ * Apply minimum row/column dimensions
+ */
+ if (r->width < masterPtr->column.used) {
+ r->width = masterPtr->column.used;
+ }
+ r->lastRow = r->height;
+ if (r->height < masterPtr->row.used) {
+ r->height = masterPtr->row.used;
+ }
+
+ /*
+ * Pass #2
+ */
+
+ curRow = curCol = -1;
+ memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
+ memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ curX = slavePtr->gridColumn;
+ curY = slavePtr->gridRow;
+ curWidth = slavePtr->gridWidth;
+ curHeight = slavePtr->gridHeight;
+
+ px = curX + curWidth;
+ py = curY + curHeight;
+
+ for (i = curX; i < (curX + curWidth); i++) {
+ yMax[i] = py;
+ }
+ for (i = curY; i < (curY + curHeight); i++) {
+ xMax[i] = px;
+ }
+
+ /* Assign the new values to the gridbag slave */
+ slavePtr->tempX = curX;
+ slavePtr->tempY = curY;
+ slavePtr->tempWidth = curWidth;
+ slavePtr->tempHeight = curHeight;
+ }
+
+ /*
+ * Pass #3
+ *
+ * Distribute the minimun widths and weights:
+ */
+
+ /* Initialize arrays to zero */
+ memset(r->minWidth, 0, r->width * sizeof(int));
+ memset(r->minHeight, 0, r->height * sizeof(int));
+ memset(r->weightX, 0, r->width * sizeof(double));
+ memset(r->weightY, 0, r->height * sizeof(double));
+ nextSize = MAXINT;
+
+ for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ if (slavePtr->tempWidth == i) {
+ px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
+
+ /*
+ * Figure out if we should use this slave's weight.
+ * If the weight is less than the total weight spanned by
+ * the width of the cell, then discard the weight.
+ * Otherwise split it the difference
+ * according to the existing weights.
+ */
+
+ weight_diff = slavePtr->weightX;
+ for (k = slavePtr->tempX; k < px; k++)
+ weight_diff -= r->weightX[k];
+ if (weight_diff > 0.0) {
+ weight = 0.0;
+ for (k = slavePtr->tempX; k < px; k++)
+ weight += r->weightX[k];
+ for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
+ double wt = r->weightX[k];
+ double dx = (wt * weight_diff) / weight;
+ r->weightX[k] += dx;
+ weight_diff -= dx;
+ weight -= wt;
+ }
+ /* Assign the remainder to the rightmost cell */
+ r->weightX[px-1] += weight_diff;
+ }
+
+ /*
+ * Calculate the minWidth array values.
+ * First, figure out how wide the current slave needs to be.
+ * Then, see if it will fit within the current minWidth values.
+ * If it won't fit, add the difference according to the
+ * weightX array.
+ */
+
+ pixels_diff = slavePtr->minWidth + slavePtr->padX +
+ slavePtr->iPadX;
+ for (k = slavePtr->tempX; k < px; k++)
+ pixels_diff -= r->minWidth[k];
+ if (pixels_diff > 0) {
+ weight = 0.0;
+ for (k = slavePtr->tempX; k < px; k++)
+ weight += r->weightX[k];
+ for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
+ double wt = r->weightX[k];
+ int dx = (int)((wt * ((double)pixels_diff)) / weight);
+ r->minWidth[k] += dx;
+ pixels_diff -= dx;
+ weight -= wt;
+ }
+ /* Any leftovers go into the rightmost cell */
+ r->minWidth[px-1] += pixels_diff;
+ }
+ } else if (slavePtr->tempWidth > i &&
+ slavePtr->tempWidth < nextSize)
+ nextSize = slavePtr->tempWidth;
+
+ if (slavePtr->tempHeight == i) {
+ py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
+
+ /*
+ * Figure out if we should use this slave's weight.
+ * If the weight is less than the total weight spanned by
+ * the height of the cell, then discard the weight.
+ * Otherwise split it the difference according to the
+ * existing weights.
+ */
+
+ weight_diff = slavePtr->weightY;
+ for (k = slavePtr->tempY; k < py; k++)
+ weight_diff -= r->weightY[k];
+ if (weight_diff > 0.0) {
+ weight = 0.0;
+ for (k = slavePtr->tempY; k < py; k++)
+ weight += r->weightY[k];
+ for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
+ double wt = r->weightY[k];
+ double dy = (wt * weight_diff) / weight;
+ r->weightY[k] += dy;
+ weight_diff -= dy;
+ weight -= wt;
+ }
+ /* Assign the remainder to the bottom cell */
+ r->weightY[py-1] += weight_diff;
+ }
+
+ /*
+ * Calculate the minHeight array values.
+ * First, figure out how tall the current slave needs to be.
+ * Then, see if it will fit within the current minHeight
+ * values. If it won't fit, add the difference according to
+ * the weightY array.
+ */
+
+ pixels_diff = slavePtr->minHeight + slavePtr->padY +
+ slavePtr->iPadY;
+ for (k = slavePtr->tempY; k < py; k++)
+ pixels_diff -= r->minHeight[k];
+ if (pixels_diff > 0) {
+ weight = 0.0;
+ for (k = slavePtr->tempY; k < py; k++)
+ weight += r->weightY[k];
+ for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
+ double wt = r->weightY[k];
+ int dy = (int)((wt * ((double)pixels_diff)) / weight);
+ r->minHeight[k] += dy;
+ pixels_diff -= dy;
+ weight -= wt;
+ }
+ /* Any leftovers go into the bottom cell */
+ r->minHeight[py-1] += pixels_diff;
+ }
+ } else if (slavePtr->tempHeight > i &&
+ slavePtr->tempHeight < nextSize)
+ nextSize = slavePtr->tempHeight;
+ }
+ }
+
+ /*
+ * Apply minimum row/column dimensions
+ */
+ for (i=0; i<masterPtr->column.used; i++) {
+ if (r->minWidth[i] < masterPtr->column.minsize[i])
+ r->minWidth[i] = masterPtr->column.minsize[i];
+ if (r->weightX[i] < masterPtr->column.weight[i])
+ r->weightX[i] = masterPtr->column.weight[i];
+ }
+ for (i=0; i<masterPtr->row.used; i++) {
+ if (r->minHeight[i] < masterPtr->row.minsize[i])
+ r->minHeight[i] = masterPtr->row.minsize[i];
+ if (r->weightY[i] < masterPtr->row.weight[i])
+ r->weightY[i] = masterPtr->row.weight[i];
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Cache the layout info after it is calculated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+GetCachedLayoutInfo(masterPtr)
+ GridBag *masterPtr;
+{
+ if (masterPtr->valid == 0) {
+ if (!masterPtr->layoutCache)
+ masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
+
+ GetLayoutInfo(masterPtr, masterPtr->layoutCache);
+ masterPtr->valid = 1;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Adjusts the x, y, width, and height fields to the correct
+ * values depending on the constraint geometry and pads.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+AdjustForGravity(gridPtr, x, y, width, height)
+ GridBag *gridPtr;
+ int *x;
+ int *y;
+ int *width;
+ int *height;
+{
+ int diffx=0, diffy=0;
+ int sticky = gridPtr->flags&STICK_ALL;
+
+ *x += gridPtr->padX/2;
+ *width -= gridPtr->padX;
+ *y += gridPtr->padY/2;
+ *height -= gridPtr->padY;
+
+ if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
+ diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
+ *width = gridPtr->minWidth + gridPtr->iPadX;
+ }
+
+ if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
+ diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
+ *height = gridPtr->minHeight + gridPtr->iPadY;
+ }
+
+ if (sticky&STICK_EAST && sticky&STICK_WEST)
+ *width += diffx;
+ if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
+ *height += diffy;
+ if (!(sticky&STICK_WEST)) {
+ if (sticky&STICK_EAST)
+ *x += diffx;
+ else
+ *x += diffx/2;
+ }
+ if (!(sticky&STICK_NORTH)) {
+ if (sticky&STICK_SOUTH)
+ *y += diffy;
+ else
+ *y += diffy/2;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Figure out the minimum size (not counting the X border) of the
+ * master based on the information from GetLayoutInfo()
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+GetMinSize(masterPtr, info, minw, minh)
+ GridBag *masterPtr;
+ LayoutInfo *info;
+ int *minw;
+ int *minh;
+{
+ int i, t;
+ int intBWidth; /* Width of internal border in parent window,
+ * if any. */
+
+ intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
+
+ t = 0;
+ for(i = 0; i < info->width; i++)
+ t += info->minWidth[i];
+ *minw = t + 2*intBWidth;
+
+ t = 0;
+ for(i = 0; i < info->height; i++)
+ t += info->minHeight[i];
+ *minh = t + 2*intBWidth;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ArrangeGrid --
+ *
+ * This procedure is invoked (using the Tk_DoWhenIdle
+ * mechanism) to re-layout a set of windows managed by
+ * the gridbag. It is invoked at idle time so that a
+ * series of gridbag requests can be merged into a single
+ * layout operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The slaves of masterPtr may get resized or moved.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ArrangeGrid(clientData)
+ ClientData clientData; /* Structure describing parent whose slaves
+ * are to be re-layed out. */
+{
+ GridBag *masterPtr = (GridBag *) clientData;
+ GridBag *slavePtr;
+ int abort;
+ int i, x, y, width, height;
+ int diffw, diffh;
+ double weight;
+ CkWindow *parent, *ancestor;
+ LayoutInfo info;
+ int intBWidth; /* Width of internal border in parent window,
+ * if any. */
+ int iPadX, iPadY;
+
+ masterPtr->flags &= ~REQUESTED_RELAYOUT;
+
+ /*
+ * If the parent has no slaves anymore, then don't do anything
+ * at all: just leave the parent's size as-is.
+ * Even if row and column constraints have been set!
+ */
+
+ if (masterPtr->slavePtr == NULL) {
+ return;
+ }
+
+ /*
+ * Abort any nested call to ArrangeGrid for this window, since
+ * we'll do everything necessary here, and set up so this call
+ * can be aborted if necessary.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->abortPtr = &abort;
+ abort = 0;
+ Ck_Preserve((ClientData) masterPtr);
+
+ /*
+ * Pass #1: scan all the slaves to figure out the total amount
+ * of space needed.
+ */
+
+ GetLayoutInfo(masterPtr, &info);
+ GetMinSize(masterPtr, &info, &width, &height);
+
+ if (((width != masterPtr->winPtr->reqWidth)
+ || (height != masterPtr->winPtr->reqHeight))
+ && !(masterPtr->flags & DONT_PROPAGATE)) {
+ Ck_GeometryRequest(masterPtr->winPtr, width, height);
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ masterPtr->valid = 0;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ goto done;
+ }
+
+ /*
+ * If the parent isn't mapped then don't do anything more: wait
+ * until it gets mapped again. Need to get at least to here to
+ * reflect size needs up the window hierarchy, but there's no
+ * point in actually mapping the slaves.
+ */
+
+ if (!(masterPtr->winPtr->flags & CK_MAPPED)) {
+ goto done;
+ }
+
+ /*
+ * If the current dimensions of the window don't match the desired
+ * dimensions, then adjust the minWidth and minHeight arrays
+ * according to the weights.
+ */
+
+ diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
+ if (diffw != 0) {
+ weight = 0.0;
+ for (i = 0; i < info.width; i++)
+ weight += info.weightX[i];
+ if (weight > MINWEIGHT) {
+ for (i = 0; i < info.width; i++) {
+ int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
+ info.minWidth[i] += dx;
+ width += dx;
+ if (info.minWidth[i] < 0) {
+ width -= info.minWidth[i];
+ info.minWidth[i] = 0;
+ }
+ }
+ }
+ diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
+ }
+ else {
+ diffw = 0;
+ }
+
+ diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
+ if (diffh != 0) {
+ weight = 0.0;
+ for (i = 0; i < info.height; i++)
+ weight += info.weightY[i];
+ if (weight > MINWEIGHT) {
+ for (i = 0; i < info.height; i++) {
+ int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
+ info.minHeight[i] += dy;
+ height += dy;
+ if (info.minHeight[i] < 0) {
+ height -= info.minHeight[i];
+ info.minHeight[i] = 0;
+ }
+ }
+ }
+ diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
+ }
+ else {
+ diffh = 0;
+ }
+
+ /*
+ * Now do the actual layout of the slaves using the layout information
+ * that has been collected.
+ */
+
+ iPadX = masterPtr->iPadX/2;
+ iPadY = masterPtr->iPadY/2;
+ intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
+ for(i = 0; i < slavePtr->tempX; i++)
+ x += info.minWidth[i];
+
+ masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
+ for(i = 0; i < slavePtr->tempY; i++)
+ y += info.minHeight[i];
+
+ width = 0;
+ for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth);
+ i++)
+ width += info.minWidth[i];
+
+ height = 0;
+ for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight);
+ i++)
+ height += info.minHeight[i];
+
+ AdjustForGravity(slavePtr, &x, &y, &width, &height);
+
+ /*
+ * If the window in which slavePtr is managed is not its
+ * parent in the window hierarchy, translate the coordinates
+ * to the coordinate system of the real X parent.
+ */
+
+ parent = slavePtr->winPtr->parentPtr;
+ for (ancestor = masterPtr->winPtr; ancestor != parent;
+ ancestor = ancestor->parentPtr) {
+ x += ancestor->x;
+ y += ancestor->y;
+ }
+
+ /*
+ * If the window is too small to be interesting then
+ * unmap it. Otherwise configure it and then make sure
+ * it's mapped.
+ */
+
+ if ((width <= 0) || (height <= 0)) {
+ Ck_UnmapWindow(slavePtr->winPtr);
+ }
+ else {
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+ if (x != slavePtr->winPtr->x ||
+ y != slavePtr->winPtr->y)
+ Ck_MoveWindow(slavePtr->winPtr, x, y);
+ /*
+ * Temporary kludge til Ck_MoveResizeWindow available !!!
+ */
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+ if (abort) {
+ goto done;
+ }
+ Ck_MapWindow(slavePtr->winPtr);
+ }
+
+ /*
+ * Changes to the window's structure could cause almost anything
+ * to happen, including deleting the parent or child. If this
+ * happens, we'll be told to abort.
+ */
+
+ if (abort) {
+ goto done;
+ }
+ }
+
+ done:
+ masterPtr->abortPtr = NULL;
+ Ck_Release((ClientData) masterPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GetGridBag --
+ *
+ * This internal procedure is used to locate a GridBag
+ * structure for a given window, creating one if one
+ * doesn't exist already.
+ *
+ * Results:
+ * The return value is a pointer to the GridBag structure
+ * corresponding to winPtr.
+ *
+ * Side effects:
+ * A new gridbag structure may be created. If so, then
+ * a callback is set up to clean things up when the
+ * window is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static GridBag *
+GetGridBag(winPtr)
+ CkWindow *winPtr; /* Pointer to window for which
+ * gridbag structure is desired. */
+{
+ GridBag *gridPtr;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there's already gridbag for this window. If not,
+ * then create a new one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) winPtr, &new);
+ if (!new) {
+ return (GridBag *) Tcl_GetHashValue(hPtr);
+ }
+ gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
+ gridPtr->winPtr = winPtr;
+ gridPtr->masterPtr = NULL;
+ gridPtr->nextPtr = NULL;
+ gridPtr->slavePtr = NULL;
+
+ gridPtr->gridColumn = gridPtr->gridRow = -1;
+ gridPtr->gridWidth = gridPtr->gridHeight = 1;
+ gridPtr->weightX = gridPtr->weightY = 0.0;
+ gridPtr->minWidth = gridPtr->minHeight = 0;
+
+ gridPtr->padX = gridPtr->padY = 0;
+ gridPtr->iPadX = gridPtr->iPadY = 0;
+ gridPtr->startx = gridPtr->starty = 0;
+ gridPtr->abortPtr = NULL;
+ gridPtr->flags = 0;
+
+ gridPtr->column.max = 0;
+ gridPtr->row.max = 0;
+ gridPtr->column.used = 0;
+ gridPtr->row.used = 0;
+
+ gridPtr->valid = 0;
+ gridPtr->layoutCache = NULL;
+
+ Tcl_SetHashValue(hPtr, gridPtr);
+ Ck_CreateEventHandler(winPtr,
+ CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ GridBagStructureProc, (ClientData) gridPtr);
+ return gridPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a gridbag from its parent's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The parent will be scheduled for re-arranging.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(gridPtr)
+ GridBag *gridPtr; /* Window to unlink. */
+{
+ GridBag *masterPtr, *gridPtr2;
+
+ masterPtr = gridPtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+ if (masterPtr->slavePtr == gridPtr) {
+ masterPtr->slavePtr = gridPtr->nextPtr;
+ }
+ else {
+ for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
+ if (gridPtr2 == NULL) {
+ panic("Unlink couldn't find previous window");
+ }
+ if (gridPtr2->nextPtr == gridPtr) {
+ gridPtr2->nextPtr = gridPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ masterPtr->valid = 0;
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ gridPtr->masterPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyGridBag --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a gridbag at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the gridbag is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyGridBag(memPtr)
+ char *memPtr; /* Info about window that is now dead. */
+{
+ GridBag *gridPtr = (GridBag *) memPtr;
+
+ if (gridPtr->column.max) {
+ ckfree((char *) gridPtr->column.minsize);
+ ckfree((char *) gridPtr->column.weight);
+ }
+ if (gridPtr->row.max) {
+ ckfree((char *) gridPtr->row.minsize);
+ ckfree((char *) gridPtr->row.weight);
+ }
+ if (gridPtr->layoutCache)
+ ckfree((char *) gridPtr->layoutCache);
+
+ ckfree((char *) gridPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridBagStructureProc --
+ *
+ * This procedure is invoked by the Ck event dispatcher in response
+ * to window change events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a window was just deleted, clean up all its gridbag-related
+ * information. If it was just resized, re-configure its slaves, if
+ * any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GridBagStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Our information about window
+ * referred to by eventPtr. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ GridBag *gridPtr = (GridBag *) clientData;
+
+ if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
+ gridPtr->valid = 0;
+ if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ GridBag *gridPtr2, *nextPtr;
+
+ if (gridPtr->masterPtr != NULL) {
+ Unlink(gridPtr);
+ }
+ for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
+ gridPtr2 = nextPtr) {
+ Ck_UnmapWindow(gridPtr2->winPtr);
+ gridPtr2->masterPtr = NULL;
+ nextPtr = gridPtr2->nextPtr;
+ gridPtr2->nextPtr = NULL;
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
+ (char *) gridPtr->winPtr));
+ if (gridPtr->flags & REQUESTED_RELAYOUT) {
+ Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
+ }
+ gridPtr->winPtr = NULL;
+ Ck_EventuallyFree((ClientData) gridPtr,
+ (Ck_FreeProc *) DestroyGridBag);
+ } else if (eventPtr->type == CK_EV_UNMAP) {
+ GridBag *gridPtr2;
+
+ for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
+ gridPtr2 = gridPtr2->nextPtr) {
+ Ck_UnmapWindow(gridPtr2->winPtr);
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureSlaves --
+ *
+ * This implements the guts of the "grid configure" command. Given
+ * a list of slaves and configuration options, it arranges for the
+ * gridbag to manage the slaves and sets the specified options.
+ *
+ * Results:
+ * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
+ * returned and interp->result is set to contain an error message.
+ *
+ * Side effects:
+ * Slave windows get taken over by the gridbag.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlaves(interp, winPtr, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Any window in application containing
+ * slaves. Used to look up slave names. */
+ int argc; /* Number of elements in argv. */
+ char *argv[]; /* Argument strings: contains one or more
+ * window names followed by any number
+ * of "option value" pairs. Caller must
+ * make sure that there is at least one
+ * window name. */
+{
+ GridBag *masterPtr, *slavePtr, *prevPtr;
+ CkWindow *other, *slave;
+#if 0
+ CkWindow *parent, *ancestor;
+#endif
+ int i, j, numWindows, c, length, tmp, positionGiven;
+ int currentColumn=0, numColumns=1;
+ int gotLayout = 0;
+ int gotWidth = 0;
+ int width;
+
+ /*
+ * Find out how many windows are specified. (shouldn't use
+ * hardwired symbols)
+ */
+
+ for (numWindows = 0; numWindows < argc; numWindows++) {
+ if (argv[numWindows][0] != '.'
+ && strcmp(argv[numWindows],"-")!=0
+ && strcmp(argv[numWindows],"^")!=0
+ && strcmp(argv[numWindows],"x")!=0) {
+ break;
+ }
+ }
+ slave = NULL;
+
+ /*
+ * Iterate over all of the slave windows, parsing the configuration
+ * options for each slave. It's a bit wasteful to re-parse the
+ * options for each slave, but things get too messy if we try to
+ * parse the arguments just once at the beginning. For example,
+ * if a slave already is managed we want to just change a few
+ * existing values without resetting everything. If there are
+ * multiple windows, the -in option only gets processed for the
+ * first window.
+ */
+
+ masterPtr = NULL;
+ prevPtr = NULL;
+ positionGiven = 0;
+ for (j = 0; j < numWindows; j++) {
+
+ /* adjust default widget location for non-widgets */
+ if (*argv[j] != '.') {
+ switch (*argv[j]) {
+ case '^': /* extend the widget in the previous row
+ * Since we don't know who the master is yet,
+ * handle these in a separate pass at the end
+ */
+ /* no break */
+ case REL_SKIP: /* skip over the next column */
+ currentColumn++;
+ break;
+ case REL_HORIZ: /* increase the span, already dealt with */
+ /* not quite right */
+ if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
+ Tcl_AppendResult(interp, "Invalid grid combination:",
+ " \"-\" can't follow \"", argv[j - 1], "\"", NULL);
+ return TCL_ERROR;
+ }
+ break;
+ default:
+ panic("Invalid grid position indicator");
+ }
+ continue;
+ }
+
+ for (numColumns = 1;
+ j + numColumns < numWindows && *argv[j + numColumns] == REL_HORIZ;
+ numColumns++) {
+ /* null body */
+ }
+ slave = Ck_NameToWindow(interp, argv[j], winPtr);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ if (slave->flags & CK_TOPLEVEL) {
+ Tcl_AppendResult(interp, "can't manage \"", argv[j],
+ "\": it's a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr = GetGridBag(slave);
+
+ /*
+ * The following statement is taken from tkPack.c:
+ *
+ * "If the slave isn't currently managed, reset all of its
+ * configuration information to default values (there could
+ * be old values left from a previous packer)."
+ *
+ * I disagree with this statement. If a slave is disabled (using
+ * "forget") and then re-enabled, I submit that 90% of the time the
+ * programmer will want it to retain its old configuration information.
+ * If the programmer doesn't want this behavior, then she can reset the
+ * defaults for herself, but she will never have to worry about keeping
+ * track of the old state.
+ */
+
+ for (i = numWindows; i < argc; i+=2) {
+ if ((i+2) > argc) {
+ Tcl_AppendResult(interp, "extra option \"", argv[i],
+ "\" (option with no value?)", (char *) NULL);
+ return TCL_ERROR;
+ }
+ length = strlen(argv[i]);
+ if (length < 2) {
+ goto badOption;
+ }
+ c = argv[i][1];
+#if 0
+ if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
+ if (j == 0) {
+ other = Ck_NameToWindow(interp, argv[i+1], winPtr);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ if (other == slave) {
+ sprintf(interp->result,
+ "Window can't be managed in itself");
+ return TCL_ERROR;
+ }
+ masterPtr = GetGridBag(other);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ } else
+#endif
+ if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
+ || (tmp < 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadX = tmp*2;
+ } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadY = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->padX = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->padY = tmp*2;
+ } else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
+ "\": must be a non-negative integer", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->gridColumn = tmp;
+ } else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
+ "\": must be a non-negative integer", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->gridRow = tmp;
+ } else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK ||
+ tmp <= 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad columnspan value \"",
+ argv[i+1],
+ "\": must be a positive integer", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->gridWidth = tmp;
+ gotWidth++;
+ } else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad rowspan value \"",
+ argv[i+1],
+ "\": must be a positive integer", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->gridHeight = tmp;
+#if 0
+ } else if ((c == 'w') &&
+ (!strcmp(argv[i], "-weightx") ||
+ !strcmp(argv[i], "-wx"))) {
+ if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
+ "\": must be a double", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->weightX = tmp_dbl;
+ } else if ((c == 'w') &&
+ (!strcmp(argv[i], "-weighty") ||
+ !strcmp(argv[i], "-wy"))) {
+ if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
+ "\": must be a double", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->weightY = tmp_dbl;
+#endif
+ } else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
+ int sticky = StringToSticky(argv[i+1]);
+ if (sticky == -1) {
+ Tcl_AppendResult(interp, "bad stickyness value \"",
+ argv[i+1],
+ "\": must be a string containing n, e, s, and/or w",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
+ } else {
+ badOption:
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+#if 0
+ argv[i], "\": must be -in, -sticky, ",
+#else
+ argv[i], "\": must be -sticky, ",
+#endif
+ "-row, -column, -rowspan, -columnspan, ",
+ "-ipadx, -ipady, -padx or -pady.",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * If no position in a gridbag list was specified and the slave
+ * is already managed, then leave it in its current location in
+ * its current gridbag list.
+ */
+
+ if (!positionGiven && (slavePtr->masterPtr != NULL)) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If the slave is going to be put back after itself then
+ * skip the whole operation, since it won't work anyway.
+ */
+
+ if (prevPtr == slavePtr) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If the "-in" option has not been specified, arrange for the
+ * slave to go at the end of the order for its parent.
+ */
+
+ if (!positionGiven) {
+ masterPtr = GetGridBag(slave->parentPtr);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ }
+#if 0
+ /*
+ * Make sure that the slave's parent is either the master or
+ * an ancestor of the master.
+ */
+
+ parent = slave->parentPtr;
+ for (ancestor = masterPtr->winPtr; ; ancestor = ancestor->parentPtr) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (ancestor->flags & CK_TOPLEVEL) {
+ Tcl_AppendResult(interp, "can't put ", argv[j],
+ " inside ", masterPtr->winPtr->parentPtr,
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+#else
+ if (masterPtr->winPtr != slave->parentPtr) {
+ Tcl_AppendResult(interp, "can't put ", argv[j],
+ " inside ", masterPtr->winPtr->parentPtr,
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+#endif
+
+ /*
+ * Unlink the slave if it's currently managed, then position it
+ * after prevPtr.
+ */
+
+ if (slavePtr->masterPtr != NULL) {
+ Unlink(slavePtr);
+ }
+ slavePtr->masterPtr = masterPtr;
+ if (prevPtr == NULL) {
+ slavePtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = slavePtr;
+ } else {
+ slavePtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = slavePtr;
+ }
+ Ck_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
+ prevPtr = slavePtr;
+
+ /* assign default row and column */
+
+ if (slavePtr->gridColumn == -1) {
+ slavePtr->gridColumn = currentColumn;
+ }
+ slavePtr->gridWidth += numColumns - 1;
+ if (slavePtr->gridRow == -1) {
+ if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
+ slavePtr->gridRow = masterPtr->layoutCache->lastRow;
+ }
+
+ /*
+ * Arrange for the parent to be re-arranged at the first
+ * idle moment.
+ */
+
+ scheduleLayout:
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->valid = 0;
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ }
+ currentColumn += slavePtr->gridWidth;
+ numColumns = 1;
+ }
+
+ /* now look for all the "^"'s */
+
+ for (j = 0; j < numWindows; j++) {
+ struct GridBag *otherPtr;
+ char *lastWindow; /* use this window to base current row/col on */
+ int match; /* found a match for the ^ */
+
+ if (*argv[j] == '.') {
+ lastWindow = argv[j];
+ }
+ if (*argv[j] != '^') {
+ continue;
+ }
+ for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
+ /* Null Body */
+ }
+ other = Ck_NameToWindow(interp, lastWindow, winPtr);
+ otherPtr = GetGridBag(other);
+ if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
+
+ for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ if (slavePtr->gridWidth == width
+ && slavePtr->gridColumn == otherPtr->gridColumn +
+ otherPtr->gridWidth
+ && slavePtr->gridRow + slavePtr->gridHeight ==
+ otherPtr->gridRow) {
+ slavePtr->gridHeight++;
+ match++;
+ }
+ lastWindow = slavePtr->winPtr->pathName;
+ }
+ if (!match) {
+ Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
+ " after ", lastWindow, (char *) NULL);
+ return TCL_ERROR;
+ }
+ j += width - 1;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Convert "Sticky" bits into a string
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+StickyToString(flags, result)
+ int flags; /* the sticky flags */
+ char *result; /* where to put the result */
+{
+ int count = 0;
+ if (flags & STICK_NORTH)
+ result[count++] = 'n';
+ if (flags & STICK_EAST)
+ result[count++] = 'e';
+ if (flags & STICK_SOUTH)
+ result[count++] = 's';
+ if (flags & STICK_WEST)
+ result[count++] = 'w';
+ if (count) {
+ result[count] = '\0';
+ } else {
+ sprintf(result,"{}");
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Convert sticky string to flags
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+StringToSticky(string)
+ char *string;
+{
+ int sticky = 0;
+ char c;
+
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case 'n': case 'N': sticky |= STICK_NORTH; break;
+ case 'e': case 'E': sticky |= STICK_EAST; break;
+ case 's': case 'S': sticky |= STICK_SOUTH; break;
+ case 'w': case 'W': sticky |= STICK_WEST; break;
+ case ' ': case ',': case '\t': case '\r': case '\n': break;
+ default: return -1;
+ }
+ }
+ return sticky;
+}
--- /dev/null
+/*
+ * ckListbox.c --
+ *
+ * This module implements listbox widgets for the
+ * toolkit. A listbox displays a collection of strings,
+ * one per line, and provides scrolling and selection.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * One record of the following type is kept for each element
+ * associated with a listbox widget:
+ */
+
+typedef struct Element {
+ int textLength; /* # non-NULL characters in text. */
+ int textWidth; /* Total width of element in screen
+ * characters. */
+ int selected; /* 1 means this item is selected, 0 means
+ * it isn't. */
+ struct Element *nextPtr; /* Next in list of all elements of this
+ * listbox, or NULL for last element. */
+ char text[4]; /* Characters of this element, NULL-
+ * terminated. The actual space allocated
+ * here will be as large as needed (> 4,
+ * most likely). Must be the last field
+ * of the record. */
+} Element;
+
+#define ElementSize(stringLength) \
+ (sizeof(Element) - 3 + stringLength)
+
+/*
+ * A data structure of the following type is kept for each listbox
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the listbox. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with listbox. */
+ Tcl_Command widgetCmd; /* Token for listbox's widget command. */
+ int numElements; /* Total number of elements in this listbox. */
+ Element *firstPtr; /* First in list of elements (NULL if no
+ * elements). */
+ Element *lastPtr; /* Last in list of elements (NULL if no
+ * elements). */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int normalBg; /* Normal background color. */
+ int normalFg; /* Normal foreground color. */
+ int normalAttr; /* Normal video attributes. */
+ int selBg; /* Select background color. */
+ int selFg; /* Select foreground color. */
+ int selAttr; /* Select video attributes. */
+ int activeBg; /* Active background color. */
+ int activeFg; /* Active foreground color. */
+ int activeAttr; /* Video attribute for active item. */
+ int width; /* Desired width of window, in characters. */
+ int height; /* Desired height of window, in lines. */
+ int topIndex; /* Index of top-most element visible in
+ * window. */
+ int fullLines; /* Number of lines that fit are completely
+ * visible in window. There may be one
+ * additional line at the bottom that is
+ * partially visible. */
+
+ /*
+ * Information to support horizontal scrolling:
+ */
+
+ int maxWidth; /* Width of widest string in listbox. */
+ int xOffset; /* The left edge of each string in the
+ * listbox is offset to the left by this
+ * many chars (0 means no offset, positive
+ * means there is an offset). */
+
+ /*
+ * Information about what's selected or active, if any.
+ */
+
+ Ck_Uid selectMode; /* Selection style: single, browse, multiple,
+ * or extended. This value isn't used in C
+ * code, but the Tcl bindings use it. */
+ int numSelected; /* Number of elements currently selected. */
+ int selectAnchor; /* Fixed end of selection (i.e. element
+ * at which selection was started.) */
+ int active; /* Index of "active" element (the one that
+ * has been selected by keyboard traversal).
+ * -1 means none. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *yScrollCmd; /* Command prefix for communicating with
+ * vertical scrollbar. NULL means no command
+ * to issue. Malloc'ed. */
+ char *xScrollCmd; /* Command prefix for communicating with
+ * horizontal scrollbar. NULL means no command
+ * to issue. Malloc'ed. */
+ int flags; /* Various flag bits: see below for
+ * definitions. */
+} Listbox;
+
+/*
+ * Flag bits for listboxes:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
+ * to be updated.
+ * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
+ * to be updated.
+ * GOT_FOCUS: Non-zero means this widget currently
+ * has the input focus.
+ */
+
+#define REDRAW_PENDING 1
+#define UPDATE_V_SCROLLBAR 2
+#define UPDATE_H_SCROLLBAR 4
+#define GOT_FOCUS 8
+
+/*
+ * Information used for argv parsing:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_LISTBOX_ACTIVE_ATTR_COLOR,
+ Ck_Offset(Listbox, activeAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_LISTBOX_ACTIVE_ATTR_MONO,
+ Ck_Offset(Listbox, activeAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_LISTBOX_ACTIVE_BG_COLOR, Ck_Offset(Listbox, activeBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_LISTBOX_ACTIVE_BG_MONO, Ck_Offset(Listbox, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_LISTBOX_ACTIVE_FG_COLOR, Ck_Offset(Listbox, activeFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_LISTBOX_ACTIVE_FG_MONO, Ck_Offset(Listbox, activeFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_LISTBOX_ATTR, Ck_Offset(Listbox, normalAttr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_LISTBOX_BG_COLOR, Ck_Offset(Listbox, normalBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_LISTBOX_BG_MONO, Ck_Offset(Listbox, normalBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LISTBOX_FG, Ck_Offset(Listbox, normalFg), 0},
+ {CK_CONFIG_INT, "-height", "height", "Height",
+ DEF_LISTBOX_HEIGHT, Ck_Offset(Listbox, height), 0},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_LISTBOX_SELECT_ATTR_COLOR,
+ Ck_Offset(Listbox, selAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_LISTBOX_SELECT_ATTR_MONO,
+ Ck_Offset(Listbox, selAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_LISTBOX_SELECT_BG_COLOR, Ck_Offset(Listbox, selBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_LISTBOX_SELECT_BG_MONO, Ck_Offset(Listbox, selBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_LISTBOX_SELECT_FG_COLOR, Ck_Offset(Listbox, selFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_LISTBOX_SELECT_FG_MONO, Ck_Offset(Listbox, selFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_UID, "-selectmode", "selectMode", "SelectMode",
+ DEF_LISTBOX_SELECT_MODE, Ck_Offset(Listbox, selectMode), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_LISTBOX_TAKE_FOCUS, Ck_Offset(Listbox, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_INT, "-width", "width", "Width",
+ DEF_LISTBOX_WIDTH, Ck_Offset(Listbox, width), 0},
+ {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ DEF_LISTBOX_SCROLL_COMMAND, Ck_Offset(Listbox, xScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ DEF_LISTBOX_SCROLL_COMMAND, Ck_Offset(Listbox, yScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
+ int offset));
+static void ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
+ int index));
+static int ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
+ Listbox *listPtr, int argc, char **argv,
+ int flags));
+static void DeleteEls _ANSI_ARGS_((Listbox *listPtr, int first,
+ int last));
+static void DestroyListbox _ANSI_ARGS_((ClientData clientData));
+static void DisplayListbox _ANSI_ARGS_((ClientData clientData));
+static int GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ Listbox *listPtr, char *string, int numElsOK,
+ int *indexPtr));
+static void InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
+ int argc, char **argv));
+static void ListboxCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void ListboxComputeGeometry _ANSI_ARGS_((Listbox *listPtr));
+static void ListboxEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
+ int first, int last));
+static void ListboxSelect _ANSI_ARGS_((Listbox *listPtr,
+ int first, int last, int select));
+static void ListboxUpdateHScrollbar _ANSI_ARGS_((Listbox *listPtr));
+static void ListboxUpdateVScrollbar _ANSI_ARGS_((Listbox *listPtr));
+static int ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
+ int y));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ListboxCmd --
+ *
+ * This procedure is invoked to process the "listbox" Tcl
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_ListboxCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Listbox *listPtr;
+ CkWindow *new;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the fields of the structure that won't be initialized
+ * by ConfigureListbox, or that ConfigureListbox requires to be
+ * initialized already (e.g. resource pointers).
+ */
+
+ listPtr = (Listbox *) ckalloc(sizeof(Listbox));
+ listPtr->winPtr = new;
+ listPtr->interp = interp;
+ listPtr->widgetCmd = Tcl_CreateCommand(interp, listPtr->winPtr->pathName,
+ ListboxWidgetCmd, (ClientData) listPtr, ListboxCmdDeletedProc);
+ listPtr->numElements = 0;
+ listPtr->firstPtr = NULL;
+ listPtr->lastPtr = NULL;
+ listPtr->normalBg = 0;
+ listPtr->normalFg = 0;
+ listPtr->normalAttr = 0;
+ listPtr->selBg = 0;
+ listPtr->selFg = 0;
+ listPtr->selAttr = 0;
+ listPtr->activeBg = 0;
+ listPtr->activeFg = 0;
+ listPtr->activeAttr = 0;
+ listPtr->width = 0;
+ listPtr->height = 0;
+ listPtr->topIndex = 0;
+ listPtr->fullLines = 1;
+ listPtr->maxWidth = 0;
+ listPtr->xOffset = 0;
+ listPtr->selectMode = NULL;
+ listPtr->numSelected = 0;
+ listPtr->selectAnchor = 0;
+ listPtr->active = 0;
+ listPtr->takeFocus = NULL;
+ listPtr->xScrollCmd = NULL;
+ listPtr->yScrollCmd = NULL;
+ listPtr->flags = 0;
+
+ Ck_SetClass(listPtr->winPtr, "Listbox");
+ Ck_CreateEventHandler(listPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY |
+ CK_EV_FOCUSIN | CK_EV_FOCUSOUT,
+ ListboxEventProc, (ClientData) listPtr);
+ if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ interp->result = listPtr->winPtr->pathName;
+ return TCL_OK;
+
+ error:
+ Ck_DestroyWindow(listPtr->winPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ListboxWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ListboxWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about listbox widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Listbox *listPtr = (Listbox *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) listPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " activate index\"",
+ (char *) NULL);
+ goto error;
+ }
+ ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
+ if (GetListboxIndex(interp, listPtr, argv[2], 0, &index)
+ != TCL_OK) {
+ goto error;
+ }
+ listPtr->active = index;
+ ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, listPtr->winPtr, configSpecs,
+ (char *) listPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, listPtr->winPtr, configSpecs,
+ (char *) listPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, listPtr->winPtr, configSpecs,
+ (char *) listPtr, argv[2], 0);
+ } else {
+ result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
+ && (length >= 2)) {
+ int i, count;
+ char index[20];
+ Element *elPtr;
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " curselection\"",
+ (char *) NULL);
+ goto error;
+ }
+ count = 0;
+ for (i = 0, elPtr = listPtr->firstPtr; elPtr != NULL;
+ i++, elPtr = elPtr->nextPtr) {
+ if (elPtr->selected) {
+ sprintf(index, "%d", i);
+ Tcl_AppendElement(interp, index);
+ count++;
+ }
+ }
+ if (count != listPtr->numSelected) {
+ panic("ListboxWidgetCmd: selection count incorrect");
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
+ int first, last;
+
+ if ((argc < 3) || (argc > 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " delete firstIndex ?lastIndex?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
+ goto error;
+ }
+ if (argc == 3) {
+ last = first;
+ } else {
+ if (GetListboxIndex(interp, listPtr, argv[3], 0, &last) != TCL_OK) {
+ goto error;
+ }
+ }
+ DeleteEls(listPtr, first, last);
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ int first, last, i;
+ Element *elPtr;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get first ?last?\"", (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
+ goto error;
+ }
+ if ((argc == 4) && (GetListboxIndex(interp, listPtr, argv[3],
+ 0, &last) != TCL_OK)) {
+ goto error;
+ }
+ for (elPtr = listPtr->firstPtr, i = 0; i < first;
+ i++, elPtr = elPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if (elPtr != NULL) {
+ if (argc == 3) {
+ interp->result = elPtr->text;
+ } else {
+ for ( ; i <= last; i++, elPtr = elPtr->nextPtr) {
+ Tcl_AppendElement(interp, elPtr->text);
+ }
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " index index\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
+ != TCL_OK) {
+ goto error;
+ }
+ sprintf(interp->result, "%d", index);
+ } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " insert index ?element element ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
+ != TCL_OK) {
+ goto error;
+ }
+ InsertEls(listPtr, index, argc-3, argv+3);
+ } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
+ int index, y;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " nearest y\"", (char *) NULL);
+ goto error;
+ }
+ if (Tcl_GetInt(interp, argv[2], &y) != TCL_OK) {
+ goto error;
+ }
+ index = NearestListboxElement(listPtr, y);
+ sprintf(interp->result, "%d", index);
+ } else if ((c == 's') && (strncmp(argv[1], "see", length) == 0)
+ && (length >= 3)) {
+ int index, diff;
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " see index\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ diff = listPtr->topIndex-index;
+ if (diff > 0) {
+ if (diff <= (listPtr->fullLines/3)) {
+ ChangeListboxView(listPtr, index);
+ } else {
+ ChangeListboxView(listPtr, index - (listPtr->fullLines-1)/2);
+ }
+ } else {
+ diff = index - (listPtr->topIndex + listPtr->fullLines - 1);
+ if (diff > 0) {
+ if (diff <= (listPtr->fullLines/3)) {
+ ChangeListboxView(listPtr, listPtr->topIndex + diff);
+ } else {
+ ChangeListboxView(listPtr,
+ index - (listPtr->fullLines-1)/2);
+ }
+ }
+ }
+ } else if ((c == 's') && (length >= 3)
+ && (strncmp(argv[1], "selection", length) == 0)) {
+ int first, last;
+
+ if ((argc != 4) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection option index ?index?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetListboxIndex(interp, listPtr, argv[3], 0, &first) != TCL_OK) {
+ goto error;
+ }
+ if (argc == 5) {
+ if (GetListboxIndex(interp, listPtr, argv[4], 0, &last) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ last = first;
+ }
+ length = strlen(argv[2]);
+ c = argv[2][0];
+ if ((c == 'a') && (strncmp(argv[2], "anchor", length) == 0)) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection anchor index\"", (char *) NULL);
+ goto error;
+ }
+ listPtr->selectAnchor = first;
+ } else if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
+ ListboxSelect(listPtr, first, last, 0);
+ } else if ((c == 'i') && (strncmp(argv[2], "includes", length) == 0)) {
+ int i;
+ Element *elPtr;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " selection includes index\"", (char *) NULL);
+ goto error;
+ }
+ for (elPtr = listPtr->firstPtr, i = 0; i < first;
+ i++, elPtr = elPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if ((elPtr != NULL) && (elPtr->selected)) {
+ interp->result = "1";
+ } else {
+ interp->result = "0";
+ }
+ } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
+ ListboxSelect(listPtr, first, last, 1);
+ } else {
+ Tcl_AppendResult(interp, "bad selection option \"", argv[2],
+ "\": must be anchor, clear, includes, or set",
+ (char *) NULL);
+ goto error;
+ }
+ } else if ((c == 's') && (length >= 2)
+ && (strncmp(argv[1], "size", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " size\"", (char *) NULL);
+ goto error;
+ }
+ sprintf(interp->result, "%d", listPtr->numElements);
+ } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
+ int index, count, type, windowWidth;
+ int offset = 0; /* Initialized to stop gcc warnings. */
+ double fraction, fraction2;
+
+ windowWidth = listPtr->winPtr->width;
+ if (argc == 2) {
+ if (listPtr->maxWidth == 0) {
+ interp->result = "0 1";
+ } else {
+ fraction = listPtr->xOffset/((double) listPtr->maxWidth);
+ fraction2 = (listPtr->xOffset + windowWidth)
+ /((double) listPtr->maxWidth);
+ if (fraction2 > 1.0) {
+ fraction2 = 1.0;
+ }
+ sprintf(interp->result, "%g %g", fraction, fraction2);
+ }
+ } else if (argc == 3) {
+ if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
+ goto error;
+ }
+ ChangeListboxOffset(listPtr, index);
+ } else {
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ goto error;
+ case CK_SCROLL_MOVETO:
+ offset = (int) fraction*listPtr->maxWidth;
+ break;
+ case CK_SCROLL_PAGES:
+ offset = listPtr->xOffset + count * windowWidth;
+ break;
+ case CK_SCROLL_UNITS:
+ offset = listPtr->xOffset + count;
+ break;
+ }
+ ChangeListboxOffset(listPtr, offset);
+ }
+ } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
+ int index, count, type;
+ double fraction, fraction2;
+
+ if (argc == 2) {
+ if (listPtr->numElements == 0) {
+ interp->result = "0 1";
+ } else {
+ fraction = listPtr->topIndex/((double) listPtr->numElements);
+ fraction2 = (listPtr->topIndex+listPtr->fullLines)
+ /((double) listPtr->numElements);
+ if (fraction2 > 1.0) {
+ fraction2 = 1.0;
+ }
+ sprintf(interp->result, "%g %g", fraction, fraction2);
+ }
+ } else if (argc == 3) {
+ if (GetListboxIndex(interp, listPtr, argv[2], 0, &index)
+ != TCL_OK) {
+ goto error;
+ }
+ ChangeListboxView(listPtr, index);
+ } else {
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ goto error;
+ case CK_SCROLL_MOVETO:
+ index = (int) (listPtr->numElements * fraction);
+ break;
+ case CK_SCROLL_PAGES:
+ if (listPtr->fullLines > 2) {
+ index = listPtr->topIndex
+ + count * (listPtr->fullLines - 2);
+ } else {
+ index = listPtr->topIndex + count;
+ }
+ break;
+ case CK_SCROLL_UNITS:
+ index = listPtr->topIndex + count;
+ break;
+ }
+ ChangeListboxView(listPtr, index);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be activate, cget, configure, ",
+ "curselection, delete, get, index, insert, nearest, ",
+ "see, selection, size, ",
+ "xview, or yview", (char *) NULL);
+ goto error;
+ }
+ Ck_Release((ClientData) listPtr);
+ return result;
+
+ error:
+ Ck_Release((ClientData) listPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyListbox --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a listbox at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the listbox is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyListbox(clientData)
+ ClientData clientData; /* Info about listbox widget. */
+{
+ register Listbox *listPtr = (Listbox *) clientData;
+ register Element *elPtr, *nextPtr;
+
+ /*
+ * Free up all of the list elements.
+ */
+
+ for (elPtr = listPtr->firstPtr; elPtr != NULL; ) {
+ nextPtr = elPtr->nextPtr;
+ ckfree((char *) elPtr);
+ elPtr = nextPtr;
+ }
+
+ Ck_FreeOptions(configSpecs, (char *) listPtr, 0);
+ ckfree((char *) listPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Listbox *listPtr = (Listbox *) clientData;
+ CkWindow *winPtr = listPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ listPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureListbox --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or reconfigure)
+ * a listbox widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as colors, border width,
+ * etc. get set for listPtr; old resources get freed,
+ * if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureListbox(interp, listPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Listbox *listPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Ck_ConfigureWidget. */
+{
+ if (Ck_ConfigureWidget(interp, listPtr->winPtr, configSpecs,
+ argc, argv, (char *) listPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Register the desired geometry for the window and arrange for
+ * the window to be redisplayed.
+ */
+
+ ListboxComputeGeometry(listPtr);
+ listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
+ ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayListbox --
+ *
+ * This procedure redraws the contents of a listbox window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayListbox(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ Listbox *listPtr = (Listbox *) clientData;
+ CkWindow *winPtr = listPtr->winPtr;
+ Element *elPtr;
+ int i, limit, y, width, cursorY;
+
+ listPtr->flags &= ~REDRAW_PENDING;
+ if (listPtr->flags & UPDATE_V_SCROLLBAR) {
+ ListboxUpdateVScrollbar(listPtr);
+ }
+ if (listPtr->flags & UPDATE_H_SCROLLBAR) {
+ ListboxUpdateHScrollbar(listPtr);
+ }
+ listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
+ if ((listPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+
+ Ck_SetWindowAttr(winPtr, listPtr->normalFg, listPtr->normalBg,
+ listPtr->normalAttr);
+ Ck_ClearToBot(winPtr, 0, 0);
+
+ /*
+ * Iterate through all of the elements of the listbox, displaying each
+ * in turn. Selected elements use a different fg/bg/attr.
+ */
+
+ limit = listPtr->topIndex + listPtr->fullLines;
+ if (limit > listPtr->numElements) {
+ limit = listPtr->numElements;
+ }
+ width = listPtr->xOffset + winPtr->width;
+ for (elPtr = listPtr->firstPtr, i = 0, y = cursorY = 0;
+ (elPtr != NULL) && (i < limit);
+ elPtr = elPtr->nextPtr, i++) {
+ if (i < listPtr->topIndex) {
+ continue;
+ }
+ if (i == listPtr->active && (listPtr->flags & GOT_FOCUS)) {
+ cursorY = y;
+ Ck_SetWindowAttr(winPtr, listPtr->activeFg, listPtr->activeBg,
+ listPtr->activeAttr |
+ (elPtr->selected ? listPtr->selAttr : 0));
+ } else if (elPtr->selected) {
+ Ck_SetWindowAttr(winPtr, listPtr->selFg, listPtr->selBg,
+ listPtr->selAttr);
+ } else {
+ Ck_SetWindowAttr(winPtr, listPtr->normalFg, listPtr->normalBg,
+ listPtr->normalAttr);
+ }
+#if CK_USE_UTF
+ if (listPtr->xOffset < elPtr->textWidth) {
+ char *p = Tcl_UtfAtIndex(elPtr->text, listPtr->xOffset);
+
+ CkDisplayChars(winPtr->mainPtr, winPtr->window, p,
+ strlen(p), 0, y, 0,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS | CK_FILL_UNTIL_EOL);
+ }
+#else
+ CkDisplayChars(winPtr->mainPtr,
+ winPtr->window, &elPtr->text[listPtr->xOffset],
+ elPtr->textLength - listPtr->xOffset, 0, y, 0,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS | CK_FILL_UNTIL_EOL);
+#endif
+ y++;
+ }
+ wmove(winPtr->window, cursorY, 0);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxComputeGeometry --
+ *
+ * This procedure is invoked to recompute geometry information
+ * such as the sizes of the elements and the overall dimensions
+ * desired for the listbox.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Geometry information is updated and a new requested size is
+ * registered for the widget. Internal border and gridding
+ * information is also set.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxComputeGeometry(listPtr)
+ Listbox *listPtr; /* Listbox whose geometry is to be
+ * recomputed. */
+{
+ int width, height;
+
+ width = listPtr->width;
+ if (width <= 0) {
+ width = listPtr->maxWidth;
+ if (width < 1) {
+ width = 1;
+ }
+ }
+ height = listPtr->height;
+ if (listPtr->height <= 0) {
+ height = listPtr->numElements;
+ if (height < 1) {
+ height = 1;
+ }
+ }
+ Ck_GeometryRequest(listPtr->winPtr, width, height);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertEls --
+ *
+ * Add new elements to a listbox widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * New information gets added to listPtr; it will be redisplayed
+ * soon, but not immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InsertEls(listPtr, index, argc, argv)
+ register Listbox *listPtr; /* Listbox that is to get the new
+ * elements. */
+ int index; /* Add the new elements before this
+ * element. */
+ int argc; /* Number of new elements to add. */
+ char **argv; /* New elements (one per entry). */
+{
+ register Element *prevPtr, *newPtr;
+ int length, i, oldMaxWidth;
+
+ /*
+ * Find the element before which the new ones will be inserted.
+ */
+
+ if (index <= 0) {
+ index = 0;
+ }
+ if (index > listPtr->numElements) {
+ index = listPtr->numElements;
+ }
+ if (index == 0) {
+ prevPtr = NULL;
+ } else if (index == listPtr->numElements) {
+ prevPtr = listPtr->lastPtr;
+ } else {
+ for (prevPtr = listPtr->firstPtr, i = index - 1; i > 0; i--) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+
+ /*
+ * For each new element, create a record, initialize it, and link
+ * it into the list of elements.
+ */
+
+ oldMaxWidth = listPtr->maxWidth;
+ for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
+ length = strlen(*argv);
+ newPtr = (Element *) ckalloc(ElementSize(length));
+ newPtr->textLength = length;
+ strcpy(newPtr->text, *argv);
+#if CK_USE_UTF
+ newPtr->textWidth = Tcl_NumUtfChars(*argv, length);
+#else
+ newPtr->textWidth = newPtr->textLength;
+#endif
+ if (newPtr->textWidth > listPtr->maxWidth) {
+ listPtr->maxWidth = newPtr->textWidth;
+ }
+ newPtr->selected = 0;
+ if (prevPtr == NULL) {
+ newPtr->nextPtr = listPtr->firstPtr;
+ listPtr->firstPtr = newPtr;
+ } else {
+ newPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = newPtr;
+ }
+ }
+ if ((prevPtr != NULL) && (prevPtr->nextPtr == NULL)) {
+ listPtr->lastPtr = prevPtr;
+ }
+ listPtr->numElements += argc;
+
+ /*
+ * Update the selection and other indexes to account for the
+ * renumbering that has just occurred. Then arrange for the new
+ * information to be displayed.
+ */
+
+ if (index <= listPtr->selectAnchor) {
+ listPtr->selectAnchor += argc;
+ }
+ if (index < listPtr->topIndex) {
+ listPtr->topIndex += argc;
+ }
+ if (index <= listPtr->active) {
+ listPtr->active += argc;
+ if ((listPtr->active >= listPtr->numElements)
+ && (listPtr->numElements > 0)) {
+ listPtr->active = listPtr->numElements-1;
+ }
+ }
+ listPtr->flags |= UPDATE_V_SCROLLBAR;
+ if (listPtr->maxWidth != oldMaxWidth) {
+ listPtr->flags |= UPDATE_H_SCROLLBAR;
+ }
+ ListboxComputeGeometry(listPtr);
+ ListboxRedrawRange(listPtr, index, listPtr->numElements-1);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteEls --
+ *
+ * Remove one or more elements from a listbox widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets freed, the listbox gets modified and (eventually)
+ * redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteEls(listPtr, first, last)
+ register Listbox *listPtr; /* Listbox widget to modify. */
+ int first; /* Index of first element to delete. */
+ int last; /* Index of last element to delete. */
+{
+ register Element *prevPtr, *elPtr;
+ int count, i, widthChanged;
+
+ /*
+ * Adjust the range to fit within the existing elements of the
+ * listbox, and make sure there's something to delete.
+ */
+
+ if (first < 0) {
+ first = 0;
+ }
+ if (last >= listPtr->numElements) {
+ last = listPtr->numElements-1;
+ }
+ count = last + 1 - first;
+ if (count <= 0) {
+ return;
+ }
+
+ /*
+ * Find the element just before the ones to delete.
+ */
+
+ if (first == 0) {
+ prevPtr = NULL;
+ } else {
+ for (i = first-1, prevPtr = listPtr->firstPtr; i > 0; i--) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+
+ /*
+ * Delete the requested number of elements.
+ */
+
+ widthChanged = 0;
+ for (i = count; i > 0; i--) {
+ if (prevPtr == NULL) {
+ elPtr = listPtr->firstPtr;
+ listPtr->firstPtr = elPtr->nextPtr;
+ if (listPtr->firstPtr == NULL) {
+ listPtr->lastPtr = NULL;
+ }
+ } else {
+ elPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = elPtr->nextPtr;
+ if (prevPtr->nextPtr == NULL) {
+ listPtr->lastPtr = prevPtr;
+ }
+ }
+ if (elPtr->textWidth == listPtr->maxWidth) {
+ widthChanged = 1;
+ }
+ if (elPtr->selected) {
+ listPtr->numSelected -= 1;
+ }
+ ckfree((char *) elPtr);
+ }
+ listPtr->numElements -= count;
+
+ /*
+ * Update the selection and viewing information to reflect the change
+ * in the element numbering, and redisplay to slide information up over
+ * the elements that were deleted.
+ */
+
+ if (first <= listPtr->selectAnchor) {
+ listPtr->selectAnchor -= count;
+ if (listPtr->selectAnchor < first) {
+ listPtr->selectAnchor = first;
+ }
+ }
+ if (first <= listPtr->topIndex) {
+ listPtr->topIndex -= count;
+ if (listPtr->topIndex < first) {
+ listPtr->topIndex = first;
+ }
+ }
+ if (listPtr->topIndex > (listPtr->numElements - listPtr->fullLines)) {
+ listPtr->topIndex = listPtr->numElements - listPtr->fullLines;
+ if (listPtr->topIndex < 0) {
+ listPtr->topIndex = 0;
+ }
+ }
+ if (listPtr->active > last) {
+ listPtr->active -= count;
+ } else if (listPtr->active >= first) {
+ listPtr->active = first;
+ if ((listPtr->active >= listPtr->numElements)
+ && (listPtr->numElements > 0)) {
+ listPtr->active = listPtr->numElements-1;
+ }
+ }
+ listPtr->flags |= UPDATE_V_SCROLLBAR;
+ ListboxComputeGeometry(listPtr);
+ if (widthChanged) {
+ int maxWidth = 0;
+
+ for (elPtr = listPtr->firstPtr; elPtr != NULL; elPtr = elPtr->nextPtr)
+ if (elPtr->textWidth > maxWidth)
+ maxWidth = elPtr->textWidth;
+ if (maxWidth != listPtr->maxWidth) {
+ listPtr->maxWidth = maxWidth;
+ listPtr->flags |= UPDATE_H_SCROLLBAR;
+ if (listPtr->xOffset + listPtr->width >= listPtr->maxWidth)
+ listPtr->xOffset = listPtr->maxWidth - listPtr->width;
+ if (listPtr->xOffset < 0)
+ listPtr->xOffset = 0;
+ }
+ }
+ ListboxRedrawRange(listPtr, first, listPtr->numElements-1);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ListboxEventProc --
+ *
+ * This procedure is invoked by the dispatcher for various
+ * events on listboxes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ListboxEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Listbox *listPtr = (Listbox *) clientData;
+
+ if (eventPtr->type == CK_EV_DESTROY) {
+ if (listPtr->winPtr != NULL) {
+ listPtr->winPtr = NULL;
+ Tcl_DeleteCommand(listPtr->interp,
+ Tcl_GetCommandName(listPtr->interp, listPtr->widgetCmd));
+ }
+ if (listPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
+ }
+ Ck_EventuallyFree((ClientData) listPtr,
+ (Ck_FreeProc *) DestroyListbox);
+ } else if (eventPtr->type == CK_EV_EXPOSE) {
+ listPtr->fullLines = listPtr->winPtr->height;
+ listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
+ ChangeListboxView(listPtr, listPtr->topIndex);
+ ChangeListboxOffset(listPtr, listPtr->xOffset);
+
+ /*
+ * Redraw the whole listbox. It's hard to tell what needs
+ * to be redrawn (e.g. if the listbox has shrunk then we
+ * may only need to redraw the borders), so just redraw
+ * everything for safety.
+ */
+
+ ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
+ } else if (eventPtr->type == CK_EV_FOCUSIN) {
+ listPtr->flags |= GOT_FOCUS;
+ ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
+ } else if (eventPtr->type == CK_EV_FOCUSOUT) {
+ listPtr->flags &= ~GOT_FOCUS;
+ ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GetListboxIndex --
+ *
+ * Parse an index into a listbox and return either its value
+ * or an error.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the index (into listPtr) corresponding to
+ * string. Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+GetListboxIndex(interp, listPtr, string, numElsOK, indexPtr)
+ Tcl_Interp *interp; /* For error messages. */
+ Listbox *listPtr; /* Listbox for which the index is being
+ * specified. */
+ char *string; /* Specifies an element in the listbox. */
+ int numElsOK; /* 0 means the return value must be less
+ * less than the number of entries in
+ * the listbox; 1 means it may also be
+ * equal to the number of entries. */
+ int *indexPtr; /* Where to store converted index. */
+{
+ int c;
+ size_t length;
+
+ length = strlen(string);
+ c = string[0];
+ if ((c == 'a') && (strncmp(string, "active", length) == 0)
+ && (length >= 2)) {
+ *indexPtr = listPtr->active;
+ } else if ((c == 'a') && (strncmp(string, "anchor", length) == 0)
+ && (length >= 2)) {
+ *indexPtr = listPtr->selectAnchor;
+ } else if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
+ *indexPtr = listPtr->numElements;
+ } else if (c == '@') {
+ int x, y;
+ char *p, *end;
+
+ p = string+1;
+ x = strtol(p, &end, 0);
+ if ((end == p) || (*end != ',')) {
+ goto badIndex;
+ }
+ p = end+1;
+ y = strtol(p, &end, 0);
+ if ((end == p) || (*end != 0)) {
+ goto badIndex;
+ }
+ *indexPtr = NearestListboxElement(listPtr, y);
+ } else {
+ if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ goto badIndex;
+ }
+ }
+ if (numElsOK) {
+ if (*indexPtr > listPtr->numElements) {
+ *indexPtr = listPtr->numElements;
+ }
+ } else if (*indexPtr >= listPtr->numElements) {
+ *indexPtr = listPtr->numElements-1;
+ }
+ if (*indexPtr < 0) {
+ *indexPtr = 0;
+ }
+ return TCL_OK;
+
+ badIndex:
+ Tcl_AppendResult(interp, "bad listbox index \"", string,
+ "\": must be active, anchor, end, @x,y, or a number",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangeListboxView --
+ *
+ * Change the view on a listbox widget so that a given element
+ * is displayed at the top.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * What's displayed on the screen is changed. If there is a
+ * scrollbar associated with this widget, then the scrollbar
+ * is instructed to change its display too.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeListboxView(listPtr, index)
+ register Listbox *listPtr; /* Information about widget. */
+ int index; /* Index of element in listPtr
+ * that should now appear at the
+ * top of the listbox. */
+{
+ if (index >= (listPtr->numElements - listPtr->fullLines)) {
+ index = listPtr->numElements - listPtr->fullLines;
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ if (listPtr->topIndex != index) {
+ listPtr->topIndex = index;
+ if (!(listPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
+ listPtr->flags |= REDRAW_PENDING;
+ }
+ listPtr->flags |= UPDATE_V_SCROLLBAR;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangListboxOffset --
+ *
+ * Change the horizontal offset for a listbox.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The listbox may be redrawn to reflect its new horizontal
+ * offset.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeListboxOffset(listPtr, offset)
+ register Listbox *listPtr; /* Information about widget. */
+ int offset; /* Desired new "xOffset" for
+ * listbox. */
+{
+ int maxOffset;
+
+ /*
+ * Make sure that the new offset is within the allowable range, and
+ * round it off to an even multiple of xScrollUnit.
+ */
+
+ maxOffset = listPtr->maxWidth - listPtr->winPtr->width;
+ if (offset > maxOffset) {
+ offset = maxOffset;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ listPtr->xOffset = offset;
+ listPtr->flags |= UPDATE_H_SCROLLBAR;
+ ListboxRedrawRange(listPtr, 0, listPtr->numElements);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * NearestListboxElement --
+ *
+ * Given a y-coordinate inside a listbox, compute the index of
+ * the element under that y-coordinate (or closest to that
+ * y-coordinate).
+ *
+ * Results:
+ * The return value is an index of an element of listPtr. If
+ * listPtr has no elements, then 0 is always returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NearestListboxElement(listPtr, y)
+ register Listbox *listPtr; /* Information about widget. */
+ int y; /* Y-coordinate in listPtr's window. */
+{
+ int index;
+
+ index = y;
+ if (index >= listPtr->fullLines) {
+ index = listPtr->fullLines - 1;
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ index += listPtr->topIndex;
+ if (index >= listPtr->numElements) {
+ index = listPtr->numElements-1;
+ }
+ return index;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxSelect --
+ *
+ * Select or deselect one or more elements in a listbox..
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All of the elements in the range between first and last are
+ * marked as either selected or deselected, depending on the
+ * "select" argument. Any items whose state changes are redisplayed.
+ * The selection is claimed from X when the number of selected
+ * elements changes from zero to non-zero.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxSelect(listPtr, first, last, select)
+ register Listbox *listPtr; /* Information about widget. */
+ int first; /* Index of first element to
+ * select or deselect. */
+ int last; /* Index of last element to
+ * select or deselect. */
+ int select; /* 1 means select items, 0 means
+ * deselect them. */
+{
+ int i, firstRedisplay, lastRedisplay, increment, oldCount;
+ Element *elPtr;
+
+ if (last < first) {
+ i = first;
+ first = last;
+ last = i;
+ }
+ if (first >= listPtr->numElements) {
+ return;
+ }
+ oldCount = listPtr->numSelected;
+ firstRedisplay = -1;
+ increment = select ? 1 : -1;
+ for (i = 0, elPtr = listPtr->firstPtr; i < first;
+ i++, elPtr = elPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ for ( ; i <= last; i++, elPtr = elPtr->nextPtr) {
+ if (elPtr->selected == select) {
+ continue;
+ }
+ listPtr->numSelected += increment;
+ elPtr->selected = select;
+ if (firstRedisplay < 0) {
+ firstRedisplay = i;
+ }
+ lastRedisplay = i;
+ }
+ if (firstRedisplay >= 0) {
+ ListboxRedrawRange(listPtr, first, last);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxRedrawRange --
+ *
+ * Ensure that a given range of elements is eventually redrawn on
+ * the display (if those elements in fact appear on the display).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxRedrawRange(listPtr, first, last)
+ register Listbox *listPtr; /* Information about widget. */
+ int first; /* Index of first element in list
+ * that needs to be redrawn. */
+ int last; /* Index of last element in list
+ * that needs to be redrawn. May
+ * be less than first;
+ * these just bracket a range. */
+{
+ if ((listPtr->winPtr == NULL) || !(listPtr->winPtr->flags & CK_MAPPED)
+ || (listPtr->flags & REDRAW_PENDING)) {
+ return;
+ }
+ Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
+ listPtr->flags |= REDRAW_PENDING;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxUpdateVScrollbar --
+ *
+ * This procedure is invoked whenever information has changed in
+ * a listbox in a way that would invalidate a vertical scrollbar
+ * display. If there is an associated scrollbar, then this command
+ * updates it by invoking a Tcl command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Tcl command is invoked, and an additional command may be
+ * invoked to process errors in the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxUpdateVScrollbar(listPtr)
+ register Listbox *listPtr; /* Information about widget. */
+{
+ char string[100];
+ double first, last;
+ int result;
+
+ if (listPtr->yScrollCmd == NULL) {
+ return;
+ }
+ if (listPtr->numElements == 0) {
+ first = 0.0;
+ last = 1.0;
+ } else {
+ first = listPtr->topIndex/((double) listPtr->numElements);
+ last = (listPtr->topIndex+listPtr->fullLines)
+ /((double) listPtr->numElements);
+ if (last > 1.0) {
+ last = 1.0;
+ }
+ }
+ sprintf(string, " %g %g", first, last);
+ result = Tcl_VarEval(listPtr->interp, listPtr->yScrollCmd, string,
+ (char *) NULL);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(listPtr->interp,
+ "\n (vertical scrolling command executed by listbox)");
+ Tk_BackgroundError(listPtr->interp);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ListboxUpdateHScrollbar --
+ *
+ * This procedure is invoked whenever information has changed in
+ * a listbox in a way that would invalidate a horizontal scrollbar
+ * display. If there is an associated horizontal scrollbar, then
+ * this command updates it by invoking a Tcl command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Tcl command is invoked, and an additional command may be
+ * invoked to process errors in the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ListboxUpdateHScrollbar(listPtr)
+ register Listbox *listPtr; /* Information about widget. */
+{
+ char string[60];
+ int result, windowWidth;
+ double first, last;
+
+ if (listPtr->xScrollCmd == NULL) {
+ return;
+ }
+ windowWidth = listPtr->winPtr->width;
+ if (listPtr->maxWidth == 0) {
+ first = 0;
+ last = 1.0;
+ } else {
+ first = listPtr->xOffset/((double) listPtr->maxWidth);
+ last = (listPtr->xOffset + windowWidth)
+ /((double) listPtr->maxWidth);
+ if (last > 1.0) {
+ last = 1.0;
+ }
+ }
+ sprintf(string, " %g %g", first, last);
+ result = Tcl_VarEval(listPtr->interp, listPtr->xScrollCmd, string,
+ (char *) NULL);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(listPtr->interp,
+ "\n (horizontal scrolling command executed by listbox)");
+ Tk_BackgroundError(listPtr->interp);
+ }
+}
--- /dev/null
+/*
+ * ckMain.c --
+ *
+ * This file contains a generic main program for Ck-based applications.
+ * It can be used as-is for many applications, just by supplying a
+ * different appInitProc procedure for each specific application.
+ * Or, it can be used as a template for creating new main programs
+ * for applications.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * Global variables used by the main program:
+ */
+
+static Tcl_Interp *interp; /* Interpreter for this application. */
+static char *fileName = NULL; /* Script to source, if any. */
+
+#ifdef TCL_MEM_DEBUG
+static char dumpFile[100]; /* Records where to dump memory allocation
+ * information. */
+static int quitFlag = 0; /* 1 means the "checkmem" command was
+ * invoked, so the application should quit
+ * and dump memory allocation information. */
+static int CheckmemCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char *argv[]));
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_Main --
+ *
+ * Main program for curses wish.
+ *
+ * Results:
+ * None. This procedure never returns (it exits the process when
+ * it's done.
+ *
+ * Side effects:
+ * This procedure initializes the toolkit and then starts
+ * interpreting commands; almost anything could happen, depending
+ * on the script being interpreted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_Main(argc, argv, appInitProc)
+ int argc; /* Number of arguments. */
+ char **argv; /* Array of argument strings. */
+ int (*appInitProc)(); /* Application-specific initialization
+ * procedure to call after most
+ * initialization but before starting
+ * to execute commands. */
+{
+ char *args, *msg, *argv0;
+ char buf[20];
+ int code;
+#if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+ Tcl_Channel errChannel;
+#endif
+
+#if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+ Tcl_FindExecutable(argv[0]);
+#endif
+
+ interp = Tcl_CreateInterp();
+
+#ifndef __WIN32__
+ if (!isatty(0) || !isatty(1)) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fprintf(stderr, "standard input/output must be terminal\n");
+
+#else
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel)
+ Tcl_Write(errChannel,
+ "standard input/output must be terminal\n", -1);
+#endif
+ Tcl_Eval(interp, "exit 1");
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Exit(1);
+#else
+ exit(1); /* Just in case */
+#endif
+ }
+#endif
+
+#ifdef TCL_MEM_DEBUG
+ Tcl_InitMemory(interp);
+ Tcl_InitMemory(interp);
+ Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0,
+ (Tcl_CmdDeleteProc *) NULL);
+#endif
+
+ /*
+ * Parse command-line arguments. Argv[1] must contain the name
+ * of the script file to process.
+ */
+
+ argv0 = argv[0];
+ if (argc > 1) {
+ fileName = argv[1];
+ argc--;
+ argv++;
+ }
+
+ /*
+ * Make command-line arguments available in the Tcl variables "argc"
+ * and "argv".
+ */
+
+ args = Tcl_Merge(argc-1, argv+1);
+ Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
+ ckfree(args);
+ sprintf(buf, "%d", argc-1);
+ Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv0,
+ TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_interactive", (fileName == NULL) ? "1" : "0",
+ TCL_GLOBAL_ONLY);
+
+ /*
+ * Invoke application-specific initialization.
+ */
+
+ if ((*appInitProc)(interp) != TCL_OK) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fprintf(stderr, "application-specific initialization failed: %s\n",
+ interp->result);
+#else
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_Write(errChannel,
+ "application-specific initialization failed: ", -1);
+ Tcl_Write(errChannel, interp->result, -1);
+ Tcl_Write(errChannel, "\n", 1);
+ }
+#endif
+ msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+ goto errorExit;
+ }
+
+ /*
+ * Invoke the script specified on the command line, if any.
+ */
+
+ if (fileName != NULL) {
+ code = Tcl_VarEval(interp, "source ", fileName, (char *) NULL);
+ if (code != TCL_OK)
+ goto error;
+ Tcl_ResetResult(interp);
+ goto mainLoop;
+ }
+
+ /*
+ * We're running interactively. Source a user-specific startup
+ * file if the application specified one and if the file exists.
+ */
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ if (tcl_RcFileName != NULL) {
+ Tcl_DString temp;
+ char *fullName;
+ FILE *f;
+
+ Tcl_DStringInit(&temp);
+ fullName = Tcl_TildeSubst(interp, fileName, &temp);
+ if (fullName == NULL)
+ fprintf(stderr, "%s\n", interp->result);
+ else {
+
+ /*
+ * Test for the existence of the rc file before trying to read it.
+ */
+
+ f = fopen(fullName, "r");
+ if (f != NULL) {
+ fclose(f);
+ if (Tcl_EvalFile(interp, fullName) != TCL_OK)
+ fprintf(stderr, "%s\n", interp->result);
+ }
+ Tcl_DStringFree(&temp);
+ }
+ }
+#else
+ fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);
+ if (fileName != NULL) {
+ Tcl_Channel c;
+ Tcl_DString temp;
+ char *fullName;
+
+ Tcl_DStringInit(&temp);
+ fullName = Tcl_TranslateFileName(interp, fileName, &temp);
+ if (fullName == NULL) {
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_Write(errChannel, interp->result, -1);
+ Tcl_Write(errChannel, "\n", 1);
+ }
+ } else {
+
+ /*
+ * Test for the existence of the rc file before trying to read it.
+ */
+
+ c = Tcl_OpenFileChannel(NULL, fullName, "r", 0);
+ if (c != (Tcl_Channel) NULL) {
+ Tcl_Close(NULL, c);
+ if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_Write(errChannel, interp->result, -1);
+ Tcl_Write(errChannel, "\n", 1);
+ }
+ }
+ }
+ Tcl_DStringFree(&temp);
+ }
+ }
+#endif
+
+mainLoop:
+ /*
+ * Loop infinitely, waiting for commands to execute.
+ */
+
+#ifdef TCL_MEM_DEBUG
+ Tcl_Eval(interp, "proc exit {{code 0}} {destroy .}");
+#endif
+
+ Ck_MainLoop();
+
+#ifdef TCL_MEM_DEBUG
+ if (quitFlag) {
+ Tcl_DeleteInterp(interp);
+ Tcl_DumpActiveMemory(dumpFile);
+ }
+#endif
+
+ /*
+ * Invoke Tcl exit command.
+ */
+
+ Tcl_Eval(interp, "exit");
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Exit(1);
+#else
+ exit(1); /* Just in case */
+#endif
+
+error:
+ msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+ if (msg == NULL) {
+ msg = interp->result;
+ }
+errorExit:
+ if (msg != NULL) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fprintf(stderr, "%s\n", msg);
+#else
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_Write(errChannel, msg, -1);
+ Tcl_Write(errChannel, "\n", 1);
+ }
+#endif
+ }
+ Tcl_Eval(interp, "exit 1");
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_Exit(1);
+#else
+ exit(1); /* Just in case */
+#endif
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckmemCmd --
+ *
+ * This is the command procedure for the "checkmem" command, which
+ * causes the application to exit after printing information about
+ * memory usage to the file passed to this command as its first
+ * argument.
+ *
+ * Results:
+ * Returns a standard Tcl completion code.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+#ifdef TCL_MEM_DEBUG
+
+static int
+CheckmemCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter for evaluation. */
+ int argc; /* Number of arguments. */
+ char *argv[]; /* String values of arguments. */
+{
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " fileName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ strcpy(dumpFile, argv[1]);
+ quitFlag = 1;
+ return TCL_OK;
+}
+#endif
+
--- /dev/null
+/*
+ * ckMenu.c --
+ *
+ * This module implements menus for the toolkit. The menus
+ * support normal button entries, plus check buttons, radio
+ * buttons, iconic forms of all of the above, and separator
+ * entries.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+#ifdef __WIN32__
+#define DestroyMenu CkDestroyMenu
+#endif
+
+/*
+ * One of the following data structures is kept for each entry of each
+ * menu managed by this file:
+ */
+
+typedef struct MenuEntry {
+ int type; /* Type of menu entry; see below for
+ * valid types. */
+ struct Menu *menuPtr; /* Menu with which this entry is associated. */
+ char *label; /* Main text label displayed in entry (NULL
+ * if no label). Malloc'ed. */
+ int labelLength; /* Number of non-NULL characters in label. */
+ int underline; /* Index of character to underline. */
+ char *accel; /* Accelerator string displayed at right
+ * of menu entry. NULL means no such
+ * accelerator. Malloc'ed. */
+ int accelLength; /* Number of non-NULL characters in
+ * accelerator. */
+
+ /*
+ * Information related to displaying entry:
+ */
+
+ Ck_Uid state; /* State of button for display purposes:
+ * normal, active, or disabled. */
+ int y; /* Y-coordinate of entry. */
+ int indicatorOn; /* True means draw indicator, false means
+ * don't draw it. */
+
+
+ int normalBg;
+ int normalFg;
+ int normalAttr;
+ int activeBg;
+ int activeFg;
+ int activeAttr;
+ int disabledBg;
+ int disabledFg;
+ int disabledAttr;
+ int underlineFg;
+ int underlineAttr;
+ int indicatorFg;
+
+ /*
+ * Information used to implement this entry's action:
+ */
+
+ char *command; /* Command to invoke when entry is invoked.
+ * Malloc'ed. */
+ char *name; /* Name of variable (for check buttons and
+ * radio buttons) or menu (for cascade
+ * entries). Malloc'ed.*/
+ char *onValue; /* Value to store in variable when selected
+ * (only for radio and check buttons).
+ * Malloc'ed. */
+ char *offValue; /* Value to store in variable when not
+ * selected (only for check buttons).
+ * Malloc'ed. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ int flags; /* Various flags. See below for definitions. */
+} MenuEntry;
+
+/*
+ * Flag values defined for menu entries:
+ *
+ * ENTRY_SELECTED: Non-zero means this is a radio or check
+ * button and that it should be drawn in
+ * the "selected" state.
+ * ENTRY_NEEDS_REDISPLAY: Non-zero means the entry should be redisplayed.
+ */
+
+#define ENTRY_SELECTED 1
+#define ENTRY_NEEDS_REDISPLAY 4
+
+/*
+ * Types defined for MenuEntries:
+ */
+
+#define COMMAND_ENTRY 0
+#define SEPARATOR_ENTRY 1
+#define CHECK_BUTTON_ENTRY 2
+#define RADIO_BUTTON_ENTRY 3
+#define CASCADE_ENTRY 4
+
+/*
+ * Mask bits for above types:
+ */
+
+#define COMMAND_MASK CK_CONFIG_USER_BIT
+#define SEPARATOR_MASK (CK_CONFIG_USER_BIT << 1)
+#define CHECK_BUTTON_MASK (CK_CONFIG_USER_BIT << 2)
+#define RADIO_BUTTON_MASK (CK_CONFIG_USER_BIT << 3)
+#define CASCADE_MASK (CK_CONFIG_USER_BIT << 4)
+#define ALL_MASK (COMMAND_MASK | SEPARATOR_MASK \
+ | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | CASCADE_MASK)
+
+/*
+ * Configuration specs for individual menu entries:
+ */
+
+static Ck_ConfigSpec entryConfigSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ACTIVE_ATTR, Ck_Offset(MenuEntry, activeAttr),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_COLOR, "-activebackground", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ACTIVE_BG, Ck_Offset(MenuEntry, activeBg),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_COLOR, "-activeforeground", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ACTIVE_FG, Ck_Offset(MenuEntry, activeFg),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_STRING, "-accelerator", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ACCELERATOR, Ck_Offset(MenuEntry, accel),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_NULL_OK},
+ {CK_CONFIG_ATTR, "-attributes", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ATTR, Ck_Offset(MenuEntry, normalAttr),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_BG, Ck_Offset(MenuEntry, normalBg),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_STRING, "-command", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_COMMAND, Ck_Offset(MenuEntry, command),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_FG, Ck_Offset(MenuEntry, normalFg),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_NULL_OK},
+ {CK_CONFIG_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_INDICATOR, Ck_Offset(MenuEntry, indicatorOn),
+ CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_STRING, "-label", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_LABEL, Ck_Offset(MenuEntry, label),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
+ {CK_CONFIG_STRING, "-menu", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_MENU, Ck_Offset(MenuEntry, name),
+ CASCADE_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-offvalue", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_OFF_VALUE, Ck_Offset(MenuEntry, offValue),
+ CHECK_BUTTON_MASK},
+ {CK_CONFIG_STRING, "-onvalue", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_ON_VALUE, Ck_Offset(MenuEntry, onValue),
+ CHECK_BUTTON_MASK},
+ {CK_CONFIG_COLOR, "-selectcolor", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_SELECT, Ck_Offset(MenuEntry, indicatorFg),
+ CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_UID, "-state", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_STATE, Ck_Offset(MenuEntry, state),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_STRING, "-value", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_VALUE, Ck_Offset(MenuEntry, onValue),
+ RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_CHECK_VARIABLE, Ck_Offset(MenuEntry, name),
+ CHECK_BUTTON_MASK|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_RADIO_VARIABLE, Ck_Offset(MenuEntry, name),
+ RADIO_BUTTON_MASK},
+ {CK_CONFIG_INT, "-underline", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_UNDERLINE, Ck_Offset(MenuEntry, underline),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_ATTR, "-underlineattributes", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_UNDERLINE, Ck_Offset(MenuEntry, underlineAttr),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_COLOR, "-underlineforeground", (char *) NULL, (char *) NULL,
+ DEF_MENU_ENTRY_UNDERLINE, Ck_Offset(MenuEntry, underlineFg),
+ COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
+ |CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * A data structure of the following type is kept for each
+ * menu managed by this file:
+ */
+
+typedef struct Menu {
+ CkWindow *winPtr; /* Window that embodies the pane. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with menu. */
+ Tcl_Command widgetCmd; /* Token for menu's widget command. */
+ MenuEntry **entries; /* Array of pointers to all the entries
+ * in the menu. NULL means no entries. */
+ int numEntries; /* Number of elements in entries. */
+ int active; /* Index of active entry. -1 means
+ * nothing active. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int normalBg;
+ int normalFg;
+ int normalAttr;
+ int activeBg;
+ int activeFg;
+ int activeAttr;
+ int disabledBg;
+ int disabledFg;
+ int disabledAttr;
+ int underlineFg;
+ int underlineAttr;
+ int indicatorFg;
+ CkBorder *borderPtr;
+ int labelWidth; /* Number of chars to allow for displaying
+ * labels in menu entries. */
+ int indicatorSpace; /* Number of chars for displaying
+ * indicators. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *postCommand; /* Command to execute just before posting
+ * this menu, or NULL. Malloc-ed. */
+ MenuEntry *postedCascade; /* Points to menu entry for cascaded
+ * submenu that is currently posted, or
+ * NULL if no submenu posted. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Menu;
+
+/*
+ * Flag bits for menus:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * RESIZE_PENDING: Non-zero means a call to ComputeMenuGeometry
+ * has already been scheduled.
+ */
+
+#define REDRAW_PENDING 1
+#define RESIZE_PENDING 2
+
+/*
+ * Configuration specs valid for the menu as a whole:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_MENU_ACTIVE_ATTR_COLOR,
+ Ck_Offset(Menu, activeAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_MENU_ACTIVE_ATTR_MONO,
+ Ck_Offset(Menu, activeAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENU_ACTIVE_BG_COLOR, Ck_Offset(Menu, activeBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENU_ACTIVE_BG_MONO, Ck_Offset(Menu, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENU_ACTIVE_FG_COLOR, Ck_Offset(Menu, activeFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENU_ACTIVE_FG_MONO, Ck_Offset(Menu, activeFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_MENU_ATTR, Ck_Offset(Menu, normalAttr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MENU_BG_COLOR, Ck_Offset(Menu, normalBg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MENU_BG_MONO, Ck_Offset(Menu, normalBg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_BORDER, "-border", "border", "Border",
+ DEF_MENU_BORDER, Ck_Offset(Menu, borderPtr), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_ATTR, "-disabledattributes", "disabledAttributes",
+ "DisabledAttributes", DEF_MENU_DISABLED_ATTR,
+ Ck_Offset(Menu, disabledAttr), 0},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "Foreground", DEF_MENU_DISABLED_BG_COLOR,
+ Ck_Offset(Menu, disabledBg), CK_CONFIG_COLOR_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "Foreground", DEF_MENU_DISABLED_BG_MONO,
+ Ck_Offset(Menu, disabledBg), CK_CONFIG_MONO_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR,
+ Ck_Offset(Menu, disabledFg), CK_CONFIG_COLOR_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENU_DISABLED_FG_MONO,
+ Ck_Offset(Menu, disabledFg), CK_CONFIG_MONO_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MENU_FG, Ck_Offset(Menu, normalFg), 0},
+ {CK_CONFIG_STRING, "-postcommand", "postCommand", "Command",
+ DEF_MENU_POST_COMMAND, Ck_Offset(Menu, postCommand),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
+ DEF_MENU_SELECT_COLOR, Ck_Offset(Menu, indicatorFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
+ DEF_MENU_SELECT_MONO, Ck_Offset(Menu, indicatorFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_MENU_TAKE_FOCUS, Ck_Offset(Menu, takeFocus), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_ATTR, "-underlineattributes", "underlineAttributes",
+ "UnderlineAttributes", DEF_MENU_UNDERLINE_ATTR,
+ Ck_Offset(Menu, underlineAttr), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_MENU_UNDERLINE_FG_COLOR,
+ Ck_Offset(Menu, underlineFg), CK_CONFIG_COLOR_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_MENU_UNDERLINE_FG_MONO,
+ Ck_Offset(Menu, underlineFg), CK_CONFIG_MONO_ONLY|CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int ActivateMenuEntry _ANSI_ARGS_((Menu *menuPtr,
+ int index));
+static void ComputeMenuGeometry _ANSI_ARGS_((
+ ClientData clientData));
+static int ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp,
+ Menu *menuPtr, int argc, char **argv,
+ int flags));
+static int ConfigureMenuEntry _ANSI_ARGS_((Tcl_Interp *interp,
+ Menu *menuPtr, MenuEntry *mePtr, int index,
+ int argc, char **argv, int flags));
+static void DestroyMenu _ANSI_ARGS_((ClientData clientData));
+static void DestroyMenuEntry _ANSI_ARGS_((ClientData clientData));
+static void DisplayMenu _ANSI_ARGS_((ClientData clientData));
+static void EventuallyRedrawMenu _ANSI_ARGS_((Menu *menuPtr,
+ MenuEntry *mePtr));
+static int GetMenuIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ Menu *menuPtr, char *string, int lastOK,
+ int *indexPtr));
+static int MenuAddOrInsert _ANSI_ARGS_((Tcl_Interp *interp,
+ Menu *menuPtr, char *indexString, int argc,
+ char **argv));
+static void MenuCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void MenuEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static MenuEntry * MenuNewEntry _ANSI_ARGS_((Menu *menuPtr, int index,
+ int type));
+static char * MenuVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static int MenuWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int PostSubmenu _ANSI_ARGS_((Tcl_Interp *interp,
+ Menu *menuPtr, MenuEntry *mePtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MenuCmd --
+ *
+ * This procedure is invoked to process the "menu" Tcl
+ * command. See the user documentation for details on
+ * what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_MenuCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *new;
+ register Menu *menuPtr;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Create the new window.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 1);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the data structure for the menu.
+ */
+
+ menuPtr = (Menu *) ckalloc(sizeof(Menu));
+ menuPtr->winPtr = new;
+ menuPtr->interp = interp;
+ menuPtr->widgetCmd = Tcl_CreateCommand(interp,
+ menuPtr->winPtr->pathName, MenuWidgetCmd,
+ (ClientData) menuPtr, MenuCmdDeletedProc);
+ menuPtr->entries = NULL;
+ menuPtr->numEntries = 0;
+ menuPtr->active = -1;
+ menuPtr->normalBg = 0;
+ menuPtr->normalFg = 0;
+ menuPtr->normalAttr = 0;
+ menuPtr->activeBg = 0;
+ menuPtr->activeFg = 0;
+ menuPtr->activeAttr = 0;
+ menuPtr->disabledBg = 0;
+ menuPtr->disabledFg = 0;
+ menuPtr->disabledAttr = 0;
+ menuPtr->underlineFg = 0;
+ menuPtr->underlineAttr = 0;
+ menuPtr->indicatorFg = 0;
+ menuPtr->borderPtr = NULL;
+ menuPtr->labelWidth = 0;
+ menuPtr->takeFocus = NULL;
+ menuPtr->postCommand = NULL;
+ menuPtr->postedCascade = NULL;
+ menuPtr->flags = 0;
+
+ Ck_SetClass(new, "Menu");
+ Ck_CreateEventHandler(menuPtr->winPtr,
+ CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MenuEventProc, (ClientData) menuPtr);
+ if (ConfigureMenu(interp, menuPtr, argc-2, argv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ interp->result = menuPtr->winPtr->pathName;
+ return TCL_OK;
+
+ error:
+ Ck_DestroyWindow(menuPtr->winPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MenuWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about menu widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Menu *menuPtr = (Menu *) clientData;
+ register MenuEntry *mePtr;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) menuPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
+ && (length >= 2)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " activate index\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (menuPtr->active == index) {
+ goto done;
+ }
+ if (index >= 0) {
+ if ((menuPtr->entries[index]->type == SEPARATOR_ENTRY)
+ || (menuPtr->entries[index]->state == ckDisabledUid)) {
+ index = -1;
+ }
+ }
+ result = ActivateMenuEntry(menuPtr, index);
+ } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)
+ && (length >= 2)) {
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " add type ?options?\"", (char *) NULL);
+ goto error;
+ }
+ if (MenuAddOrInsert(interp, menuPtr, (char *) NULL,
+ argc-2, argv+2) != TCL_OK) {
+ goto error;
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, menuPtr->winPtr, configSpecs,
+ (char *) menuPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, menuPtr->winPtr, configSpecs,
+ (char *) menuPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, menuPtr->winPtr, configSpecs,
+ (char *) menuPtr, argv[2], 0);
+ } else {
+ result = ConfigureMenu(interp, menuPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
+ int first, last, i, numDeleted;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " delete first ?last?\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &first) != TCL_OK) {
+ goto error;
+ }
+ if (argc == 3) {
+ last = first;
+ } else {
+ if (GetMenuIndex(interp, menuPtr, argv[3], 0, &last) != TCL_OK) {
+ goto error;
+ }
+ }
+ if ((first < 0) || (last < first)) {
+ goto done;
+ }
+ numDeleted = last + 1 - first;
+ for (i = first; i <= last; i++) {
+ Ck_EventuallyFree((ClientData) menuPtr->entries[i],
+ (Ck_FreeProc *) DestroyMenuEntry);
+ }
+ for (i = last+1; i < menuPtr->numEntries; i++) {
+ menuPtr->entries[i-numDeleted] = menuPtr->entries[i];
+ }
+ menuPtr->numEntries -= numDeleted;
+ if ((menuPtr->active >= first) && (menuPtr->active <= last)) {
+ menuPtr->active = -1;
+ } else if (menuPtr->active > last) {
+ menuPtr->active -= numDeleted;
+ }
+ if (!(menuPtr->flags & RESIZE_PENDING)) {
+ menuPtr->flags |= RESIZE_PENDING;
+ Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
+ }
+ } else if ((c == 'e') && (length >= 7)
+ && (strncmp(argv[1], "entrycget", length) == 0)) {
+ int index;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " entrycget index option\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ goto done;
+ }
+ mePtr = menuPtr->entries[index];
+ Ck_Preserve((ClientData) mePtr);
+ result = Ck_ConfigureValue(interp, menuPtr->winPtr, entryConfigSpecs,
+ (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type);
+ Ck_Release((ClientData) mePtr);
+ } else if ((c == 'e') && (length >= 7)
+ && (strncmp(argv[1], "entryconfigure", length) == 0)) {
+ int index;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " entryconfigure index ?option value ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ goto done;
+ }
+ mePtr = menuPtr->entries[index];
+ Ck_Preserve((ClientData) mePtr);
+ if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, menuPtr->winPtr,
+ entryConfigSpecs, (char *) mePtr, (char *) NULL,
+ COMMAND_MASK << mePtr->type);
+ } else if (argc == 4) {
+ result = Ck_ConfigureInfo(interp, menuPtr->winPtr,
+ entryConfigSpecs,
+ (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type);
+ } else {
+ result = ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc-3,
+ argv+3, CK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
+ }
+ Ck_Release((ClientData) mePtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " index string\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ interp->result = "none";
+ } else {
+ sprintf(interp->result, "%d", index);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
+ && (length >= 3)) {
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " insert index type ?options?\"", (char *) NULL);
+ goto error;
+ }
+ if (MenuAddOrInsert(interp, menuPtr, argv[2],
+ argc-3, argv+3) != TCL_OK) {
+ goto error;
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
+ && (length >= 3)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " invoke index\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ goto done;
+ }
+ mePtr = menuPtr->entries[index];
+ if (mePtr->state == ckDisabledUid) {
+ goto done;
+ }
+ Ck_Preserve((ClientData) mePtr);
+ if (mePtr->type == CHECK_BUTTON_ENTRY) {
+ if (mePtr->flags & ENTRY_SELECTED) {
+ if (Tcl_SetVar(interp, mePtr->name, mePtr->offValue,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ }
+ } else {
+ if (Tcl_SetVar(interp, mePtr->name, mePtr->onValue,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ }
+ }
+ } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
+ if (Tcl_SetVar(interp, mePtr->name, mePtr->onValue,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+ result = TCL_ERROR;
+ }
+ }
+ if ((result == TCL_OK) && (mePtr->command != NULL)) {
+ result = CkCopyAndGlobalEval(interp, mePtr->command);
+ }
+ if ((result == TCL_OK) && (mePtr->type == CASCADE_ENTRY)) {
+ result = PostSubmenu(menuPtr->interp, menuPtr, mePtr);
+ }
+ Ck_Release((ClientData) mePtr);
+ } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)
+ && (length == 4)) {
+ int x, y, tmp;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " post x y\"", (char *) NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+
+ /*
+ * De-activate any active element.
+ */
+
+ ActivateMenuEntry(menuPtr, -1);
+
+ /*
+ * If there is a command for the menu, execute it. This
+ * may change the size of the menu, so be sure to recompute
+ * the menu's geometry if needed.
+ */
+
+ if (menuPtr->postCommand != NULL) {
+ result = CkCopyAndGlobalEval(menuPtr->interp,
+ menuPtr->postCommand);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (menuPtr->flags & RESIZE_PENDING) {
+ Tk_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
+ ComputeMenuGeometry((ClientData) menuPtr);
+ }
+ }
+ if (menuPtr->borderPtr != NULL)
+ x -= 1;
+ tmp = menuPtr->winPtr->mainPtr->maxWidth - menuPtr->winPtr->reqWidth;
+ if (x > tmp) {
+ x = tmp;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ tmp = menuPtr->winPtr->mainPtr->maxHeight - menuPtr->winPtr->reqHeight;
+ if (y > tmp) {
+ y = tmp;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ if (x != menuPtr->winPtr->x || y != menuPtr->winPtr->y) {
+ Ck_MoveWindow(menuPtr->winPtr, x, y);
+ }
+ if (menuPtr->winPtr->reqWidth != menuPtr->winPtr->width ||
+ menuPtr->winPtr->reqHeight != menuPtr->winPtr->reqHeight) {
+ Ck_ResizeWindow(menuPtr->winPtr,
+ menuPtr->winPtr->reqWidth, menuPtr->winPtr->reqHeight);
+ }
+ if (!(menuPtr->winPtr->flags & CK_MAPPED)) {
+ Ck_MapWindow(menuPtr->winPtr);
+ }
+ } else if ((c == 'p') && (strncmp(argv[1], "postcascade", length) == 0)
+ && (length > 4)) {
+ int index;
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " postcascade index\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if ((index < 0) || (menuPtr->entries[index]->type != CASCADE_ENTRY)) {
+ result = PostSubmenu(interp, menuPtr, (MenuEntry *) NULL);
+ } else {
+ result = PostSubmenu(interp, menuPtr, menuPtr->entries[index]);
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
+ int index;
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " type index\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ goto done;
+ }
+ mePtr = menuPtr->entries[index];
+ switch (mePtr->type) {
+ case COMMAND_ENTRY:
+ interp->result = "command";
+ break;
+ case SEPARATOR_ENTRY:
+ interp->result = "separator";
+ break;
+ case CHECK_BUTTON_ENTRY:
+ interp->result = "checkbutton";
+ break;
+ case RADIO_BUTTON_ENTRY:
+ interp->result = "radiobutton";
+ break;
+ case CASCADE_ENTRY:
+ interp->result = "cascade";
+ break;
+ }
+ } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " unpost\"", (char *) NULL);
+ goto error;
+ }
+ Ck_UnmapWindow(menuPtr->winPtr);
+ if (result == TCL_OK) {
+ result = PostSubmenu(interp, menuPtr, (MenuEntry *) NULL);
+ }
+ } else if ((c == 'y') && (strncmp(argv[1], "yposition", length) == 0)) {
+ int index;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " yposition index\"", (char *) NULL);
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, argv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index < 0) {
+ interp->result = "0";
+ } else {
+ sprintf(interp->result, "%d", menuPtr->entries[index]->y);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be activate, add, cget, configure, delete, ",
+ "entrycget, entryconfigure, index, insert, invoke, ",
+ "post, postcascade, type, unpost, or yposition",
+ (char *) NULL);
+ goto error;
+ }
+ done:
+ Ck_Release((ClientData) menuPtr);
+ return result;
+
+ error:
+ Ck_Release((ClientData) menuPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyMenu --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a menu at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the menu is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyMenu(clientData)
+ ClientData clientData; /* Info about menu widget. */
+{
+ register Menu *menuPtr = (Menu *) clientData;
+ int i;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ DestroyMenuEntry((ClientData) menuPtr->entries[i]);
+ }
+ if (menuPtr->entries != NULL) {
+ ckfree((char *) menuPtr->entries);
+ }
+ Ck_FreeOptions(configSpecs, (char *) menuPtr, 0);
+ ckfree((char *) menuPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyMenuEntry --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a menu entry at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the menu entry is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyMenuEntry(clientData)
+ ClientData clientData; /* Pointer to entry to be freed. */
+{
+ register MenuEntry *mePtr = (MenuEntry *) clientData;
+ Menu *menuPtr = mePtr->menuPtr;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (menuPtr->postedCascade == mePtr) {
+ /*
+ * Ignore errors while unposting the menu, since it's possible
+ * that the menu has already been deleted and the unpost will
+ * generate an error.
+ */
+
+ PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL);
+ }
+ if (mePtr->name != NULL) {
+ Tcl_UntraceVar(menuPtr->interp, mePtr->name,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuVarProc, (ClientData) mePtr);
+ }
+ Ck_FreeOptions(entryConfigSpecs, (char *) mePtr,
+ (COMMAND_MASK << mePtr->type));
+ ckfree((char *) mePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureMenu --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or reconfigure)
+ * a menu widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as colors, font, etc. get set
+ * for menuPtr; old resources get freed, if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureMenu(interp, menuPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Menu *menuPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ int i;
+
+ if (Ck_ConfigureWidget(interp, menuPtr->winPtr, configSpecs,
+ argc, argv, (char *) menuPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * After reconfiguring a menu, we need to reconfigure all of the
+ * entries in the menu, since some of the things in the children
+ * (such as graphics contexts) may have to change to reflect changes
+ * in the parent.
+ */
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ MenuEntry *mePtr;
+
+ mePtr = menuPtr->entries[i];
+ ConfigureMenuEntry(interp, menuPtr, mePtr, i, 0, (char **) NULL,
+ CK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
+ }
+
+ Ck_SetInternalBorder(menuPtr->winPtr, menuPtr->borderPtr != NULL);
+
+ if (!(menuPtr->flags & RESIZE_PENDING)) {
+ menuPtr->flags |= RESIZE_PENDING;
+ Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureMenuEntry --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or reconfigure)
+ * one entry in a menu.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as label and accelerator get
+ * set for mePtr; old resources get freed, if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Menu *menuPtr; /* Information about whole menu. */
+ register MenuEntry *mePtr; /* Information about menu entry; may
+ * or may not already have values for
+ * some fields. */
+ int index; /* Index of mePtr within menuPtr's
+ * entries. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Additional flags to pass to
+ * Tk_ConfigureWidget. */
+{
+ /*
+ * If this entry is a cascade and the cascade is posted, then unpost
+ * it before reconfiguring the entry (otherwise the reconfigure might
+ * change the name of the cascaded entry, leaving a posted menu
+ * high and dry).
+ */
+
+ if (menuPtr->postedCascade == mePtr) {
+ if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL)
+ != TCL_OK) {
+ Tk_BackgroundError(menuPtr->interp);
+ }
+ }
+
+ /*
+ * If this entry is a check button or radio button, then remove
+ * its old trace procedure.
+ */
+
+ if ((mePtr->name != NULL) &&
+ ((mePtr->type == CHECK_BUTTON_ENTRY)
+ || (mePtr->type == RADIO_BUTTON_ENTRY))) {
+ Tcl_UntraceVar(menuPtr->interp, mePtr->name,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuVarProc, (ClientData) mePtr);
+ }
+
+ if (Ck_ConfigureWidget(interp, menuPtr->winPtr, entryConfigSpecs,
+ argc, argv, (char *) mePtr,
+ flags | (COMMAND_MASK << mePtr->type)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * The code below handles special configuration stuff not taken
+ * care of by Ck_ConfigureWidget, such as special processing for
+ * defaults, sizing strings, graphics contexts, etc.
+ */
+
+ if (mePtr->label == NULL) {
+ mePtr->labelLength = 0;
+ } else {
+ mePtr->labelLength = strlen(mePtr->label);
+ }
+ if (mePtr->accel == NULL) {
+ mePtr->accelLength = 0;
+ } else {
+ mePtr->accelLength = strlen(mePtr->accel);
+ }
+
+ if (mePtr->state == ckActiveUid) {
+ if (index != menuPtr->active) {
+ ActivateMenuEntry(menuPtr, index);
+ }
+ } else {
+ if (index == menuPtr->active) {
+ ActivateMenuEntry(menuPtr, -1);
+ }
+ if ((mePtr->state != ckNormalUid) && (mePtr->state != ckDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", mePtr->state,
+ "\": must be normal, active, or disabled", (char *) NULL);
+ mePtr->state = ckNormalUid;
+ return TCL_ERROR;
+ }
+ }
+
+ if ((mePtr->type == CHECK_BUTTON_ENTRY)
+ || (mePtr->type == RADIO_BUTTON_ENTRY)) {
+ char *value;
+
+ if (mePtr->name == NULL) {
+ mePtr->name = (char *) ckalloc(mePtr->labelLength + 1);
+ strcpy(mePtr->name, (mePtr->label == NULL) ? "" : mePtr->label);
+ }
+ if (mePtr->onValue == NULL) {
+ mePtr->onValue = (char *) ckalloc(mePtr->labelLength + 1);
+ strcpy(mePtr->onValue, (mePtr->label == NULL) ? "" : mePtr->label);
+ }
+
+ /*
+ * Select the entry if the associated variable has the
+ * appropriate value, initialize the variable if it doesn't
+ * exist, then set a trace on the variable to monitor future
+ * changes to its value.
+ */
+
+ value = Tcl_GetVar(interp, mePtr->name, TCL_GLOBAL_ONLY);
+ mePtr->flags &= ~ENTRY_SELECTED;
+ if (value != NULL) {
+ if (strcmp(value, mePtr->onValue) == 0) {
+ mePtr->flags |= ENTRY_SELECTED;
+ }
+ } else {
+ Tcl_SetVar(interp, mePtr->name,
+ (mePtr->type == CHECK_BUTTON_ENTRY) ? mePtr->offValue : "",
+ TCL_GLOBAL_ONLY);
+ }
+ Tcl_TraceVar(interp, mePtr->name,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuVarProc, (ClientData) mePtr);
+ }
+
+ if (!(menuPtr->flags & RESIZE_PENDING)) {
+ menuPtr->flags |= RESIZE_PENDING;
+ Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ComputeMenuGeometry --
+ *
+ * This procedure is invoked to recompute the size and
+ * layout of a menu. It is called as a when-idle handler so
+ * that it only gets done once, even if a group of changes is
+ * made to the menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Fields of menu entries are changed to reflect their
+ * current positions, and the size of the menu window
+ * itself may be changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ComputeMenuGeometry(clientData)
+ ClientData clientData; /* Structure describing menu. */
+{
+ Menu *menuPtr = (Menu *) clientData;
+ CkWindow *winPtr = menuPtr->winPtr;
+ register MenuEntry *mePtr;
+ int maxLabelWidth, maxIndicatorWidth, maxAccelWidth;
+ int width, height, indicatorSpace, dummy;
+ int i, y;
+
+ if (menuPtr->winPtr == NULL) {
+ return;
+ }
+
+ maxLabelWidth = maxIndicatorWidth = maxAccelWidth = 0;
+ y = 0;
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ mePtr = menuPtr->entries[i];
+ indicatorSpace = 0;
+
+ if (mePtr->label != NULL) {
+ CkMeasureChars(winPtr->mainPtr,
+ mePtr->label, mePtr->labelLength, 0,
+ 100000, 0, CK_NEWLINES_NOT_SPECIAL, &width, &dummy);
+ } else {
+ width = 0;
+ }
+ if (mePtr->indicatorOn && (mePtr->type == CHECK_BUTTON_ENTRY ||
+ mePtr->type == RADIO_BUTTON_ENTRY)) {
+ indicatorSpace = 4;
+ }
+ if (width > maxLabelWidth) {
+ maxLabelWidth = width;
+ }
+ if (mePtr->type == CASCADE_ENTRY) {
+ width = 2;
+ } else if (mePtr->accel != NULL) {
+ CkMeasureChars(winPtr->mainPtr,
+ mePtr->accel, mePtr->accelLength, 0,
+ 100000, 0, CK_NEWLINES_NOT_SPECIAL, &width, &dummy);
+ } else {
+ width = 0;
+ }
+ if (width > maxAccelWidth) {
+ maxAccelWidth = width;
+ }
+ if (indicatorSpace > maxIndicatorWidth) {
+ maxIndicatorWidth = indicatorSpace;
+ }
+ mePtr->y = y;
+ y++;
+ }
+
+ /*
+ * Got all the sizes. Update fields in the menu structure, then
+ * resize the window if necessary. Leave margins on either side
+ * of the indicator (or just one margin if there is no indicator).
+ * Leave another margin on the right side of the label, plus yet
+ * another margin to the right of the accelerator (if there is one).
+ */
+
+ menuPtr->indicatorSpace = maxIndicatorWidth;
+ menuPtr->labelWidth = maxLabelWidth;
+ width = menuPtr->indicatorSpace + menuPtr->labelWidth + maxAccelWidth;
+ height = y;
+
+ if (width <= 0) {
+ width = 1;
+ }
+ if (height <= 0) {
+ height = 1;
+ }
+
+ if (menuPtr->borderPtr != NULL) {
+ width += 2;
+ height += 2;
+ }
+ if (width != menuPtr->winPtr->reqWidth ||
+ height != menuPtr->winPtr->reqHeight) {
+ Ck_GeometryRequest(menuPtr->winPtr, width, height);
+ } else {
+ /*
+ * Must always force a redisplay here if the window is mapped
+ * (even if the size didn't change, something else might have
+ * changed in the menu, such as a label or accelerator). The
+ * resize will force a redisplay above.
+ */
+
+ EventuallyRedrawMenu(menuPtr, (MenuEntry *) NULL);
+ }
+ menuPtr->flags &= ~RESIZE_PENDING;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayMenu --
+ *
+ * This procedure is invoked to display a menu widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayMenu(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register Menu *menuPtr = (Menu *) clientData;
+ register MenuEntry *mePtr;
+ register CkWindow *winPtr = menuPtr->winPtr;
+ int index, leftEdge, x, y, cursorX, cursorY;
+ int fg, nFg, aFg, dFg;
+ int bg, nBg, aBg, dBg;
+ int attr, nAt, aAt, dAt;
+
+ menuPtr->flags &= ~REDRAW_PENDING;
+ if (menuPtr->winPtr == NULL || !(winPtr->flags & CK_MAPPED))
+ return;
+
+ x = cursorX = menuPtr->borderPtr != NULL ? 1 : 0;
+ y = cursorY = menuPtr->borderPtr != NULL ? 1 : 0;
+
+ /*
+ * Loop through all of the entries, drawing them one at a time.
+ */
+
+ leftEdge = menuPtr->indicatorSpace + x;
+
+ for (index = 0; index < menuPtr->numEntries; index++, y++) {
+ mePtr = menuPtr->entries[index];
+ if (mePtr->state == ckActiveUid) {
+ cursorY = y;
+ if (mePtr->type == CASCADE_ENTRY)
+ cursorX = winPtr->width - x - 1;
+ else if (mePtr->type == CHECK_BUTTON_ENTRY ||
+ mePtr->type == RADIO_BUTTON_ENTRY)
+ cursorX = x + 1;
+ }
+ if (!(mePtr->flags & ENTRY_NEEDS_REDISPLAY)) {
+ continue;
+ }
+ mePtr->flags &= ~ENTRY_NEEDS_REDISPLAY;
+
+ /*
+ * Colors.
+ */
+
+ nBg = mePtr->normalBg < 0 ? menuPtr->normalBg : mePtr->normalBg;
+ aBg = mePtr->activeBg < 0 ? menuPtr->activeBg : mePtr->activeBg;
+ dBg = mePtr->disabledBg < 0 ? menuPtr->disabledBg : mePtr->disabledBg;
+ nFg = mePtr->normalFg < 0 ? menuPtr->normalFg : mePtr->normalFg;
+ aFg = mePtr->activeFg < 0 ? menuPtr->activeFg : mePtr->activeFg;
+ dFg = mePtr->disabledFg < 0 ? menuPtr->disabledFg : mePtr->disabledFg;
+ nAt = mePtr->normalAttr < 0 ? menuPtr->normalAttr : mePtr->normalAttr;
+ aAt = mePtr->activeAttr < 0 ? menuPtr->activeAttr : mePtr->activeAttr;
+ dAt = mePtr->disabledAttr < 0 ? menuPtr->disabledAttr :
+ mePtr->disabledAttr;
+
+ if (mePtr->state == ckActiveUid) {
+ bg = aBg; fg = aFg; attr = aAt;
+ } else if (mePtr->state == ckDisabledUid) {
+ bg = dBg; fg = dFg; attr = dAt;
+ } else {
+ bg = nBg; fg = nFg; attr = nAt;
+ }
+
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ Ck_ClearToEol(winPtr, x, y);
+
+ if (mePtr->label != NULL) {
+ CkDisplayChars(winPtr->mainPtr, winPtr->window,
+ mePtr->label, mePtr->labelLength,
+ leftEdge, y, leftEdge,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
+ if (mePtr->underline >= 0 && mePtr->state == ckNormalUid) {
+ Ck_SetWindowAttr(winPtr, mePtr->underlineFg < 0 ?
+ menuPtr->underlineFg : mePtr->underlineFg, bg,
+ mePtr->underlineAttr < 0 ? menuPtr->underlineAttr :
+ mePtr->underlineAttr);
+ CkUnderlineChars(winPtr->mainPtr, winPtr->window, mePtr->label,
+ mePtr->labelLength, leftEdge, y, leftEdge,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ mePtr->underline, mePtr->underline);
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ }
+ }
+
+ /*
+ * Draw accelerator or cascade arrow.
+ */
+
+ if (mePtr->type == CASCADE_ENTRY) {
+ int gchar;
+
+ Ck_GetGChar(menuPtr->interp, "rarrow", &gchar);
+ mvwaddch(winPtr->window, y, winPtr->width - x - 1, gchar);
+ } else if (mePtr->accel != NULL) {
+ CkDisplayChars(winPtr->mainPtr, winPtr->window,
+ mePtr->accel, mePtr->accelLength,
+ leftEdge + menuPtr->labelWidth, y,
+ leftEdge + menuPtr->labelWidth,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
+ }
+
+ /*
+ * Draw check-button/radio-button indicators.
+ */
+
+ if (mePtr->indicatorOn && (mePtr->type == CHECK_BUTTON_ENTRY ||
+ mePtr->type == RADIO_BUTTON_ENTRY)) {
+ wmove(winPtr->window, y, x);
+ Ck_SetWindowAttr(winPtr, nFg, nBg, nAt);
+ waddstr(winPtr->window, mePtr->type == CHECK_BUTTON_ENTRY ?
+ "[ ]" : "( )");
+ if (mePtr->flags & ENTRY_SELECTED) {
+ int gchar;
+
+ Ck_GetGChar(menuPtr->interp,
+ mePtr->type == CHECK_BUTTON_ENTRY ? "diamond" : "bullet",
+ &gchar);
+ Ck_SetWindowAttr(winPtr, mePtr->indicatorFg < 0 ?
+ menuPtr->indicatorFg : mePtr->indicatorFg, nBg, nAt);
+ mvwaddch(winPtr->window, y, x + 1, gchar);
+ }
+ }
+
+ /*
+ * Draw separator.
+ */
+
+ if (mePtr->type == SEPARATOR_ENTRY) {
+ int i, gchar;
+
+ wmove(winPtr->window, y, x);
+ Ck_SetWindowAttr(winPtr, nFg, nBg, nAt);
+ Ck_GetGChar(menuPtr->interp, "hline", &gchar);
+ for (i = x; i < winPtr->width - x; i++)
+ waddch(winPtr->window, gchar);
+ }
+ }
+ if (menuPtr->borderPtr != NULL) {
+ Ck_SetWindowAttr(winPtr, menuPtr->normalFg, menuPtr->normalBg,
+ menuPtr->normalAttr);
+ Ck_DrawBorder(winPtr, menuPtr->borderPtr, 0, 0,
+ winPtr->width, winPtr->height);
+ }
+ wmove(winPtr->window, cursorY, cursorX);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GetMenuIndex --
+ *
+ * Parse a textual index into a menu and return the numerical
+ * index of the indicated entry.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the entry index corresponding to string
+ * (ranges from -1 to the number of entries in the menu minus
+ * one). Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+GetMenuIndex(interp, menuPtr, string, lastOK, indexPtr)
+ Tcl_Interp *interp; /* For error messages. */
+ Menu *menuPtr; /* Menu for which the index is being
+ * specified. */
+ char *string; /* Specification of an entry in menu. See
+ * manual entry for valid .*/
+ int lastOK; /* Non-zero means its OK to return index
+ * just *after* last entry. */
+ int *indexPtr; /* Where to store converted relief. */
+{
+ int i;
+
+ if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {
+ *indexPtr = menuPtr->active;
+ return TCL_OK;
+ }
+
+ if (((string[0] == 'l') && (strcmp(string, "last") == 0))
+ || ((string[0] == 'e') && (strcmp(string, "end") == 0))) {
+ *indexPtr = menuPtr->numEntries - ((lastOK) ? 0 : 1);
+ return TCL_OK;
+ }
+
+ if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {
+ *indexPtr = -1;
+ return TCL_OK;
+ }
+
+ if (string[0] == '@') {
+ if (Tcl_GetInt(interp, string+1, &i) == TCL_OK) {
+ if (menuPtr->borderPtr != NULL)
+ i -= 1;
+ if (i >= menuPtr->numEntries)
+ i = -1;
+ if (i < 0)
+ i = -1;
+ *indexPtr = i;
+ return TCL_OK;
+ } else {
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ }
+ }
+
+ if (isdigit((unsigned char) string[0])) {
+ if (Tcl_GetInt(interp, string, &i) == TCL_OK) {
+ if (i >= menuPtr->numEntries) {
+ if (lastOK) {
+ i = menuPtr->numEntries;
+ } else {
+ i = menuPtr->numEntries-1;
+ }
+ } else if (i < 0) {
+ i = -1;
+ }
+ *indexPtr = i;
+ return TCL_OK;
+ }
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ }
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ char *label;
+
+ label = menuPtr->entries[i]->label;
+ if ((label != NULL)
+ && (Tcl_StringMatch(menuPtr->entries[i]->label, string))) {
+ *indexPtr = i;
+ return TCL_OK;
+ }
+ }
+
+ Tcl_AppendResult(interp, "bad menu entry index \"",
+ string, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on menus.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MenuEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Menu *menuPtr = (Menu *) clientData;
+ if (eventPtr->type == CK_EV_EXPOSE || eventPtr->type == CK_EV_MAP) {
+ EventuallyRedrawMenu(menuPtr, (MenuEntry *) NULL);
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (menuPtr->winPtr != NULL) {
+ menuPtr->winPtr = NULL;
+ Tcl_DeleteCommand(menuPtr->interp,
+ Tcl_GetCommandName(menuPtr->interp, menuPtr->widgetCmd));
+ }
+ if (menuPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayMenu, (ClientData) menuPtr);
+ }
+ if (menuPtr->flags & RESIZE_PENDING) {
+ Tk_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
+ }
+ Ck_EventuallyFree((ClientData) menuPtr, (Ck_FreeProc *) DestroyMenu);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Menu *menuPtr = (Menu *) clientData;
+ CkWindow *winPtr = menuPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case tkwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ menuPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuNewEntry --
+ *
+ * This procedure allocates and initializes a new menu entry.
+ *
+ * Results:
+ * The return value is a pointer to a new menu entry structure,
+ * which has been malloc-ed, initialized, and entered into the
+ * entry array for the menu.
+ *
+ * Side effects:
+ * Storage gets allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static MenuEntry *
+MenuNewEntry(menuPtr, index, type)
+ Menu *menuPtr; /* Menu that will hold the new entry. */
+ int index; /* Where in the menu the new entry is to
+ * go. */
+ int type; /* The type of the new entry. */
+{
+ MenuEntry *mePtr;
+ MenuEntry **newEntries;
+ int i;
+
+ /*
+ * Create a new array of entries with an empty slot for the
+ * new entry.
+ */
+
+ newEntries = (MenuEntry **) ckalloc((unsigned)
+ ((menuPtr->numEntries+1)*sizeof(MenuEntry *)));
+ for (i = 0; i < index; i++) {
+ newEntries[i] = menuPtr->entries[i];
+ }
+ for ( ; i < menuPtr->numEntries; i++) {
+ newEntries[i+1] = menuPtr->entries[i];
+ }
+ if (menuPtr->numEntries != 0) {
+ ckfree((char *) menuPtr->entries);
+ }
+ menuPtr->entries = newEntries;
+ menuPtr->numEntries++;
+ menuPtr->entries[index] = mePtr = (MenuEntry *) ckalloc(sizeof(MenuEntry));
+ mePtr->type = type;
+ mePtr->menuPtr = menuPtr;
+ mePtr->label = NULL;
+ mePtr->labelLength = 0;
+ mePtr->underline = -1;
+ mePtr->accel = NULL;
+ mePtr->accelLength = 0;
+ mePtr->state = ckNormalUid;
+ mePtr->y = 0;
+ mePtr->indicatorOn = 1;
+ mePtr->normalBg = -1;
+ mePtr->normalFg = -1;
+ mePtr->normalAttr = -1;
+ mePtr->activeBg = -1;
+ mePtr->activeFg = -1;
+ mePtr->activeAttr = -1;
+ mePtr->disabledBg = -1;
+ mePtr->disabledFg = -1;
+ mePtr->disabledAttr = -1;
+ mePtr->underlineFg = -1;
+ mePtr->underlineAttr = -1;
+ mePtr->indicatorFg = -1;
+ mePtr->command = NULL;
+ mePtr->name = NULL;
+ mePtr->onValue = NULL;
+ mePtr->offValue = NULL;
+ mePtr->flags = 0;
+ return mePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuAddOrInsert --
+ *
+ * This procedure does all of the work of the "add" and "insert"
+ * widget commands, allowing the code for these to be shared.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * A new menu entry is created in menuPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+MenuAddOrInsert(interp, menuPtr, indexString, argc, argv)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Menu *menuPtr; /* Widget in which to create new
+ * entry. */
+ char *indexString; /* String describing index at which
+ * to insert. NULL means insert at
+ * end. */
+ int argc; /* Number of elements in argv. */
+ char **argv; /* Arguments to command: first arg
+ * is type of entry, others are
+ * config options. */
+{
+ int c, type, i, index;
+ size_t length;
+ MenuEntry *mePtr;
+
+ if (indexString != NULL) {
+ if (GetMenuIndex(interp, menuPtr, indexString, 1, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ index = menuPtr->numEntries;
+ }
+ if (index < 0) {
+ Tcl_AppendResult(interp, "bad index \"", indexString, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Figure out the type of the new entry.
+ */
+
+ c = argv[0][0];
+ length = strlen(argv[0]);
+ if ((c == 'c') && (strncmp(argv[0], "cascade", length) == 0)
+ && (length >= 2)) {
+ type = CASCADE_ENTRY;
+ } else if ((c == 'c') && (strncmp(argv[0], "checkbutton", length) == 0)
+ && (length >= 2)) {
+ type = CHECK_BUTTON_ENTRY;
+ } else if ((c == 'c') && (strncmp(argv[0], "command", length) == 0)
+ && (length >= 2)) {
+ type = COMMAND_ENTRY;
+ } else if ((c == 'r')
+ && (strncmp(argv[0], "radiobutton", length) == 0)) {
+ type = RADIO_BUTTON_ENTRY;
+ } else if ((c == 's')
+ && (strncmp(argv[0], "separator", length) == 0)) {
+ type = SEPARATOR_ENTRY;
+ } else {
+ Tcl_AppendResult(interp, "bad menu entry type \"",
+ argv[0], "\": must be cascade, checkbutton, ",
+ "command, radiobutton, or separator", (char *) NULL);
+ return TCL_ERROR;
+ }
+ mePtr = MenuNewEntry(menuPtr, index, type);
+ if (ConfigureMenuEntry(interp, menuPtr, mePtr, index,
+ argc-1, argv+1, 0) != TCL_OK) {
+ DestroyMenuEntry((ClientData) mePtr);
+ for (i = index+1; i < menuPtr->numEntries; i++) {
+ menuPtr->entries[i-1] = menuPtr->entries[i];
+ }
+ menuPtr->numEntries--;
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuVarProc --
+ *
+ * This procedure is invoked when someone changes the
+ * state variable associated with a radiobutton or checkbutton
+ * menu entry. The entry's selected state is set to match
+ * the value of the variable.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The menu entry may become selected or deselected.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+MenuVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about menu entry. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* First part of variable's name. */
+ char *name2; /* Second part of variable's name. */
+ int flags; /* Describes what just happened. */
+{
+ MenuEntry *mePtr = (MenuEntry *) clientData;
+ Menu *menuPtr;
+ char *value;
+
+ menuPtr = mePtr->menuPtr;
+
+ /*
+ * If the variable is being unset, then re-establish the
+ * trace unless the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ mePtr->flags &= ~ENTRY_SELECTED;
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_TraceVar(interp, mePtr->name,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuVarProc, clientData);
+ }
+ EventuallyRedrawMenu(menuPtr, (MenuEntry *) NULL);
+ return (char *) NULL;
+ }
+
+ /*
+ * Use the value of the variable to update the selected status of
+ * the menu entry.
+ */
+
+ value = Tcl_GetVar(interp, mePtr->name, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (strcmp(value, mePtr->onValue) == 0) {
+ if (mePtr->flags & ENTRY_SELECTED) {
+ return (char *) NULL;
+ }
+ mePtr->flags |= ENTRY_SELECTED;
+ } else if (mePtr->flags & ENTRY_SELECTED) {
+ mePtr->flags &= ~ENTRY_SELECTED;
+ } else {
+ return (char *) NULL;
+ }
+ EventuallyRedrawMenu(menuPtr, (MenuEntry *) NULL);
+ return (char *) NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EventuallyRedrawMenu --
+ *
+ * Arrange for an entry of a menu, or the whole menu, to be
+ * redisplayed at some point in the future.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A when-idle hander is scheduled to do the redisplay, if there
+ * isn't one already scheduled.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EventuallyRedrawMenu(menuPtr, mePtr)
+ register Menu *menuPtr; /* Information about menu to redraw. */
+ register MenuEntry *mePtr; /* Entry to redraw. NULL means redraw
+ * all the entries in the menu. */
+{
+ int i;
+
+ if (menuPtr->winPtr == NULL) {
+ return;
+ }
+ if (mePtr != NULL) {
+ mePtr->flags |= ENTRY_NEEDS_REDISPLAY;
+ } else {
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ menuPtr->entries[i]->flags |= ENTRY_NEEDS_REDISPLAY;
+ }
+ }
+ if ((menuPtr->winPtr == NULL) || !(menuPtr->winPtr->flags & CK_MAPPED)
+ || (menuPtr->flags & REDRAW_PENDING)) {
+ return;
+ }
+ Tk_DoWhenIdle(DisplayMenu, (ClientData) menuPtr);
+ menuPtr->flags |= REDRAW_PENDING;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * PostSubmenu --
+ *
+ * This procedure arranges for a particular submenu (i.e. the
+ * menu corresponding to a given cascade entry) to be
+ * posted.
+ *
+ * Results:
+ * A standard Tcl return result. Errors may occur in the
+ * Tcl commands generated to post and unpost submenus.
+ *
+ * Side effects:
+ * If there is already a submenu posted, it is unposted.
+ * The new submenu is then posted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+PostSubmenu(interp, menuPtr, mePtr)
+ Tcl_Interp *interp; /* Used for invoking sub-commands and
+ * reporting errors. */
+ register Menu *menuPtr; /* Information about menu as a whole. */
+ register MenuEntry *mePtr; /* Info about submenu that is to be
+ * posted. NULL means make sure that
+ * no submenu is posted. */
+{
+ char string[30];
+ int result, x, y;
+ CkWindow *winPtr;
+
+ if (mePtr == menuPtr->postedCascade) {
+ return TCL_OK;
+ }
+
+ if (menuPtr->postedCascade != NULL) {
+ /*
+ * Note: when unposting a submenu, we have to redraw the entire
+ * parent menu. This is because of a combination of the following
+ * things:
+ * (a) the submenu partially overlaps the parent.
+ * (b) the submenu specifies "save under", which causes the X
+ * server to make a copy of the information under it when it
+ * is posted. When the submenu is unposted, the X server
+ * copies this data back and doesn't generate any Expose
+ * events for the parent.
+ * (c) the parent may have redisplayed itself after the submenu
+ * was posted, in which case the saved information is no
+ * longer correct.
+ * The simplest solution is just force a complete redisplay of
+ * the parent.
+ */
+
+ EventuallyRedrawMenu(menuPtr, (MenuEntry *) NULL);
+ result = Tcl_VarEval(interp, menuPtr->postedCascade->name,
+ " unpost", (char *) NULL);
+ menuPtr->postedCascade = NULL;
+ if (result != TCL_OK) {
+ return result;
+ }
+ }
+
+ if ((mePtr != NULL) && (mePtr->name != NULL)
+ && (menuPtr->winPtr->flags & CK_MAPPED)) {
+ /*
+ * Make sure that the cascaded submenu is a child of the
+ * parent menu.
+ */
+
+ winPtr = Ck_NameToWindow(interp, mePtr->name, menuPtr->winPtr);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (winPtr->parentPtr != menuPtr->winPtr) {
+ Tcl_AppendResult(interp, "cascaded sub-menu ",
+ winPtr->pathName, " must be a child of ",
+ menuPtr->winPtr->pathName, (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Position the cascade with its upper left corner slightly
+ * below and to the left of the upper right corner of the
+ * menu entry (this is an attempt to match Motif behavior).
+ */
+ x = menuPtr->winPtr->x;
+ y = menuPtr->winPtr->y;
+ x += menuPtr->winPtr->width;
+ y += mePtr->y;
+ sprintf(string, "%d %d", x, y);
+ result = Tcl_VarEval(interp, mePtr->name, " post ", string,
+ (char *) NULL);
+ if (result != TCL_OK) {
+ return result;
+ }
+ menuPtr->postedCascade = mePtr;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActivateMenuEntry --
+ *
+ * This procedure is invoked to make a particular menu entry
+ * the active one, deactivating any other entry that might
+ * currently be active.
+ *
+ * Results:
+ * The return value is a standard Tcl result (errors can occur
+ * while posting and unposting submenus).
+ *
+ * Side effects:
+ * Menu entries get redisplayed, and the active entry changes.
+ * Submenus may get posted and unposted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ActivateMenuEntry(menuPtr, index)
+ register Menu *menuPtr; /* Menu in which to activate. */
+ int index; /* Index of entry to activate, or
+ * -1 to deactivate all entries. */
+{
+ register MenuEntry *mePtr;
+ int result = TCL_OK;
+
+ if (menuPtr->active >= 0) {
+ mePtr = menuPtr->entries[menuPtr->active];
+
+ /*
+ * Don't change the state unless it's currently active (state
+ * might already have been changed to disabled).
+ */
+
+ if (mePtr->state == ckActiveUid) {
+ mePtr->state = ckNormalUid;
+ }
+ EventuallyRedrawMenu(menuPtr, menuPtr->entries[menuPtr->active]);
+ }
+ menuPtr->active = index;
+ if (index >= 0) {
+ mePtr = menuPtr->entries[index];
+ mePtr->state = ckActiveUid;
+ EventuallyRedrawMenu(menuPtr, mePtr);
+ }
+ return result;
+}
--- /dev/null
+/*
+ * ckMenubutton.c --
+ *
+ * This module implements button-like widgets that are used
+ * to invoke pull-down menus.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the widget. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with menubutton. */
+ Tcl_Command widgetCmd; /* Token for menubutton's widget command. */
+ char *menuName; /* Name of menu associated with widget.
+ * Malloc-ed. */
+
+ /*
+ * Information about what's displayed in the menu button:
+ */
+
+ char *text; /* Text to display in button (malloc'ed)
+ * or NULL. */
+ int numChars; /* # of characters in text. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, button displays the contents
+ * of this variable. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ Ck_Uid state; /* State of button for display purposes:
+ * normal, active, or disabled. */
+ int normalFg; /* Foreground color in normal mode. */
+ int normalBg; /* Background color in normal mode. */
+ int normalAttr; /* Attributes in normal mode. */
+ int activeFg; /* Foreground color in active mode. */
+ int activeBg; /* Ditto, background color. */
+ int activeAttr; /* Attributes in active mode. */
+ int disabledBg; /* Background color when disabled. */
+ int disabledFg; /* Foreground color when disabled. */
+ int disabledAttr; /* Attributes when disabled. */
+ int underlineFg; /* Foreground color for underlined char. */
+ int underlineAttr; /* Attribute for underlined character. */
+ int indicatorFg; /* Foreground color for indicator. */
+ int underline; /* Index of underlined character, < 0 if
+ * no underlining. */
+ int width, height; /* If > 0, these specify dimensions to request
+ * for window, in characters for text and in
+ * pixels for bitmaps. In this case the actual
+ * size of the text string or bitmap is
+ * ignored in computing desired window size. */
+ Ck_Anchor anchor; /* Where text/bitmap should be displayed
+ * inside window region. */
+ int indicatorOn; /* Non-zero means display indicator; 0 means
+ * don't display. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} MenuButton;
+
+/*
+ * Flag bits for buttons:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * POSTED: Non-zero means that the menu associated
+ * with this button has been posted (typically
+ * because of an active button press).
+ * GOT_FOCUS: Non-zero means this button currently
+ * has the input focus.
+ */
+
+#define REDRAW_PENDING 1
+#define POSTED 2
+#define GOT_FOCUS 4
+
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_MENUBUTTON_ACTIVE_ATTR_COLOR,
+ Ck_Offset(MenuButton, activeAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_MENUBUTTON_ACTIVE_ATTR_MONO,
+ Ck_Offset(MenuButton, activeAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_MENUBUTTON_ATTR, Ck_Offset(MenuButton, normalAttr), 0},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENUBUTTON_ACTIVE_BG_COLOR, Ck_Offset(MenuButton, activeBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_MENUBUTTON_ACTIVE_BG_MONO, Ck_Offset(MenuButton, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENUBUTTON_ACTIVE_FG_COLOR, Ck_Offset(MenuButton, activeFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_MENUBUTTON_ACTIVE_FG_MONO, Ck_Offset(MenuButton, activeFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MENUBUTTON_ANCHOR, Ck_Offset(MenuButton, anchor), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MENUBUTTON_BG_COLOR, Ck_Offset(MenuButton, normalBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MENUBUTTON_BG_MONO, Ck_Offset(MenuButton, normalBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_ATTR, "-disabledattributes", "disabledAttributes",
+ "DisabledAttributes", DEF_MENUBUTTON_DISABLED_ATTR,
+ Ck_Offset(MenuButton, disabledAttr), 0},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
+ Ck_Offset(MenuButton, disabledBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_MENUBUTTON_DISABLED_BG_MONO,
+ Ck_Offset(MenuButton, disabledBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENUBUTTON_DISABLED_BG_COLOR,
+ Ck_Offset(MenuButton, disabledFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
+ Ck_Offset(MenuButton, disabledFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MENUBUTTON_FG, Ck_Offset(MenuButton, normalFg), 0},
+ {CK_CONFIG_COORD, "-height", "height", "Height",
+ DEF_MENUBUTTON_HEIGHT, Ck_Offset(MenuButton, height), 0},
+ {CK_CONFIG_COLOR, "-indicatorforeground", "indicatorForeground",
+ "Foreground", DEF_MENUBUTTON_INDICATOR_FG_COLOR,
+ Ck_Offset(MenuButton, indicatorFg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-indicatorforeground", "indicatorForeground",
+ "Foreground", DEF_MENUBUTTON_INDICATOR_FG_MONO,
+ Ck_Offset(MenuButton, indicatorFg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
+ DEF_MENUBUTTON_INDICATOR, Ck_Offset(MenuButton, indicatorOn), 0},
+ {CK_CONFIG_STRING, "-menu", "menu", "Menu",
+ DEF_MENUBUTTON_MENU, Ck_Offset(MenuButton, menuName),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_UID, "-state", "state", "State",
+ DEF_MENUBUTTON_STATE, Ck_Offset(MenuButton, state), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_MENUBUTTON_TAKE_FOCUS, Ck_Offset(MenuButton, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_MENUBUTTON_TEXT, Ck_Offset(MenuButton, text), 0},
+ {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_MENUBUTTON_TEXT_VARIABLE, Ck_Offset(MenuButton, textVarName),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_INT, "-underline", "underline", "Underline",
+ DEF_MENUBUTTON_UNDERLINE, Ck_Offset(MenuButton, underline), 0},
+ {CK_CONFIG_ATTR, "-underlineattributes", "underlineAttributes",
+ "UnderlineAttributes", DEF_MENUBUTTON_UNDERLINE_ATTR,
+ Ck_Offset(MenuButton, underlineAttr), 0},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_MENUBUTTON_UNDERLINE_FG_COLOR,
+ Ck_Offset(MenuButton, underlineFg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
+ "UnderlineForeground", DEF_MENUBUTTON_UNDERLINE_FG_MONO,
+ Ck_Offset(MenuButton, underlineFg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COORD, "-width", "width", "Width",
+ DEF_MENUBUTTON_WIDTH, Ck_Offset(MenuButton, width), 0},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ComputeMenuButtonGeometry _ANSI_ARGS_((
+ MenuButton *mbPtr));
+static void MenuButtonCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static char * MenuButtonTextVarProc _ANSI_ARGS_((
+ ClientData clientData, Tcl_Interp *interp,
+ char *name1, char *name2, int flags));
+static int MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
+ MenuButton *mbPtr, int argc, char **argv,
+ int flags));
+static void DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
+static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MenubuttonCmd --
+ *
+ * This procedure is invoked to process the "menubutton"
+ * Tcl commands. See the user documentation for details
+ * on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_MenubuttonCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register MenuButton *mbPtr;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *new;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Create the new window.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the data structure for the button.
+ */
+
+ mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
+ mbPtr->winPtr = new;
+ mbPtr->interp = interp;
+ mbPtr->widgetCmd = Tcl_CreateCommand(interp, mbPtr->winPtr->pathName,
+ MenuButtonWidgetCmd, (ClientData) mbPtr, MenuButtonCmdDeletedProc);
+ mbPtr->menuName = NULL;
+ mbPtr->text = NULL;
+ mbPtr->numChars = 0;
+
+ mbPtr->textVarName = NULL;
+ mbPtr->state = ckNormalUid;
+ mbPtr->normalBg = 0;
+ mbPtr->normalFg = 0;
+ mbPtr->normalAttr = 0;
+ mbPtr->activeBg = 0;
+ mbPtr->activeFg = 0;
+ mbPtr->activeAttr = 0;
+ mbPtr->disabledBg = 0;
+ mbPtr->disabledFg = 0;
+ mbPtr->disabledAttr = 0;
+ mbPtr->underlineFg = 0;
+ mbPtr->underlineAttr = 0;
+ mbPtr->indicatorFg = 0;
+ mbPtr->underline = -1;
+ mbPtr->width = 0;
+ mbPtr->height = 0;
+ mbPtr->anchor = CK_ANCHOR_CENTER;
+ mbPtr->indicatorOn = 0;
+ mbPtr->takeFocus = NULL;
+ mbPtr->flags = 0;
+
+ Ck_SetClass(mbPtr->winPtr, "Menubutton");
+ Ck_CreateEventHandler(mbPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
+ MenuButtonEventProc, (ClientData) mbPtr);
+ if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
+ Ck_DestroyWindow(mbPtr->winPtr);
+ return TCL_ERROR;
+ }
+
+ interp->result = mbPtr->winPtr->pathName;
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MenuButtonWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about button widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register MenuButton *mbPtr = (MenuButton *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) mbPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, mbPtr->winPtr, configSpecs,
+ (char *) mbPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, mbPtr->winPtr, configSpecs,
+ (char *) mbPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, mbPtr->winPtr, configSpecs,
+ (char *) mbPtr, argv[2], 0);
+ } else {
+ result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure",
+ (char *) NULL);
+ goto error;
+ }
+ Ck_Release((ClientData) mbPtr);
+ return result;
+
+ error:
+ Ck_Release((ClientData) mbPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyMenuButton --
+ *
+ * This procedure is invoked to recycle all of the resources
+ * associated with a button widget. It is invoked as a
+ * when-idle handler in order to make sure that there is no
+ * other use of the button pending at the time of the deletion.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyMenuButton(clientData)
+ ClientData clientData; /* Info about button widget. */
+{
+ register MenuButton *mbPtr = (MenuButton *) clientData;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (mbPtr->textVarName != NULL) {
+ Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData) mbPtr);
+ }
+ Ck_FreeOptions(configSpecs, (char *) mbPtr, 0);
+ ckfree((char *) mbPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureMenuButton --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Tk option database, in order to configure (or
+ * reconfigure) a menubutton widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for mbPtr; old resources get freed, if there
+ * were any. The menubutton is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register MenuButton *mbPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ int result;
+
+ /*
+ * Eliminate any existing trace on variables monitored by the menubutton.
+ */
+
+ if (mbPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData) mbPtr);
+ }
+
+ result = Ck_ConfigureWidget(interp, mbPtr->winPtr, configSpecs,
+ argc, argv, (char *) mbPtr, flags);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * A few options need special processing, such as setting the
+ * background from a 3-D border, or filling in complicated
+ * defaults that couldn't be specified to Tk_ConfigureWidget.
+ */
+
+ if ((mbPtr->state != ckNormalUid) && (mbPtr->state != ckActiveUid)
+ && (mbPtr->state != ckDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
+ "\": must be normal, active, or disabled", (char *) NULL);
+ mbPtr->state = ckNormalUid;
+ return TCL_ERROR;
+ }
+
+ if (mbPtr->textVarName != NULL) {
+ /*
+ * The menubutton displays a variable. Set up a trace to watch
+ * for any changes in it.
+ */
+
+ char *value;
+
+ value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
+ TCL_GLOBAL_ONLY);
+ } else {
+ if (mbPtr->text != NULL) {
+ ckfree(mbPtr->text);
+ }
+ mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(mbPtr->text, value);
+ }
+ Tcl_TraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, (ClientData) mbPtr);
+ }
+
+ ComputeMenuButtonGeometry(mbPtr);
+
+ /*
+ * Lastly, arrange for the button to be redisplayed.
+ */
+
+ if ((mbPtr->winPtr->flags & CK_MAPPED)
+ && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayMenuButton --
+ *
+ * This procedure is invoked to display a menubutton widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menubutton in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayMenuButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ MenuButton *mbPtr = (MenuButton *) clientData;
+ int x, y, fg, bg, attr, textWidth, charWidth;
+ CkWindow *winPtr = mbPtr->winPtr;
+
+ mbPtr->flags &= ~REDRAW_PENDING;
+ if ((mbPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+
+ if (mbPtr->state == ckDisabledUid) {
+ fg = mbPtr->disabledFg;
+ bg = mbPtr->disabledBg;
+ attr = mbPtr->disabledAttr;
+ } else if (mbPtr->state == ckActiveUid) {
+ fg = mbPtr->activeFg;
+ bg = mbPtr->activeBg;
+ attr = mbPtr->activeAttr;
+ } else {
+ fg = mbPtr->normalFg;
+ bg = mbPtr->normalBg;
+ attr = mbPtr->normalAttr;
+ }
+
+ /*
+ * Display text for button.
+ */
+
+ if (mbPtr->text != NULL)
+ CkMeasureChars(winPtr->mainPtr, mbPtr->text, mbPtr->numChars, 0,
+ winPtr->width, 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ &textWidth, &charWidth);
+ else
+ textWidth = 0;
+
+ switch (mbPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_W: case CK_ANCHOR_SW:
+ x = 0;
+ break;
+ case CK_ANCHOR_N: case CK_ANCHOR_CENTER: case CK_ANCHOR_S:
+ x = (winPtr->width - textWidth) / 2;
+ if (mbPtr->indicatorOn)
+ x--;
+ break;
+ default:
+ x = winPtr->width - textWidth;
+ if (mbPtr->indicatorOn)
+ x -= 2;
+ break;
+ }
+ if (x + textWidth > winPtr->width)
+ textWidth = winPtr->width - x;
+
+ switch (mbPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
+ y = 0;
+ break;
+ case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
+ y = (winPtr->height - 1) / 2;
+ break;
+ default:
+ y = winPtr->height - 1;
+ if (y < 0)
+ y = 0;
+ break;
+ }
+
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ Ck_ClearToBot(winPtr, 0, 0);
+ if (mbPtr->text != NULL) {
+ CkDisplayChars(winPtr->mainPtr, winPtr->window, mbPtr->text,
+ charWidth, x, y,
+ 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
+ if (mbPtr->underline >= 0 && mbPtr->state == ckNormalUid) {
+ Ck_SetWindowAttr(winPtr, mbPtr->underlineFg, bg,
+ mbPtr->underlineAttr);
+ CkUnderlineChars(winPtr->mainPtr, winPtr->window,
+ mbPtr->text, charWidth, x, y,
+ 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ mbPtr->underline, mbPtr->underline);
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ }
+ }
+ if (mbPtr->indicatorOn) {
+ int gchar;
+
+ x = textWidth + 2;
+ if (x >= winPtr->width)
+ x = winPtr->width - 1;
+ Ck_GetGChar(mbPtr->interp, "diamond", &gchar);
+ Ck_SetWindowAttr(winPtr, mbPtr->indicatorFg, bg, attr);
+ mvwaddch(winPtr->window, y, x, gchar);
+ }
+ Ck_SetWindowAttr(winPtr, fg, bg, attr);
+ wmove(winPtr->window, y, x);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on buttons.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MenuButtonEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ MenuButton *mbPtr = (MenuButton *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ if (mbPtr->winPtr != NULL && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (mbPtr->winPtr != NULL) {
+ mbPtr->winPtr = NULL;
+ Tcl_DeleteCommand(mbPtr->interp,
+ Tcl_GetCommandName(mbPtr->interp, mbPtr->widgetCmd));
+ }
+ if (mbPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
+ }
+ Ck_EventuallyFree((ClientData) mbPtr,
+ (Ck_FreeProc *) DestroyMenuButton);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuButtonCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuButtonCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ MenuButton *mbPtr = (MenuButton *) clientData;
+ CkWindow *winPtr = mbPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ mbPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeMenuButtonGeometry --
+ *
+ * After changes in a menu button's text or bitmap, this procedure
+ * recomputes the menu button's geometry and passes this information
+ * along to the geometry manager for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu button's window may change size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeMenuButtonGeometry(mbPtr)
+ register MenuButton *mbPtr; /* Widget record for menu button. */
+{
+ int width, height, dummy;
+ CkWindow *winPtr = mbPtr->winPtr;
+
+ mbPtr->numChars = mbPtr->text == NULL ? 0 : strlen(mbPtr->text);
+ if (mbPtr->height > 0)
+ height = mbPtr->height;
+ else
+ height = 1;
+ if (mbPtr->width > 0)
+ width = mbPtr->width;
+ else
+ CkMeasureChars(winPtr->mainPtr, mbPtr->text == NULL ? "" : mbPtr->text,
+ mbPtr->numChars, 0, 100000, 0,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
+ &width, &dummy);
+
+ /*
+ * When issuing the geometry request, add extra space for the indicator
+ * if any.
+ */
+
+ if (mbPtr->indicatorOn)
+ width += 2;
+
+ Ck_GeometryRequest(mbPtr->winPtr, width, height);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuButtonTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in a menu button.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the menu button will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about button. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ register MenuButton *mbPtr = (MenuButton *) clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
+ TCL_GLOBAL_ONLY);
+ Tcl_TraceVar(interp, mbPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MenuButtonTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+
+ value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (mbPtr->text != NULL) {
+ ckfree(mbPtr->text);
+ }
+ mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(mbPtr->text, value);
+ ComputeMenuButtonGeometry(mbPtr);
+
+ if ((mbPtr->winPtr != NULL) && (mbPtr->winPtr->flags & CK_MAPPED)
+ && !(mbPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
+ mbPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
--- /dev/null
+/*
+ * ckMessage.c --
+ *
+ * This module implements a message widgets for the
+ * toolkit. A message widget displays a multi-line string
+ * in a window according to a particular aspect ratio.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each message
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the message. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with message. */
+ Tcl_Command widgetCmd; /* Token for message's widget command. */
+ char *string; /* String displayed in message. */
+ int numChars; /* Number of characters in string, not
+ * including terminating NULL character. */
+ char *textVarName; /* Name of variable (malloc'ed) or NULL.
+ * If non-NULL, message displays the contents
+ * of this variable. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int bg, fg; /* Foreground and background colors. */
+ int attr; /* Video attributes. */
+ Ck_Anchor anchor; /* Where to position text within window region
+ * if window is larger or smaller than
+ * needed. */
+ int width; /* User-requested width. 0 means compute
+ * width using aspect ratio below. */
+ int aspect; /* Desired aspect ratio for window
+ * (100*width/height). */
+ int lineLength; /* Length of each line. Computed
+ * from width and/or aspect. */
+ int msgHeight; /* Total lines needed to display message. */
+ Ck_Justify justify; /* Justification for text. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Message;
+
+/*
+ * Flag bits for messages:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ */
+
+#define REDRAW_PENDING 1
+
+/*
+ * Information used for argv parsing.
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_MESSAGE_ANCHOR, Ck_Offset(Message, anchor), 0},
+ {CK_CONFIG_INT, "-aspect", "aspect", "Aspect",
+ DEF_MESSAGE_ASPECT, Ck_Offset(Message, aspect), 0},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_MESSAGE_ATTR, Ck_Offset(Message, attr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MESSAGE_BG_COLOR, Ck_Offset(Message, bg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MESSAGE_BG_MONO, Ck_Offset(Message, bg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MESSAGE_FG_COLOR, Ck_Offset(Message, fg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MESSAGE_FG_MONO, Ck_Offset(Message, fg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_MESSAGE_JUSTIFY, Ck_Offset(Message, justify), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_MESSAGE_TAKE_FOCUS, Ck_Offset(Message, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-text", "text", "Text",
+ DEF_MESSAGE_TEXT, Ck_Offset(Message, string), 0},
+ {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_MESSAGE_TEXT_VARIABLE, Ck_Offset(Message, textVarName),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COORD, "-width", "width", "Width",
+ DEF_MESSAGE_WIDTH, Ck_Offset(Message, width), 0},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void MessageEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static char * MessageTextVarProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+static int MessageWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static void MessageCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr));
+static int ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp,
+ Message *msgPtr, int argc, char **argv,
+ int flags));
+static void DestroyMessage _ANSI_ARGS_((ClientData clientData));
+static void DisplayMessage _ANSI_ARGS_((ClientData clientData));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MessageCmd --
+ *
+ * This procedure is invoked to process the "message" Tcl
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_MessageCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Message *msgPtr;
+ CkWindow *new;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ msgPtr = (Message *) ckalloc(sizeof (Message));
+ msgPtr->winPtr = new;
+ msgPtr->interp = interp;
+ msgPtr->widgetCmd = Tcl_CreateCommand(interp,
+ msgPtr->winPtr->pathName, MessageWidgetCmd,
+ (ClientData) msgPtr, MessageCmdDeletedProc);
+ msgPtr->string = NULL;
+ msgPtr->numChars = 0;
+ msgPtr->textVarName = NULL;
+ msgPtr->bg = 0;
+ msgPtr->fg = 0;
+ msgPtr->attr = 0;
+ msgPtr->anchor = CK_ANCHOR_CENTER;
+ msgPtr->width = 0;
+ msgPtr->aspect = 150;
+ msgPtr->lineLength = 0;
+ msgPtr->msgHeight = 0;
+ msgPtr->justify = CK_JUSTIFY_LEFT;
+ msgPtr->takeFocus = NULL;
+ msgPtr->flags = 0;
+
+ Ck_SetClass(msgPtr->winPtr, "Message");
+ Ck_CreateEventHandler(msgPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
+ MessageEventProc, (ClientData) msgPtr);
+ if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ interp->result = msgPtr->winPtr->pathName;
+ return TCL_OK;
+
+error:
+ Ck_DestroyWindow(msgPtr->winPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MessageWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MessageWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about message widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Message *msgPtr = (Message *) clientData;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return Ck_ConfigureValue(interp, msgPtr->winPtr, configSpecs,
+ (char *) msgPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ return Ck_ConfigureInfo(interp, msgPtr->winPtr, configSpecs,
+ (char *) msgPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ return Ck_ConfigureInfo(interp, msgPtr->winPtr, configSpecs,
+ (char *) msgPtr, argv[2], 0);
+ } else {
+ return ConfigureMessage(interp, msgPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure", (char *) NULL);
+ return TCL_ERROR;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyMessage --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a message at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the message is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyMessage(clientData)
+ ClientData clientData; /* Info about message widget. */
+{
+ register Message *msgPtr = (Message *) clientData;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (msgPtr->textVarName != NULL) {
+ Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MessageTextVarProc, (ClientData) msgPtr);
+ }
+ Ck_FreeOptions(configSpecs, (char *) msgPtr, 0);
+ ckfree((char *) msgPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MessageCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MessageCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Message *msgPtr = (Message *) clientData;
+ CkWindow *winPtr = msgPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ msgPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureMessage --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or
+ * reconfigure) a message widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors,
+ * etc. get set for msgPtr; old resources get freed, if there
+ * were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureMessage(interp, msgPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Message *msgPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Tk_ConfigureWidget. */
+{
+ /*
+ * Eliminate any existing trace on a variable monitored by the message.
+ */
+
+ if (msgPtr->textVarName != NULL) {
+ Tcl_UntraceVar(interp, msgPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MessageTextVarProc, (ClientData) msgPtr);
+ }
+
+ if (Ck_ConfigureWidget(interp, msgPtr->winPtr, configSpecs,
+ argc, argv, (char *) msgPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * If the message is to display the value of a variable, then set up
+ * a trace on the variable's value, create the variable if it doesn't
+ * exist, and fetch its current value.
+ */
+
+ if (msgPtr->textVarName != NULL) {
+ char *value;
+
+ value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ Tcl_SetVar(interp, msgPtr->textVarName,
+ msgPtr->string == NULL ? "" : msgPtr->string,
+ TCL_GLOBAL_ONLY);
+ } else {
+ if (msgPtr->string != NULL) {
+ ckfree(msgPtr->string);
+ }
+ msgPtr->string = (char *) ckalloc((unsigned) (strlen(value) + 1));
+ strcpy(msgPtr->string, value);
+ }
+ Tcl_TraceVar(interp, msgPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MessageTextVarProc, (ClientData) msgPtr);
+ }
+
+ /*
+ * A few other options need special processing, such as setting
+ * the background from a 3-D border or handling special defaults
+ * that couldn't be specified to Tk_ConfigureWidget.
+ */
+
+ if (msgPtr->string == NULL) {
+ msgPtr->string = ckalloc(1);
+ msgPtr->string[0] = '\0';
+ }
+ msgPtr->numChars = strlen(msgPtr->string);
+
+ /*
+ * Recompute the desired geometry for the window, and arrange for
+ * the window to be redisplayed.
+ */
+
+ ComputeMessageGeometry(msgPtr);
+ if ((msgPtr->winPtr != NULL) && (msgPtr->winPtr->flags & CK_MAPPED)
+ && !(msgPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
+ msgPtr->flags |= REDRAW_PENDING;
+ }
+
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ComputeMessageGeometry --
+ *
+ * Compute the desired geometry for a message window,
+ * taking into account the desired aspect ratio for the
+ * window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Ck_GeometryRequest is called to inform the geometry
+ * manager of the desired geometry for this window.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ComputeMessageGeometry(msgPtr)
+ register Message *msgPtr; /* Information about window. */
+{
+ char *p;
+ int width, inc, height, numLines;
+ int thisWidth, maxWidth;
+ int aspect, lowerBound, upperBound, dummy;
+ CkWindow *winPtr = msgPtr->winPtr;
+
+ /*
+ * Compute acceptable bounds for the final aspect ratio.
+ */
+
+ aspect = msgPtr->aspect/10;
+ if (aspect < 5) {
+ aspect = 5;
+ }
+ lowerBound = msgPtr->aspect - aspect;
+ upperBound = msgPtr->aspect + aspect;
+
+ /*
+ * Do the computation in multiple passes: start off with
+ * a very wide window, and compute its height. Then change
+ * the width and try again. Reduce the size of the change
+ * and iterate until dimensions are found that approximate
+ * the desired aspect ratio. Or, if the user gave an explicit
+ * width then just use that.
+ */
+
+ if (msgPtr->width > 0) {
+ width = msgPtr->width;
+ inc = 0;
+ } else {
+ width = msgPtr->winPtr->mainPtr->winPtr->width;
+ inc = width/2;
+ }
+ for ( ; ; inc /= 2) {
+ maxWidth = 0;
+ for (numLines = 1, p = msgPtr->string; ; numLines++) {
+ if (*p == '\n') {
+ p++;
+ continue;
+ }
+#if CK_USE_UTF
+ CkMeasureChars(winPtr->mainPtr, p,
+ msgPtr->numChars - (p - msgPtr->string),
+ 0, width, 0, CK_WHOLE_WORDS|CK_AT_LEAST_ONE, &thisWidth,
+ &dummy);
+ p += dummy;
+#else
+ p += CkMeasureChars(winPtr->mainPtr, p,
+ msgPtr->numChars - (p - msgPtr->string),
+ 0, width, 0, CK_WHOLE_WORDS|CK_AT_LEAST_ONE, &thisWidth,
+ &dummy);
+#endif
+ if (thisWidth > maxWidth) {
+ maxWidth = thisWidth;
+ }
+ if (*p == 0) {
+ break;
+ }
+
+ /*
+ * Skip spaces and tabs at the beginning of a line, unless
+ * they follow a user-requested newline.
+ */
+
+ while (isspace((unsigned char) (*p))) {
+ if (*p == '\n') {
+ p++;
+ break;
+ }
+ p++;
+ }
+ }
+
+ height = numLines;
+ if (inc <= 2) {
+ break;
+ }
+ aspect = (100 * maxWidth) / height;
+ if (aspect < lowerBound) {
+ width += inc;
+ } else if (aspect > upperBound) {
+ width -= inc;
+ } else {
+ break;
+ }
+ }
+ msgPtr->lineLength = maxWidth;
+ msgPtr->msgHeight = numLines;
+ Ck_GeometryRequest(msgPtr->winPtr, maxWidth, height);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayMessage --
+ *
+ * This procedure redraws the contents of a message window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayMessage(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ register Message *msgPtr = (Message *) clientData;
+ register CkWindow *winPtr = msgPtr->winPtr;
+ char *p;
+ int x, y, lineLength, numChars, charsLeft, dummy;
+
+ msgPtr->flags &= ~REDRAW_PENDING;
+ if (msgPtr->winPtr == NULL || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+
+ Ck_SetWindowAttr(winPtr, msgPtr->fg, msgPtr->bg, msgPtr->attr);
+ Ck_ClearToBot(winPtr, 0, 0);
+
+ /*
+ * Compute starting y-location for message based on message size
+ * and anchor option.
+ */
+
+ switch (msgPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
+ y = 0;
+ break;
+ case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
+ y = (winPtr->height - msgPtr->msgHeight) / 2;
+ break;
+ default:
+ y = winPtr->height - msgPtr->msgHeight;
+ break;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+
+ /*
+ * Work through the string to display one line at a time.
+ * Display each line in three steps. First compute the
+ * line's width, then figure out where to display the
+ * line to justify it properly, then display the line.
+ */
+
+ for (p = msgPtr->string, charsLeft = msgPtr->numChars; *p != 0; y++) {
+ if (*p == '\n') {
+ p++;
+ charsLeft--;
+ continue;
+ }
+ numChars = CkMeasureChars(winPtr->mainPtr, p, charsLeft, 0,
+ msgPtr->lineLength,
+ 0, CK_WHOLE_WORDS | CK_AT_LEAST_ONE, &lineLength, &dummy);
+ switch (msgPtr->anchor) {
+ case CK_ANCHOR_NW: case CK_ANCHOR_W: case CK_ANCHOR_SW:
+ x = 0;
+ break;
+ case CK_ANCHOR_N: case CK_ANCHOR_CENTER: case CK_ANCHOR_S:
+ x = (winPtr->width - msgPtr->lineLength) / 2;
+ break;
+ default:
+ x = winPtr->width - msgPtr->lineLength;
+ break;
+ }
+ if (msgPtr->justify == CK_JUSTIFY_CENTER) {
+ x += (msgPtr->lineLength - lineLength) / 2;
+ } else if (msgPtr->justify == CK_JUSTIFY_RIGHT) {
+ x += msgPtr->lineLength - lineLength;
+ }
+#if 0
+ if (x < 0) {
+ x = 0;
+ }
+#endif
+ CkDisplayChars(winPtr->mainPtr, winPtr->window, p, numChars,
+ x, y, x, 0);
+ p += numChars;
+ charsLeft -= numChars;
+
+ /*
+ * Skip blanks at the beginning of a line, unless they follow
+ * a user-requested newline.
+ */
+
+ while (isspace((unsigned char) (*p))) {
+ charsLeft--;
+ if (*p == '\n') {
+ p++;
+ break;
+ }
+ p++;
+ }
+ }
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MessageEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various
+ * events on messages.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MessageEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Message *msgPtr = (Message *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ if (msgPtr->winPtr != NULL && !(msgPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
+ msgPtr->flags |= REDRAW_PENDING;
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (msgPtr->winPtr != NULL) {
+ msgPtr->winPtr = NULL;
+ Tcl_DeleteCommand(msgPtr->interp,
+ Tcl_GetCommandName(msgPtr->interp, msgPtr->widgetCmd));
+ }
+ if (msgPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayMessage, (ClientData) msgPtr);
+ }
+ Ck_EventuallyFree((ClientData) msgPtr, (Ck_FreeProc *) DestroyMessage);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MessageTextVarProc --
+ *
+ * This procedure is invoked when someone changes the variable
+ * whose contents are to be displayed in a message.
+ *
+ * Results:
+ * NULL is always returned.
+ *
+ * Side effects:
+ * The text displayed in the message will change to match the
+ * variable.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+MessageTextVarProc(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Information about message. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ register Message *msgPtr = (Message *) clientData;
+ char *value;
+
+ /*
+ * If the variable is unset, then immediately recreate it unless
+ * the whole interpreter is going away.
+ */
+
+ if (flags & TCL_TRACE_UNSETS) {
+ if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+ Tcl_SetVar(interp, msgPtr->textVarName,
+ msgPtr->string == NULL ? "" : msgPtr->string,
+ TCL_GLOBAL_ONLY);
+ Tcl_TraceVar(interp, msgPtr->textVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ MessageTextVarProc, clientData);
+ }
+ return (char *) NULL;
+ }
+
+ value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
+ if (value == NULL) {
+ value = "";
+ }
+ if (msgPtr->string != NULL) {
+ ckfree(msgPtr->string);
+ }
+ msgPtr->numChars = strlen(value);
+ msgPtr->string = (char *) ckalloc((unsigned) (msgPtr->numChars + 1));
+ strcpy(msgPtr->string, value);
+ ComputeMessageGeometry(msgPtr);
+
+ if ((msgPtr->winPtr != NULL) && (msgPtr->winPtr->flags & CK_MAPPED)
+ && !(msgPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
+ msgPtr->flags |= REDRAW_PENDING;
+ }
+ return (char *) NULL;
+}
--- /dev/null
+/*
+ * ckOption.c --
+ *
+ * This module contains procedures to manage the option
+ * database, which allows various strings to be associated
+ * with windows either by name or by class or both.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * The option database is stored as one tree for each main window.
+ * Each name or class field in an option is associated with a node or
+ * leaf of the tree. For example, the options "x.y.z" and "x.y*a"
+ * each correspond to three nodes in the tree; they share the nodes
+ * "x" and "x.y", but have different leaf nodes. One of the following
+ * structures exists for each node or leaf in the option tree. It is
+ * actually stored as part of the parent node, and describes a particular
+ * child of the parent.
+ */
+
+typedef struct Element {
+ Ck_Uid nameUid; /* Name or class from one element of
+ * an option spec. */
+ union {
+ struct ElArray *arrayPtr; /* If this is an intermediate node,
+ * a pointer to a structure describing
+ * the remaining elements of all
+ * options whose prefixes are the
+ * same up through this element. */
+ Ck_Uid valueUid; /* For leaf nodes, this is the string
+ * value of the option. */
+ } child;
+ int priority; /* Used to select among matching
+ * options. Includes both the
+ * priority level and a serial #.
+ * Greater value means higher
+ * priority. Irrelevant except in
+ * leaf nodes. */
+ int flags; /* OR-ed combination of bits. See
+ * below for values. */
+} Element;
+
+/*
+ * Flags in Element structures:
+ *
+ * CLASS - Non-zero means this element refers to a class,
+ * Zero means this element refers to a name.
+ * NODE - Zero means this is a leaf element (the child
+ * field is a value, not a pointer to another node).
+ * One means this is a node element.
+ * WILDCARD - Non-zero means this there was a star in the
+ * original specification just before this element.
+ * Zero means there was a dot.
+ */
+
+#define TYPE_MASK 0x7
+
+#define CLASS 0x1
+#define NODE 0x2
+#define WILDCARD 0x4
+
+#define EXACT_LEAF_NAME 0x0
+#define EXACT_LEAF_CLASS 0x1
+#define EXACT_NODE_NAME 0x2
+#define EXACT_NODE_CLASS 0x3
+#define WILDCARD_LEAF_NAME 0x4
+#define WILDCARD_LEAF_CLASS 0x5
+#define WILDCARD_NODE_NAME 0x6
+#define WILDCARD_NODE_CLASS 0x7
+
+/*
+ * The following structure is used to manage a dynamic array of
+ * Elements. These structures are used for two purposes: to store
+ * the contents of a node in the option tree, and for the option
+ * stacks described below.
+ */
+
+typedef struct ElArray {
+ int arraySize; /* Number of elements actually
+ * allocated in the "els" array. */
+ int numUsed; /* Number of elements currently in
+ * use out of els. */
+ Element *nextToUse; /* Pointer to &els[numUsed]. */
+ Element els[1]; /* Array of structures describing
+ * children of this node. The
+ * array will actually contain enough
+ * elements for all of the children
+ * (and even a few extras, perhaps).
+ * This must be the last field in
+ * the structure. */
+} ElArray;
+
+#define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \
+ + ((numEls)-1)*sizeof(Element)))
+#define INITIAL_SIZE 5
+
+/*
+ * In addition to the option tree, which is a relatively static structure,
+ * there are eight additional structures called "stacks", which are used
+ * to speed up queries into the option database. The stack structures
+ * are designed for the situation where an individual widget makes repeated
+ * requests for its particular options. The requests differ only in
+ * their last name/class, so during the first request we extract all
+ * the options pertaining to the particular widget and save them in a
+ * stack-like cache; subsequent requests for the same widget can search
+ * the cache relatively quickly. In fact, the cache is a hierarchical
+ * one, storing a list of relevant options for this widget and all of
+ * its ancestors up to the application root; hence the name "stack".
+ *
+ * Each of the eight stacks consists of an array of Elements, ordered in
+ * terms of levels in the window hierarchy. All the elements relevant
+ * for the top-level widget appear first in the array, followed by all
+ * those from the next-level widget on the path to the current widget,
+ * etc. down to those for the current widget.
+ *
+ * Cached information is divided into eight stacks according to the
+ * CLASS, NODE, and WILDCARD flags. Leaf and non-leaf information is
+ * kept separate to speed up individual probes (non-leaf information is
+ * only relevant when building the stacks, but isn't relevant when
+ * making probes; similarly, only non-leaf information is relevant
+ * when the stacks are being extended to the next widget down in the
+ * widget hierarchy). Wildcard elements are handled separately from
+ * "exact" elements because once they appear at a particular level in
+ * the stack they remain active for all deeper levels; exact elements
+ * are only relevant at a particular level. For example, when searching
+ * for options relevant in a particular window, the entire wildcard
+ * stacks get checked, but only the portions of the exact stacks that
+ * pertain to the window's parent. Lastly, name and class stacks are
+ * kept separate because different search keys are used when searching
+ * them; keeping them separate speeds up the searches.
+ */
+
+#define NUM_STACKS 8
+static ElArray *stacks[NUM_STACKS];
+static CkWindow *cachedWindow = NULL; /* Lowest-level window currently
+ * loaded in stacks at present.
+ * NULL means stacks have never
+ * been used, or have been
+ * invalidated because of a change
+ * to the database. */
+
+/*
+ * One of the following structures is used to keep track of each
+ * level in the stacks.
+ */
+
+typedef struct StackLevel {
+ CkWindow *winPtr; /* Window corresponding to this stack
+ * level. */
+ int bases[NUM_STACKS]; /* For each stack, index of first
+ * element on stack corresponding to
+ * this level (used to restore "numUsed"
+ * fields when popping out of a level. */
+} StackLevel;
+
+/*
+ * Information about all of the stack levels that are currently
+ * active. This array grows dynamically to become as large as needed.
+ */
+
+static StackLevel *levels = NULL;
+ /* Array describing current stack. */
+static int numLevels = 0; /* Total space allocated. */
+static int curLevel = -1; /* Highest level currently in use. Note:
+ * curLevel is never 0! (I don't remember
+ * why anymore...) */
+
+/*
+ * The variable below is a serial number for all options entered into
+ * the database so far. It increments on each addition to the option
+ * database. It is used in computing option priorities, so that the
+ * most recent entry wins when choosing between options at the same
+ * priority level.
+ */
+
+static int serial = 0;
+
+/*
+ * Special "no match" Element to use as default for searches.
+ */
+
+static Element defaultMatch;
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static int AddFromString _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, char *string, int priority));
+static void ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr));
+static ElArray * ExtendArray _ANSI_ARGS_((ElArray *arrayPtr,
+ Element *elPtr));
+static void ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr,
+ int leaf));
+static ElArray * NewArray _ANSI_ARGS_((int numEls));
+static void OptionInit _ANSI_ARGS_((CkMainInfo *mainPtr));
+static int ParsePriority _ANSI_ARGS_((Tcl_Interp *interp,
+ char *string));
+static int ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, char *fileName, int priority));
+static void SetupStacks _ANSI_ARGS_((CkWindow *winPtr, int leaf));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_AddOption --
+ *
+ * Add a new option to the option database.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information is added to the option database.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_AddOption(winPtr, name, value, priority)
+ CkWindow *winPtr; /* Window pointer; option will be associated
+ * with main window for this window. */
+ char *name; /* Multi-element name of option. */
+ char *value; /* String value for option. */
+ int priority; /* Overall priority level to use for
+ * this option, such as CK_USER_DEFAULT_PRIO
+ * or CK_INTERACTIVE_PRIO. Must be between
+ * 0 and CK_MAX_PRIO. */
+{
+ register ElArray **arrayPtrPtr;
+ register Element *elPtr;
+ Element newEl;
+ register char *p;
+ char *field;
+ int count, firstField, length;
+#define TMP_SIZE 100
+ char tmp[TMP_SIZE+1];
+
+ winPtr = winPtr->mainPtr->winPtr;
+
+ if (winPtr->mainPtr->optionRootPtr == NULL) {
+ OptionInit(winPtr->mainPtr);
+ }
+ cachedWindow = NULL; /* Invalidate the cache. */
+
+ /*
+ * Compute the priority for the new element, including both the
+ * overall level and the serial number (to disambiguate with the
+ * level).
+ */
+
+ if (priority < 0) {
+ priority = 0;
+ } else if (priority > CK_MAX_PRIO) {
+ priority = CK_MAX_PRIO;
+ }
+ newEl.priority = (priority << 24) + serial;
+ serial++;
+
+ /*
+ * Parse the option one field at a time.
+ */
+
+ arrayPtrPtr = &(winPtr->mainPtr->optionRootPtr);
+ p = name;
+ for (firstField = 1; ; firstField = 0) {
+
+ /*
+ * Scan the next field from the name and convert it to a Tk_Uid.
+ * Must copy the field before calling Tk_Uid, so that a terminating
+ * NULL may be added without modifying the source string.
+ */
+
+ if (*p == '*') {
+ newEl.flags = WILDCARD;
+ p++;
+ } else {
+ newEl.flags = 0;
+ }
+ field = p;
+ while ((*p != 0) && (*p != '.') && (*p != '*')) {
+ p++;
+ }
+ length = p - field;
+ if (length > TMP_SIZE) {
+ length = TMP_SIZE;
+ }
+ strncpy(tmp, field, (size_t) length);
+ tmp[length] = 0;
+ newEl.nameUid = Ck_GetUid(tmp);
+ if (isupper((unsigned char) *field)) {
+ newEl.flags |= CLASS;
+ }
+
+ if (*p != 0) {
+
+ /*
+ * New element will be a node. If this option can't possibly
+ * apply to this main window, then just skip it. Otherwise,
+ * add it to the parent, if it isn't already there, and descend
+ * into it.
+ */
+
+ newEl.flags |= NODE;
+ if (firstField && !(newEl.flags & WILDCARD)
+ && (newEl.nameUid != winPtr->nameUid)
+ && (newEl.nameUid != winPtr->classUid)) {
+ return;
+ }
+ for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
+ ; elPtr++, count--) {
+ if (count == 0) {
+ newEl.child.arrayPtr = NewArray(5);
+ *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
+ arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr);
+ break;
+ }
+ if ((elPtr->nameUid == newEl.nameUid)
+ && (elPtr->flags == newEl.flags)) {
+ arrayPtrPtr = &(elPtr->child.arrayPtr);
+ break;
+ }
+ }
+ if (*p == '.') {
+ p++;
+ }
+ } else {
+
+ /*
+ * New element is a leaf. Add it to the parent, if it isn't
+ * already there. If it exists already, keep whichever value
+ * has highest priority.
+ */
+
+ newEl.child.valueUid = Ck_GetUid(value);
+ for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
+ ; elPtr++, count--) {
+ if (count == 0) {
+ *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
+ return;
+ }
+ if ((elPtr->nameUid == newEl.nameUid)
+ && (elPtr->flags == newEl.flags)) {
+ if (elPtr->priority < newEl.priority) {
+ elPtr->priority = newEl.priority;
+ elPtr->child.valueUid = newEl.child.valueUid;
+ }
+ return;
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetOption --
+ *
+ * Retrieve an option from the option database.
+ *
+ * Results:
+ * The return value is the value specified in the option
+ * database for the given name and class on the given
+ * window. If there is nothing specified in the database
+ * for that option, then NULL is returned.
+ *
+ * Side effects:
+ * The internal caches used to speed up option mapping
+ * may be modified, if this tkwin is different from the
+ * last tkwin used for option retrieval.
+ *
+ *--------------------------------------------------------------
+ */
+
+Ck_Uid
+Ck_GetOption(winPtr, name, className)
+ CkWindow *winPtr; /* Pointer to window that option is
+ * associated with. */
+ char *name; /* Name of option. */
+ char *className; /* Class of option. NULL means there
+ * is no class for this option: just
+ * check for name. */
+{
+ Ck_Uid nameId, classId;
+ register Element *elPtr, *bestPtr;
+ register int count;
+
+ /*
+ * Note: no need to call OptionInit here: it will be done by
+ * the SetupStacks call below (squeeze out those nanoseconds).
+ */
+
+ if (winPtr != cachedWindow) {
+ SetupStacks(winPtr, 1);
+ }
+
+ nameId = Ck_GetUid(name);
+ bestPtr = &defaultMatch;
+ for (elPtr = stacks[EXACT_LEAF_NAME]->els,
+ count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
+ elPtr++, count--) {
+ if ((elPtr->nameUid == nameId)
+ && (elPtr->priority > bestPtr->priority)) {
+ bestPtr = elPtr;
+ }
+ }
+ for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
+ count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
+ elPtr++, count--) {
+ if ((elPtr->nameUid == nameId)
+ && (elPtr->priority > bestPtr->priority)) {
+ bestPtr = elPtr;
+ }
+ }
+ if (className != NULL) {
+ classId = Ck_GetUid(className);
+ for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
+ count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
+ elPtr++, count--) {
+ if ((elPtr->nameUid == classId)
+ && (elPtr->priority > bestPtr->priority)) {
+ bestPtr = elPtr;
+ }
+ }
+ for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
+ count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
+ elPtr++, count--) {
+ if ((elPtr->nameUid == classId)
+ && (elPtr->priority > bestPtr->priority)) {
+ bestPtr = elPtr;
+ }
+ }
+ }
+ return bestPtr->child.valueUid;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_OptionCmd --
+ *
+ * This procedure is invoked to process the "option" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_OptionCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr = (CkWindow *) clientData;
+ size_t length;
+ char c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " cmd arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
+ int priority;
+
+ if ((argc != 4) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " add pattern value ?priority?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ priority = CK_INTERACTIVE_PRIO;
+ } else {
+ priority = ParsePriority(interp, argv[4]);
+ if (priority < 0) {
+ return TCL_ERROR;
+ }
+ }
+ Ck_AddOption(winPtr, argv[2], argv[3], priority);
+ return TCL_OK;
+ } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
+ CkMainInfo *mainPtr;
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " clear\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ mainPtr = winPtr->mainPtr;
+ if (mainPtr->optionRootPtr != NULL) {
+ ClearOptionTree(mainPtr->optionRootPtr);
+ mainPtr->optionRootPtr = NULL;
+ }
+ cachedWindow = NULL;
+ return TCL_OK;
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ CkWindow *winPtr2;
+ Ck_Uid value;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get window name class\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ winPtr2 = Ck_NameToWindow(interp, argv[2], winPtr);
+ if (winPtr2 == NULL) {
+ return TCL_ERROR;
+ }
+ value = Ck_GetOption(winPtr2, argv[3], argv[4]);
+ if (value != NULL) {
+ interp->result = value;
+ }
+ return TCL_OK;
+ } else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) {
+ int priority;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " readfile fileName ?priority?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ priority = ParsePriority(interp, argv[3]);
+ if (priority < 0) {
+ return TCL_ERROR;
+ }
+ } else {
+ priority = CK_INTERACTIVE_PRIO;
+ }
+ return ReadOptionFile(interp, winPtr, argv[2], priority);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be add, clear, get, or readfile", (char *) NULL);
+ return TCL_ERROR;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkOptionDeadWindow --
+ *
+ * This procedure is called whenever a window is deleted.
+ * It cleans up any option-related stuff associated with
+ * the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Option-related resources are freed. See code below
+ * for details.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkOptionDeadWindow(winPtr)
+ register CkWindow *winPtr; /* Window to be cleaned up. */
+{
+ /*
+ * If this window is in the option stacks, then clear the stacks.
+ */
+
+ if (winPtr->optionLevel != -1) {
+ int i;
+
+ for (i = 1; i <= curLevel; i++) {
+ levels[i].winPtr->optionLevel = -1;
+ }
+ curLevel = -1;
+ cachedWindow = NULL;
+ }
+
+ /*
+ * If this window was a main window, then delete its option
+ * database.
+ */
+
+ if ((winPtr->mainPtr->winPtr == winPtr)
+ && (winPtr->mainPtr->optionRootPtr != NULL)) {
+ ClearOptionTree(winPtr->mainPtr->optionRootPtr);
+ winPtr->mainPtr->optionRootPtr = NULL;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkOptionClassChanged --
+ *
+ * This procedure is invoked when a window's class changes. If
+ * the window is on the option cache, this procedure flushes
+ * any information for the window, since the new class could change
+ * what is relevant.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The option cache may be flushed in part or in whole.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkOptionClassChanged(winPtr)
+ CkWindow *winPtr; /* Window whose class changed. */
+{
+ int i, j, *basePtr;
+ ElArray *arrayPtr;
+
+ if (winPtr->optionLevel == -1) {
+ return;
+ }
+
+ /*
+ * Find the lowest stack level that refers to this window, then
+ * flush all of the levels above the matching one.
+ */
+
+ for (i = 1; i <= curLevel; i++) {
+ if (levels[i].winPtr == winPtr) {
+ for (j = i; j <= curLevel; j++) {
+ levels[j].winPtr->optionLevel = -1;
+ }
+ curLevel = i-1;
+ basePtr = levels[i].bases;
+ for (j = 0; j < NUM_STACKS; j++) {
+ arrayPtr = stacks[j];
+ arrayPtr->numUsed = basePtr[j];
+ arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
+ }
+ if (curLevel <= 0) {
+ cachedWindow = NULL;
+ } else {
+ cachedWindow = levels[curLevel].winPtr;
+ }
+ break;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParsePriority --
+ *
+ * Parse a string priority value.
+ *
+ * Results:
+ * The return value is the integer priority level corresponding
+ * to string, or -1 if string doesn't point to a valid priority level.
+ * In this case, an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ParsePriority(interp, string)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ char *string; /* Describes a priority level, either
+ * symbolically or numerically. */
+{
+ int priority, c;
+ size_t length;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'w')
+ && (strncmp(string, "widgetDefault", length) == 0)) {
+ return CK_WIDGET_DEFAULT_PRIO;
+ } else if ((c == 's')
+ && (strncmp(string, "startupFile", length) == 0)) {
+ return CK_STARTUP_FILE_PRIO;
+ } else if ((c == 'u')
+ && (strncmp(string, "userDefault", length) == 0)) {
+ return CK_USER_DEFAULT_PRIO;
+ } else if ((c == 'i')
+ && (strncmp(string, "interactive", length) == 0)) {
+ return CK_INTERACTIVE_PRIO;
+ } else {
+ char *end;
+
+ priority = strtoul(string, &end, 0);
+ if ((end == string) || (*end != 0) || (priority < 0)
+ || (priority > 100)) {
+ Tcl_AppendResult(interp, "bad priority level \"", string,
+ "\": must be widgetDefault, startupFile, userDefault, ",
+ "interactive, or a number between 0 and 100",
+ (char *) NULL);
+ return -1;
+ }
+ }
+ return priority;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AddFromString --
+ *
+ * Given a string containing lines in the standard format for
+ * X resources (see other documentation for details on what this
+ * is), parse the resource specifications and enter them as options
+ * for tkwin's main window.
+ *
+ * Results:
+ * The return value is a standard Tcl return code. In the case of
+ * an error in parsing string, TCL_ERROR will be returned and an
+ * error message will be left in interp->result. The memory at
+ * string is totally trashed by this procedure. If you care about
+ * its contents, make a copy before calling here.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+AddFromString(interp, winPtr, string, priority)
+ Tcl_Interp *interp; /* Interpreter to use for reporting results. */
+ CkWindow *winPtr; /* Pointer to window: options are entered
+ * for this window's main window. */
+ char *string; /* String containing option specifiers. */
+ int priority; /* Priority level to use for options in
+ * this string, such as TK_USER_DEFAULT_PRIO
+ * or TK_INTERACTIVE_PRIO. Must be between
+ * 0 and TK_MAX_PRIO. */
+{
+ register char *src, *dst;
+ char *name, *value;
+ int lineNum;
+
+ src = string;
+ lineNum = 1;
+ while (1) {
+
+ /*
+ * Skip leading white space and empty lines and comment lines, and
+ * check for the end of the spec.
+ */
+
+ while ((*src == ' ') || (*src == '\t')) {
+ src++;
+ }
+ if ((*src == '#') || (*src == '!')) {
+ do {
+ src++;
+ if ((src[0] == '\\') && (src[1] == '\n')) {
+ src += 2;
+ lineNum++;
+ }
+ } while ((*src != '\n') && (*src != 0));
+ }
+ if (*src == '\n') {
+ src++;
+ lineNum++;
+ continue;
+ }
+ if (*src == '\0') {
+ break;
+ }
+
+ /*
+ * Parse off the option name, collapsing out backslash-newline
+ * sequences of course.
+ */
+
+ dst = name = src;
+ while (*src != ':') {
+ if ((*src == '\0') || (*src == '\n')) {
+ sprintf(interp->result, "missing colon on line %d",
+ lineNum);
+ return TCL_ERROR;
+ }
+ if ((src[0] == '\\') && (src[1] == '\n')) {
+ src += 2;
+ lineNum++;
+ } else {
+ *dst = *src;
+ dst++;
+ src++;
+ }
+ }
+
+ /*
+ * Eliminate trailing white space on the name, and null-terminate
+ * it.
+ */
+
+ while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) {
+ dst--;
+ }
+ *dst = '\0';
+
+ /*
+ * Skip white space between the name and the value.
+ */
+
+ src++;
+ while ((*src == ' ') || (*src == '\t')) {
+ src++;
+ }
+ if (*src == '\0') {
+ sprintf(interp->result, "missing value on line %d", lineNum);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Parse off the value, squeezing out backslash-newline sequences
+ * along the way.
+ */
+
+ dst = value = src;
+ while (*src != '\n') {
+ if (*src == '\0') {
+ sprintf(interp->result, "missing newline on line %d",
+ lineNum);
+ return TCL_ERROR;
+ }
+ if ((src[0] == '\\') && (src[1] == '\n')) {
+ src += 2;
+ lineNum++;
+ } else {
+ *dst = *src;
+ dst++;
+ src++;
+ }
+ }
+ *dst = 0;
+
+ /*
+ * Enter the option into the database.
+ */
+
+ Ck_AddOption(winPtr, name, value, priority);
+ src++;
+ lineNum++;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadOptionFile --
+ *
+ * Read a file of options ("resources" in the old X terminology)
+ * and load them into the option database.
+ *
+ * Results:
+ * The return value is a standard Tcl return code. In the case of
+ * an error in parsing string, TCL_ERROR will be returned and an
+ * error message will be left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ReadOptionFile(interp, winPtr, fileName, priority)
+ Tcl_Interp *interp; /* Interpreter to use for reporting results. */
+ CkWindow *winPtr; /* Pointer to window: options are entered
+ * for this window's main window. */
+ char *fileName; /* Name of file containing options. */
+ int priority; /* Priority level to use for options in
+ * this file, such as TK_USER_DEFAULT_PRIO
+ * or TK_INTERACTIVE_PRIO. Must be between
+ * 0 and TK_MAX_PRIO. */
+{
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ char *realName, *buffer;
+ int fileId, result;
+ struct stat statBuf;
+ Tcl_DString newName;
+
+ realName = Tcl_TildeSubst(interp, fileName, &newName);
+ if (realName == NULL) {
+ return TCL_ERROR;
+ }
+ fileId = open(realName, O_RDONLY, 0);
+ Tcl_DStringFree(&newName);
+ if (fileId < 0) {
+ Tcl_AppendResult(interp, "couldn't read file \"", fileName, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (fstat(fileId, &statBuf) == -1) {
+ Tcl_AppendResult(interp, "couldn't stat file \"", fileName, "\"",
+ (char *) NULL);
+ close(fileId);
+ return TCL_ERROR;
+ }
+ buffer = (char *) ckalloc((unsigned) statBuf.st_size+1);
+ if (read(fileId, buffer, (unsigned) statBuf.st_size) != statBuf.st_size) {
+ Tcl_AppendResult(interp, "error reading file \"", fileName, "\"",
+ (char *) NULL);
+ close(fileId);
+ return TCL_ERROR;
+ }
+ close(fileId);
+ buffer[statBuf.st_size] = 0;
+ result = AddFromString(interp, winPtr, buffer, priority);
+ ckfree(buffer);
+ return result;
+#else
+ char *realName, *buffer;
+ int result, bufferSize;
+ Tcl_Channel chan;
+ Tcl_DString newName;
+
+ realName = Tcl_TranslateFileName(interp, fileName, &newName);
+ if (realName == NULL) {
+ return TCL_ERROR;
+ }
+ chan = Tcl_OpenFileChannel(interp, realName, "r", 0);
+ Tcl_DStringFree(&newName);
+ if (chan == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Compute size of file by seeking to the end of the file.
+ */
+
+ bufferSize = Tcl_Seek(chan, 0L, SEEK_END);
+ if (bufferSize < 0) {
+ Tcl_AppendResult(interp, "error getting file size of \"",
+ fileName, "\"", (char *) NULL);
+ Tcl_Close(NULL, chan);
+ return TCL_ERROR;
+ }
+ Tcl_Seek(chan, 0L, SEEK_SET);
+ buffer = (char *) ckalloc((unsigned) bufferSize + 1);
+ if (Tcl_Read(chan, buffer, bufferSize) != bufferSize) {
+ ckfree(buffer);
+ Tcl_AppendResult(interp, "error reading file \"", fileName, "\"",
+ (char *) NULL);
+ Tcl_Close(NULL, chan);
+ return TCL_ERROR;
+ }
+ Tcl_Close(NULL, chan);
+ buffer[bufferSize] = 0;
+ result = AddFromString(interp, winPtr, buffer, priority);
+ ckfree(buffer);
+ return result;
+#endif
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * NewArray --
+ *
+ * Create a new ElArray structure of a given size.
+ *
+ * Results:
+ * The return value is a pointer to a properly initialized
+ * element array with "numEls" space. The array is marked
+ * as having no active elements.
+ *
+ * Side effects:
+ * Memory is allocated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static ElArray *
+NewArray(numEls)
+ int numEls; /* How many elements of space to allocate. */
+{
+ register ElArray *arrayPtr;
+
+ arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls));
+ arrayPtr->arraySize = numEls;
+ arrayPtr->numUsed = 0;
+ arrayPtr->nextToUse = arrayPtr->els;
+ return arrayPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ExtendArray --
+ *
+ * Add a new element to an array, extending the array if
+ * necessary.
+ *
+ * Results:
+ * The return value is a pointer to the new array, which
+ * will be different from arrayPtr if the array got expanded.
+ *
+ * Side effects:
+ * Memory may be allocated or freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static ElArray *
+ExtendArray(arrayPtr, elPtr)
+ register ElArray *arrayPtr; /* Array to be extended. */
+ register Element *elPtr; /* Element to be copied into array. */
+{
+ /*
+ * If the current array has filled up, make it bigger.
+ */
+
+ if (arrayPtr->numUsed >= arrayPtr->arraySize) {
+ register ElArray *newPtr;
+
+ newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize));
+ newPtr->arraySize = 2*arrayPtr->arraySize;
+ newPtr->numUsed = arrayPtr->numUsed;
+ newPtr->nextToUse = &newPtr->els[newPtr->numUsed];
+ memcpy((VOID *) newPtr->els, (VOID *) arrayPtr->els,
+ (arrayPtr->arraySize*sizeof(Element)));
+ ckfree((char *) arrayPtr);
+ arrayPtr = newPtr;
+ }
+
+ *arrayPtr->nextToUse = *elPtr;
+ arrayPtr->nextToUse++;
+ arrayPtr->numUsed++;
+ return arrayPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * SetupStacks --
+ *
+ * Arrange the stacks so that they cache all the option
+ * information for a particular window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The stacks are modified to hold information for tkwin
+ * and all its ancestors in the window hierarchy.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+SetupStacks(winPtr, leaf)
+ CkWindow *winPtr; /* Window for which information is to
+ * be cached. */
+ int leaf; /* Non-zero means this is the leaf
+ * window being probed. Zero means this
+ * is an ancestor of the desired leaf. */
+{
+ int level, i, *iPtr;
+ register StackLevel *levelPtr;
+ register ElArray *arrayPtr;
+
+ /*
+ * The following array defines the order in which the current
+ * stacks are searched to find matching entries to add to the
+ * stacks. Given the current priority-based scheme, the order
+ * below is no longer relevant; all that matters is that an
+ * element is on the list *somewhere*. The ordering is a relic
+ * of the old days when priorities were determined differently.
+ */
+
+ static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME,
+ EXACT_NODE_CLASS, EXACT_NODE_NAME, -1};
+
+ if (winPtr->mainPtr->optionRootPtr == NULL) {
+ OptionInit(winPtr->mainPtr);
+ }
+
+ /*
+ * Step 1: make sure that options are cached for this window's
+ * parent.
+ */
+
+ if (winPtr->parentPtr != NULL) {
+ level = winPtr->parentPtr->optionLevel;
+ if ((level == -1) || (cachedWindow == NULL)) {
+ SetupStacks(winPtr->parentPtr, 0);
+ level = winPtr->parentPtr->optionLevel;
+ }
+ level++;
+ } else {
+ level = 1;
+ }
+
+ /*
+ * Step 2: pop extra unneeded information off the stacks and
+ * mark those windows as no longer having cached information.
+ */
+
+ if (curLevel >= level) {
+ while (curLevel >= level) {
+ levels[curLevel].winPtr->optionLevel = -1;
+ curLevel--;
+ }
+ levelPtr = &levels[level];
+ for (i = 0; i < NUM_STACKS; i++) {
+ arrayPtr = stacks[i];
+ arrayPtr->numUsed = levelPtr->bases[i];
+ arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
+ }
+ }
+ curLevel = winPtr->optionLevel = level;
+
+ /*
+ * Step 3: if the root database information isn't loaded or
+ * isn't valid, initialize level 0 of the stack from the
+ * database root (this only happens if winPtr is a main window).
+ */
+
+ if ((curLevel == 1)
+ && ((cachedWindow == NULL)
+ || (cachedWindow->mainPtr != winPtr->mainPtr))) {
+ for (i = 0; i < NUM_STACKS; i++) {
+ arrayPtr = stacks[i];
+ arrayPtr->numUsed = 0;
+ arrayPtr->nextToUse = arrayPtr->els;
+ }
+ ExtendStacks(winPtr->mainPtr->optionRootPtr, 0);
+ }
+
+ /*
+ * Step 4: create a new stack level; grow the level array if
+ * we've run out of levels. Clear the stacks for EXACT_LEAF_NAME
+ * and EXACT_LEAF_CLASS (anything that was there is of no use
+ * any more).
+ */
+
+ if (curLevel >= numLevels) {
+ StackLevel *newLevels;
+
+ newLevels = (StackLevel *) ckalloc((unsigned)
+ (numLevels*2*sizeof(StackLevel)));
+ memcpy((VOID *) newLevels, (VOID *) levels,
+ (numLevels*sizeof(StackLevel)));
+ ckfree((char *) levels);
+ numLevels *= 2;
+ levels = newLevels;
+ }
+ levelPtr = &levels[curLevel];
+ levelPtr->winPtr = winPtr;
+ arrayPtr = stacks[EXACT_LEAF_NAME];
+ arrayPtr->numUsed = 0;
+ arrayPtr->nextToUse = arrayPtr->els;
+ arrayPtr = stacks[EXACT_LEAF_CLASS];
+ arrayPtr->numUsed = 0;
+ arrayPtr->nextToUse = arrayPtr->els;
+ levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed;
+ levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed;
+ levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed;
+ levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed;
+ levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed;
+ levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed;
+ levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed;
+ levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed;
+
+
+ /*
+ * Step 5: scan the current stack level looking for matches to this
+ * window's name or class; where found, add new information to the
+ * stacks.
+ */
+
+ for (iPtr = searchOrder; *iPtr != -1; iPtr++) {
+ register Element *elPtr;
+ int count;
+ Ck_Uid id;
+
+ i = *iPtr;
+ if (i & CLASS) {
+ id = winPtr->classUid;
+ } else {
+ id = winPtr->nameUid;
+ }
+ elPtr = stacks[i]->els;
+ count = levelPtr->bases[i];
+
+ /*
+ * For wildcard stacks, check all entries; for non-wildcard
+ * stacks, only check things that matched in the parent.
+ */
+
+ if (!(i & WILDCARD)) {
+ elPtr += levelPtr[-1].bases[i];
+ count -= levelPtr[-1].bases[i];
+ }
+ for ( ; count > 0; elPtr++, count--) {
+ if (elPtr->nameUid != id) {
+ continue;
+ }
+ ExtendStacks(elPtr->child.arrayPtr, leaf);
+ }
+ }
+ cachedWindow = winPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ExtendStacks --
+ *
+ * Given an element array, copy all the elements from the
+ * array onto the system stacks (except for irrelevant leaf
+ * elements).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The option stacks are extended.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ExtendStacks(arrayPtr, leaf)
+ ElArray *arrayPtr; /* Array of elements to copy onto stacks. */
+ int leaf; /* If zero, then don't copy exact leaf
+ * elements. */
+{
+ register int count;
+ register Element *elPtr;
+
+ for (elPtr = arrayPtr->els, count = arrayPtr->numUsed;
+ count > 0; elPtr++, count--) {
+ if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) {
+ continue;
+ }
+ stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OptionInit --
+ *
+ * Initialize data structures for option handling.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Option-related data structures get initialized.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+OptionInit(mainPtr)
+ register CkMainInfo *mainPtr; /* Top-level information about
+ * window that isn't initialized
+ * yet. */
+{
+ int i;
+
+ /*
+ * First, once-only initialization.
+ */
+
+ if (numLevels == 0) {
+
+ numLevels = 5;
+ levels = (StackLevel *) ckalloc((unsigned) (5*sizeof(StackLevel)));
+ for (i = 0; i < NUM_STACKS; i++) {
+ stacks[i] = NewArray(10);
+ levels[0].bases[i] = 0;
+ }
+
+ defaultMatch.nameUid = NULL;
+ defaultMatch.child.valueUid = NULL;
+ defaultMatch.priority = -1;
+ defaultMatch.flags = 0;
+ }
+
+ /*
+ * Then, per-main-window initialization. Create and delete dummy
+ * interpreter for message logging.
+ */
+
+ mainPtr->optionRootPtr = NewArray(20);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ClearOptionTree --
+ *
+ * This procedure is called to erase everything in a
+ * hierarchical option database.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All the options associated with arrayPtr are deleted,
+ * along with all option subtrees. The space pointed to
+ * by arrayPtr is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ClearOptionTree(arrayPtr)
+ ElArray *arrayPtr; /* Array of options; delete everything
+ * referred to recursively by this. */
+{
+ register Element *elPtr;
+ int count;
+
+ for (count = arrayPtr->numUsed, elPtr = arrayPtr->els; count > 0;
+ count--, elPtr++) {
+ if (elPtr->flags & NODE) {
+ ClearOptionTree(elPtr->child.arrayPtr);
+ }
+ }
+ ckfree((char *) arrayPtr);
+}
--- /dev/null
+/*
+ * ckPack.c --
+ *
+ * This file contains code to implement the "packer"
+ * geometry manager.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
+
+/* For each window that the packer cares about (either because
+ * the window is managed by the packer or because the window
+ * has slaves that are managed by the packer), there is a
+ * structure of the following type:
+ */
+
+typedef struct Packer {
+ CkWindow *winPtr; /* Pointer to window. NULL means that
+ * the window has been deleted, but the
+ * packer hasn't had a chance to clean up
+ * yet because the structure is still in
+ * use. */
+ struct Packer *masterPtr; /* Master window within which this window
+ * is packed (NULL means this window
+ * isn't managed by the packer). */
+ struct Packer *nextPtr; /* Next window packed within same
+ * parent. List is priority-ordered:
+ * first on list gets packed first. */
+ struct Packer *slavePtr; /* First in list of slaves packed
+ * inside this window (NULL means
+ * no packed slaves). */
+ Side side; /* Side of parent against which
+ * this window is packed. */
+ Ck_Anchor anchor; /* If frame allocated for window is larger
+ * than window needs, this indicates how
+ * where to position window in frame. */
+ int padX, padY; /* Total additional pixels to leave around the
+ * window (half of this space is left on each
+ * side). This is space *outside* the window:
+ * we'll allocate extra space in frame but
+ * won't enlarge window). */
+ int iPadX, iPadY; /* Total extra pixels to allocate inside the
+ * window (half this amount will appear on
+ * each side). */
+ int *abortPtr; /* If non-NULL, it means that there is a nested
+ * call to ArrangePacking already working on
+ * this window. *abortPtr may be set to 1 to
+ * abort that nested call. This happens, for
+ * example, if tkwin or any of its slaves
+ * is deleted. */
+ int flags; /* Miscellaneous flags; see below
+ * for definitions. */
+} Packer;
+
+/*
+ * Flag values for Packer structures:
+ *
+ * REQUESTED_REPACK: 1 means a Ck_DoWhenIdle request
+ * has already been made to repack
+ * all the slaves of this window.
+ * FILLX: 1 means if frame allocated for window
+ * is wider than window needs, expand window
+ * to fill frame. 0 means don't make window
+ * any larger than needed.
+ * FILLY: Same as FILLX, except for height.
+ * EXPAND: 1 means this window's frame will absorb any
+ * extra space in the parent window.
+ * DONT_PROPAGATE: 1 means don't set this window's requested
+ * size. 0 means if this window is a master
+ * then Tk will set its requested size to fit
+ * the needs of its slaves.
+ */
+
+#define REQUESTED_REPACK 1
+#define FILLX 2
+#define FILLY 4
+#define EXPAND 8
+#define DONT_PROPAGATE 16
+
+/*
+ * Hash table used to map from CkWindow pointers to corresponding
+ * Packer structures:
+ */
+
+static Tcl_HashTable packerHashTable;
+
+/*
+ * Have statics in this module been initialized?
+ */
+
+static int initialized = 0;
+
+/*
+ * The following structure is the official type record for the
+ * packer:
+ */
+
+static void PackReqProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+static void PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+
+static Ck_GeomMgr packerType = {
+ "pack", /* name */
+ PackReqProc, /* requestProc */
+ PackLostSlaveProc, /* lostSlaveProc */
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ArrangePacking _ANSI_ARGS_((ClientData clientData));
+static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, int argc, char *argv[]));
+static Packer * GetPacker _ANSI_ARGS_((CkWindow *winPtr));
+static void PackStructureProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void Unlink _ANSI_ARGS_((Packer *packPtr));
+static int XExpansion _ANSI_ARGS_((Packer *slavePtr,
+ int cavityWidth));
+static int YExpansion _ANSI_ARGS_((Packer *slavePtr,
+ int cavityHeight));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_PackCmd --
+ *
+ * This procedure is invoked to process the "pack" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_PackCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ size_t length;
+ int c;
+
+ if ((argc >= 2) && (argv[1][0] == '.')) {
+ return ConfigureSlaves(interp, mainPtr, argc-1, argv+1);
+ }
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argv[2][0] != '.') {
+ Tcl_AppendResult(interp, "bad argument \"", argv[2],
+ "\": must be name of window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return ConfigureSlaves(interp, mainPtr, argc-2, argv+2);
+ } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
+ CkWindow *slave;
+ Packer *slavePtr;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ slave = Ck_NameToWindow(interp, argv[i], mainPtr);
+ if (slave == NULL) {
+ continue;
+ }
+ slavePtr = GetPacker(slave);
+ if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
+ Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
+ (ClientData) NULL);
+ if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr,
+ slavePtr->masterPtr->winPtr);
+ }
+ Unlink(slavePtr);
+ Ck_UnmapWindow(slavePtr->winPtr);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
+ register Packer *slavePtr;
+ CkWindow *slave;
+ char buffer[300];
+ static char *sideNames[] = {"top", "bottom", "left", "right"};
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " info window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slave = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetPacker(slave);
+ if (slavePtr->masterPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_AppendElement(interp, "-in");
+ Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
+ Tcl_AppendElement(interp, "-anchor");
+ Tcl_AppendElement(interp, Ck_NameOfAnchor(slavePtr->anchor));
+ Tcl_AppendResult(interp, " -expand ",
+ (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
+ (char *) NULL);
+ switch (slavePtr->flags & (FILLX|FILLY)) {
+ case 0:
+ Tcl_AppendResult(interp, "none", (char *) NULL);
+ break;
+ case FILLX:
+ Tcl_AppendResult(interp, "x", (char *) NULL);
+ break;
+ case FILLY:
+ Tcl_AppendResult(interp, "y", (char *) NULL);
+ break;
+ case FILLX|FILLY:
+ Tcl_AppendResult(interp, "both", (char *) NULL);
+ break;
+ }
+ sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
+ slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
+ slavePtr->padY/2);
+ Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
+ (char *) NULL);
+ } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
+ CkWindow *master;
+ Packer *masterPtr;
+ int propagate;
+
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " propagate window ?boolean?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(master);
+ if (argc == 3) {
+ if (masterPtr->flags & DONT_PROPAGATE) {
+ interp->result = "0";
+ } else {
+ interp->result = "1";
+ }
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (propagate) {
+ masterPtr->flags &= ~DONT_PROPAGATE;
+
+ /*
+ * Repack the master to allow new geometry information to
+ * propagate upwards to the master's master.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ } else {
+ masterPtr->flags |= DONT_PROPAGATE;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
+ CkWindow *master;
+ Packer *masterPtr, *slavePtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " slaves window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Ck_NameToWindow(interp, argv[2], mainPtr);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(master);
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be configure, forget, info, ",
+ "propagate, or slaves", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * PackReqProc --
+ *
+ * This procedure is invoked by Ck_GeometryRequest for
+ * windows managed by the packer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for winPtr, and all its managed siblings, to
+ * be re-packed at the next idle point.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+PackReqProc(clientData, winPtr)
+ ClientData clientData; /* Packer's information about
+ * window that got new preferred
+ * geometry. */
+ CkWindow *winPtr; /* Other information about the window. */
+{
+ register Packer *packPtr = (Packer *) clientData;
+
+ packPtr = packPtr->masterPtr;
+ if (!(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * PackLostSlaveProc --
+ *
+ * This procedure is invoked whenever some other geometry
+ * claims control over a slave that used to be managed by us.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets all packer-related information about the slave.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+PackLostSlaveProc(clientData, winPtr)
+ ClientData clientData; /* Packer structure for slave window that
+ * was stolen away. */
+ CkWindow *winPtr; /* Pointer to window. */
+{
+ register Packer *slavePtr = (Packer *) clientData;
+
+ if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
+ }
+ Unlink(slavePtr);
+ Ck_UnmapWindow(slavePtr->winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ArrangePacking --
+ *
+ * This procedure is invoked (using the Tcl_DoWhenIdle
+ * mechanism) to re-layout a set of windows managed by
+ * the packer. It is invoked at idle time so that a
+ * series of packer requests can be merged into a single
+ * layout operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The packed slaves of masterPtr may get resized or
+ * moved.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ArrangePacking(clientData)
+ ClientData clientData; /* Structure describing parent whose slaves
+ * are to be re-layed out. */
+{
+ register Packer *masterPtr = (Packer *) clientData;
+ register Packer *slavePtr;
+ int cavityX, cavityY, cavityWidth, cavityHeight;
+ /* These variables keep track of the
+ * as-yet-unallocated space remaining in
+ * the middle of the parent window. */
+ int frameX, frameY, frameWidth, frameHeight;
+ /* These variables keep track of the frame
+ * allocated to the current window. */
+ int x, y, width, height; /* These variables are used to hold the
+ * actual geometry of the current window. */
+ int intBWidth; /* Width of internal border in parent window,
+ * if any. */
+ int abort; /* May get set to non-zero to abort this
+ * repacking operation. */
+ int borderX, borderY;
+ int maxWidth, maxHeight, tmp;
+
+ masterPtr->flags &= ~REQUESTED_REPACK;
+
+ /*
+ * If the parent has no slaves anymore, then don't do anything
+ * at all: just leave the parent's size as-is.
+ */
+
+ if (masterPtr->slavePtr == NULL) {
+ return;
+ }
+
+ /*
+ * Abort any nested call to ArrangePacking for this window, since
+ * we'll do everything necessary here, and set up so this call
+ * can be aborted if necessary.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->abortPtr = &abort;
+ abort = 0;
+ Ck_Preserve((ClientData) masterPtr);
+
+ /*
+ * Pass #1: scan all the slaves to figure out the total amount
+ * of space needed. Two separate width and height values are
+ * computed:
+ *
+ * width - Holds the sum of the widths (plus padding) of
+ * all the slaves seen so far that were packed LEFT
+ * or RIGHT.
+ * height - Holds the sum of the heights (plus padding) of
+ * all the slaves seen so far that were packed TOP
+ * or BOTTOM.
+ *
+ * maxWidth - Gradually builds up the width needed by the master
+ * to just barely satisfy all the slave's needs. For
+ * each slave, the code computes the width needed for
+ * all the slaves so far and updates maxWidth if the
+ * new value is greater.
+ * maxHeight - Same as maxWidth, except keeps height info.
+ */
+
+ intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
+ width = height = maxWidth = maxHeight = 2*intBWidth;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ tmp = slavePtr->winPtr->reqWidth
+ + slavePtr->padX + slavePtr->iPadX + width;
+ if (tmp > maxWidth) {
+ maxWidth = tmp;
+ }
+ height += slavePtr->winPtr->reqHeight
+ + slavePtr->padY + slavePtr->iPadY;
+ } else {
+ tmp = slavePtr->winPtr->reqHeight
+ + slavePtr->padY + slavePtr->iPadY + height;
+ if (tmp > maxHeight) {
+ maxHeight = tmp;
+ }
+ width += slavePtr->winPtr->reqWidth
+ + slavePtr->padX + slavePtr->iPadX;
+ }
+ }
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ if (height > maxHeight) {
+ maxHeight = height;
+ }
+
+ /*
+ * If the total amount of space needed in the parent window has
+ * changed, and if we're propagating geometry information, then
+ * notify the next geometry manager up and requeue ourselves to
+ * start again after the parent has had a chance to
+ * resize us.
+ */
+
+ if (((maxWidth != masterPtr->winPtr->reqWidth)
+ || (maxHeight != masterPtr->winPtr->reqHeight))
+ && !(masterPtr->flags & DONT_PROPAGATE)) {
+ Ck_GeometryRequest(masterPtr->winPtr, maxWidth, maxHeight);
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ goto done;
+ }
+
+ /*
+ * Pass #2: scan the slaves a second time assigning
+ * new sizes. The "cavity" variables keep track of the
+ * unclaimed space in the cavity of the window; this
+ * shrinks inward as we allocate windows around the
+ * edges. The "frame" variables keep track of the space
+ * allocated to the current window and its frame. The
+ * current window is then placed somewhere inside the
+ * frame, depending on anchor.
+ */
+
+ cavityX = cavityY = x = y = intBWidth;
+ cavityWidth = masterPtr->winPtr->width - 2*intBWidth;
+ cavityHeight = masterPtr->winPtr->height - 2*intBWidth;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ frameWidth = cavityWidth;
+ frameHeight = slavePtr->winPtr->reqHeight
+ + slavePtr->padY + slavePtr->iPadY;
+ if (slavePtr->flags & EXPAND) {
+ frameHeight += YExpansion(slavePtr, cavityHeight);
+ }
+ cavityHeight -= frameHeight;
+ if (cavityHeight < 0) {
+ frameHeight += cavityHeight;
+ cavityHeight = 0;
+ }
+ frameX = cavityX;
+ if (slavePtr->side == TOP) {
+ frameY = cavityY;
+ cavityY += frameHeight;
+ } else {
+ frameY = cavityY + cavityHeight;
+ }
+ } else {
+ frameHeight = cavityHeight;
+ frameWidth = slavePtr->winPtr->reqWidth
+ + slavePtr->padX + slavePtr->iPadX;
+ if (slavePtr->flags & EXPAND) {
+ frameWidth += XExpansion(slavePtr, cavityWidth);
+ }
+ cavityWidth -= frameWidth;
+ if (cavityWidth < 0) {
+ frameWidth += cavityWidth;
+ cavityWidth = 0;
+ }
+ frameY = cavityY;
+ if (slavePtr->side == LEFT) {
+ frameX = cavityX;
+ cavityX += frameWidth;
+ } else {
+ frameX = cavityX + cavityWidth;
+ }
+ }
+
+ /*
+ * Now that we've got the size of the frame for the window,
+ * compute the window's actual size and location using the
+ * fill, padding, and frame factors. The variables "borderX"
+ * and "borderY" are used to handle the differences between
+ * old-style packing and the new style (in old-style, iPadX
+ * and iPadY are always zero and padding is completely ignored
+ * except when computing frame size).
+ */
+
+ borderX = slavePtr->padX;
+ borderY = slavePtr->padY;
+ width = slavePtr->winPtr->reqWidth + slavePtr->iPadX;
+ if ((slavePtr->flags & FILLX)
+ || (width > (frameWidth - borderX))) {
+ width = frameWidth - borderX;
+ }
+ height = slavePtr->winPtr->reqHeight + slavePtr->iPadY;
+ if ((slavePtr->flags & FILLY)
+ || (height > (frameHeight - borderY))) {
+ height = frameHeight - borderY;
+ }
+ borderX /= 2;
+ borderY /= 2;
+ switch (slavePtr->anchor) {
+ case CK_ANCHOR_N:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + borderY;
+ break;
+ case CK_ANCHOR_NE:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + borderY;
+ break;
+ case CK_ANCHOR_E:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ case CK_ANCHOR_SE:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case CK_ANCHOR_S:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case CK_ANCHOR_SW:
+ x = frameX + borderX;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case CK_ANCHOR_W:
+ x = frameX + borderX;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ case CK_ANCHOR_NW:
+ x = frameX + borderX;
+ y = frameY + borderY;
+ break;
+ case CK_ANCHOR_CENTER:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ default:
+ panic("bad frame factor in ArrangePacking");
+ }
+
+ /*
+ * The final step is to set the position, size, and mapped/unmapped
+ * state of the slave.
+ */
+
+ if (masterPtr->winPtr == slavePtr->winPtr->parentPtr) {
+ if (width <= 0 || height <= 0) {
+ Ck_UnmapWindow(slavePtr->winPtr);
+ } else {
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+ if (x != slavePtr->winPtr->x ||
+ y != slavePtr->winPtr->y)
+ Ck_MoveWindow(slavePtr->winPtr, x, y);
+ /*
+ * Temporary kludge til Ck_MoveResizeWindow available !!!
+ */
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+ if (abort)
+ goto done;
+
+ /*
+ * Don't map the slave if the master isn't mapped: wait
+ * until the master gets mapped later.
+ */
+
+ if (masterPtr->winPtr->flags & CK_MAPPED) {
+ Ck_MapWindow(slavePtr->winPtr);
+ }
+ }
+ } else {
+ if ((width <= 0) || (height <= 0)) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr, masterPtr->winPtr);
+ Ck_UnmapWindow(slavePtr->winPtr);
+ } else {
+ Ck_MaintainGeometry(slavePtr->winPtr, masterPtr->winPtr,
+ x, y, width, height);
+ }
+ }
+
+ /*
+ * Changes to the window's structure could cause almost anything
+ * to happen, including deleting the parent or child. If this
+ * happens, we'll be told to abort.
+ */
+
+ if (abort) {
+ goto done;
+ }
+ }
+
+done:
+ masterPtr->abortPtr = NULL;
+ Ck_Release((ClientData) masterPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XExpansion --
+ *
+ * Given a list of packed slaves, the first of which is packed
+ * on the left or right and is expandable, compute how much to
+ * expand the child.
+ *
+ * Results:
+ * The return value is the number of additional pixels to give to
+ * the child.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+XExpansion(slavePtr, cavityWidth)
+ register Packer *slavePtr; /* First in list of remaining
+ * slaves. */
+ int cavityWidth; /* Horizontal space left for all
+ * remaining slaves. */
+{
+ int numExpand, minExpand, curExpand;
+ int childWidth;
+
+ /*
+ * This procedure is tricky because windows packed top or bottom can
+ * be interspersed among expandable windows packed left or right.
+ * Scan through the list, keeping a running sum of the widths of
+ * all left and right windows (actually, count the cavity space not
+ * allocated) and a running count of all expandable left and right
+ * windows. At each top or bottom window, and at the end of the
+ * list, compute the expansion factor that seems reasonable at that
+ * point. Return the smallest factor seen at any of these points.
+ */
+
+ minExpand = cavityWidth;
+ numExpand = 0;
+ for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
+ childWidth = slavePtr->winPtr->reqWidth
+ + slavePtr->padX + slavePtr->iPadX;
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ curExpand = (cavityWidth - childWidth)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ } else {
+ cavityWidth -= childWidth;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ curExpand = cavityWidth/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ return (minExpand < 0) ? 0 : minExpand;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * YExpansion --
+ *
+ * Given a list of packed slaves, the first of which is packed
+ * on the top or bottom and is expandable, compute how much to
+ * expand the child.
+ *
+ * Results:
+ * The return value is the number of additional pixels to give to
+ * the child.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+YExpansion(slavePtr, cavityHeight)
+ register Packer *slavePtr; /* First in list of remaining
+ * slaves. */
+ int cavityHeight; /* Vertical space left for all
+ * remaining slaves. */
+{
+ int numExpand, minExpand, curExpand;
+ int childHeight;
+
+ /*
+ * See comments for XExpansion.
+ */
+
+ minExpand = cavityHeight;
+ numExpand = 0;
+ for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
+ childHeight = slavePtr->winPtr->reqHeight
+ + slavePtr->padY + slavePtr->iPadY;
+ if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
+ curExpand = (cavityHeight - childHeight)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ } else {
+ cavityHeight -= childHeight;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ curExpand = cavityHeight/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ return (minExpand < 0) ? 0 : minExpand;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * GetPacker --
+ *
+ * This internal procedure is used to locate a Packer
+ * structure for a given window, creating one if one
+ * doesn't exist already.
+ *
+ * Results:
+ * The return value is a pointer to the Packer structure
+ * corresponding to tkwin.
+ *
+ * Side effects:
+ * A new packer structure may be created. If so, then
+ * a callback is set up to clean things up when the
+ * window is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Packer *
+GetPacker(winPtr)
+ CkWindow *winPtr; /* Pointer to window for which
+ * packer structure is desired. */
+{
+ register Packer *packPtr;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there's already packer for this window. If not,
+ * then create a new one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) winPtr, &new);
+ if (!new) {
+ return (Packer *) Tcl_GetHashValue(hPtr);
+ }
+ packPtr = (Packer *) ckalloc(sizeof (Packer));
+ packPtr->winPtr = winPtr;
+ packPtr->masterPtr = NULL;
+ packPtr->nextPtr = NULL;
+ packPtr->slavePtr = NULL;
+ packPtr->side = TOP;
+ packPtr->anchor = CK_ANCHOR_CENTER;
+ packPtr->padX = packPtr->padY = 0;
+ packPtr->iPadX = packPtr->iPadY = 0;
+ packPtr->abortPtr = NULL;
+ packPtr->flags = 0;
+ Tcl_SetHashValue(hPtr, packPtr);
+ Ck_CreateEventHandler(winPtr,
+ CK_EV_DESTROY | CK_EV_MAP | CK_EV_EXPOSE,
+ PackStructureProc, (ClientData) packPtr);
+ return packPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a packer from its parent's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The parent will be scheduled for repacking.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(packPtr)
+ register Packer *packPtr; /* Window to unlink. */
+{
+ register Packer *masterPtr, *packPtr2;
+
+ masterPtr = packPtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+ if (masterPtr->slavePtr == packPtr) {
+ masterPtr->slavePtr = packPtr->nextPtr;
+ } else {
+ for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
+ if (packPtr2 == NULL) {
+ panic("Unlink couldn't find previous window");
+ }
+ if (packPtr2->nextPtr == packPtr) {
+ packPtr2->nextPtr = packPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ packPtr->masterPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPacker --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a packer at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the packer is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyPacker(clientData)
+ ClientData clientData; /* Info about packed window that
+ * is now dead. */
+{
+ register Packer *packPtr = (Packer *) clientData;
+ ckfree((char *) packPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * PackStructureProc --
+ *
+ * This procedure is invoked by the event dispatcher in response
+ * to CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a window was just deleted, clean up all its packer-related
+ * information. If it was just resized, repack its slaves, if
+ * any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PackStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Our information about window
+ * referred to by eventPtr. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ register Packer *packPtr = (Packer *) clientData;
+
+ if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
+ if ((packPtr->slavePtr != NULL)
+ && !(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ register Packer *slavePtr, *nextPtr;
+
+ if (packPtr->masterPtr != NULL) {
+ Unlink(packPtr);
+ }
+ for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
+ slavePtr = nextPtr) {
+ Ck_ManageGeometry(slavePtr->winPtr, (Ck_GeomMgr *) NULL,
+ (ClientData) NULL);
+ Ck_UnmapWindow(slavePtr->winPtr);
+ slavePtr->masterPtr = NULL;
+ nextPtr = slavePtr->nextPtr;
+ slavePtr->nextPtr = NULL;
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
+ (char *) packPtr->winPtr));
+ if (packPtr->flags & REQUESTED_REPACK) {
+ Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
+ }
+ packPtr->winPtr = NULL;
+ Ck_EventuallyFree((ClientData) packPtr, (Ck_FreeProc *) DestroyPacker);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureSlaves --
+ *
+ * This implements the guts of the "pack configure" command. Given
+ * a list of slaves and configuration options, it arranges for the
+ * packer to manage the slaves and sets the specified options.
+ *
+ * Results:
+ * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
+ * returned and interp->result is set to contain an error message.
+ *
+ * Side effects:
+ * Slave windows get taken over by the packer.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlaves(interp, winPtr, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ CkWindow *winPtr; /* Any window in application containing
+ * slaves. Used to look up slave names. */
+ int argc; /* Number of elements in argv. */
+ char *argv[]; /* Argument strings: contains one or more
+ * window names followed by any number
+ * of "option value" pairs. Caller must
+ * make sure that there is at least one
+ * window name. */
+{
+ Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
+ CkWindow *other, *slave, *parent, *ancestor;
+ int i, j, numWindows, c, tmp, positionGiven;
+ size_t length;
+
+ /*
+ * Find out how many windows are specified.
+ */
+
+ for (numWindows = 0; numWindows < argc; numWindows++) {
+ if (argv[numWindows][0] != '.') {
+ break;
+ }
+ }
+
+ /*
+ * Iterate over all of the slave windows, parsing the configuration
+ * options for each slave. It's a bit wasteful to re-parse the
+ * options for each slave, but things get too messy if we try to
+ * parse the arguments just once at the beginning. For example,
+ * if a slave already is packed we want to just change a few
+ * existing values without resetting everything. If there are
+ * multiple windows, the -after, -before, and -in options only
+ * get processed for the first window.
+ */
+
+ masterPtr = NULL;
+ prevPtr = NULL;
+ positionGiven = 0;
+ for (j = 0; j < numWindows; j++) {
+ slave = Ck_NameToWindow(interp, argv[j], winPtr);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ if (slave->flags & CK_TOPLEVEL) {
+ Tcl_AppendResult(interp, "can't pack \"", argv[j],
+ "\": it's a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr = GetPacker(slave);
+
+ /*
+ * If the slave isn't currently packed, reset all of its
+ * configuration information to default values (there could
+ * be old values left from a previous packing).
+ */
+
+ if (slavePtr->masterPtr == NULL) {
+ slavePtr->side = TOP;
+ slavePtr->anchor = CK_ANCHOR_CENTER;
+ slavePtr->padX = slavePtr->padY = 0;
+ slavePtr->iPadX = slavePtr->iPadY = 0;
+ slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
+ }
+
+ for (i = numWindows; i < argc; i+=2) {
+ if ((i+2) > argc) {
+ Tcl_AppendResult(interp, "extra option \"", argv[i],
+ "\" (option with no value?)", (char *) NULL);
+ return TCL_ERROR;
+ }
+ length = strlen(argv[i]);
+ if (length < 2) {
+ goto badOption;
+ }
+ c = argv[i][1];
+ if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
+ && (length >= 2)) {
+ if (j == 0) {
+ other = Ck_NameToWindow(interp, argv[i+1], winPtr);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ prevPtr = GetPacker(other);
+ if (prevPtr->masterPtr == NULL) {
+ notPacked:
+ Tcl_AppendResult(interp, "window \"", argv[i+1],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ masterPtr = prevPtr->masterPtr;
+ positionGiven = 1;
+ }
+ } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
+ && (length >= 2)) {
+ if (Ck_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if ((c == 'b')
+ && (strncmp(argv[i], "-before", length) == 0)) {
+ if (j == 0) {
+ other = Ck_NameToWindow(interp, argv[i+1], winPtr);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ otherPtr = GetPacker(other);
+ if (otherPtr->masterPtr == NULL) {
+ goto notPacked;
+ }
+ masterPtr = otherPtr->masterPtr;
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr == otherPtr) {
+ prevPtr = NULL;
+ } else {
+ while (prevPtr->nextPtr != otherPtr) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ } else if ((c == 'e')
+ && (strncmp(argv[i], "-expand", length) == 0)) {
+ if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ slavePtr->flags &= ~EXPAND;
+ if (tmp) {
+ slavePtr->flags |= EXPAND;
+ }
+ } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
+ if (strcmp(argv[i+1], "none") == 0) {
+ slavePtr->flags &= ~(FILLX|FILLY);
+ } else if (strcmp(argv[i+1], "x") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
+ } else if (strcmp(argv[i+1], "y") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
+ } else if (strcmp(argv[i+1], "both") == 0) {
+ slavePtr->flags |= FILLX|FILLY;
+ } else {
+ Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
+ "\": must be none, x, y, or both", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
+ if (j == 0) {
+ other = Ck_NameToWindow(interp, argv[i+1], winPtr);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(other);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp < 0)) {
+ badPad:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadX = tmp*2;
+ } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->iPadY = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->padX = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
+ if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->padY = tmp*2;
+ } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
+ c = argv[i+1][0];
+ if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
+ slavePtr->side = TOP;
+ } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
+ slavePtr->side = BOTTOM;
+ } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
+ slavePtr->side = LEFT;
+ } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
+ slavePtr->side = RIGHT;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", argv[i+1],
+ "\": must be top, bottom, left, or right",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ badOption:
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+ argv[i], "\": must be -after, -anchor, -before, ",
+ "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
+ "-pady, or -side", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * If no position in a packing list was specified and the slave
+ * is already packed, then leave it in its current location in
+ * its current packing list.
+ */
+
+ if (!positionGiven && (slavePtr->masterPtr != NULL)) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If the slave is going to be put back after itself then
+ * skip the whole operation, since it won't work anyway.
+ */
+
+ if (prevPtr == slavePtr) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If none of the "-before", or "-after" options has
+ * been specified, arrange for the slave to go at the end of
+ * the order for its parent.
+ */
+
+ if (!positionGiven) {
+ masterPtr = GetPacker(slave->parentPtr);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ }
+
+ /*
+ * Make sure that the slave's parent is either the master or
+ * an ancestor of the master.
+ */
+
+ parent = slave->parentPtr;
+ for (ancestor = masterPtr->winPtr; ;
+ ancestor = ancestor->parentPtr) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (ancestor->flags & CK_TOPLEVEL) {
+ Tcl_AppendResult(interp, "can't pack ", argv[j],
+ " inside ", masterPtr->winPtr->pathName,
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (slave == masterPtr->winPtr) {
+ Tcl_AppendResult(interp, "can't pack ", argv[j],
+ " inside itself", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Unpack the slave if it's currently packed, then position it
+ * after prevPtr.
+ */
+
+ if (slavePtr->masterPtr != NULL) {
+ if ((slavePtr->masterPtr != masterPtr) &&
+ (slavePtr->masterPtr->winPtr
+ != slavePtr->winPtr->parentPtr)) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr,
+ slavePtr->masterPtr->winPtr);
+ }
+ Unlink(slavePtr);
+ }
+ slavePtr->masterPtr = masterPtr;
+ if (prevPtr == NULL) {
+ slavePtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = slavePtr;
+ } else {
+ slavePtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = slavePtr;
+ }
+ Ck_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
+ prevPtr = slavePtr;
+
+ /*
+ * Arrange for the parent to be re-packed at the first
+ * idle moment.
+ */
+
+ scheduleLayout:
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ }
+ return TCL_OK;
+}
--- /dev/null
+/*
+ * ckPlace.c --
+ *
+ * This file contains code to implement a simple geometry manager
+ * for Ck based on absolute placement or "rubber-sheet" placement.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * Border modes for relative placement:
+ *
+ * BM_INSIDE: relative distances computed using area inside
+ * all borders of master window.
+ * BM_IGNORE: border issues are ignored: place relative to
+ * master's actual window size.
+ */
+
+typedef enum {BM_INSIDE, BM_IGNORE} BorderMode;
+
+/*
+ * For each window whose geometry is managed by the placer there is
+ * a structure of the following type:
+ */
+
+typedef struct Slave {
+ CkWindow *winPtr; /* Pointer to window. */
+ struct Master *masterPtr; /* Pointer to information for window
+ * relative to which winPtr is placed.
+ * This isn't necessarily the logical
+ * parent of winPtr. NULL means the
+ * master was deleted or never assigned. */
+ struct Slave *nextPtr; /* Next in list of windows placed relative
+ * to same master (NULL for end of list). */
+
+ /*
+ * Geometry information for window; where there are both relative
+ * and absolute values for the same attribute (e.g. x and relX) only
+ * one of them is actually used, depending on flags.
+ */
+
+ int x, y; /* X and Y coordinates for winPtr. */
+ double relX, relY; /* X and Y coordinates relative to size of
+ * master. */
+ int width, height; /* Absolute dimensions for winPtr. */
+ double relWidth, relHeight; /* Dimensions for winPtr relative to size of
+ * master. */
+ Ck_Anchor anchor; /* Which point on winPtr is placed at the
+ * given position. */
+ BorderMode borderMode; /* How to treat borders of master window. */
+ int flags; /* Various flags; see below for bit
+ * definitions. */
+} Slave;
+
+/*
+ * Flag definitions for Slave structures:
+ *
+ * CHILD_REL_X - 1 means use relX field; 0 means use x.
+ * CHILD_REL_Y - 1 means use relY field; 0 means use y;
+ * CHILD_WIDTH - 1 means use width field;
+ * CHILD_REL_WIDTH - 1 means use relWidth; if neither this nor
+ * CHILD_WIDTH is 1, use window's requested
+ * width.
+ * CHILD_HEIGHT - 1 means use height field;
+ * CHILD_REL_HEIGHT - 1 means use relHeight; if neither this nor
+ * CHILD_HEIGHT is 1, use window's requested
+ * height.
+ */
+
+#define CHILD_REL_X 1
+#define CHILD_REL_Y 2
+#define CHILD_WIDTH 4
+#define CHILD_REL_WIDTH 8
+#define CHILD_HEIGHT 0x10
+#define CHILD_REL_HEIGHT 0x20
+
+/*
+ * For each master window that has a slave managed by the placer there
+ * is a structure of the following form:
+ */
+
+typedef struct Master {
+ CkWindow *winPtr; /* Pointer to master window. */
+ struct Slave *slavePtr; /* First in linked list of slaves
+ * placed relative to this master. */
+ int flags; /* See below for bit definitions. */
+} Master;
+
+/*
+ * Flag definitions for masters:
+ *
+ * PARENT_RECONFIG_PENDING - 1 means that a call to RecomputePlacement
+ * is already pending via a Do_When_Idle handler.
+ */
+
+#define PARENT_RECONFIG_PENDING 1
+
+/*
+ * The hash tables below both use CkWindow pointers as keys. They map
+ * from CkWindows to Slave and Master structures for windows, if they
+ * exist.
+ */
+
+static int initialized = 0;
+static Tcl_HashTable masterTable;
+static Tcl_HashTable slaveTable;
+
+/*
+ * The following structure is the official type record for the
+ * placer:
+ */
+
+static void PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+static void PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr));
+
+static Ck_GeomMgr placerType = {
+ "place", /* name */
+ PlaceRequestProc, /* requestProc */
+ PlaceLostSlaveProc, /* lostSlaveProc */
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static int ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
+ Slave *slavePtr, int argc, char **argv));
+static Slave * FindSlave _ANSI_ARGS_((CkWindow *winPtr));
+static Master * FindMaster _ANSI_ARGS_((CkWindow *winPtr));
+static void MasterStructureProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void RecomputePlacement _ANSI_ARGS_((ClientData clientData));
+static void UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_PlaceCmd --
+ *
+ * This procedure is invoked to process the "place" Tcl
+ * commands. See the user documentation for details on
+ * what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_PlaceCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *winPtr;
+ Slave *slavePtr;
+ Tcl_HashEntry *hPtr;
+ int length;
+ char c;
+
+ /*
+ * Initialize, if that hasn't been done yet.
+ */
+
+ if (!initialized) {
+ Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
+ Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
+ initialized = 1;
+ }
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option|pathName args", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ /*
+ * Handle special shortcut where window name is first argument.
+ */
+
+ if (c == '.') {
+ winPtr = Ck_NameToWindow(interp, argv[1], (CkWindow *) clientData);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = FindSlave(winPtr);
+ return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
+ }
+
+ /*
+ * Handle more general case of option followed by window name followed
+ * by possible additional arguments.
+ */
+
+ winPtr = Ck_NameToWindow(interp, argv[2], (CkWindow *) clientData);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argc < 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0],
+ " configure pathName option value ?option value ...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr = FindSlave(winPtr);
+ return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
+ } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " forget pathName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Tcl_FindHashEntry(&slaveTable, (char *) winPtr);
+ if (hPtr == NULL) {
+ return TCL_OK;
+ }
+ slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
+ UnlinkSlave(slavePtr);
+ Tcl_DeleteHashEntry(hPtr);
+ Ck_DeleteEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ SlaveStructureProc, (ClientData) slavePtr);
+ Ck_ManageGeometry(winPtr, (Ck_GeomMgr *) NULL, (ClientData) NULL);
+ Ck_UnmapWindow(winPtr);
+ ckfree((char *) slavePtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
+ char buffer[50];
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " info pathName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Tcl_FindHashEntry(&slaveTable, (char *) winPtr);
+ if (hPtr == NULL) {
+ return TCL_OK;
+ }
+ slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
+ if (slavePtr->flags & CHILD_REL_X) {
+ sprintf(buffer, "-relx %.4g", slavePtr->relX);
+ } else {
+ sprintf(buffer, "-x %d", slavePtr->x);
+ }
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ if (slavePtr->flags & CHILD_REL_Y) {
+ sprintf(buffer, " -rely %.4g", slavePtr->relY);
+ } else {
+ sprintf(buffer, " -y %d", slavePtr->y);
+ }
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ if (slavePtr->flags & CHILD_REL_WIDTH) {
+ sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ } else if (slavePtr->flags & CHILD_WIDTH) {
+ sprintf(buffer, " -width %d", slavePtr->width);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ }
+ if (slavePtr->flags & CHILD_REL_HEIGHT) {
+ sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ } else if (slavePtr->flags & CHILD_HEIGHT) {
+ sprintf(buffer, " -height %d", slavePtr->height);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ }
+ Tcl_AppendResult(interp, " -anchor ", Ck_NameOfAnchor(slavePtr->anchor),
+ (char *) NULL);
+ if (slavePtr->borderMode == BM_IGNORE) {
+ Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " slaves pathName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Tcl_FindHashEntry(&masterTable, (char *) winPtr);
+ if (hPtr != NULL) {
+ Master *masterPtr;
+ masterPtr = (Master *) Tcl_GetHashValue(hPtr);
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
+ "\": must be configure, forget, info, or slaves",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindSlave --
+ *
+ * Given a CkWindow *, find the Slave structure corresponding
+ * to that window (making a new one if necessary).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new Slave structure may be created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Slave *
+FindSlave(winPtr)
+ CkWindow *winPtr; /* Pointer to desired slave. */
+{
+ Tcl_HashEntry *hPtr;
+ Slave *slavePtr;
+ int new;
+
+ hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) winPtr, &new);
+ if (new) {
+ slavePtr = (Slave *) ckalloc(sizeof (Slave));
+ slavePtr->winPtr = winPtr;
+ slavePtr->masterPtr = NULL;
+ slavePtr->nextPtr = NULL;
+ slavePtr->x = slavePtr->y = 0;
+ slavePtr->relX = slavePtr->relY = 0.0;
+ slavePtr->width = slavePtr->height = 0;
+ slavePtr->relWidth = slavePtr->relHeight = 0.0;
+ slavePtr->anchor = CK_ANCHOR_NW;
+ slavePtr->borderMode = BM_INSIDE;
+ slavePtr->flags = 0;
+ Tcl_SetHashValue(hPtr, slavePtr);
+ Ck_CreateEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ SlaveStructureProc, (ClientData) slavePtr);
+ Ck_ManageGeometry(winPtr, &placerType, (ClientData) slavePtr);
+ } else {
+ slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
+ }
+ return slavePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkSlave --
+ *
+ * This procedure removes a slave window from the chain of slaves
+ * in its master.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The slave list of slavePtr's master changes.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UnlinkSlave(slavePtr)
+ Slave *slavePtr; /* Slave structure to be unlinked. */
+{
+ register Master *masterPtr;
+ register Slave *prevPtr;
+
+ masterPtr = slavePtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+ if (masterPtr->slavePtr == slavePtr) {
+ masterPtr->slavePtr = slavePtr->nextPtr;
+ } else {
+ for (prevPtr = masterPtr->slavePtr; ;
+ prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ panic("UnlinkSlave couldn't find slave to unlink");
+ }
+ if (prevPtr->nextPtr == slavePtr) {
+ prevPtr->nextPtr = slavePtr->nextPtr;
+ break;
+ }
+ }
+ }
+ slavePtr->masterPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindMaster --
+ *
+ * Given a CkWindow *, find the Master structure corresponding
+ * to that window (making a new one if necessary).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new Master structure may be created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Master *
+FindMaster(winPtr)
+ CkWindow *winPtr; /* Pointer to desired master. */
+{
+ Tcl_HashEntry *hPtr;
+ Master *masterPtr;
+ int new;
+
+ hPtr = Tcl_CreateHashEntry(&masterTable, (char *) winPtr, &new);
+ if (new) {
+ masterPtr = (Master *) ckalloc(sizeof (Master));
+ masterPtr->winPtr = winPtr;
+ masterPtr->slavePtr = NULL;
+ masterPtr->flags = 0;
+ Tcl_SetHashValue(hPtr, masterPtr);
+ /*
+ * Special case: for toplevels winPtr is NULL,
+ * therefore don't create event handler.
+ */
+ if (winPtr != NULL)
+ Ck_CreateEventHandler(masterPtr->winPtr,
+ CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ MasterStructureProc, (ClientData) masterPtr);
+ } else {
+ masterPtr = (Master *) Tcl_GetHashValue(hPtr);
+ }
+ return masterPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureSlave --
+ *
+ * This procedure is called to process an argv/argc list to
+ * reconfigure the placement of a window.
+ *
+ * Results:
+ * A standard Tcl result. If an error occurs then a message is
+ * left in interp->result.
+ *
+ * Side effects:
+ * Information in slavePtr may change, and slavePtr's master is
+ * scheduled for reconfiguration.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlave(interp, slavePtr, argc, argv)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Slave *slavePtr; /* Pointer to current information
+ * about slave. */
+ int argc; /* Number of config arguments. */
+ char **argv; /* String values for arguments. */
+{
+ Master *masterPtr;
+ int c, length, result;
+ double d;
+
+ result = TCL_OK;
+ for ( ; argc > 0; argc -= 2, argv += 2) {
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "extra option \"", argv[0],
+ "\" (option with no value?)", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ length = strlen(argv[0]);
+ c = argv[0][1];
+ if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
+ if (Ck_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else if ((c == 'b')
+ && (strncmp(argv[0], "-bordermode", length) == 0)) {
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
+ && (length >= 2)) {
+ slavePtr->borderMode = BM_IGNORE;
+ } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
+ && (length >= 2)) {
+ slavePtr->borderMode = BM_INSIDE;
+ } else {
+ Tcl_AppendResult(interp, "bad border mode \"", argv[1],
+ "\": must be ignore or inside",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
+ if (argv[1][0] == 0) {
+ slavePtr->flags &= ~(CHILD_REL_HEIGHT|CHILD_HEIGHT);
+ } else {
+ if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
+ &slavePtr->height) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->flags &= ~CHILD_REL_HEIGHT;
+ slavePtr->flags |= CHILD_HEIGHT;
+ }
+ } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
+ && (length >= 5)) {
+ if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->relHeight = d;
+ slavePtr->flags |= CHILD_REL_HEIGHT;
+ slavePtr->flags &= ~CHILD_HEIGHT;
+ } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
+ && (length >= 5)) {
+ if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->relWidth = d;
+ slavePtr->flags |= CHILD_REL_WIDTH;
+ slavePtr->flags &= ~CHILD_WIDTH;
+ } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
+ && (length >= 5)) {
+ if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->relX = d;
+ slavePtr->flags |= CHILD_REL_X;
+ } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
+ && (length >= 5)) {
+ if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->relY = d;
+ slavePtr->flags |= CHILD_REL_Y;
+ } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
+ if (argv[1][0] == 0) {
+ slavePtr->flags &= ~(CHILD_REL_WIDTH|CHILD_WIDTH);
+ } else {
+ if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
+ &slavePtr->width) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->flags &= ~CHILD_REL_WIDTH;
+ slavePtr->flags |= CHILD_WIDTH;
+ }
+ } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
+ if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
+ &slavePtr->x) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->flags &= ~CHILD_REL_X;
+ } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
+ if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
+ &slavePtr->y) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ slavePtr->flags &= ~CHILD_REL_Y;
+ } else {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+ argv[0], "\": must be -anchor, -bordermode, -height, ",
+ "-relheight, -relwidth, -relx, -rely, -width, ",
+ "-x, or -y", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ }
+
+ /*
+ * Arrange for a placement recalculation in the master.
+ */
+
+done:
+ masterPtr = slavePtr->masterPtr;
+ if (masterPtr == NULL) {
+ masterPtr = FindMaster((slavePtr->winPtr->flags & CK_TOPLEVEL) ?
+ NULL : slavePtr->winPtr->parentPtr);
+ slavePtr->masterPtr = masterPtr;
+ slavePtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = slavePtr;
+ }
+ if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
+ masterPtr->flags |= PARENT_RECONFIG_PENDING;
+ Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
+ }
+ return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecomputePlacement --
+ *
+ * This procedure is called as a when-idle handler. It recomputes
+ * the geometries of all the slaves of a given master.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Windows may change size or shape.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RecomputePlacement(clientData)
+ ClientData clientData; /* Pointer to Master record. */
+{
+ Master *masterPtr = (Master *) clientData;
+ Slave *slavePtr;
+ CkWindow *ancestor, *realMaster;
+ int x, y, width, height;
+ int masterWidth, masterHeight, masterBW;
+
+ masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
+
+ /*
+ * Iterate over all the slaves for the master. Each slave's
+ * geometry can be computed independently of the other slaves.
+ */
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ /*
+ * Step 1: compute size and borderwidth of master, taking into
+ * account desired border mode.
+ */
+
+ masterBW = 0;
+
+ /*
+ * Special case: masterPtr->winPtr == NULL, use entire screen !
+ */
+
+ if (masterPtr->winPtr == NULL) {
+ masterWidth = slavePtr->winPtr->mainPtr->maxWidth;
+ masterHeight = slavePtr->winPtr->mainPtr->maxHeight;
+ } else {
+ masterWidth = masterPtr->winPtr->width;
+ masterHeight = masterPtr->winPtr->height;
+ if (slavePtr->borderMode == BM_INSIDE) {
+ masterBW = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
+ }
+ masterWidth -= 2*masterBW;
+ masterHeight -= 2*masterBW;
+ }
+
+ /*
+ * Step 2: compute size of slave (outside dimensions including
+ * border) and location of anchor point within master.
+ */
+
+ x = slavePtr->x;
+ if (slavePtr->flags & CHILD_REL_X) {
+ x = (int) ((slavePtr->relX*masterWidth) +
+ ((slavePtr->relX > 0) ? 0.5 : -0.5));
+ }
+ x += masterBW;
+ y = slavePtr->y;
+ if (slavePtr->flags & CHILD_REL_Y) {
+ y = (int) ((slavePtr->relY*masterHeight) +
+ ((slavePtr->relY > 0) ? 0.5 : -0.5));
+ }
+ y += masterBW;
+ if (slavePtr->flags & CHILD_REL_WIDTH) {
+ width = (int) ((slavePtr->relWidth*masterWidth) + 0.5);
+ } else if (slavePtr->flags & CHILD_WIDTH) {
+ width = slavePtr->width;
+ } else {
+ width = slavePtr->winPtr->reqWidth;
+ }
+ if (slavePtr->flags & CHILD_REL_HEIGHT) {
+ height = (int) ((slavePtr->relHeight*masterHeight) + 0.5);
+ } else if (slavePtr->flags & CHILD_HEIGHT) {
+ height = slavePtr->height;
+ } else {
+ height = slavePtr->winPtr->reqHeight;
+ }
+
+ /*
+ * Step 3: adjust the x and y positions so that the desired
+ * anchor point on the slave appears at that position. Also
+ * adjust for the border mode and master's border.
+ */
+
+ switch (slavePtr->anchor) {
+ case CK_ANCHOR_N:
+ x -= width/2;
+ break;
+ case CK_ANCHOR_NE:
+ x -= width;
+ break;
+ case CK_ANCHOR_E:
+ x -= width;
+ y -= height/2;
+ break;
+ case CK_ANCHOR_SE:
+ x -= width;
+ y -= height;
+ break;
+ case CK_ANCHOR_S:
+ x -= width/2;
+ y -= height;
+ break;
+ case CK_ANCHOR_SW:
+ y -= height;
+ break;
+ case CK_ANCHOR_W:
+ y -= height/2;
+ break;
+ case CK_ANCHOR_NW:
+ break;
+ case CK_ANCHOR_CENTER:
+ x -= width/2;
+ y -= height/2;
+ break;
+ }
+
+ /*
+ * Step 4: if masterPtr isn't actually the master of slavePtr,
+ * then translate the x and y coordinates back into the coordinate
+ * system of masterPtr.
+ */
+
+ for (ancestor = masterPtr->winPtr,
+ realMaster = slavePtr->winPtr->parentPtr;
+ ancestor != NULL && ancestor != realMaster;
+ ancestor = ancestor->parentPtr) {
+ x += ancestor->x;
+ y += ancestor->y;
+ }
+
+ /*
+ * Step 5: adjust width and height again to reflect inside dimensions
+ * of window rather than outside. Also make sure that the width and
+ * height aren't zero.
+ */
+
+ if (width <= 0) {
+ width = 1;
+ }
+ if (height <= 0) {
+ height = 1;
+ }
+
+ /*
+ * Step 6: see if the window's size or location has changed; if
+ * so then resize and/or move it.
+ */
+
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+ if (x != slavePtr->winPtr->x ||
+ y != slavePtr->winPtr->y)
+ Ck_MoveWindow(slavePtr->winPtr, x, y);
+ /*
+ * Temporary kludge til Ck_MoveResizeWindow available !!!
+ */
+ if (width != slavePtr->winPtr->width ||
+ height != slavePtr->winPtr->height)
+ Ck_ResizeWindow(slavePtr->winPtr, width, height);
+
+ Ck_MapWindow(slavePtr->winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MasterStructureProc --
+ *
+ * This procedure is invoked by the event handler when
+ * CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events occur for
+ * a master window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Structures get cleaned up if the window was deleted. If the
+ * window was resized then slave geometries get recomputed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MasterStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to Master structure for window
+ * referred to by eventPtr. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ Master *masterPtr = (Master *) clientData;
+ Slave *slavePtr, *nextPtr;
+
+ if (eventPtr->type == CK_EV_EXPOSE ||
+ eventPtr->type == CK_EV_MAP) {
+ if ((masterPtr->slavePtr != NULL)
+ && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
+ masterPtr->flags |= PARENT_RECONFIG_PENDING;
+ Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
+ }
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = nextPtr) {
+ slavePtr->masterPtr = NULL;
+ nextPtr = slavePtr->nextPtr;
+ slavePtr->nextPtr = NULL;
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
+ (char *) masterPtr->winPtr));
+ if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
+ Tk_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
+ }
+ masterPtr->winPtr = NULL;
+ ckfree((char *) masterPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SlaveStructureProc --
+ *
+ * This procedure is invoked by the event handler when
+ * CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events occur for a
+ * slave window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Structures get cleaned up if the window was deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SlaveStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to Slave structure for window
+ * referred to by eventPtr. */
+ CkEvent *eventPtr; /* Describes what just happened. */
+{
+ Slave *slavePtr = (Slave *) clientData;
+
+ if (eventPtr->type == CK_EV_DESTROY) {
+ UnlinkSlave(slavePtr);
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
+ (char *) slavePtr->winPtr));
+ ckfree((char *) slavePtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * PlaceRequestProc --
+ *
+ * This procedure is invoked whenever a slave managed by us
+ * changes its requested geometry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window will get relayed out, if its requested size has
+ * anything to do with its actual size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PlaceRequestProc(clientData, winPtr)
+ ClientData clientData; /* Pointer to our record for slave. */
+ CkWindow *winPtr; /* Window that changed its desired
+ * size. */
+{
+ Slave *slavePtr = (Slave *) clientData;
+ Master *masterPtr;
+
+ if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
+ && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
+ return;
+ }
+ masterPtr = slavePtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+ if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
+ masterPtr->flags |= PARENT_RECONFIG_PENDING;
+ Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * PlaceLostSlaveProc --
+ *
+ * This procedure is invoked whenever some other geometry
+ * claims control over a slave that used to be managed by us.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets all placer-related information about the slave.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+PlaceLostSlaveProc(clientData, winPtr)
+ ClientData clientData; /* Slave structure for slave window that
+ * was stolen away. */
+ CkWindow *winPtr; /* Slave window. */
+{
+ register Slave *slavePtr = (Slave *) clientData;
+
+ if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
+ Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
+ }
+ Ck_UnmapWindow(winPtr);
+ UnlinkSlave(slavePtr);
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable, (char *) winPtr));
+ Ck_DeleteEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
+ SlaveStructureProc, (ClientData) slavePtr);
+ ckfree((char *) slavePtr);
+}
--- /dev/null
+/*
+ * ckPort.h --
+ *
+ * This file is included by all of the curses wish C files.
+ * It contains information that may be configuration-dependent,
+ * such as #includes for system include files and a few other things.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef _CKPORT
+#define _CKPORT
+
+#if defined(_WIN32) || defined(WIN32)
+# include <windows.h>
+#endif
+
+/*
+ * Macro to use instead of "void" for arguments that must have
+ * type "void *" in ANSI C; maps them to type "char *" in
+ * non-ANSI systems. This macro may be used in some of the include
+ * files below, which is why it is defined here.
+ */
+
+#ifndef VOID
+# ifdef __STDC__
+# define VOID void
+# else
+# define VOID char
+# endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#if defined(_WIN32) || defined(WIN32)
+# include <limits.h>
+#else
+# ifdef HAVE_LIMITS_H
+# include <limits.h>
+# else
+# include "compat/limits.h"
+# endif
+#endif
+#include <math.h>
+#if !defined(_WIN32) && !defined(WIN32)
+# include <pwd.h>
+#endif
+#ifdef NO_STDLIB_H
+# include "compat/stdlib.h"
+#else
+# include <stdlib.h>
+#endif
+#include <string.h>
+#include <sys/types.h>
+#if !defined(_WIN32) && !defined(WIN32)
+# include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#include <sys/stat.h>
+#if !defined(_WIN32) && !defined(WIN32)
+# include <sys/time.h>
+#endif
+#ifndef _TCL
+# include <tcl.h>
+#endif
+#if !defined(_WIN32) && !defined(WIN32)
+# ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# else
+# include "compat/unistd.h"
+# endif
+#endif
+
+#if (TCL_MAJOR_VERSION < 7)
+#error Tcl major version must be 7 or greater
+#endif
+
+/*
+ * Not all systems declare the errno variable in errno.h. so this
+ * file does it explicitly.
+ */
+
+#if !defined(_WIN32) && !defined(WIN32)
+extern int errno;
+#endif
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+
+/*
+ * The following macro defines the type of the mask arguments to
+ * select:
+ */
+
+#ifndef NO_FD_SET
+# define SELECT_MASK fd_set
+#else
+# ifndef _AIX
+ typedef long fd_mask;
+# endif
+# if defined(_IBMR2)
+# define SELECT_MASK void
+# else
+# define SELECT_MASK int
+# endif
+#endif
+
+/*
+ * Define "NBBY" (number of bits per byte) if it's not already defined.
+ */
+
+#ifndef NBBY
+# define NBBY 8
+#endif
+
+/*
+ * The following macro defines the number of fd_masks in an fd_set:
+ */
+
+#ifndef FD_SETSIZE
+# ifdef OPEN_MAX
+# define FD_SETSIZE OPEN_MAX
+# else
+# define FD_SETSIZE 256
+# endif
+#endif
+#if !defined(howmany)
+# define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+#ifndef NFDBITS
+# define NFDBITS NBBY*sizeof(fd_mask)
+#endif
+#define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
+
+/*
+ * The following macro checks to see whether there is buffered
+ * input data available for a stdio FILE. This has to be done
+ * in different ways on different systems. TK_FILE_GPTR and
+ * TK_FILE_COUNT are #defined by autoconf.
+ */
+
+#ifdef TK_FILE_COUNT
+# define TK_READ_DATA_PENDING(f) ((f)->TK_FILE_COUNT > 0)
+#else
+# ifdef TK_FILE_GPTR
+# define TK_READ_DATA_PENDING(f) ((f)->_gptr < (f)->_egptr)
+# else
+# ifdef TK_FILE_READ_PTR
+# define TK_READ_DATA_PENDING(f) ((f)->_IO_read_ptr != (f)->_IO_read_end)
+# else
+ /*
+ * Don't know what to do for this system; whoever installs
+ * Tk will have to write a function TkReadDataPending to do
+ * the job.
+ */
+ EXTERN int TkReadDataPending _ANSI_ARGS_((FILE *f));
+# define TK_READ_DATA_PENDING(f) TkReadDataPending(f)
+# endif
+# endif
+#endif
+
+/*
+ * Substitute Tcl's own versions for several system calls. The
+ * Tcl versions retry automatically if interrupted by signals.
+ */
+
+#define open(a,b,c) TclOpen(a,b,c)
+#define read(a,b,c) TclRead(a,b,c)
+#define waitpid(a,b,c) TclWaitpid(a,b,c)
+#define write(a,b,c) TclWrite(a,b,c)
+EXTERN int TclOpen _ANSI_ARGS_((char *path, int oflag, mode_t mode));
+EXTERN int TclRead _ANSI_ARGS_((int fd, VOID *buf,
+ unsigned int numBytes));
+EXTERN int TclWaitpid _ANSI_ARGS_((pid_t pid, int *statPtr, int options));
+EXTERN int TclWrite _ANSI_ARGS_((int fd, VOID *buf,
+ unsigned int numBytes));
+
+/*
+ * If this system has a BSDgettimeofday function (e.g. IRIX) use it
+ * instead of gettimeofday; the gettimeofday function has a different
+ * interface than the BSD one that this code expects.
+ */
+
+#ifdef HAVE_BSDGETTIMEOFDAY
+# define gettimeofday BSDgettimeofday
+#endif
+#ifdef GETTOD_NOT_DECLARED
+EXTERN int gettimeofday _ANSI_ARGS_((struct timeval *tp,
+ struct timezone *tzp));
+#endif
+
+#else
+
+/*
+ * Provide some defines to get some Tk functionality which was in
+ * tkEvent.c prior to Tcl version 7.5.
+ */
+
+#define Tk_BackgroundError Tcl_BackgroundError
+#define Tk_DoWhenIdle Tcl_DoWhenIdle
+#define Tk_DoWhenIdle2 Tcl_DoWhenIdle
+#define Tk_CancelIdleCall Tcl_CancelIdleCall
+#define Tk_CreateTimerHandler Tcl_CreateTimerHandler
+#define Tk_DeleteTimerHandler Tcl_DeleteTimerHandler
+#define Tk_AfterCmd Tcl_AfterCmd
+#define Tk_FileeventCmd Tcl_FileEventCmd
+#define Tk_DoOneEvent Tcl_DoOneEvent
+
+#endif
+
+/*
+ * Declarations for various library procedures that may not be declared
+ * in any other header file.
+ */
+
+#ifndef panic
+extern void panic();
+#endif
+
+ /* Return type for signal(), this taken from TclX.
+ */
+
+#ifndef RETSIGTYPE
+# define RETSIGTYPE void
+#endif
+
+typedef RETSIGTYPE (*Ck_SignalProc) _ANSI_ARGS_((int));
+
+
+#endif /* _CKPORT */
--- /dev/null
+/*
+ * ckPreserve.c --
+ *
+ * This file contains a collection of procedures that are used
+ * to make sure that widget records and other data structures
+ * aren't reallocated when there are nested procedures that
+ * depend on their existence.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+
+/*
+ * The following data structure is used to keep track of all the
+ * Ck_Preserve calls that are still in effect. It grows as needed
+ * to accommodate any number of calls in effect.
+ */
+
+typedef struct {
+ ClientData clientData; /* Address of preserved block. */
+ int refCount; /* Number of Ck_Preserve calls in effect
+ * for block. */
+ int mustFree; /* Non-zero means Ck_EventuallyFree was
+ * called while a Ck_Preserve call was in
+ * effect, so the structure must be freed
+ * when refCount becomes zero. */
+ Ck_FreeProc *freeProc; /* Procedure to call to free. */
+} Reference;
+
+static Reference *refArray; /* First in array of references. */
+static int spaceAvl = 0; /* Total number of structures available
+ * at *firstRefPtr. */
+static int inUse = 0; /* Count of structures currently in use
+ * in refArray. */
+#define INITIAL_SIZE 2
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_Preserve --
+ *
+ * This procedure is used by a procedure to declare its interest
+ * in a particular block of memory, so that the block will not be
+ * reallocated until a matching call to Ck_Release has been made.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information is retained so that the block of memory will
+ * not be freed until at least the matching call to Ck_Release.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_Preserve(clientData)
+ ClientData clientData; /* Pointer to malloc'ed block of memory. */
+{
+ register Reference *refPtr;
+ int i;
+
+ /*
+ * See if there is already a reference for this pointer. If so,
+ * just increment its reference count.
+ */
+
+ for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
+ if (refPtr->clientData == clientData) {
+ refPtr->refCount++;
+ return;
+ }
+ }
+
+ /*
+ * Make a reference array if it doesn't already exist, or make it
+ * bigger if it is full.
+ */
+
+ if (inUse == spaceAvl) {
+ if (spaceAvl == 0) {
+ refArray = (Reference *) ckalloc((unsigned)
+ (INITIAL_SIZE*sizeof(Reference)));
+ spaceAvl = INITIAL_SIZE;
+ } else {
+ Reference *new;
+
+ new = (Reference *) ckalloc((unsigned)
+ (2*spaceAvl*sizeof(Reference)));
+ memcpy((VOID *) new, (VOID *) refArray, spaceAvl*sizeof(Reference));
+ ckfree((char *) refArray);
+ refArray = new;
+ spaceAvl *= 2;
+ }
+ }
+
+ /*
+ * Make a new entry for the new reference.
+ */
+
+ refPtr = &refArray[inUse];
+ refPtr->clientData = clientData;
+ refPtr->refCount = 1;
+ refPtr->mustFree = 0;
+ inUse += 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_Release --
+ *
+ * This procedure is called to cancel a previous call to
+ * Ck_Preserve, thereby allowing a block of memory to be
+ * freed (if no one else cares about it).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If Ck_EventuallyFree has been called for clientData, and if
+ * no other call to Ck_Preserve is still in effect, the block of
+ * memory is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_Release(clientData)
+ ClientData clientData; /* Pointer to malloc'ed block of memory. */
+{
+ register Reference *refPtr;
+ int i;
+
+ for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
+ if (refPtr->clientData != clientData) {
+ continue;
+ }
+ refPtr->refCount--;
+ if (refPtr->refCount == 0) {
+ if (refPtr->mustFree) {
+ if (refPtr->freeProc == (Ck_FreeProc *) free) {
+ ckfree((char *) refPtr->clientData);
+ } else {
+ (*refPtr->freeProc)(refPtr->clientData);
+ }
+ }
+
+ /*
+ * Copy down the last reference in the array to fill the
+ * hole left by the unused reference.
+ */
+
+ inUse--;
+ if (i < inUse) {
+ refArray[i] = refArray[inUse];
+ }
+ }
+ return;
+ }
+
+ /*
+ * Reference not found. This is a bug in the caller.
+ */
+
+ panic("Ck_Release couldn't find reference for 0x%x", clientData);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_EventuallyFree --
+ *
+ * Free up a block of memory, unless a call to Ck_Preserve is in
+ * effect for that block. In this case, defer the free until all
+ * calls to Ck_Preserve have been undone by matching calls to
+ * Ck_Release.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Ptr may be released by calling free().
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_EventuallyFree(clientData, freeProc)
+ ClientData clientData; /* Pointer to malloc'ed block of memory. */
+ Ck_FreeProc *freeProc; /* Procedure to actually do free. */
+{
+ register Reference *refPtr;
+ int i;
+
+ /*
+ * See if there is a reference for this pointer. If so, set its
+ * "mustFree" flag (the flag had better not be set already!).
+ */
+
+ for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
+ if (refPtr->clientData != clientData) {
+ continue;
+ }
+ if (refPtr->mustFree) {
+ panic("Ck_EventuallyFree called twice for 0x%x\n", clientData);
+ }
+ refPtr->mustFree = 1;
+ refPtr->freeProc = freeProc;
+ return;
+ }
+
+ /*
+ * No reference for this block. Free it now.
+ */
+
+ if (freeProc == (Ck_FreeProc *) free) {
+ ckfree((char *) clientData);
+ } else {
+ (*freeProc)(clientData);
+ }
+}
+
+#endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
--- /dev/null
+/*
+ * ckRecorder.c --
+ *
+ * This file provides a simple event recorder.
+ *
+ * Copyright (c) 1996-1999 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+/*
+ * There is one structure of the following type for the global data
+ * of the recorder.
+ */
+
+typedef struct {
+ CkWindow *mainPtr;
+ Tcl_Interp *interp;
+ int timerRunning;
+ Tk_TimerToken timer;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ struct timeval lastEvent;
+ FILE *record;
+ FILE *replay;
+#else
+ Tcl_Time lastEvent;
+ Tcl_Channel record;
+ Tcl_Channel replay;
+#endif
+ int withDelay;
+ CkEvent event;
+} Recorder;
+
+static Recorder *ckRecorder = NULL;
+
+/*
+ * Internal procedures.
+ */
+
+static int RecorderInput _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+static int DStringGets _ANSI_ARGS_((FILE *filePtr, Tcl_DString *dsPtr));
+#else
+static int DStringGets _ANSI_ARGS_((Tcl_Channel chan,
+ Tcl_DString *dsPtr));
+#endif
+static void DeliverEvent _ANSI_ARGS_((ClientData clientData));
+static void RecorderReplay _ANSI_ARGS_((ClientData clientData));
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecorderInput --
+ *
+ * This procedure is installed as generic event handler.
+ * For certain events it adds lines to the recorder file.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+RecorderInput(clientData, eventPtr)
+ ClientData clientData;
+ CkEvent *eventPtr;
+{
+ Recorder *recPtr = (Recorder *) clientData;
+ int hadEvent = 0, type = eventPtr->any.type;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ struct timeval now;
+#else
+ Tcl_Time now;
+ extern void TclpGetTime _ANSI_ARGS_((Tcl_Time *timePtr));
+#endif
+ char buffer[64];
+ char *keySym, *barCode, *result;
+ char *argv[16];
+
+ if (recPtr->record == NULL) {
+ Ck_DeleteGenericHandler(RecorderInput, clientData);
+ return 0;
+ }
+
+ if (type != CK_EV_KEYPRESS && type != CK_EV_BARCODE &&
+ type != CK_EV_MOUSE_UP && type != CK_EV_MOUSE_DOWN)
+ return 0;
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ gettimeofday(&now, (struct timezone *) NULL);
+ if (recPtr->withDelay && recPtr->lastEvent.tv_sec != 0 &&
+ recPtr->lastEvent.tv_usec != 0) {
+ double diff;
+
+ diff = now.tv_sec * 1000 + now.tv_usec / 1000;
+ diff -= recPtr->lastEvent.tv_sec * 1000 +
+ recPtr->lastEvent.tv_usec / 1000;
+ if (diff > 50) {
+ if (diff > 3600000)
+ diff = 3600000;
+ fprintf(recPtr->record, "<Delay> %d\n", (int) diff);
+ hadEvent++;
+ }
+ }
+#else
+ TclpGetTime(&now);
+ if (recPtr->withDelay && recPtr->lastEvent.sec != 0 &&
+ recPtr->lastEvent.usec != 0) {
+ double diff;
+ char string[100];
+
+ diff = now.sec * 1000 + now.usec / 1000;
+ diff -= recPtr->lastEvent.sec * 1000 +
+ recPtr->lastEvent.usec / 1000;
+ if (diff > 50) {
+ if (diff > 3600000)
+ diff = 3600000;
+ sprintf(string, "<Delay> %d\n", (int) diff);
+ Tcl_Write(recPtr->record, string, strlen(string));
+ hadEvent++;
+ }
+ }
+#endif
+
+ switch (type) {
+ case CK_EV_KEYPRESS:
+ argv[2] = NULL;
+ keySym = CkKeysymToString(eventPtr->key.keycode, 1);
+ if (strcmp(keySym, "NoSymbol") != 0)
+ argv[2] = keySym;
+ else if (eventPtr->key.keycode > 0 &&
+ eventPtr->key.keycode < 256) {
+ /* Unsafe, ie not portable */
+ sprintf(buffer, "0x%2x", eventPtr->key.keycode);
+ argv[2] = buffer;
+ }
+ if (argv[2] != NULL) {
+ argv[0] = "<Key>";
+ argv[1] = eventPtr->key.winPtr == NULL ? "" :
+ eventPtr->key.winPtr->pathName;
+ result = Tcl_Merge(3, argv);
+printPctSNL:
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fprintf(recPtr->record, "%s\n", result);
+#else
+ Tcl_Write(recPtr->record, result, strlen(result));
+ Tcl_Write(recPtr->record, "\n", 1);
+#endif
+ ckfree(result);
+ hadEvent++;
+ }
+ break;
+
+ case CK_EV_BARCODE:
+ barCode = CkGetBarcodeData(recPtr->mainPtr->mainPtr);
+ if (barCode != NULL) {
+ argv[0] = "<BarCode>";
+ argv[1] = eventPtr->key.winPtr == NULL ? "" :
+ eventPtr->key.winPtr->pathName;
+ argv[2] = barCode;
+ result = Tcl_Merge(3, argv);
+ goto printPctSNL;
+ }
+ break;
+
+ case CK_EV_MOUSE_UP:
+ case CK_EV_MOUSE_DOWN:
+ {
+ char bbuf[16], xbuf[16], ybuf[16], rxbuf[16], rybuf[16];
+
+ argv[0] = type == CK_EV_MOUSE_DOWN ?
+ "<ButtonPress>" : "<ButtonRelease>";
+ argv[1] = eventPtr->mouse.winPtr == NULL ? "" :
+ eventPtr->mouse.winPtr->pathName;
+ sprintf(bbuf, "%d", eventPtr->mouse.button);
+ argv[2] = bbuf;
+ sprintf(xbuf, "%d", eventPtr->mouse.x);
+ argv[3] = xbuf;
+ sprintf(ybuf, "%d", eventPtr->mouse.y);
+ argv[4] = ybuf;
+ sprintf(rxbuf, "%d", eventPtr->mouse.rootx);
+ argv[5] = rxbuf;
+ sprintf(rybuf, "%d", eventPtr->mouse.rooty);
+ argv[6] = rybuf;
+ result = Tcl_Merge(7, argv);
+ goto printPctSNL;
+ }
+ break;
+ }
+
+ if (hadEvent) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fflush(recPtr->record);
+#else
+ Tcl_Flush(recPtr->record);
+#endif
+ recPtr->lastEvent = now;
+ }
+
+ return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DStringGets --
+ *
+ * Similar to the fgets library routine, a dynamic string is
+ * read from a file. Can deal with backslash-newline continuation.
+ * lines.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+static int
+DStringGets(filePtr, dsPtr)
+ FILE *filePtr;
+ Tcl_DString *dsPtr;
+{
+ int count, c, p = EOF;
+ char buf;
+
+ for (count = 0;;) {
+ c = getc(filePtr);
+ if (c == EOF)
+ return count ? TCL_OK : TCL_ERROR;
+ else if (c == '\n') {
+ if (p == '\\')
+ c = ' ';
+ else
+ return TCL_OK;
+ }
+ buf = c;
+ Tcl_DStringAppend(dsPtr, &buf, 1);
+ p = c;
+ }
+ /* Not reached. */
+}
+#else
+static int
+DStringGets(chan, dsPtr)
+ Tcl_Channel chan;
+ Tcl_DString *dsPtr;
+{
+ char *p;
+ int length, code;
+
+ for (;;) {
+ code = Tcl_Gets(chan, dsPtr);
+ length = Tcl_DStringLength(dsPtr);
+ if (code == -1)
+ return length == 0 ? TCL_ERROR : TCL_OK;
+ if (length > 0) {
+ p = Tcl_DStringValue(dsPtr) + length - 1;
+ if (*p != '\\')
+ return TCL_OK;
+ *p = ' ';
+ } else {
+ return TCL_OK;
+ }
+ }
+ /* Not reached. */
+}
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeliverEvent --
+ *
+ * Call by do-when-idle mechanism, dispatched by replay handler.
+ * Deliver event, but first reschedule replay handler. This order
+ * is essential !
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeliverEvent(clientData)
+ ClientData clientData;
+{
+ Recorder *recPtr = (Recorder *) clientData;
+
+ Tk_DoWhenIdle(RecorderReplay, (ClientData) recPtr);
+ Ck_HandleEvent(recPtr->mainPtr->mainPtr, &recPtr->event);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecorderReplay --
+ *
+ * Replay handler, called by the do-when-idle mechanism or by a
+ * timer's expiration.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RecorderReplay(clientData)
+ ClientData clientData;
+{
+ Recorder *recPtr = (Recorder *) clientData;
+ Tcl_DString input;
+ char *p;
+ int getsResult, delayValue = 0, doidle = 1;
+
+ recPtr->timerRunning = 0;
+ if (recPtr->replay == NULL)
+ return;
+
+ Tcl_DStringInit(&input);
+ while ((getsResult = DStringGets(recPtr->replay, &input)) == TCL_OK) {
+ p = Tcl_DStringValue(&input);
+ while (*p == ' ' || *p == '\t')
+ ++p;
+ if (*p == '#') {
+ Tcl_DStringTrunc(&input, 0);
+ continue;
+ }
+ if (*p == '<') {
+ CkEvent event;
+ int cmdError = TCL_OK, deliver = 0;
+ int argc;
+ char **argv;
+
+ if (Tcl_SplitList(recPtr->interp, p, &argc, &argv) != TCL_OK) {
+ Tk_BackgroundError(recPtr->interp);
+ getsResult = TCL_ERROR;
+ break;
+ }
+ if (strcmp(argv[0], "<Delay>") == 0) {
+ if (argc != 2) {
+badNumArgs:
+ Tcl_AppendResult(recPtr->interp,
+ "wrong # args for ", argv[0], (char *) NULL);
+ cmdError = TCL_ERROR;
+ } else
+ cmdError = Tcl_GetInt(recPtr->interp, argv[1],
+ &delayValue);
+ } else if (strcmp(argv[0], "<Key>") == 0) {
+ int keySym;
+
+ if (argc != 3)
+ goto badNumArgs;
+ event.any.type = CK_EV_KEYPRESS;
+ if (argv[1][0] == '\0')
+ event.any.winPtr = NULL;
+ else if ((event.any.winPtr = Ck_NameToWindow(recPtr->interp,
+ argv[1], recPtr->mainPtr)) == NULL)
+ cmdError = TCL_ERROR;
+ else if (strncmp(argv[2], "Control-", 8) == 0 &&
+ strlen(argv[2]) == 9) {
+ event.key.keycode = argv[2][8] - 0x40;
+ if (event.key.keycode > 0x20)
+ event.key.keycode -= 0x20;
+ deliver++;
+ } else if (strncmp(argv[2], "0x", 2) == 0 &&
+ strlen(argv[2]) == 4) {
+ sscanf(&argv[2][2], "%x", &event.key.keycode);
+ deliver++;
+ } else if ((keySym = CkStringToKeysym(argv[2])) != NoSymbol) {
+ event.key.keycode = keySym;
+ deliver++;
+ }
+ } else if (strcmp(argv[0], "<BarCode>") == 0) {
+ if (argc != 3)
+ goto badNumArgs;
+
+ } else if (strcmp(argv[0], "<ButtonPress>") == 0) {
+ if (argc != 7)
+ goto badNumArgs;
+ event.any.type = CK_EV_MOUSE_DOWN;
+doMouse:
+ if (argv[1][0] == '\0')
+ event.any.winPtr = NULL;
+ else if ((event.any.winPtr = Ck_NameToWindow(recPtr->interp,
+ argv[1], recPtr->mainPtr)) == NULL)
+ cmdError = TCL_ERROR;
+ else {
+ cmdError |= Tcl_GetInt(recPtr->interp, argv[2],
+ &event.mouse.button);
+ cmdError |= Tcl_GetInt(recPtr->interp, argv[3],
+ &event.mouse.x);
+ cmdError |= Tcl_GetInt(recPtr->interp, argv[4],
+ &event.mouse.y);
+ cmdError |= Tcl_GetInt(recPtr->interp, argv[5],
+ &event.mouse.rootx);
+ cmdError |= Tcl_GetInt(recPtr->interp, argv[6],
+ &event.mouse.rooty);
+ if (cmdError == TCL_OK)
+ deliver++;
+ }
+ } else if (strcmp(argv[0], "<ButtonRelease>") == 0) {
+ if (argc != 7)
+ goto badNumArgs;
+ event.any.type = CK_EV_MOUSE_UP;
+ goto doMouse;
+ }
+ ckfree((char *) argv);
+ if (cmdError != TCL_OK) {
+ Tk_BackgroundError(recPtr->interp);
+ getsResult = cmdError;
+ } else if (deliver) {
+ doidle = delayValue = 0;
+ recPtr->event = event;
+ Tk_DoWhenIdle(DeliverEvent, (ClientData) recPtr);
+ }
+ break;
+ } else if (Tcl_GlobalEval(recPtr->interp, p) != TCL_OK) {
+ Tk_BackgroundError(recPtr->interp);
+ getsResult = TCL_ERROR;
+ break;
+ }
+ Tcl_DStringTrunc(&input, 0);
+ }
+ if (getsResult != TCL_OK) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fclose(recPtr->replay);
+#else
+ Tcl_Close(NULL, recPtr->replay);
+#endif
+ recPtr->replay = NULL;
+ } else if (delayValue != 0) {
+ recPtr->timerRunning = 1;
+ recPtr->timer = Tk_CreateTimerHandler(delayValue, RecorderReplay,
+ (ClientData) recPtr);
+ } else if (doidle != 0) {
+ Tk_DoWhenIdle(RecorderReplay, (ClientData) recPtr);
+ }
+ Tcl_DStringFree(&input);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_RecorderCmd --
+ *
+ * This procedure is invoked to process the "recorder" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_RecorderCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Recorder *recPtr = ckRecorder;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ int length;
+ char c;
+
+ if (recPtr == NULL) {
+ recPtr = (Recorder *) ckalloc(sizeof (Recorder));
+ recPtr->mainPtr = mainPtr;
+ recPtr->interp = NULL;
+ recPtr->timerRunning = 0;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ recPtr->lastEvent.tv_sec = recPtr->lastEvent.tv_usec = 0;
+#else
+ recPtr->lastEvent.sec = recPtr->lastEvent.usec = 0;
+#endif
+ recPtr->record = NULL;
+ recPtr->replay = NULL;
+ recPtr->withDelay = 0;
+ ckRecorder = recPtr;
+ }
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'r') && (strncmp(argv[1], "replay", length) == 0)) {
+ char *fileName;
+ Tcl_DString buffer;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ FILE *newReplay;
+#else
+ Tcl_Channel newReplay;
+#endif
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " replay fileName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fileName = Tcl_TildeSubst(interp, argv[2], &buffer);
+ if (fileName == NULL) {
+replayError:
+ Tcl_DStringFree(&buffer);
+ return TCL_ERROR;
+ }
+ newReplay = fopen(fileName, "r");
+ if (newReplay == NULL) {
+ Tcl_AppendResult(interp, "error opening \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *) NULL);
+ goto replayError;
+ }
+ Tcl_DStringFree(&buffer);
+ DStringGets(newReplay, &buffer);
+ if (strncmp("# CK-RECORDER", Tcl_DStringValue(&buffer), 13) != 0) {
+ fclose(newReplay);
+ Tcl_AppendResult(interp, "invalid file for replay", (char *) NULL);
+ goto replayError;
+ }
+#else
+ fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
+ if (fileName == NULL) {
+replayError:
+ Tcl_DStringFree(&buffer);
+ return TCL_ERROR;
+ }
+ newReplay = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+ if (newReplay == NULL)
+ goto replayError;
+ Tcl_DStringFree(&buffer);
+ Tcl_Gets(newReplay, &buffer);
+ if (strncmp("# CK-RECORDER", Tcl_DStringValue(&buffer), 13) != 0) {
+ Tcl_Close(NULL, newReplay);
+ Tcl_AppendResult(interp, "invalid file for replay", (char *) NULL);
+ goto replayError;
+ }
+#endif
+ if (recPtr->replay != NULL) {
+ if (recPtr->timerRunning)
+ Tk_DeleteTimerHandler(recPtr->timer);
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fclose(recPtr->replay);
+#else
+ Tcl_Close(NULL, recPtr->replay);
+#endif
+ recPtr->timerRunning = 0;
+ }
+ recPtr->replay = newReplay;
+ recPtr->interp = interp;
+ Tk_DoWhenIdle(RecorderReplay, (ClientData) recPtr);
+ } else if ((c == 's') && (strncmp(argv[1], "start", length) == 0) &&
+ (length > 1)) {
+ char *fileName;
+ int withDelay = 0, fileArg = 2;
+ Tcl_DString buffer;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ FILE *newRecord;
+ time_t now;
+#else
+ Tcl_Channel newRecord;
+ char *string;
+#endif
+
+ if (argc < 3 || argc > 4) {
+badStartArgs:
+ Tcl_AppendResult(interp, "wrong # or bad args: should be \"",
+ argv[0], " start ?-withdelay? fileName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ if (strcmp(argv[2], "-withdelay") != 0)
+ goto badStartArgs;
+ withDelay++;
+ fileArg++;
+ }
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fileName = Tcl_TildeSubst(interp, argv[fileArg], &buffer);
+ if (fileName == NULL) {
+startError:
+ Tcl_DStringFree(&buffer);
+ return TCL_ERROR;
+ }
+ newRecord = fopen(fileName, "w");
+ if (newRecord == NULL) {
+ Tcl_AppendResult(interp, "error opening \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *) NULL);
+ goto startError;
+ }
+ if (recPtr->record != NULL)
+ fclose(recPtr->record);
+ else {
+ recPtr->lastEvent.tv_sec = recPtr->lastEvent.tv_usec = 0;
+ Ck_CreateGenericHandler(RecorderInput, recPtr);
+ }
+ recPtr->record = newRecord;
+ recPtr->withDelay = withDelay;
+ time(&now);
+ fprintf(recPtr->record, "# CK-RECORDER\n# %s", ctime(&now));
+ fprintf(recPtr->record, "# %s %s\n",
+ Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY),
+ Tcl_GetVar(interp, "argv", TCL_GLOBAL_ONLY));
+ Tcl_DStringFree(&buffer);
+#else
+ fileName = Tcl_TranslateFileName(interp, argv[fileArg], &buffer);
+ if (fileName == NULL) {
+startError:
+ Tcl_DStringFree(&buffer);
+ return TCL_ERROR;
+ }
+ newRecord = Tcl_OpenFileChannel(interp, fileName, "w", 0666);
+ if (newRecord == NULL)
+ goto startError;
+ if (recPtr->record != NULL)
+ Tcl_Close(NULL, recPtr->record);
+ else {
+ recPtr->lastEvent.sec = recPtr->lastEvent.usec = 0;
+ Ck_CreateGenericHandler(RecorderInput, (ClientData) recPtr);
+ }
+ recPtr->record = newRecord;
+ recPtr->withDelay = withDelay;
+ string = "# CK-RECORDER\n# ";
+ Tcl_Write(recPtr->record, string, strlen(string));
+ Tcl_Eval(interp, "clock format [clock seconds]");
+ Tcl_Write(recPtr->record, interp->result, strlen(interp->result));
+ Tcl_ResetResult(interp);
+ Tcl_Write(recPtr->record, "\n# ", 3);
+ string = Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY);
+ Tcl_Write(recPtr->record, string, strlen(string));
+ Tcl_Write(recPtr->record, " ", 1);
+ string = Tcl_GetVar(interp, "argv", TCL_GLOBAL_ONLY);
+ Tcl_Write(recPtr->record, string, strlen(string));
+ Tcl_Write(recPtr->record, "\n", 1);
+ Tcl_DStringFree(&buffer);
+#endif
+ } else if ((c == 's') && (strncmp(argv[1], "stop", length) == 0) &&
+ (length > 1)) {
+ if (argc > 3) {
+badStopArgs:
+ Tcl_AppendResult(interp, "wrong # or bad args: should be \"",
+ argv[0], " stop ?replay?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (strcmp(argv[2], "replay") != 0)
+ goto badStopArgs;
+ if (recPtr->replay != NULL) {
+ if (recPtr->timerRunning)
+ Tk_DeleteTimerHandler(recPtr->timer);
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fclose(recPtr->replay);
+#else
+ Tcl_Close(NULL, recPtr->replay);
+#endif
+ recPtr->replay = NULL;
+ recPtr->timerRunning = 0;
+ }
+ } else if (recPtr->record != NULL) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ fclose(recPtr->record);
+#else
+ Tcl_Close(NULL, recPtr->record);
+#endif
+ Ck_DeleteGenericHandler(RecorderInput, (ClientData) recPtr);
+ recPtr->record = NULL;
+ }
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " replay, start, or stop\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
--- /dev/null
+/*
+ * ckScrollbar.c --
+ *
+ * This module implements a scrollbar widgets for the
+ * toolkit. A scrollbar displays a slider and two arrows;
+ * mouse clicks on features within the scrollbar cause
+ * scrolling commands to be invoked.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * A data structure of the following type is kept for each scrollbar
+ * widget managed by this file:
+ */
+
+typedef struct {
+ CkWindow *winPtr; /* Window that embodies the scrollbar. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with scrollbar. */
+ Tcl_Command widgetCmd; /* Token for scrollbar's widget command. */
+ Ck_Uid orientUid; /* Orientation for window ("vertical" or
+ * "horizontal"). */
+ int vertical; /* Non-zero means vertical orientation
+ * requested, zero means horizontal. */
+ char *command; /* Command prefix to use when invoking
+ * scrolling commands. NULL means don't
+ * invoke commands. Malloc'ed. */
+ int commandSize; /* Number of non-NULL bytes in command. */
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int normalBg; /* Used for drawing background. */
+ int normalFg; /* Used for drawing foreground. */
+ int normalAttr; /* Video attributes for normal mode. */
+ int activeBg; /* Background in active mode. */
+ int activeFg; /* Foreground in active mode. */
+ int activeAttr; /* Video attributes for active mode. */
+
+ int sliderFirst; /* Coordinate of top or left edge
+ * of slider area. */
+ int sliderLast; /* Coordinate just after bottom
+ * or right edge of slider area. */
+ /*
+ * Information describing the application related to the scrollbar.
+ * This information is provided by the application by invoking the
+ * "set" widget command.
+ */
+
+ double firstFraction; /* Position of first visible thing in window,
+ * specified as a fraction between 0 and
+ * 1.0. */
+ double lastFraction; /* Position of last visible thing in window,
+ * specified as a fraction between 0 and
+ * 1.0. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Scrollbar;
+
+/*
+ * Flag bits for scrollbars:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * ACTIVATED: 1 means draw in activated mode,
+ * 0 means draw in normal mode
+ */
+
+#define REDRAW_PENDING 1
+#define ACTIVATED 2
+
+/*
+ * Legal values for identifying position in scrollbar. These
+ * are the return values from the ScrollbarPosition procedure.
+ */
+
+#define OUTSIDE 0
+#define TOP_ARROW 1
+#define TOP_GAP 2
+#define SLIDER 3
+#define BOTTOM_GAP 4
+#define BOTTOM_ARROW 5
+
+/*
+ * Minimum slider length, in pixels (designed to make sure that the slider
+ * is always easy to grab with the mouse).
+ */
+
+#define MIN_SLIDER_LENGTH 1
+
+/*
+ * Information used for argv parsing.
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes", "Attributes",
+ DEF_SCROLLBAR_ACTIVE_ATTR_COLOR, Ck_Offset(Scrollbar, activeAttr),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes", "Attributes",
+ DEF_SCROLLBAR_ACTIVE_ATTR_MONO, Ck_Offset(Scrollbar, activeAttr),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_SCROLLBAR_ACTIVE_BG_COLOR, Ck_Offset(Scrollbar, activeBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_SCROLLBAR_ACTIVE_BG_MONO, Ck_Offset(Scrollbar, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_SCROLLBAR_ACTIVE_FG_COLOR, Ck_Offset(Scrollbar, activeFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_SCROLLBAR_ACTIVE_FG_MONO, Ck_Offset(Scrollbar, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_SCROLLBAR_ATTR, Ck_Offset(Scrollbar, normalAttr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_SCROLLBAR_BG_COLOR, Ck_Offset(Scrollbar, normalBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_SCROLLBAR_BG_MONO, Ck_Offset(Scrollbar, normalBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_STRING, "-command", "command", "Command",
+ DEF_SCROLLBAR_COMMAND, Ck_Offset(Scrollbar, command),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_SCROLLBAR_FG_COLOR, Ck_Offset(Scrollbar, normalFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_SCROLLBAR_FG_MONO, Ck_Offset(Scrollbar, normalFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_UID, "-orient", "orient", "Orient",
+ DEF_SCROLLBAR_ORIENT, Ck_Offset(Scrollbar, orientUid), 0},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_SCROLLBAR_TAKE_FOCUS, Ck_Offset(Scrollbar, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ComputeScrollbarGeometry _ANSI_ARGS_((
+ Scrollbar *scrollPtr));
+static int ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
+ Scrollbar *scrollPtr, int argc, char **argv,
+ int flags));
+static void DestroyScrollbar _ANSI_ARGS_((ClientData clientData));
+static void DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
+static void EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
+static void ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static void ScrollbarCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static int ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
+ int x, int y));
+static int ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *, int argc, char **argv));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ScrollbarCmd --
+ *
+ * This procedure is invoked to process the "scrollbar" Tcl
+ * command. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_ScrollbarCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ register Scrollbar *scrollPtr;
+ CkWindow *new;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize fields that won't be initialized by ConfigureScrollbar,
+ * or which ConfigureScrollbar expects to have reasonable values
+ * (e.g. resource pointers).
+ */
+
+ scrollPtr = (Scrollbar *) ckalloc(sizeof (Scrollbar));
+ scrollPtr->winPtr = new;
+ scrollPtr->interp = interp;
+ scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
+ scrollPtr->winPtr->pathName, ScrollbarWidgetCmd,
+ (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
+ scrollPtr->orientUid = NULL;
+ scrollPtr->vertical = 0;
+ scrollPtr->command = NULL;
+ scrollPtr->commandSize = 0;
+ scrollPtr->normalBg = 0;
+ scrollPtr->normalFg = 0;
+ scrollPtr->normalAttr = 0;
+ scrollPtr->activeBg = 0;
+ scrollPtr->activeFg = 0;
+ scrollPtr->activeAttr = 0;
+ scrollPtr->firstFraction = 0.0;
+ scrollPtr->lastFraction = 0.0;
+ scrollPtr->takeFocus = NULL;
+ scrollPtr->flags = 0;
+
+ Ck_SetClass(scrollPtr->winPtr, "Scrollbar");
+ Ck_CreateEventHandler(scrollPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
+ ScrollbarEventProc, (ClientData) scrollPtr);
+ if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ interp->result = scrollPtr->winPtr->pathName;
+ return TCL_OK;
+
+error:
+ Ck_DestroyWindow(scrollPtr->winPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ScrollbarWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about scrollbar
+ * widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register Scrollbar *scrollPtr = (Scrollbar *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) scrollPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " activate\"", (char *) NULL);
+ goto error;
+ }
+ if (!(scrollPtr->flags & ACTIVATED)) {
+ scrollPtr->flags |= ACTIVATED;
+ EventuallyRedraw(scrollPtr);
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, scrollPtr->winPtr, configSpecs,
+ (char *) scrollPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, scrollPtr->winPtr, configSpecs,
+ (char *) scrollPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, scrollPtr->winPtr, configSpecs,
+ (char *) scrollPtr, argv[2], 0);
+ } else {
+ result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " deactivate\"", (char *) NULL);
+ goto error;
+ }
+ if (scrollPtr->flags & ACTIVATED) {
+ scrollPtr->flags &= ~ACTIVATED;
+ EventuallyRedraw(scrollPtr);
+ }
+ } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
+ int x, y, pos, length;
+ double fraction;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " fraction x y\"", (char *) NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+ if (scrollPtr->vertical) {
+ pos = y - 1;
+ length = scrollPtr->winPtr->height - 1 - 2;
+ } else {
+ pos = x - 1;
+ length = scrollPtr->winPtr->width - 1 - 2;
+ }
+ if (length == 0) {
+ fraction = 0.0;
+ } else {
+ fraction = ((double) pos / (double) length);
+ }
+ if (fraction < 0) {
+ fraction = 0;
+ } else if (fraction > 1.0) {
+ fraction = 1.0;
+ }
+ sprintf(interp->result, "%g", fraction);
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get\"", (char *) NULL);
+ goto error;
+ }
+ Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
+ Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
+ Tcl_AppendResult(interp, first, " ", last, (char *) NULL);
+ } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
+ int x, y, thing;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " identify x y\"", (char *) NULL);
+ goto error;
+ }
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+ thing = ScrollbarPosition(scrollPtr, x, y);
+ switch (thing) {
+ case TOP_ARROW: interp->result = "arrow1"; break;
+ case TOP_GAP: interp->result = "trough1"; break;
+ case SLIDER: interp->result = "slider"; break;
+ case BOTTOM_GAP: interp->result = "trough2"; break;
+ case BOTTOM_ARROW: interp->result = "arrow2"; break;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
+ if (argc == 4) {
+ double first, last;
+
+ if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
+ goto error;
+ }
+ if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
+ goto error;
+ }
+ if (first < 0) {
+ scrollPtr->firstFraction = 0;
+ } else if (first > 1.0) {
+ scrollPtr->firstFraction = 1.0;
+ } else {
+ scrollPtr->firstFraction = first;
+ }
+ if (last < scrollPtr->firstFraction) {
+ scrollPtr->lastFraction = scrollPtr->firstFraction;
+ } else if (last > 1.0) {
+ scrollPtr->lastFraction = 1.0;
+ } else {
+ scrollPtr->lastFraction = last;
+ }
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " set firstFraction lastFraction\"",
+ (char *) NULL);
+ goto error;
+ }
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be activate, cget, configure, deactivate, ",
+ "fraction, get, or set", (char *) NULL);
+ goto error;
+ }
+ Ck_Release((ClientData) scrollPtr);
+ return result;
+
+error:
+ Ck_Release((ClientData) scrollPtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyScrollbar --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a scrollbar at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the scrollbar is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyScrollbar(clientData)
+ ClientData clientData; /* Info about scrollbar widget. */
+{
+ register Scrollbar *scrollPtr = (Scrollbar *) clientData;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ Ck_FreeOptions(configSpecs, (char *) scrollPtr, 0);
+ ckfree((char *) scrollPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollbarCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ScrollbarCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Scrollbar *scrollPtr = (Scrollbar *) clientData;
+ CkWindow *winPtr = scrollPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ scrollPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureScrollbar --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or
+ * reconfigure) a scrollbar widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as colors, border width,
+ * etc. get set for scrollPtr; old resources get freed,
+ * if there were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register Scrollbar *scrollPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to
+ * Ck_ConfigureWidget. */
+{
+ size_t length;
+
+ if (Ck_ConfigureWidget(interp, scrollPtr->winPtr, configSpecs,
+ argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * A few options need special processing, such as parsing the
+ * orientation or setting the background from a 3-D border.
+ */
+
+ length = strlen(scrollPtr->orientUid);
+ if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
+ scrollPtr->vertical = 1;
+ } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
+ scrollPtr->vertical = 0;
+ } else {
+ Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
+ "\": must be vertical or horizontal", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ if (scrollPtr->command != NULL) {
+ scrollPtr->commandSize = strlen(scrollPtr->command);
+ } else {
+ scrollPtr->commandSize = 0;
+ }
+
+ /*
+ * Register the desired geometry for the window (leave enough space
+ * for the two arrows plus a minimum-size slider, plus border around
+ * the whole window, if any). Then arrange for the window to be
+ * redisplayed.
+ */
+
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayScrollbar --
+ *
+ * This procedure redraws the contents of a scrollbar window.
+ * It is invoked as a do-when-idle handler, so it only runs
+ * when there's nothing else for the application to do.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayScrollbar(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ register Scrollbar *scrollPtr = (Scrollbar *) clientData;
+ register CkWindow *winPtr = scrollPtr->winPtr;
+ int width, i, gchar;
+
+ if ((scrollPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ goto done;
+ }
+
+ width = (scrollPtr->vertical ? winPtr->height : winPtr->width);
+
+ if (scrollPtr->flags & ACTIVATED) {
+ Ck_SetWindowAttr(winPtr, scrollPtr->activeFg,
+ scrollPtr->activeBg, scrollPtr->activeAttr);
+ } else {
+ Ck_SetWindowAttr(winPtr, scrollPtr->normalFg,
+ scrollPtr->normalBg, scrollPtr->normalAttr);
+ }
+
+ /*
+ * Fill space left with blanks.
+ */
+
+ if (scrollPtr->vertical) {
+ for (i = 0; i < width; i++) {
+ wmove(winPtr->window, i, 0);
+ waddch(winPtr->window, ' ');
+ }
+ } else {
+ wmove(winPtr->window, 0, 0);
+ for (i = 0; i < width; i++) {
+ waddch(winPtr->window, ' ');
+ }
+ }
+
+ /*
+ * Display the slider.
+ */
+
+ Ck_GetGChar(scrollPtr->interp, "ckboard", &gchar);
+ if (scrollPtr->vertical) {
+ for (i = scrollPtr->sliderFirst; i < scrollPtr->sliderLast; i++) {
+ mvwaddch(winPtr->window, i, 0, gchar);
+ }
+ } else {
+ wmove(winPtr->window, 0, scrollPtr->sliderFirst);
+ for (i = scrollPtr->sliderFirst; i < scrollPtr->sliderLast; i++) {
+ waddch(winPtr->window, gchar);
+ }
+ }
+
+ /*
+ * Display top or left arrow.
+ */
+
+ Ck_GetGChar(scrollPtr->interp, scrollPtr->vertical ? "uarrow" : "larrow",
+ &gchar);
+ mvwaddch(winPtr->window, 0, 0, gchar);
+
+ /*
+ * Display the bottom or right arrow.
+ */
+
+ Ck_GetGChar(scrollPtr->interp, scrollPtr->vertical ? "darrow" : "rarrow",
+ &gchar);
+ scrollPtr->vertical ? wmove(winPtr->window, width - 1, 0) :
+ wmove(winPtr->window, 0, width - 1);
+ waddch(winPtr->window, gchar);
+
+ Ck_EventuallyRefresh(winPtr);
+
+done:
+ scrollPtr->flags &= ~REDRAW_PENDING;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarEventProc --
+ *
+ * This procedure is invoked by the dispatcher for various
+ * events on scrollbars.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ScrollbarEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Scrollbar *scrollPtr = (Scrollbar *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ ComputeScrollbarGeometry(scrollPtr);
+ EventuallyRedraw(scrollPtr);
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (scrollPtr->winPtr != NULL) {
+ scrollPtr->winPtr = NULL;
+ Tcl_DeleteCommand(scrollPtr->interp,
+ Tcl_GetCommandName(scrollPtr->interp, scrollPtr->widgetCmd));
+ }
+ if (scrollPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
+ }
+ Ck_EventuallyFree((ClientData) scrollPtr,
+ (Ck_FreeProc *) DestroyScrollbar);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeScrollbarGeometry --
+ *
+ * After changes in a scrollbar's size or configuration, this
+ * procedure recomputes various geometry information used in
+ * displaying the scrollbar.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The scrollbar will be displayed differently.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ComputeScrollbarGeometry(scrollPtr)
+ register Scrollbar *scrollPtr; /* Scrollbar whose geometry may
+ * have changed. */
+{
+ int fieldLength;
+
+ fieldLength = (scrollPtr->vertical ? scrollPtr->winPtr->height
+ : scrollPtr->winPtr->width) - 2;
+ if (fieldLength < 0) {
+ fieldLength = 0;
+ }
+ scrollPtr->sliderFirst = (int) (fieldLength * scrollPtr->firstFraction);
+ scrollPtr->sliderLast = (int) (fieldLength * scrollPtr->lastFraction);
+
+ /*
+ * Adjust the slider so that some piece of it is always
+ * displayed in the scrollbar and so that it has at least
+ * a minimal width (so it can be grabbed with the mouse).
+ */
+
+ if (scrollPtr->sliderFirst > fieldLength) {
+ scrollPtr->sliderFirst = fieldLength;
+ }
+ if (scrollPtr->sliderFirst < 0) {
+ scrollPtr->sliderFirst = 0;
+ }
+ if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
+ + MIN_SLIDER_LENGTH)) {
+ scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
+ }
+ if (scrollPtr->sliderLast > fieldLength) {
+ scrollPtr->sliderLast = fieldLength;
+ }
+ scrollPtr->sliderFirst += 1;
+ scrollPtr->sliderLast += 1;
+
+ /*
+ * Register the desired geometry for the window (leave enough space
+ * for the two arrows plus a minimum-size slider, plus border around
+ * the whole window, if any). Then arrange for the window to be
+ * redisplayed.
+ */
+
+ if (scrollPtr->vertical) {
+ Ck_GeometryRequest(scrollPtr->winPtr, 1, 2 + MIN_SLIDER_LENGTH);
+ } else {
+ Ck_GeometryRequest(scrollPtr->winPtr, 2 + MIN_SLIDER_LENGTH, 1);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarPosition --
+ *
+ * Determine the scrollbar element corresponding to a
+ * given position.
+ *
+ * Results:
+ * One of TOP_ARROW, TOP_GAP, etc., indicating which element
+ * of the scrollbar covers the position given by (x, y). If
+ * (x,y) is outside the scrollbar entirely, then OUTSIDE is
+ * returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ScrollbarPosition(scrollPtr, x, y)
+ register Scrollbar *scrollPtr; /* Scrollbar widget record. */
+ int x, y; /* Coordinates within scrollPtr's
+ * window. */
+{
+ int length, width, tmp;
+
+ if (scrollPtr->vertical) {
+ length = scrollPtr->winPtr->height;
+ width = scrollPtr->winPtr->width;
+ } else {
+ tmp = x;
+ x = y;
+ y = tmp;
+ length = scrollPtr->winPtr->width;
+ width = scrollPtr->winPtr->height;
+ }
+
+ if (x < 0 || x >= width || y < 0 || y >= length)
+ return OUTSIDE;
+
+ if (y == 0)
+ return TOP_ARROW;
+ if (y < scrollPtr->sliderFirst)
+ return TOP_GAP;
+ if (y < scrollPtr->sliderLast)
+ return SLIDER;
+ if (y == length - 1)
+ return BOTTOM_ARROW;
+ return BOTTOM_GAP;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * EventuallyRedraw --
+ *
+ * Arrange for one or more of the fields of a scrollbar
+ * to be redrawn.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EventuallyRedraw(scrollPtr)
+ register Scrollbar *scrollPtr; /* Information about widget. */
+{
+ if ((scrollPtr->winPtr == NULL) ||
+ !(scrollPtr->winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+ if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
+ Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
+ scrollPtr->flags |= REDRAW_PENDING;
+ }
+}
--- /dev/null
+/*
+ * ckText.c --
+ *
+ * This module provides a big chunk of the implementation of
+ * multi-line editable text widgets for ck. Among other things,
+ * it provides the Tcl command interfaces to text widgets and
+ * the display code. The B-tree representation of text is
+ * implemented elsewhere.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+#include "default.h"
+
+/*
+ * Information used to parse text configuration options:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_TEXT_ATTR, Ck_Offset(CkText, attr), 0},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_TEXT_BG_COLOR, Ck_Offset(CkText, bg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_TEXT_BG_MONO, Ck_Offset(CkText, bg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TEXT_FG, Ck_Offset(CkText, fg), 0},
+ {CK_CONFIG_COORD, "-height", "height", "Height",
+ DEF_TEXT_HEIGHT, Ck_Offset(CkText, height), 0},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_TEXT_SELECT_ATTR_COLOR,
+ Ck_Offset(CkText, selAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_TEXT_SELECT_ATTR_MONO,
+ Ck_Offset(CkText, selAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TEXT_SELECT_BG_COLOR, Ck_Offset(CkText, selBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TEXT_SELECT_BG_MONO, Ck_Offset(CkText, selBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TEXT_SELECT_FG_COLOR, Ck_Offset(CkText, selFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TEXT_SELECT_FG_MONO, Ck_Offset(CkText, selFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_UID, "-state", "state", "State",
+ DEF_TEXT_STATE, Ck_Offset(CkText, state), 0},
+ {CK_CONFIG_STRING, "-tabs", "tabs", "Tabs",
+ DEF_TEXT_TABS, Ck_Offset(CkText, tabOptionString), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_TEXT_TAKE_FOCUS, Ck_Offset(CkText, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COORD, "-width", "width", "Width",
+ DEF_TEXT_WIDTH, Ck_Offset(CkText, width), 0},
+ {CK_CONFIG_UID, "-wrap", "wrap", "Wrap",
+ DEF_TEXT_WRAP, Ck_Offset(CkText, wrapMode), 0},
+ {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ DEF_TEXT_XSCROLL_COMMAND, Ck_Offset(CkText, xScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ DEF_TEXT_YSCROLL_COMMAND, Ck_Offset(CkText, yScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Ck_Uid's used to represent text states:
+ */
+
+Ck_Uid ckTextCharUid = NULL;
+Ck_Uid ckTextDisabledUid = NULL;
+Ck_Uid ckTextNoneUid = NULL;
+Ck_Uid ckTextNormalUid = NULL;
+Ck_Uid ckTextWordUid = NULL;
+
+/*
+ * Boolean variable indicating whether or not special debugging code
+ * should be executed.
+ */
+
+int ckTextDebug = 0;
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
+ CkText *textPtr, int argc, char **argv, int flags));
+static int DeleteChars _ANSI_ARGS_((CkText *textPtr,
+ char *index1String, char *index2String));
+static void DestroyText _ANSI_ARGS_((ClientData clientData));
+static void InsertChars _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, char *string));
+static void TextCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void TextEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static int TextSearchCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+static int TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_TextCmd --
+ *
+ * This procedure is invoked to process the "text" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_TextCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *new;
+ register CkText *textPtr;
+ CkTextIndex startIndex;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Perform once-only initialization:
+ */
+
+ if (ckTextNormalUid == NULL) {
+ ckTextCharUid = Ck_GetUid("char");
+ ckTextDisabledUid = Ck_GetUid("disabled");
+ ckTextNoneUid = Ck_GetUid("none");
+ ckTextNormalUid = Ck_GetUid("normal");
+ ckTextWordUid = Ck_GetUid("word");
+ }
+
+ /*
+ * Create the window.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ textPtr = (CkText *) ckalloc(sizeof(CkText));
+ textPtr->winPtr = new;
+ textPtr->interp = interp;
+ textPtr->widgetCmd = Tcl_CreateCommand(interp,
+ new->pathName, TextWidgetCmd, (ClientData) textPtr,
+ TextCmdDeletedProc);
+ textPtr->tree = CkBTreeCreate();
+ Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);
+ textPtr->numTags = 0;
+ Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&textPtr->windowTable, TCL_STRING_KEYS);
+ textPtr->state = ckTextNormalUid;
+ textPtr->bg = 0;
+ textPtr->fg = 0;
+ textPtr->attr = 0;
+ textPtr->tabOptionString = NULL;
+ textPtr->tabArrayPtr = NULL;
+ textPtr->wrapMode = ckTextCharUid;
+ textPtr->width = 0;
+ textPtr->height = 0;
+ textPtr->prevWidth = new->width;
+ textPtr->prevHeight = new->height;
+ CkTextCreateDInfo(textPtr);
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, 0, 0, &startIndex);
+#else
+ CkTextMakeIndex(textPtr->tree, 0, 0, &startIndex);
+#endif
+ CkTextSetYView(textPtr, &startIndex, 0);
+ textPtr->selTagPtr = NULL;
+ textPtr->selBg = 0;
+ textPtr->selFg = 0;
+ textPtr->selAttr = 0;
+ textPtr->abortSelections = 0;
+ textPtr->insertMarkPtr = NULL;
+ textPtr->bindingTable = NULL;
+ textPtr->currentMarkPtr = NULL;
+ textPtr->pickEvent.type = -1;
+ textPtr->numCurTags = 0;
+ textPtr->curTagArrayPtr = NULL;
+ textPtr->takeFocus = NULL;
+ textPtr->xScrollCmd = NULL;
+ textPtr->yScrollCmd = NULL;
+ textPtr->flags = 0;
+
+ /*
+ * Create the "sel" tag and the "current" and "insert" marks.
+ */
+
+ textPtr->selTagPtr = CkTextCreateTag(textPtr, "sel");
+ textPtr->currentMarkPtr = CkTextSetMark(textPtr, "current", &startIndex);
+ textPtr->insertMarkPtr = CkTextSetMark(textPtr, "insert", &startIndex);
+
+ Ck_SetClass(new, "Text");
+ Ck_CreateEventHandler(textPtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_DESTROY | CK_EV_MAP | CK_EV_FOCUSIN |
+ CK_EV_FOCUSOUT,
+ TextEventProc, (ClientData) textPtr);
+ Ck_CreateEventHandler(textPtr->winPtr, CK_EV_KEYPRESS,
+ CkTextBindProc, (ClientData) textPtr);
+ if (ConfigureText(interp, textPtr, argc-2, argv+2, 0) != TCL_OK) {
+ Ck_DestroyWindow(textPtr->winPtr);
+ return TCL_ERROR;
+ }
+ interp->result = textPtr->winPtr->pathName;
+
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TextWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a text widget. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+TextWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ register CkText *textPtr = (CkText *) clientData;
+ int result = TCL_OK;
+ size_t length;
+ int c;
+ CkTextIndex index1, index2;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) textPtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
+ int x, y, width, height;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " bbox index\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextCharBbox(textPtr, &index1, &x, &y, &width, &height) == 0) {
+ sprintf(interp->result, "%d %d %d %d", x, y, width, height);
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ result = Ck_ConfigureValue(interp, textPtr->winPtr, configSpecs,
+ (char *) textPtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)
+ && (length >= 3)) {
+ int relation, value;
+ char *p;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " compare index1 op index2\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if ((CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK)
+ || (CkTextGetIndex(interp, textPtr, argv[4], &index2)
+ != TCL_OK)) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ relation = CkTextIndexCmp(&index1, &index2);
+ p = argv[3];
+ if (p[0] == '<') {
+ value = (relation < 0);
+ if ((p[1] == '=') && (p[2] == 0)) {
+ value = (relation <= 0);
+ } else if (p[1] != 0) {
+ compareError:
+ Tcl_AppendResult(interp, "bad comparison operator \"",
+ argv[3], "\": must be <, <=, ==, >=, >, or !=",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else if (p[0] == '>') {
+ value = (relation > 0);
+ if ((p[1] == '=') && (p[2] == 0)) {
+ value = (relation >= 0);
+ } else if (p[1] != 0) {
+ goto compareError;
+ }
+ } else if ((p[0] == '=') && (p[1] == '=') && (p[2] == 0)) {
+ value = (relation == 0);
+ } else if ((p[0] == '!') && (p[1] == '=') && (p[2] == 0)) {
+ value = (relation != 0);
+ } else {
+ goto compareError;
+ }
+ interp->result = (value) ? "1" : "0";
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 3)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, textPtr->winPtr, configSpecs,
+ (char *) textPtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, textPtr->winPtr, configSpecs,
+ (char *) textPtr, argv[2], 0);
+ } else {
+ result = ConfigureText(interp, textPtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)
+ && (length >= 3)) {
+ if (argc > 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " debug boolean\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (argc == 2) {
+ interp->result = (ckBTreeDebug) ? "1" : "0";
+ } else {
+ if (Tcl_GetBoolean(interp, argv[2], &ckBTreeDebug) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ ckTextDebug = ckBTreeDebug;
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
+ && (length >= 3)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " delete index1 ?index2?\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (textPtr->state == ckTextNormalUid) {
+ result = DeleteChars(textPtr, argv[2],
+ (argc == 4) ? argv[3] : (char *) NULL);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "dlineinfo", length) == 0)
+ && (length >= 2)) {
+ int x, y, width, height, base;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " dlineinfo index\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextDLineInfo(textPtr, &index1, &x, &y, &width, &height, &base)
+ == 0) {
+ sprintf(interp->result, "%d %d %d %d %d", x, y, width,
+ height, base);
+ }
+ } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " get index1 ?index2?\"", (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (argc == 3) {
+ index2 = index1;
+ CkTextIndexForwChars(&index2, 1, &index2);
+ } else if (CkTextGetIndex(interp, textPtr, argv[3], &index2)
+ != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextIndexCmp(&index1, &index2) >= 0) {
+ goto done;
+ }
+ while (1) {
+ int offset, last, savedChar;
+ CkTextSegment *segPtr;
+
+ segPtr = CkTextIndexToSeg(&index1, &offset);
+ last = segPtr->size;
+ if (index1.linePtr == index2.linePtr) {
+ int last2;
+
+ if (index2.charIndex == index1.charIndex) {
+ break;
+ }
+ last2 = index2.charIndex - index1.charIndex + offset;
+ if (last2 < last) {
+ last = last2;
+ }
+ }
+ if (segPtr->typePtr == &ckTextCharType) {
+ savedChar = segPtr->body.chars[last];
+ segPtr->body.chars[last] = 0;
+ Tcl_AppendResult(interp, segPtr->body.chars + offset,
+ (char *) NULL);
+ segPtr->body.chars[last] = savedChar;
+ }
+ CkTextIndexForwChars(&index1, last-offset, &index1);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
+ && (length >= 3)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " index index\"",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ CkTextPrintIndex(&index1, interp->result);
+ } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
+ && (length >= 3)) {
+ int i, j, numTags;
+ char **tagNames;
+ CkTextTag **oldTagArrayPtr;
+
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0],
+ " insert index chars ?tagList chars tagList ...?\"",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ if (textPtr->state == ckTextNormalUid) {
+ for (j = 3; j < argc; j += 2) {
+ InsertChars(textPtr, &index1, argv[j]);
+ if (argc > (j+1)) {
+ CkTextIndexForwChars(&index1, (int) strlen(argv[j]),
+ &index2);
+ oldTagArrayPtr = CkBTreeGetTags(&index1, &numTags);
+ if (oldTagArrayPtr != NULL) {
+ for (i = 0; i < numTags; i++) {
+ CkBTreeTag(&index1, &index2, oldTagArrayPtr[i], 0);
+ }
+ ckfree((char *) oldTagArrayPtr);
+ }
+ if (Tcl_SplitList(interp, argv[j+1], &numTags, &tagNames)
+ != TCL_OK) {
+ result = TCL_ERROR;
+ goto done;
+ }
+ for (i = 0; i < numTags; i++) {
+ CkBTreeTag(&index1, &index2,
+ CkTextCreateTag(textPtr, tagNames[i]), 1);
+ }
+ ckfree((char *) tagNames);
+ index1 = index2;
+ }
+ }
+ }
+ } else if ((c == 'm') && (strncmp(argv[1], "mark", length) == 0)) {
+ result = CkTextMarkCmd(textPtr, interp, argc, argv);
+ } else if ((c == 's') && (strcmp(argv[1], "search") == 0)
+ && (length >= 3)) {
+ result = TextSearchCmd(textPtr, interp, argc, argv);
+ } else if ((c == 's') && (strcmp(argv[1], "see") == 0) && (length >= 3)) {
+ result = CkTextSeeCmd(textPtr, interp, argc, argv);
+ } else if ((c == 't') && (strcmp(argv[1], "tag") == 0)) {
+ result = CkTextTagCmd(textPtr, interp, argc, argv);
+#if 0
+ } else if ((c == 'w') && (strncmp(argv[1], "window", length) == 0)) {
+ result = CkTextWindowCmd(textPtr, interp, argc, argv);
+#endif
+ } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
+ result = CkTextXviewCmd(textPtr, interp, argc, argv);
+ } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)
+ && (length >= 2)) {
+ result = CkTextYviewCmd(textPtr, interp, argc, argv);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be bbox, cget, compare, configure, debug, delete, ",
+ "dlineinfo, get, index, insert, mark, scan, search, see, ",
+ "tag, window, xview, or yview",
+ (char *) NULL);
+ result = TCL_ERROR;
+ }
+
+ done:
+ Ck_Release((ClientData) textPtr);
+ return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyText --
+ *
+ * This procedure is invoked by Ck_EventuallyFree or Ck_Release
+ * to clean up the internal structure of a text at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the text is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyText(clientData)
+ ClientData clientData; /* Info about text widget. */
+{
+ register CkText *textPtr = (CkText *) clientData;
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+ CkTextTag *tagPtr;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff. Special note: free up display-related information
+ * before deleting the B-tree, since display-related stuff
+ * may refer to stuff in the B-tree.
+ */
+
+ CkTextFreeDInfo(textPtr);
+ CkBTreeDestroy(textPtr->tree);
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ tagPtr = (CkTextTag *) Tcl_GetHashValue(hPtr);
+ CkTextFreeTag(textPtr, tagPtr);
+ }
+ Tcl_DeleteHashTable(&textPtr->tagTable);
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ ckfree((char *) Tcl_GetHashValue(hPtr));
+ }
+ Tcl_DeleteHashTable(&textPtr->markTable);
+ if (textPtr->tabArrayPtr != NULL) {
+ ckfree((char *) textPtr->tabArrayPtr);
+ }
+ if (textPtr->bindingTable != NULL) {
+ Ck_DeleteBindingTable(textPtr->bindingTable);
+ }
+
+ /*
+ * NOTE: do NOT free up selBorder, selBdString, or selFgColorPtr:
+ * they are duplicates of information in the "sel" tag, which was
+ * freed up as part of deleting the tags above.
+ */
+
+ Ck_FreeOptions(configSpecs, (char *) textPtr, 0);
+ ckfree((char *) textPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureText --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the Ck option database, in order to configure (or
+ * reconfigure) a text widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for textPtr; old resources get freed, if there
+ * were any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureText(interp, textPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ register CkText *textPtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Ck_ConfigureWidget. */
+{
+ if (Ck_ConfigureWidget(interp, textPtr->winPtr, configSpecs,
+ argc, argv, (char *) textPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * A few other options also need special processing, such as parsing
+ * the geometry and setting the background from a 3-D border.
+ */
+
+ if ((textPtr->state != ckTextNormalUid)
+ && (textPtr->state != ckTextDisabledUid)) {
+ Tcl_AppendResult(interp, "bad state value \"", textPtr->state,
+ "\": must be normal or disabled", (char *) NULL);
+ textPtr->state = ckTextNormalUid;
+ return TCL_ERROR;
+ }
+
+ if ((textPtr->wrapMode != ckTextCharUid)
+ && (textPtr->wrapMode != ckTextNoneUid)
+ && (textPtr->wrapMode != ckTextWordUid)) {
+ Tcl_AppendResult(interp, "bad wrap mode \"", textPtr->wrapMode,
+ "\": must be char, none, or word", (char *) NULL);
+ textPtr->wrapMode = ckTextCharUid;
+ return TCL_ERROR;
+ }
+
+ /*
+ * Parse tab stops.
+ */
+
+ if (textPtr->tabArrayPtr != NULL) {
+ ckfree((char *) textPtr->tabArrayPtr);
+ textPtr->tabArrayPtr = NULL;
+ }
+ if (textPtr->tabOptionString != NULL) {
+ textPtr->tabArrayPtr = CkTextGetTabs(interp, textPtr->winPtr,
+ textPtr->tabOptionString);
+ if (textPtr->tabArrayPtr == NULL) {
+ Tcl_AddErrorInfo(interp,"\n (while processing -tabs option)");
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * Make sure that configuration options are properly mirrored
+ * between the widget record and the "sel" tags. NOTE: we don't
+ * have to free up information during the mirroring; old
+ * information was freed when it was replaced in the widget
+ * record.
+ */
+
+ textPtr->selTagPtr->bg = textPtr->selBg;
+ textPtr->selTagPtr->fg = textPtr->selFg;
+ textPtr->selTagPtr->attr = textPtr->selAttr;
+ textPtr->selTagPtr->affectsDisplay = 0;
+#if 0
+/* ??? */
+ if ((textPtr->selTagPtr->border != NULL)
+ || (textPtr->selTagPtr->bdString != NULL)
+ || (textPtr->selTagPtr->reliefString != NULL)
+ || (textPtr->selTagPtr->bgStipple != None)
+ || (textPtr->selTagPtr->fgColor != NULL)
+ || (textPtr->selTagPtr->fontPtr != None)
+ || (textPtr->selTagPtr->fgStipple != None)
+ || (textPtr->selTagPtr->justifyString != NULL)
+ || (textPtr->selTagPtr->lMargin1String != NULL)
+ || (textPtr->selTagPtr->lMargin2String != NULL)
+ || (textPtr->selTagPtr->offsetString != NULL)
+ || (textPtr->selTagPtr->overstrikeString != NULL)
+ || (textPtr->selTagPtr->rMarginString != NULL)
+ || (textPtr->selTagPtr->spacing1String != NULL)
+ || (textPtr->selTagPtr->spacing2String != NULL)
+ || (textPtr->selTagPtr->spacing3String != NULL)
+ || (textPtr->selTagPtr->tabString != NULL)
+ || (textPtr->selTagPtr->underlineString != NULL)
+ || (textPtr->selTagPtr->wrapMode != NULL)) {
+ textPtr->selTagPtr->affectsDisplay = 1;
+ }
+#endif
+ CkTextRedrawTag(textPtr, (CkTextIndex *) NULL, (CkTextIndex *) NULL,
+ textPtr->selTagPtr, 1);
+
+ /*
+ * Register the desired geometry for the window, and arrange for
+ * the window to be redisplayed.
+ */
+
+ if (textPtr->width <= 0) {
+ textPtr->width = 1;
+ }
+ if (textPtr->height <= 0) {
+ textPtr->height = 1;
+ }
+ Ck_GeometryRequest(textPtr->winPtr, textPtr->width, textPtr->height);
+
+ CkTextRelayoutWindow(textPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TextEventProc --
+ *
+ * This procedure is invoked by the Ck dispatcher on
+ * structure changes to a text. For texts with 3D
+ * borders, this procedure is also invoked for exposures.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+TextEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ register CkEvent *eventPtr; /* Information about event. */
+{
+ register CkText *textPtr = (CkText *) clientData;
+ CkWindow *winPtr = textPtr->winPtr;
+ CkTextIndex index, index2;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ if ((textPtr->prevWidth != winPtr->width)
+ || (textPtr->prevHeight != winPtr->height)) {
+ CkTextRelayoutWindow(textPtr);
+ textPtr->prevWidth = winPtr->width;
+ textPtr->prevHeight = winPtr->height;
+ }
+ CkTextRedrawRegion(textPtr, 0, 0, winPtr->width, winPtr->height);
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (textPtr->winPtr != NULL) {
+ textPtr->winPtr = NULL;
+ Tcl_DeleteCommand(textPtr->interp,
+ Tcl_GetCommandName(textPtr->interp,
+ textPtr->widgetCmd));
+ }
+ Ck_EventuallyFree((ClientData) textPtr, (Ck_FreeProc *) DestroyText);
+ } else if ((eventPtr->type == CK_EV_FOCUSIN)
+ || (eventPtr->type == CK_EV_FOCUSOUT)) {
+ if (eventPtr->type == CK_EV_FOCUSIN) {
+ textPtr->flags |= GOT_FOCUS;
+ } else {
+ textPtr->flags &= ~GOT_FOCUS;
+ }
+ CkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
+ CkTextIndexForwChars(&index, 1, &index2);
+ CkTextChanged(textPtr, &index, &index2);
+ CkTextRedrawRegion(textPtr, 0, 0, winPtr->width,
+ winPtr->height);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TextCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ CkText *textPtr = (CkText *) clientData;
+ CkWindow *winPtr = textPtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case ckwin
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ textPtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * InsertChars --
+ *
+ * This procedure implements most of the functionality of the
+ * "insert" widget command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The characters in "string" get added to the text just before
+ * the character indicated by "indexPtr".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InsertChars(textPtr, indexPtr, string)
+ CkText *textPtr; /* Overall information about text widget. */
+ CkTextIndex *indexPtr; /* Where to insert new characters. May be
+ * modified and/or invalidated. */
+ char *string; /* Null-terminated string containing new
+ * information to add to text. */
+{
+ int lineIndex;
+
+ /*
+ * Don't allow insertions on the last (dummy) line of the text.
+ */
+
+ lineIndex = CkBTreeLineIndex(indexPtr->linePtr);
+ if (lineIndex == CkBTreeNumLines(textPtr->tree)) {
+ lineIndex--;
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, indexPtr);
+#else
+ CkTextMakeIndex(textPtr->tree, lineIndex, 1000000, indexPtr);
+#endif
+ }
+
+ /*
+ * Notify the display module that lines are about to change, then do
+ * the insertion.
+ */
+
+ CkTextChanged(textPtr, indexPtr, indexPtr);
+ CkBTreeInsertChars(indexPtr, string);
+
+ /*
+ * Invalidate any selection retrievals in progress.
+ */
+
+ textPtr->abortSelections = 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteChars --
+ *
+ * This procedure implements most of the functionality of the
+ * "delete" widget command.
+ *
+ * Results:
+ * Returns a standard Tcl result, and leaves an error message
+ * in textPtr->interp if there is an error.
+ *
+ * Side effects:
+ * Characters get deleted from the text.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DeleteChars(textPtr, index1String, index2String)
+ CkText *textPtr; /* Overall information about text widget. */
+ char *index1String; /* String describing location of first
+ * character to delete. */
+ char *index2String; /* String describing location of last
+ * character to delete. NULL means just
+ * delete the one character given by
+ * index1String. */
+{
+ int line1, line2, line, charIndex, resetView;
+ CkTextIndex index1, index2;
+
+ /*
+ * Parse the starting and stopping indices.
+ */
+
+ if (CkTextGetIndex(textPtr->interp, textPtr, index1String, &index1)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (index2String != NULL) {
+ if (CkTextGetIndex(textPtr->interp, textPtr, index2String, &index2)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ index2 = index1;
+ CkTextIndexForwChars(&index2, 1, &index2);
+ }
+
+ /*
+ * Make sure there's really something to delete.
+ */
+
+ if (CkTextIndexCmp(&index1, &index2) >= 0) {
+ return TCL_OK;
+ }
+
+ /*
+ * The code below is ugly, but it's needed to make sure there
+ * is always a dummy empty line at the end of the text. If the
+ * final newline of the file (just before the dummy line) is being
+ * deleted, then back up index to just before the newline. If
+ * there is a newline just before the first character being deleted,
+ * then back up the first index too, so that an even number of lines
+ * gets deleted. Furthermore, remove any tags that are present on
+ * the newline that isn't going to be deleted after all (this simulates
+ * deleting the newline and then adding a "clean" one back again).
+ */
+
+ line1 = CkBTreeLineIndex(index1.linePtr);
+ line2 = CkBTreeLineIndex(index2.linePtr);
+ if (line2 == CkBTreeNumLines(textPtr->tree)) {
+ CkTextTag **arrayPtr;
+ int arraySize, i;
+ CkTextIndex oldIndex2;
+
+ oldIndex2 = index2;
+ CkTextIndexBackChars(&oldIndex2, 1, &index2);
+ line2--;
+ if ((index1.charIndex == 0) && (line1 != 0)) {
+ CkTextIndexBackChars(&index1, 1, &index1);
+ line1--;
+ }
+ arrayPtr = CkBTreeGetTags(&index2, &arraySize);
+ if (arrayPtr != NULL) {
+ for (i = 0; i < arraySize; i++) {
+ CkBTreeTag(&index2, &oldIndex2, arrayPtr[i], 0);
+ }
+ ckfree((char *) arrayPtr);
+ }
+ }
+
+ /*
+ * Tell the display what's about to happen so it can discard
+ * obsolete display information, then do the deletion. Also,
+ * if the deletion involves the top line on the screen, then
+ * we have to reset the view (the deletion will invalidate
+ * textPtr->topIndex). Compute what the new first character
+ * will be, then do the deletion, then reset the view.
+ */
+
+ CkTextChanged(textPtr, &index1, &index2);
+ resetView = line = charIndex = 0;
+ if (CkTextIndexCmp(&index2, &textPtr->topIndex) >= 0) {
+ if (CkTextIndexCmp(&index1, &textPtr->topIndex) <= 0) {
+ /*
+ * Deletion range straddles topIndex: use the beginning
+ * of the range as the new topIndex.
+ */
+
+ resetView = 1;
+ line = line1;
+ charIndex = index1.charIndex;
+ } else if (index1.linePtr == textPtr->topIndex.linePtr) {
+ /*
+ * Deletion range starts on top line but after topIndex.
+ * Use the current topIndex as the new one.
+ */
+
+ resetView = 1;
+ line = line1;
+ charIndex = textPtr->topIndex.charIndex;
+ }
+ } else if (index2.linePtr == textPtr->topIndex.linePtr) {
+ /*
+ * Deletion range ends on top line but before topIndex.
+ * Figure out what will be the new character index for
+ * the character currently pointed to by topIndex.
+ */
+
+ resetView = 1;
+ line = line2;
+ charIndex = textPtr->topIndex.charIndex;
+ if (index1.linePtr != index2.linePtr) {
+ charIndex -= index2.charIndex;
+ } else {
+ charIndex -= (index2.charIndex - index1.charIndex);
+ }
+ }
+ CkBTreeDeleteChars(&index1, &index2);
+ if (resetView) {
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, line, charIndex, &index1);
+#else
+ CkTextMakeIndex(textPtr->tree, line, charIndex, &index1);
+#endif
+ CkTextSetYView(textPtr, &index1, 0);
+ }
+
+ /*
+ * Invalidate any selection retrievals in progress.
+ */
+
+ textPtr->abortSelections = 1;
+
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TextSearchCmd --
+ *
+ * This procedure is invoked to process the "search" widget command
+ * for text widgets. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TextSearchCmd(textPtr, interp, argc, argv)
+ CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int backwards, exact, c, i, argsLeft, noCase, leftToScan;
+ size_t length;
+ int numLines, startingLine, startingChar, lineNum, firstChar, lastChar;
+ int code, matchLength, matchChar, passes, stopLine, searchWholeText;
+ int patLength;
+ char *arg, *pattern, *varName, *p, *startOfLine;
+ char buffer[20];
+ CkTextIndex index, stopIndex;
+ Tcl_DString line, patDString;
+ CkTextSegment *segPtr;
+ CkTextLine *linePtr;
+ Tcl_RegExp regexp = NULL; /* Initialization needed only to
+ * prevent compiler warning. */
+
+ /*
+ * Parse switches and other arguments.
+ */
+
+ exact = 1;
+ backwards = 0;
+ noCase = 0;
+ varName = NULL;
+ for (i = 2; i < argc; i++) {
+ arg = argv[i];
+ if (arg[0] != '-') {
+ break;
+ }
+ length = strlen(arg);
+ if (length < 2) {
+ badSwitch:
+ Tcl_AppendResult(interp, "bad switch \"", arg,
+ "\": must be -forward, -backward, -exact, -regexp, ",
+ "-nocase, -count, or --", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = arg[1];
+ if ((c == 'b') && (strncmp(argv[i], "-backwards", length) == 0)) {
+ backwards = 1;
+ } else if ((c == 'c') && (strncmp(argv[i], "-count", length) == 0)) {
+ if (i >= (argc-1)) {
+ interp->result = "no value given for \"-count\" option";
+ return TCL_ERROR;
+ }
+ i++;
+ varName = argv[i];
+ } else if ((c == 'e') && (strncmp(argv[i], "-exact", length) == 0)) {
+ exact = 1;
+ } else if ((c == 'f') && (strncmp(argv[i], "-forwards", length) == 0)) {
+ backwards = 0;
+ } else if ((c == 'n') && (strncmp(argv[i], "-nocase", length) == 0)) {
+ noCase = 1;
+ } else if ((c == 'r') && (strncmp(argv[i], "-regexp", length) == 0)) {
+ exact = 0;
+ } else if ((c == '-') && (strncmp(argv[i], "--", length) == 0)) {
+ i++;
+ break;
+ } else {
+ goto badSwitch;
+ }
+ }
+ argsLeft = argc - (i+2);
+ if ((argsLeft != 0) && (argsLeft != 1)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " search ?switches? pattern index ?stopIndex?",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ pattern = argv[i];
+
+ /*
+ * Convert the pattern to lower-case if we're supposed to ignore case.
+ */
+
+ if (noCase) {
+ Tcl_DStringInit(&patDString);
+ Tcl_DStringAppend(&patDString, pattern, -1);
+ pattern = Tcl_DStringValue(&patDString);
+#if CK_USE_UTF
+ Tcl_UtfToLower(pattern);
+#else
+ for (p = pattern; *p != 0; p++) {
+ if (isupper((unsigned char) *p)) {
+ *p = tolower((unsigned char) *p);
+ }
+ }
+#endif
+ }
+
+ if (CkTextGetIndex(interp, textPtr, argv[i+1], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ numLines = CkBTreeNumLines(textPtr->tree);
+ startingLine = CkBTreeLineIndex(index.linePtr);
+ startingChar = index.charIndex;
+ if (startingLine >= numLines) {
+ if (backwards) {
+ startingLine = CkBTreeNumLines(textPtr->tree) - 1;
+ startingChar = CkBTreeCharsInLine(CkBTreeFindLine(textPtr->tree,
+ startingLine));
+ } else {
+ startingLine = 0;
+ startingChar = 0;
+ }
+ }
+ if (argsLeft == 1) {
+ if (CkTextGetIndex(interp, textPtr, argv[i+2], &stopIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ stopLine = CkBTreeLineIndex(stopIndex.linePtr);
+ if (!backwards && (stopLine == numLines)) {
+ stopLine = numLines-1;
+ }
+ searchWholeText = 0;
+ } else {
+ stopLine = 0;
+ searchWholeText = 1;
+ }
+
+ /*
+ * Scan through all of the lines of the text circularly, starting
+ * at the given index.
+ */
+
+ matchLength = patLength = 0; /* Only needed to prevent compiler
+ * warnings. */
+ if (exact) {
+ patLength = strlen(pattern);
+ } else {
+ regexp = Tcl_RegExpCompile(interp, pattern);
+ if (regexp == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ lineNum = startingLine;
+ code = TCL_OK;
+ Tcl_DStringInit(&line);
+ for (passes = 0; passes < 2; ) {
+ if (lineNum >= numLines) {
+ /*
+ * Don't search the dummy last line of the text.
+ */
+
+ goto nextLine;
+ }
+
+ /*
+ * Extract the text from the line. If we're doing regular
+ * expression matching, drop the newline from the line, so
+ * that "$" can be used to match the end of the line.
+ */
+
+ linePtr = CkBTreeFindLine(textPtr->tree, lineNum);
+ for (segPtr = linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr != &ckTextCharType) {
+ continue;
+ }
+ Tcl_DStringAppend(&line, segPtr->body.chars, segPtr->size);
+ }
+ if (!exact) {
+ Tcl_DStringSetLength(&line, Tcl_DStringLength(&line)-1);
+ }
+ startOfLine = Tcl_DStringValue(&line);
+
+ /*
+ * If we're ignoring case, convert the line to lower case.
+ */
+
+ if (noCase) {
+#if CK_USE_UTF
+ Tcl_DStringSetLength(&line,
+ Tcl_UtfToLower(Tcl_DStringValue(&line)));
+#else
+ for (p = Tcl_DStringValue(&line); *p != 0; p++) {
+ if (isupper((unsigned char) *p)) {
+ *p = tolower((unsigned char) *p);
+ }
+ }
+#endif
+ }
+
+ /*
+ * Check for matches within the current line. If so, and if we're
+ * searching backwards, repeat the search to find the last match
+ * in the line.
+ */
+
+ matchChar = -1;
+ firstChar = 0;
+ lastChar = INT_MAX;
+ if (lineNum == startingLine) {
+ int indexInDString;
+
+ /*
+ * The starting line is tricky: the first time we see it
+ * we check one part of the line, and the second pass through
+ * we check the other part of the line. We have to be very
+ * careful here because there could be embedded windows or
+ * other things that are not in the extracted line. Rescan
+ * the original line to compute the index in it of the first
+ * character.
+ */
+
+ indexInDString = startingChar;
+ for (segPtr = linePtr->segPtr, leftToScan = startingChar;
+ leftToScan > 0; segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr != &ckTextCharType) {
+ indexInDString -= segPtr->size;
+ }
+ leftToScan -= segPtr->size;
+ }
+
+ passes++;
+ if ((passes == 1) ^ backwards) {
+ /*
+ * Only use the last part of the line.
+ */
+
+ firstChar = indexInDString;
+ if (firstChar >= Tcl_DStringLength(&line)) {
+ goto nextLine;
+ }
+ } else {
+ /*
+ * Use only the first part of the line.
+ */
+
+ lastChar = indexInDString;
+ }
+ }
+ do {
+ int thisLength;
+#if CK_USE_UTF
+ Tcl_UniChar ch;
+#endif
+
+ if (exact) {
+ p = strstr(startOfLine + firstChar, pattern);
+ if (p == NULL) {
+ break;
+ }
+ i = p - startOfLine;
+ thisLength = patLength;
+ } else {
+ char *start, *end;
+ int match;
+
+ match = Tcl_RegExpExec(interp, regexp,
+ startOfLine + firstChar, startOfLine);
+ if (match < 0) {
+ code = TCL_ERROR;
+ goto done;
+ }
+ if (!match) {
+ break;
+ }
+ Tcl_RegExpRange(regexp, 0, &start, &end);
+ i = start - startOfLine;
+ thisLength = end - start;
+ }
+ if (i >= lastChar) {
+ break;
+ }
+ matchChar = i;
+ matchLength = thisLength;
+#if CK_USE_UTF
+ firstChar = i + Tcl_UtfToUniChar(startOfLine + matchChar, &ch);
+#else
+ firstChar = matchChar+1;
+#endif
+ } while (backwards);
+
+ /*
+ * If we found a match then we're done. Make sure that
+ * the match occurred before the stopping index, if one was
+ * specified.
+ */
+
+ if (matchChar >= 0) {
+#if CK_USE_UTF
+ int numChars;
+
+ numChars = Tcl_NumUtfChars(startOfLine + matchChar,
+ matchLength);
+#endif
+
+ /*
+ * The index information returned by the regular expression
+ * parser only considers textual information: it doesn't
+ * account for embedded windows or any other non-textual info.
+ * Scan through the line's segments again to adjust both
+ * matchChar and matchCount.
+ */
+
+ for (segPtr = linePtr->segPtr, leftToScan = matchChar;
+ leftToScan >= 0; segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr != &ckTextCharType) {
+ matchChar += segPtr->size;
+ continue;
+ }
+ leftToScan -= segPtr->size;
+ }
+ for (leftToScan += matchLength; leftToScan > 0;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr != &ckTextCharType) {
+#if CK_USE_UTF
+ numChars += segPtr->size;
+#else
+ matchLength += segPtr->size;
+#endif
+ continue;
+ }
+ leftToScan -= segPtr->size;
+ }
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, lineNum, matchChar, &index);
+#else
+ CkTextMakeIndex(textPtr->tree, lineNum, matchChar, &index);
+#endif
+ if (!searchWholeText) {
+ if (!backwards && (CkTextIndexCmp(&index, &stopIndex) >= 0)) {
+ goto done;
+ }
+ if (backwards && (CkTextIndexCmp(&index, &stopIndex) < 0)) {
+ goto done;
+ }
+ }
+ if (varName != NULL) {
+#if CK_USE_UTF
+ sprintf(buffer, "%d", numChars);
+#else
+ sprintf(buffer, "%d", matchLength);
+#endif
+ if (Tcl_SetVar(interp, varName, buffer, TCL_LEAVE_ERR_MSG)
+ == NULL) {
+ code = TCL_ERROR;
+ goto done;
+ }
+ }
+ CkTextPrintIndex(&index, interp->result);
+ goto done;
+ }
+
+ /*
+ * Go to the next (or previous) line;
+ */
+
+ nextLine:
+ if (backwards) {
+ lineNum--;
+ if (!searchWholeText) {
+ if (lineNum < stopLine) {
+ break;
+ }
+ } else if (lineNum < 0) {
+ lineNum = numLines-1;
+ }
+ } else {
+ lineNum++;
+ if (!searchWholeText) {
+ if (lineNum > stopLine) {
+ break;
+ }
+ } else if (lineNum >= numLines) {
+ lineNum = 0;
+ }
+ }
+ Tcl_DStringSetLength(&line, 0);
+ }
+ done:
+ Tcl_DStringFree(&line);
+ if (noCase) {
+ Tcl_DStringFree(&patDString);
+ }
+ return code;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextGetTabs --
+ *
+ * Parses a string description of a set of tab stops.
+ *
+ * Results:
+ * The return value is a pointer to a malloc'ed structure holding
+ * parsed information about the tab stops. If an error occurred
+ * then the return value is NULL and an error message is left in
+ * interp->result.
+ *
+ * Side effects:
+ * Memory is allocated for the structure that is returned. It is
+ * up to the caller to free this structure when it is no longer
+ * needed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextTabArray *
+CkTextGetTabs(interp, winPtr, string)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ CkWindow *winPtr; /* Window in which the tabs will be
+ * used. */
+ char *string; /* Description of the tab stops. See
+ * text manual entry for details. */
+{
+ int argc, i, count, c;
+ char **argv;
+ CkTextTabArray *tabArrayPtr;
+ CkTextTab *tabPtr;
+#if CK_USE_UTF
+ Tcl_UniChar ch;
+#endif
+
+ if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
+ return NULL;
+ }
+
+ /*
+ * First find out how many entries we need to allocate in the
+ * tab array.
+ */
+
+ count = 0;
+ for (i = 0; i < argc; i++) {
+ c = argv[i][0];
+ if ((c != 'l') && (c != 'r') && (c != 'c') && (c != 'n')) {
+ count++;
+ }
+ }
+
+ /*
+ * Parse the elements of the list one at a time to fill in the
+ * array.
+ */
+
+ tabArrayPtr = (CkTextTabArray *) ckalloc((unsigned)
+ (sizeof(CkTextTabArray) + (count-1)*sizeof(CkTextTab)));
+ tabArrayPtr->numTabs = 0;
+ for (i = 0, tabPtr = &tabArrayPtr->tabs[0]; i < argc; i++, tabPtr++) {
+ if (Ck_GetCoord(interp, winPtr, argv[i], &tabPtr->location)
+ != TCL_OK) {
+ goto error;
+ }
+ tabArrayPtr->numTabs++;
+
+ /*
+ * See if there is an explicit alignment in the next list
+ * element. Otherwise just use "left".
+ */
+
+ tabPtr->alignment = LEFT;
+ if ((i+1) == argc) {
+ continue;
+ }
+#if CK_USE_UTF
+ Tcl_UtfToUniChar(argv[i+1], &ch);
+ if (!Tcl_UniCharIsAlpha(ch)) {
+ continue;
+ }
+#else
+ c = (unsigned char) argv[i+1][0];
+ if (!isalpha(c)) {
+ continue;
+ }
+#endif
+ i += 1;
+ if ((c == 'l') && (strncmp(argv[i], "left",
+ strlen(argv[i])) == 0)) {
+ tabPtr->alignment = LEFT;
+ } else if ((c == 'r') && (strncmp(argv[i], "right",
+ strlen(argv[i])) == 0)) {
+ tabPtr->alignment = RIGHT;
+ } else if ((c == 'c') && (strncmp(argv[i], "center",
+ strlen(argv[i])) == 0)) {
+ tabPtr->alignment = CENTER;
+ } else if ((c == 'n') && (strncmp(argv[i],
+ "numeric", strlen(argv[i])) == 0)) {
+ tabPtr->alignment = NUMERIC;
+ } else {
+ Tcl_AppendResult(interp, "bad tab alignment \"",
+ argv[i], "\": must be left, right, center, or numeric",
+ (char *) NULL);
+ goto error;
+ }
+ }
+ ckfree((char *) argv);
+ return tabArrayPtr;
+
+ error:
+ ckfree((char *) tabArrayPtr);
+ ckfree((char *) argv);
+ return NULL;
+}
--- /dev/null
+/*
+ * ckText.h --
+ *
+ * Declarations shared among the files that implement text
+ * widgets.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef _CKTEXT_H
+#define _CKTEXT_H
+
+#ifndef _CK
+#include "ck.h"
+#endif
+
+/*
+ * Opaque types for structures whose guts are only needed by a single
+ * file:
+ */
+
+typedef struct CkTextBTree *CkTextBTree;
+
+/*
+ * The data structure below defines a single line of text (from newline
+ * to newline, not necessarily what appears on one line of the screen).
+ */
+
+typedef struct CkTextLine {
+ struct Node *parentPtr; /* Pointer to parent node containing
+ * line. */
+ struct CkTextLine *nextPtr; /* Next in linked list of lines with
+ * same parent node in B-tree. NULL
+ * means end of list. */
+ struct CkTextSegment *segPtr; /* First in ordered list of segments
+ * that make up the line. */
+} CkTextLine;
+
+/*
+ * -----------------------------------------------------------------------
+ * Segments: each line is divided into one or more segments, where each
+ * segment is one of several things, such as a group of characters, a
+ * tag toggle, a mark, or an embedded widget. Each segment starts with
+ * a standard header followed by a body that varies from type to type.
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * The data structure below defines the body of a segment that represents
+ * a tag toggle. There is one of these structures at both the beginning
+ * and end of each tagged range.
+ */
+
+typedef struct CkTextToggle {
+ struct CkTextTag *tagPtr; /* Tag that starts or ends here. */
+ int inNodeCounts; /* 1 means this toggle has been
+ * accounted for in node toggle
+ * counts; 0 means it hasn't, yet. */
+} CkTextToggle;
+
+/*
+ * The data structure below defines line segments that represent
+ * marks. There is one of these for each mark in the text.
+ */
+
+typedef struct CkTextMark {
+ struct CkText *textPtr; /* Overall information about text
+ * widget. */
+ CkTextLine *linePtr; /* Line structure that contains the
+ * segment. */
+ Tcl_HashEntry *hPtr; /* Pointer to hash table entry for mark
+ * (in textPtr->markTable). */
+} CkTextMark;
+
+/*
+ * A structure of the following type holds information for each window
+ * embedded in a text widget. This information is only used by the
+ * file ckTextWind.c
+ */
+
+typedef struct CkTextEmbWindow {
+ struct CkText *textPtr; /* Information about the overall text
+ * widget. */
+ CkTextLine *linePtr; /* Line structure that contains this
+ * window. */
+ CkWindow winPtr; /* Window for this segment. NULL
+ * means that the window hasn't
+ * been created yet. */
+ char *create; /* Script to create window on-demand.
+ * NULL means no such script.
+ * Malloc-ed. */
+ int align; /* How to align window in vertical
+ * space. See definitions in
+ * ckTextWind.c. */
+ int padX, padY; /* Padding to leave around each side
+ * of window, in pixels. */
+ int stretch; /* Should window stretch to fill
+ * vertical space of line (except for
+ * pady)? 0 or 1. */
+ int chunkCount; /* Number of display chunks that
+ * refer to this window. */
+ int displayed; /* Non-zero means that the window
+ * has been displayed on the screen
+ * recently. */
+} CkTextEmbWindow;
+
+/*
+ * The data structure below defines line segments.
+ */
+
+typedef struct CkTextSegment {
+ struct Ck_SegType *typePtr; /* Pointer to record describing
+ * segment's type. */
+ struct CkTextSegment *nextPtr; /* Next in list of segments for this
+ * line, or NULL for end of list. */
+ int size; /* Size of this segment (# of bytes
+ * of index space it occupies). */
+ union {
+ char chars[4]; /* Characters that make up character
+ * info. Actual length varies to
+ * hold as many characters as needed.*/
+ CkTextToggle toggle; /* Information about tag toggle. */
+ CkTextMark mark; /* Information about mark. */
+ CkTextEmbWindow ew; /* Information about embedded
+ * window. */
+ } body;
+} CkTextSegment;
+
+/*
+ * Data structures of the type defined below are used during the
+ * execution of Tcl commands to keep track of various interesting
+ * places in a text. An index is only valid up until the next
+ * modification to the character structure of the b-tree so they
+ * can't be retained across Tcl commands. However, mods to marks
+ * or tags don't invalidate indices.
+ */
+
+typedef struct CkTextIndex {
+ CkTextBTree tree; /* Tree containing desired position. */
+ CkTextLine *linePtr; /* Pointer to line containing position
+ * of interest. */
+ int charIndex; /* Index within line of desired
+ * character (0 means first one). */
+} CkTextIndex;
+
+/*
+ * Types for procedure pointers stored in CkTextDispChunk strutures:
+ */
+
+typedef struct CkTextDispChunk CkTextDispChunk;
+
+typedef void Ck_ChunkDisplayProc _ANSI_ARGS_((
+ CkTextDispChunk *chunkPtr, int x, int y,
+ int height, int baseline, WINDOW *window,
+ int screenY));
+typedef void Ck_ChunkUndisplayProc _ANSI_ARGS_((
+ struct CkText *textPtr,
+ CkTextDispChunk *chunkPtr));
+typedef int Ck_ChunkMeasureProc _ANSI_ARGS_((
+ CkTextDispChunk *chunkPtr, int x));
+typedef void Ck_ChunkBboxProc _ANSI_ARGS_((
+ CkTextDispChunk *chunkPtr, int index, int y,
+ int lineHeight, int baseline, int *xPtr,
+ int *yPtr, int *widthPtr, int *heightPtr));
+
+/*
+ * The structure below represents a chunk of stuff that is displayed
+ * together on the screen. This structure is allocated and freed by
+ * generic display code but most of its fields are filled in by
+ * segment-type-specific code.
+ */
+
+struct CkTextDispChunk {
+ /*
+ * The fields below are set by the type-independent code before
+ * calling the segment-type-specific layoutProc. They should not
+ * be modified by segment-type-specific code.
+ */
+
+ int x; /* X position of chunk, in pixels.
+ * This position is measured from the
+ * left edge of the logical line,
+ * not from the left edge of the
+ * window (i.e. it doesn't change
+ * under horizontal scrolling). */
+ struct CkTextDispChunk *nextPtr; /* Next chunk in the display line
+ * or NULL for the end of the list. */
+ struct Style *stylePtr; /* Display information, known only
+ * to ckTextDisp.c. */
+
+ /*
+ * The fields below are set by the layoutProc that creates the
+ * chunk.
+ */
+
+ Ck_ChunkDisplayProc *displayProc; /* Procedure to invoke to draw this
+ * chunk on the display or an
+ * off-screen pixmap. */
+ Ck_ChunkUndisplayProc *undisplayProc;
+ /* Procedure to invoke when segment
+ * ceases to be displayed on screen
+ * anymore. */
+ Ck_ChunkMeasureProc *measureProc; /* Procedure to find character under
+ * a given x-location. */
+ Ck_ChunkBboxProc *bboxProc; /* Procedure to find bounding box
+ * of character in chunk. */
+ int numChars; /* Number of characters that will be
+ * displayed in the chunk. */
+ int minHeight; /* Minimum total line height needed
+ * by this chunk. */
+ int width; /* Width of this chunk, in pixels.
+ * Initially set by chunk-specific
+ * code, but may be increased to
+ * include tab or extra space at end
+ * of line. */
+ int breakIndex; /* Index within chunk of last
+ * acceptable position for a line
+ * (break just before this character).
+ * <= 0 means don't break during or
+ * immediately after this chunk. */
+ ClientData clientData; /* Additional information for use
+ * of displayProc and undisplayProc. */
+};
+
+/*
+ * One data structure of the following type is used for each tag in a
+ * text widget. These structures are kept in textPtr->tagTable and
+ * referred to in other structures.
+ */
+
+typedef struct CkTextTag {
+ char *name; /* Name of this tag. This field is actually
+ * a pointer to the key from the entry in
+ * textPtr->tagTable, so it needn't be freed
+ * explicitly. */
+ int priority; /* Priority of this tag within widget. 0
+ * means lowest priority. Exactly one tag
+ * has each integer value between 0 and
+ * numTags-1. */
+
+ /*
+ * Information for displaying text with this tag. The information
+ * belows acts as an override on information specified by lower-priority
+ * tags. If no value is specified, then the next-lower-priority tag
+ * on the text determins the value. The text widget itself provides
+ * defaults if no tag specifies an override.
+ */
+
+ int bg, fg, attr; /* Foreground/background/video attributes
+ * for text. -1 means no value specified. */
+ char *justifyString; /* -justify option string (malloc-ed).
+ * NULL means option not specified. */
+ Ck_Justify justify; /* How to justify text: CK_JUSTIFY_LEFT,
+ * CK_JUSTIFY_RIGHT, or CK_JUSTIFY_CENTER.
+ * Only valid if justifyString is non-NULL. */
+ char *lMargin1String; /* -lmargin1 option string (malloc-ed).
+ * NULL means option not specified. */
+ int lMargin1; /* Left margin for first display line of
+ * each text line, in pixels. Only valid
+ * if lMargin1String is non-NULL. */
+ char *lMargin2String; /* -lmargin2 option string (malloc-ed).
+ * NULL means option not specified. */
+ int lMargin2; /* Left margin for second and later display
+ * lines of each text line, in pixels. Only
+ * valid if lMargin2String is non-NULL. */
+ char *rMarginString; /* -rmargin option string (malloc-ed).
+ * NULL means option not specified. */
+ int rMargin; /* Right margin for text, in pixels. Only
+ * valid if rMarginString is non-NULL. */
+ char *tabString; /* -tabs option string (malloc-ed).
+ * NULL means option not specified. */
+ struct CkTextTabArray *tabArrayPtr;
+ /* Info about tabs for tag (malloc-ed)
+ * or NULL. Corresponds to tabString. */
+ Ck_Uid wrapMode; /* How to handle wrap-around for this tag.
+ * Must be ckTextCharUid, ckTextNoneUid,
+ * ckTextWordUid, or NULL to use wrapMode
+ * for whole widget. */
+ int affectsDisplay; /* Non-zero means that this tag affects the
+ * way information is displayed on the screen
+ * (so need to redisplay if tag changes). */
+} CkTextTag;
+
+#define TK_TAG_AFFECTS_DISPLAY 0x1
+#define TK_TAG_UNDERLINE 0x2
+#define TK_TAG_JUSTIFY 0x4
+#define TK_TAG_OFFSET 0x10
+
+/*
+ * The data structure below is used for searching a B-tree for transitions
+ * on a single tag (or for all tag transitions). No code outside of
+ * ckTextBTree.c should ever modify any of the fields in these structures,
+ * but it's OK to use them for read-only information.
+ */
+
+typedef struct CkTextSearch {
+ CkTextIndex curIndex; /* Position of last tag transition
+ * returned by CkBTreeNextTag, or
+ * index of start of segment
+ * containing starting position for
+ * search if CkBTreeNextTag hasn't
+ * been called yet, or same as
+ * stopIndex if search is over. */
+ CkTextSegment *segPtr; /* Actual tag segment returned by last
+ * call to CkBTreeNextTag, or NULL if
+ * CkBTreeNextTag hasn't returned
+ * anything yet. */
+ CkTextSegment *nextPtr; /* Where to resume search in next
+ * call to CkBTreeNextTag. */
+ CkTextSegment *lastPtr; /* Stop search before just before
+ * considering this segment. */
+ CkTextTag *tagPtr; /* Tag to search for (or tag found, if
+ * allTags is non-zero). */
+ int linesLeft; /* Lines left to search (including
+ * curIndex and stopIndex). When
+ * this becomes <= 0 the search is
+ * over. */
+ int allTags; /* Non-zero means ignore tag check:
+ * search for transitions on all
+ * tags. */
+} CkTextSearch;
+
+/*
+ * The following data structure describes a single tab stop.
+ */
+
+typedef enum {LEFT, RIGHT, CENTER, NUMERIC} CkTextTabAlign;
+
+typedef struct CkTextTab {
+ int location; /* Offset in pixels of this tab stop
+ * from the left margin (lmargin2) of
+ * the text. */
+ CkTextTabAlign alignment; /* Where the tab stop appears relative
+ * to the text. */
+} CkTextTab;
+
+typedef struct CkTextTabArray {
+ int numTabs; /* Number of tab stops. */
+ CkTextTab tabs[1]; /* Array of tabs. The actual size
+ * will be numTabs. THIS FIELD MUST
+ * BE THE LAST IN THE STRUCTURE. */
+} CkTextTabArray;
+
+/*
+ * A data structure of the following type is kept for each text widget that
+ * currently exists for this process:
+ */
+
+typedef struct CkText {
+ CkWindow *winPtr; /* Window that embodies the text. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for text's widget command. */
+ CkTextBTree tree; /* B-tree representation of text and tags for
+ * widget. */
+ Tcl_HashTable tagTable; /* Hash table that maps from tag names to
+ * pointers to CkTextTag structures. */
+ int numTags; /* Number of tags currently defined for
+ * widget; needed to keep track of
+ * priorities. */
+ Tcl_HashTable markTable; /* Hash table that maps from mark names to
+ * pointers to mark segments. */
+ Tcl_HashTable windowTable; /* Hash table that maps from window names
+ * to pointers to window segments. If a
+ * window segment doesn't yet have an
+ * associated window, there is no entry for
+ * it here. */
+ Ck_Uid state; /* Normal or disabled. Text is read-only
+ * when disabled. */
+
+ /*
+ * Default information for displaying (may be overridden by tags
+ * applied to ranges of characters).
+ */
+
+ int bg, fg, attr;
+ char *tabOptionString; /* Value of -tabs option string (malloc'ed). */
+ CkTextTabArray *tabArrayPtr;
+ /* Information about tab stops (malloc'ed).
+ * NULL means perform default tabbing
+ * behavior. */
+
+ /*
+ * Additional information used for displaying:
+ */
+
+ Ck_Uid wrapMode; /* How to handle wrap-around. Must be
+ * ckTextCharUid, ckTextNoneUid, or
+ * ckTextWordUid. */
+ int width, height; /* Desired dimensions for window, measured
+ * in characters. */
+ int prevWidth, prevHeight; /* Last known dimensions of window; used to
+ * detect changes in size. */
+ CkTextIndex topIndex; /* Identifies first character in top display
+ * line of window. */
+ struct DInfo *dInfoPtr; /* Information maintained by ckTextDisp.c. */
+
+ /*
+ * Information related to selection.
+ */
+
+ int selFg, selBg, selAttr;
+ CkTextTag *selTagPtr; /* Pointer to "sel" tag. Used to tell when
+ * a new selection has been made. */
+ CkTextIndex selIndex; /* Used during multi-pass selection retrievals.
+ * This index identifies the next character
+ * to be returned from the selection. */
+ int abortSelections; /* Set to 1 whenever the text is modified
+ * in a way that interferes with selection
+ * retrieval: used to abort incremental
+ * selection retrievals. */
+ int selOffset; /* Offset in selection corresponding to
+ * selLine and selCh. -1 means neither
+ * this information nor selIndex is of any
+ * use. */
+ int insertX, insertY; /* Window coordinates of HW cursor. */
+
+ /*
+ * Information related to insertion cursor:
+ */
+
+ CkTextSegment *insertMarkPtr;
+ /* Points to segment for "insert" mark. */
+
+ /*
+ * Information used for event bindings associated with tags:
+ */
+
+ Ck_BindingTable bindingTable;
+ /* Table of all bindings currently defined
+ * for this widget. NULL means that no
+ * bindings exist, so the table hasn't been
+ * created. Each "object" used for this
+ * table is the address of a tag. */
+ CkTextSegment *currentMarkPtr;
+ /* Pointer to segment for "current" mark,
+ * or NULL if none. */
+ CkEvent pickEvent; /* The event from which the current character
+ * was chosen. Must be saved so that we
+ * can repick after modifications to the
+ * text. */
+ int numCurTags; /* Number of tags associated with character
+ * at current mark. */
+ CkTextTag **curTagArrayPtr; /* Pointer to array of tags for current
+ * mark, or NULL if none. */
+
+ /*
+ * Miscellaneous additional information:
+ */
+
+ char *takeFocus; /* Value of -takeFocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *xScrollCmd; /* Prefix of command to issue to update
+ * horizontal scrollbar when view changes. */
+ char *yScrollCmd; /* Prefix of command to issue to update
+ * vertical scrollbar when view changes. */
+ int flags; /* Miscellaneous flags; see below for
+ * definitions. */
+} CkText;
+
+/*
+ * Flag values for CkText records:
+ *
+ * GOT_SELECTION: Non-zero means we've already claimed the
+ * selection.
+ * INSERT_ON: Non-zero means insertion cursor should be
+ * displayed on screen.
+ * GOT_FOCUS: Non-zero means this window has the input
+ * focus.
+ * UPDATE_SCROLLBARS: Non-zero means scrollbar(s) should be updated
+ * during next redisplay operation.
+ */
+
+#define GOT_SELECTION 1
+#define INSERT_ON 2
+#define GOT_FOCUS 4
+#define UPDATE_SCROLLBARS 0x10
+#define NEED_REPICK 0x20
+
+/*
+ * Records of the following type define segment types in terms of
+ * a collection of procedures that may be called to manipulate
+ * segments of that type.
+ */
+
+typedef CkTextSegment * Ck_SegSplitProc _ANSI_ARGS_((
+ struct CkTextSegment *segPtr, int index));
+typedef int Ck_SegDeleteProc _ANSI_ARGS_((
+ struct CkTextSegment *segPtr,
+ CkTextLine *linePtr, int treeGone));
+typedef CkTextSegment * Ck_SegCleanupProc _ANSI_ARGS_((
+ struct CkTextSegment *segPtr, CkTextLine *linePtr));
+typedef void Ck_SegLineChangeProc _ANSI_ARGS_((
+ struct CkTextSegment *segPtr, CkTextLine *linePtr));
+typedef int Ck_SegLayoutProc _ANSI_ARGS_((struct CkText *textPtr,
+ struct CkTextIndex *indexPtr, CkTextSegment *segPtr,
+ int offset, int maxX, int maxChars,
+ int noCharsYet, Ck_Uid wrapMode,
+ struct CkTextDispChunk *chunkPtr));
+typedef void Ck_SegCheckProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+
+typedef struct Ck_SegType {
+ char *name; /* Name of this kind of segment. */
+ int leftGravity; /* If a segment has zero size (e.g. a
+ * mark or tag toggle), does it
+ * attach to character to its left
+ * or right? 1 means left, 0 means
+ * right. */
+ Ck_SegSplitProc *splitProc; /* Procedure to split large segment
+ * into two smaller ones. */
+ Ck_SegDeleteProc *deleteProc; /* Procedure to call to delete
+ * segment. */
+ Ck_SegCleanupProc *cleanupProc; /* After any change to a line, this
+ * procedure is invoked for all
+ * segments left in the line to
+ * perform any cleanup they wish
+ * (e.g. joining neighboring
+ * segments). */
+ Ck_SegLineChangeProc *lineChangeProc;
+ /* Invoked when a segment is about
+ * to be moved from its current line
+ * to an earlier line because of
+ * a deletion. The linePtr is that
+ * for the segment's old line.
+ * CleanupProc will be invoked after
+ * the deletion is finished. */
+ Ck_SegLayoutProc *layoutProc; /* Returns size information when
+ * figuring out what to display in
+ * window. */
+ Ck_SegCheckProc *checkProc; /* Called during consistency checks
+ * to check internal consistency of
+ * segment. */
+} Ck_SegType;
+
+/*
+ * The constant below is used to specify a line when what is really
+ * wanted is the entire text. For now, just use a very big number.
+ */
+
+#define TK_END_OF_TEXT 1000000
+
+/*
+ * The following definition specifies the maximum number of characters
+ * needed in a string to hold a position specifier.
+ */
+
+#define TK_POS_CHARS 30
+
+/*
+ * Declarations for variables shared among the text-related files:
+ */
+
+extern int ckBTreeDebug;
+extern int ckTextDebug;
+extern Ck_SegType ckTextCharType;
+extern Ck_Uid ckTextCharUid;
+extern Ck_Uid ckTextDisabledUid;
+extern Ck_SegType ckTextLeftMarkType;
+extern Ck_Uid ckTextNoneUid;
+extern Ck_Uid ckTextNormalUid;
+extern Ck_SegType ckTextRightMarkType;
+extern Ck_SegType ckTextToggleOnType;
+extern Ck_SegType ckTextToggleOffType;
+extern Ck_Uid ckTextWordUid;
+
+/*
+ * Declarations for procedures that are used by the text-related files
+ * but shouldn't be used anywhere else in Ck (or by Ck clients):
+ */
+
+extern int CkBTreeCharTagged _ANSI_ARGS_((CkTextIndex *indexPtr,
+ CkTextTag *tagPtr));
+extern void CkBTreeCheck _ANSI_ARGS_((CkTextBTree tree));
+extern int CkBTreeCharsInLine _ANSI_ARGS_((CkTextLine *linePtr));
+extern CkTextBTree CkBTreeCreate _ANSI_ARGS_((void));
+extern void CkBTreeDestroy _ANSI_ARGS_((CkTextBTree tree));
+extern void CkBTreeDeleteChars _ANSI_ARGS_((CkTextIndex *index1Ptr,
+ CkTextIndex *index2Ptr));
+extern CkTextLine * CkBTreeFindLine _ANSI_ARGS_((CkTextBTree tree,
+ int line));
+extern CkTextTag ** CkBTreeGetTags _ANSI_ARGS_((CkTextIndex *indexPtr,
+ int *numTagsPtr));
+extern void CkBTreeInsertChars _ANSI_ARGS_((CkTextIndex *indexPtr,
+ char *string));
+extern int CkBTreeLineIndex _ANSI_ARGS_((CkTextLine *linePtr));
+extern void CkBTreeLinkSegment _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextIndex *indexPtr));
+extern CkTextLine * CkBTreeNextLine _ANSI_ARGS_((CkTextLine *linePtr));
+extern int CkBTreeNextTag _ANSI_ARGS_((CkTextSearch *searchPtr));
+extern int CkBTreeNumLines _ANSI_ARGS_((CkTextBTree tree));
+extern void CkBTreeStartSearch _ANSI_ARGS_((CkTextIndex *index1Ptr,
+ CkTextIndex *index2Ptr, CkTextTag *tagPtr,
+ CkTextSearch *searchPtr));
+extern void CkBTreeTag _ANSI_ARGS_((CkTextIndex *index1Ptr,
+ CkTextIndex *index2Ptr, CkTextTag *tagPtr,
+ int add));
+extern void CkBTreeUnlinkSegment _ANSI_ARGS_((CkTextBTree tree,
+ CkTextSegment *segPtr, CkTextLine *linePtr));
+extern void CkTextBindProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+extern void CkTextChanged _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *index1Ptr, CkTextIndex *index2Ptr));
+extern int CkTextCharBbox _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, int *xPtr, int *yPtr,
+ int *widthPtr, int *heightPtr));
+extern int CkTextCharLayoutProc _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, CkTextSegment *segPtr,
+ int offset, int maxX, int maxChars, int noBreakYet,
+ Ck_Uid wrapMode, CkTextDispChunk *chunkPtr));
+extern void CkTextCreateDInfo _ANSI_ARGS_((CkText *textPtr));
+extern int CkTextDLineInfo _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, int *xPtr, int *yPtr,
+ int *widthPtr, int *heightPtr, int *basePtr));
+extern CkTextTag * CkTextCreateTag _ANSI_ARGS_((CkText *textPtr,
+ char *tagName));
+extern void CkTextFreeDInfo _ANSI_ARGS_((CkText *textPtr));
+extern void CkTextFreeTag _ANSI_ARGS_((CkText *textPtr,
+ CkTextTag *tagPtr));
+extern int CkTextGetIndex _ANSI_ARGS_((Tcl_Interp *interp,
+ CkText *textPtr, char *string,
+ CkTextIndex *indexPtr));
+extern CkTextTabArray * CkTextGetTabs _ANSI_ARGS_((Tcl_Interp *interp,
+ CkWindow *winPtr, char *string));
+#if CK_USE_UTF
+extern void CkTextIndexBackBytes _ANSI_ARGS_((CkTextIndex *srcPtr,
+ int count, CkTextIndex *dstPtr));
+#endif
+extern void CkTextIndexBackChars _ANSI_ARGS_((CkTextIndex *srcPtr,
+ int count, CkTextIndex *dstPtr));
+extern int CkTextIndexCmp _ANSI_ARGS_((CkTextIndex *index1Ptr,
+ CkTextIndex *index2Ptr));
+#if CK_USE_UTF
+extern void CkTextIndexForwBytes _ANSI_ARGS_((CkTextIndex *srcPtr,
+ int count, CkTextIndex *dstPtr));
+#endif
+extern void CkTextIndexForwChars _ANSI_ARGS_((CkTextIndex *srcPtr,
+ int count, CkTextIndex *dstPtr));
+extern CkTextSegment * CkTextIndexToSeg _ANSI_ARGS_((CkTextIndex *indexPtr,
+ int *offsetPtr));
+extern void CkTextInsertDisplayProc _ANSI_ARGS_((
+ CkTextDispChunk *chunkPtr, int x, int y, int height,
+ int baseline, WINDOW *window, int screenY));
+extern void CkTextLostSelection _ANSI_ARGS_((
+ ClientData clientData));
+#if CK_USE_UTF
+extern CkTextIndex * CkTextMakeByteIndex _ANSI_ARGS_((CkTextBTree tree,
+ int lineIndex, int byteIndex,
+ CkTextIndex *indexPtr));
+#endif
+extern CkTextIndex * CkTextMakeIndex _ANSI_ARGS_((CkTextBTree tree,
+ int lineIndex, int charIndex,
+ CkTextIndex *indexPtr));
+extern int CkTextMarkCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextMarkNameToIndex _ANSI_ARGS_((CkText *textPtr,
+ char *name, CkTextIndex *indexPtr));
+extern void CkTextMarkSegToIndex _ANSI_ARGS_((CkText *textPtr,
+ CkTextSegment *markPtr, CkTextIndex *indexPtr));
+extern void CkTextEventuallyRepick _ANSI_ARGS_((CkText *textPtr));
+extern void CkTextPickCurrent _ANSI_ARGS_((CkText *textPtr,
+ CkEvent *eventPtr));
+extern void CkTextPixelIndex _ANSI_ARGS_((CkText *textPtr,
+ int x, int y, CkTextIndex *indexPtr));
+extern void CkTextPrintIndex _ANSI_ARGS_((CkTextIndex *indexPtr,
+ char *string));
+extern void CkTextRedrawRegion _ANSI_ARGS_((CkText *textPtr,
+ int x, int y, int width, int height));
+extern void CkTextRedrawTag _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *index1Ptr, CkTextIndex *index2Ptr,
+ CkTextTag *tagPtr, int withTag));
+extern void CkTextRelayoutWindow _ANSI_ARGS_((CkText *textPtr));
+extern int CkTextScanCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextSeeCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextSegToOffset _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+extern CkTextSegment * CkTextSetMark _ANSI_ARGS_((CkText *textPtr, char *name,
+ CkTextIndex *indexPtr));
+extern void CkTextSetYView _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, int pickPlace));
+extern int CkTextTagCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextWindowCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextWindowIndex _ANSI_ARGS_((CkText *textPtr,
+ char *name, CkTextIndex *indexPtr));
+extern int CkTextXviewCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+extern int CkTextYviewCmd _ANSI_ARGS_((CkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
+
+#endif /* _CKTEXT_H */
--- /dev/null
+/*
+ * ckTextBTree.c --
+ *
+ * This file contains code that manages the B-tree representation
+ * of text for Ck's text widget and implements character and
+ * toggle segment types.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+
+/*
+ * The data structure below keeps summary information about one tag as part
+ * of the tag information in a node.
+ */
+
+typedef struct Summary {
+ CkTextTag *tagPtr; /* Handle for tag. */
+ int toggleCount; /* Number of transitions into or
+ * out of this tag that occur in
+ * the subtree rooted at this node. */
+ struct Summary *nextPtr; /* Next in list of all tags for same
+ * node, or NULL if at end of list. */
+} Summary;
+
+/*
+ * The data structure below defines a node in the B-tree.
+ */
+
+typedef struct Node {
+ struct Node *parentPtr; /* Pointer to parent node, or NULL if
+ * this is the root. */
+ struct Node *nextPtr; /* Next in list of siblings with the
+ * same parent node, or NULL for end
+ * of list. */
+ Summary *summaryPtr; /* First in malloc-ed list of info
+ * about tags in this subtree (NULL if
+ * no tag info in the subtree). */
+ int level; /* Level of this node in the B-tree.
+ * 0 refers to the bottom of the tree
+ * (children are lines, not nodes). */
+ union { /* First in linked list of children. */
+ struct Node *nodePtr; /* Used if level > 0. */
+ CkTextLine *linePtr; /* Used if level == 0. */
+ } children;
+ int numChildren; /* Number of children of this node. */
+ int numLines; /* Total number of lines (leaves) in
+ * the subtree rooted here. */
+} Node;
+
+/*
+ * Upper and lower bounds on how many children a node may have:
+ * rebalance when either of these limits is exceeded. MAX_CHILDREN
+ * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
+ */
+
+#define MAX_CHILDREN 12
+#define MIN_CHILDREN 6
+
+/*
+ * The data structure below defines an entire B-tree.
+ */
+
+typedef struct BTree {
+ Node *rootPtr; /* Pointer to root of B-tree. */
+} BTree;
+
+/*
+ * The structure below is used to pass information between
+ * CkBTreeGetTags and IncCount:
+ */
+
+typedef struct TagInfo {
+ int numTags; /* Number of tags for which there
+ * is currently information in
+ * tags and counts. */
+ int arraySize; /* Number of entries allocated for
+ * tags and counts. */
+ CkTextTag **tagPtrs; /* Array of tags seen so far.
+ * Malloc-ed. */
+ int *counts; /* Toggle count (so far) for each
+ * entry in tags. Malloc-ed. */
+} TagInfo;
+
+/*
+ * Variable that indicates whether to enable consistency checks for
+ * debugging.
+ */
+
+int ckBTreeDebug = 0;
+
+/*
+ * Macros that determine how much space to allocate for new segments:
+ */
+
+#define CSEG_SIZE(chars) ((unsigned) (Ck_Offset(CkTextSegment, body) \
+ + 1 + (chars)))
+#define TSEG_SIZE ((unsigned) (Ck_Offset(CkTextSegment, body) \
+ + sizeof(CkTextToggle)))
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static void ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
+ CkTextTag *tagPtr, int delta));
+static void CharCheckProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static int CharDeleteProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr, int treeGone));
+static CkTextSegment * CharCleanupProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static CkTextSegment * CharSplitProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ int index));
+static void CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
+static void CleanupLine _ANSI_ARGS_((CkTextLine *linePtr));
+static void DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
+static void DestroyNode _ANSI_ARGS_((Node *nodePtr));
+static void IncCount _ANSI_ARGS_((CkTextTag *tagPtr, int inc,
+ TagInfo *tagInfoPtr));
+static void Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
+static void RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
+static CkTextSegment * SplitSeg _ANSI_ARGS_((CkTextIndex *indexPtr));
+static void ToggleCheckProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static CkTextSegment * ToggleCleanupProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static int ToggleDeleteProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr, int treeGone));
+static void ToggleLineChangeProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+
+/*
+ * Type record for character segments:
+ */
+
+Ck_SegType ckTextCharType = {
+ "character", /* name */
+ 0, /* leftGravity */
+ CharSplitProc, /* splitProc */
+ CharDeleteProc, /* deleteProc */
+ CharCleanupProc, /* cleanupProc */
+ (Ck_SegLineChangeProc *) NULL, /* lineChangeProc */
+ CkTextCharLayoutProc, /* layoutProc */
+ CharCheckProc /* checkProc */
+};
+
+/*
+ * Type record for segments marking the beginning of a tagged
+ * range:
+ */
+
+Ck_SegType ckTextToggleOnType = {
+ "toggleOn", /* name */
+ 0, /* leftGravity */
+ (Ck_SegSplitProc *) NULL, /* splitProc */
+ ToggleDeleteProc, /* deleteProc */
+ ToggleCleanupProc, /* cleanupProc */
+ ToggleLineChangeProc, /* lineChangeProc */
+ (Ck_SegLayoutProc *) NULL, /* layoutProc */
+ ToggleCheckProc /* checkProc */
+};
+
+/*
+ * Type record for segments marking the end of a tagged
+ * range:
+ */
+
+Ck_SegType ckTextToggleOffType = {
+ "toggleOff", /* name */
+ 1, /* leftGravity */
+ (Ck_SegSplitProc *) NULL, /* splitProc */
+ ToggleDeleteProc, /* deleteProc */
+ ToggleCleanupProc, /* cleanupProc */
+ ToggleLineChangeProc, /* lineChangeProc */
+ (Ck_SegLayoutProc *) NULL, /* layoutProc */
+ ToggleCheckProc /* checkProc */
+};
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeCreate --
+ *
+ * This procedure is called to create a new text B-tree.
+ *
+ * Results:
+ * The return value is a pointer to a new B-tree containing
+ * one line with nothing but a newline character.
+ *
+ * Side effects:
+ * Memory is allocated and initialized.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextBTree
+CkBTreeCreate()
+{
+ register BTree *treePtr;
+ register Node *rootPtr;
+ register CkTextLine *linePtr, *linePtr2;
+ register CkTextSegment *segPtr;
+
+ /*
+ * The tree will initially have two empty lines. The second line
+ * isn't actually part of the tree's contents, but its presence
+ * makes several operations easier. The tree will have one node,
+ * which is also the root of the tree.
+ */
+
+ rootPtr = (Node *) ckalloc(sizeof(Node));
+ linePtr = (CkTextLine *) ckalloc(sizeof(CkTextLine));
+ linePtr2 = (CkTextLine *) ckalloc(sizeof(CkTextLine));
+ rootPtr->parentPtr = NULL;
+ rootPtr->nextPtr = NULL;
+ rootPtr->summaryPtr = NULL;
+ rootPtr->level = 0;
+ rootPtr->children.linePtr = linePtr;
+ rootPtr->numChildren = 2;
+ rootPtr->numLines = 2;
+
+ linePtr->parentPtr = rootPtr;
+ linePtr->nextPtr = linePtr2;
+ segPtr = (CkTextSegment *) ckalloc(CSEG_SIZE(1));
+ linePtr->segPtr = segPtr;
+ segPtr->typePtr = &ckTextCharType;
+ segPtr->nextPtr = NULL;
+ segPtr->size = 1;
+ segPtr->body.chars[0] = '\n';
+ segPtr->body.chars[1] = 0;
+
+ linePtr2->parentPtr = rootPtr;
+ linePtr2->nextPtr = NULL;
+ segPtr = (CkTextSegment *) ckalloc(CSEG_SIZE(1));
+ linePtr2->segPtr = segPtr;
+ segPtr->typePtr = &ckTextCharType;
+ segPtr->nextPtr = NULL;
+ segPtr->size = 1;
+ segPtr->body.chars[0] = '\n';
+ segPtr->body.chars[1] = 0;
+
+ treePtr = (BTree *) ckalloc(sizeof(BTree));
+ treePtr->rootPtr = rootPtr;
+
+ return (CkTextBTree) treePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeDestroy --
+ *
+ * Delete a B-tree, recycling all of the storage it contains.
+ *
+ * Results:
+ * The tree given by treePtr is deleted. TreePtr should never
+ * again be used.
+ *
+ * Side effects:
+ * Memory is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeDestroy(tree)
+ CkTextBTree tree; /* Pointer to tree to delete. */
+{
+ BTree *treePtr = (BTree *) tree;
+
+ DestroyNode(treePtr->rootPtr);
+ ckfree((char *) treePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyNode --
+ *
+ * This is a recursive utility procedure used during the deletion
+ * of a B-tree.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All the storage for nodePtr and its descendants is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyNode(nodePtr)
+ register Node *nodePtr;
+{
+ if (nodePtr->level == 0) {
+ CkTextLine *linePtr;
+ CkTextSegment *segPtr;
+
+ while (nodePtr->children.linePtr != NULL) {
+ linePtr = nodePtr->children.linePtr;
+ nodePtr->children.linePtr = linePtr->nextPtr;
+ while (linePtr->segPtr != NULL) {
+ segPtr = linePtr->segPtr;
+ linePtr->segPtr = segPtr->nextPtr;
+ (*segPtr->typePtr->deleteProc)(segPtr, linePtr, 1);
+ }
+ ckfree((char *) linePtr);
+ }
+ } else {
+ register Node *childPtr;
+
+ while (nodePtr->children.nodePtr != NULL) {
+ childPtr = nodePtr->children.nodePtr;
+ nodePtr->children.nodePtr = childPtr->nextPtr;
+ DestroyNode(childPtr);
+ }
+ }
+ DeleteSummaries(nodePtr->summaryPtr);
+ ckfree((char *) nodePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteSummaries --
+ *
+ * Free up all of the memory in a list of tag summaries associated
+ * with a node.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Storage is released.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteSummaries(summaryPtr)
+ register Summary *summaryPtr; /* First in list of node's tag
+ * summaries. */
+{
+ register Summary *nextPtr;
+ while (summaryPtr != NULL) {
+ nextPtr = summaryPtr->nextPtr;
+ ckfree((char *) summaryPtr);
+ summaryPtr = nextPtr;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeInsertChars --
+ *
+ * Insert characters at a given position in a B-tree.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Characters are added to the B-tree at the given position.
+ * If the string contains newlines, new lines will be added,
+ * which could cause the structure of the B-tree to change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeInsertChars(indexPtr, string)
+ register CkTextIndex *indexPtr; /* Indicates where to insert text.
+ * When the procedure returns, this
+ * index is no longer valid because
+ * of changes to the segment
+ * structure. */
+ char *string; /* Pointer to bytes to insert (may
+ * contain newlines, must be null-
+ * terminated). */
+{
+ register Node *nodePtr;
+ register CkTextSegment *prevPtr; /* The segment just before the first
+ * new segment (NULL means new segment
+ * is at beginning of line). */
+ CkTextSegment *curPtr; /* Current segment; new characters
+ * are inserted just after this one.
+ * NULL means insert at beginning of
+ * line. */
+ CkTextLine *linePtr; /* Current line (new segments are
+ * added to this line). */
+ register CkTextSegment *segPtr;
+ CkTextLine *newLinePtr;
+ int chunkSize; /* # characters in current chunk. */
+ register char *eol; /* Pointer to character just after last
+ * one in current chunk. */
+ int changeToLineCount; /* Counts change to total number of
+ * lines in file. */
+
+ prevPtr = SplitSeg(indexPtr);
+ linePtr = indexPtr->linePtr;
+ curPtr = prevPtr;
+
+ /*
+ * Chop the string up into lines and create a new segment for
+ * each line, plus a new line for the leftovers from the
+ * previous line.
+ */
+
+ changeToLineCount = 0;
+ while (*string != 0) {
+ for (eol = string; *eol != 0; eol++) {
+ if (*eol == '\n') {
+ eol++;
+ break;
+ }
+ }
+ chunkSize = eol-string;
+ segPtr = (CkTextSegment *) ckalloc(CSEG_SIZE(chunkSize));
+ segPtr->typePtr = &ckTextCharType;
+ if (curPtr == NULL) {
+ segPtr->nextPtr = linePtr->segPtr;
+ linePtr->segPtr = segPtr;
+ } else {
+ segPtr->nextPtr = curPtr->nextPtr;
+ curPtr->nextPtr = segPtr;
+ }
+ segPtr->size = chunkSize;
+ strncpy(segPtr->body.chars, string, (size_t) chunkSize);
+ segPtr->body.chars[chunkSize] = 0;
+ curPtr = segPtr;
+
+ if (eol[-1] != '\n') {
+ break;
+ }
+
+ /*
+ * The chunk ended with a newline, so create a new CkTextLine
+ * and move the remainder of the old line to it.
+ */
+
+ newLinePtr = (CkTextLine *) ckalloc(sizeof(CkTextLine));
+ newLinePtr->parentPtr = linePtr->parentPtr;
+ newLinePtr->nextPtr = linePtr->nextPtr;
+ linePtr->nextPtr = newLinePtr;
+ newLinePtr->segPtr = segPtr->nextPtr;
+ segPtr->nextPtr = NULL;
+ linePtr = newLinePtr;
+ curPtr = NULL;
+ changeToLineCount++;
+
+ string = eol;
+ }
+
+ /*
+ * Cleanup the starting line for the insertion, plus the ending
+ * line if it's different.
+ */
+
+ CleanupLine(indexPtr->linePtr);
+ if (linePtr != indexPtr->linePtr) {
+ CleanupLine(linePtr);
+ }
+
+ /*
+ * Increment the line counts in all the parent nodes of the insertion
+ * point, then rebalance the tree if necessary.
+ */
+
+ for (nodePtr = linePtr->parentPtr ; nodePtr != NULL;
+ nodePtr = nodePtr->parentPtr) {
+ nodePtr->numLines += changeToLineCount;
+ }
+ nodePtr = linePtr->parentPtr;
+ nodePtr->numChildren += changeToLineCount;
+ if (nodePtr->numChildren > MAX_CHILDREN) {
+ Rebalance((BTree *) indexPtr->tree, nodePtr);
+ }
+
+ if (ckBTreeDebug) {
+ CkBTreeCheck(indexPtr->tree);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * SplitSeg --
+ *
+ * This procedure is called before adding or deleting
+ * segments. It does three things: (a) it finds the segment
+ * containing indexPtr; (b) if there are several such
+ * segments (because some segments have zero length) then
+ * it picks the first segment that does not have left
+ * gravity; (c) if the index refers to the middle of
+ * a segment then it splits the segment so that the
+ * index now refers to the beginning of a segment.
+ *
+ * Results:
+ * The return value is a pointer to the segment just
+ * before the segment corresponding to indexPtr (as
+ * described above). If the segment corresponding to
+ * indexPtr is the first in its line then the return
+ * value is NULL.
+ *
+ * Side effects:
+ * The segment referred to by indexPtr is split unless
+ * indexPtr refers to its first character.
+ *
+ *--------------------------------------------------------------
+ */
+
+static CkTextSegment *
+SplitSeg(indexPtr)
+ CkTextIndex *indexPtr; /* Index identifying position
+ * at which to split a segment. */
+{
+ CkTextSegment *prevPtr, *segPtr;
+ int count;
+
+ for (count = indexPtr->charIndex, prevPtr = NULL,
+ segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
+ count -= segPtr->size, prevPtr = segPtr, segPtr = segPtr->nextPtr) {
+ if (segPtr->size > count) {
+ if (count == 0) {
+ return prevPtr;
+ }
+ segPtr = (*segPtr->typePtr->splitProc)(segPtr, count);
+ if (prevPtr == NULL) {
+ indexPtr->linePtr->segPtr = segPtr;
+ } else {
+ prevPtr->nextPtr = segPtr;
+ }
+ return segPtr;
+ } else if ((segPtr->size == 0) && (count == 0)
+ && !segPtr->typePtr->leftGravity) {
+ return prevPtr;
+ }
+ }
+ panic("SplitSeg reached end of line!");
+ return NULL;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CleanupLine --
+ *
+ * This procedure is called after modifications have been
+ * made to a line. It scans over all of the segments in
+ * the line, giving each a chance to clean itself up, e.g.
+ * by merging with the following segments, updating internal
+ * information, etc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on what the segment-specific cleanup procedures do.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+CleanupLine(linePtr)
+ CkTextLine *linePtr; /* Line to be cleaned up. */
+{
+ CkTextSegment *segPtr, **prevPtrPtr;
+ int anyChanges;
+
+ /*
+ * Make a pass over all of the segments in the line, giving each
+ * a chance to clean itself up. This could potentially change
+ * the structure of the line, e.g. by merging two segments
+ * together or having two segments cancel themselves; if so,
+ * then repeat the whole process again, since the first structure
+ * change might make other structure changes possible. Repeat
+ * until eventually there are no changes.
+ */
+
+ while (1) {
+ anyChanges = 0;
+ for (prevPtrPtr = &linePtr->segPtr, segPtr = *prevPtrPtr;
+ segPtr != NULL;
+ prevPtrPtr = &(*prevPtrPtr)->nextPtr, segPtr = *prevPtrPtr) {
+ if (segPtr->typePtr->cleanupProc != NULL) {
+ *prevPtrPtr = (*segPtr->typePtr->cleanupProc)(segPtr, linePtr);
+ if (segPtr != *prevPtrPtr) {
+ anyChanges = 1;
+ }
+ }
+ }
+ if (!anyChanges) {
+ break;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeDeleteChars --
+ *
+ * Delete a range of characters from a B-tree. The caller
+ * must make sure that the final newline of the B-tree is
+ * never deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information is deleted from the B-tree. This can cause the
+ * internal structure of the B-tree to change. Note: because
+ * of changes to the B-tree structure, the indices pointed
+ * to by index1Ptr and index2Ptr should not be used after this
+ * procedure returns.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeDeleteChars(index1Ptr, index2Ptr)
+ register CkTextIndex *index1Ptr; /* Indicates first character that is
+ * to be deleted. */
+ register CkTextIndex *index2Ptr; /* Indicates character just after the
+ * last one that is to be deleted. */
+{
+ CkTextSegment *prevPtr; /* The segment just before the start
+ * of the deletion range. */
+ CkTextSegment *lastPtr; /* The segment just after the end
+ * of the deletion range. */
+ CkTextSegment *segPtr, *nextPtr;
+ CkTextLine *curLinePtr;
+ Node *curNodePtr, *nodePtr;
+
+ /*
+ * Tricky point: split at index2Ptr first; otherwise the split
+ * at index2Ptr may invalidate segPtr and/or prevPtr.
+ */
+
+ lastPtr = SplitSeg(index2Ptr);
+ if (lastPtr != NULL) {
+ lastPtr = lastPtr->nextPtr;
+ } else {
+ lastPtr = index2Ptr->linePtr->segPtr;
+ }
+ prevPtr = SplitSeg(index1Ptr);
+ if (prevPtr != NULL) {
+ segPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = lastPtr;
+ } else {
+ segPtr = index1Ptr->linePtr->segPtr;
+ index1Ptr->linePtr->segPtr = lastPtr;
+ }
+
+ /*
+ * Delete all of the segments between prevPtr and lastPtr.
+ */
+
+ curLinePtr = index1Ptr->linePtr;
+ curNodePtr = curLinePtr->parentPtr;
+ while (segPtr != lastPtr) {
+ if (segPtr == NULL) {
+ CkTextLine *nextLinePtr;
+
+ /*
+ * We just ran off the end of a line. First find the
+ * next line, then go back to the old line and delete it
+ * (unless it's the starting line for the range).
+ */
+
+ nextLinePtr = CkBTreeNextLine(curLinePtr);
+ if (curLinePtr != index1Ptr->linePtr) {
+ if (curNodePtr == index1Ptr->linePtr->parentPtr) {
+ index1Ptr->linePtr->nextPtr = curLinePtr->nextPtr;
+ } else {
+ curNodePtr->children.linePtr = curLinePtr->nextPtr;
+ }
+ for (nodePtr = curNodePtr; nodePtr != NULL;
+ nodePtr = nodePtr->parentPtr) {
+ nodePtr->numLines--;
+ }
+ curNodePtr->numChildren--;
+ ckfree((char *) curLinePtr);
+ }
+ curLinePtr = nextLinePtr;
+ segPtr = curLinePtr->segPtr;
+
+ /*
+ * If the node is empty then delete it and its parents,
+ * recursively upwards until a non-empty node is found.
+ */
+
+ while (curNodePtr->numChildren == 0) {
+ Node *parentPtr;
+
+ parentPtr = curNodePtr->parentPtr;
+ if (parentPtr->children.nodePtr == curNodePtr) {
+ parentPtr->children.nodePtr = curNodePtr->nextPtr;
+ } else {
+ Node *prevNodePtr = parentPtr->children.nodePtr;
+ while (prevNodePtr->nextPtr != curNodePtr) {
+ prevNodePtr = prevNodePtr->nextPtr;
+ }
+ prevNodePtr->nextPtr = curNodePtr->nextPtr;
+ }
+ parentPtr->numChildren--;
+ ckfree((char *) curNodePtr);
+ curNodePtr = parentPtr;
+ }
+ curNodePtr = curLinePtr->parentPtr;
+ continue;
+ }
+
+ nextPtr = segPtr->nextPtr;
+ if ((*segPtr->typePtr->deleteProc)(segPtr, curLinePtr, 0) != 0) {
+ /*
+ * This segment refuses to die. Move it to prevPtr and
+ * advance prevPtr if the segment has left gravity.
+ */
+
+ if (prevPtr == NULL) {
+ segPtr->nextPtr = index1Ptr->linePtr->segPtr;
+ index1Ptr->linePtr->segPtr = segPtr;
+ } else {
+ segPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = segPtr;
+ }
+ if (segPtr->typePtr->leftGravity) {
+ prevPtr = segPtr;
+ }
+ }
+ segPtr = nextPtr;
+ }
+
+ /*
+ * If the beginning and end of the deletion range are in different
+ * lines, join the two lines together and discard the ending line.
+ */
+
+ if (index1Ptr->linePtr != index2Ptr->linePtr) {
+ CkTextLine *prevLinePtr;
+
+ for (segPtr = lastPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr->lineChangeProc != NULL) {
+ (*segPtr->typePtr->lineChangeProc)(segPtr, index2Ptr->linePtr);
+ }
+ }
+ curNodePtr = index2Ptr->linePtr->parentPtr;
+ for (nodePtr = curNodePtr; nodePtr != NULL;
+ nodePtr = nodePtr->parentPtr) {
+ nodePtr->numLines--;
+ }
+ curNodePtr->numChildren--;
+ prevLinePtr = curNodePtr->children.linePtr;
+ if (prevLinePtr == index2Ptr->linePtr) {
+ curNodePtr->children.linePtr = index2Ptr->linePtr->nextPtr;
+ } else {
+ while (prevLinePtr->nextPtr != index2Ptr->linePtr) {
+ prevLinePtr = prevLinePtr->nextPtr;
+ }
+ prevLinePtr->nextPtr = index2Ptr->linePtr->nextPtr;
+ }
+ ckfree((char *) index2Ptr->linePtr);
+ Rebalance((BTree *) index2Ptr->tree, curNodePtr);
+ }
+
+ /*
+ * Cleanup the segments in the new line.
+ */
+
+ CleanupLine(index1Ptr->linePtr);
+
+ /*
+ * Lastly, rebalance the first node of the range.
+ */
+
+ Rebalance((BTree *) index1Ptr->tree, index1Ptr->linePtr->parentPtr);
+ if (ckBTreeDebug) {
+ CkBTreeCheck(index1Ptr->tree);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeFindLine --
+ *
+ * Find a particular line in a B-tree based on its line number.
+ *
+ * Results:
+ * The return value is a pointer to the line structure for the
+ * line whose index is "line", or NULL if no such line exists.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextLine *
+CkBTreeFindLine(tree, line)
+ CkTextBTree tree; /* B-tree in which to find line. */
+ int line; /* Index of desired line. */
+{
+ BTree *treePtr = (BTree *) tree;
+ register Node *nodePtr;
+ register CkTextLine *linePtr;
+ int linesLeft;
+
+ nodePtr = treePtr->rootPtr;
+ linesLeft = line;
+ if ((line < 0) || (line >= nodePtr->numLines)) {
+ return NULL;
+ }
+
+ /*
+ * Work down through levels of the tree until a node is found at
+ * level 0.
+ */
+
+ while (nodePtr->level != 0) {
+ for (nodePtr = nodePtr->children.nodePtr;
+ nodePtr->numLines <= linesLeft;
+ nodePtr = nodePtr->nextPtr) {
+ if (nodePtr == NULL) {
+ panic("CkBTreeFindLine ran out of nodes");
+ }
+ linesLeft -= nodePtr->numLines;
+ }
+ }
+
+ /*
+ * Work through the lines attached to the level-0 node.
+ */
+
+ for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
+ linePtr = linePtr->nextPtr) {
+ if (linePtr == NULL) {
+ panic("CkBTreeFindLine ran out of lines");
+ }
+ linesLeft -= 1;
+ }
+ return linePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeNextLine --
+ *
+ * Given an existing line in a B-tree, this procedure locates the
+ * next line in the B-tree. This procedure is used for scanning
+ * through the B-tree.
+ *
+ * Results:
+ * The return value is a pointer to the line that immediately
+ * follows linePtr, or NULL if there is no such line.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextLine *
+CkBTreeNextLine(linePtr)
+ register CkTextLine *linePtr; /* Pointer to existing line in
+ * B-tree. */
+{
+ register Node *nodePtr;
+
+ if (linePtr->nextPtr != NULL) {
+ return linePtr->nextPtr;
+ }
+
+ /*
+ * This was the last line associated with the particular parent node.
+ * Search up the tree for the next node, then search down from that
+ * node to find the first line,
+ */
+
+ for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
+ if (nodePtr->nextPtr != NULL) {
+ nodePtr = nodePtr->nextPtr;
+ break;
+ }
+ if (nodePtr->parentPtr == NULL) {
+ return (CkTextLine *) NULL;
+ }
+ }
+ while (nodePtr->level > 0) {
+ nodePtr = nodePtr->children.nodePtr;
+ }
+ return nodePtr->children.linePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeLineIndex --
+ *
+ * Given a pointer to a line in a B-tree, return the numerical
+ * index of that line.
+ *
+ * Results:
+ * The result is the index of linePtr within the tree, where 0
+ * corresponds to the first line in the tree.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkBTreeLineIndex(linePtr)
+ CkTextLine *linePtr; /* Pointer to existing line in
+ * B-tree. */
+{
+ register CkTextLine *linePtr2;
+ register Node *nodePtr, *parentPtr, *nodePtr2;
+ int index;
+
+ /*
+ * First count how many lines precede this one in its level-0
+ * node.
+ */
+
+ nodePtr = linePtr->parentPtr;
+ index = 0;
+ for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
+ linePtr2 = linePtr2->nextPtr) {
+ if (linePtr2 == NULL) {
+ panic("CkBTreeLineIndex couldn't find line");
+ }
+ index += 1;
+ }
+
+ /*
+ * Now work up through the levels of the tree one at a time,
+ * counting how many lines are in nodes preceding the current
+ * node.
+ */
+
+ for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
+ nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
+ for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
+ nodePtr2 = nodePtr2->nextPtr) {
+ if (nodePtr2 == NULL) {
+ panic("CkBTreeLineIndex couldn't find node");
+ }
+ index += nodePtr2->numLines;
+ }
+ }
+ return index;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeLinkSegment --
+ *
+ * This procedure adds a new segment to a B-tree at a given
+ * location.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * SegPtr will be linked into its tree.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+CkBTreeLinkSegment(segPtr, indexPtr)
+ CkTextSegment *segPtr; /* Pointer to new segment to be added to
+ * B-tree. Should be completely initialized
+ * by caller except for nextPtr field. */
+ CkTextIndex *indexPtr; /* Where to add segment: it gets linked
+ * in just before the segment indicated
+ * here. */
+{
+ register CkTextSegment *prevPtr;
+
+ prevPtr = SplitSeg(indexPtr);
+ if (prevPtr == NULL) {
+ segPtr->nextPtr = indexPtr->linePtr->segPtr;
+ indexPtr->linePtr->segPtr = segPtr;
+ } else {
+ segPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = segPtr;
+ }
+ CleanupLine(indexPtr->linePtr);
+ if (ckBTreeDebug) {
+ CkBTreeCheck(indexPtr->tree);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeUnlinkSegment --
+ *
+ * This procedure unlinks a segment from its line in a B-tree.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * SegPtr will be unlinked from linePtr. The segment itself
+ * isn't modified by this procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+CkBTreeUnlinkSegment(tree, segPtr, linePtr)
+ CkTextBTree tree; /* Tree containing segment. */
+ CkTextSegment *segPtr; /* Segment to be unlinked. */
+ CkTextLine *linePtr; /* Line that currently contains
+ * segment. */
+{
+ register CkTextSegment *prevPtr;
+
+ if (linePtr->segPtr == segPtr) {
+ linePtr->segPtr = segPtr->nextPtr;
+ } else {
+ for (prevPtr = linePtr->segPtr; prevPtr->nextPtr != segPtr;
+ prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ prevPtr->nextPtr = segPtr->nextPtr;
+ }
+ CleanupLine(linePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeTag --
+ *
+ * Turn a given tag on or off for a given range of characters in
+ * a B-tree of text.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The given tag is added to the given range of characters
+ * in the tree or removed from all those characters, depending
+ * on the "add" argument. The structure of the btree is modified
+ * enough that index1Ptr and index2Ptr are no longer valid after
+ * this procedure returns, and the indexes may be modified by
+ * this procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeTag(index1Ptr, index2Ptr, tagPtr, add)
+ register CkTextIndex *index1Ptr; /* Indicates first character in
+ * range. */
+ register CkTextIndex *index2Ptr; /* Indicates character just after the
+ * last one in range. */
+ CkTextTag *tagPtr; /* Tag to add or remove. */
+ int add; /* One means add tag to the given
+ * range of characters; zero means
+ * remove the tag from the range. */
+{
+ CkTextSegment *segPtr, *prevPtr;
+ CkTextSearch search;
+ CkTextLine *cleanupLinePtr;
+ int oldState;
+
+ /*
+ * See whether the tag is present at the start of the range. If
+ * the state doesn't already match what we want then add a toggle
+ * there.
+ */
+
+ oldState = CkBTreeCharTagged(index1Ptr, tagPtr);
+ if ((add != 0) ^ oldState) {
+ segPtr = (CkTextSegment *) ckalloc(TSEG_SIZE);
+ segPtr->typePtr = (add) ? &ckTextToggleOnType : &ckTextToggleOffType;
+ prevPtr = SplitSeg(index1Ptr);
+ if (prevPtr == NULL) {
+ segPtr->nextPtr = index1Ptr->linePtr->segPtr;
+ index1Ptr->linePtr->segPtr = segPtr;
+ } else {
+ segPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = segPtr;
+ }
+ segPtr->size = 0;
+ segPtr->body.toggle.tagPtr = tagPtr;
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+
+ /*
+ * Scan the range of characters and delete any internal tag
+ * transitions. Keep track of what the old state was at the end
+ * of the range, and add a toggle there if it's needed.
+ */
+
+ CkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
+ cleanupLinePtr = index1Ptr->linePtr;
+ while (CkBTreeNextTag(&search)) {
+ oldState ^= 1;
+ segPtr = search.segPtr;
+ prevPtr = search.curIndex.linePtr->segPtr;
+ if (prevPtr == segPtr) {
+ search.curIndex.linePtr->segPtr = segPtr->nextPtr;
+ } else {
+ while (prevPtr->nextPtr != segPtr) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ prevPtr->nextPtr = segPtr->nextPtr;
+ }
+ if (segPtr->body.toggle.inNodeCounts) {
+ ChangeNodeToggleCount(search.curIndex.linePtr->parentPtr,
+ segPtr->body.toggle.tagPtr, -1);
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+ ckfree((char *) segPtr);
+
+ /*
+ * The code below is a bit tricky. After deleting a toggle
+ * we eventually have to call CleanupLine, in order to allow
+ * character segments to be merged together. To do this, we
+ * remember in cleanupLinePtr a line that needs to be
+ * cleaned up, but we don't clean it up until we've moved
+ * on to a different line. That way the cleanup process
+ * won't goof up segPtr.
+ */
+
+ if (cleanupLinePtr != search.curIndex.linePtr) {
+ CleanupLine(cleanupLinePtr);
+ cleanupLinePtr = search.curIndex.linePtr;
+ }
+ }
+ if ((add != 0) ^ oldState) {
+ segPtr = (CkTextSegment *) ckalloc(TSEG_SIZE);
+ segPtr->typePtr = (add) ? &ckTextToggleOffType : &ckTextToggleOnType;
+ prevPtr = SplitSeg(index2Ptr);
+ if (prevPtr == NULL) {
+ segPtr->nextPtr = index2Ptr->linePtr->segPtr;
+ index2Ptr->linePtr->segPtr = segPtr;
+ } else {
+ segPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = segPtr;
+ }
+ segPtr->size = 0;
+ segPtr->body.toggle.tagPtr = tagPtr;
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+
+ /*
+ * Cleanup cleanupLinePtr and the last line of the range, if
+ * these are different.
+ */
+
+ CleanupLine(cleanupLinePtr);
+ if (cleanupLinePtr != index2Ptr->linePtr) {
+ CleanupLine(index2Ptr->linePtr);
+ }
+
+ if (ckBTreeDebug) {
+ CkBTreeCheck(index1Ptr->tree);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangeNodeToggleCount --
+ *
+ * This procedure increments or decrements the toggle count for
+ * a particular tag in a particular node and all its ancestors.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The toggle count for tag is adjusted up or down by "delta" in
+ * nodePtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeNodeToggleCount(nodePtr, tagPtr, delta)
+ register Node *nodePtr; /* Node whose toggle count for a tag
+ * must be changed. */
+ CkTextTag *tagPtr; /* Information about tag. */
+ int delta; /* Amount to add to current toggle
+ * count for tag (may be negative). */
+{
+ register Summary *summaryPtr, *prevPtr;
+
+ /*
+ * Iterate over the node and all of its ancestors.
+ */
+
+ for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
+ /*
+ * See if there's already an entry for this tag for this node. If so,
+ * perhaps all we have to do is adjust its count.
+ */
+
+ for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
+ summaryPtr != NULL;
+ prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr->tagPtr != tagPtr) {
+ continue;
+ }
+ summaryPtr->toggleCount += delta;
+ if (summaryPtr->toggleCount > 0) {
+ goto nextAncestor;
+ }
+ if (summaryPtr->toggleCount < 0) {
+ panic("ChangeNodeToggleCount: negative toggle count");
+ }
+
+ /*
+ * Zero count; must remove this tag from the list.
+ */
+
+ if (prevPtr == NULL) {
+ nodePtr->summaryPtr = summaryPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = summaryPtr->nextPtr;
+ }
+ ckfree((char *) summaryPtr);
+ goto nextAncestor;
+ }
+
+ /*
+ * This tag isn't in the list. Add a new entry to the list.
+ */
+
+ if (delta < 0) {
+ panic("ChangeNodeToggleCount: negative delta, no tag entry");
+ }
+ summaryPtr = (Summary *) ckalloc(sizeof(Summary));
+ summaryPtr->tagPtr = tagPtr;
+ summaryPtr->toggleCount = delta;
+ summaryPtr->nextPtr = nodePtr->summaryPtr;
+ nodePtr->summaryPtr = summaryPtr;
+
+ nextAncestor:
+ continue;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeStartSearch --
+ *
+ * This procedure sets up a search for tag transitions involving
+ * a given tag (or all tags) in a given range of the text.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The information at *searchPtr is set up so that subsequent calls
+ * to CkBTreeNextTag will return information about the locations of
+ * tag transitions. Note that CkBTreeNextTag must be called to get
+ * the first transition.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
+ CkTextIndex *index1Ptr; /* Search starts here. Tag toggles
+ * at this position will not be
+ * returned. */
+ CkTextIndex *index2Ptr; /* Search stops here. Tag toggles
+ * at this position *will* be
+ * returned. */
+ CkTextTag *tagPtr; /* Tag to search for. NULL means
+ * search for any tag. */
+ register CkTextSearch *searchPtr; /* Where to store information about
+ * search's progress. */
+{
+ int offset;
+
+ searchPtr->curIndex = *index1Ptr;
+ searchPtr->segPtr = NULL;
+ searchPtr->nextPtr = CkTextIndexToSeg(index1Ptr, &offset);
+ searchPtr->curIndex.charIndex -= offset;
+ searchPtr->lastPtr = CkTextIndexToSeg(index2Ptr, (int *) NULL);
+ searchPtr->tagPtr = tagPtr;
+ searchPtr->linesLeft = CkBTreeLineIndex(index2Ptr->linePtr) + 1
+ - CkBTreeLineIndex(index1Ptr->linePtr);
+ searchPtr->allTags = (tagPtr == NULL);
+ if (searchPtr->linesLeft == 1) {
+ /*
+ * Starting and stopping segments are in the same line; mark the
+ * search as over immediately if the second segment is before the
+ * first.
+ */
+
+ if (index1Ptr->charIndex >= index2Ptr->charIndex) {
+ searchPtr->linesLeft = 0;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeNextTag --
+ *
+ * Once a tag search has begun, successive calls to this procedure
+ * return successive tag toggles. Note: it is NOT SAFE to call this
+ * procedure if characters have been inserted into or deleted from
+ * the B-tree since the call to CkBTreeStartSearch.
+ *
+ * Results:
+ * The return value is 1 if another toggle was found that met the
+ * criteria specified in the call to CkBTreeStartSearch; in this
+ * case searchPtr->curIndex gives the toggle's position and
+ * searchPtr->curTagPtr points to its segment. 0 is returned if
+ * no more matching tag transitions were found; in this case
+ * searchPtr->curIndex is the same as searchPtr->stopIndex.
+ *
+ * Side effects:
+ * Information in *searchPtr is modified to update the state of the
+ * search and indicate where the next tag toggle is located.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkBTreeNextTag(searchPtr)
+ register CkTextSearch *searchPtr; /* Information about search in
+ * progress; must have been set up by
+ * call to CkBTreeStartSearch. */
+{
+ register CkTextSegment *segPtr;
+ register Node *nodePtr;
+ register Summary *summaryPtr;
+
+ if (searchPtr->linesLeft <= 0) {
+ goto searchOver;
+ }
+
+ /*
+ * The outermost loop iterates over lines that may potentially contain
+ * a relevant tag transition, starting from the current segment in
+ * the current line.
+ */
+
+ segPtr = searchPtr->nextPtr;
+ while (1) {
+ /*
+ * Check for more tags on the current line.
+ */
+
+ for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ if (segPtr == searchPtr->lastPtr) {
+ goto searchOver;
+ }
+ if (((segPtr->typePtr == &ckTextToggleOnType)
+ || (segPtr->typePtr == &ckTextToggleOffType))
+ && (searchPtr->allTags
+ || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
+ searchPtr->segPtr = segPtr;
+ searchPtr->nextPtr = segPtr->nextPtr;
+ searchPtr->tagPtr = segPtr->body.toggle.tagPtr;
+ return 1;
+ }
+ searchPtr->curIndex.charIndex += segPtr->size;
+ }
+
+ /*
+ * See if there are more lines associated with the current parent
+ * node. If so, go back to the top of the loop to search the next
+ * one.
+ */
+
+ nodePtr = searchPtr->curIndex.linePtr->parentPtr;
+ searchPtr->curIndex.linePtr = searchPtr->curIndex.linePtr->nextPtr;
+ searchPtr->linesLeft--;
+ if (searchPtr->linesLeft <= 0) {
+ goto searchOver;
+ }
+ if (searchPtr->curIndex.linePtr != NULL) {
+ segPtr = searchPtr->curIndex.linePtr->segPtr;
+ searchPtr->curIndex.charIndex = 0;
+ continue;
+ }
+
+ /*
+ * Search across and up through the B-tree's node hierarchy looking
+ * for the next node that has a relevant tag transition somewhere in
+ * its subtree. Be sure to update linesLeft as we skip over large
+ * chunks of lines.
+ */
+
+ while (1) {
+ while (nodePtr->nextPtr == NULL) {
+ if (nodePtr->parentPtr == NULL) {
+ goto searchOver;
+ }
+ nodePtr = nodePtr->parentPtr;
+ }
+ nodePtr = nodePtr->nextPtr;
+ for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ if ((searchPtr->allTags) ||
+ (summaryPtr->tagPtr == searchPtr->tagPtr)) {
+ goto gotNodeWithTag;
+ }
+ }
+ searchPtr->linesLeft -= nodePtr->numLines;
+ }
+
+ /*
+ * At this point we've found a subtree that has a relevant tag
+ * transition. Now search down (and across) through that subtree
+ * to find the first level-0 node that has a relevant tag transition.
+ */
+
+ gotNodeWithTag:
+ while (nodePtr->level > 0) {
+ for (nodePtr = nodePtr->children.nodePtr; ;
+ nodePtr = nodePtr->nextPtr) {
+ for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ if ((searchPtr->allTags)
+ || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
+ goto nextChild;
+ }
+ }
+ searchPtr->linesLeft -= nodePtr->numLines;
+ if (nodePtr->nextPtr == NULL) {
+ panic("CkBTreeNextTag found incorrect tag summary info.");
+ }
+ }
+ nextChild:
+ continue;
+ }
+
+ /*
+ * Now we're down to a level-0 node that contains a line that contains
+ * a relevant tag transition. Set up line information and go back to
+ * the beginning of the loop to search through lines.
+ */
+
+ searchPtr->curIndex.linePtr = nodePtr->children.linePtr;
+ searchPtr->curIndex.charIndex = 0;
+ segPtr = searchPtr->curIndex.linePtr->segPtr;
+ if (searchPtr->linesLeft <= 0) {
+ goto searchOver;
+ }
+ continue;
+ }
+
+ searchOver:
+ searchPtr->linesLeft = 0;
+ searchPtr->segPtr = NULL;
+ return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeCharTagged --
+ *
+ * Determine whether a particular character has a particular tag.
+ *
+ * Results:
+ * The return value is 1 if the given tag is in effect at the
+ * character given by linePtr and ch, and 0 otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkBTreeCharTagged(indexPtr, tagPtr)
+ CkTextIndex *indexPtr; /* Indicates a character position at
+ * which to check for a tag. */
+ CkTextTag *tagPtr; /* Tag of interest. */
+{
+ register Node *nodePtr;
+ register CkTextLine *siblingLinePtr;
+ register CkTextSegment *segPtr;
+ CkTextSegment *toggleSegPtr;
+ int toggles, index;
+
+ /*
+ * Check for toggles for the tag in indexPtr's line but before
+ * indexPtr. If there is one, its type indicates whether or
+ * not the character is tagged.
+ */
+
+ toggleSegPtr = NULL;
+ for (index = 0, segPtr = indexPtr->linePtr->segPtr;
+ (index + segPtr->size) <= indexPtr->charIndex;
+ index += segPtr->size, segPtr = segPtr->nextPtr) {
+ if (((segPtr->typePtr == &ckTextToggleOnType)
+ || (segPtr->typePtr == &ckTextToggleOffType))
+ && (segPtr->body.toggle.tagPtr == tagPtr)) {
+ toggleSegPtr = segPtr;
+ }
+ }
+ if (toggleSegPtr != NULL) {
+ return (toggleSegPtr->typePtr == &ckTextToggleOnType);
+ }
+
+ /*
+ * No toggle in this line. Look for toggles for the tag in lines
+ * that are predecessors of indexPtr->linePtr but under the same
+ * level-0 node.
+ */
+
+ toggles = 0;
+ for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
+ siblingLinePtr != indexPtr->linePtr;
+ siblingLinePtr = siblingLinePtr->nextPtr) {
+ for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if (((segPtr->typePtr == &ckTextToggleOnType)
+ || (segPtr->typePtr == &ckTextToggleOffType))
+ && (segPtr->body.toggle.tagPtr == tagPtr)) {
+ toggleSegPtr = segPtr;
+ }
+ }
+ }
+ if (toggleSegPtr != NULL) {
+ return (toggleSegPtr->typePtr == &ckTextToggleOnType);
+ }
+
+ /*
+ * No toggle in this node. Scan upwards through the ancestors of
+ * this node, counting the number of toggles of the given tag in
+ * siblings that precede that node.
+ */
+
+ toggles = 0;
+ for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
+ nodePtr = nodePtr->parentPtr) {
+ register Node *siblingPtr;
+ register Summary *summaryPtr;
+
+ for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
+ siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
+ for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr->tagPtr == tagPtr) {
+ toggles += summaryPtr->toggleCount;
+ }
+ }
+ }
+ }
+
+ /*
+ * An odd number of toggles means that the tag is present at the
+ * given point.
+ */
+
+ return toggles & 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeGetTags --
+ *
+ * Return information about all of the tags that are associated
+ * with a particular character in a B-tree of text.
+ *
+ * Results:
+ * The return value is a malloc-ed array containing pointers to
+ * information for each of the tags that is associated with
+ * the character at the position given by linePtr and ch. The
+ * word at *numTagsPtr is filled in with the number of pointers
+ * in the array. It is up to the caller to free the array by
+ * passing it to free. If there are no tags at the given character
+ * then a NULL pointer is returned and *numTagsPtr will be set to 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+CkTextTag **
+CkBTreeGetTags(indexPtr, numTagsPtr)
+ CkTextIndex *indexPtr; /* Indicates a particular position in
+ * the B-tree. */
+ int *numTagsPtr; /* Store number of tags found at this
+ * location. */
+{
+ register Node *nodePtr;
+ register CkTextLine *siblingLinePtr;
+ register CkTextSegment *segPtr;
+ int src, dst, index;
+ TagInfo tagInfo;
+#define NUM_TAG_INFOS 10
+
+ tagInfo.numTags = 0;
+ tagInfo.arraySize = NUM_TAG_INFOS;
+ tagInfo.tagPtrs = (CkTextTag **) ckalloc((unsigned)
+ NUM_TAG_INFOS*sizeof(CkTextTag *));
+ tagInfo.counts = (int *) ckalloc((unsigned)
+ NUM_TAG_INFOS*sizeof(int));
+
+ /*
+ * Record tag toggles within the line of indexPtr but preceding
+ * indexPtr.
+ */
+
+ for (index = 0, segPtr = indexPtr->linePtr->segPtr;
+ (index + segPtr->size) <= indexPtr->charIndex;
+ index += segPtr->size, segPtr = segPtr->nextPtr) {
+ if ((segPtr->typePtr == &ckTextToggleOnType)
+ || (segPtr->typePtr == &ckTextToggleOffType)) {
+ IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
+ }
+ }
+
+ /*
+ * Record toggles for tags in lines that are predecessors of
+ * indexPtr->linePtr but under the same level-0 node.
+ */
+
+ for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
+ siblingLinePtr != indexPtr->linePtr;
+ siblingLinePtr = siblingLinePtr->nextPtr) {
+ for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if ((segPtr->typePtr == &ckTextToggleOnType)
+ || (segPtr->typePtr == &ckTextToggleOffType)) {
+ IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
+ }
+ }
+ }
+
+ /*
+ * For each node in the ancestry of this line, record tag toggles
+ * for all siblings that precede that node.
+ */
+
+ for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
+ nodePtr = nodePtr->parentPtr) {
+ register Node *siblingPtr;
+ register Summary *summaryPtr;
+
+ for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
+ siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
+ for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr->toggleCount & 1) {
+ IncCount(summaryPtr->tagPtr, summaryPtr->toggleCount,
+ &tagInfo);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through the tag information and squash out all of the tags
+ * that have even toggle counts (these tags exist before the point
+ * of interest, but not at the desired character itself).
+ */
+
+ for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
+ if (tagInfo.counts[src] & 1) {
+ tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
+ dst++;
+ }
+ }
+ *numTagsPtr = dst;
+ ckfree((char *) tagInfo.counts);
+ if (dst == 0) {
+ ckfree((char *) tagInfo.tagPtrs);
+ return NULL;
+ }
+ return tagInfo.tagPtrs;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * IncCount --
+ *
+ * This is a utility procedure used by CkBTreeGetTags. It
+ * increments the count for a particular tag, adding a new
+ * entry for that tag if there wasn't one previously.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The information at *tagInfoPtr may be modified, and the arrays
+ * may be reallocated to make them larger.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+IncCount(tagPtr, inc, tagInfoPtr)
+ CkTextTag *tagPtr; /* Handle for tag. */
+ int inc; /* Amount by which to increment tag count. */
+ TagInfo *tagInfoPtr; /* Holds cumulative information about tags;
+ * increment count here. */
+{
+ register CkTextTag **tagPtrPtr;
+ int count;
+
+ for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
+ count > 0; tagPtrPtr++, count--) {
+ if (*tagPtrPtr == tagPtr) {
+ tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
+ return;
+ }
+ }
+
+ /*
+ * There isn't currently an entry for this tag, so we have to
+ * make a new one. If the arrays are full, then enlarge the
+ * arrays first.
+ */
+
+ if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
+ CkTextTag **newTags;
+ int *newCounts, newSize;
+
+ newSize = 2*tagInfoPtr->arraySize;
+ newTags = (CkTextTag **) ckalloc((unsigned)
+ (newSize*sizeof(CkTextTag *)));
+ memcpy((VOID *) newTags, (VOID *) tagInfoPtr->tagPtrs,
+ tagInfoPtr->arraySize * sizeof(CkTextTag *));
+ ckfree((char *) tagInfoPtr->tagPtrs);
+ tagInfoPtr->tagPtrs = newTags;
+ newCounts = (int *) ckalloc((unsigned) (newSize*sizeof(int)));
+ memcpy((VOID *) newCounts, (VOID *) tagInfoPtr->counts,
+ tagInfoPtr->arraySize * sizeof(int));
+ ckfree((char *) tagInfoPtr->counts);
+ tagInfoPtr->counts = newCounts;
+ tagInfoPtr->arraySize = newSize;
+ }
+
+ tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
+ tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
+ tagInfoPtr->numTags++;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeCheck --
+ *
+ * This procedure runs a set of consistency checks over a B-tree
+ * and panics if any inconsistencies are found.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a structural defect is found, the procedure panics with an
+ * error message.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkBTreeCheck(tree)
+ CkTextBTree tree; /* Tree to check. */
+{
+ BTree *treePtr = (BTree *) tree;
+ register Summary *summaryPtr;
+ register Node *nodePtr;
+ register CkTextLine *linePtr;
+ register CkTextSegment *segPtr;
+
+ /*
+ * Make sure that overall there is an even count of tag transitions
+ * for the whole tree.
+ */
+
+ for (summaryPtr = treePtr->rootPtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr->toggleCount & 1) {
+ panic("CkBTreeCheck found odd toggle count for \"%s\" (%d)",
+ summaryPtr->tagPtr->name, summaryPtr->toggleCount);
+ }
+ }
+
+ /*
+ * Call a recursive procedure to do the main body of checks.
+ */
+
+ nodePtr = treePtr->rootPtr;
+ CheckNodeConsistency(treePtr->rootPtr);
+
+ /*
+ * Make sure that there are at least two lines in the text and
+ * that the last line has no characters except a newline.
+ */
+
+ if (nodePtr->numLines < 2) {
+ panic("CkBTreeCheck: less than 2 lines in tree");
+ }
+ while (nodePtr->level > 0) {
+ nodePtr = nodePtr->children.nodePtr;
+ while (nodePtr->nextPtr != NULL) {
+ nodePtr = nodePtr->nextPtr;
+ }
+ }
+ linePtr = nodePtr->children.linePtr;
+ while (linePtr->nextPtr != NULL) {
+ linePtr = linePtr->nextPtr;
+ }
+ segPtr = linePtr->segPtr;
+ while ((segPtr->typePtr == &ckTextToggleOffType)
+ || (segPtr->typePtr == &ckTextRightMarkType)
+ || (segPtr->typePtr == &ckTextLeftMarkType)) {
+ /*
+ * It's OK to toggle a tag off in the last line, but
+ * not to start a new range. It's also OK to have marks
+ * in the last line.
+ */
+
+ segPtr = segPtr->nextPtr;
+ }
+ if (segPtr->typePtr != &ckTextCharType) {
+ panic("CkBTreeCheck: last line has bogus segment type");
+ }
+ if (segPtr->nextPtr != NULL) {
+ panic("CkBTreeCheck: last line has too many segments");
+ }
+ if (segPtr->size != 1) {
+ panic("CkBTreeCheck: last line has wrong # characters: %d",
+ segPtr->size);
+ }
+ if ((segPtr->body.chars[0] != '\n') || (segPtr->body.chars[1] != 0)) {
+ panic("CkBTreeCheck: last line had bad value: %s",
+ segPtr->body.chars);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckNodeConsistency --
+ *
+ * This procedure is called as part of consistency checking for
+ * B-trees: it checks several aspects of a node and also runs
+ * checks recursively on the node's children.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If anything suspicious is found in the tree structure, the
+ * procedure panics.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+CheckNodeConsistency(nodePtr)
+ register Node *nodePtr; /* Node whose subtree should be
+ * checked. */
+{
+ register Node *childNodePtr;
+ register Summary *summaryPtr, *summaryPtr2;
+ register CkTextLine *linePtr;
+ register CkTextSegment *segPtr;
+ int numChildren, numLines, toggleCount, minChildren;
+
+ if (nodePtr->parentPtr != NULL) {
+ minChildren = MIN_CHILDREN;
+ } else if (nodePtr->level > 0) {
+ minChildren = 2;
+ } else {
+ minChildren = 1;
+ }
+ if ((nodePtr->numChildren < minChildren)
+ || (nodePtr->numChildren > MAX_CHILDREN)) {
+ panic("CheckNodeConsistency: bad child count (%d)",
+ nodePtr->numChildren);
+ }
+
+ numChildren = 0;
+ numLines = 0;
+ if (nodePtr->level == 0) {
+ for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
+ linePtr = linePtr->nextPtr) {
+ if (linePtr->parentPtr != nodePtr) {
+ panic("CheckNodeConsistency: line doesn't point to parent");
+ }
+ if (linePtr->segPtr == NULL) {
+ panic("CheckNodeConsistency: line has no segments");
+ }
+ for (segPtr = linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr->checkProc != NULL) {
+ (*segPtr->typePtr->checkProc)(segPtr, linePtr);
+ }
+ if ((segPtr->size == 0) && (!segPtr->typePtr->leftGravity)
+ && (segPtr->nextPtr != NULL)
+ && (segPtr->nextPtr->size == 0)
+ && (segPtr->nextPtr->typePtr->leftGravity)) {
+ panic("CheckNodeConsistency: wrong segment order for gravity");
+ }
+ if ((segPtr->nextPtr == NULL)
+ && (segPtr->typePtr != &ckTextCharType)) {
+ panic("CheckNodeConsistency: line ended with wrong type");
+ }
+ }
+ numChildren++;
+ numLines++;
+ }
+ } else {
+ for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
+ childNodePtr = childNodePtr->nextPtr) {
+ if (childNodePtr->parentPtr != nodePtr) {
+ panic("CheckNodeConsistency: node doesn't point to parent");
+ }
+ if (childNodePtr->level != (nodePtr->level-1)) {
+ panic("CheckNodeConsistency: level mismatch (%d %d)",
+ nodePtr->level, childNodePtr->level);
+ }
+ CheckNodeConsistency(childNodePtr);
+ for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ for (summaryPtr2 = nodePtr->summaryPtr; ;
+ summaryPtr2 = summaryPtr2->nextPtr) {
+ if (summaryPtr2 == NULL) {
+ panic("CheckNodeConsistency: node tag \"%s\" not %s",
+ summaryPtr->tagPtr->name,
+ "present in parent summaries");
+ }
+ if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
+ break;
+ }
+ }
+ }
+ numChildren++;
+ numLines += childNodePtr->numLines;
+ }
+ }
+ if (numChildren != nodePtr->numChildren) {
+ panic("CheckNodeConsistency: mismatch in numChildren (%d %d)",
+ numChildren, nodePtr->numChildren);
+ }
+ if (numLines != nodePtr->numLines) {
+ panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
+ numLines, nodePtr->numLines);
+ }
+
+ for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ toggleCount = 0;
+ if (nodePtr->level == 0) {
+ for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
+ linePtr = linePtr->nextPtr) {
+ for (segPtr = linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if ((segPtr->typePtr != &ckTextToggleOnType)
+ && (segPtr->typePtr != &ckTextToggleOffType)) {
+ continue;
+ }
+ if (segPtr->body.toggle.tagPtr == summaryPtr->tagPtr) {
+ toggleCount ++;
+ }
+ }
+ }
+ } else {
+ for (childNodePtr = nodePtr->children.nodePtr;
+ childNodePtr != NULL;
+ childNodePtr = childNodePtr->nextPtr) {
+ for (summaryPtr2 = childNodePtr->summaryPtr;
+ summaryPtr2 != NULL;
+ summaryPtr2 = summaryPtr2->nextPtr) {
+ if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
+ toggleCount += summaryPtr2->toggleCount;
+ }
+ }
+ }
+ }
+ if (toggleCount != summaryPtr->toggleCount) {
+ panic("CheckNodeConsistency: mismatch in toggleCount (%d %d)",
+ toggleCount, summaryPtr->toggleCount);
+ }
+ for (summaryPtr2 = summaryPtr->nextPtr; summaryPtr2 != NULL;
+ summaryPtr2 = summaryPtr2->nextPtr) {
+ if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
+ panic("CheckNodeConsistency: duplicated node tag: %s",
+ summaryPtr->tagPtr->name);
+ }
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Rebalance --
+ *
+ * This procedure is called when a node of a B-tree appears to be
+ * out of balance (too many children, or too few). It rebalances
+ * that node and all of its ancestors in the tree.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The internal structure of treePtr may change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Rebalance(treePtr, nodePtr)
+ BTree *treePtr; /* Tree that is being rebalanced. */
+ register Node *nodePtr; /* Node that may be out of balance. */
+{
+ /*
+ * Loop over the entire ancestral chain of the node, working up
+ * through the tree one node at a time until the root node has
+ * been processed.
+ */
+
+ for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
+ register Node *newPtr, *childPtr;
+ register CkTextLine *linePtr;
+ int i;
+
+ /*
+ * Check to see if the node has too many children. If it does,
+ * then split off all but the first MIN_CHILDREN into a separate
+ * node following the original one. Then repeat until the
+ * node has a decent size.
+ */
+
+ if (nodePtr->numChildren > MAX_CHILDREN) {
+ while (1) {
+ /*
+ * If the node being split is the root node, then make a
+ * new root node above it first.
+ */
+
+ if (nodePtr->parentPtr == NULL) {
+ newPtr = (Node *) ckalloc(sizeof(Node));
+ newPtr->parentPtr = NULL;
+ newPtr->nextPtr = NULL;
+ newPtr->summaryPtr = NULL;
+ newPtr->level = nodePtr->level + 1;
+ newPtr->children.nodePtr = nodePtr;
+ newPtr->numChildren = 1;
+ newPtr->numLines = nodePtr->numLines;
+ RecomputeNodeCounts(newPtr);
+ treePtr->rootPtr = newPtr;
+ }
+ newPtr = (Node *) ckalloc(sizeof(Node));
+ newPtr->parentPtr = nodePtr->parentPtr;
+ newPtr->nextPtr = nodePtr->nextPtr;
+ nodePtr->nextPtr = newPtr;
+ newPtr->summaryPtr = NULL;
+ newPtr->level = nodePtr->level;
+ newPtr->numChildren = nodePtr->numChildren - MIN_CHILDREN;
+ if (nodePtr->level == 0) {
+ for (i = MIN_CHILDREN-1,
+ linePtr = nodePtr->children.linePtr;
+ i > 0; i--, linePtr = linePtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ newPtr->children.linePtr = linePtr->nextPtr;
+ linePtr->nextPtr = NULL;
+ } else {
+ for (i = MIN_CHILDREN-1,
+ childPtr = nodePtr->children.nodePtr;
+ i > 0; i--, childPtr = childPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ newPtr->children.nodePtr = childPtr->nextPtr;
+ childPtr->nextPtr = NULL;
+ }
+ RecomputeNodeCounts(nodePtr);
+ nodePtr->parentPtr->numChildren++;
+ nodePtr = newPtr;
+ if (nodePtr->numChildren <= MAX_CHILDREN) {
+ RecomputeNodeCounts(nodePtr);
+ break;
+ }
+ }
+ }
+
+ while (nodePtr->numChildren < MIN_CHILDREN) {
+ register Node *otherPtr;
+ Node *halfwayNodePtr = NULL; /* Initialization needed only */
+ CkTextLine *halfwayLinePtr = NULL; /* to prevent cc warnings. */
+ int totalChildren, firstChildren, i;
+
+ /*
+ * Too few children for this node. If this is the root then,
+ * it's OK for it to have less than MIN_CHILDREN children
+ * as long as it's got at least two. If it has only one
+ * (and isn't at level 0), then chop the root node out of
+ * the tree and use its child as the new root.
+ */
+
+ if (nodePtr->parentPtr == NULL) {
+ if ((nodePtr->numChildren == 1) && (nodePtr->level > 0)) {
+ treePtr->rootPtr = nodePtr->children.nodePtr;
+ treePtr->rootPtr->parentPtr = NULL;
+ DeleteSummaries(nodePtr->summaryPtr);
+ ckfree((char *) nodePtr);
+ }
+ return;
+ }
+
+ /*
+ * Not the root. Make sure that there are siblings to
+ * balance with.
+ */
+
+ if (nodePtr->parentPtr->numChildren < 2) {
+ Rebalance(treePtr, nodePtr->parentPtr);
+ continue;
+ }
+
+ /*
+ * Find a sibling neighbor to borrow from, and arrange for
+ * nodePtr to be the earlier of the pair.
+ */
+
+ if (nodePtr->nextPtr == NULL) {
+ for (otherPtr = nodePtr->parentPtr->children.nodePtr;
+ otherPtr->nextPtr != nodePtr;
+ otherPtr = otherPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ nodePtr = otherPtr;
+ }
+ otherPtr = nodePtr->nextPtr;
+
+ /*
+ * We're going to either merge the two siblings together
+ * into one node or redivide the children among them to
+ * balance their loads. As preparation, join their two
+ * child lists into a single list and remember the half-way
+ * point in the list.
+ */
+
+ totalChildren = nodePtr->numChildren + otherPtr->numChildren;
+ firstChildren = totalChildren/2;
+ if (nodePtr->children.nodePtr == NULL) {
+ nodePtr->children = otherPtr->children;
+ otherPtr->children.nodePtr = NULL;
+ otherPtr->children.linePtr = NULL;
+ }
+ if (nodePtr->level == 0) {
+ register CkTextLine *linePtr;
+
+ for (linePtr = nodePtr->children.linePtr, i = 1;
+ linePtr->nextPtr != NULL;
+ linePtr = linePtr->nextPtr, i++) {
+ if (i == firstChildren) {
+ halfwayLinePtr = linePtr;
+ }
+ }
+ linePtr->nextPtr = otherPtr->children.linePtr;
+ while (i <= firstChildren) {
+ halfwayLinePtr = linePtr;
+ linePtr = linePtr->nextPtr;
+ i++;
+ }
+ } else {
+ register Node *childPtr;
+
+ for (childPtr = nodePtr->children.nodePtr, i = 1;
+ childPtr->nextPtr != NULL;
+ childPtr = childPtr->nextPtr, i++) {
+ if (i <= firstChildren) {
+ if (i == firstChildren) {
+ halfwayNodePtr = childPtr;
+ }
+ }
+ }
+ childPtr->nextPtr = otherPtr->children.nodePtr;
+ while (i <= firstChildren) {
+ halfwayNodePtr = childPtr;
+ childPtr = childPtr->nextPtr;
+ i++;
+ }
+ }
+
+ /*
+ * If the two siblings can simply be merged together, do it.
+ */
+
+ if (totalChildren <= MAX_CHILDREN) {
+ RecomputeNodeCounts(nodePtr);
+ nodePtr->nextPtr = otherPtr->nextPtr;
+ nodePtr->parentPtr->numChildren--;
+ DeleteSummaries(otherPtr->summaryPtr);
+ ckfree((char *) otherPtr);
+ continue;
+ }
+
+ /*
+ * The siblings can't be merged, so just divide their
+ * children evenly between them.
+ */
+
+ if (nodePtr->level == 0) {
+ otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
+ halfwayLinePtr->nextPtr = NULL;
+ } else {
+ otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
+ halfwayNodePtr->nextPtr = NULL;
+ }
+ RecomputeNodeCounts(nodePtr);
+ RecomputeNodeCounts(otherPtr);
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecomputeNodeCounts --
+ *
+ * This procedure is called to recompute all the counts in a node
+ * (tags, child information, etc.) by scanning the information in
+ * its descendants. This procedure is called during rebalancing
+ * when a node's child structure has changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The tag counts for nodePtr are modified to reflect its current
+ * child structure, as are its numChildren and numLines fields.
+ * Also, all of the childrens' parentPtr fields are made to point
+ * to nodePtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RecomputeNodeCounts(nodePtr)
+ register Node *nodePtr; /* Node whose tag summary information
+ * must be recomputed. */
+{
+ register Summary *summaryPtr, *summaryPtr2;
+ register Node *childPtr;
+ register CkTextLine *linePtr;
+ register CkTextSegment *segPtr;
+ CkTextTag *tagPtr;
+
+ /*
+ * Zero out all the existing counts for the node, but don't delete
+ * the existing Summary records (most of them will probably be reused).
+ */
+
+ for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
+ summaryPtr = summaryPtr->nextPtr) {
+ summaryPtr->toggleCount = 0;
+ }
+ nodePtr->numChildren = 0;
+ nodePtr->numLines = 0;
+
+ /*
+ * Scan through the children, adding the childrens' tag counts into
+ * the node's tag counts and adding new Summary structures if
+ * necessary.
+ */
+
+ if (nodePtr->level == 0) {
+ for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
+ linePtr = linePtr->nextPtr) {
+ nodePtr->numChildren++;
+ nodePtr->numLines++;
+ linePtr->parentPtr = nodePtr;
+ for (segPtr = linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ if (((segPtr->typePtr != &ckTextToggleOnType)
+ && (segPtr->typePtr != &ckTextToggleOffType))
+ || !(segPtr->body.toggle.inNodeCounts)) {
+ continue;
+ }
+ tagPtr = segPtr->body.toggle.tagPtr;
+ for (summaryPtr = nodePtr->summaryPtr; ;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr == NULL) {
+ summaryPtr = (Summary *) ckalloc(sizeof(Summary));
+ summaryPtr->tagPtr = tagPtr;
+ summaryPtr->toggleCount = 1;
+ summaryPtr->nextPtr = nodePtr->summaryPtr;
+ nodePtr->summaryPtr = summaryPtr;
+ break;
+ }
+ if (summaryPtr->tagPtr == tagPtr) {
+ summaryPtr->toggleCount++;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ for (childPtr = nodePtr->children.nodePtr; childPtr != NULL;
+ childPtr = childPtr->nextPtr) {
+ nodePtr->numChildren++;
+ nodePtr->numLines += childPtr->numLines;
+ childPtr->parentPtr = nodePtr;
+ for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
+ summaryPtr2 = summaryPtr2->nextPtr) {
+ for (summaryPtr = nodePtr->summaryPtr; ;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr == NULL) {
+ summaryPtr = (Summary *) ckalloc(sizeof(Summary));
+ summaryPtr->tagPtr = summaryPtr2->tagPtr;
+ summaryPtr->toggleCount = summaryPtr2->toggleCount;
+ summaryPtr->nextPtr = nodePtr->summaryPtr;
+ nodePtr->summaryPtr = summaryPtr;
+ break;
+ }
+ if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
+ summaryPtr->toggleCount += summaryPtr2->toggleCount;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Scan through the node's tag records again and delete any Summary
+ * records that still have a zero count.
+ */
+
+ summaryPtr2 = NULL;
+ for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
+ if (summaryPtr->toggleCount > 0) {
+ summaryPtr2 = summaryPtr;
+ summaryPtr = summaryPtr->nextPtr;
+ continue;
+ }
+ if (summaryPtr2 != NULL) {
+ summaryPtr2->nextPtr = summaryPtr->nextPtr;
+ ckfree((char *) summaryPtr);
+ summaryPtr = summaryPtr2->nextPtr;
+ } else {
+ nodePtr->summaryPtr = summaryPtr->nextPtr;
+ ckfree((char *) summaryPtr);
+ summaryPtr = nodePtr->summaryPtr;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeNumLines --
+ *
+ * This procedure returns a count of the number of lines of
+ * text present in a given B-tree.
+ *
+ * Results:
+ * The return value is a count of the number of usable lines
+ * in tree (i.e. it doesn't include the dummy line that is just
+ * used to mark the end of the tree).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkBTreeNumLines(tree)
+ CkTextBTree tree; /* Information about tree. */
+{
+ BTree *treePtr = (BTree *) tree;
+ return treePtr->rootPtr->numLines - 1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharSplitProc --
+ *
+ * This procedure implements splitting for character segments.
+ *
+ * Results:
+ * The return value is a pointer to a chain of two segments
+ * that have the same characters as segPtr except split
+ * among the two segments.
+ *
+ * Side effects:
+ * Storage for segPtr is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static CkTextSegment *
+CharSplitProc(segPtr, index)
+ CkTextSegment *segPtr; /* Pointer to segment to split. */
+ int index; /* Position within segment at which
+ * to split. */
+{
+ CkTextSegment *newPtr1, *newPtr2;
+
+ newPtr1 = (CkTextSegment *) ckalloc(CSEG_SIZE(index));
+ newPtr2 = (CkTextSegment *) ckalloc(
+ CSEG_SIZE(segPtr->size - index));
+ newPtr1->typePtr = &ckTextCharType;
+ newPtr1->nextPtr = newPtr2;
+ newPtr1->size = index;
+ strncpy(newPtr1->body.chars, segPtr->body.chars, (size_t) index);
+ newPtr1->body.chars[index] = 0;
+ newPtr2->typePtr = &ckTextCharType;
+ newPtr2->nextPtr = segPtr->nextPtr;
+ newPtr2->size = segPtr->size - index;
+ strcpy(newPtr2->body.chars, segPtr->body.chars + index);
+ ckfree((char*) segPtr);
+ return newPtr1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharCleanupProc --
+ *
+ * This procedure merges adjacent character segments into
+ * a single character segment, if possible.
+ *
+ * Results:
+ * The return value is a pointer to the first segment in
+ * the (new) list of segments that used to start with segPtr.
+ *
+ * Side effects:
+ * Storage for the segments may be allocated and freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static CkTextSegment *
+CharCleanupProc(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Pointer to first of two adjacent
+ * segments to join. */
+ CkTextLine *linePtr; /* Line containing segments (not
+ * used). */
+{
+ CkTextSegment *segPtr2, *newPtr;
+
+ segPtr2 = segPtr->nextPtr;
+ if ((segPtr2 == NULL) || (segPtr2->typePtr != &ckTextCharType)) {
+ return segPtr;
+ }
+ newPtr = (CkTextSegment *) ckalloc(CSEG_SIZE(
+ segPtr->size + segPtr2->size));
+ newPtr->typePtr = &ckTextCharType;
+ newPtr->nextPtr = segPtr2->nextPtr;
+ newPtr->size = segPtr->size + segPtr2->size;
+ strcpy(newPtr->body.chars, segPtr->body.chars);
+ strcpy(newPtr->body.chars + segPtr->size, segPtr2->body.chars);
+ ckfree((char*) segPtr);
+ ckfree((char*) segPtr2);
+ return newPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharDeleteProc --
+ *
+ * This procedure is invoked to delete a character segment.
+ *
+ * Results:
+ * Always returns 0 to indicate that the segment was deleted.
+ *
+ * Side effects:
+ * Storage for the segment is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+CharDeleteProc(segPtr, linePtr, treeGone)
+ CkTextSegment *segPtr; /* Segment to delete. */
+ CkTextLine *linePtr; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ ckfree((char*) segPtr);
+ return 0;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharCheckProc --
+ *
+ * This procedure is invoked to perform consistency checks
+ * on character segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the segment isn't inconsistent then the procedure
+ * panics.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+CharCheckProc(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line containing segment. */
+{
+ /*
+ * Make sure that the segment contains the number of
+ * characters indicated by its header, and that the last
+ * segment in a line ends in a newline. Also make sure
+ * that there aren't ever two character segments adjacent
+ * to each other: they should be merged together.
+ */
+
+ if (segPtr->size <= 0) {
+ panic("CharCheckProc: segment has size <= 0");
+ }
+ if ((int) strlen(segPtr->body.chars) != segPtr->size) {
+ panic("CharCheckProc: segment has wrong size");
+ }
+ if (segPtr->nextPtr == NULL) {
+ if (segPtr->body.chars[segPtr->size-1] != '\n') {
+ panic("CharCheckProc: line doesn't end with newline");
+ }
+ } else {
+ if (segPtr->nextPtr->typePtr == &ckTextCharType) {
+ panic("CharCheckProc: adjacent character segments weren't merged");
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ToggleDeleteProc --
+ *
+ * This procedure is invoked to delete toggle segments.
+ *
+ * Results:
+ * Returns 1 to indicate that the segment may not be deleted,
+ * unless the entire B-tree is going away.
+ *
+ * Side effects:
+ * If the tree is going away then the toggle's memory is
+ * freed; otherwise the toggle counts in nodes above the
+ * segment get updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ToggleDeleteProc(segPtr, linePtr, treeGone)
+ CkTextSegment *segPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ if (treeGone) {
+ ckfree((char *) segPtr);
+ return 0;
+ }
+
+ /*
+ * This toggle is in the middle of a range of characters that's
+ * being deleted. Refuse to die. We'll be moved to the end of
+ * the deleted range and our cleanup procedure will be called
+ * later. Decrement node toggle counts here, and set a flag
+ * so we'll re-increment them in the cleanup procedure.
+ */
+
+ if (segPtr->body.toggle.inNodeCounts) {
+ ChangeNodeToggleCount(linePtr->parentPtr,
+ segPtr->body.toggle.tagPtr, -1);
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+ return 1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ToggleCleanupProc --
+ *
+ * This procedure when a toggle is part of a line that's
+ * been modified in some way. It's invoked after the
+ * modifications are complete.
+ *
+ * Results:
+ * The return value is the head segment in a new list
+ * that is to replace the tail of the line that used to
+ * start at segPtr. This allows the procedure to delete
+ * or modify segPtr.
+ *
+ * Side effects:
+ * Toggle counts in the nodes above the new line will be
+ * updated if they're not already. Toggles may be collapsed
+ * if there are duplicate toggles at the same position.
+ *
+ *--------------------------------------------------------------
+ */
+
+static CkTextSegment *
+ToggleCleanupProc(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line that now contains segment. */
+{
+ CkTextSegment *segPtr2, *prevPtr;
+ int counts;
+
+ /*
+ * If this is a toggle-off segment, look ahead through the next
+ * segments to see if there's a toggle-on segment for the same tag
+ * before any segments with non-zero size. If so then the two
+ * toggles cancel each other; remove them both.
+ */
+
+ if (segPtr->typePtr == &ckTextToggleOffType) {
+ for (prevPtr = segPtr, segPtr2 = prevPtr->nextPtr;
+ (segPtr2 != NULL) && (segPtr2->size == 0);
+ prevPtr = segPtr2, segPtr2 = prevPtr->nextPtr) {
+ if (segPtr2->typePtr != &ckTextToggleOnType) {
+ continue;
+ }
+ if (segPtr2->body.toggle.tagPtr != segPtr->body.toggle.tagPtr) {
+ continue;
+ }
+ counts = segPtr->body.toggle.inNodeCounts
+ + segPtr2->body.toggle.inNodeCounts;
+ if (counts != 0) {
+ ChangeNodeToggleCount(linePtr->parentPtr,
+ segPtr->body.toggle.tagPtr, -counts);
+ }
+ prevPtr->nextPtr = segPtr2->nextPtr;
+ ckfree((char *) segPtr2);
+ segPtr2 = segPtr->nextPtr;
+ ckfree((char *) segPtr);
+ return segPtr2;
+ }
+ }
+
+ if (!segPtr->body.toggle.inNodeCounts) {
+ ChangeNodeToggleCount(linePtr->parentPtr,
+ segPtr->body.toggle.tagPtr, 1);
+ segPtr->body.toggle.inNodeCounts = 1;
+ }
+ return segPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ToggleLineChangeProc --
+ *
+ * This procedure is invoked when a toggle segment is about
+ * to move from one line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Toggle counts are decremented in the nodes above the line.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ToggleLineChangeProc(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line that used to contain segment. */
+{
+ if (segPtr->body.toggle.inNodeCounts) {
+ ChangeNodeToggleCount(linePtr->parentPtr,
+ segPtr->body.toggle.tagPtr, -1);
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * ToggleCheckProc --
+ *
+ * This procedure is invoked to perform consistency checks
+ * on toggle segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a consistency problem is found the procedure panics.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ToggleCheckProc(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line containing segment. */
+{
+ register Summary *summaryPtr;
+
+ if (segPtr->size != 0) {
+ panic("ToggleCheckProc: segment had non-zero size");
+ }
+ if (!segPtr->body.toggle.inNodeCounts) {
+ panic("ToggleCheckProc: toggle counts not updated in nodes");
+ }
+ for (summaryPtr = linePtr->parentPtr->summaryPtr; ;
+ summaryPtr = summaryPtr->nextPtr) {
+ if (summaryPtr == NULL) {
+ panic("ToggleCheckProc: tag not present in node");
+ }
+ if (summaryPtr->tagPtr == segPtr->body.toggle.tagPtr) {
+ break;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkBTreeCharsInLine --
+ *
+ * This procedure returns a count of the number of characters
+ * in a given line.
+ *
+ * Results:
+ * The return value is the character count for linePtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkBTreeCharsInLine(linePtr)
+ CkTextLine *linePtr; /* Line whose characters should be
+ * counted. */
+{
+ CkTextSegment *segPtr;
+ int count = 0;
+
+#if CK_USE_UTF
+ for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ count += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
+ } else {
+ count += segPtr->size;
+ }
+ }
+#else
+ for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ count += segPtr->size;
+ }
+#endif
+ return count;
+}
--- /dev/null
+/*
+ * ckTextDisp.c --
+ *
+ * This module provides facilities to display text widgets. It is
+ * the only place where information is kept about the screen layout
+ * of text widgets.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+
+/*
+ * The following structure describes how to display a range of characters.
+ * The information is generated by scanning all of the tags associated
+ * with the characters and combining that with default information for
+ * the overall widget. These structures form the hash keys for
+ * dInfoPtr->styleTable.
+ */
+
+typedef struct StyleValues {
+ int fg, bg, attr;
+ int justify; /* Justification style for text. */
+ int lMargin1; /* Left margin, in pixels, for first display
+ * line of each text line. */
+ int lMargin2; /* Left margin, in pixels, for second and
+ * later display lines of each text line. */
+ int rMargin; /* Right margin, in pixels. */
+ CkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
+ * be NULL). */
+ Ck_Uid wrapMode; /* How to handle wrap-around for this tag.
+ * One of ckTextCharUid, ckTextNoneUid,
+ * or ckTextWordUid. */
+} StyleValues;
+
+/*
+ * The following structure extends the StyleValues structure above with
+ * graphics contexts used to actually draw the characters. The entries
+ * in dInfoPtr->styleTable point to structures of this type.
+ */
+
+typedef struct Style {
+ int refCount; /* Number of times this structure is
+ * referenced in Chunks. */
+ StyleValues *sValuePtr; /* Raw information from which GCs were
+ * derived. */
+ Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable. Used
+ * to delete entry. */
+} Style;
+
+/*
+ * The following macro determines whether two styles have the same
+ * background so that, for example, no beveled border should be drawn
+ * between them.
+ */
+
+#define SAME_BACKGROUND(s1, s2) \
+ (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
+ && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
+ && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
+ && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
+
+/*
+ * The following structure describes one line of the display, which may
+ * be either part or all of one line of the text.
+ */
+
+typedef struct DLine {
+ CkTextIndex index; /* Identifies first character in text
+ * that is displayed on this line. */
+ int count; /* Number of characters accounted for by this
+ * display line, including a trailing space
+ * or newline that isn't actually displayed. */
+ int y; /* Y-position at which line is supposed to
+ * be drawn (topmost pixel of rectangular
+ * area occupied by line). */
+ int oldY; /* Y-position at which line currently
+ * appears on display. -1 means line isn't
+ * currently visible on display and must be
+ * redrawn. This is used to move lines by
+ * scrolling rather than re-drawing. */
+ int height; /* Height of line, in chars. */
+ int length; /* Total length of line, in chars. */
+ CkTextDispChunk *chunkPtr; /* Pointer to first chunk in list of all
+ * of those that are displayed on this
+ * line of the screen. */
+ struct DLine *nextPtr; /* Next in list of all display lines for
+ * this window. The list is sorted in
+ * order from top to bottom. Note: the
+ * next DLine doesn't always correspond
+ * to the next line of text: (a) can have
+ * multiple DLines for one text line, and
+ * (b) can have gaps where DLine's have been
+ * deleted because they're out of date. */
+ int flags; /* Various flag bits: see below for values. */
+} DLine;
+
+/*
+ * Flag bits for DLine structures:
+ *
+ * NEW_LAYOUT - Non-zero means that the line has been
+ * re-layed out since the last time the
+ * display was updated.
+ * TOP_LINE - Non-zero means that this was the top line
+ * in the window the last time that the window
+ * was laid out. This is important because
+ * a line may be displayed differently if its
+ * at the top or bottom than if it's in the
+ * middle (e.g. beveled edges aren't displayed
+ * for middle lines if the adjacent line has
+ * a similar background).
+ * BOTTOM_LINE - Non-zero means that this was the bottom line
+ * in the window the last time that the window
+ * was laid out.
+ */
+
+#define NEW_LAYOUT 2
+#define TOP_LINE 4
+#define BOTTOM_LINE 8
+
+/*
+ * Overall display information for a text widget:
+ */
+
+typedef struct DInfo {
+ Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
+ * to Styles for this widget. */
+ DLine *dLinePtr; /* First in list of all display lines for
+ * this widget, in order from top to bottom. */
+ int x; /* First x-coordinate that may be used for
+ * actually displaying line information.
+ * Leaves space for border, etc. */
+ int y; /* First y-coordinate that may be used for
+ * actually displaying line information.
+ * Leaves space for border, etc. */
+ int maxX; /* First x-coordinate to right of available
+ * space for displaying lines. */
+ int maxY; /* First y-coordinate below available
+ * space for displaying lines. */
+ int topOfEof; /* Top-most pixel (lowest y-value) that has
+ * been drawn in the appropriate fashion for
+ * the portion of the window after the last
+ * line of the text. This field is used to
+ * figure out when to redraw part or all of
+ * the eof field. */
+
+ /*
+ * Information used for scrolling:
+ */
+
+ int newCharOffset; /* Desired x scroll position, measured as the
+ * number of average-size characters off-screen
+ * to the left for a line with no left
+ * margin. */
+ int curOffset; /* Actual x scroll position, measured as the
+ * number of chars off-screen to the left. */
+ int maxLength; /* Length in chars of longest line that's
+ * visible in window (length may exceed window
+ * size). If there's no wrapping, this will
+ * be zero. */
+ double xScrollFirst, xScrollLast;
+ /* Most recent values reported to horizontal
+ * scrollbar; used to eliminate unnecessary
+ * reports. */
+ double yScrollFirst, yScrollLast;
+ /* Most recent values reported to vertical
+ * scrollbar; used to eliminate unnecessary
+ * reports. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ int dLinesInvalidated; /* This value is set to 1 whenever something
+ * happens that invalidates information in
+ * DLine structures; if a redisplay
+ * is in progress, it will see this and
+ * abort the redisplay. This is needed
+ * because, for example, an embedded window
+ * could change its size when it is first
+ * displayed, invalidating the DLine that
+ * is currently being displayed. If redisplay
+ * continues, it will use freed memory and
+ * could dump core. */
+ int flags; /* Various flag values: see below for
+ * definitions. */
+} DInfo;
+
+/*
+ * In CkTextDispChunk structures for character segments, the clientData
+ * field points to one of the following structures:
+ */
+
+typedef struct CharInfo {
+ int numChars; /* Number of characters to display. */
+ CkWindow *winPtr; /* For Ck_SetWindowAttr. */
+ char chars[4]; /* Characters to display. Actual size
+ * will be numChars, not 4. THIS MUST BE
+ * THE LAST FIELD IN THE STRUCTURE. */
+} CharInfo;
+
+/*
+ * Flag values for DInfo structures:
+ *
+ * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
+ * for this window are partially or completely
+ * out of date and need to be recomputed.
+ * REDRAW_PENDING: Means that a when-idle handler has been
+ * scheduled to update the display.
+ * REDRAW_BORDERS: Means window border or pad area has
+ * potentially been damaged and must be redrawn.
+ * REPICK_NEEDED: 1 means that the widget has been modified
+ * in a way that could change the current
+ * character (a different character might be
+ * under the mouse cursor now). Need to
+ * recompute the current character before
+ * the next redisplay.
+ */
+
+#define DINFO_OUT_OF_DATE 1
+#define REDRAW_PENDING 2
+#define REDRAW_BORDERS 4
+#define REPICK_NEEDED 8
+
+/*
+ * The following counters keep statistics about redisplay that can be
+ * checked to see how clever this code is at reducing redisplays.
+ */
+
+static int numRedisplays; /* Number of calls to DisplayText. */
+static int linesRedrawn; /* Number of calls to DisplayDLine. */
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void AdjustForTab _ANSI_ARGS_((CkText *textPtr,
+ CkTextTabArray *tabArrayPtr, int index,
+ CkTextDispChunk *chunkPtr));
+static void CharBboxProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
+ int index, int y, int lineHeight, int baseline,
+ int *xPtr, int *yPtr, int *widthPtr,
+ int *heightPtr));
+static void CharDisplayProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
+ int x, int y, int height, int baseline,
+ WINDOW *window, int screenY));
+static int CharMeasureProc _ANSI_ARGS_((CkTextDispChunk *chunkPtr,
+ int x));
+static void CharUndisplayProc _ANSI_ARGS_((CkText *textPtr,
+ CkTextDispChunk *chunkPtr));
+static void DisplayDLine _ANSI_ARGS_((CkText *textPtr,
+ DLine *dlPtr, DLine *prevPtr, WINDOW *window));
+static void DisplayText _ANSI_ARGS_((ClientData clientData));
+static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr,
+ CkTextIndex *indexPtr));
+static void FreeDLines _ANSI_ARGS_((CkText *textPtr,
+ DLine *firstPtr, DLine *lastPtr, int unlink));
+static void FreeStyle _ANSI_ARGS_((CkText *textPtr,
+ Style *stylePtr));
+static Style * GetStyle _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr));
+static void GetXView _ANSI_ARGS_((Tcl_Interp *interp,
+ CkText *textPtr, int report));
+static void GetYView _ANSI_ARGS_((Tcl_Interp *interp,
+ CkText *textPtr, int report));
+static DLine * LayoutDLine _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr));
+static void MeasureUp _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *srcPtr, int distance,
+ CkTextIndex *dstPtr));
+static void UpdateDisplayInfo _ANSI_ARGS_((CkText *textPtr));
+static void ScrollByLines _ANSI_ARGS_((CkText *textPtr,
+ int offset));
+static int SizeOfTab _ANSI_ARGS_((CkText *textPtr,
+ CkTextTabArray *tabArrayPtr, int index, int x,
+ int maxX));
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextCreateDInfo --
+ *
+ * This procedure is called when a new text widget is created.
+ * Its job is to set up display-related information for the widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A DInfo data structure is allocated and initialized and attached
+ * to textPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextCreateDInfo(textPtr)
+ CkText *textPtr; /* Overall information for text widget. */
+{
+ register DInfo *dInfoPtr;
+
+ dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
+ Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
+ dInfoPtr->dLinePtr = NULL;
+ dInfoPtr->topOfEof = 0;
+ dInfoPtr->newCharOffset = 0;
+ dInfoPtr->curOffset = 0;
+ dInfoPtr->maxLength = 0;
+ dInfoPtr->xScrollFirst = -1;
+ dInfoPtr->xScrollLast = -1;
+ dInfoPtr->yScrollFirst = -1;
+ dInfoPtr->yScrollLast = -1;
+ dInfoPtr->dLinesInvalidated = 0;
+ dInfoPtr->flags = DINFO_OUT_OF_DATE;
+ textPtr->dInfoPtr = dInfoPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextFreeDInfo --
+ *
+ * This procedure is called to free up all of the private display
+ * information kept by this file for a text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Lots of resources get freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextFreeDInfo(textPtr)
+ CkText *textPtr; /* Overall information for text widget. */
+{
+ register DInfo *dInfoPtr = textPtr->dInfoPtr;
+
+ /*
+ * Be careful to free up styleTable *after* freeing up all the
+ * DLines, so that the hash table is still intact to free up the
+ * style-related information from the lines. Once the lines are
+ * all free then styleTable will be empty.
+ */
+
+ FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
+ Tcl_DeleteHashTable(&dInfoPtr->styleTable);
+ if (dInfoPtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
+ }
+ ckfree((char *) dInfoPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetStyle --
+ *
+ * This procedure creates all the information needed to display
+ * text at a particular location.
+ *
+ * Results:
+ * The return value is a pointer to a Style structure that
+ * corresponds to *sValuePtr.
+ *
+ * Side effects:
+ * A new entry may be created in the style table for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Style *
+GetStyle(textPtr, indexPtr)
+ CkText *textPtr; /* Overall information about text widget. */
+ CkTextIndex *indexPtr; /* The character in the text for which
+ * display information is wanted. */
+{
+ CkTextTag **tagPtrs;
+ register CkTextTag *tagPtr;
+ StyleValues styleValues;
+ Style *stylePtr;
+ Tcl_HashEntry *hPtr;
+ int numTags, new, i;
+
+ /*
+ * The variables below keep track of the highest-priority specification
+ * that has occurred for each of the various fields of the StyleValues.
+ */
+
+ int bgPrio, fgPrio, attrPrio, justifyPrio;
+ int lMargin1Prio, lMargin2Prio, rMarginPrio;
+ int tabPrio, wrapPrio;
+
+ /*
+ * Find out what tags are present for the character, then compute
+ * a StyleValues structure corresponding to those tags (scan
+ * through all of the tags, saving information for the highest-
+ * priority tag).
+ */
+
+ tagPtrs = CkBTreeGetTags(indexPtr, &numTags);
+ bgPrio = fgPrio = attrPrio = justifyPrio = -1;
+ lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
+ tabPrio = wrapPrio = -1;
+ memset((VOID *) &styleValues, 0, sizeof(StyleValues));
+ styleValues.fg = textPtr->fg;
+ styleValues.bg = textPtr->bg;
+ styleValues.attr = textPtr->attr;
+ styleValues.justify = CK_JUSTIFY_LEFT;
+ styleValues.tabArrayPtr = textPtr->tabArrayPtr;
+ styleValues.wrapMode = textPtr->wrapMode;
+ for (i = 0 ; i < numTags; i++) {
+ tagPtr = tagPtrs[i];
+ if ((tagPtr->bg != -1) && (tagPtr->priority > bgPrio)) {
+ styleValues.bg = tagPtr->bg;
+ bgPrio = tagPtr->priority;
+ }
+ if ((tagPtr->fg != -1) && (tagPtr->priority > fgPrio)) {
+ styleValues.fg = tagPtr->fg;
+ fgPrio = tagPtr->priority;
+ }
+ if ((tagPtr->attr != -1) && (tagPtr->priority > attrPrio)) {
+ styleValues.attr = tagPtr->attr;
+ attrPrio = tagPtr->priority;
+ }
+ if ((tagPtr->justifyString != NULL)
+ && (tagPtr->priority > justifyPrio)) {
+ styleValues.justify = tagPtr->justify;
+ justifyPrio = tagPtr->priority;
+ }
+ if ((tagPtr->lMargin1String != NULL)
+ && (tagPtr->priority > lMargin1Prio)) {
+ styleValues.lMargin1 = tagPtr->lMargin1;
+ lMargin1Prio = tagPtr->priority;
+ }
+ if ((tagPtr->lMargin2String != NULL)
+ && (tagPtr->priority > lMargin2Prio)) {
+ styleValues.lMargin2 = tagPtr->lMargin2;
+ lMargin2Prio = tagPtr->priority;
+ }
+ if ((tagPtr->rMarginString != NULL)
+ && (tagPtr->priority > rMarginPrio)) {
+ styleValues.rMargin = tagPtr->rMargin;
+ rMarginPrio = tagPtr->priority;
+ }
+ if ((tagPtr->tabString != NULL)
+ && (tagPtr->priority > tabPrio)) {
+ styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
+ tabPrio = tagPtr->priority;
+ }
+ if ((tagPtr->wrapMode != NULL)
+ && (tagPtr->priority > wrapPrio)) {
+ styleValues.wrapMode = tagPtr->wrapMode;
+ wrapPrio = tagPtr->priority;
+ }
+ }
+ if (tagPtrs != NULL) {
+ ckfree((char *) tagPtrs);
+ }
+
+ /*
+ * Use an existing style if there's one around that matches.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
+ (char *) &styleValues, &new);
+ if (!new) {
+ stylePtr = (Style *) Tcl_GetHashValue(hPtr);
+ stylePtr->refCount++;
+ return stylePtr;
+ }
+
+ /*
+ * No existing style matched. Make a new one.
+ */
+
+ stylePtr = (Style *) ckalloc(sizeof(Style));
+ stylePtr->refCount = 1;
+ stylePtr->sValuePtr = (StyleValues *)
+ Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
+ stylePtr->hPtr = hPtr;
+ Tcl_SetHashValue(hPtr, stylePtr);
+ return stylePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeStyle --
+ *
+ * This procedure is called when a Style structure is no longer
+ * needed. It decrements the reference count and frees up the
+ * space for the style structure if the reference count is 0.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The storage and other resources associated with the style
+ * are freed up if no-one's still using it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeStyle(textPtr, stylePtr)
+ CkText *textPtr; /* Information about overall widget. */
+ register Style *stylePtr; /* Information about style to be freed. */
+
+{
+ stylePtr->refCount--;
+ if (stylePtr->refCount == 0) {
+ Tcl_DeleteHashEntry(stylePtr->hPtr);
+ ckfree((char *) stylePtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * LayoutDLine --
+ *
+ * This procedure generates a single DLine structure for a display
+ * line whose leftmost character is given by indexPtr.
+ *
+ * Results:
+ * The return value is a pointer to a DLine structure desribing the
+ * display line. All fields are filled in and correct except for
+ * y and nextPtr.
+ *
+ * Side effects:
+ * Storage is allocated for the new DLine.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static DLine *
+LayoutDLine(textPtr, indexPtr)
+ CkText *textPtr; /* Overall information about text widget. */
+ CkTextIndex *indexPtr; /* Beginning of display line. May not
+ * necessarily point to a character segment. */
+{
+ register DLine *dlPtr; /* New display line. */
+ CkTextSegment *segPtr; /* Current segment in text. */
+ CkTextDispChunk *lastChunkPtr; /* Last chunk allocated so far
+ * for line. */
+ CkTextDispChunk *chunkPtr; /* Current chunk. */
+ CkTextIndex curIndex;
+ CkTextDispChunk *breakChunkPtr; /* Chunk containing best word break
+ * point, if any. */
+ CkTextIndex breakIndex; /* Index of first character in
+ * breakChunkPtr. */
+ int breakCharOffset; /* Character within breakChunkPtr just
+ * to right of best break point. */
+ int noCharsYet; /* Non-zero means that no characters
+ * have been placed on the line yet. */
+ int justify; /* How to justify line: taken from
+ * style for first character in line. */
+ int jIndent; /* Additional indentation (beyond
+ * margins) due to justification. */
+ int rMargin; /* Right margin width for line. */
+ Ck_Uid wrapMode; /* Wrap mode to use for this line. */
+ int x = 0, maxX = 0; /* Initializations needed only to
+ * stop compiler warnings. */
+ int wholeLine; /* Non-zero means this display line
+ * runs to the end of the text line. */
+ int tabIndex; /* Index of the current tab stop. */
+ int gotTab; /* Non-zero means the current chunk
+ * contains a tab. */
+ CkTextDispChunk *tabChunkPtr; /* Pointer to the chunk containing
+ * the previous tab stop. */
+ int maxChars; /* Maximum number of characters to
+ * include in this chunk. */
+ CkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
+ * style for first character on line. */
+ int tabSize; /* Number of pixels consumed by current
+ * tab stop. */
+ int offset, code;
+ StyleValues *sValuePtr;
+
+ /*
+ * Create and initialize a new DLine structure.
+ */
+
+ dlPtr = (DLine *) ckalloc(sizeof(DLine));
+ dlPtr->index = *indexPtr;
+ dlPtr->count = 0;
+ dlPtr->y = 0;
+ dlPtr->oldY = -1;
+ dlPtr->height = 0;
+ dlPtr->chunkPtr = NULL;
+ dlPtr->nextPtr = NULL;
+ dlPtr->flags = NEW_LAYOUT;
+
+ /*
+ * Each iteration of the loop below creates one CkTextDispChunk for
+ * the new display line. The line will always have at least one
+ * chunk (for the newline character at the end, if there's nothing
+ * else available).
+ */
+
+ curIndex = *indexPtr;
+ lastChunkPtr = NULL;
+ chunkPtr = NULL;
+ noCharsYet = 1;
+ breakChunkPtr = NULL;
+ breakCharOffset = 0;
+ justify = CK_JUSTIFY_LEFT;
+ tabIndex = -1;
+ tabChunkPtr = NULL;
+ tabArrayPtr = NULL;
+ rMargin = 0;
+ wrapMode = ckTextCharUid;
+ tabSize = 0;
+
+ /*
+ * Find the first segment to consider for the line. Can't call
+ * CkTextIndexToSeg for this because it won't return a segment
+ * with zero size (such as the insertion cursor's mark).
+ */
+
+ for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
+ (offset > 0) && (offset >= segPtr->size);
+ offset -= segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+
+ while (segPtr != NULL) {
+ if (segPtr->typePtr->layoutProc == NULL) {
+ segPtr = segPtr->nextPtr;
+ offset = 0;
+ continue;
+ }
+ if (chunkPtr == NULL) {
+ chunkPtr = (CkTextDispChunk *) ckalloc(sizeof(CkTextDispChunk));
+ chunkPtr->nextPtr = NULL;
+ }
+ chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
+
+ /*
+ * Save style information such as justification and indentation,
+ * up until the first character is encountered, then retain that
+ * information for the rest of the line.
+ */
+
+ if (noCharsYet) {
+ tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
+ justify = chunkPtr->stylePtr->sValuePtr->justify;
+ rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
+ wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
+ x = ((curIndex.charIndex == 0)
+ ? chunkPtr->stylePtr->sValuePtr->lMargin1
+ : chunkPtr->stylePtr->sValuePtr->lMargin2);
+ if (wrapMode == ckTextNoneUid) {
+ maxX = INT_MAX;
+ } else {
+ maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
+ - rMargin;
+ if (maxX < x) {
+ maxX = x;
+ }
+ }
+ }
+
+ /*
+ * See if there is a tab in the current chunk; if so, only
+ * layout characters up to (and including) the tab.
+ */
+
+ gotTab = 0;
+ maxChars = segPtr->size - offset;
+ if (justify == CK_JUSTIFY_LEFT) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ char *p;
+
+ for (p = segPtr->body.chars + offset; *p != 0; p++) {
+ if (*p == '\t') {
+ maxChars = (p + 1 - segPtr->body.chars) - offset;
+ gotTab = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ chunkPtr->x = x;
+ code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
+ offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
+ chunkPtr);
+ if (code <= 0) {
+ FreeStyle(textPtr, chunkPtr->stylePtr);
+ if (code < 0) {
+ /*
+ * This segment doesn't wish to display itself (e.g. most
+ * marks).
+ */
+
+ segPtr = segPtr->nextPtr;
+ offset = 0;
+ continue;
+ }
+
+ /*
+ * No characters from this segment fit in the window: this
+ * means we're at the end of the display line.
+ */
+
+ if (chunkPtr != NULL) {
+ ckfree((char *) chunkPtr);
+ }
+ break;
+ }
+ if (chunkPtr->numChars > 0) {
+ noCharsYet = 0;
+ }
+ if (lastChunkPtr == NULL) {
+ dlPtr->chunkPtr = chunkPtr;
+ } else {
+ lastChunkPtr->nextPtr = chunkPtr;
+ }
+ lastChunkPtr = chunkPtr;
+ x += chunkPtr->width;
+ if (chunkPtr->breakIndex > 0) {
+ breakCharOffset = chunkPtr->breakIndex;
+ breakIndex = curIndex;
+ breakChunkPtr = chunkPtr;
+ }
+ if (chunkPtr->numChars != maxChars) {
+ break;
+ }
+
+ /*
+ * If we're at a new tab, adjust the layout for all the chunks
+ * pertaining to the previous tab. Also adjust the amount of
+ * space left in the line to account for space that will be eaten
+ * up by the tab.
+ */
+
+ if (gotTab) {
+ if (tabIndex >= 0) {
+ AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
+ x = chunkPtr->x + chunkPtr->width;
+ }
+ tabIndex++;
+ tabChunkPtr = chunkPtr;
+ tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
+ if (tabSize >= (maxX - x)) {
+ break;
+ }
+ }
+ curIndex.charIndex += chunkPtr->numChars;
+ offset += chunkPtr->numChars;
+ if (offset >= segPtr->size) {
+ offset = 0;
+ segPtr = segPtr->nextPtr;
+ }
+ chunkPtr = NULL;
+ }
+ if (noCharsYet) {
+ panic("LayoutDLine couldn't place any characters on a line");
+ }
+ wholeLine = (segPtr == NULL);
+
+ /*
+ * We're at the end of the display line. Throw away everything
+ * after the most recent word break, if there is one; this may
+ * potentially require the last chunk to be layed out again.
+ */
+
+ if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
+ || (breakCharOffset != lastChunkPtr->numChars))) {
+ while (1) {
+ chunkPtr = breakChunkPtr->nextPtr;
+ if (chunkPtr == NULL) {
+ break;
+ }
+ FreeStyle(textPtr, chunkPtr->stylePtr);
+ breakChunkPtr->nextPtr = chunkPtr->nextPtr;
+ (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
+ ckfree((char *) chunkPtr);
+ }
+ if (breakCharOffset != breakChunkPtr->numChars) {
+ (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
+ segPtr = CkTextIndexToSeg(&breakIndex, &offset);
+ (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
+ segPtr, offset, maxX, breakCharOffset, 0,
+ wrapMode, breakChunkPtr);
+ }
+ lastChunkPtr = breakChunkPtr;
+ wholeLine = 0;
+ }
+
+ /*
+ * Make tab adjustments for the last tab stop, if there is one.
+ */
+
+ if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
+ AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
+ }
+
+ /*
+ * Make one more pass over the line to recompute various things
+ * like its height, length, and total number of characters. Also
+ * modify the x-locations of chunks to reflect justification.
+ * If we're not wrapping, I'm not sure what is the best way to
+ * handle left and center justification: should the total length,
+ * for purposes of justification, be (a) the window width, (b)
+ * the length of the longest line in the window, or (c) the length
+ * of the longest line in the text? (c) isn't available, (b) seems
+ * weird, since it can change with vertical scrolling, so (a) is
+ * what is implemented below.
+ */
+
+ if (wrapMode == ckTextNoneUid) {
+ maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
+ }
+ dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
+ if (justify == CK_JUSTIFY_LEFT) {
+ jIndent = 0;
+ } else if (justify == CK_JUSTIFY_RIGHT) {
+ jIndent = maxX - dlPtr->length;
+ } else {
+ jIndent = (maxX - dlPtr->length)/2;
+ }
+ for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
+ chunkPtr = chunkPtr->nextPtr) {
+ chunkPtr->x += jIndent;
+ dlPtr->count += chunkPtr->numChars;
+ if (chunkPtr->minHeight > dlPtr->height) {
+ dlPtr->height = chunkPtr->minHeight;
+ }
+ sValuePtr = chunkPtr->stylePtr->sValuePtr;
+ }
+ sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
+
+ /*
+ * Recompute line length: may have changed because of justification.
+ */
+
+ dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
+ return dlPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateDisplayInfo --
+ *
+ * This procedure is invoked to recompute some or all of the
+ * DLine structures for a text widget. At the time it is called
+ * the DLine structures still left in the widget are guaranteed
+ * to be correct except that (a) the y-coordinates aren't
+ * necessarily correct, (b) there may be missing structures
+ * (the DLine structures get removed as soon as they are potentially
+ * out-of-date), and (c) DLine structures that don't start at the
+ * beginning of a line may be incorrect if previous information in
+ * the same line changed size in a way that moved a line boundary
+ * (DLines for any info that changed will have been deleted, but
+ * not DLines for unchanged info in the same text line).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Upon return, the DLine information for textPtr correctly reflects
+ * the positions where characters will be displayed. However, this
+ * procedure doesn't actually bring the display up-to-date.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateDisplayInfo(textPtr)
+ CkText *textPtr; /* Text widget to update. */
+{
+ register DInfo *dInfoPtr = textPtr->dInfoPtr;
+ register DLine *dlPtr, *prevPtr;
+ CkTextIndex index;
+ CkTextLine *lastLinePtr;
+ int y, maxY, maxOffset;
+
+ if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
+ return;
+ }
+ dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
+
+ /*
+ * Delete any DLines that are now above the top of the window.
+ */
+
+ index = textPtr->topIndex;
+ dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
+ if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
+ FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
+ }
+
+ /*
+ *--------------------------------------------------------------
+ * Scan through the contents of the window from top to bottom,
+ * recomputing information for lines that are missing.
+ *--------------------------------------------------------------
+ */
+
+ lastLinePtr = CkBTreeFindLine(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree));
+ dlPtr = dInfoPtr->dLinePtr;
+ prevPtr = NULL;
+ y = dInfoPtr->y;
+ maxY = dInfoPtr->maxY;
+ while (1) {
+ register DLine *newPtr;
+
+ if (index.linePtr == lastLinePtr) {
+ break;
+ }
+
+ /*
+ * There are three possibilities right now:
+ * (a) the next DLine (dlPtr) corresponds exactly to the next
+ * information we want to display: just use it as-is.
+ * (b) the next DLine corresponds to a different line, or to
+ * a segment that will be coming later in the same line:
+ * leave this DLine alone in the hopes that we'll be able
+ * to use it later, then create a new DLine in front of
+ * it.
+ * (c) the next DLine corresponds to a segment in the line we
+ * want, but it's a segment that has already been processed
+ * or will never be processed. Delete the DLine and try
+ * again.
+ *
+ * One other twist on all this. It's possible for 3D borders
+ * to interact between lines (see DisplayLineBackground) so if
+ * a line is relayed out and has styles with 3D borders, its
+ * neighbors have to be redrawn if they have 3D borders too,
+ * since the interactions could have changed (the neighbors
+ * don't have to be relayed out, just redrawn).
+ */
+
+ if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
+ /*
+ * Case (b) -- must make new DLine.
+ */
+
+ makeNewDLine:
+ if (ckTextDebug) {
+ char string[TK_POS_CHARS];
+
+ /*
+ * Debugging is enabled, so keep a log of all the lines
+ * that were re-layed out. The test suite uses this
+ * information.
+ */
+
+ CkTextPrintIndex(&index, string);
+ Tcl_SetVar2(textPtr->interp, "ck_textRelayout", (char *) NULL,
+ string,
+ TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
+ }
+ newPtr = LayoutDLine(textPtr, &index);
+ if (prevPtr == NULL) {
+ dInfoPtr->dLinePtr = newPtr;
+ } else {
+ prevPtr->nextPtr = newPtr;
+ }
+ newPtr->nextPtr = dlPtr;
+ dlPtr = newPtr;
+ } else {
+ /*
+ * DlPtr refers to the line we want. Next check the
+ * index within the line.
+ */
+
+ if (index.charIndex == dlPtr->index.charIndex) {
+ /*
+ * Case (a) -- can use existing display line as-is.
+ */
+ goto lineOK;
+ }
+ if (index.charIndex < dlPtr->index.charIndex) {
+ goto makeNewDLine;
+ }
+
+ /*
+ * Case (c) -- dlPtr is useless. Discard it and start
+ * again with the next display line.
+ */
+
+ newPtr = dlPtr->nextPtr;
+ FreeDLines(textPtr, dlPtr, newPtr, 0);
+ dlPtr = newPtr;
+ continue;
+ }
+
+ /*
+ * Advance to the start of the next line.
+ */
+
+ lineOK:
+ dlPtr->y = y;
+ y += dlPtr->height;
+#if CK_USE_UTF
+ CkTextIndexForwBytes(&index, dlPtr->count, &index);
+#else
+ CkTextIndexForwChars(&index, dlPtr->count, &index);
+#endif
+ prevPtr = dlPtr;
+ dlPtr = dlPtr->nextPtr;
+
+ /*
+ * If we switched text lines, delete any DLines left for the
+ * old text line.
+ */
+
+ if (index.linePtr != prevPtr->index.linePtr) {
+ register DLine *nextPtr;
+
+ nextPtr = dlPtr;
+ while ((nextPtr != NULL)
+ && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
+ nextPtr = nextPtr->nextPtr;
+ }
+ if (nextPtr != dlPtr) {
+ FreeDLines(textPtr, dlPtr, nextPtr, 0);
+ prevPtr->nextPtr = nextPtr;
+ dlPtr = nextPtr;
+ }
+ }
+
+ /*
+ * It's important to have the following check here rather than in
+ * the while statement for the loop, so that there's always at least
+ * one DLine generated, regardless of how small the window is. This
+ * keeps a lot of other code from breaking.
+ */
+
+ if (y >= maxY) {
+ break;
+ }
+ }
+
+ /*
+ * Delete any DLine structures that don't fit on the screen.
+ */
+
+ FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
+
+ /*
+ *--------------------------------------------------------------
+ * If there is extra space at the bottom of the window (because
+ * we've hit the end of the text), then bring in more lines at
+ * the top of the window, if there are any, to fill in the view.
+ *--------------------------------------------------------------
+ */
+
+ if (y < maxY) {
+ int lineNum, spaceLeft, charsToCount;
+ DLine *lowestPtr;
+
+ /*
+ * Layout an entire text line (potentially > 1 display line),
+ * then link in as many display lines as fit without moving
+ * the bottom line out of the window. Repeat this until
+ * all the extra space has been used up or we've reached the
+ * beginning of the text.
+ */
+
+ spaceLeft = maxY - y;
+ lineNum = CkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
+ charsToCount = dInfoPtr->dLinePtr->index.charIndex;
+ if (charsToCount == 0) {
+ charsToCount = INT_MAX;
+ lineNum--;
+ }
+ for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
+ index.linePtr = CkBTreeFindLine(textPtr->tree, lineNum);
+ index.charIndex = 0;
+ lowestPtr = NULL;
+ do {
+ dlPtr = LayoutDLine(textPtr, &index);
+ dlPtr->nextPtr = lowestPtr;
+ lowestPtr = dlPtr;
+#if CK_USE_UTF
+ if (dlPtr->length == 0 && dlPtr->height == 0) {
+ charsToCount--;
+ break;
+ }
+ CkTextIndexForwBytes(&index, dlPtr->count, &index);
+#else
+ CkTextIndexForwChars(&index, dlPtr->count, &index);
+#endif
+ charsToCount -= dlPtr->count;
+ } while ((charsToCount > 0)
+ && (index.linePtr == lowestPtr->index.linePtr));
+
+ /*
+ * Scan through the display lines from the bottom one up to
+ * the top one.
+ */
+
+ while (lowestPtr != NULL) {
+ dlPtr = lowestPtr;
+ spaceLeft -= dlPtr->height;
+ if (spaceLeft < 0) {
+ break;
+ }
+ lowestPtr = dlPtr->nextPtr;
+ dlPtr->nextPtr = dInfoPtr->dLinePtr;
+ dInfoPtr->dLinePtr = dlPtr;
+ if (ckTextDebug) {
+ char string[TK_POS_CHARS];
+
+ CkTextPrintIndex(&dlPtr->index, string);
+ Tcl_SetVar2(textPtr->interp, "ck_textRelayout",
+ (char *) NULL, string,
+ TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
+ }
+ }
+ FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
+ charsToCount = INT_MAX;
+ }
+
+ /*
+ * Now we're all done except that the y-coordinates in all the
+ * DLines are wrong and the top index for the text is wrong.
+ * Update them.
+ */
+
+ textPtr->topIndex = dInfoPtr->dLinePtr->index;
+ y = dInfoPtr->y;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
+ dlPtr = dlPtr->nextPtr) {
+ if (y > dInfoPtr->maxY) {
+ panic("Added too many new lines in UpdateDisplayInfo");
+ }
+ dlPtr->y = y;
+ y += dlPtr->height;
+ }
+ }
+
+ /*
+ *--------------------------------------------------------------
+ * If the old top or bottom line has scrolled elsewhere on the
+ * screen, we may not be able to re-use its old contents by
+ * copying bits (e.g., a beveled edge that was drawn when it was
+ * at the top or bottom won't be drawn when the line is in the
+ * middle and its neighbor has a matching background). Similarly,
+ * if the new top or bottom line came from somewhere else on the
+ * screen, we may not be able to copy the old bits.
+ *--------------------------------------------------------------
+ */
+
+ dlPtr = dInfoPtr->dLinePtr;
+ while (1) {
+ if (dlPtr->nextPtr == NULL) {
+ dlPtr->flags &= ~TOP_LINE;
+ dlPtr->flags |= BOTTOM_LINE;
+ break;
+ }
+ dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
+ dlPtr = dlPtr->nextPtr;
+ }
+ dInfoPtr->dLinePtr->flags |= TOP_LINE;
+
+ /*
+ * Arrange for scrollbars to be updated.
+ */
+
+ textPtr->flags |= UPDATE_SCROLLBARS;
+
+ /*
+ *--------------------------------------------------------------
+ * Deal with horizontal scrolling:
+ * 1. If there's empty space to the right of the longest line,
+ * shift the screen to the right to fill in the empty space.
+ * 2. If the desired horizontal scroll position has changed,
+ * force a full redisplay of all the lines in the widget.
+ * 3. If the wrap mode isn't "none" then re-scroll to the base
+ * position.
+ *--------------------------------------------------------------
+ */
+
+ dInfoPtr->maxLength = 0;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
+ dlPtr = dlPtr->nextPtr) {
+ if (dlPtr->length > dInfoPtr->maxLength) {
+ dInfoPtr->maxLength = dlPtr->length;
+ }
+ }
+
+ maxOffset = dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x);
+ if (dInfoPtr->newCharOffset >= maxOffset) {
+ dInfoPtr->newCharOffset = maxOffset != 0 ? maxOffset + 1 : 0;
+ }
+ if (dInfoPtr->newCharOffset < 0) {
+ dInfoPtr->newCharOffset = 0;
+ }
+ if (dInfoPtr->newCharOffset != dInfoPtr->curOffset) {
+ dInfoPtr->curOffset = dInfoPtr->newCharOffset;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
+ dlPtr = dlPtr->nextPtr) {
+ dlPtr->oldY = -1;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeDLines --
+ *
+ * This procedure is called to free up all of the resources
+ * associated with one or more DLine structures.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory gets freed and various other resources are released.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeDLines(textPtr, firstPtr, lastPtr, unlink)
+ CkText *textPtr; /* Information about overall text
+ * widget. */
+ register DLine *firstPtr; /* Pointer to first DLine to free up. */
+ DLine *lastPtr; /* Pointer to DLine just after last
+ * one to free (NULL means everything
+ * starting with firstPtr). */
+ int unlink; /* 1 means DLines are currently linked
+ * into the list rooted at
+ * textPtr->dInfoPtr->dLinePtr and
+ * they have to be unlinked. 0 means
+ * just free without unlinking. */
+{
+ register CkTextDispChunk *chunkPtr, *nextChunkPtr;
+ register DLine *nextDLinePtr;
+
+ if (unlink) {
+ if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
+ textPtr->dInfoPtr->dLinePtr = lastPtr;
+ } else {
+ register DLine *prevPtr;
+ for (prevPtr = textPtr->dInfoPtr->dLinePtr;
+ prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ prevPtr->nextPtr = lastPtr;
+ }
+ }
+ while (firstPtr != lastPtr) {
+ nextDLinePtr = firstPtr->nextPtr;
+ for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
+ chunkPtr = nextChunkPtr) {
+ if (chunkPtr->undisplayProc != NULL) {
+ (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
+ }
+ FreeStyle(textPtr, chunkPtr->stylePtr);
+ nextChunkPtr = chunkPtr->nextPtr;
+ ckfree((char *) chunkPtr);
+ }
+ ckfree((char *) firstPtr);
+ firstPtr = nextDLinePtr;
+ }
+ textPtr->dInfoPtr->dLinesInvalidated = 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayDLine --
+ *
+ * This procedure is invoked to draw a single line on the
+ * screen.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The line given by dlPtr is drawn at its correct position in
+ * textPtr's window. Note that this is one *display* line, not
+ * one *text* line.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayDLine(textPtr, dlPtr, prevPtr, window)
+ CkText *textPtr; /* Text widget in which to draw line. */
+ register DLine *dlPtr; /* Information about line to draw. */
+ DLine *prevPtr; /* Line just before one to draw, or NULL
+ * if dlPtr is the top line. */
+ WINDOW *window;
+{
+ register CkTextDispChunk *chunkPtr;
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ int x;
+
+ /*
+ * First, clear the area of the line to the background color for the
+ * text widget.
+ */
+
+ Ck_SetWindowAttr(textPtr->winPtr, textPtr->fg, textPtr->bg, textPtr->attr);
+ Ck_ClearToEol(textPtr->winPtr, 0, dlPtr->y);
+
+ /*
+ * Make yet another pass through all of the chunks to redraw all of
+ * foreground information. Note: we have to call the displayProc
+ * even for chunks that are off-screen. This is needed, for
+ * example, so that embedded windows can be unmapped in this case.
+ */
+
+ for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
+ chunkPtr = chunkPtr->nextPtr) {
+ x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curOffset;
+ if (chunkPtr->displayProc == CkTextInsertDisplayProc) {
+ (*chunkPtr->displayProc)(chunkPtr, x, 0, dlPtr->height,
+ 0, window, dlPtr->y);
+ continue;
+ }
+ if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
+ /*
+ * Note: we have to call the displayProc even for chunks
+ * that are off-screen. This is needed, for example, so
+ * that embedded windows can be unmapped in this case.
+ * Display the chunk at a coordinate that can be clearly
+ * identified by the displayProc as being off-screen to
+ * the left (the displayProc may not be able to tell if
+ * something is off to the right).
+ */
+
+ (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
+ 0, dlPtr->height, 0, window, dlPtr->y);
+ } else {
+ (*chunkPtr->displayProc)(chunkPtr, x, 0,
+ dlPtr->height, 0, window, dlPtr->y);
+ }
+ if (dInfoPtr->dLinesInvalidated) {
+ return;
+ }
+ }
+ linesRedrawn++;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayText --
+ *
+ * This procedure is invoked as a when-idle handler to update the
+ * display. It only redisplays the parts of the text widget that
+ * are out of date.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information is redrawn on the screen.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayText(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register CkText *textPtr = (CkText *) clientData;
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ CkWindow *winPtr;
+ register DLine *dlPtr;
+ DLine *prevPtr;
+ int maxHeight;
+ int bottomY = 0; /* Initialization needed only to stop
+ * compiler warnings. */
+
+ if (textPtr->winPtr == NULL) {
+ /*
+ * The widget has been deleted. Don't do anything.
+ */
+
+ return;
+ }
+
+ if (ckTextDebug) {
+ Tcl_SetVar2(textPtr->interp, "ck_textRelayout", (char *) NULL,
+ "", TCL_GLOBAL_ONLY);
+ }
+
+ if (!(textPtr->winPtr->flags & CK_MAPPED) ||
+ (dInfoPtr->maxX <= dInfoPtr->x) || (dInfoPtr->maxY <= dInfoPtr->y)) {
+ UpdateDisplayInfo(textPtr);
+ dInfoPtr->flags &= ~REDRAW_PENDING;
+ goto doScrollbars;
+ }
+ numRedisplays++;
+ if (ckTextDebug) {
+ Tcl_SetVar2(textPtr->interp, "ck_textRedraw", (char *) NULL,
+ "", TCL_GLOBAL_ONLY);
+ }
+
+ /*
+ * Choose a new current item if that is needed (this could cause
+ * event handlers to be invoked, hence the preserve/release calls
+ * and the loop, since the handlers could conceivably necessitate
+ * yet another current item calculation). The ckwin check is because
+ * the whole window could go away in the Ck_Release call.
+ */
+
+ while (dInfoPtr->flags & REPICK_NEEDED) {
+ Ck_Preserve((ClientData) textPtr);
+ dInfoPtr->flags &= ~REPICK_NEEDED;
+ CkTextPickCurrent(textPtr, &textPtr->pickEvent);
+ winPtr = textPtr->winPtr;
+ Ck_Release((ClientData) textPtr);
+ if (winPtr == NULL) {
+ return;
+ }
+ }
+
+ /*
+ * First recompute what's supposed to be displayed.
+ */
+
+ UpdateDisplayInfo(textPtr);
+ dInfoPtr->dLinesInvalidated = 0;
+ dInfoPtr->flags &= ~REDRAW_PENDING;
+
+ maxHeight = -1;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
+ if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
+ maxHeight = dlPtr->height;
+ }
+ bottomY = dlPtr->y + dlPtr->height;
+ }
+ if (maxHeight > dInfoPtr->maxY) {
+ maxHeight = dInfoPtr->maxY;
+ }
+
+
+ /*
+ * Now we have to redraw the lines that couldn't be updated by
+ * scrolling. First, compute the height of the largest line and
+ * allocate an off-screen pixmap to use for double-buffered
+ * displays.
+ */
+
+ if (maxHeight > 0) {
+ for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
+ (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
+ prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
+ if (dlPtr->oldY != dlPtr->y) {
+ if (ckTextDebug) {
+ char string[TK_POS_CHARS];
+ CkTextPrintIndex(&dlPtr->index, string);
+ Tcl_SetVar2(textPtr->interp, "ck_textRedraw",
+ (char *) NULL, string,
+ TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
+ }
+ DisplayDLine(textPtr, dlPtr, prevPtr, textPtr->winPtr->window);
+ if (dInfoPtr->dLinesInvalidated) {
+ goto done;
+ }
+ dlPtr->oldY = dlPtr->y;
+ dlPtr->flags &= ~NEW_LAYOUT;
+ }
+ }
+ }
+
+ /*
+ * See if we need to refresh the part of the window below the
+ * last line of text (if there is any such area).
+ */
+
+ if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
+ dInfoPtr->topOfEof = dInfoPtr->maxY;
+ }
+ if (bottomY < dInfoPtr->topOfEof) {
+ if (ckTextDebug) {
+ Tcl_SetVar2(textPtr->interp, "ck_textRedraw",
+ (char *) NULL, "eof",
+ TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
+ }
+ Ck_SetWindowAttr(textPtr->winPtr, textPtr->fg, textPtr->bg,
+ textPtr->attr);
+ Ck_ClearToBot(textPtr->winPtr, 0, bottomY);
+ }
+ dInfoPtr->topOfEof = bottomY;
+
+doScrollbars:
+
+ /*
+ * Update the vertical scrollbar, if there is one. Note: it's
+ * important to clear REDRAW_PENDING here, just in case the
+ * scroll procedure does something that requires redisplay.
+ */
+
+ if (textPtr->flags & UPDATE_SCROLLBARS) {
+ textPtr->flags &= ~UPDATE_SCROLLBARS;
+ if (textPtr->yScrollCmd != NULL) {
+ GetYView(textPtr->interp, textPtr, 1);
+ }
+
+ /*
+ * Update the horizontal scrollbar, if any.
+ */
+
+ if (textPtr->xScrollCmd != NULL) {
+ GetXView(textPtr->interp, textPtr, 1);
+ }
+ }
+done:
+ if (textPtr->insertX >= 0 &&
+ textPtr->insertX < textPtr->winPtr->width &&
+ textPtr->insertY >= 0 &&
+ textPtr->insertY < textPtr->winPtr->height &&
+ textPtr->winPtr->window != NULL) {
+ wmove(textPtr->winPtr->window, textPtr->insertY, textPtr->insertX);
+ Ck_SetHWCursor(textPtr->winPtr, 1);
+ } else
+ Ck_SetHWCursor(textPtr->winPtr, 0);
+ Ck_EventuallyRefresh(textPtr->winPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextEventuallyRepick --
+ *
+ * This procedure is invoked whenever something happens that
+ * could change the current character or the tags associated
+ * with it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A repick is scheduled as an idle handler.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+CkTextEventuallyRepick(textPtr)
+ CkText *textPtr; /* Widget record for text widget. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+
+ dInfoPtr->flags |= REPICK_NEEDED;
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ dInfoPtr->flags |= REDRAW_PENDING;
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextRedrawRegion --
+ *
+ * This procedure is invoked to schedule a redisplay for a given
+ * region of a text widget. The redisplay itself may not occur
+ * immediately: it's scheduled as a when-idle handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information will eventually be redrawn on the screen.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+CkTextRedrawRegion(textPtr, x, y, width, height)
+ CkText *textPtr; /* Widget record for text widget. */
+ int x, y; /* Coordinates of upper-left corner of area
+ * to be redrawn, in pixels relative to
+ * textPtr's window. */
+ int width, height; /* Width and height of area to be redrawn. */
+{
+ register DLine *dlPtr;
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ int maxY;
+
+ /*
+ * Find all lines that overlap the given region and mark them for
+ * redisplay.
+ */
+
+ maxY = y + height;
+ for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
+ dlPtr = dlPtr->nextPtr) {
+ if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
+ dlPtr->oldY = -1;
+ }
+ }
+ if (dInfoPtr->topOfEof < maxY) {
+ dInfoPtr->topOfEof = maxY;
+ }
+
+ /*
+ * Schedule the redisplay operation if there isn't one already
+ * scheduled.
+ */
+
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ dInfoPtr->flags |= REDRAW_PENDING;
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextChanged --
+ *
+ * This procedure is invoked when info in a text widget is about
+ * to be modified in a way that changes how it is displayed (e.g.
+ * characters were inserted or deleted, or tag information was
+ * changed). This procedure must be called *before* a change is
+ * made, so that indexes in the display information are still
+ * valid.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The range of character between index1Ptr (inclusive) and
+ * index2Ptr (exclusive) will be redisplayed at some point in the
+ * future (the actual redisplay is scheduled as a when-idle handler).
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextChanged(textPtr, index1Ptr, index2Ptr)
+ CkText *textPtr; /* Widget record for text widget. */
+ CkTextIndex *index1Ptr; /* Index of first character to redisplay. */
+ CkTextIndex *index2Ptr; /* Index of character just after last one
+ * to redisplay. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ DLine *firstPtr, *lastPtr;
+ CkTextIndex rounded;
+
+ /*
+ * Schedule both a redisplay and a recomputation of display information.
+ * It's done here rather than the end of the procedure for two reasons:
+ *
+ * 1. If there are no display lines to update we'll want to return
+ * immediately, well before the end of the procedure.
+ * 2. It's important to arrange for the redisplay BEFORE calling
+ * FreeDLines. The reason for this is subtle and has to do with
+ * embedded windows. The chunk delete procedure for an embedded
+ * window will schedule an idle handler to unmap the window.
+ * However, we want the idle handler for redisplay to be called
+ * first, so that it can put the embedded window back on the screen
+ * again (if appropriate). This will prevent the window from ever
+ * being unmapped, and thereby avoid flashing.
+ */
+
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+
+ /*
+ * Find the DLines corresponding to index1Ptr and index2Ptr. There
+ * is one tricky thing here, which is that we have to relayout in
+ * units of whole text lines: round index1Ptr back to the beginning
+ * of its text line, and include all the display lines after index2,
+ * up to the end of its text line. This is necessary because the
+ * indices stored in the display lines will no longer be valid. It's
+ * also needed because any edit could change the way lines wrap.
+ */
+
+ rounded = *index1Ptr;
+ rounded.charIndex = 0;
+ firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
+ if (firstPtr == NULL) {
+ return;
+ }
+ lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
+ while ((lastPtr != NULL)
+ && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
+ lastPtr = lastPtr->nextPtr;
+ }
+
+ /*
+ * Delete all the DLines from firstPtr up to but not including lastPtr.
+ */
+
+ FreeDLines(textPtr, firstPtr, lastPtr, 1);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextRedrawTag --
+ *
+ * This procedure is invoked to request a redraw of all characters
+ * in a given range that have a particular tag on or off. It's
+ * called, for example, when tag options change.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information on the screen may be redrawn, and the layout of
+ * the screen may change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
+ CkText *textPtr; /* Widget record for text widget. */
+ CkTextIndex *index1Ptr; /* First character in range to consider
+ * for redisplay. NULL means start at
+ * beginning of text. */
+ CkTextIndex *index2Ptr; /* Character just after last one to consider
+ * for redisplay. NULL means process all
+ * the characters in the text. */
+ CkTextTag *tagPtr; /* Information about tag. */
+ int withTag; /* 1 means redraw characters that have the
+ * tag, 0 means redraw those without. */
+{
+ register DLine *dlPtr;
+ DLine *endPtr;
+ int tagOn;
+ CkTextSearch search;
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ CkTextIndex endOfText, *endIndexPtr;
+
+ /*
+ * Round up the starting position if it's before the first line
+ * visible on the screen (we only care about what's on the screen).
+ */
+
+ dlPtr = dInfoPtr->dLinePtr;
+ if (dlPtr == NULL) {
+ return;
+ }
+ if ((index1Ptr == NULL) || (CkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
+ index1Ptr = &dlPtr->index;
+ }
+
+ /*
+ * Set the stopping position if it wasn't specified.
+ */
+
+ if (index2Ptr == NULL) {
+#if CK_USE_UTF
+ index2Ptr = CkTextMakeByteIndex(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree), 0, &endOfText);
+#else
+ index2Ptr = CkTextMakeIndex(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree), 0, &endOfText);
+#endif
+ }
+
+ /*
+ * Initialize a search through all transitions on the tag, starting
+ * with the first transition where the tag's current state is different
+ * from what it will eventually be.
+ */
+
+ CkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
+ tagOn = CkBTreeCharTagged(index1Ptr, tagPtr);
+ if (tagOn != withTag) {
+ if (!CkBTreeNextTag(&search)) {
+ return;
+ }
+ }
+
+ /*
+ * Schedule a redisplay and layout recalculation if they aren't
+ * already pending. This has to be done before calling FreeDLines,
+ * for the reason given in CkTextChanged.
+ */
+
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+
+ /*
+ * Each loop through the loop below is for one range of characters
+ * where the tag's current state is different than its eventual
+ * state. At the top of the loop, search contains information about
+ * the first character in the range.
+ */
+
+ while (1) {
+ /*
+ * Find the first DLine structure in the range. Note: if the
+ * desired character isn't the first in its text line, then look
+ * for the character just before it instead. This is needed to
+ * handle the case where the first character of a wrapped
+ * display line just got smaller, so that it now fits on the
+ * line before: need to relayout the line containing the
+ * previous character.
+ */
+
+ if (search.curIndex.charIndex == 0) {
+ dlPtr = FindDLine(dlPtr, &search.curIndex);
+ } else {
+ CkTextIndex tmp;
+
+ tmp = search.curIndex;
+ tmp.charIndex -= 1;
+ dlPtr = FindDLine(dlPtr, &tmp);
+ }
+ if (dlPtr == NULL) {
+ break;
+ }
+
+ /*
+ * Find the first DLine structure that's past the end of the range.
+ */
+
+ if (!CkBTreeNextTag(&search)) {
+ endIndexPtr = index2Ptr;
+ } else {
+ endIndexPtr = &search.curIndex;
+ }
+ endPtr = FindDLine(dlPtr, endIndexPtr);
+ if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
+ && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
+ endPtr = endPtr->nextPtr;
+ }
+
+ /*
+ * Delete all of the display lines in the range, so that they'll
+ * be re-layed out and redrawn.
+ */
+
+ FreeDLines(textPtr, dlPtr, endPtr, 1);
+ dlPtr = endPtr;
+
+ /*
+ * Find the first text line in the next range.
+ */
+
+ if (!CkBTreeNextTag(&search)) {
+ break;
+ }
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextRelayoutWindow --
+ *
+ * This procedure is called when something has happened that
+ * invalidates the whole layout of characters on the screen, such
+ * as a change in a configuration option for the overall text
+ * widget or a change in the window size. It causes all display
+ * information to be recomputed and the window to be redrawn.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All the display information will be recomputed for the window
+ * and the window will be redrawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextRelayoutWindow(textPtr)
+ CkText *textPtr; /* Widget record for text widget. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+
+ /*
+ * Schedule the window redisplay. See CkTextChanged for the
+ * reason why this has to be done before any calls to FreeDLines.
+ */
+
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+
+ /*
+ * Throw away all the current layout information.
+ */
+
+ FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
+ dInfoPtr->dLinePtr = NULL;
+
+ /*
+ * Recompute some overall things for the layout. Even if the
+ * window gets very small, pretend that there's at least one
+ * pixel of drawing space in it.
+ */
+
+ dInfoPtr->x = 0;
+ dInfoPtr->y = 0;
+ dInfoPtr->maxX = textPtr->winPtr->width;
+ dInfoPtr->maxY = textPtr->winPtr->height;
+ dInfoPtr->topOfEof = dInfoPtr->maxY;
+
+ /*
+ * If the upper-left character isn't the first in a line, recompute
+ * it. This is necessary because a change in the window's size
+ * or options could change the way lines wrap.
+ */
+
+ if (textPtr->topIndex.charIndex != 0) {
+ MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextSetYView --
+ *
+ * This procedure is called to specify what lines are to be
+ * displayed in a text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The display will (eventually) be updated so that the position
+ * given by "indexPtr" is visible on the screen at the position
+ * determined by "pickPlace".
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextSetYView(textPtr, indexPtr, pickPlace)
+ CkText *textPtr; /* Widget record for text widget. */
+ CkTextIndex *indexPtr; /* Position that is to appear somewhere
+ * in the view. */
+ int pickPlace; /* 0 means topLine must appear at top of
+ * screen. 1 means we get to pick where it
+ * appears: minimize screen motion or else
+ * display line at center of screen. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ register DLine *dlPtr;
+ int bottomY, close, lineIndex;
+ CkTextIndex tmpIndex, rounded;
+
+ /*
+ * If the specified position is the extra line at the end of the
+ * text, round it back to the last real line.
+ */
+
+ lineIndex = CkBTreeLineIndex(indexPtr->linePtr);
+ if (lineIndex == CkBTreeNumLines(indexPtr->tree)) {
+ CkTextIndexBackChars(indexPtr, 1, &rounded);
+ indexPtr = &rounded;
+ }
+
+ if (!pickPlace) {
+ /*
+ * The specified position must go at the top of the screen.
+ * Just leave all the DLine's alone: we may be able to reuse
+ * some of the information that's currently on the screen
+ * without redisplaying it all.
+ */
+
+ if (indexPtr->charIndex == 0) {
+ textPtr->topIndex = *indexPtr;
+ } else {
+ MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
+ }
+ goto scheduleUpdate;
+ }
+
+ /*
+ * We have to pick where to display the index. First, bring
+ * the display information up to date and see if the index will be
+ * completely visible in the current screen configuration. If so
+ * then there's nothing to do.
+ */
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+ dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ if (dlPtr != NULL) {
+ if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
+ /*
+ * Part of the line hangs off the bottom of the screen;
+ * pretend the whole line is off-screen.
+ */
+
+ dlPtr = NULL;
+ } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
+ && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
+ return;
+ }
+ }
+
+ /*
+ * The desired line isn't already on-screen.
+ */
+
+ bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
+ close = (dInfoPtr->maxY - dInfoPtr->y)/3;
+ if (dlPtr != NULL) {
+ /*
+ * The desired line is above the top of screen. If it is
+ * "close" to the top of the window then make it the top
+ * line on the screen.
+ */
+
+ MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
+ if (CkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
+ MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
+ goto scheduleUpdate;
+ }
+ } else {
+ /*
+ * The desired line is below the bottom of the screen. If it is
+ * "close" to the bottom of the screen then position it at the
+ * bottom of the screen.
+ */
+
+ MeasureUp(textPtr, indexPtr, close, &tmpIndex);
+ if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
+ bottomY = dInfoPtr->maxY - dInfoPtr->y;
+ }
+ }
+
+ /*
+ * Our job now is to arrange the display so that indexPtr appears
+ * as low on the screen as possible but with its bottom no lower
+ * than bottomY. BottomY is the bottom of the window if the
+ * desired line is just below the current screen, otherwise it
+ * is the center of the window.
+ */
+
+ MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
+
+ scheduleUpdate:
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MeasureUp --
+ *
+ * Given one index, find the index of the first character
+ * on the highest display line that would be displayed no more
+ * than "distance" pixels above the given index.
+ *
+ * Results:
+ * *dstPtr is filled in with the index of the first character
+ * on a display line. The display line is found by measuring
+ * up "distance" pixels above the pixel just below an imaginary
+ * display line that contains srcPtr. If the display line
+ * that covers this coordinate actually extends above the
+ * coordinate, then return the index of the next lower line
+ * instead (i.e. the returned index will be completely visible
+ * at or below the given y-coordinate).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MeasureUp(textPtr, srcPtr, distance, dstPtr)
+ CkText *textPtr; /* Text widget in which to measure. */
+ CkTextIndex *srcPtr; /* Index of character from which to start
+ * measuring. */
+ int distance; /* Vertical distance in pixels measured
+ * from the pixel just below the lowest
+ * one in srcPtr's line. */
+ CkTextIndex *dstPtr; /* Index to fill in with result. */
+{
+ int lineNum; /* Number of current line. */
+ int charsToCount; /* Maximum number of characters to measure
+ * in current line. */
+ CkTextIndex bestIndex; /* Best candidate seen so far for result. */
+ CkTextIndex index;
+ DLine *dlPtr, *lowestPtr;
+ int noBestYet; /* 1 means bestIndex hasn't been set. */
+
+ noBestYet = 1;
+ charsToCount = srcPtr->charIndex + 1;
+ index.tree = srcPtr->tree;
+ for (lineNum = CkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
+ lineNum--) {
+ /*
+ * Layout an entire text line (potentially > 1 display line).
+ * For the first line, which contains srcPtr, only layout the
+ * part up through srcPtr (charsToCount is non-infinite to
+ * accomplish this). Make a list of all the display lines
+ * in backwards order (the lowest DLine on the screen is first
+ * in the list).
+ */
+
+ index.linePtr = CkBTreeFindLine(srcPtr->tree, lineNum);
+ index.charIndex = 0;
+ lowestPtr = NULL;
+ do {
+ dlPtr = LayoutDLine(textPtr, &index);
+ dlPtr->nextPtr = lowestPtr;
+ lowestPtr = dlPtr;
+#if CK_USE_UTF
+ CkTextIndexForwBytes(&index, dlPtr->count, &index);
+#else
+ CkTextIndexForwChars(&index, dlPtr->count, &index);
+#endif
+ charsToCount -= dlPtr->count;
+ } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
+
+ /*
+ * Scan through the display lines to see if we've covered enough
+ * vertical distance. If so, save the starting index for the
+ * line at the desired location.
+ */
+
+ for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
+ distance -= dlPtr->height;
+ if (distance < 0) {
+ *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
+ break;
+ }
+ bestIndex = dlPtr->index;
+ noBestYet = 0;
+ }
+
+ /*
+ * Discard the display lines, then either return or prepare
+ * for the next display line to lay out.
+ */
+
+ FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
+ if (distance < 0) {
+ return;
+ }
+ charsToCount = INT_MAX; /* Consider all chars. in next line. */
+ }
+
+ /*
+ * Ran off the beginning of the text. Return the first character
+ * in the text.
+ */
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
+#else
+ CkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
+#endif
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextSeeCmd --
+ *
+ * This procedure is invoked to process the "see" option for
+ * the widget command for text widgets. See the user documentation
+ * for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextSeeCmd(textPtr, interp, argc, argv)
+ CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "see". */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ CkTextIndex index;
+ int x, y, width, height, lineWidth, charCount, oneThird, delta;
+ DLine *dlPtr;
+ CkTextDispChunk *chunkPtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " see index\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * If the specified position is the extra line at the end of the
+ * text, round it back to the last real line.
+ */
+
+ if (CkBTreeLineIndex(index.linePtr) == CkBTreeNumLines(index.tree)) {
+ CkTextIndexBackChars(&index, 1, &index);
+ }
+
+ /*
+ * First get the desired position into the vertical range of the window.
+ */
+
+ CkTextSetYView(textPtr, &index, 1);
+
+ /*
+ * Now make sure that the character is in view horizontally.
+ */
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+ lineWidth = dInfoPtr->maxX - dInfoPtr->x;
+ if (dInfoPtr->maxLength < lineWidth) {
+ return TCL_OK;
+ }
+
+ /*
+ * Find the chunk that contains the desired index.
+ */
+
+ dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
+ charCount = index.charIndex - dlPtr->index.charIndex;
+ for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
+ if (charCount < chunkPtr->numChars) {
+ break;
+ }
+ charCount -= chunkPtr->numChars;
+ }
+
+ /*
+ * Call a chunk-specific procedure to find the horizontal range of
+ * the character within the chunk.
+ */
+
+ (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y,
+ dlPtr->height, 0, &x, &y, &width, &height);
+ delta = x - dInfoPtr->curOffset;
+ oneThird = lineWidth/3;
+ if (delta < 0) {
+ if (delta < -oneThird) {
+ dInfoPtr->newCharOffset = (x - lineWidth/2);
+ } else {
+ dInfoPtr->newCharOffset -= -delta;
+ }
+ } else {
+ delta -= (lineWidth - width);
+ if (delta >= 0) {
+ if (delta > oneThird) {
+ dInfoPtr->newCharOffset = (x - lineWidth/2);
+ } else {
+ dInfoPtr->newCharOffset += delta + 1;
+ }
+ } else {
+ return TCL_OK;
+ }
+ }
+ dInfoPtr->flags |= DINFO_OUT_OF_DATE;
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ dInfoPtr->flags |= REDRAW_PENDING;
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextXviewCmd --
+ *
+ * This procedure is invoked to process the "xview" option for
+ * the widget command for text widgets. See the user documentation
+ * for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextXviewCmd(textPtr, interp, argc, argv)
+ CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "xview". */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ int type, charsPerPage, count, newOffset;
+ double fraction;
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+
+ if (argc == 2) {
+ GetXView(interp, textPtr, 0);
+ return TCL_OK;
+ }
+
+ newOffset = dInfoPtr->newCharOffset;
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ return TCL_ERROR;
+ case CK_SCROLL_MOVETO:
+ newOffset = (int) (fraction * dInfoPtr->maxLength);
+ break;
+ case CK_SCROLL_PAGES:
+ charsPerPage = dInfoPtr->maxX - dInfoPtr->x - 2;
+ if (charsPerPage < 1) {
+ charsPerPage = 1;
+ }
+ newOffset += charsPerPage*count;
+ break;
+ case CK_SCROLL_UNITS:
+ newOffset += count;
+ break;
+ }
+
+ dInfoPtr->newCharOffset = newOffset;
+ dInfoPtr->flags |= DINFO_OUT_OF_DATE;
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ dInfoPtr->flags |= REDRAW_PENDING;
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollByLines --
+ *
+ * This procedure is called to scroll a text widget up or down
+ * by a given number of lines.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The view in textPtr's window changes to reflect the value
+ * of "offset".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ScrollByLines(textPtr, offset)
+ CkText *textPtr; /* Widget to scroll. */
+ int offset; /* Amount by which to scroll, in *screen*
+ * lines. Positive means that information
+ * later in text becomes visible, negative
+ * means that information earlier in the
+ * text becomes visible. */
+{
+ int i, charsToCount, lineNum;
+ CkTextIndex new, index;
+ CkTextLine *lastLinePtr;
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ DLine *dlPtr, *lowestPtr;
+
+ if (offset < 0) {
+ /*
+ * Must scroll up (to show earlier information in the text).
+ * The code below is similar to that in MeasureUp, except that
+ * it counts lines instead of pixels.
+ */
+
+ charsToCount = textPtr->topIndex.charIndex + 1;
+ index.tree = textPtr->tree;
+ offset--; /* Skip line containing topIndex. */
+ for (lineNum = CkBTreeLineIndex(textPtr->topIndex.linePtr);
+ lineNum >= 0; lineNum--) {
+ index.linePtr = CkBTreeFindLine(textPtr->tree, lineNum);
+ index.charIndex = 0;
+ lowestPtr = NULL;
+ do {
+ dlPtr = LayoutDLine(textPtr, &index);
+ dlPtr->nextPtr = lowestPtr;
+ lowestPtr = dlPtr;
+#if CK_USE_UTF
+ CkTextIndexForwBytes(&index, dlPtr->count, &index);
+#else
+ CkTextIndexForwChars(&index, dlPtr->count, &index);
+#endif
+ charsToCount -= dlPtr->count;
+ } while ((charsToCount > 0)
+ && (index.linePtr == dlPtr->index.linePtr));
+
+ for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
+ offset++;
+ if (offset == 0) {
+ textPtr->topIndex = dlPtr->index;
+ break;
+ }
+ }
+
+ /*
+ * Discard the display lines, then either return or prepare
+ * for the next display line to lay out.
+ */
+
+ FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
+ if (offset >= 0) {
+ goto scheduleUpdate;
+ }
+ charsToCount = INT_MAX;
+ }
+
+ /*
+ * Ran off the beginning of the text. Return the first character
+ * in the text.
+ */
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
+#else
+ CkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
+#endif
+ } else {
+ /*
+ * Scrolling down, to show later information in the text.
+ * Just count lines from the current top of the window.
+ */
+
+ lastLinePtr = CkBTreeFindLine(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree));
+ for (i = 0; i < offset; i++) {
+ dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
+ dlPtr->nextPtr = NULL;
+#if CK_USE_UTF
+ if (dlPtr->length == 0 && dlPtr->height == 0) {
+ offset++;
+ }
+ CkTextIndexForwBytes(&textPtr->topIndex, dlPtr->count, &new);
+#else
+ CkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
+#endif
+ FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
+ if (new.linePtr == lastLinePtr) {
+ break;
+ }
+ textPtr->topIndex = new;
+ }
+ }
+
+ scheduleUpdate:
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextYviewCmd --
+ *
+ * This procedure is invoked to process the "yview" option for
+ * the widget command for text widgets. See the user documentation
+ * for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextYviewCmd(textPtr, interp, argc, argv)
+ CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "yview". */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ int pickPlace, lineNum, type, lineHeight;
+ int pixels, count;
+ size_t switchLength;
+ double fraction;
+ CkTextIndex index, new;
+ CkTextLine *lastLinePtr;
+ DLine *dlPtr;
+#if CK_USE_UTF
+ int bytesInLine;
+#endif
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+
+ if (argc == 2) {
+ GetYView(interp, textPtr, 0);
+ return TCL_OK;
+ }
+
+ /*
+ * Next, handle the old syntax: "pathName yview ?-pickplace? where"
+ */
+
+ pickPlace = 0;
+ if (argv[2][0] == '-') {
+ switchLength = strlen(argv[2]);
+ if ((switchLength >= 2)
+ && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
+ pickPlace = 1;
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " yview -pickplace lineNum|index\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+ if ((argc == 3) || pickPlace) {
+ if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
+#else
+ CkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
+#endif
+ CkTextSetYView(textPtr, &index, 0);
+ return TCL_OK;
+ }
+
+ /*
+ * The argument must be a regular text index.
+ */
+
+ Tcl_ResetResult(interp);
+ if (CkTextGetIndex(interp, textPtr, argv[2+pickPlace],
+ &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ CkTextSetYView(textPtr, &index, pickPlace);
+ return TCL_OK;
+ }
+
+ /*
+ * New syntax: dispatch based on argv[2].
+ */
+
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ return TCL_ERROR;
+ case CK_SCROLL_MOVETO:
+#if CK_USE_UTF
+ if (fraction > 1.0) {
+ fraction = 1.0;
+ }
+ if (fraction < 0) {
+ fraction = 0;
+ }
+ fraction *= CkBTreeNumLines(textPtr->tree);
+ lineNum = (int) fraction;
+ CkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
+ bytesInLine = CkBTreeCharsInLine(index.linePtr);
+ index.charIndex = (int)((bytesInLine * (fraction-lineNum)) + 0.5);
+ if (index.charIndex >= bytesInLine) {
+ CkTextMakeByteIndex(textPtr->tree, lineNum + 1, 0, &index);
+ }
+#else
+ fraction *= CkBTreeNumLines(textPtr->tree);
+ lineNum = (int) fraction;
+ CkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
+ CkTextIndexBackChars(&index, 1, &index);
+ index.charIndex = (int) ((index.charIndex+1)*(fraction-lineNum));
+#endif
+ CkTextSetYView(textPtr, &index, 0);
+ break;
+ case CK_SCROLL_PAGES:
+ /*
+ * Scroll up or down by screenfulls. Actually, use the
+ * window height minus two lines, so that there's some
+ * overlap between adjacent pages.
+ */
+
+ lineHeight = 1;
+ if (count < 0) {
+ pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*(-count)
+ + lineHeight;
+ MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
+ if (CkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
+ /*
+ * A page of scrolling ended up being less than one line.
+ * Scroll one line anyway.
+ */
+
+ count = -1;
+ goto scrollByLines;
+ }
+ textPtr->topIndex = new;
+ } else {
+ /*
+ * Scrolling down by pages. Layout lines starting at the
+ * top index and count through the desired vertical distance.
+ */
+
+ pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*count;
+ lastLinePtr = CkBTreeFindLine(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree));
+ do {
+ dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
+ dlPtr->nextPtr = NULL;
+#if CK_USE_UTF
+ CkTextIndexForwBytes(&textPtr->topIndex, dlPtr->count,
+ &new);
+#else
+ CkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
+ &new);
+#endif
+ pixels -= dlPtr->height;
+ FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
+ if (new.linePtr == lastLinePtr) {
+ break;
+ }
+ textPtr->topIndex = new;
+ } while (pixels > 0);
+ }
+ if (!(dInfoPtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
+ }
+ dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
+ break;
+ case CK_SCROLL_UNITS:
+ scrollByLines:
+ ScrollByLines(textPtr, count);
+ break;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetXView --
+ *
+ * This procedure computes the fractions that indicate what's
+ * visible in a text window and, optionally, evaluates a
+ * Tcl script to report them to the text's associated scrollbar.
+ *
+ * Results:
+ * If report is zero, then interp->result is filled in with
+ * two real numbers separated by a space, giving the position of
+ * the left and right edges of the window as fractions from 0 to
+ * 1, where 0 means the left edge of the text and 1 means the right
+ * edge. If report is non-zero, then interp->result isn't modified
+ * directly, but instead a script is evaluated in interp to report
+ * the new horizontal scroll position to the scrollbar (if the scroll
+ * position hasn't changed then no script is invoked).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetXView(interp, textPtr, report)
+ Tcl_Interp *interp; /* If "report" is FALSE, string
+ * describing visible range gets
+ * stored in interp->result. */
+ CkText *textPtr; /* Information about text widget. */
+ int report; /* Non-zero means report info to
+ * scrollbar if it has changed. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ char buffer[200];
+ double first, last;
+ int code;
+
+ if (dInfoPtr->maxLength > 0) {
+ first = ((double) dInfoPtr->curOffset)
+ / dInfoPtr->maxLength;
+ last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
+ / dInfoPtr->maxLength;
+ if (last > 1.0) {
+ last = 1.0;
+ }
+ } else {
+ first = 0;
+ last = 1.0;
+ }
+ if (!report) {
+ sprintf(interp->result, "%g %g", first, last);
+ return;
+ }
+ if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
+ return;
+ }
+ dInfoPtr->xScrollFirst = first;
+ dInfoPtr->xScrollLast = last;
+ sprintf(buffer, " %g %g", first, last);
+ code = Tcl_VarEval(interp, textPtr->xScrollCmd,
+ buffer, (char *) NULL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (horizontal scrolling command executed by text)");
+ Tk_BackgroundError(interp);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetYView --
+ *
+ * This procedure computes the fractions that indicate what's
+ * visible in a text window and, optionally, evaluates a
+ * Tcl script to report them to the text's associated scrollbar.
+ *
+ * Results:
+ * If report is zero, then interp->result is filled in with
+ * two real numbers separated by a space, giving the position of
+ * the top and bottom of the window as fractions from 0 to 1, where
+ * 0 means the beginning of the text and 1 means the end. If
+ * report is non-zero, then interp->result isn't modified directly,
+ * but a script is evaluated in interp to report the new scroll
+ * position to the scrollbar (if the scroll position hasn't changed
+ * then no script is invoked).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetYView(interp, textPtr, report)
+ Tcl_Interp *interp; /* If "report" is FALSE, string
+ * describing visible range gets
+ * stored in interp->result. */
+ CkText *textPtr; /* Information about text widget. */
+ int report; /* Non-zero means report info to
+ * scrollbar if it has changed. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ char buffer[200];
+ double first, last;
+ DLine *dlPtr;
+ int totalLines, code, count;
+
+ dlPtr = dInfoPtr->dLinePtr;
+ totalLines = CkBTreeNumLines(textPtr->tree);
+ first = ((double) CkBTreeLineIndex(dlPtr->index.linePtr))
+ + ((double) dlPtr->index.charIndex)
+ / (CkBTreeCharsInLine(dlPtr->index.linePtr));
+ first /= totalLines;
+ while (1) {
+ if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
+ /*
+ * The last line is only partially visible, so don't
+ * count its characters in what's visible.
+ */
+ count = 0;
+ break;
+ }
+ if (dlPtr->nextPtr == NULL) {
+ count = dlPtr->count;
+ break;
+ }
+ dlPtr = dlPtr->nextPtr;
+ }
+ last = ((double) CkBTreeLineIndex(dlPtr->index.linePtr))
+ + ((double) (dlPtr->index.charIndex + count))
+ / (CkBTreeCharsInLine(dlPtr->index.linePtr));
+ last /= totalLines;
+ if (!report) {
+ sprintf(interp->result, "%g %g", first, last);
+ return;
+ }
+ if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
+ return;
+ }
+ dInfoPtr->yScrollFirst = first;
+ dInfoPtr->yScrollLast = last;
+ sprintf(buffer, " %g %g", first, last);
+ code = Tcl_VarEval(interp, textPtr->yScrollCmd,
+ buffer, (char *) NULL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (vertical scrolling command executed by text)");
+ Tk_BackgroundError(interp);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindDLine --
+ *
+ * This procedure is called to find the DLine corresponding to a
+ * given text index.
+ *
+ * Results:
+ * The return value is a pointer to the first DLine found in the
+ * list headed by dlPtr that displays information at or after the
+ * specified position. If there is no such line in the list then
+ * NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static DLine *
+FindDLine(dlPtr, indexPtr)
+ register DLine *dlPtr; /* Pointer to first in list of DLines
+ * to search. */
+ CkTextIndex *indexPtr; /* Index of desired character. */
+{
+ CkTextLine *linePtr;
+
+ if (dlPtr == NULL) {
+ return NULL;
+ }
+ if (CkBTreeLineIndex(indexPtr->linePtr)
+ < CkBTreeLineIndex(dlPtr->index.linePtr)) {
+ /*
+ * The first display line is already past the desired line.
+ */
+ return dlPtr;
+ }
+
+ /*
+ * Find the first display line that covers the desired text line.
+ */
+
+ linePtr = dlPtr->index.linePtr;
+ while (linePtr != indexPtr->linePtr) {
+ while (dlPtr->index.linePtr == linePtr) {
+ dlPtr = dlPtr->nextPtr;
+ if (dlPtr == NULL) {
+ return NULL;
+ }
+ }
+ linePtr = CkBTreeNextLine(linePtr);
+ if (linePtr == NULL) {
+ panic("FindDLine reached end of text");
+ }
+ }
+ if (indexPtr->linePtr != dlPtr->index.linePtr) {
+ return dlPtr;
+ }
+
+ /*
+ * Now get to the right position within the text line.
+ */
+
+ while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
+ dlPtr = dlPtr->nextPtr;
+ if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
+ break;
+ }
+ }
+ return dlPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextPixelIndex --
+ *
+ * Given an (x,y) coordinate on the screen, find the location of
+ * the character closest to that location.
+ *
+ * Results:
+ * The index at *indexPtr is modified to refer to the character
+ * on the display that is closest to (x,y).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextPixelIndex(textPtr, x, y, indexPtr)
+ CkText *textPtr; /* Widget record for text widget. */
+ int x, y; /* Pixel coordinates of point in widget's
+ * window. */
+ CkTextIndex *indexPtr; /* This index gets filled in with the
+ * index of the character nearest to (x,y). */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ register DLine *dlPtr;
+ register CkTextDispChunk *chunkPtr;
+
+ /*
+ * Make sure that all of the layout information about what's
+ * displayed where on the screen is up-to-date.
+ */
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+
+ /*
+ * If the coordinates are above the top of the window, then adjust
+ * them to refer to the upper-right corner of the window. If they're
+ * off to one side or the other, then adjust to the closest side.
+ */
+
+ if (y < dInfoPtr->y) {
+ y = dInfoPtr->y;
+ x = dInfoPtr->x;
+ }
+ if (x >= dInfoPtr->maxX) {
+ x = dInfoPtr->maxX - 1;
+ }
+ if (x < dInfoPtr->x) {
+ x = dInfoPtr->x;
+ }
+
+ /*
+ * Find the display line containing the desired y-coordinate.
+ */
+
+ for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
+ dlPtr = dlPtr->nextPtr) {
+ if (dlPtr->nextPtr == NULL) {
+ /*
+ * Y-coordinate is off the bottom of the displayed text.
+ * Use the last character on the last line.
+ */
+
+ x = dInfoPtr->maxX - 1;
+ break;
+ }
+ }
+
+ /*
+ * Scan through the line's chunks to find the one that contains
+ * the desired x-coordinate. Before doing this, translate the
+ * x-coordinate from the coordinate system of the window to the
+ * coordinate system of the line (to take account of x-scrolling).
+ */
+
+ *indexPtr = dlPtr->index;
+ x = x - dInfoPtr->x + dInfoPtr->curOffset;
+ for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
+ indexPtr->charIndex += chunkPtr->numChars,
+ chunkPtr = chunkPtr->nextPtr) {
+ if (chunkPtr->nextPtr == NULL) {
+#if CK_USE_UTF
+ indexPtr->charIndex += chunkPtr->numChars;
+ CkTextIndexBackChars(indexPtr, 1, indexPtr);
+#else
+ indexPtr->charIndex += chunkPtr->numChars - 1;
+#endif
+ return;
+ }
+ }
+
+ /*
+ * If the chunk has more than one character in it, ask it which
+ * character is at the desired location.
+ */
+
+ if (chunkPtr->numChars > 1) {
+ indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextCharBbox --
+ *
+ * Given an index, find the bounding box of the screen area
+ * occupied by that character.
+ *
+ * Results:
+ * Zero is returned if the character is on the screen. -1
+ * means the character isn't on the screen. If the return value
+ * is 0, then the bounding box of the part of the character that's
+ * visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
+ * and *heightPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
+ CkText *textPtr; /* Widget record for text widget. */
+ CkTextIndex *indexPtr; /* Index of character whose bounding
+ * box is desired. */
+ int *xPtr, *yPtr; /* Filled with character's upper-left
+ * coordinate. */
+ int *widthPtr, *heightPtr; /* Filled in with character's dimensions. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ DLine *dlPtr;
+ register CkTextDispChunk *chunkPtr;
+ int index;
+
+ /*
+ * Make sure that all of the screen layout information is up to date.
+ */
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+
+ /*
+ * Find the display line containing the desired index.
+ */
+
+ dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ if ((dlPtr == NULL) || (CkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
+ return -1;
+ }
+
+ /*
+ * Find the chunk within the line that contains the desired
+ * index.
+ */
+
+ index = indexPtr->charIndex - dlPtr->index.charIndex;
+ for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
+ if (chunkPtr == NULL) {
+ return -1;
+ }
+ if (index < chunkPtr->numChars) {
+ break;
+ }
+ index -= chunkPtr->numChars;
+ }
+
+ /*
+ * Call a chunk-specific procedure to find the horizontal range of
+ * the character within the chunk, then fill in the vertical range.
+ * The x-coordinate returned by bboxProc is a coordinate within a
+ * line, not a coordinate on the screen. Translate it to reflect
+ * horizontal scrolling.
+ */
+
+ (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y,
+ dlPtr->height, 0, xPtr, yPtr, widthPtr,
+ heightPtr);
+ *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curOffset;
+ if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
+ /*
+ * Last character in display line. Give it all the space up to
+ * the line.
+ */
+
+ if (*xPtr > dInfoPtr->maxX) {
+ *xPtr = dInfoPtr->maxX;
+ }
+ *widthPtr = dInfoPtr->maxX - *xPtr;
+ }
+ if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
+ return -1;
+ }
+ if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
+ *widthPtr = dInfoPtr->maxX - *xPtr;
+ if (*widthPtr <= 0) {
+ return -1;
+ }
+ }
+ if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
+ *heightPtr = dInfoPtr->maxY - *yPtr;
+ if (*heightPtr <= 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextDLineInfo --
+ *
+ * Given an index, return information about the display line
+ * containing that character.
+ *
+ * Results:
+ * Zero is returned if the character is on the screen. -1
+ * means the character isn't on the screen. If the return value
+ * is 0, then information is returned in the variables pointed
+ * to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
+ CkText *textPtr; /* Widget record for text widget. */
+ CkTextIndex *indexPtr; /* Index of character whose bounding
+ * box is desired. */
+ int *xPtr, *yPtr; /* Filled with line's upper-left
+ * coordinate. */
+ int *widthPtr, *heightPtr; /* Filled in with line's dimensions. */
+ int *basePtr; /* Filled in with the baseline position,
+ * measured as an offset down from *yPtr. */
+{
+ DInfo *dInfoPtr = textPtr->dInfoPtr;
+ DLine *dlPtr;
+
+ /*
+ * Make sure that all of the screen layout information is up to date.
+ */
+
+ if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
+ UpdateDisplayInfo(textPtr);
+ }
+
+ /*
+ * Find the display line containing the desired index.
+ */
+
+ dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ if ((dlPtr == NULL) || (CkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
+ return -1;
+ }
+
+ *xPtr = dInfoPtr->x - dInfoPtr->curOffset + dlPtr->chunkPtr->x;
+ *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
+ *yPtr = dlPtr->y;
+ if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
+ *heightPtr = dInfoPtr->maxY - dlPtr->y;
+ } else {
+ *heightPtr = dlPtr->height;
+ }
+ *basePtr = 0;
+ return 0;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextCharLayoutProc --
+ *
+ * This procedure is the "layoutProc" for character segments.
+ *
+ * Results:
+ * If there is something to display for the chunk then a
+ * non-zero value is returned and the fields of chunkPtr
+ * will be filled in (see the declaration of CkTextDispChunk
+ * in ckText.h for details). If zero is returned it means
+ * that no characters from this chunk fit in the window.
+ * If -1 is returned it means that this segment just doesn't
+ * need to be displayed (never happens for text).
+ *
+ * Side effects:
+ * Memory is allocated to hold additional information about
+ * the chunk.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
+ noCharsYet, wrapMode, chunkPtr)
+ CkText *textPtr; /* Text widget being layed out. */
+ CkTextIndex *indexPtr; /* Index of first character to lay out
+ * (corresponds to segPtr and offset). */
+ CkTextSegment *segPtr; /* Segment being layed out. */
+ int offset; /* Offset within segment of first character
+ * to consider. */
+ int maxX; /* Chunk must not occupy pixels at this
+ * position or higher. */
+ int maxChars; /* Chunk must not include more than this
+ * many characters. */
+ int noCharsYet; /* Non-zero means no characters have been
+ * assigned to this display line yet. */
+ Ck_Uid wrapMode; /* How to handle line wrapping: ckTextCharUid,
+ * ckTextNoneUid, or ckTextWordUid. */
+ register CkTextDispChunk *chunkPtr;
+ /* Structure to fill in with information
+ * about this chunk. The x field has already
+ * been set by the caller. */
+{
+ int nextX, charsThatFit, count, dummy;
+ CharInfo *ciPtr;
+ char *p;
+ CkTextSegment *nextPtr;
+ CkWindow *winPtr = textPtr->winPtr;
+
+ /*
+ * Figure out how many characters will fit in the space we've got.
+ * Include the next character, even though it won't fit completely,
+ * if any of the following is true:
+ * (a) the chunk contains no characters and the display line contains
+ * no characters yet (i.e. the line isn't wide enough to hold
+ * even a single character).
+ * (b) at least one pixel of the character is visible, we haven't
+ * already exceeded the character limit, and the next character
+ * is a white space character.
+ */
+
+ p = segPtr->body.chars + offset;
+ CkMeasureChars(winPtr->mainPtr, p, maxChars, chunkPtr->x,
+ maxX, 0, CK_IGNORE_TABS, &nextX, &charsThatFit);
+ if (charsThatFit < maxChars) {
+ if ((charsThatFit == 0) && noCharsYet) {
+ charsThatFit = 1;
+ CkMeasureChars(winPtr->mainPtr, p, 1, chunkPtr->x, INT_MAX, 0,
+ CK_IGNORE_TABS, &nextX, &dummy);
+ }
+ if (p[charsThatFit] == '\n') {
+ /*
+ * A newline character takes up no space, so if the previous
+ * character fits then so does the newline.
+ */
+
+ charsThatFit++;
+ }
+ if (charsThatFit == 0) {
+ return 0;
+ }
+ }
+
+ /*
+ * Fill in the chunk structure and allocate and initialize a
+ * CharInfo structure. If the last character is a newline
+ * then don't bother to display it.
+ */
+
+ chunkPtr->displayProc = CharDisplayProc;
+ chunkPtr->undisplayProc = CharUndisplayProc;
+ chunkPtr->measureProc = CharMeasureProc;
+ chunkPtr->bboxProc = CharBboxProc;
+ chunkPtr->numChars = charsThatFit;
+ chunkPtr->minHeight = 1;
+ chunkPtr->width = nextX - chunkPtr->x;
+ chunkPtr->breakIndex = -1;
+ ciPtr = (CharInfo *) ckalloc((unsigned)
+ (sizeof(CharInfo) - 3 + charsThatFit));
+ chunkPtr->clientData = (ClientData) ciPtr;
+ ciPtr->numChars = charsThatFit;
+ ciPtr->winPtr = textPtr->winPtr;
+ strncpy(ciPtr->chars, p, (size_t) charsThatFit);
+ if (p[charsThatFit-1] == '\n') {
+ ciPtr->numChars--;
+ }
+
+ /*
+ * Compute a break location. If we're in word wrap mode, a
+ * break can occur after any space character, or at the end of
+ * the chunk if the next segment (ignoring those with zero size)
+ * is not a character segment.
+ */
+
+ if (wrapMode != ckTextWordUid) {
+ chunkPtr->breakIndex = chunkPtr->numChars;
+ } else {
+ for (count = charsThatFit, p += charsThatFit-1; count > 0;
+ count--, p--) {
+ if (isspace((unsigned char) *p)) {
+ chunkPtr->breakIndex = count;
+ break;
+ }
+ }
+ if ((charsThatFit+offset) == segPtr->size) {
+ for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
+ nextPtr = nextPtr->nextPtr) {
+ if (nextPtr->size != 0) {
+ if (nextPtr->typePtr != &ckTextCharType) {
+ chunkPtr->breakIndex = chunkPtr->numChars;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharDisplayProc --
+ *
+ * This procedure is called to display a character chunk on
+ * the screen or in an off-screen pixmap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Graphics are drawn.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+CharDisplayProc(chunkPtr, x, y, height, baseline, window, screenY)
+ CkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
+ int x; /* X-position in dst at which to
+ * draw this chunk (may differ from
+ * the x-position in the chunk because
+ * of scrolling). */
+ int y; /* Y-position at which to draw this
+ * chunk in dst. */
+ int height; /* Total height of line. */
+ int baseline; /* Offset of baseline from y. */
+ WINDOW *window;
+ int screenY; /* Y-coordinate in text window that
+ * corresponds to y. */
+{
+ CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
+ Style *stylePtr;
+ StyleValues *sValuePtr;
+
+ if ((x + chunkPtr->width) <= 0) {
+ /*
+ * The chunk is off-screen.
+ */
+
+ return;
+ }
+
+ stylePtr = chunkPtr->stylePtr;
+ sValuePtr = stylePtr->sValuePtr;
+
+ /*
+ * Draw the text for this chunk.
+ */
+
+ if (ciPtr->numChars > 0) {
+ Ck_SetWindowAttr(ciPtr->winPtr, sValuePtr->fg, sValuePtr->bg,
+ sValuePtr->attr);
+ CkDisplayChars(ciPtr->winPtr->mainPtr, window, ciPtr->chars,
+ ciPtr->numChars, x,
+ screenY + baseline, x - chunkPtr->x, CK_IGNORE_TABS);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharUndisplayProc --
+ *
+ * This procedure is called when a character chunk is no
+ * longer going to be displayed. It frees up resources
+ * that were allocated to display the chunk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and other resources get freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+CharUndisplayProc(textPtr, chunkPtr)
+ CkText *textPtr; /* Overall information about text
+ * widget. */
+ CkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
+{
+ CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
+
+ ckfree((char *) ciPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharMeasureProc --
+ *
+ * This procedure is called to determine which character in
+ * a character chunk lies over a given x-coordinate.
+ *
+ * Results:
+ * The return value is the index *within the chunk* of the
+ * character that covers the position given by "x".
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+CharMeasureProc(chunkPtr, x)
+ CkTextDispChunk *chunkPtr; /* Chunk containing desired coord. */
+ int x; /* X-coordinate, in same coordinate
+ * system as chunkPtr->x. */
+{
+ CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
+ int endX, charX;
+
+ CkMeasureChars(ciPtr->winPtr->mainPtr,
+ ciPtr->chars, chunkPtr->numChars-1, chunkPtr->x,
+ x, 0, CK_IGNORE_TABS, &endX, &charX);
+ return charX;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CharBboxProc --
+ *
+ * This procedure is called to compute the bounding box of
+ * the area occupied by a single character.
+ *
+ * Results:
+ * There is no return value. *xPtr and *yPtr are filled in
+ * with the coordinates of the upper left corner of the
+ * character, and *widthPtr and *heightPtr are filled in with
+ * the dimensions of the character in pixels. Note: not all
+ * of the returned bbox is necessarily visible on the screen
+ * (the rightmost part might be off-screen to the right,
+ * and the bottommost part might be off-screen to the bottom).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
+ widthPtr, heightPtr)
+ CkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
+ int index; /* Index of desired character within
+ * the chunk. */
+ int y; /* Topmost pixel in area allocated
+ * for this line. */
+ int lineHeight; /* Height of line, in pixels. */
+ int baseline; /* Location of line's baseline, in
+ * pixels measured down from y. */
+ int *xPtr, *yPtr; /* Gets filled in with coords of
+ * character's upper-left pixel.
+ * X-coord is in same coordinate
+ * system as chunkPtr->x. */
+ int *widthPtr; /* Gets filled in with width of
+ * character, in pixels. */
+ int *heightPtr; /* Gets filled in with height of
+ * character, in pixels. */
+{
+ CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
+ CkWindow *winPtr = ciPtr->winPtr;
+ int maxX, dummy;
+
+ maxX = chunkPtr->width + chunkPtr->x;
+ CkMeasureChars(winPtr->mainPtr,
+ ciPtr->chars, index, chunkPtr->x, 1000000, 0,
+ CK_IGNORE_TABS, xPtr, &dummy);
+ if (index == ciPtr->numChars) {
+ /*
+ * This situation only happens if the last character in a line
+ * is a space character, in which case it absorbs all of the
+ * extra space in the line (see CkTextCharLayoutProc).
+ */
+
+ *widthPtr = maxX - *xPtr;
+ } else if ((ciPtr->chars[index] == '\t')
+ && (index == (ciPtr->numChars-1))) {
+ /*
+ * The desired character is a tab character that terminates a
+ * chunk; give it all the space left in the chunk.
+ */
+
+ *widthPtr = maxX - *xPtr;
+ } else {
+ CkMeasureChars(winPtr->mainPtr,
+ ciPtr->chars + index, 1, *xPtr, 1000000, 0,
+ CK_IGNORE_TABS, widthPtr, &dummy);
+ if (*widthPtr > maxX) {
+ *widthPtr = maxX - *xPtr;
+ } else {
+ *widthPtr -= *xPtr;
+ }
+ }
+ *yPtr = y + baseline;
+ *heightPtr = 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustForTab --
+ *
+ * This procedure is called to move a series of chunks right
+ * in order to align them with a tab stop.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The width of chunkPtr gets adjusted so that it absorbs the
+ * extra space due to the tab. The x locations in all the chunks
+ * after chunkPtr are adjusted rightward to align with the tab
+ * stop given by tabArrayPtr and index.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
+ CkText *textPtr; /* Information about the text widget as
+ * a whole. */
+ CkTextTabArray *tabArrayPtr; /* Information about the tab stops
+ * that apply to this line. May be
+ * NULL to indicate default tabbing
+ * (every 8 chars). */
+ int index; /* Index of current tab stop. */
+ CkTextDispChunk *chunkPtr; /* Chunk whose last character is
+ * the tab; the following chunks
+ * contain information to be shifted
+ * right. */
+
+{
+ int x, desired, delta, width, decimal, i, gotDigit;
+ CkTextDispChunk *chunkPtr2, *decimalChunkPtr;
+ CkTextTab *tabPtr;
+ CharInfo *ciPtr = NULL; /* Initialization needed only to
+ * prevent compiler warnings. */
+ int tabX, prev, spaceWidth, dummy;
+ char *p;
+ CkTextTabAlign alignment;
+
+ if (chunkPtr->nextPtr == NULL) {
+ /*
+ * Nothing after the actual tab; just return.
+ */
+
+ return;
+ }
+
+ /*
+ * If no tab information has been given, do the usual thing:
+ * round up to the next boundary of 8 average-sized characters.
+ */
+
+ x = chunkPtr->nextPtr->x;
+ if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
+ /*
+ * No tab information has been given, so use the default
+ * interpretation of tabs.
+ */
+
+ CkMeasureChars(textPtr->winPtr->mainPtr,
+ "\t", 1, x, INT_MAX, 0, 0, &desired, &dummy);
+ goto update;
+ }
+
+ if (index < tabArrayPtr->numTabs) {
+ alignment = tabArrayPtr->tabs[index].alignment;
+ tabX = tabArrayPtr->tabs[index].location;
+ } else {
+ /*
+ * Ran out of tab stops; compute a tab position by extrapolating
+ * from the last two tab positions.
+ */
+
+ if (tabArrayPtr->numTabs > 1) {
+ prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
+ } else {
+ prev = 0;
+ }
+ alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
+ tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
+ + (index + 1 - tabArrayPtr->numTabs)
+ * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
+ }
+
+ tabPtr = &tabArrayPtr->tabs[index];
+ if (alignment == LEFT) {
+ desired = tabX;
+ goto update;
+ }
+
+ if ((alignment == CENTER) || (alignment == RIGHT)) {
+ /*
+ * Compute the width of all the information in the tab group,
+ * then use it to pick a desired location.
+ */
+
+ width = 0;
+ for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
+ chunkPtr2 = chunkPtr2->nextPtr) {
+ width += chunkPtr2->width;
+ }
+ if (alignment == CENTER) {
+ desired = tabX - width/2;
+ } else {
+ desired = tabX - width;
+ }
+ goto update;
+ }
+
+ /*
+ * Must be numeric alignment. Search through the text to be
+ * tabbed, looking for the last , or . before the first character
+ * that isn't a number, comma, period, or sign.
+ */
+
+ decimalChunkPtr = NULL;
+ decimal = gotDigit = 0;
+ for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
+ chunkPtr2 = chunkPtr2->nextPtr) {
+ if (chunkPtr2->displayProc != CharDisplayProc) {
+ continue;
+ }
+ ciPtr = (CharInfo *) chunkPtr2->clientData;
+ for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
+ if (isdigit((unsigned char) *p)) {
+ gotDigit = 1;
+ } else if ((*p == '.') || (*p == ',')) {
+ decimal = p-ciPtr->chars;
+ decimalChunkPtr = chunkPtr2;
+ } else if (gotDigit) {
+ if (decimalChunkPtr == NULL) {
+ decimal = p-ciPtr->chars;
+ decimalChunkPtr = chunkPtr2;
+ }
+ goto endOfNumber;
+ }
+ }
+ }
+ endOfNumber:
+ if (decimalChunkPtr != NULL) {
+ int curX;
+
+ ciPtr = (CharInfo *) decimalChunkPtr->clientData;
+ CkMeasureChars(ciPtr->winPtr->mainPtr,
+ ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0,
+ CK_IGNORE_TABS, &curX, &dummy);
+ desired = tabX - (curX - x);
+ goto update;
+ } else {
+ /*
+ * There wasn't a decimal point. Right justify the text.
+ */
+
+ width = 0;
+ for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
+ chunkPtr2 = chunkPtr2->nextPtr) {
+ width += chunkPtr2->width;
+ }
+ desired = tabX - width;
+ }
+
+ /*
+ * Shift all of the chunks to the right so that the left edge is
+ * at the desired location, then expand the chunk containing the
+ * tab. Be sure that the tab occupies at least the width of a
+ * space character.
+ */
+
+ update:
+ delta = desired - x;
+ CkMeasureChars(textPtr->winPtr->mainPtr, " ", 1, 0, INT_MAX,
+ 0, 0, &spaceWidth, &dummy);
+ if (delta < spaceWidth) {
+ delta = spaceWidth;
+ }
+ for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
+ chunkPtr2 = chunkPtr2->nextPtr) {
+ chunkPtr2->x += delta;
+ }
+ chunkPtr->width += delta;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SizeOfTab --
+ *
+ * This returns an estimate of the amount of white space that will
+ * be consumed by a tab.
+ *
+ * Results:
+ * The return value is the minimum number of pixels that will
+ * be occupied by the index'th tab of tabArrayPtr, assuming that
+ * the current position on the line is x and the end of the
+ * line is maxX. For numeric tabs, this is a conservative
+ * estimate. The return value is always >= 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
+ CkText *textPtr; /* Information about the text widget as
+ * a whole. */
+ CkTextTabArray *tabArrayPtr; /* Information about the tab stops
+ * that apply to this line. NULL
+ * means use default tabbing (every
+ * 8 chars.) */
+ int index; /* Index of current tab stop. */
+ int x; /* Current x-location in line. Only
+ * used if tabArrayPtr == NULL. */
+ int maxX; /* X-location of pixel just past the
+ * right edge of the line. */
+{
+ int tabX, prev, result, spaceWidth, dummy;
+ CkTextTabAlign alignment;
+ CkWindow *winPtr = textPtr->winPtr;
+
+ if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
+ CkMeasureChars(winPtr->mainPtr, "\t", 1, x, INT_MAX,
+ 0, 0, &tabX, &dummy);
+ return tabX - x;
+ }
+ if (index < tabArrayPtr->numTabs) {
+ tabX = tabArrayPtr->tabs[index].location;
+ alignment = tabArrayPtr->tabs[index].alignment;
+ } else {
+ /*
+ * Ran out of tab stops; compute a tab position by extrapolating
+ * from the last two tab positions.
+ */
+
+ if (tabArrayPtr->numTabs > 1) {
+ prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
+ } else {
+ prev = 0;
+ }
+ tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
+ + (index + 1 - tabArrayPtr->numTabs)
+ * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
+ alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
+ }
+ if (alignment == CENTER) {
+ /*
+ * Be very careful in the arithmetic below, because maxX may
+ * be the largest positive number: watch out for integer
+ * overflow.
+ */
+
+ if ((maxX-tabX) < (tabX - x)) {
+ result = (maxX - x) - 2*(maxX - tabX);
+ } else {
+ result = 0;
+ }
+ goto done;
+ }
+ if (alignment == RIGHT) {
+ result = 0;
+ goto done;
+ }
+
+ /*
+ * Note: this treats NUMERIC alignment the same as LEFT
+ * alignment, which is somewhat conservative. However, it's
+ * pretty tricky at this point to figure out exactly where
+ * the damn decimal point will be.
+ */
+
+ if (tabX > x) {
+ result = tabX - x;
+ } else {
+ result = 0;
+ }
+
+ done:
+ CkMeasureChars(winPtr->mainPtr, " ", 1, 0, INT_MAX,
+ 0, 0, &spaceWidth, &dummy);
+ if (result < spaceWidth) {
+ result = spaceWidth;
+ }
+ return result;
+}
--- /dev/null
+/*
+ * ckTextIndex.c --
+ *
+ * This module provides procedures that manipulate indices for
+ * text widgets.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1995-2000 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+
+/*
+ * Index to use to select last character in line (very large integer):
+ */
+
+#define LAST_CHAR 1000000
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static char * ForwBack _ANSI_ARGS_((char *string,
+ CkTextIndex *indexPtr));
+static char * StartEnd _ANSI_ARGS_(( char *string,
+ CkTextIndex *indexPtr));
+\f
+#if CK_USE_UTF
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CkTextMakeByteIndex --
+ *
+ * Given a line index and a byte index, look things up in the B-tree
+ * and fill in a CkTextIndex structure.
+ *
+ * Results:
+ * The structure at *indexPtr is filled in with information about the
+ * character at lineIndex and byteIndex (or the closest existing
+ * character, if the specified one doesn't exist), and indexPtr is
+ * returned as result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+CkTextIndex *
+CkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
+ CkTextBTree tree; /* Tree that lineIndex and charIndex refer
+ * to. */
+ int lineIndex; /* Index of desired line (0 means first
+ * line of text). */
+ int byteIndex; /* Byte index of desired character. */
+ CkTextIndex *indexPtr; /* Structure to fill in. */
+{
+ CkTextSegment *segPtr;
+ int index;
+ char *p, *start;
+ Tcl_UniChar ch;
+
+ indexPtr->tree = tree;
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ byteIndex = 0;
+ }
+ if (byteIndex < 0) {
+ byteIndex = 0;
+ }
+ indexPtr->linePtr = CkBTreeFindLine(tree, lineIndex);
+ if (indexPtr->linePtr == NULL) {
+ indexPtr->linePtr = CkBTreeFindLine(tree, CkBTreeNumLines(tree));
+ byteIndex = 0;
+ }
+ if (byteIndex == 0) {
+ indexPtr->charIndex = byteIndex;
+ return indexPtr;
+ }
+
+ /*
+ * Verify that the index is within the range of the line and points
+ * to a valid character boundary.
+ */
+
+ index = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (segPtr == NULL) {
+ /*
+ * Use the index of the last character in the line. Since
+ * the last character on the line is guaranteed to be a '\n',
+ * we can back up a constant sizeof(char) bytes.
+ */
+
+ indexPtr->charIndex = index - sizeof(char);
+ break;
+ }
+ if (index + segPtr->size > byteIndex) {
+ indexPtr->charIndex = byteIndex;
+ if ((byteIndex > index) && (segPtr->typePtr == &ckTextCharType)) {
+ /*
+ * Prevent UTF-8 character from being split up by ensuring
+ * that byteIndex falls on a character boundary. If index
+ * falls in the middle of a UTF-8 character, it will be
+ * adjusted to the end of that UTF-8 character.
+ */
+
+ start = segPtr->body.chars + (byteIndex - index);
+ p = Tcl_UtfPrev(start, segPtr->body.chars);
+ p += Tcl_UtfToUniChar(p, &ch);
+ indexPtr->charIndex += p - start;
+ }
+ break;
+ }
+ index += segPtr->size;
+ }
+ return indexPtr;
+}
+#endif
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextMakeIndex --
+ *
+ * Given a line index and a character index, look things up
+ * in the B-tree and fill in a CkTextIndex structure.
+ *
+ * Results:
+ * The structure at *indexPtr is filled in with information
+ * about the character at lineIndex and charIndex (or the
+ * closest existing character, if the specified one doesn't
+ * exist), and indexPtr is returned as result.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+CkTextIndex *
+CkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
+ CkTextBTree tree; /* Tree that lineIndex and charIndex refer
+ * to. */
+ int lineIndex; /* Index of desired line (0 means first
+ * line of text). */
+ int charIndex; /* Index of desired character. */
+ CkTextIndex *indexPtr; /* Structure to fill in. */
+{
+ register CkTextSegment *segPtr;
+ int index;
+#if CK_USE_UTF
+ char *p, *start, *end;
+ int offset;
+ Tcl_UniChar ch;
+#endif
+
+ indexPtr->tree = tree;
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ charIndex = 0;
+ }
+ if (charIndex < 0) {
+ charIndex = 0;
+ }
+ indexPtr->linePtr = CkBTreeFindLine(tree, lineIndex);
+ if (indexPtr->linePtr == NULL) {
+ indexPtr->linePtr = CkBTreeFindLine(tree, CkBTreeNumLines(tree));
+ charIndex = 0;
+ }
+
+ /*
+ * Verify that the index is within the range of the line.
+ * If not, just use the index of the last character in the line.
+ */
+
+ for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
+ segPtr = segPtr->nextPtr) {
+ if (segPtr == NULL) {
+ indexPtr->charIndex = index-1;
+ break;
+ }
+#if CK_USE_UTF
+ if (segPtr->typePtr == &ckTextCharType) {
+ /*
+ * Turn character offset into a byte offset.
+ */
+
+ start = segPtr->body.chars;
+ end = start + segPtr->size;
+ for (p = start; p < end; p += offset) {
+ if (charIndex == 0) {
+ indexPtr->charIndex = index;
+ return indexPtr;
+ }
+ charIndex--;
+ offset = Tcl_UtfToUniChar(p, &ch);
+ index += offset;
+ }
+ } else {
+ if (charIndex < segPtr->size) {
+ indexPtr->charIndex = index;
+ break;
+ }
+ charIndex -= segPtr->size;
+ index += segPtr->size;
+ }
+#else
+ index += segPtr->size;
+ if (index > charIndex) {
+ indexPtr->charIndex = charIndex;
+ break;
+ }
+#endif
+ }
+ return indexPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextIndexToSeg --
+ *
+ * Given an index, this procedure returns the segment and
+ * offset within segment for the index.
+ *
+ * Results:
+ * The return value is a pointer to the segment referred to
+ * by indexPtr; this will always be a segment with non-zero
+ * size. The variable at *offsetPtr is set to hold the
+ * integer offset within the segment of the character
+ * given by indexPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+CkTextSegment *
+CkTextIndexToSeg(indexPtr, offsetPtr)
+ CkTextIndex *indexPtr; /* Text index. */
+ int *offsetPtr; /* Where to store offset within
+ * segment, or NULL if offset isn't
+ * wanted. */
+{
+ register CkTextSegment *segPtr;
+ int offset;
+
+ for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
+ offset >= segPtr->size;
+ offset -= segPtr->size, segPtr = segPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if (offsetPtr != NULL) {
+ *offsetPtr = offset;
+ }
+ return segPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextSegToOffset --
+ *
+ * Given a segment pointer and the line containing it, this
+ * procedure returns the offset of the segment within its
+ * line.
+ *
+ * Results:
+ * The return value is the offset (within its line) of the
+ * first character in segPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextSegToOffset(segPtr, linePtr)
+ CkTextSegment *segPtr; /* Segment whose offset is desired. */
+ CkTextLine *linePtr; /* Line containing segPtr. */
+{
+ CkTextSegment *segPtr2;
+ int offset;
+
+ offset = 0;
+ for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
+ segPtr2 = segPtr2->nextPtr) {
+ offset += segPtr2->size;
+ }
+ return offset;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextGetIndex --
+ *
+ * Given a string, return the line and character indices that
+ * it describes.
+ *
+ * Results:
+ * The return value is a standard Tcl return result. If
+ * TCL_OK is returned, then everything went well and the index
+ * at *indexPtr is filled in; otherwise TCL_ERROR is returned
+ * and an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkTextGetIndex(interp, textPtr, string, indexPtr)
+ Tcl_Interp *interp; /* Use this for error reporting. */
+ CkText *textPtr; /* Information about text widget. */
+ char *string; /* Textual description of position. */
+ CkTextIndex *indexPtr; /* Index structure to fill in. */
+{
+ register char *p;
+ char *end, *endOfBase;
+ Tcl_HashEntry *hPtr;
+ CkTextTag *tagPtr;
+ CkTextSearch search;
+ CkTextIndex first, last;
+ int wantLast, result;
+ char c;
+
+ /*
+ *---------------------------------------------------------------------
+ * Stage 1: check to see if the index consists of nothing but a mar
+ * name. We do this check now even though it's also done later, in
+ * order to allow mark names that include funny characters such as
+ * spaces or "+1c".
+ *---------------------------------------------------------------------
+ */
+
+ if (CkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
+ return TCL_OK;
+ }
+
+ /*
+ *------------------------------------------------
+ * Stage 2: start again by parsing the base index.
+ *------------------------------------------------
+ */
+
+ indexPtr->tree = textPtr->tree;
+
+ /*
+ * First look for the form "tag.first" or "tag.last" where "tag"
+ * is the name of a valid tag. Try to use up as much as possible
+ * of the string in this check (strrchr instead of strchr below).
+ * Doing the check now, and in this way, allows tag names to include
+ * funny characters like "@" or "+1c".
+ */
+
+ p = strrchr(string, '.');
+ if (p != NULL) {
+ if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
+ wantLast = 0;
+ endOfBase = p+6;
+ } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
+ wantLast = 1;
+ endOfBase = p+5;
+ } else {
+ goto tryxy;
+ }
+ *p = 0;
+ hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
+ *p = '.';
+ if (hPtr == NULL) {
+ goto tryxy;
+ }
+ tagPtr = (CkTextTag *) Tcl_GetHashValue(hPtr);
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
+ CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree), 0,
+ &last);
+#else
+ CkTextMakeIndex(textPtr->tree, 0, 0, &first);
+ CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree), 0,
+ &last);
+#endif
+ CkBTreeStartSearch(&first, &last, tagPtr, &search);
+ if (!CkBTreeCharTagged(&first, tagPtr) && !CkBTreeNextTag(&search)) {
+ Tcl_AppendResult(interp,
+ "text doesn't contain any characters tagged with \"",
+ Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = search.curIndex;
+ if (wantLast) {
+ while (CkBTreeNextTag(&search)) {
+ *indexPtr = search.curIndex;
+ }
+ }
+ goto gotBase;
+ }
+
+ tryxy:
+ if (string[0] == '@') {
+ /*
+ * Find character at a given x,y location in the window.
+ */
+
+ int x, y;
+
+ p = string+1;
+ x = strtol(p, &end, 0);
+ if ((end == p) || (*end != ',')) {
+ goto error;
+ }
+ p = end+1;
+ y = strtol(p, &end, 0);
+ if (end == p) {
+ goto error;
+ }
+ CkTextPixelIndex(textPtr, x, y, indexPtr);
+ endOfBase = end;
+ goto gotBase;
+ }
+
+ if (isdigit((unsigned char) string[0]) || (string[0] == '-')) {
+ int lineIndex, charIndex;
+
+ /*
+ * Base is identified with line and character indices.
+ */
+
+ lineIndex = strtol(string, &end, 0) - 1;
+ if ((end == string) || (*end != '.')) {
+ goto error;
+ }
+ p = end+1;
+ if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
+ charIndex = LAST_CHAR;
+ endOfBase = p+3;
+ } else {
+ charIndex = strtol(p, &end, 0);
+ if (end == p) {
+ goto error;
+ }
+ endOfBase = end;
+ }
+ CkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
+ goto gotBase;
+ }
+
+ for (p = string; *p != 0; p++) {
+ if (isspace((unsigned char) *p) || (*p == '+') || (*p == '-')) {
+ break;
+ }
+ }
+ endOfBase = p;
+#if 0
+ if (string[0] == '.') {
+ /*
+ * See if the base position is the name of an embedded window.
+ */
+
+ c = *endOfBase;
+ *endOfBase = 0;
+ result = CkTextWindowIndex(textPtr, string, indexPtr);
+ *endOfBase = c;
+ if (result != 0) {
+ goto gotBase;
+ }
+ }
+#endif
+ if ((string[0] == 'e')
+ && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
+ /*
+ * Base position is end of text.
+ */
+
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, indexPtr);
+#else
+ CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, indexPtr);
+#endif
+ goto gotBase;
+ } else {
+ /*
+ * See if the base position is the name of a mark.
+ */
+
+ c = *endOfBase;
+ *endOfBase = 0;
+ result = CkTextMarkNameToIndex(textPtr, string, indexPtr);
+ *endOfBase = c;
+ if (result == TCL_OK) {
+ goto gotBase;
+ }
+ }
+ goto error;
+
+ /*
+ *-------------------------------------------------------------------
+ * Stage 3: process zero or more modifiers. Each modifier is either
+ * a keyword like "wordend" or "linestart", or it has the form
+ * "op count units" where op is + or -, count is a number, and units
+ * is "chars" or "lines".
+ *-------------------------------------------------------------------
+ */
+
+ gotBase:
+ p = endOfBase;
+ while (1) {
+ while (isspace((unsigned char) *p)) {
+ p++;
+ }
+ if (*p == 0) {
+ break;
+ }
+
+ if ((*p == '+') || (*p == '-')) {
+ p = ForwBack(p, indexPtr);
+ } else {
+ p = StartEnd(p, indexPtr);
+ }
+ if (p == NULL) {
+ goto error;
+ }
+ }
+ return TCL_OK;
+
+ error:
+ Tcl_AppendResult(interp, "bad text index \"", string, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextPrintIndex --
+ *
+ *
+ * This procedure generates a string description of an index,
+ * suitable for reading in again later.
+ *
+ * Results:
+ * The characters pointed to by string are modified.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextPrintIndex(indexPtr, string)
+ CkTextIndex *indexPtr; /* Pointer to index. */
+ char *string; /* Place to store the position. Must have
+ * at least TK_POS_CHARS characters. */
+{
+#if CK_USE_UTF
+ CkTextSegment *segPtr;
+ int numBytes, charIndex;
+
+ numBytes = indexPtr->charIndex;
+ charIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (numBytes <= segPtr->size) {
+ break;
+ }
+ if (segPtr->typePtr == &ckTextCharType) {
+ charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
+ } else {
+ charIndex += segPtr->size;
+ }
+ numBytes -= segPtr->size;
+ }
+ if (segPtr->typePtr == &ckTextCharType) {
+ charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
+ } else {
+ charIndex += numBytes;
+ }
+ sprintf(string, "%d.%d", CkBTreeLineIndex(indexPtr->linePtr) + 1,
+ charIndex);
+#else
+ sprintf(string, "%d.%d", CkBTreeLineIndex(indexPtr->linePtr) + 1,
+ indexPtr->charIndex);
+#endif
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextIndexCmp --
+ *
+ * Compare two indices to see which one is earlier in
+ * the text.
+ *
+ * Results:
+ * The return value is 0 if index1Ptr and index2Ptr refer
+ * to the same position in the file, -1 if index1Ptr refers
+ * to an earlier position than index2Ptr, and 1 otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextIndexCmp(index1Ptr, index2Ptr)
+ CkTextIndex *index1Ptr; /* First index. */
+ CkTextIndex *index2Ptr; /* Second index. */
+{
+ int line1, line2;
+
+ if (index1Ptr->linePtr == index2Ptr->linePtr) {
+ if (index1Ptr->charIndex < index2Ptr->charIndex) {
+ return -1;
+ } else if (index1Ptr->charIndex > index2Ptr->charIndex) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ line1 = CkBTreeLineIndex(index1Ptr->linePtr);
+ line2 = CkBTreeLineIndex(index2Ptr->linePtr);
+ if (line1 < line2) {
+ return -1;
+ }
+ if (line1 > line2) {
+ return 1;
+ }
+ return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ForwBack --
+ *
+ * This procedure handles +/- modifiers for indices to adjust
+ * the index forwards or backwards.
+ *
+ * Results:
+ * If the modifier in string is successfully parsed then the
+ * return value is the address of the first character after the
+ * modifier, and *indexPtr is updated to reflect the modifier.
+ * If there is a syntax error in the modifier then NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+ForwBack(string, indexPtr)
+ char *string; /* String to parse for additional info
+ * about modifier (count and units).
+ * Points to "+" or "-" that starts
+ * modifier. */
+ CkTextIndex *indexPtr; /* Index to update as specified in string. */
+{
+ register char *p;
+ char *end, *units;
+ int count, lineIndex;
+ size_t length;
+
+ /*
+ * Get the count (how many units forward or backward).
+ */
+
+ p = string+1;
+ while (isspace((unsigned char) *p)) {
+ p++;
+ }
+ count = strtol(p, &end, 0);
+ if (end == p) {
+ return NULL;
+ }
+ p = end;
+ while (isspace((unsigned char) *p)) {
+ p++;
+ }
+
+ /*
+ * Find the end of this modifier (next space or + or - character),
+ * then parse the unit specifier and update the position
+ * accordingly.
+ */
+
+ units = p;
+ while ((*p != 0) && !isspace((unsigned char) *p)
+ && (*p != '+') && (*p != '-')) {
+ p++;
+ }
+ length = p - units;
+ if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
+ if (*string == '+') {
+ CkTextIndexForwChars(indexPtr, count, indexPtr);
+ } else {
+ CkTextIndexBackChars(indexPtr, count, indexPtr);
+ }
+ } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
+ lineIndex = CkBTreeLineIndex(indexPtr->linePtr);
+ if (*string == '+') {
+ lineIndex += count;
+ } else {
+ lineIndex -= count;
+
+ /*
+ * The check below retains the character position, even
+ * if the line runs off the start of the file. Without
+ * it, the character position will get reset to 0 by
+ * CkTextMakeIndex.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ }
+ }
+#if CK_USE_UTF
+ CkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
+ indexPtr);
+#else
+ CkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
+ indexPtr);
+#endif
+ } else {
+ return NULL;
+ }
+ return p;
+}
+\f
+#if CK_USE_UTF
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CkTextIndexForwBytes --
+ *
+ * Given an index for a text widget, this procedure creates a new
+ * index that points "count" bytes ahead of the source index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" bytes after
+ * srcPtr, or to the last character in the CkText if there aren't
+ * "count" bytes left.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+CkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
+ CkTextIndex *srcPtr; /* Source index. */
+ int byteCount; /* How many bytes forward to move. May be
+ * negative. */
+ CkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ CkTextLine *linePtr;
+ CkTextSegment *segPtr;
+ int lineLength;
+
+ if (byteCount < 0) {
+ CkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+ dstPtr->charIndex += byteCount;
+ while (1) {
+ /*
+ * Compute the length of the current line.
+ */
+
+ lineLength = 0;
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ lineLength += segPtr->size;
+ }
+
+ /*
+ * If the new index is in the same line then we're done.
+ * Otherwise go on to the next line.
+ */
+
+ if (dstPtr->charIndex < lineLength) {
+ return;
+ }
+ dstPtr->charIndex -= lineLength;
+ linePtr = CkBTreeNextLine(dstPtr->linePtr);
+ if (linePtr == NULL) {
+ dstPtr->charIndex = lineLength - 1;
+ return;
+ }
+ dstPtr->linePtr = linePtr;
+ }
+}
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextIndexForwChars --
+ *
+ * Given an index for a text widget, this procedure creates a
+ * new index that points "count" characters ahead of the source
+ * index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" characters
+ * after srcPtr, or to the last character in the file if there aren't
+ * "count" characters left in the file.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextIndexForwChars(srcPtr, count, dstPtr)
+ CkTextIndex *srcPtr; /* Source index. */
+ int count; /* How many characters forward to
+ * move. May be negative. */
+ CkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ CkTextLine *linePtr;
+ CkTextSegment *segPtr;
+ int lineLength;
+#if CK_USE_UTF
+ int byteOffset;
+ char *p, *start, *end;
+ Tcl_UniChar ch;
+#endif
+
+ if (count < 0) {
+ CkTextIndexBackChars(srcPtr, -count, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+
+#if CK_USE_UTF
+ segPtr = CkTextIndexToSeg(dstPtr, &byteOffset);
+ while (1) {
+
+ /*
+ * Go through each segment in line looking for specified character
+ * index.
+ */
+
+ for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ start = segPtr->body.chars + byteOffset;
+ end = segPtr->body.chars + segPtr->size;
+ for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
+ if (count == 0) {
+ dstPtr->charIndex += (p - start);
+ return;
+ }
+ count--;
+ }
+ } else {
+ if (count < segPtr->size - byteOffset) {
+ dstPtr->charIndex += count;
+ return;
+ }
+ count -= segPtr->size - byteOffset;
+ }
+ dstPtr->charIndex += segPtr->size - byteOffset;
+ byteOffset = 0;
+ }
+
+ /*
+ * Go to the next line. If we are at the end of the text item,
+ * back up one byte (for the terminal '\n' character) and return
+ * that index.
+ */
+
+ linePtr = CkBTreeNextLine(dstPtr->linePtr);
+ if (linePtr == NULL) {
+ dstPtr->charIndex -= sizeof(char);
+ return;
+ }
+ dstPtr->linePtr = linePtr;
+ dstPtr->charIndex = 0;
+ segPtr = dstPtr->linePtr->segPtr;
+ }
+#else
+ dstPtr->charIndex += count;
+ while (1) {
+ /*
+ * Compute the length of the current line.
+ */
+
+ lineLength = 0;
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ lineLength += segPtr->size;
+ }
+
+ /*
+ * If the new index is in the same line then we're done.
+ * Otherwise go on to the next line.
+ */
+
+ if (dstPtr->charIndex < lineLength) {
+ return;
+ }
+ dstPtr->charIndex -= lineLength;
+ linePtr = CkBTreeNextLine(dstPtr->linePtr);
+ if (linePtr == NULL) {
+ dstPtr->charIndex = lineLength - 1;
+ return;
+ }
+ dstPtr->linePtr = linePtr;
+ }
+#endif
+}
+\f
+#if CK_USE_UTF
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CkTextIndexBackBytes --
+ *
+ * Given an index for a text widget, this procedure creates a new
+ * index that points "count" bytes earlier than the source index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" bytes before
+ * srcPtr, or to the first character in the CkText if there aren't
+ * "count" bytes earlier than srcPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+CkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
+ CkTextIndex *srcPtr; /* Source index. */
+ int byteCount; /* How many bytes backward to move. May be
+ * negative. */
+ CkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ CkTextSegment *segPtr;
+ int lineIndex;
+
+ if (byteCount < 0) {
+ CkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+ dstPtr->charIndex -= byteCount;
+ lineIndex = -1;
+ while (dstPtr->charIndex < 0) {
+ /*
+ * Move back one line in the text. If we run off the beginning
+ * of the file then just return the first character in the text.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
+ }
+ if (lineIndex == 0) {
+ dstPtr->charIndex = 0;
+ return;
+ }
+ lineIndex--;
+ dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
+
+ /*
+ * Compute the length of the line and add that to dstPtr->charIndex.
+ */
+
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ dstPtr->charIndex += segPtr->size;
+ }
+ }
+}
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextIndexBackChars --
+ *
+ * Given an index for a text widget, this procedure creates a
+ * new index that points "count" characters earlier than the
+ * source index.
+ *
+ * Results:
+ * *dstPtr is modified to refer to the character "count" characters
+ * before srcPtr, or to the first character in the file if there aren't
+ * "count" characters earlier than srcPtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextIndexBackChars(srcPtr, count, dstPtr)
+ CkTextIndex *srcPtr; /* Source index. */
+ int count; /* How many characters backward to
+ * move. May be negative. */
+ CkTextIndex *dstPtr; /* Destination index: gets modified. */
+{
+ CkTextSegment *segPtr;
+ int lineIndex;
+#if CK_USE_UTF
+ CkTextSegment *oldPtr;
+ int segSize;
+ char *p, *start, *end;
+#endif
+
+ if (count < 0) {
+ CkTextIndexForwChars(srcPtr, -count, dstPtr);
+ return;
+ }
+
+ *dstPtr = *srcPtr;
+#if CK_USE_UTF
+
+ /*
+ * Find offset within seg that contains byteIndex.
+ * Move backward specified number of chars.
+ */
+
+ lineIndex = -1;
+
+ segSize = dstPtr->charIndex;
+ for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (segSize <= segPtr->size) {
+ break;
+ }
+ segSize -= segPtr->size;
+ }
+ while (1) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ start = segPtr->body.chars;
+ end = segPtr->body.chars + segSize;
+ for (p = end; ; p = Tcl_UtfPrev(p, start)) {
+ if (count == 0) {
+ dstPtr->charIndex -= (end - p);
+ return;
+ }
+ if (p == start) {
+ break;
+ }
+ count--;
+ }
+ } else {
+ if (count <= segSize) {
+ dstPtr->charIndex -= count;
+ return;
+ }
+ count -= segSize;
+ }
+ dstPtr->charIndex -= segSize;
+
+ /*
+ * Move back into previous segment.
+ */
+
+ oldPtr = segPtr;
+ segPtr = dstPtr->linePtr->segPtr;
+ if (segPtr != oldPtr) {
+ for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
+ /* Empty body. */
+ }
+ segSize = segPtr->size;
+ continue;
+ }
+
+ /*
+ * Move back to previous line.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
+ }
+ if (lineIndex == 0) {
+ dstPtr->charIndex = 0;
+ return;
+ }
+ lineIndex--;
+ dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
+
+ /*
+ * Compute the length of the line and add that to dstPtr->byteIndex.
+ */
+
+ oldPtr = dstPtr->linePtr->segPtr;
+ for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ dstPtr->charIndex += segPtr->size;
+ oldPtr = segPtr;
+ }
+ segPtr = oldPtr;
+ segSize = segPtr->size;
+ }
+#else
+ dstPtr->charIndex -= count;
+ lineIndex = -1;
+ while (dstPtr->charIndex < 0) {
+ /*
+ * Move back one line in the text. If we run off the beginning
+ * of the file then just return the first character in the text.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = CkBTreeLineIndex(dstPtr->linePtr);
+ }
+ if (lineIndex == 0) {
+ dstPtr->charIndex = 0;
+ return;
+ }
+ lineIndex--;
+ dstPtr->linePtr = CkBTreeFindLine(dstPtr->tree, lineIndex);
+
+ /*
+ * Compute the length of the line and add that to dstPtr->charIndex.
+ */
+
+ for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ dstPtr->charIndex += segPtr->size;
+ }
+ }
+#endif
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * StartEnd --
+ *
+ * This procedure handles modifiers like "wordstart" and "lineend"
+ * to adjust indices forwards or backwards.
+ *
+ * Results:
+ * If the modifier is successfully parsed then the return value
+ * is the address of the first character after the modifier, and
+ * *indexPtr is updated to reflect the modifier. If there is a
+ * syntax error in the modifier then NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+StartEnd(string, indexPtr)
+ char *string; /* String to parse for additional info
+ * about modifier (count and units).
+ * Points to first character of modifer
+ * word. */
+ CkTextIndex *indexPtr; /* Index to mdoify based on string. */
+{
+ char *p;
+ int c, offset;
+ size_t length;
+ register CkTextSegment *segPtr;
+
+ /*
+ * Find the end of the modifier word.
+ */
+
+ for (p = string; isalnum((unsigned char) *p); p++) {
+ /* Empty loop body. */
+ }
+ length = p-string;
+ if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
+ && (length >= 5)) {
+ indexPtr->charIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ indexPtr->charIndex += segPtr->size;
+ }
+ indexPtr->charIndex -= 1;
+ } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
+ && (length >= 5)) {
+ indexPtr->charIndex = 0;
+ } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
+ && (length >= 5)) {
+ int firstChar = 1;
+
+ /*
+ * If the current character isn't part of a word then just move
+ * forward one character. Otherwise move forward until finding
+ * a character that isn't part of a word and stop there.
+ */
+
+ segPtr = CkTextIndexToSeg(indexPtr, &offset);
+ while (1) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ c = segPtr->body.chars[offset];
+ if (!isalnum((unsigned char) c) && (c != '_')) {
+ break;
+ }
+ firstChar = 0;
+ }
+ offset += 1;
+ indexPtr->charIndex += 1;
+ if (offset >= segPtr->size) {
+ segPtr = CkTextIndexToSeg(indexPtr, &offset);
+ }
+ }
+ if (firstChar) {
+ CkTextIndexForwChars(indexPtr, 1, indexPtr);
+ }
+ } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
+ && (length >= 5)) {
+ int firstChar = 1;
+
+ /*
+ * Starting with the current character, look for one that's not
+ * part of a word and keep moving backward until you find one.
+ * Then if the character found wasn't the first one, move forward
+ * again one position.
+ */
+
+ segPtr = CkTextIndexToSeg(indexPtr, &offset);
+ while (1) {
+ if (segPtr->typePtr == &ckTextCharType) {
+ c = segPtr->body.chars[offset];
+ if (!isalnum((unsigned char) c) && (c != '_')) {
+ break;
+ }
+ firstChar = 0;
+ }
+ offset -= 1;
+ indexPtr->charIndex -= 1;
+ if (offset < 0) {
+ if (indexPtr->charIndex < 0) {
+ indexPtr->charIndex = 0;
+ goto done;
+ }
+ segPtr = CkTextIndexToSeg(indexPtr, &offset);
+ }
+ }
+ if (!firstChar) {
+ CkTextIndexForwChars(indexPtr, 1, indexPtr);
+ }
+ } else {
+ return NULL;
+ }
+ done:
+ return p;
+}
--- /dev/null
+/*
+ * ckTextMark.c --
+ *
+ * This file contains the procedure that implement marks for
+ * text widgets.
+ *
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+
+/*
+ * Macro that determines the size of a mark segment:
+ */
+
+#define MSEG_SIZE ((unsigned) (Ck_Offset(CkTextSegment, body) \
+ + sizeof(CkTextMark)))
+
+/*
+ * Forward references for procedures defined in this file:
+ */
+
+static void InsertUndisplayProc _ANSI_ARGS_((CkText *textPtr,
+ CkTextDispChunk *chunkPtr));
+static int MarkDeleteProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr, int treeGone));
+static CkTextSegment * MarkCleanupProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static void MarkCheckProc _ANSI_ARGS_((CkTextSegment *segPtr,
+ CkTextLine *linePtr));
+static int MarkLayoutProc _ANSI_ARGS_((CkText *textPtr,
+ CkTextIndex *indexPtr, CkTextSegment *segPtr,
+ int offset, int maxX, int maxChars,
+ int noCharsYet, Ck_Uid wrapMode,
+ CkTextDispChunk *chunkPtr));
+
+/*
+ * The following structures declare the "mark" segment types.
+ * There are actually two types for marks, one with left gravity
+ * and one with right gravity. They are identical except for
+ * their gravity property.
+ */
+
+Ck_SegType ckTextRightMarkType = {
+ "mark", /* name */
+ 0, /* leftGravity */
+ (Ck_SegSplitProc *) NULL, /* splitProc */
+ MarkDeleteProc, /* deleteProc */
+ MarkCleanupProc, /* cleanupProc */
+ (Ck_SegLineChangeProc *) NULL, /* lineChangeProc */
+ MarkLayoutProc, /* layoutProc */
+ MarkCheckProc /* checkProc */
+};
+
+Ck_SegType ckTextLeftMarkType = {
+ "mark", /* name */
+ 1, /* leftGravity */
+ (Ck_SegSplitProc *) NULL, /* splitProc */
+ MarkDeleteProc, /* deleteProc */
+ MarkCleanupProc, /* cleanupProc */
+ (Ck_SegLineChangeProc *) NULL, /* lineChangeProc */
+ MarkLayoutProc, /* layoutProc */
+ MarkCheckProc /* checkProc */
+};
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextMarkCmd --
+ *
+ * This procedure is invoked to process the "mark" options of
+ * the widget command for text widgets. See the user documentation
+ * for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextMarkCmd(textPtr, interp, argc, argv)
+ register CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "mark". */
+{
+ int c, i;
+ size_t length;
+ Tcl_HashEntry *hPtr;
+ CkTextSegment *markPtr;
+ Tcl_HashSearch search;
+ CkTextIndex index;
+ Ck_SegType *newTypePtr;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " mark option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'g') && (strncmp(argv[2], "gravity", length) == 0)) {
+ if (argc > 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " mark gravity markName ?gravity?",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[3]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "there is no mark named \"",
+ argv[3], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ markPtr = (CkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (argc == 4) {
+ if (markPtr->typePtr == &ckTextRightMarkType) {
+ interp->result = "right";
+ } else {
+ interp->result = "left";
+ }
+ return TCL_OK;
+ }
+ length = strlen(argv[4]);
+ c = argv[4][0];
+ if ((c == 'l') && (strncmp(argv[4], "left", length) == 0)) {
+ newTypePtr = &ckTextLeftMarkType;
+ } else if ((c == 'r') && (strncmp(argv[4], "right", length) == 0)) {
+ newTypePtr = &ckTextRightMarkType;
+ } else {
+ Tcl_AppendResult(interp, "bad mark gravity \"",
+ argv[4], "\": must be left or right", (char *) NULL);
+ return TCL_ERROR;
+ }
+ CkTextMarkSegToIndex(textPtr, markPtr, &index);
+ CkBTreeUnlinkSegment(textPtr->tree, markPtr,
+ markPtr->body.mark.linePtr);
+ markPtr->typePtr = newTypePtr;
+ CkBTreeLinkSegment(markPtr, &index);
+ } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " mark names\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_AppendElement(interp,
+ Tcl_GetHashKey(&textPtr->markTable, hPtr));
+ }
+ } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " mark set markName index\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[4], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ CkTextSetMark(textPtr, argv[3], &index);
+ } else if ((c == 'u') && (strncmp(argv[2], "unset", length) == 0)) {
+ for (i = 3; i < argc; i++) {
+ hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[i]);
+ if (hPtr != NULL) {
+ markPtr = (CkTextSegment *) Tcl_GetHashValue(hPtr);
+ if ((markPtr == textPtr->insertMarkPtr)
+ || (markPtr == textPtr->currentMarkPtr)) {
+ continue;
+ }
+ CkBTreeUnlinkSegment(textPtr->tree, markPtr,
+ markPtr->body.mark.linePtr);
+ Tcl_DeleteHashEntry(hPtr);
+ ckfree((char *) markPtr);
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad mark option \"", argv[2],
+ "\": must be gravity, names, set, or unset",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextSetMark --
+ *
+ * Set a mark to a particular position, creating a new mark if
+ * one doesn't already exist.
+ *
+ * Results:
+ * The return value is a pointer to the mark that was just set.
+ *
+ * Side effects:
+ * A new mark is created, or an existing mark is moved.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextSegment *
+CkTextSetMark(textPtr, name, indexPtr)
+ CkText *textPtr; /* Text widget in which to create mark. */
+ char *name; /* Name of mark to set. */
+ CkTextIndex *indexPtr; /* Where to set mark. */
+{
+ Tcl_HashEntry *hPtr;
+ CkTextSegment *markPtr;
+ CkTextIndex insertIndex;
+ int new;
+
+ hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
+ markPtr = (CkTextSegment *) Tcl_GetHashValue(hPtr);
+ if (!new) {
+ /*
+ * If this is the insertion point that's being moved, be sure
+ * to force a display update at the old position. Also, don't
+ * let the insertion cursor be after the final newline of the
+ * file.
+ */
+
+ if (markPtr == textPtr->insertMarkPtr) {
+ CkTextIndex index, index2;
+ CkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
+ CkTextIndexForwChars(&index, 1, &index2);
+ CkTextChanged(textPtr, &index, &index2);
+ if (CkBTreeLineIndex(indexPtr->linePtr)
+ == CkBTreeNumLines(textPtr->tree)) {
+ CkTextIndexBackChars(indexPtr, 1, &insertIndex);
+ indexPtr = &insertIndex;
+ }
+ }
+ CkBTreeUnlinkSegment(textPtr->tree, markPtr,
+ markPtr->body.mark.linePtr);
+ } else {
+ markPtr = (CkTextSegment *) ckalloc(MSEG_SIZE);
+ markPtr->typePtr = &ckTextRightMarkType;
+ markPtr->size = 0;
+ markPtr->body.mark.textPtr = textPtr;
+ markPtr->body.mark.linePtr = indexPtr->linePtr;
+ markPtr->body.mark.hPtr = hPtr;
+ Tcl_SetHashValue(hPtr, markPtr);
+ }
+ CkBTreeLinkSegment(markPtr, indexPtr);
+
+ /*
+ * If the mark is the insertion cursor, then update the screen at the
+ * mark's new location.
+ */
+
+ if (markPtr == textPtr->insertMarkPtr) {
+ CkTextIndex index2;
+
+ CkTextIndexForwChars(indexPtr, 1, &index2);
+ CkTextChanged(textPtr, indexPtr, &index2);
+ }
+ return markPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextMarkSegToIndex --
+ *
+ * Given a segment that is a mark, create an index that
+ * refers to the next text character (or other text segment
+ * with non-zero size) after the mark.
+ *
+ * Results:
+ * *IndexPtr is filled in with index information.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
+ CkText *textPtr; /* Text widget containing mark. */
+ CkTextSegment *markPtr; /* Mark segment. */
+ CkTextIndex *indexPtr; /* Index information gets stored here. */
+{
+ CkTextSegment *segPtr;
+
+ indexPtr->tree = textPtr->tree;
+ indexPtr->linePtr = markPtr->body.mark.linePtr;
+ indexPtr->charIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
+ segPtr = segPtr->nextPtr) {
+ indexPtr->charIndex += segPtr->size;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextMarkNameToIndex --
+ *
+ * Given the name of a mark, return an index corresponding
+ * to the mark name.
+ *
+ * Results:
+ * The return value is TCL_OK if "name" exists as a mark in
+ * the text widget. In this case *indexPtr is filled in with
+ * the next segment whose after the mark whose size is
+ * non-zero. TCL_ERROR is returned if the mark doesn't exist
+ * in the text widget.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextMarkNameToIndex(textPtr, name, indexPtr)
+ CkText *textPtr; /* Text widget containing mark. */
+ char *name; /* Name of mark. */
+ CkTextIndex *indexPtr; /* Index information gets stored here. */
+{
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&textPtr->markTable, name);
+ if (hPtr == NULL) {
+ return TCL_ERROR;
+ }
+ CkTextMarkSegToIndex(textPtr, (CkTextSegment *) Tcl_GetHashValue(hPtr),
+ indexPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkDeleteProc --
+ *
+ * This procedure is invoked by the text B-tree code whenever
+ * a mark lies in a range of characters being deleted.
+ *
+ * Results:
+ * Returns 1 to indicate that deletion has been rejected.
+ *
+ * Side effects:
+ * None (even if the whole tree is being deleted we don't
+ * free up the mark; it will be done elsewhere).
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+MarkDeleteProc(segPtr, linePtr, treeGone)
+ CkTextSegment *segPtr; /* Segment being deleted. */
+ CkTextLine *linePtr; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ return 1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkCleanupProc --
+ *
+ * This procedure is invoked by the B-tree code whenever a
+ * mark segment is moved from one line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The linePtr field of the segment gets updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static CkTextSegment *
+MarkCleanupProc(markPtr, linePtr)
+ CkTextSegment *markPtr; /* Mark segment that's being moved. */
+ CkTextLine *linePtr; /* Line that now contains segment. */
+{
+ markPtr->body.mark.linePtr = linePtr;
+ return markPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkLayoutProc --
+ *
+ * This procedure is the "layoutProc" for mark segments.
+ *
+ * Results:
+ * If the mark isn't the insertion cursor then the return
+ * value is -1 to indicate that this segment shouldn't be
+ * displayed. If the mark is the insertion character then
+ * 1 is returned and the chunkPtr structure is filled in.
+ *
+ * Side effects:
+ * None, except for filling in chunkPtr.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /*ARGSUSED*/
+static int
+MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
+ noCharsYet, wrapMode, chunkPtr)
+ CkText *textPtr; /* Text widget being layed out. */
+ CkTextIndex *indexPtr; /* Identifies first character in chunk. */
+ CkTextSegment *segPtr; /* Segment corresponding to indexPtr. */
+ int offset; /* Offset within segPtr corresponding to
+ * indexPtr (always 0). */
+ int maxX; /* Chunk must not occupy pixels at this
+ * position or higher. */
+ int maxChars; /* Chunk must not include more than this
+ * many characters. */
+ int noCharsYet; /* Non-zero means no characters have been
+ * assigned to this line yet. */
+ Ck_Uid wrapMode; /* Not used. */
+ register CkTextDispChunk *chunkPtr;
+ /* Structure to fill in with information
+ * about this chunk. The x field has already
+ * been set by the caller. */
+{
+ if (segPtr != textPtr->insertMarkPtr) {
+ return -1;
+ }
+
+ chunkPtr->displayProc = CkTextInsertDisplayProc;
+ chunkPtr->undisplayProc = InsertUndisplayProc;
+ chunkPtr->measureProc = (Ck_ChunkMeasureProc *) NULL;
+ chunkPtr->bboxProc = (Ck_ChunkBboxProc *) NULL;
+ chunkPtr->numChars = 0;
+ chunkPtr->minHeight = 0;
+ chunkPtr->width = 0;
+
+ /*
+ * Note: can't break a line after the insertion cursor: this
+ * prevents the insertion cursor from being stranded at the end
+ * of a line.
+ */
+
+ chunkPtr->breakIndex = -1;
+ chunkPtr->clientData = (ClientData) textPtr;
+ return 1;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextInsertDisplayProc --
+ *
+ * This procedure is called to display the insertion
+ * cursor.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Graphics are drawn.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+CkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, window, screenY)
+ CkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
+ int x; /* X-position in dst at which to
+ * draw this chunk (may differ from
+ * the x-position in the chunk because
+ * of scrolling). */
+ int y; /* Y-position at which to draw this
+ * chunk in dst (x-position is in
+ * the chunk itself). */
+ int height; /* Total height of line. */
+ int baseline; /* Offset of baseline from y. */
+ WINDOW *window; /* Curses window. */
+ int screenY; /* Y-coordinate in text window that
+ * corresponds to y. */
+{
+ CkText *textPtr = (CkText *) chunkPtr->clientData;
+
+ textPtr->insertY = screenY;
+ textPtr->insertX = x;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * InsertUndisplayProc --
+ *
+ * This procedure is called when the insertion cursor is no
+ * longer at a visible point on the display. It does nothing
+ * right now.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+InsertUndisplayProc(textPtr, chunkPtr)
+ CkText *textPtr; /* Overall information about text
+ * widget. */
+ CkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
+{
+ return;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * MarkCheckProc --
+ *
+ * This procedure is invoked by the B-tree code to perform
+ * consistency checks on mark segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The procedure panics if it detects anything wrong with
+ * the mark.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+MarkCheckProc(markPtr, linePtr)
+ CkTextSegment *markPtr; /* Segment to check. */
+ CkTextLine *linePtr; /* Line containing segment. */
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+
+ if (markPtr->body.mark.linePtr != linePtr) {
+ panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
+ }
+
+ /*
+ * Make sure that the mark is still present in the text's mark
+ * hash table.
+ */
+
+ for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
+ &search); hPtr != markPtr->body.mark.hPtr;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ if (hPtr == NULL) {
+ panic("MarkCheckProc couldn't find hash table entry for mark");
+ }
+ }
+}
--- /dev/null
+/*
+ * ckTextTag.c --
+ *
+ * This module implements the "tag" subcommand of the widget command
+ * for text widgets, plus most of the other high-level functions
+ * related to tags.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1995 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "ckText.h"
+#include "default.h"
+
+/*
+ * Information used for parsing tag configuration information:
+ */
+
+static Ck_ConfigSpec tagConfigSpecs[] = {
+ {CK_CONFIG_ATTR, "-attributes", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, attr), 0},
+ {CK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, bg), 0},
+ {CK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, fg), 0},
+ {CK_CONFIG_STRING, "-justify", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, justifyString), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-lmargin1", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, lMargin1String), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-lmargin2", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, lMargin2String), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-rmargin", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, rMarginString), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-tabs", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, tabString), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_UID, "-wrap", (char *) NULL, (char *) NULL,
+ (char *) NULL, Ck_Offset(CkTextTag, wrapMode),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ChangeTagPriority _ANSI_ARGS_((CkText *textPtr,
+ CkTextTag *tagPtr, int prio));
+static CkTextTag * FindTag _ANSI_ARGS_((Tcl_Interp *interp,
+ CkText *textPtr, char *tagName));
+static void SortTags _ANSI_ARGS_((int numTags,
+ CkTextTag **tagArrayPtr));
+static int TagSortProc _ANSI_ARGS_((CONST VOID *first,
+ CONST VOID *second));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextTagCmd --
+ *
+ * This procedure is invoked to process the "tag" options of
+ * the widget command for text widgets. See the user documentation
+ * for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkTextTagCmd(textPtr, interp, argc, argv)
+ register CkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "tag". */
+{
+ int c, i, addTag;
+ size_t length;
+ char *fullOption;
+ register CkTextTag *tagPtr;
+ CkTextIndex first, last, index1, index2;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'a') && (strncmp(argv[2], "add", length) == 0)) {
+ fullOption = "add";
+ addTag = 1;
+
+ addAndRemove:
+ if (argc < 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag ", fullOption,
+ " tagName index1 ?index2 index1 index2 ...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = CkTextCreateTag(textPtr, argv[3]);
+ for (i = 4; i < argc; i += 2) {
+ if (CkTextGetIndex(interp, textPtr, argv[i], &index1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc > (i+1)) {
+ if (CkTextGetIndex(interp, textPtr, argv[i+1], &index2)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (CkTextIndexCmp(&index1, &index2) >= 0) {
+ return TCL_OK;
+ }
+ } else {
+ index2 = index1;
+ CkTextIndexForwChars(&index2, 1, &index2);
+ }
+
+ if (tagPtr->affectsDisplay) {
+ CkTextRedrawTag(textPtr, &index1, &index2, tagPtr, !addTag);
+ } else {
+ /*
+ * Still need to trigger enter/leave events on tags that
+ * have changed.
+ */
+
+ CkTextEventuallyRepick(textPtr);
+ }
+ CkBTreeTag(&index1, &index2, tagPtr, addTag);
+
+ /*
+ * If the tag is "sel" then grab the selection if we're supposed
+ * to export it and don't already have it. Also, invalidate
+ * partially-completed selection retrievals.
+ */
+
+ if (tagPtr == textPtr->selTagPtr) {
+ textPtr->abortSelections = 1;
+ }
+ }
+ } else if ((c == 'b') && (strncmp(argv[2], "bind", length) == 0)) {
+ if ((argc < 4) || (argc > 6)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag bind tagName ?sequence? ?command?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = CkTextCreateTag(textPtr, argv[3]);
+
+ /*
+ * Make a binding table if the widget doesn't already have
+ * one.
+ */
+
+ if (textPtr->bindingTable == NULL) {
+ textPtr->bindingTable = Ck_CreateBindingTable(interp);
+ }
+
+ if (argc == 6) {
+ int append = 0;
+ unsigned long mask;
+
+ if (argv[5][0] == 0) {
+ return Ck_DeleteBinding(interp, textPtr->bindingTable,
+ (ClientData) tagPtr, argv[4]);
+ }
+ if (argv[5][0] == '+') {
+ argv[5]++;
+ append = 1;
+ }
+ mask = Ck_CreateBinding(interp, textPtr->bindingTable,
+ (ClientData) tagPtr, argv[4], argv[5], append);
+ if (mask != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if (argc == 5) {
+ char *command;
+
+ command = Ck_GetBinding(interp, textPtr->bindingTable,
+ (ClientData) tagPtr, argv[4]);
+ if (command == NULL) {
+ return TCL_ERROR;
+ }
+ interp->result = command;
+ } else {
+ Ck_GetAllBindings(interp, textPtr->bindingTable,
+ (ClientData) tagPtr);
+ }
+ } else if ((c == 'c') && (strncmp(argv[2], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag cget tagName option\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = FindTag(interp, textPtr, argv[3]);
+ if (tagPtr == NULL) {
+ return TCL_ERROR;
+ }
+ return Ck_ConfigureValue(interp, textPtr->winPtr, tagConfigSpecs,
+ (char *) tagPtr, argv[4], 0);
+ } else if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag configure tagName ?option? ?value? ",
+ "?option value ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = CkTextCreateTag(textPtr, argv[3]);
+ if (argc == 4) {
+ return Ck_ConfigureInfo(interp, textPtr->winPtr, tagConfigSpecs,
+ (char *) tagPtr, (char *) NULL, 0);
+ } else if (argc == 5) {
+ return Ck_ConfigureInfo(interp, textPtr->winPtr, tagConfigSpecs,
+ (char *) tagPtr, argv[4], 0);
+ } else {
+ int result;
+
+ result = Ck_ConfigureWidget(interp, textPtr->winPtr,
+ tagConfigSpecs, argc-4, argv+4, (char *) tagPtr, 0);
+ /*
+ * Some of the configuration options, like -underline
+ * and -justify, require additional translation (this is
+ * needed because we need to distinguish a particular value
+ * of an option from "unspecified").
+ */
+
+ if (tagPtr->justifyString != NULL) {
+ if (Ck_GetJustify(interp, tagPtr->justifyString,
+ &tagPtr->justify) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (tagPtr->lMargin1String != NULL) {
+ if (Ck_GetCoord(interp, textPtr->winPtr,
+ tagPtr->lMargin1String, &tagPtr->lMargin1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (tagPtr->lMargin2String != NULL) {
+ if (Ck_GetCoord(interp, textPtr->winPtr,
+ tagPtr->lMargin2String, &tagPtr->lMargin2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (tagPtr->rMarginString != NULL) {
+ if (Ck_GetCoord(interp, textPtr->winPtr,
+ tagPtr->rMarginString, &tagPtr->rMargin) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (tagPtr->tabArrayPtr != NULL) {
+ ckfree((char *) tagPtr->tabArrayPtr);
+ tagPtr->tabArrayPtr = NULL;
+ }
+ if (tagPtr->tabString != NULL) {
+ tagPtr->tabArrayPtr = CkTextGetTabs(interp, textPtr->winPtr,
+ tagPtr->tabString);
+ if (tagPtr->tabArrayPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if ((tagPtr->wrapMode != NULL)
+ && (tagPtr->wrapMode != ckTextCharUid)
+ && (tagPtr->wrapMode != ckTextNoneUid)
+ && (tagPtr->wrapMode != ckTextWordUid)) {
+ Tcl_AppendResult(interp, "bad wrap mode \"", tagPtr->wrapMode,
+ "\": must be char, none, or word", (char *) NULL);
+ tagPtr->wrapMode = NULL;
+ return TCL_ERROR;
+ }
+
+ /*
+ * If the "sel" tag was changed, be sure to mirror information
+ * from the tag back into the text widget record. NOTE: we
+ * don't have to free up information in the widget record
+ * before overwriting it, because it was mirrored in the tag
+ * and hence freed when the tag field was overwritten.
+ */
+
+ if (tagPtr == textPtr->selTagPtr) {
+ textPtr->selBg = tagPtr->bg;
+ textPtr->selFg = tagPtr->fg;
+ textPtr->selAttr = tagPtr->attr;
+ }
+ tagPtr->affectsDisplay = 1;
+ CkTextRedrawTag(textPtr, (CkTextIndex *) NULL,
+ (CkTextIndex *) NULL, tagPtr, 1);
+ return result;
+ }
+ } else if ((c == 'd') && (strncmp(argv[2], "delete", length) == 0)) {
+ Tcl_HashEntry *hPtr;
+
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag delete tagName tagName ...\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ for (i = 3; i < argc; i++) {
+ hPtr = Tcl_FindHashEntry(&textPtr->tagTable, argv[i]);
+ if (hPtr == NULL) {
+ continue;
+ }
+ tagPtr = (CkTextTag *) Tcl_GetHashValue(hPtr);
+ if (tagPtr == textPtr->selTagPtr) {
+ continue;
+ }
+ if (tagPtr->affectsDisplay) {
+ CkTextRedrawTag(textPtr, (CkTextIndex *) NULL,
+ (CkTextIndex *) NULL, tagPtr, 1);
+ }
+#if CK_USE_UTF
+ CkBTreeTag(CkTextMakeByteIndex(textPtr->tree, 0, 0, &first),
+ CkTextMakeByteIndex(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree), 0, &last),
+ tagPtr, 0);
+#else
+ CkBTreeTag(CkTextMakeIndex(textPtr->tree, 0, 0, &first),
+ CkTextMakeIndex(textPtr->tree,
+ CkBTreeNumLines(textPtr->tree), 0, &last),
+ tagPtr, 0);
+#endif
+ Tcl_DeleteHashEntry(hPtr);
+ if (textPtr->bindingTable != NULL) {
+ Ck_DeleteAllBindings(textPtr->bindingTable,
+ (ClientData) tagPtr);
+ }
+
+ /*
+ * Update the tag priorities to reflect the deletion of this tag.
+ */
+
+ ChangeTagPriority(textPtr, tagPtr, textPtr->numTags-1);
+ textPtr->numTags -= 1;
+ CkTextFreeTag(textPtr, tagPtr);
+ }
+ } else if ((c == 'l') && (strncmp(argv[2], "lower", length) == 0)) {
+ CkTextTag *tagPtr2;
+ int prio;
+
+ if ((argc != 4) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag lower tagName ?belowThis?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = FindTag(interp, textPtr, argv[3]);
+ if (tagPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (argc == 5) {
+ tagPtr2 = FindTag(interp, textPtr, argv[4]);
+ if (tagPtr2 == NULL) {
+ return TCL_ERROR;
+ }
+ if (tagPtr->priority < tagPtr2->priority) {
+ prio = tagPtr2->priority - 1;
+ } else {
+ prio = tagPtr2->priority;
+ }
+ } else {
+ prio = 0;
+ }
+ ChangeTagPriority(textPtr, tagPtr, prio);
+ CkTextRedrawTag(textPtr, (CkTextIndex *) NULL, (CkTextIndex *) NULL,
+ tagPtr, 1);
+ } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)
+ && (length >= 2)) {
+ CkTextTag **arrayPtr;
+ int arraySize;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag names ?index?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+
+ arrayPtr = (CkTextTag **) ckalloc((unsigned)
+ (textPtr->numTags * sizeof(CkTextTag *)));
+ for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
+ arrayPtr[i] = (CkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ arraySize = textPtr->numTags;
+ } else {
+ if (CkTextGetIndex(interp, textPtr, argv[3], &index1)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ arrayPtr = CkBTreeGetTags(&index1, &arraySize);
+ if (arrayPtr == NULL) {
+ return TCL_OK;
+ }
+ }
+ SortTags(arraySize, arrayPtr);
+ for (i = 0; i < arraySize; i++) {
+ tagPtr = arrayPtr[i];
+ Tcl_AppendElement(interp, tagPtr->name);
+ }
+ ckfree((char *) arrayPtr);
+ } else if ((c == 'n') && (strncmp(argv[2], "nextrange", length) == 0)
+ && (length >= 2)) {
+ CkTextSearch tSearch;
+ char position[TK_POS_CHARS];
+
+ if ((argc != 5) && (argc != 6)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag nextrange tagName index1 ?index2?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
+ if (tagPtr == NULL) {
+ return TCL_OK;
+ }
+ if (CkTextGetIndex(interp, textPtr, argv[4], &index1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, &last);
+#else
+ CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, &last);
+#endif
+ if (argc == 5) {
+ index2 = last;
+ } else if (CkTextGetIndex(interp, textPtr, argv[5], &index2)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * The search below is a bit tricky. Rather than use the B-tree
+ * facilities to stop the search at index2, let it search up
+ * until the end of the file but check for a position past index2
+ * ourselves. The reason for doing it this way is that we only
+ * care whether the *start* of the range is before index2; once
+ * we find the start, we don't want CkBTreeNextTag to abort the
+ * search because the end of the range is after index2.
+ */
+
+ CkBTreeStartSearch(&index1, &last, tagPtr, &tSearch);
+ if (CkBTreeCharTagged(&index1, tagPtr)) {
+ CkTextSegment *segPtr;
+ int offset;
+
+ /*
+ * The first character is tagged. See if there is an
+ * on-toggle just before the character. If not, then
+ * skip to the end of this tagged range.
+ */
+
+ for (segPtr = index1.linePtr->segPtr, offset = index1.charIndex;
+ offset >= 0;
+ offset -= segPtr->size, segPtr = segPtr->nextPtr) {
+ if ((offset == 0) && (segPtr->typePtr == &ckTextToggleOnType)
+ && (segPtr->body.toggle.tagPtr == tagPtr)) {
+ goto gotStart;
+ }
+ }
+ if (!CkBTreeNextTag(&tSearch)) {
+ return TCL_OK;
+ }
+ }
+
+ /*
+ * Find the start of the tagged range.
+ */
+
+ if (!CkBTreeNextTag(&tSearch)) {
+ return TCL_OK;
+ }
+ gotStart:
+ if (CkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
+ return TCL_OK;
+ }
+ CkTextPrintIndex(&tSearch.curIndex, position);
+ Tcl_AppendElement(interp, position);
+ CkBTreeNextTag(&tSearch);
+ CkTextPrintIndex(&tSearch.curIndex, position);
+ Tcl_AppendElement(interp, position);
+ } else if ((c == 'r') && (strncmp(argv[2], "raise", length) == 0)
+ && (length >= 3)) {
+ CkTextTag *tagPtr2;
+ int prio;
+
+ if ((argc != 4) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag raise tagName ?aboveThis?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = FindTag(interp, textPtr, argv[3]);
+ if (tagPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (argc == 5) {
+ tagPtr2 = FindTag(interp, textPtr, argv[4]);
+ if (tagPtr2 == NULL) {
+ return TCL_ERROR;
+ }
+ if (tagPtr->priority <= tagPtr2->priority) {
+ prio = tagPtr2->priority;
+ } else {
+ prio = tagPtr2->priority + 1;
+ }
+ } else {
+ prio = textPtr->numTags-1;
+ }
+ ChangeTagPriority(textPtr, tagPtr, prio);
+ CkTextRedrawTag(textPtr, (CkTextIndex *) NULL, (CkTextIndex *) NULL,
+ tagPtr, 1);
+ } else if ((c == 'r') && (strncmp(argv[2], "ranges", length) == 0)
+ && (length >= 3)) {
+ CkTextSearch tSearch;
+ char position[TK_POS_CHARS];
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " tag ranges tagName\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
+ if (tagPtr == NULL) {
+ return TCL_OK;
+ }
+#if CK_USE_UTF
+ CkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
+ CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, &last);
+#else
+ CkTextMakeIndex(textPtr->tree, 0, 0, &first);
+ CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
+ 0, &last);
+#endif
+ CkBTreeStartSearch(&first, &last, tagPtr, &tSearch);
+ if (CkBTreeCharTagged(&first, tagPtr)) {
+ CkTextPrintIndex(&first, position);
+ Tcl_AppendElement(interp, position);
+ }
+ while (CkBTreeNextTag(&tSearch)) {
+ CkTextPrintIndex(&tSearch.curIndex, position);
+ Tcl_AppendElement(interp, position);
+ }
+ } else if ((c == 'r') && (strncmp(argv[2], "remove", length) == 0)
+ && (length >= 2)) {
+ fullOption = "remove";
+ addTag = 0;
+ goto addAndRemove;
+ } else {
+ Tcl_AppendResult(interp, "bad tag option \"", argv[2],
+ "\": must be add, bind, cget, configure, delete, lower, ",
+ "names, nextrange, raise, ranges, or remove",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextCreateTag --
+ *
+ * Find the record describing a tag within a given text widget,
+ * creating a new record if one doesn't already exist.
+ *
+ * Results:
+ * The return value is a pointer to the CkTextTag record for tagName.
+ *
+ * Side effects:
+ * A new tag record is created if there isn't one already defined
+ * for tagName.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkTextTag *
+CkTextCreateTag(textPtr, tagName)
+ CkText *textPtr; /* Widget in which tag is being used. */
+ char *tagName; /* Name of desired tag. */
+{
+ register CkTextTag *tagPtr;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ hPtr = Tcl_CreateHashEntry(&textPtr->tagTable, tagName, &new);
+ if (!new) {
+ return (CkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+
+ /*
+ * No existing entry. Create a new one, initialize it, and add a
+ * pointer to it to the hash table entry.
+ */
+
+ tagPtr = (CkTextTag *) ckalloc(sizeof(CkTextTag));
+ tagPtr->name = Tcl_GetHashKey(&textPtr->tagTable, hPtr);
+ tagPtr->priority = textPtr->numTags;
+ tagPtr->bg = -1;
+ tagPtr->fg = -1;
+ tagPtr->attr = -1;
+ tagPtr->justifyString = NULL;
+ tagPtr->justify = CK_JUSTIFY_LEFT;
+ tagPtr->lMargin1String = NULL;
+ tagPtr->lMargin1 = 0;
+ tagPtr->lMargin2String = NULL;
+ tagPtr->lMargin2 = 0;
+ tagPtr->rMarginString = NULL;
+ tagPtr->rMargin = 0;
+ tagPtr->tabString = NULL;
+ tagPtr->tabArrayPtr = NULL;
+ tagPtr->wrapMode = NULL;
+ tagPtr->affectsDisplay = 0;
+ textPtr->numTags++;
+ Tcl_SetHashValue(hPtr, tagPtr);
+ return tagPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindTag --
+ *
+ * See if tag is defined for a given widget.
+ *
+ * Results:
+ * If tagName is defined in textPtr, a pointer to its CkTextTag
+ * structure is returned. Otherwise NULL is returned and an
+ * error message is recorded in interp->result unless interp
+ * is NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static CkTextTag *
+FindTag(interp, textPtr, tagName)
+ Tcl_Interp *interp; /* Interpreter to use for error message;
+ * if NULL, then don't record an error
+ * message. */
+ CkText *textPtr; /* Widget in which tag is being used. */
+ char *tagName; /* Name of desired tag. */
+{
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&textPtr->tagTable, tagName);
+ if (hPtr != NULL) {
+ return (CkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "tag \"", tagName,
+ "\" isn't defined in text widget", (char *) NULL);
+ }
+ return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkTextFreeTag --
+ *
+ * This procedure is called when a tag is deleted to free up the
+ * memory and other resources associated with the tag.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and other resources are freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CkTextFreeTag(textPtr, tagPtr)
+ CkText *textPtr; /* Info about overall widget. */
+ register CkTextTag *tagPtr; /* Tag being deleted. */
+{
+ if (tagPtr->justifyString != NULL) {
+ ckfree(tagPtr->justifyString);
+ }
+ if (tagPtr->lMargin1String != NULL) {
+ ckfree(tagPtr->lMargin1String);
+ }
+ if (tagPtr->lMargin2String != NULL) {
+ ckfree(tagPtr->lMargin2String);
+ }
+ if (tagPtr->rMarginString != NULL) {
+ ckfree(tagPtr->rMarginString);
+ }
+ if (tagPtr->tabString != NULL) {
+ ckfree(tagPtr->tabString);
+ }
+ if (tagPtr->tabArrayPtr != NULL) {
+ ckfree((char *) tagPtr->tabArrayPtr);
+ }
+ ckfree((char *) tagPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortTags --
+ *
+ * This procedure sorts an array of tag pointers in increasing
+ * order of priority, optimizing for the common case where the
+ * array is small.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SortTags(numTags, tagArrayPtr)
+ int numTags; /* Number of tag pointers at *tagArrayPtr. */
+ CkTextTag **tagArrayPtr; /* Pointer to array of pointers. */
+{
+ int i, j, prio;
+ register CkTextTag **tagPtrPtr;
+ CkTextTag **maxPtrPtr, *tmp;
+
+ if (numTags < 2) {
+ return;
+ }
+ if (numTags < 20) {
+ for (i = numTags-1; i > 0; i--, tagArrayPtr++) {
+ maxPtrPtr = tagPtrPtr = tagArrayPtr;
+ prio = tagPtrPtr[0]->priority;
+ for (j = i, tagPtrPtr++; j > 0; j--, tagPtrPtr++) {
+ if (tagPtrPtr[0]->priority < prio) {
+ prio = tagPtrPtr[0]->priority;
+ maxPtrPtr = tagPtrPtr;
+ }
+ }
+ tmp = *maxPtrPtr;
+ *maxPtrPtr = *tagArrayPtr;
+ *tagArrayPtr = tmp;
+ }
+ } else {
+ qsort((VOID *) tagArrayPtr, (unsigned) numTags, sizeof (CkTextTag *),
+ TagSortProc);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagSortProc --
+ *
+ * This procedure is called by qsort when sorting an array of
+ * tags in priority order.
+ *
+ * Results:
+ * The return value is -1 if the first argument should be before
+ * the second element (i.e. it has lower priority), 0 if it's
+ * equivalent (this should never happen!), and 1 if it should be
+ * after the second element.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TagSortProc(first, second)
+ CONST VOID *first, *second; /* Elements to be compared. */
+{
+ CkTextTag *tagPtr1, *tagPtr2;
+
+ tagPtr1 = * (CkTextTag **) first;
+ tagPtr2 = * (CkTextTag **) second;
+ return tagPtr1->priority - tagPtr2->priority;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangeTagPriority --
+ *
+ * This procedure changes the priority of a tag by modifying
+ * its priority and the priorities of other tags that are affected
+ * by the change.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Priorities may be changed for some or all of the tags in
+ * textPtr. The tags will be arranged so that there is exactly
+ * one tag at each priority level between 0 and textPtr->numTags-1,
+ * with tagPtr at priority "prio".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeTagPriority(textPtr, tagPtr, prio)
+ CkText *textPtr; /* Information about text widget. */
+ CkTextTag *tagPtr; /* Tag whose priority is to be
+ * changed. */
+ int prio; /* New priority for tag. */
+{
+ int low, high, delta;
+ register CkTextTag *tagPtr2;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+
+ if (prio < 0) {
+ prio = 0;
+ }
+ if (prio >= textPtr->numTags) {
+ prio = textPtr->numTags-1;
+ }
+ if (prio == tagPtr->priority) {
+ return;
+ } else if (prio < tagPtr->priority) {
+ low = prio;
+ high = tagPtr->priority-1;
+ delta = 1;
+ } else {
+ low = tagPtr->priority+1;
+ high = prio;
+ delta = -1;
+ }
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ tagPtr2 = (CkTextTag *) Tcl_GetHashValue(hPtr);
+ if ((tagPtr2->priority >= low) && (tagPtr2->priority <= high)) {
+ tagPtr2->priority += delta;
+ }
+ }
+ tagPtr->priority = prio;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextBindProc --
+ *
+ * This procedure is invoked by the Ck dispatcher to handle
+ * events associated with bindings on items.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command invoked as part of the binding
+ * (if there was any).
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkTextBindProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to canvas structure. */
+ CkEvent *eventPtr; /* Pointer to X event that just
+ * happened. */
+{
+ CkText *textPtr = (CkText *) clientData;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkTextPickCurrent --
+ *
+ * Find the character containing the coordinates in an event
+ * and place the "current" mark on that character. If the
+ * "current" mark has moved then generate a fake leave event
+ * on the old current character and a fake enter event on the new
+ * current character.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The current mark for textPtr may change. If it does,
+ * then the commands associated with character entry and leave
+ * could do just about anything.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkTextPickCurrent(textPtr, eventPtr)
+ register CkText *textPtr; /* Text widget in which to select
+ * current character. */
+ CkEvent *eventPtr; /* Event describing location of
+ * mouse cursor. Must be EnterWindow,
+ * LeaveWindow, ButtonRelease, or
+ * MotionNotify. */
+{
+}
--- /dev/null
+/*
+ * ckTree.c --
+ *
+ * This module implements a tree widget.
+ *
+ * Copyright (c) 1996 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+#include "default.h"
+
+/*
+ * Widget defaults:
+ */
+
+#define DEF_TREE_ACTIVE_ATTR_COLOR "normal"
+#define DEF_TREE_ACTIVE_ATTR_MONO "reverse"
+#define DEF_TREE_ACTIVE_BG_COLOR "white"
+#define DEF_TREE_ACTIVE_BG_MONO "black"
+#define DEF_TREE_ACTIVE_FG_COLOR "black"
+#define DEF_TREE_ACTIVE_FG_MONO "white"
+#define DEF_TREE_ATTR_COLOR "normal"
+#define DEF_TREE_ATTR_MONO "normal"
+#define DEF_TREE_BG_COLOR "black"
+#define DEF_TREE_BG_MONO "black"
+#define DEF_TREE_FG_COLOR "white"
+#define DEF_TREE_FG_MONO "white"
+#define DEF_TREE_HEIGHT "10"
+#define DEF_TREE_SELECT_ATTR_COLOR "bold"
+#define DEF_TREE_SELECT_ATTR_MONO "bold"
+#define DEF_TREE_SELECT_BG_COLOR "black"
+#define DEF_TREE_SELECT_BG_MONO "black"
+#define DEF_TREE_SELECT_FG_COLOR "white"
+#define DEF_TREE_SELECT_FG_MONO "white"
+#define DEF_TREE_TAKE_FOCUS "1"
+#define DEF_TREE_WIDTH "40"
+#define DEF_TREE_SCROLL_COMMAND NULL
+
+/*
+ * A node in the tree is represented by this data structure.
+ */
+
+#define TAG_SPACE 5
+
+typedef struct Node {
+ int id; /* Unique id of the node. */
+ int level; /* Level in tree, 0 means root. */
+ struct Tree *tree; /* Pointer to widget. */
+ struct Node *parent; /* Pointer to parent node or NULL. */
+ struct Node *next; /* Pointer to next node in this level. */
+ struct Node *firstChild, *lastChild;
+ Ck_Uid staticTagSpace[TAG_SPACE];
+ Ck_Uid *tagPtr; /* Pointer to tag array. */
+ int tagSpace; /* Total size of tag array. */
+ int numTags; /* Number of tags in tag array. */
+ int fg; /* Foreground color of node's text. */
+ int bg; /* Background color of node's text. */
+ int attr; /* Video attributes of node's text. */
+ char *text; /* Text to display for this node. */
+ int textWidth; /* Width of node's text. */
+ int flags; /* Flag bits (see below). */
+} Node;
+
+/*
+ * Flag bits for node:
+ *
+ * SELECTED: Non-zero means node is selected
+ * SHOWCHILDREN: Non-zero means if node has children
+ * they shall be displayed.
+ */
+
+#define SELECTED 1
+#define SHOWCHILDREN 2
+
+/*
+ * Custom option for handling "-tags" options for tree nodes:
+ */
+
+static int TreeTagsParseProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, CkWindow *winPtr, char *value,
+ char *widgRec, int offset));
+static char * TreeTagsPrintProc _ANSI_ARGS_((ClientData clientData,
+ CkWindow *winPtr, char *widgRec, int offset,
+ Tcl_FreeProc **freeProcPtr));
+
+Ck_CustomOption treeTagsOption = {
+ TreeTagsParseProc,
+ TreeTagsPrintProc,
+ (ClientData) NULL
+};
+
+/*
+ * Information used for parsing configuration specs for nodes:
+ */
+
+static Ck_ConfigSpec nodeConfigSpecs[] = {
+ {CK_CONFIG_ATTR, "-attributes", (char *) NULL, (char *) NULL,
+ "", Ck_Offset(Node, attr), CK_CONFIG_DONT_SET_DEFAULT },
+ {CK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
+ "", Ck_Offset(Node, bg), CK_CONFIG_DONT_SET_DEFAULT },
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
+ "", Ck_Offset(Node, fg), CK_CONFIG_DONT_SET_DEFAULT},
+ {CK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, CK_CONFIG_NULL_OK, &treeTagsOption},
+ {CK_CONFIG_STRING, "-text", (char *) NULL, (char *) NULL,
+ NULL, Ck_Offset(Node, text), CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * A data structure of the following type is kept for each
+ * widget managed by this file:
+ */
+
+typedef struct Tree {
+ CkWindow *winPtr; /* Window that embodies the widget. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up.*/
+ Tcl_Interp *interp; /* Interpreter associated with menubutton. */
+ Tcl_Command widgetCmd; /* Token for menubutton's widget command. */
+
+ int idCount; /* For unique ids for nodes. */
+
+ /*
+ * Information about what's displayed in the menu button:
+ */
+
+ Node *firstChild, *lastChild;
+ Tcl_HashTable nodeTable;
+
+ /*
+ * Information used when displaying widget:
+ */
+
+ int normalFg; /* Foreground color in normal mode. */
+ int normalBg; /* Background color in normal mode. */
+ int normalAttr; /* Attributes in normal mode. */
+ int activeFg; /* Foreground color in active mode. */
+ int activeBg; /* Ditto, background color. */
+ int activeAttr; /* Attributes in active mode. */
+ int selectFg; /* Foreground color for selected nodes. */
+ int selectBg; /* Ditto, background color. */
+ int selectAttr; /* Attributes for selected nodes. */
+
+ int width, height; /* If > 0, these specify dimensions to request
+ * for window, in characters for text and in
+ * pixels for bitmaps. In this case the actual
+ * size of the text string or bitmap is
+ * ignored in computing desired window size. */
+
+ int visibleNodes; /* Total number of visible nodes. */
+ int topIndex; /* Index of starting line. */
+ Node *topNode; /* Node at top line of window. */
+ Node *activeNode; /* Node which has active tag or NULL. */
+
+ int leadingSpace; /* For displaying: size of leadingString. */
+ int *leadingString; /* Malloc'ed leading vertical lines for
+ * displaying. */
+
+ /*
+ * Miscellaneous information:
+ */
+
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ char *yScrollCmd; /* Command prefix for communicating with
+ * vertical scrollbar. NULL means no command
+ * to issue. Malloc'ed. */
+ char *xScrollCmd; /* Command prefix for communicating with
+ * horizontal scrollbar. NULL means no command
+ * to issue. Malloc'ed. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} Tree;
+
+/*
+ * Flag bits for entire tree:
+ *
+ * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
+ * has already been queued to redraw
+ * this window.
+ * GOT_FOCUS: Non-zero means this button currently
+ * has the input focus.
+ * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
+ * to be updated.
+ * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
+ * to be updated.
+ */
+
+#define REDRAW_PENDING 1
+#define GOT_FOCUS 2
+#define UPDATE_V_SCROLLBAR 4
+#define UPDATE_H_SCROLLBAR 4
+
+/*
+ * Information used for parsing configuration specs:
+ */
+
+static Ck_ConfigSpec configSpecs[] = {
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_TREE_ACTIVE_ATTR_COLOR,
+ Ck_Offset(Tree, activeAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
+ "ActiveAttributes", DEF_TREE_ACTIVE_ATTR_MONO,
+ Ck_Offset(Tree, activeAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_TREE_ATTR_COLOR, Ck_Offset(Tree, normalAttr),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
+ DEF_TREE_ATTR_MONO, Ck_Offset(Tree, normalAttr),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_TREE_ACTIVE_BG_COLOR, Ck_Offset(Tree, activeBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
+ DEF_TREE_ACTIVE_BG_MONO, Ck_Offset(Tree, activeBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_TREE_ACTIVE_FG_COLOR, Ck_Offset(Tree, activeFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
+ DEF_TREE_ACTIVE_FG_MONO, Ck_Offset(Tree, activeFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_TREE_BG_COLOR, Ck_Offset(Tree, normalBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_TREE_BG_MONO, Ck_Offset(Tree, normalBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, 0},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TREE_FG_COLOR, Ck_Offset(Tree, normalFg), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_TREE_FG_MONO, Ck_Offset(Tree, normalFg), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COORD, "-height", "height", "Height",
+ DEF_TREE_HEIGHT, Ck_Offset(Tree, height), 0},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_TREE_SELECT_ATTR_COLOR,
+ Ck_Offset(Tree, selectAttr), CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
+ "SelectAttributes", DEF_TREE_SELECT_ATTR_MONO,
+ Ck_Offset(Tree, selectAttr), CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TREE_SELECT_BG_COLOR, Ck_Offset(Tree, selectBg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
+ DEF_TREE_SELECT_BG_MONO, Ck_Offset(Tree, selectBg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TREE_SELECT_FG_COLOR, Ck_Offset(Tree, selectFg),
+ CK_CONFIG_COLOR_ONLY},
+ {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_TREE_SELECT_FG_MONO, Ck_Offset(Tree, selectFg),
+ CK_CONFIG_MONO_ONLY},
+ {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_TREE_TAKE_FOCUS, Ck_Offset(Tree, takeFocus),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_COORD, "-width", "width", "Width",
+ DEF_TREE_WIDTH, Ck_Offset(Tree, width), 0},
+ {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ DEF_TREE_SCROLL_COMMAND, Ck_Offset(Tree, xScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ DEF_TREE_SCROLL_COMMAND, Ck_Offset(Tree, yScrollCmd),
+ CK_CONFIG_NULL_OK},
+ {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ * The structure defined below is used to keep track of a tag search
+ * in progress. Only the "prevPtr" field should be accessed by anyone
+ * other than StartTagSearch and NextNode.
+ */
+
+typedef struct TagSearch {
+ Tree *treePtr; /* Tree widget being searched. */
+ Tcl_HashSearch search; /* Hash search for nodeTable. */
+ Ck_Uid tag; /* Tag to search for. 0 means return
+ * all nodes. */
+ int searchOver; /* Non-zero means NextNode should always
+ * return NULL. */
+} TagSearch;
+
+static Ck_Uid allUid = NULL;
+static Ck_Uid hideChildrenUid = NULL;
+static Ck_Uid activeUid = NULL;
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static Node * StartTagSearch _ANSI_ARGS_((Tree *treePtr,
+ char *tag, TagSearch *searchPtr));
+static Node * NextNode _ANSI_ARGS_((TagSearch *searchPtr));
+static void DoNode _ANSI_ARGS_((Tcl_Interp *interp,
+ Node *nodePtr, Ck_Uid tag));
+static void TreeCmdDeletedProc _ANSI_ARGS_((
+ ClientData clientData));
+static void TreeEventProc _ANSI_ARGS_((ClientData clientData,
+ CkEvent *eventPtr));
+static int TreeWidgetCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int ConfigureTree _ANSI_ARGS_((Tcl_Interp *interp,
+ Tree *treePtr, int argc, char **argv,
+ int flags));
+static void DestroyTree _ANSI_ARGS_((ClientData clientData));
+static void DisplayTree _ANSI_ARGS_((ClientData clientData));
+static void TreeEventuallyRedraw _ANSI_ARGS_((Tree *treePtr));
+static int FindNodes _ANSI_ARGS_((Tcl_Interp *interp,
+ Tree *treePtr, int argc, char **argv,
+ char *newTag, char *cmdName, char *option));
+static void DeleteNode _ANSI_ARGS_((Tree *treePtr, Node *nodePtr));
+static void RecomputeVisibleNodes _ANSI_ARGS_((Tree *treePtr));
+static void ChangeTreeView _ANSI_ARGS_((Tree *treePtr, int index));
+static void TreeUpdateVScrollbar _ANSI_ARGS_((Tree *treePtr));
+static int GetNodeYCoord _ANSI_ARGS_((Tree *treePtr,
+ Node *thisPtr, int *yPtr));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_TreeCmd --
+ *
+ * This procedure is invoked to process the "tree"
+ * Tcl commands. See the user documentation for details
+ * on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_TreeCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tree *treePtr;
+ CkWindow *mainPtr = (CkWindow *) clientData;
+ CkWindow *new;
+
+ allUid = Ck_GetUid("all");
+ hideChildrenUid = Ck_GetUid("hidechildren");
+ activeUid = Ck_GetUid("active");
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " pathName ?options?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Create the new window.
+ */
+
+ new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
+ if (new == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Initialize the data structure for the button.
+ */
+
+ treePtr = (Tree *) ckalloc(sizeof (Tree));
+ treePtr->winPtr = new;
+ treePtr->interp = interp;
+ treePtr->widgetCmd = Tcl_CreateCommand(interp, treePtr->winPtr->pathName,
+ TreeWidgetCmd, (ClientData) treePtr, TreeCmdDeletedProc);
+ treePtr->idCount = 0;
+ treePtr->firstChild = treePtr->lastChild = NULL;
+ Tcl_InitHashTable(&treePtr->nodeTable, TCL_ONE_WORD_KEYS);
+ treePtr->normalBg = 0;
+ treePtr->normalFg = 0;
+ treePtr->normalAttr = 0;
+ treePtr->activeBg = 0;
+ treePtr->activeFg = 0;
+ treePtr->activeAttr = 0;
+ treePtr->selectBg = 0;
+ treePtr->selectFg = 0;
+ treePtr->selectAttr = 0;
+ treePtr->width = 0;
+ treePtr->height = 0;
+ treePtr->visibleNodes = 0;
+ treePtr->topIndex = 0;
+ treePtr->topNode = NULL;
+ treePtr->activeNode = NULL;
+ treePtr->leadingSpace = 0;
+ treePtr->leadingString = NULL;
+ treePtr->takeFocus = NULL;
+ treePtr->xScrollCmd = NULL;
+ treePtr->yScrollCmd = NULL;
+ treePtr->flags = 0;
+
+ Ck_SetClass(treePtr->winPtr, "Tree");
+ Ck_CreateEventHandler(treePtr->winPtr,
+ CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY |
+ CK_EV_FOCUSIN | CK_EV_FOCUSOUT, TreeEventProc, (ClientData) treePtr);
+ if (ConfigureTree(interp, treePtr, argc-2, argv+2, 0) != TCL_OK) {
+ Ck_DestroyWindow(treePtr->winPtr);
+ return TCL_ERROR;
+ }
+
+ interp->result = treePtr->winPtr->pathName;
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TreeWidgetCmd --
+ *
+ * This procedure is invoked to process the Tcl command
+ * that corresponds to a widget managed by this module.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+TreeWidgetCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about button widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tree *treePtr = (Tree *) clientData;
+ int result = TCL_OK, redraw = 0, recompute = 0;
+ size_t length;
+ int c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Ck_Preserve((ClientData) treePtr);
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " addtags tag searchCommand ?arg arg ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = FindNodes(interp, treePtr, argc-3, argv+3, argv[2], argv[0],
+ " addtag tag");
+ } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cget option\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = Ck_ConfigureValue(interp, treePtr->winPtr, configSpecs,
+ (char *) treePtr, argv[2], 0);
+ } else if ((c == 'c') && (strncmp(argv[1], "children", length) == 0)
+ && (length >= 2)) {
+ Node *nodePtr = NULL;
+ TagSearch search;
+
+ if (argc > 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " children ?tagOrId?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (argc > 2) {
+ nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ if (nodePtr == NULL)
+ goto error;
+ }
+ if (nodePtr == NULL)
+ nodePtr = treePtr->firstChild;
+ else
+ nodePtr = nodePtr->firstChild;
+ while (nodePtr != NULL) {
+ DoNode(interp, nodePtr, (Ck_Uid) NULL);
+ nodePtr = nodePtr->next;
+ }
+ result = TCL_OK;
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
+ && (length >= 2)) {
+ if (argc == 2) {
+ result = Ck_ConfigureInfo(interp, treePtr->winPtr, configSpecs,
+ (char *) treePtr, (char *) NULL, 0);
+ } else if (argc == 3) {
+ result = Ck_ConfigureInfo(interp, treePtr->winPtr, configSpecs,
+ (char *) treePtr, argv[2], 0);
+ } else {
+ result = ConfigureTree(interp, treePtr, argc-2, argv+2,
+ CK_CONFIG_ARGV_ONLY);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
+ && (length >= 2)) {
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ for (;;) {
+ Node *nodePtr;
+ TagSearch search;
+
+ nodePtr = StartTagSearch(treePtr, argv[i], &search);
+ if (nodePtr == NULL)
+ break;
+ DeleteNode(treePtr, nodePtr);
+ recompute++;
+ }
+ }
+ if (recompute)
+ redraw++;
+ } else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
+ && (length >= 2)) {
+ Ck_Uid tag;
+ int i;
+ Node *nodePtr;
+ TagSearch search;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " dtag tagOrId ?tagToDelete?\"",
+ (char *) NULL);
+ goto error;
+ }
+ if (argc == 4) {
+ tag = Ck_GetUid(argv[3]);
+ } else {
+ tag = Ck_GetUid(argv[2]);
+ }
+ for (nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ nodePtr != NULL; nodePtr = NextNode(&search)) {
+ for (i = nodePtr->numTags-1; i >= 0; i--) {
+ if (nodePtr->tagPtr[i] == tag) {
+ nodePtr->tagPtr[i] = nodePtr->tagPtr[nodePtr->numTags-1];
+ nodePtr->numTags--;
+ if (tag == activeUid)
+ redraw++;
+ else if (tag == hideChildrenUid) {
+ if (!(nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr->flags |= SHOWCHILDREN;
+ recompute++;
+ redraw++;
+ }
+ }
+ }
+ }
+ }
+ } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
+ && (length >= 2)) {
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " find searchCommand ?arg arg ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ result = FindNodes(interp, treePtr, argc - 2, argv + 2, (char *) NULL,
+ argv[0], " find");
+ } else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
+ Node *nodePtr;
+ TagSearch search;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " gettags tagOrId\"", (char *) NULL);
+ goto error;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ if (nodePtr != NULL) {
+ int i;
+
+ for (i = 0; i < nodePtr->numTags; i++) {
+ Tcl_AppendElement(interp, (char *) nodePtr->tagPtr[i]);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
+ int optargc = 2, id;
+ Node *nodePtr = NULL, *new;
+ char *end;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " insert ?id? ?option value ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ id = strtoul(argv[2], &end, 0);
+ if (*end == 0) {
+ if (end != argv[2]) {
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) id);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "no node with id \"", argv[2],
+ "\"", (char *) NULL);
+ goto error;
+ }
+ nodePtr = (Node *) Tcl_GetHashValue(hPtr);
+ }
+ optargc = 3;
+ }
+
+ new = (Node *) ckalloc (sizeof (Node));
+ new->id = treePtr->idCount++;
+ new->level = nodePtr == NULL ? 0 : nodePtr->level + 1;
+ new->tree = treePtr;
+ new->parent = nodePtr;
+ new->next = NULL;
+ new->firstChild = new->lastChild = NULL;
+ new->tagPtr = new->staticTagSpace;
+ new->tagSpace = TAG_SPACE;
+ new->numTags = 0;
+ new->fg = new->bg = new->attr = -1;
+ new->text = NULL;
+ new->textWidth = 0;
+ new->flags = SHOWCHILDREN;
+
+ if (new->level * 2 > treePtr->leadingSpace) {
+ int *newString;
+
+ treePtr->leadingSpace = new->level * 8;
+ newString = (int *) ckalloc(treePtr->leadingSpace * sizeof (int));
+ if (treePtr->leadingString != NULL)
+ ckfree((char *) treePtr->leadingString);
+ treePtr->leadingString = newString;
+ }
+
+ result = Ck_ConfigureWidget(interp, treePtr->winPtr,
+ nodeConfigSpecs, argc - optargc, &argv[optargc],
+ (char *) new, CK_CONFIG_ARGV_ONLY);
+
+ if (result == TCL_OK) {
+ Tcl_HashEntry *hPtr;
+ int newHash;
+ char buf[32];
+
+ hPtr = Tcl_CreateHashEntry(&treePtr->nodeTable,
+ (char *) new->id, &newHash);
+ Tcl_SetHashValue(hPtr, (ClientData) new);
+ if (new->parent == NULL) {
+ new->level = 0;
+ if (treePtr->lastChild == NULL)
+ treePtr->firstChild = new;
+ else
+ treePtr->lastChild->next = new;
+ treePtr->lastChild = new;
+ } else {
+ new->level = nodePtr->level + 1;
+ if (nodePtr->lastChild == NULL)
+ nodePtr->firstChild = new;
+ else
+ nodePtr->lastChild->next = new;
+ nodePtr->lastChild = new;
+ }
+ recompute++;
+ redraw++;
+ sprintf(buf, "%d", new->id);
+ Tcl_AppendResult(interp, buf, (char *) NULL);
+ } else {
+ ckfree((char *) new);
+ }
+ } else if ((c == 'n') && (strncmp(argv[1], "nodecget", length) == 0)
+ && (length >= 6)) {
+ Node *nodePtr;
+ TagSearch search;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " nodecget tagOrId option\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ if (nodePtr != NULL) {
+ result = Ck_ConfigureValue(treePtr->interp, treePtr->winPtr,
+ nodeConfigSpecs, (char *) nodePtr,
+ argv[3], 0);
+ }
+ } else if ((c == 'n') && (strncmp(argv[1], "nodeconfigure", length) == 0)
+ && (length >= 6)) {
+ Node *nodePtr;
+ TagSearch search;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " nodeconfigure tagOrId ?option value ...?\"",
+ (char *) NULL);
+ goto error;
+ }
+ for (nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ nodePtr != NULL; nodePtr = NextNode(&search)) {
+ if (argc == 3) {
+ result = Ck_ConfigureInfo(treePtr->interp, treePtr->winPtr,
+ nodeConfigSpecs, (char *) nodePtr,
+ (char *) NULL, 0);
+ } else if (argc == 4) {
+ result = Ck_ConfigureInfo(treePtr->interp, treePtr->winPtr,
+ nodeConfigSpecs, (char *) nodePtr,
+ argv[3], 0);
+ } else {
+ result = Ck_ConfigureWidget(interp, treePtr->winPtr,
+ nodeConfigSpecs, argc - 3, &argv[3],
+ (char *) nodePtr, CK_CONFIG_ARGV_ONLY);
+ redraw++;
+ }
+ if ((result != TCL_OK) || (argc < 5)) {
+ break;
+ }
+ }
+ } else if ((c == 'p') && (strncmp(argv[1], "parent", length) == 0)
+ && (length >= 2)) {
+ Node *nodePtr;
+ TagSearch search;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " parent tagOrId\"",
+ (char *) NULL);
+ goto error;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ if (nodePtr == NULL)
+ goto error;
+ if (nodePtr->parent != NULL)
+ DoNode(interp, nodePtr->parent, (Ck_Uid) NULL);
+ result = TCL_OK;
+ } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
+ && (length >= 2)) {
+ Node *nodePtr;
+ TagSearch search;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " select option tagOrId\"", (char *) NULL);
+ goto error;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[3], &search);
+ if (nodePtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a selectable node \"",
+ argv[3], "\"", (char *) NULL);
+ goto error;
+ }
+ length = strlen(argv[2]);
+ c = argv[2][0];
+ if ((c == 'c') && (argv[2] != NULL) &&
+ (strncmp(argv[2], "clear", length) == 0)) {
+ do {
+ nodePtr->flags &= ~SELECTED;
+ nodePtr = NextNode(&search);
+ redraw++;
+ } while (nodePtr != NULL);
+ } else if ((c == 'i') && (strncmp(argv[2], "includes", length) == 0)) {
+ interp->result = (nodePtr->flags & SELECTED) ? "1" : "0";
+ } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
+ do {
+ nodePtr->flags |= SELECTED;
+ nodePtr = NextNode(&search);
+ redraw++;
+ } while (nodePtr != NULL);
+ } else {
+ Tcl_AppendResult(interp, "bad select option \"", argv[2],
+ "\": must be clear, includes, or set", (char *) NULL);
+ goto error;
+ }
+ } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
+ int type, count;
+ double fraction;
+
+ if (argc == 2) {
+ } else {
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ goto error;
+ case CK_SCROLL_MOVETO:
+ break;
+ case CK_SCROLL_PAGES:
+ break;
+ case CK_SCROLL_UNITS:
+ break;
+ }
+ }
+ } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
+ int type, count, index;
+ double fraction;
+
+ if (argc == 2) {
+ if (treePtr->visibleNodes == 0) {
+ interp->result = "0 1";
+ } else {
+ double fraction2;
+
+ fraction = treePtr->topIndex / (double) treePtr->visibleNodes;
+ fraction2 = (treePtr->topIndex + treePtr->winPtr->height) /
+ (double) treePtr->visibleNodes;
+ if (fraction2 > 1.0) {
+ fraction2 = 1.0;
+ }
+ sprintf(interp->result, "%g %g", fraction, fraction2);
+ }
+ } else if (argc == 3) {
+ Node *nodePtr;
+ TagSearch search;
+
+ nodePtr = StartTagSearch(treePtr, argv[2], &search);
+ if (nodePtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a selectable node \"",
+ argv[3], "\"", (char *) NULL);
+ goto error;
+ }
+ if (GetNodeYCoord(treePtr, nodePtr, &index) == TCL_OK) {
+ if (index < treePtr->topIndex ||
+ index >= treePtr->topIndex + treePtr->winPtr->height)
+ ChangeTreeView(treePtr,
+ index - treePtr->winPtr->height / 2);
+ }
+ } else {
+ type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
+ switch (type) {
+ case CK_SCROLL_ERROR:
+ goto error;
+ case CK_SCROLL_MOVETO:
+ index = (int) (treePtr->visibleNodes * fraction + 0.5);
+ break;
+ case CK_SCROLL_PAGES:
+ if (treePtr->visibleNodes > 2) {
+ index = treePtr->topIndex
+ + count * (treePtr->winPtr->height - 2);
+ } else {
+ index = treePtr->topIndex + count;
+ }
+ break;
+ case CK_SCROLL_UNITS:
+ index = treePtr->topIndex + count;
+ break;
+ }
+ ChangeTreeView(treePtr, index);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be cget or configure",
+ (char *) NULL);
+ goto error;
+ }
+ if (recompute)
+ RecomputeVisibleNodes(treePtr);
+ if (redraw)
+ TreeEventuallyRedraw(treePtr);
+
+ Ck_Release((ClientData) treePtr);
+ return result;
+
+error:
+ Ck_Release((ClientData) treePtr);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyTree --
+ *
+ * This procedure is invoked to recycle all of the resources
+ * associated with a tree widget. It is invoked as a
+ * when-idle handler in order to make sure that there is no
+ * other use of the tree pending at the time of the deletion.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyTree(clientData)
+ ClientData clientData; /* Info about tree widget. */
+{
+ Tree *treePtr = (Tree *) clientData;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ Node *nodePtr;
+
+ /*
+ * Free up all the stuff that requires special handling, then
+ * let Ck_FreeOptions handle all the standard option-related
+ * stuff.
+ */
+
+ if (treePtr->leadingString != NULL) {
+ ckfree((char *) treePtr->leadingString);
+ treePtr->leadingString = NULL;
+ }
+
+ hPtr = Tcl_FirstHashEntry(&treePtr->nodeTable, &search);
+ while (hPtr != NULL) {
+ nodePtr = (Node *) Tcl_GetHashValue(hPtr);
+ Ck_FreeOptions(nodeConfigSpecs, (char *) nodePtr, 0);
+ if (nodePtr->tagPtr != nodePtr->staticTagSpace)
+ ckfree((char *) nodePtr->tagPtr);
+ ckfree((char *) nodePtr);
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&treePtr->nodeTable);
+
+ Ck_FreeOptions(configSpecs, (char *) treePtr, 0);
+ ckfree((char *) treePtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureTree --
+ *
+ * This procedure is called to process an argv/argc list, plus
+ * the option database, in order to configure (or
+ * reconfigure) a tree widget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font,
+ * etc. get set for treePtr; old resources get freed, if there
+ * were any. The tree is redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureTree(interp, treePtr, argc, argv, flags)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ Tree *treePtr; /* Information about widget; may or may
+ * not already have values for some fields. */
+ int argc; /* Number of valid entries in argv. */
+ char **argv; /* Arguments. */
+ int flags; /* Flags to pass to Ck_ConfigureWidget. */
+{
+ int result, width, height;
+
+ result = Ck_ConfigureWidget(interp, treePtr->winPtr, configSpecs,
+ argc, argv, (char *) treePtr, flags);
+ if (result != TCL_OK)
+ return TCL_ERROR;
+ width = treePtr->width;
+ if (width <= 0)
+ width = 1;
+ height = treePtr->height;
+ if (height <= 0)
+ height = 1;
+ Ck_GeometryRequest(treePtr->winPtr, width, height);
+ TreeEventuallyRedraw(treePtr);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeEventuallyRedraw --
+ *
+ * This procedure is called to dispatch a do-when-idle
+ * handler for redrawing the tree.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TreeEventuallyRedraw(treePtr)
+ Tree *treePtr;
+{
+ if ((treePtr->winPtr->flags & CK_MAPPED)
+ && !(treePtr->flags & REDRAW_PENDING)) {
+ Tk_DoWhenIdle(DisplayTree, (ClientData) treePtr);
+ treePtr->flags |= REDRAW_PENDING;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteNode --
+ *
+ * This procedure is called to delete a node and its children.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteNode(treePtr, nodePtr)
+ Tree *treePtr;
+ Node *nodePtr;
+{
+ Node *childPtr, *thisPtr, *prevPtr;
+ Tcl_HashEntry *hPtr;
+
+ while ((childPtr = nodePtr->firstChild) != NULL)
+ DeleteNode(treePtr, childPtr);
+
+ if (treePtr->topNode == nodePtr)
+ treePtr->topNode = nodePtr->parent;
+ if (treePtr->activeNode == nodePtr)
+ treePtr->activeNode = NULL;
+
+ prevPtr = NULL;
+ if (nodePtr->parent == NULL) {
+ thisPtr = treePtr->firstChild;
+ } else {
+ thisPtr = nodePtr->parent->firstChild;
+ }
+ for (; thisPtr != NULL; prevPtr = thisPtr, thisPtr = thisPtr->next) {
+ if (thisPtr == nodePtr) {
+ if (prevPtr == NULL) {
+ if (nodePtr->parent == NULL)
+ treePtr->firstChild = nodePtr->next;
+ else
+ nodePtr->parent->firstChild = nodePtr->next;
+ } else
+ prevPtr->next = nodePtr->next;
+ if (nodePtr->next == NULL) {
+ if (nodePtr->parent == NULL)
+ treePtr->lastChild = prevPtr;
+ else
+ nodePtr->parent->lastChild = prevPtr;
+ }
+ break;
+ }
+ }
+
+ hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) nodePtr->id);
+ Tcl_DeleteHashEntry(hPtr);
+ Ck_FreeOptions(nodeConfigSpecs, (char *) nodePtr, 0);
+ if (nodePtr->tagPtr != nodePtr->staticTagSpace)
+ ckfree((char *) nodePtr->tagPtr);
+ ckfree((char *) nodePtr);
+}
+\f
+
+static void
+DeleteActiveTag(treePtr)
+ Tree *treePtr;
+{
+ int i;
+ Node *nodePtr = treePtr->activeNode;
+
+ if (nodePtr == NULL)
+ return;
+
+ for (i = nodePtr->numTags-1; i >= 0; i--) {
+ if (nodePtr->tagPtr[i] == activeUid) {
+ nodePtr->tagPtr[i] = nodePtr->tagPtr[nodePtr->numTags-1];
+ nodePtr->numTags--;
+ }
+ }
+ treePtr->activeNode = NULL;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayTree --
+ *
+ * This procedure is invoked to display a tree widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayTree(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ Tree *treePtr = (Tree *) clientData;
+ CkWindow *winPtr = treePtr->winPtr;
+ Node *nodePtr, *nextPtr, *parentPtr;
+ int i, x, y, mustRestore, rarrow;
+ int ulcorner, urcorner, llcorner, lrcorner, lvline, lhline, ltee, ttee;
+ WINDOW *window;
+
+ treePtr->flags &= ~REDRAW_PENDING;
+ if ((treePtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
+ return;
+ }
+ if (treePtr->flags & UPDATE_V_SCROLLBAR) {
+ TreeUpdateVScrollbar(treePtr);
+ }
+ treePtr->flags &= ~(REDRAW_PENDING|UPDATE_H_SCROLLBAR|UPDATE_V_SCROLLBAR);
+
+ if (treePtr->firstChild == NULL) {
+ Ck_ClearToBot(winPtr, 0, 0);
+ Ck_EventuallyRefresh(winPtr);
+ return;
+ }
+
+ Ck_GetGChar(NULL, "rarrow", &rarrow);
+ Ck_GetGChar(NULL, "ulcorner", &ulcorner);
+ Ck_GetGChar(NULL, "urcorner", &urcorner);
+ Ck_GetGChar(NULL, "llcorner", &llcorner);
+ Ck_GetGChar(NULL, "lrcorner", &lrcorner);
+ Ck_GetGChar(NULL, "vline", &lvline);
+ Ck_GetGChar(NULL, "hline", &lhline);
+ Ck_GetGChar(NULL, "ltee", <ee);
+ Ck_GetGChar(NULL, "ttee", &ttee);
+
+ Ck_SetWindowAttr(winPtr, treePtr->normalFg, treePtr->normalBg,
+ treePtr->normalAttr);
+
+ nodePtr = treePtr->topNode;
+
+ i = nodePtr->level * 2 - 1;
+ nextPtr = nodePtr->parent;
+ while (i >= 0) {
+ treePtr->leadingString[i] = ' ';
+ parentPtr = nextPtr->parent;
+ if (parentPtr != NULL) {
+ if (parentPtr->lastChild == nextPtr)
+ treePtr->leadingString[i - 1] = ' ';
+ else
+ treePtr->leadingString[i - 1] = lvline;
+ } else if (treePtr->lastChild == nextPtr)
+ treePtr->leadingString[i - 1] = ' ';
+ else
+ treePtr->leadingString[i - 1] = lvline;
+ nextPtr = parentPtr;
+ i -= 2;
+ }
+
+ window = winPtr->window;
+ y = 0;
+ while (nodePtr != NULL && y < winPtr->height) {
+ x = mustRestore = 0;
+ wmove(window, y, x);
+ if (nodePtr == treePtr->firstChild) {
+ waddch(window, (nodePtr == treePtr->lastChild) ? lhline : ulcorner);
+ x++;
+ } else if (nodePtr == treePtr->lastChild) {
+ waddch(window, llcorner);
+ x++;
+ } else if (nodePtr->level == 0) {
+ waddch(window, ltee);
+ x++;
+ }
+ for (i = 0; i < nodePtr->level * 2 && x < winPtr->width; i++) {
+ waddch(window, treePtr->leadingString[i]);
+ x++;
+ }
+ if (nodePtr->parent != NULL) {
+ if (x < winPtr->width) {
+ if (nodePtr == nodePtr->parent->lastChild)
+ waddch(window, llcorner);
+ else
+ waddch(window, ltee);
+ x++;
+ }
+ }
+ if (x < winPtr->width) {
+ waddch(window, lhline);
+ x++;
+ }
+ if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
+ if (x < winPtr->width) {
+ waddch(window, ttee);
+ x++;
+ }
+ nextPtr = nodePtr->firstChild;
+ } else {
+ if (x < winPtr->width) {
+ waddch(window, (nodePtr->firstChild == NULL) ? lhline : rarrow);
+ x++;
+ }
+ nextPtr = nodePtr->next;
+ if (nextPtr == NULL) {
+ parentPtr = nodePtr->parent;
+ while (nextPtr == NULL) {
+ if (parentPtr == NULL) {
+ break;
+ }
+ nextPtr = parentPtr->next;
+ if (nextPtr == NULL) {
+ parentPtr = parentPtr->parent;
+ }
+ }
+ }
+ }
+ if (x < winPtr->width) {
+ waddch(window, ' ');
+ x++;
+ }
+
+ if (nodePtr == treePtr->activeNode && (treePtr->flags & GOT_FOCUS)) {
+ Ck_SetWindowAttr(winPtr, treePtr->activeFg, treePtr->activeBg,
+ treePtr->activeAttr | ((nodePtr->flags & SELECTED) ?
+ treePtr->selectAttr : 0));
+ mustRestore = 1;
+ } else if (nodePtr->flags & SELECTED) {
+ Ck_SetWindowAttr(winPtr, treePtr->selectFg, treePtr->selectBg,
+ treePtr->selectAttr);
+ mustRestore = 1;
+ }
+ CkDisplayChars(winPtr->mainPtr, window,
+ nodePtr->text, strlen(nodePtr->text),
+ x, y, 0,
+ CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
+ if (mustRestore)
+ Ck_SetWindowAttr(winPtr, treePtr->normalFg, treePtr->normalBg,
+ treePtr->normalAttr);
+ Ck_ClearToEol(winPtr, -1, -1);
+
+ i = nodePtr->level * 2;
+ treePtr->leadingString[i] = nodePtr->next != NULL ? lvline : ' ';
+ treePtr->leadingString[i + 1] = ' ';
+
+ nodePtr = nextPtr;
+ y++;
+ }
+ if (y < winPtr->height)
+ Ck_ClearToBot(winPtr, 0, y);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TreeEventProc --
+ *
+ * This procedure is invoked by the dispatcher for various
+ * events on trees.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get
+ * cleaned up. When it gets exposed, it is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+TreeEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about window. */
+ CkEvent *eventPtr; /* Information about event. */
+{
+ Tree *treePtr = (Tree *) clientData;
+
+ if (eventPtr->type == CK_EV_EXPOSE) {
+ TreeEventuallyRedraw(treePtr);
+ } else if (eventPtr->type == CK_EV_DESTROY) {
+ if (treePtr->winPtr != NULL) {
+ treePtr->winPtr = NULL;
+ Tcl_DeleteCommand(treePtr->interp,
+ Tcl_GetCommandName(treePtr->interp, treePtr->widgetCmd));
+ }
+ if (treePtr->flags & REDRAW_PENDING) {
+ Tk_CancelIdleCall(DisplayTree, (ClientData) treePtr);
+ }
+ Ck_EventuallyFree((ClientData) treePtr, (Ck_FreeProc *) DestroyTree);
+ } else if (eventPtr->type == CK_EV_FOCUSIN) {
+ treePtr->flags |= GOT_FOCUS;
+ TreeEventuallyRedraw(treePtr);
+ } else if (eventPtr->type == CK_EV_FOCUSOUT) {
+ treePtr->flags &= ~GOT_FOCUS;
+ TreeEventuallyRedraw(treePtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeCmdDeletedProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If
+ * the widget isn't already in the process of being destroyed,
+ * this command destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TreeCmdDeletedProc(clientData)
+ ClientData clientData; /* Pointer to widget record for widget. */
+{
+ Tree *treePtr = (Tree *) clientData;
+ CkWindow *winPtr = treePtr->winPtr;
+
+ /*
+ * This procedure could be invoked either because the window was
+ * destroyed and the command was then deleted (in which case winPtr
+ * is NULL) or because the command was deleted, and then this procedure
+ * destroys the widget.
+ */
+
+ if (winPtr != NULL) {
+ treePtr->winPtr = NULL;
+ Ck_DestroyWindow(winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecomputeVisibleNodes --
+ *
+ * Display parameters are recomputed.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RecomputeVisibleNodes(treePtr)
+ Tree *treePtr;
+{
+ int count = 0, top = -1;
+ Node *nodePtr, *nextPtr = NULL;
+
+ nodePtr = treePtr->firstChild;
+ if (nodePtr == NULL)
+ treePtr->topNode = NULL;
+ while (nodePtr != NULL) {
+ if (nodePtr->parent == NULL)
+ nextPtr = nodePtr->next;
+ if (nodePtr == treePtr->topNode)
+ top = count;
+ if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr = nodePtr->firstChild;
+ } else if (nodePtr->next != NULL)
+ nodePtr = nodePtr->next;
+ else {
+ while (nodePtr != NULL) {
+ nodePtr = nodePtr->parent;
+ if (nodePtr != NULL && nodePtr->next != NULL) {
+ nodePtr = nodePtr->next;
+ break;
+ }
+ }
+ if (nodePtr == NULL)
+ nodePtr = nextPtr;
+ }
+ count++;
+ }
+ if (top < 0) {
+ treePtr->topNode = treePtr->firstChild;
+ top = 0;
+ }
+ if (top != treePtr->topIndex || count != treePtr->visibleNodes)
+ treePtr->flags |= UPDATE_V_SCROLLBAR;
+ treePtr->topIndex = top;
+ treePtr->visibleNodes = count;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangeTreeView --
+ *
+ * Change the vertical view on a tree widget so that a given element
+ * is displayed at the top.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * What's displayed on the screen is changed. If there is a
+ * scrollbar associated with this widget, then the scrollbar
+ * is instructed to change its display too.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeTreeView(treePtr, index)
+ Tree *treePtr; /* Information about widget. */
+ int index; /* Index of element in treePtr
+ * that should now appear at the
+ * top of the tree. */
+{
+ int count;
+ Node *nodePtr, *nextPtr = NULL;
+
+ if (index >= treePtr->visibleNodes - treePtr->winPtr->height)
+ index = treePtr->visibleNodes - treePtr->winPtr->height;
+ if (index < 0)
+ index = 0;
+ if (treePtr->topIndex != index) {
+ if (index < treePtr->topIndex) {
+ count = 0;
+ nodePtr = treePtr->firstChild;
+ } else {
+ count = treePtr->topIndex;
+ nodePtr = treePtr->topNode;
+ }
+ while (nodePtr != NULL) {
+ if (nodePtr->parent == NULL)
+ nextPtr = nodePtr->next;
+ if (count == index)
+ break;
+ if (nodePtr->firstChild != NULL &&
+ (nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr = nodePtr->firstChild;
+ } else if (nodePtr->next != NULL)
+ nodePtr = nodePtr->next;
+ else {
+ while (nodePtr != NULL) {
+ nodePtr = nodePtr->parent;
+ if (nodePtr != NULL && nodePtr->next != NULL) {
+ nodePtr = nodePtr->next;
+ break;
+ }
+ }
+ if (nodePtr == NULL)
+ nodePtr = nextPtr;
+ }
+ count++;
+ }
+ treePtr->topNode = nodePtr;
+ treePtr->topIndex = count;
+ treePtr->flags |= UPDATE_V_SCROLLBAR;
+ TreeEventuallyRedraw(treePtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNodeYCoord --
+ *
+ * Given node return the window Y coordinate corresponding to it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetNodeYCoord(treePtr, thisPtr, yPtr)
+ Tree *treePtr; /* Information about widget. */
+ Node *thisPtr;
+ int *yPtr;
+{
+ int count;
+ Node *nodePtr, *nextPtr = NULL;
+
+ count = 0;
+ nodePtr = treePtr->firstChild;
+ while (nodePtr != NULL) {
+ if (thisPtr == nodePtr) {
+ *yPtr = count;
+ return TCL_OK;
+ }
+ if (nodePtr->parent == NULL)
+ nextPtr = nodePtr->next;
+ if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr = nodePtr->firstChild;
+ } else if (nodePtr->next != NULL)
+ nodePtr = nodePtr->next;
+ else {
+ while (nodePtr != NULL) {
+ nodePtr = nodePtr->parent;
+ if (nodePtr != NULL && nodePtr->next != NULL) {
+ nodePtr = nodePtr->next;
+ break;
+ }
+ }
+ if (nodePtr == NULL)
+ nodePtr = nextPtr;
+ }
+ count++;
+ }
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TreeTagsParseProc --
+ *
+ * This procedure is invoked during option processing to handle
+ * "-tags" options for tree nodes.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * The tags for a given node get replaced by those indicated
+ * in the value argument.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+TreeTagsParseProc(clientData, interp, winPtr, value, widgRec, offset)
+ ClientData clientData; /* Not used.*/
+ Tcl_Interp *interp; /* Used for reporting errors. */
+ CkWindow *winPtr; /* Window containing tree widget. */
+ char *value; /* Value of option (list of tag
+ * names). */
+ char *widgRec; /* Pointer to record for item. */
+ int offset; /* Offset into item (ignored). */
+{
+ Node *nodePtr = (Node *) widgRec, *activeNode = NULL;
+ int argc, i, hideChildren = 0, redraw = 0, recompute = 0;
+ char **argv;
+ Ck_Uid *newPtr;
+
+ /*
+ * Break the value up into the individual tag names.
+ */
+
+ if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Check for special tags.
+ */
+ for (i = 0; i < nodePtr->numTags; i++) {
+ if (nodePtr->tagPtr[i] == activeUid) {
+ DeleteActiveTag(nodePtr->tree);
+ redraw++;
+ }
+ }
+
+ /*
+ * Make sure that there's enough space in the node to hold the
+ * tag names.
+ */
+
+ if (nodePtr->tagSpace < argc) {
+ newPtr = (Ck_Uid *) ckalloc((unsigned) (argc * sizeof(Ck_Uid)));
+ for (i = nodePtr->numTags-1; i >= 0; i--) {
+ newPtr[i] = nodePtr->tagPtr[i];
+ }
+ if (nodePtr->tagPtr != nodePtr->staticTagSpace) {
+ ckfree((char *) nodePtr->tagPtr);
+ }
+ nodePtr->tagPtr = newPtr;
+ nodePtr->tagSpace = argc;
+ }
+ nodePtr->numTags = argc;
+ for (i = 0; i < argc; i++) {
+ nodePtr->tagPtr[i] = Ck_GetUid(argv[i]);
+ if (nodePtr->tagPtr[i] == hideChildrenUid)
+ hideChildren++;
+ else if (nodePtr->tagPtr[i] == activeUid)
+ activeNode = nodePtr;
+ }
+ ckfree((char *) argv);
+ if (hideChildren && (nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr->flags &= ~SHOWCHILDREN;
+ recompute++;
+ redraw++;
+ } else if (!hideChildren && !(nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr->flags |= SHOWCHILDREN;
+ recompute++;
+ redraw++;
+ }
+ if (activeNode != NULL) {
+ DeleteActiveTag(nodePtr->tree);
+ nodePtr->tree->activeNode = activeNode;
+ redraw++;
+ }
+ if (recompute)
+ RecomputeVisibleNodes(nodePtr->tree);
+ if (redraw)
+ TreeEventuallyRedraw(nodePtr->tree);
+ return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TreeTagsPrintProc --
+ *
+ * This procedure is invoked by the Ck configuration code
+ * to produce a printable string for the "-tags" configuration
+ * option for tree nodes.
+ *
+ * Results:
+ * The return value is a string describing all the tags for
+ * the node referred to by "widgRec". In addition, *freeProcPtr
+ * is filled in with the address of a procedure to call to free
+ * the result string when it's no longer needed (or NULL to
+ * indicate that the string doesn't need to be freed).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+TreeTagsPrintProc(clientData, winPtr, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Ignored. */
+ CkWindow *winPtr; /* Window containing tree widget. */
+ char *widgRec; /* Pointer to record for item. */
+ int offset; /* Ignored. */
+ Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
+ * information about how to reclaim
+ * storage for return string. */
+{
+ Node *nodePtr = (Node *) widgRec;
+
+ if (nodePtr->numTags == 0) {
+ *freeProcPtr = (Tcl_FreeProc *) NULL;
+ return "";
+ }
+ if (nodePtr->numTags == 1) {
+ *freeProcPtr = (Tcl_FreeProc *) NULL;
+ return (char *) nodePtr->tagPtr[0];
+ }
+ *freeProcPtr = (Tcl_FreeProc *) free;
+ return Tcl_Merge(nodePtr->numTags, (char **) nodePtr->tagPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * StartTagSearch --
+ *
+ * This procedure is called to initiate an enumeration of
+ * all nodes in a given tree that contain a given tag.
+ *
+ * Results:
+ * The return value is a pointer to the first node in
+ * treePtr that matches tag, or NULL if there is no
+ * such node. The information at *searchPtr is initialized
+ * such that successive calls to NextNode will return
+ * successive nodes that match tag.
+ *
+ * Side effects:
+ * SearchPtr is linked into a list of searches in progress
+ * on treePtr, so that elements can safely be deleted
+ * while the search is in progress. EndTagSearch must be
+ * called at the end of the search to unlink searchPtr from
+ * this list.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Node *
+StartTagSearch(treePtr, tag, searchPtr)
+ Tree *treePtr; /* Tree whose nodes are to be
+ * searched. */
+ char *tag; /* String giving tag value. */
+ TagSearch *searchPtr; /* Record describing tag search;
+ * will be initialized here. */
+{
+ int id;
+ Tcl_HashEntry *hPtr;
+ Node *nodePtr;
+ Ck_Uid *tagPtr;
+ Ck_Uid uid;
+ int count;
+
+ /*
+ * Initialize the search.
+ */
+
+ nodePtr = NULL;
+ searchPtr->treePtr = treePtr;
+ searchPtr->searchOver = 0;
+
+ /*
+ * Find the first matching node in one of several ways. If the tag
+ * is a number then it selects the single node with the matching
+ * identifier.
+ */
+
+ if (isdigit((unsigned char) (*tag))) {
+ char *end;
+
+ id = strtoul(tag, &end, 0);
+ if (*end == 0) {
+
+ hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) id);
+ if (hPtr != NULL)
+ nodePtr = (Node *) Tcl_GetHashValue(hPtr);
+ searchPtr->searchOver = 1;
+ return nodePtr;
+ }
+ }
+
+ hPtr = Tcl_FirstHashEntry(&treePtr->nodeTable, &searchPtr->search);
+ if (hPtr == NULL) {
+ searchPtr->searchOver = 1;
+ return nodePtr;
+ }
+ searchPtr->tag = uid = Ck_GetUid(tag);
+ if (uid == allUid) {
+
+ /*
+ * All nodes match.
+ */
+
+ searchPtr->tag = NULL;
+ return (Node *) Tcl_GetHashValue(hPtr);
+ }
+
+ do {
+ nodePtr = (Node *) Tcl_GetHashValue(hPtr);
+ for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
+ count > 0;
+ tagPtr++, count--) {
+ if (*tagPtr == uid) {
+ return nodePtr;
+ }
+ }
+ hPtr = Tcl_NextHashEntry(&searchPtr->search);
+ } while (hPtr != NULL);
+
+ searchPtr->searchOver = 1;
+ return NULL;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * NextNode --
+ *
+ * This procedure returns successive nodes that match a given
+ * tag; it should be called only after StartTagSearch has been
+ * used to begin a search.
+ *
+ * Results:
+ * The return value is a pointer to the next node that matches
+ * the tag specified to StartTagSearch, or NULL if no such
+ * node exists. *SearchPtr is updated so that the next call
+ * to this procedure will return the next node.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Node *
+NextNode(searchPtr)
+ TagSearch *searchPtr; /* Record describing search in
+ * progress. */
+{
+ Node *nodePtr;
+ Tcl_HashEntry *hPtr;
+ int count;
+ Ck_Uid uid;
+ Ck_Uid *tagPtr;
+
+ if (searchPtr->searchOver)
+ return NULL;
+
+ hPtr = Tcl_NextHashEntry(&searchPtr->search);
+ if (hPtr == NULL) {
+ searchPtr->searchOver = 1;
+ return NULL;
+ }
+
+ /*
+ * Handle special case of "all" search by returning next node.
+ */
+
+ uid = searchPtr->tag;
+ if (uid == NULL) {
+ return (Node *) Tcl_GetHashValue(hPtr);
+ }
+
+ /*
+ * Look for a node with a particular tag.
+ */
+
+ do {
+ nodePtr = (Node *) Tcl_GetHashValue(hPtr);
+ for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
+ count > 0;
+ tagPtr++, count--) {
+ if (*tagPtr == uid) {
+ return nodePtr;
+ }
+ }
+ hPtr = Tcl_NextHashEntry(&searchPtr->search);
+ } while (hPtr != NULL);
+
+ searchPtr->searchOver = 1;
+ return NULL;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * DoNode --
+ *
+ * This is a utility procedure called by FindNodes. It
+ * either adds nodePtr's id to the result forming in interp,
+ * or it adds a new tag to nodePtr, depending on the value
+ * of tag.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If tag is NULL then nodePtr's id is added as a list element
+ * to interp->result; otherwise tag is added to nodePtr's
+ * list of tags.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DoNode(interp, nodePtr, tag)
+ Tcl_Interp *interp; /* Interpreter in which to (possibly)
+ * record node id. */
+ Node *nodePtr; /* Node to (possibly) modify. */
+ Ck_Uid tag; /* Tag to add to those already
+ * present for node, or NULL. */
+{
+ Ck_Uid *tagPtr;
+ int count;
+
+ /*
+ * Handle the "add-to-result" case and return, if appropriate.
+ */
+
+ if (tag == NULL) {
+ char msg[30];
+
+ sprintf(msg, "%d", nodePtr->id);
+ Tcl_AppendElement(interp, msg);
+ return;
+ }
+
+ for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
+ count > 0; tagPtr++, count--) {
+ if (tag == *tagPtr)
+ return;
+ }
+
+ /*
+ * Grow the tag space if there's no more room left in the current
+ * block.
+ */
+
+ if (nodePtr->tagSpace == nodePtr->numTags) {
+ Ck_Uid *newTagPtr;
+
+ nodePtr->tagSpace += TAG_SPACE;
+ newTagPtr = (Ck_Uid *) ckalloc((unsigned)
+ (nodePtr->tagSpace * sizeof (Ck_Uid)));
+ memcpy(newTagPtr, nodePtr->tagPtr, nodePtr->numTags * sizeof (Ck_Uid));
+ if (nodePtr->tagPtr != nodePtr->staticTagSpace) {
+ ckfree((char *) nodePtr->tagPtr);
+ }
+ nodePtr->tagPtr = newTagPtr;
+ tagPtr = &nodePtr->tagPtr[nodePtr->numTags];
+ }
+
+ /*
+ * Add in the new tag.
+ */
+
+ *tagPtr = tag;
+ nodePtr->numTags++;
+
+ if (tag == activeUid) {
+ DeleteActiveTag(nodePtr->tree);
+ nodePtr->tree->activeNode = nodePtr;
+ TreeEventuallyRedraw(nodePtr->tree);
+ } else if (tag == hideChildrenUid) {
+ nodePtr->flags &= ~SHOWCHILDREN;
+ RecomputeVisibleNodes(nodePtr->tree);
+ TreeEventuallyRedraw(nodePtr->tree);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * FindNodes --
+ *
+ * This procedure does all the work of implementing the
+ * "find" and "addtag" options of the tree widget command,
+ * which locate nodes that have certain features (location,
+ * tags).
+ *
+ * Results:
+ * A standard Tcl return value. If newTag is NULL, then a
+ * list of ids from all the nodes that match argc/argv is
+ * returned in interp->result. If newTag is NULL, then
+ * the normal interp->result is an empty string. If an error
+ * occurs, then interp->result will hold an error message.
+ *
+ * Side effects:
+ * If newTag is non-NULL, then all the nodes that match the
+ * information in argc/argv have that tag added to their
+ * lists of tags.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+FindNodes(interp, treePtr, argc, argv, newTag, cmdName, option)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tree *treePtr; /* Tree whose nodes are to be
+ * searched. */
+ int argc; /* Number of entries in argv. Must be
+ * greater than zero. */
+ char **argv; /* Arguments that describe what items
+ * to search for (see user doc on
+ * "find" and "addtag" options). */
+ char *newTag; /* If non-NULL, gives new tag to set
+ * on all found items; if NULL, then
+ * ids of found items are returned
+ * in interp->result. */
+ char *cmdName; /* Name of original Tcl command, for
+ * use in error messages. */
+ char *option; /* For error messages: gives option
+ * from Tcl command and other stuff
+ * up to what's in argc/argv. */
+{
+ int c;
+ size_t length;
+ TagSearch search;
+ Node *nodePtr;
+ Ck_Uid uid;
+
+ if (newTag != NULL) {
+ uid = Ck_GetUid(newTag);
+ } else {
+ uid = NULL;
+ }
+ c = argv[0][0];
+ length = strlen(argv[0]);
+ if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
+ && (length >= 2)) {
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"",
+ cmdName, option, " all", (char *) NULL);
+ return TCL_ERROR;
+ }
+ for (nodePtr = StartTagSearch(treePtr, "all", &search);
+ nodePtr != NULL; nodePtr = NextNode(&search)) {
+ DoNode(interp, nodePtr, uid);
+ }
+ } else if ((c == 'n') && (strncmp(argv[0], "next", length) == 0) &&
+ length > 2) {
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"",
+ cmdName, option, " next tagOrId", (char *) NULL);
+ return TCL_ERROR;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[1], &search);
+ if (nodePtr == NULL)
+ nodePtr = treePtr->firstChild;
+ if (nodePtr != NULL) {
+ if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN))
+ nodePtr = nodePtr->firstChild;
+ else if (nodePtr->next != NULL)
+ nodePtr = nodePtr->next;
+ else {
+ while (nodePtr != NULL) {
+ nodePtr = nodePtr->parent;
+ if (nodePtr != NULL && nodePtr->next != NULL) {
+ nodePtr = nodePtr->next;
+ break;
+ }
+ }
+ }
+ if (nodePtr != NULL)
+ DoNode(interp, nodePtr, uid);
+ }
+ } else if ((c == 'n') && (strncmp(argv[0], "nearest", length) == 0) &&
+ length > 2) {
+ int x, y, count;
+ Node *nextPtr = NULL;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"",
+ cmdName, option, " nearest x y", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Ck_GetCoord(interp, treePtr->winPtr, argv[1], &x) != TCL_OK ||
+ Ck_GetCoord(interp, treePtr->winPtr, argv[2], &y) != TCL_OK)
+ return TCL_ERROR;
+ if (y >= treePtr->winPtr->height)
+ y = treePtr->winPtr->height - 1;
+
+ count = 0;
+ nodePtr = treePtr->topNode;
+ while (nodePtr != NULL) {
+ if (count == y)
+ break;
+ if (nodePtr->parent == NULL)
+ nextPtr = nodePtr->next;
+ if (nodePtr->firstChild != NULL &&
+ (nodePtr->flags & SHOWCHILDREN)) {
+ nodePtr = nodePtr->firstChild;
+ } else if (nodePtr->next != NULL)
+ nodePtr = nodePtr->next;
+ else {
+ while (nodePtr != NULL) {
+ nodePtr = nodePtr->parent;
+ if (nodePtr != NULL && nodePtr->next != NULL) {
+ nodePtr = nodePtr->next;
+ break;
+ }
+ }
+ if (nodePtr == NULL)
+ nodePtr = nextPtr;
+ }
+ count++;
+ }
+ if (nodePtr != NULL)
+ sprintf(interp->result, "%d", nodePtr->id);
+ } else if ((c == 'p') && (strncmp(argv[0], "prev", length) == 0)) {
+ int done = 0;
+ Node *parentPtr, *nextPtr;
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"",
+ cmdName, option, " prev tagOrId", (char *) NULL);
+ return TCL_ERROR;
+ }
+ nodePtr = StartTagSearch(treePtr, argv[1], &search);
+ if (nodePtr == NULL)
+ nodePtr = treePtr->firstChild;
+ if (nodePtr != NULL) {
+ parentPtr = nodePtr->parent;
+ if (parentPtr != NULL) {
+ if (nodePtr == parentPtr->firstChild) {
+ nextPtr = parentPtr;
+ done = 1;
+ } else
+ nextPtr = parentPtr->firstChild;
+ } else
+ parentPtr = nextPtr = treePtr->firstChild;
+ if (!done) {
+ for (;nextPtr != NULL && nextPtr->next != nodePtr;
+ nextPtr = nextPtr->next) {
+ /* Empty loop body. */
+ }
+ if (nextPtr == NULL)
+ nextPtr = parentPtr->parent;
+ if (nextPtr == NULL)
+ nextPtr = treePtr->firstChild;
+ else {
+ while (nextPtr->lastChild != NULL &&
+ (nextPtr->flags & SHOWCHILDREN))
+ nextPtr = nextPtr->lastChild;
+ }
+ }
+ DoNode(interp, nextPtr, uid);
+ }
+ } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"",
+ cmdName, option, " withtag tagOrId", (char *) NULL);
+ return TCL_ERROR;
+ }
+ for (nodePtr = StartTagSearch(treePtr, argv[1], &search);
+ nodePtr != NULL; nodePtr = NextNode(&search)) {
+ DoNode(interp, nodePtr, uid);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad search command \"", argv[0],
+ "\": must be all, nearest, or withtag", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TreeUpdateVScrollbar --
+ *
+ * This procedure is invoked whenever information has changed in
+ * a tree in a way that would invalidate a vertical scrollbar
+ * display. If there is an associated scrollbar, then this command
+ * updates it by invoking a Tcl command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Tcl command is invoked, and an additional command may be
+ * invoked to process errors in the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TreeUpdateVScrollbar(treePtr)
+ Tree *treePtr; /* Information about widget. */
+{
+ char string[100];
+ double first, last;
+ int result;
+
+ if (treePtr->yScrollCmd == NULL) {
+ return;
+ }
+ if (treePtr->visibleNodes == 0) {
+ first = 0.0;
+ last = 1.0;
+ } else {
+ first = treePtr->topIndex / ((double) treePtr->visibleNodes);
+ last = (treePtr->topIndex + treePtr->winPtr->height)
+ / ((double) treePtr->visibleNodes);
+ if (last > 1.0) {
+ last = 1.0;
+ }
+ }
+ sprintf(string, " %g %g", first, last);
+ result = Tcl_VarEval(treePtr->interp, treePtr->yScrollCmd, string,
+ (char *) NULL);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(treePtr->interp,
+ "\n (vertical scrolling command executed by tree)");
+ Tk_BackgroundError(treePtr->interp);
+ }
+}
--- /dev/null
+/*
+ * ckUtil.c --
+ *
+ * Miscellaneous utility functions.
+ *
+ * Copyright (c) 1995 Christian Werner.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+#define REPLACE 1
+#define NORMAL 2
+#define TAB 3
+#define NEWLINE 4
+#define GCHAR 5
+
+struct charType {
+ char type; /* Type of char, see definitions above. */
+ char width; /* Width if replaced by backslash sequence. */
+};
+
+struct charEncoding {
+ char *name; /* Name for this encoding table. */
+ struct charType ct[256]; /* Encoding table. */
+};
+
+/*
+ * For ISO8859, codes 0x81..0x99 are mapped to ACS characters
+ * according to this table:
+ */
+
+static char *gcharTab[] = {
+ "ulcorner", "llcorner", "urcorner", "lrcorner",
+ "ltee", "rtee", "btee", "ttee",
+ "hline", "vline", "plus", "s1",
+ "s9", "diamond", "ckboard", "degree",
+ "plminus", "bullet", "larrow", "rarrow",
+ "darrow", "uarrow", "board", "lantern",
+ "block"
+};
+
+static struct charEncoding EncodingTable[] = {
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ISO 8859 encoding.
+ *
+ *----------------------------------------------------------------------
+ */
+
+{
+ "ISO8859", {
+
+ { REPLACE, 4 }, /* \x00 */
+ { REPLACE, 4 }, /* \x01 */
+ { REPLACE, 4 }, /* \x02 */
+ { REPLACE, 4 }, /* \x03 */
+ { REPLACE, 4 }, /* \x04 */
+ { REPLACE, 4 }, /* \x05 */
+ { REPLACE, 4 }, /* \x06 */
+ { REPLACE, 4 }, /* \x07 */
+ { REPLACE, 2 }, /* \b */
+ { TAB, 2 }, /* \t */
+ { NEWLINE, 2 }, /* \n */
+ { REPLACE, 4 }, /* \x0b */
+ { REPLACE, 2 }, /* \f */
+ { REPLACE, 2 }, /* \r */
+ { REPLACE, 4 }, /* 0x0e */
+ { REPLACE, 4 }, /* 0x0f */
+
+ /* 0x10 .. 0x1f */
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+
+ /* ' ' .. '/' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '0' .. '?' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '@' .. 'O' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 'P' .. '_' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '`' .. 'o' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 'p' .. '~' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ { REPLACE, 4 }, /* 0x7f */
+
+ /* 0x80 .. 0x8f */
+ { REPLACE, 4 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+ { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+ { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+ { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+
+ /* 0x90 .. 0x9f */
+ { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+ { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
+ { GCHAR, 1 }, { GCHAR, 1 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+
+ /* 0xa0 .. 0xaf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xb0 .. 0xbf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xc0 .. 0xcf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xd0 .. 0xdf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xe0 .. 0xef */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xf0 .. 0xff */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ }
+},
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * IBM code page 437 encoding.
+ *
+ *----------------------------------------------------------------------
+ */
+
+{
+ "IBM437", {
+
+ { REPLACE, 4 }, /* \x00 */
+ { REPLACE, 4 }, /* \x01 */
+ { REPLACE, 4 }, /* \x02 */
+ { REPLACE, 4 }, /* \x03 */
+ { REPLACE, 4 }, /* \x04 */
+ { REPLACE, 4 }, /* \x05 */
+ { REPLACE, 4 }, /* \x06 */
+ { REPLACE, 4 }, /* \x07 */
+ { REPLACE, 2 }, /* \b */
+ { TAB, 2 }, /* \t */
+ { NEWLINE, 2 }, /* \n */
+ { REPLACE, 4 }, /* \x0b */
+ { REPLACE, 2 }, /* \f */
+ { REPLACE, 2 }, /* \r */
+ { REPLACE, 4 }, /* 0x0e */
+ { REPLACE, 4 }, /* 0x0f */
+
+ /* 0x10 .. 0x1f */
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+ { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
+
+ /* ' ' .. '/' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '0' .. '?' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '@' .. 'O' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 'P' .. '_' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* '`' .. 'o' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 'p' .. '~' */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ { REPLACE, 4 }, /* 0x7f */
+
+ /* 0x80 .. 0x8f */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0x90 .. 0x9a */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ { NORMAL, 1 }, /* 0x9b */
+
+ /* 0x9c .. 0x9f */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xa0 .. 0xaf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xb0 .. 0xbf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xc0 .. 0xcf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xd0 .. 0xdf */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xe0 .. 0xef */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ /* 0xf0 .. 0xfe */
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+ { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
+
+ { NORMAL, 1 } /* 0xff */
+
+ }
+}
+
+};
+
+/*
+ * This is the switch for char encoding.
+ */
+
+static int Encoding = 0;
+
+#define CHARTYPE(x) EncodingTable[Encoding].ct[(x)]
+
+/*
+ * Characters used when displaying control sequences.
+ */
+
+static char hexChars[] = "0123456789abcdefxtnvr\\";
+
+/*
+ * The following table maps some control characters to sequences
+ * like '\n' rather than '\x10'. A zero entry in the table means
+ * no such mapping exists, and the table only maps characters
+ * less than 0x10.
+ */
+
+static char mapChars[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 'b', 't', 'n', 0, 'f', 'r', 0
+};
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CkCopyAndGlobalEval --
+ *
+ * This procedure makes a copy of a script then calls Tcl_GlobalEval
+ * to evaluate it. It's used in situations where the execution of
+ * a command may cause the original command string to be reallocated.
+ *
+ * Results:
+ * Returns the result of evaluating script, including both a standard
+ * Tcl completion code and a string in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+CkCopyAndGlobalEval(interp, script)
+ Tcl_Interp *interp; /* Interpreter in which to evaluate
+ * script. */
+ char *script; /* Script to evaluate. */
+{
+ Tcl_DString buffer;
+ int code;
+
+ Tcl_DStringInit(&buffer);
+ Tcl_DStringAppend(&buffer, script, -1);
+ code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
+ Tcl_DStringFree(&buffer);
+ return code;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_GetScrollInfo --
+ *
+ * This procedure is invoked to parse "xview" and "yview"
+ * scrolling commands for widgets using the new scrolling
+ * command syntax ("moveto" or "scroll" options).
+ *
+ * Results:
+ * The return value is either CK_SCROLL_MOVETO, CK_SCROLL_PAGES,
+ * CK_SCROLL_UNITS, or CK_SCROLL_ERROR. This indicates whether
+ * the command was successfully parsed and what form the command
+ * took. If CK_SCROLL_MOVETO, *dblPtr is filled in with the
+ * desired position; if CK_SCROLL_PAGES or CK_SCROLL_UNITS,
+ * *intPtr is filled in with the number of lines to move (may be
+ * negative); if CK_SCROLL_ERROR, interp->result contains an
+ * error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_GetScrollInfo(interp, argc, argv, dblPtr, intPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ int argc; /* # arguments for command. */
+ char **argv; /* Arguments for command. */
+ double *dblPtr; /* Filled in with argument "moveto"
+ * option, if any. */
+ int *intPtr; /* Filled in with number of pages
+ * or lines to scroll, if any. */
+{
+ int c;
+ size_t length;
+
+ length = strlen(argv[2]);
+ c = argv[2][0];
+ if ((c == 'm') && (strncmp(argv[2], "moveto", length) == 0)) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ", argv[1], " moveto fraction\"",
+ (char *) NULL);
+ return CK_SCROLL_ERROR;
+ }
+ if (Tcl_GetDouble(interp, argv[3], dblPtr) != TCL_OK) {
+ return CK_SCROLL_ERROR;
+ }
+ return CK_SCROLL_MOVETO;
+ } else if ((c == 's')
+ && (strncmp(argv[2], "scroll", length) == 0)) {
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ", argv[1], " scroll number units|pages\"",
+ (char *) NULL);
+ return CK_SCROLL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[3], intPtr) != TCL_OK) {
+ return CK_SCROLL_ERROR;
+ }
+ length = strlen(argv[4]);
+ c = argv[4][0];
+ if ((c == 'p') && (strncmp(argv[4], "pages", length) == 0)) {
+ return CK_SCROLL_PAGES;
+ } else if ((c == 'u')
+ && (strncmp(argv[4], "units", length) == 0)) {
+ return CK_SCROLL_UNITS;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[4],
+ "\": must be units or pages", (char *) NULL);
+ return CK_SCROLL_ERROR;
+ }
+ }
+ Tcl_AppendResult(interp, "unknown option \"", argv[2],
+ "\": must be moveto or scroll", (char *) NULL);
+ return CK_SCROLL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_SetEncoding --
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_SetEncoding(interp, name)
+ Tcl_Interp *interp;
+ char *name;
+{
+ int i;
+
+ for (i = 0; i < sizeof (EncodingTable) / sizeof (EncodingTable[0]); i++)
+ if (strcmp(name, EncodingTable[i].name) == 0) {
+ Encoding = i;
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "no encoding \"", name, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_GetEncoding --
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Ck_GetEncoding(interp)
+ Tcl_Interp *interp;
+{
+ interp->result = EncodingTable[Encoding].name;
+ return TCL_OK;
+}
+\f
+#if CK_USE_UTF
+/*
+ *--------------------------------------------------------------
+ *
+ * MakeISO --
+ *
+ * Procedure to convert UTF-8 representation to
+ * ISO8859-1 for printing on screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+MakeISO(mainPtr, string, numChars, lenPtr)
+ CkMainInfo *mainPtr;
+ char *string;
+ int numChars;
+ int *lenPtr;
+{
+ char *p;
+
+ Tcl_DStringFree(&mainPtr->isoBuffer);
+ p = Tcl_UtfToExternalDString(mainPtr->isoEncoding, string,
+ numChars, &mainPtr->isoBuffer);
+ if (lenPtr) {
+ *lenPtr = Tcl_DStringLength(&mainPtr->isoBuffer);
+ }
+ return p;
+}
+#endif
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkMeasureChars --
+ *
+ * Measure the number of characters from a string that
+ * will fit in a given horizontal span. The measurement
+ * is done under the assumption that CkDisplayChars will
+ * be used to actually display the characters.
+ *
+ * Results:
+ * The return value is the number of characters from source
+ * that fit in the span given by startX and maxX. *nextXPtr
+ * is filled in with the x-coordinate at which the first
+ * character that didn't fit would be drawn, if it were to
+ * be drawn.
+ *
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+CkMeasureChars(mainPtr, source, maxChars, startX, maxX,
+ tabOrigin, flags, nextXPtr, nextCPtr)
+ CkMainInfo *mainPtr; /* Needed for encoding. */
+ char *source; /* Characters to be displayed. Need not
+ * be NULL-terminated. */
+ int maxChars; /* Maximum # of characters to consider from
+ * source. */
+ int startX; /* X-position at which first character will
+ * be drawn. */
+ int maxX; /* Don't consider any character that would
+ * cross this x-position. */
+ int tabOrigin; /* X-location that serves as "origin" for
+ * tab stops. */
+ int flags; /* Various flag bits OR-ed together.
+ * CK_WHOLE_WORDS means stop on a word boundary
+ * (just before a space character) if
+ * possible. CK_AT_LEAST_ONE means always
+ * return a value of at least one, even
+ * if the character doesn't fit.
+ * CK_PARTIAL_OK means it's OK to display only
+ * a part of the last character in the line.
+ * CK_NEWLINES_NOT_SPECIAL means that newlines
+ * are treated just like other control chars:
+ * they don't terminate the line.
+ * CK_IGNORE_TABS means give all tabs zero
+ * width. */
+ int *nextXPtr; /* Return x-position of terminating
+ * character here. */
+ int *nextCPtr; /* Return byte position of terminating
+ character in source. */
+{
+ register char *p; /* Current character. */
+ register int c;
+ char *term; /* Pointer to most recent character that
+ * may legally be a terminating character. */
+ int termX; /* X-position just after term. */
+ int curX; /* X-position corresponding to p. */
+ int newX; /* X-position corresponding to p+1. */
+ int rem;
+#if CK_USE_UTF
+ int n, m, srcRead, dstWrote, dstChars, nChars = 0;
+ Tcl_UniChar uch;
+ char buf[TCL_UTF_MAX], buf2[TCL_UTF_MAX];
+
+ /*
+ * Scan the input string one character at a time, until a character
+ * is found that crosses maxX.
+ */
+
+ newX = curX = startX;
+ termX = 0;
+ term = source;
+ for (p = source; *p != '\0' && maxChars > 0;) {
+ char *p2;
+
+ n = Tcl_UtfToUniChar(p, &uch);
+ p2 = p + n;
+ ++nChars;
+ maxChars -= n;
+ m = Tcl_UniCharToUtf(uch, buf);
+ Tcl_UtfToExternal(NULL, mainPtr->isoEncoding, buf, m,
+ TCL_ENCODING_START | TCL_ENCODING_END,
+ NULL, buf2, sizeof (buf2), &srcRead,
+ &dstWrote, &dstChars);
+ if (buf2[0] == '\0') {
+ buf2[0] = '?';
+ }
+ c = buf2[0] & 0xFF;
+ if ((CHARTYPE(c).type == NORMAL) || (CHARTYPE(c).type == REPLACE) ||
+ (CHARTYPE(c).type == GCHAR)) {
+ newX += CHARTYPE(c).width;
+ } else if (CHARTYPE(c).type == TAB) {
+ if (!(flags & CK_IGNORE_TABS)) {
+ newX += 8;
+ rem = (newX - tabOrigin) % 8;
+ if (rem < 0) {
+ rem += 8;
+ }
+ newX -= rem;
+ }
+ } else if (CHARTYPE(c).type == NEWLINE) {
+ if (flags & CK_NEWLINES_NOT_SPECIAL) {
+ newX += CHARTYPE(c).width;
+ } else {
+ break;
+ }
+ }
+ if (newX > maxX) {
+ break;
+ }
+ p = p2;
+ if (maxChars > 1) {
+ n = Tcl_UtfToUniChar(p, &uch);
+ m = Tcl_UniCharToUtf(uch, buf);
+ Tcl_UtfToExternal(NULL, mainPtr->isoEncoding, buf, m,
+ TCL_ENCODING_START | TCL_ENCODING_END,
+ NULL, buf2, sizeof (buf2), &srcRead,
+ &dstWrote, &dstChars);
+ if (buf2[0] == '\0') {
+ buf2[0] = '?';
+ }
+ c = buf2[0] & 0xff;
+ } else {
+ c = 0;
+ }
+ if (isspace(c) || (c == 0)) {
+ term = p2;
+ termX = newX;
+ }
+ curX = newX;
+ }
+
+ /*
+ * P points to the first character that doesn't fit in the desired
+ * span. Use the flags to figure out what to return.
+ */
+
+ if ((flags & CK_PARTIAL_OK) && (curX < maxX)) {
+ curX = newX;
+ n = Tcl_UtfToUniChar(p, &uch);
+ p += n;
+ ++nChars;
+ }
+ if ((flags & CK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
+ && !isspace((unsigned char) *term)) {
+ term = p;
+ termX = curX;
+ if (term == source) {
+ n = Tcl_UtfToUniChar(term, &uch);
+ term += n;
+ ++nChars;
+ }
+ } else if ((maxChars == 0) || !(flags & CK_WHOLE_WORDS)) {
+ term = p;
+ termX = curX;
+ }
+ *nextXPtr = termX;
+ *nextCPtr = term - source;
+ return nChars;
+#else
+ /*
+ * Scan the input string one character at a time, until a character
+ * is found that crosses maxX.
+ */
+
+ newX = curX = startX;
+ termX = 0;
+ term = source;
+ for (p = source, c = *p & 0xff; c != '\0' && maxChars > 0;
+ p++, maxChars--) {
+ if ((CHARTYPE(c).type == NORMAL) || (CHARTYPE(c).type == REPLACE) ||
+ (CHARTYPE(c).type == GCHAR)) {
+ newX += CHARTYPE(c).width;
+ } else if (CHARTYPE(c).type == TAB) {
+ if (!(flags & CK_IGNORE_TABS)) {
+ newX += 8;
+ rem = (newX - tabOrigin) % 8;
+ if (rem < 0) {
+ rem += 8;
+ }
+ newX -= rem;
+ }
+ } else if (CHARTYPE(c).type == NEWLINE) {
+ if (flags & CK_NEWLINES_NOT_SPECIAL) {
+ newX += CHARTYPE(c).width;
+ } else {
+ break;
+ }
+ }
+ if (newX > maxX) {
+ break;
+ }
+ if (maxChars > 1) {
+ c = p[1] & 0xff;
+ } else {
+ c = 0;
+ }
+ if (isspace(c) || (c == 0)) {
+ term = p+1;
+ termX = newX;
+ }
+ curX = newX;
+ }
+
+ /*
+ * P points to the first character that doesn't fit in the desired
+ * span. Use the flags to figure out what to return.
+ */
+
+ if ((flags & CK_PARTIAL_OK) && (curX < maxX)) {
+ curX = newX;
+ p++;
+ }
+ if ((flags & CK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
+ && !isspace((unsigned char) *term)) {
+ term = p;
+ termX = curX;
+ if (term == source) {
+ term++;
+ termX = newX;
+ }
+ } else if ((maxChars == 0) || !(flags & CK_WHOLE_WORDS)) {
+ term = p;
+ termX = curX;
+ }
+ *nextXPtr = termX;
+ *nextCPtr = termX;
+ return term - source;
+#endif
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkDisplayChars --
+ *
+ * Draw a string of characters on the screen, converting
+ * tabs to the right number of spaces and control characters
+ * to sequences of the form "\xhh" where hh are two hex
+ * digits.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkDisplayChars(mainPtr, window, string, numChars, x, y, tabOrigin, flags)
+ CkMainInfo *mainPtr; /* Needed for encoding. */
+ WINDOW *window; /* Curses window. */
+ char *string; /* Characters to be displayed. */
+ int numChars; /* Number of characters to display from
+ * string. */
+ int x, y; /* Coordinates at which to draw string. */
+ int tabOrigin; /* X-location that serves as "origin" for
+ * tab stops. */
+ int flags; /* Flags to control display. Only
+ * CK_NEWLINES_NOT_SPECIAL, CK_IGNORE_TABS
+ * and CK_FILL_UNTIL_EOL are supported right
+ * now. See CkMeasureChars for information
+ * about it. */
+{
+ register char *p; /* Current character being scanned. */
+ register int c;
+ int startX; /* X-coordinate corresponding to start. */
+ int curX; /* X-coordinate corresponding to p. */
+ char replace[10];
+ int rem, dummy, maxX;
+
+#if CK_USE_UTF
+ string = MakeISO(mainPtr, string, numChars, &numChars);
+#endif
+
+ /*
+ * Scan the string one character at a time and display the
+ * character.
+ */
+
+ getmaxyx(window, dummy, maxX);
+ maxX -= x;
+ if (numChars > maxX)
+ numChars = maxX;
+ p = string;
+ if (x < 0) {
+ numChars += x;
+ p -= x;
+ x = 0;
+ }
+ wmove(window, y, x);
+ startX = curX = x;
+ for (; numChars > 0; numChars--, p++) {
+ c = *p & 0xff;
+ if (c == '\0')
+ break;
+ if (CHARTYPE(c).type == NORMAL) {
+ waddch(window, c);
+ startX++;
+ continue;
+ }
+ if (CHARTYPE(c).type == TAB) {
+ if (!(flags & CK_IGNORE_TABS)) {
+ curX += 8;
+ rem = (curX - tabOrigin) % 8;
+ if (rem < 0) {
+ rem += 8;
+ }
+ curX -= rem;
+ }
+ while (startX < curX) {
+ waddch(window, ' ');
+ startX++;
+ }
+ continue;
+ } else if (CHARTYPE(c).type == GCHAR) {
+ int gchar;
+
+ if (Ck_GetGChar(NULL, gcharTab[c - 0x81], &gchar) != TCL_OK)
+ goto replaceChar;
+ waddch(window, gchar);
+ startX++;
+ continue;
+ } else if (CHARTYPE(c).type == REPLACE || (CHARTYPE(c).type == NEWLINE
+ && (flags & CK_NEWLINES_NOT_SPECIAL))) {
+replaceChar:
+ if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
+ replace[0] = '\\';
+ replace[1] = mapChars[c];
+ replace[2] = '\0';
+ waddstr(window, replace);
+ curX += 2;
+ } else {
+ replace[0] = '\\';
+ replace[1] = 'x';
+ replace[2] = hexChars[(c >> 4) & 0xf];
+ replace[3] = hexChars[c & 0xf];
+ replace[4] = '\0';
+ waddstr(window, replace);
+ curX += 4;
+ }
+ } else if (CHARTYPE(c).type == NEWLINE) {
+ y++;
+ wmove(window, y, x);
+ curX = x;
+ }
+ startX = curX;
+ }
+ if (flags & CK_FILL_UNTIL_EOL) {
+ while (startX < maxX) {
+ waddch(window, ' ');
+ startX++;
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * CkUnderlineChars --
+ *
+ * Draw a range of string of characters on the screen,
+ * converting tabs to the right number of spaces and control
+ * characters to sequences of the form "\xhh" where hh are two hex
+ * digits.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+CkUnderlineChars(mainPtr, window, string, numChars, x, y, tabOrigin,
+ flags, first, last)
+ CkMainInfo *mainPtr; /* Needed for encoding. */
+ WINDOW *window; /* Curses window. */
+ char *string; /* Characters to be displayed. */
+ int numChars; /* Number of characters to display from
+ * string. */
+ int x, y; /* Coordinates at which to draw string. */
+ int tabOrigin; /* X-location that serves as "origin" for
+ * tab stops. */
+ int flags; /* Flags to control display. Only
+ * CK_NEWLINES_NOT_SPECIAL, CK_IGNORE_TABS
+ * and CK_FILL_UNTIL_EOL are supported right
+ * now. See CkMeasureChars for information
+ * about it. */
+ int first, last; /* Range: First and last characters to
+ * display. */
+{
+ register char *p; /* Current character being scanned. */
+ register int c, count;
+ int startX; /* X-coordinate corresponding to start. */
+ int curX; /* X-coordinate corresponding to p. */
+ char replace[10];
+ int rem, dummy, maxX;
+
+#if CK_USE_UTF
+ string = MakeISO(mainPtr, string, numChars, &numChars);
+#endif
+
+ /*
+ * Scan the string one character at a time and display the
+ * character.
+ */
+
+ count = 0;
+ getmaxyx(window, dummy, maxX);
+ maxX -= x;
+ if (numChars > maxX)
+ numChars = maxX;
+ p = string;
+ if (x < 0) {
+ numChars += x;
+ count += x;
+ p -= x;
+ x = 0;
+ }
+ wmove(window, y, x);
+ startX = curX = x;
+ for (; numChars > 0 && count <= last; numChars--, count++, p++) {
+ c = *p & 0xff;
+ if (c == '\0')
+ break;
+ if (CHARTYPE(c).type == NORMAL) {
+ startX++;
+ if (count >= first)
+ waddch(window, c);
+ else
+ wmove(window, y, startX);
+ continue;
+ }
+ if (CHARTYPE(c).type == TAB) {
+ if (!(flags & CK_IGNORE_TABS)) {
+ curX += 8;
+ rem = (curX - tabOrigin) % 8;
+ if (rem < 0) {
+ rem += 8;
+ }
+ curX -= rem;
+ }
+ while (startX < curX) {
+ startX++;
+ if (count >= first)
+ waddch(window, ' ');
+ else
+ wmove(window, y, startX);
+ }
+ continue;
+ } else if (CHARTYPE(c).type == GCHAR) {
+ int gchar;
+
+ if (Ck_GetGChar(NULL, gcharTab[c - 0x81], &gchar) != TCL_OK)
+ goto replaceChar;
+ startX++;
+ if (count >= first)
+ waddch(window, gchar);
+ else
+ wmove(window, y, startX);
+ continue;
+ } else if (CHARTYPE(c).type == REPLACE || (CHARTYPE(c).type == NEWLINE
+ && (flags & CK_NEWLINES_NOT_SPECIAL))) {
+replaceChar:
+ if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
+ replace[0] = '\\';
+ replace[1] = mapChars[c];
+ replace[2] = '\0';
+ curX += 2;
+ if (count >= first)
+ waddstr(window, replace);
+ else
+ wmove(window, y, curX);
+ } else {
+ replace[0] = '\\';
+ replace[1] = 'x';
+ replace[2] = hexChars[(c >> 4) & 0xf];
+ replace[3] = hexChars[c & 0xf];
+ replace[4] = '\0';
+ curX += 4;
+ if (count >= first)
+ waddstr(window, replace);
+ else
+ wmove(window, y, curX);
+ }
+ } else if (CHARTYPE(c).type == NEWLINE) {
+ y++;
+ wmove(window, y, x);
+ curX = x;
+ }
+ startX = curX;
+ }
+}
+
--- /dev/null
+/*
+ * ckWindow.c --
+ *
+ * This file provides basic window-manipulation procedures.
+ *
+ * Copyright (c) 1995-2001 Christian Werner.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+#ifdef HAVE_GPM
+#include "gpm.h"
+#endif
+
+/*
+ * Main information.
+ */
+
+CkMainInfo *ckMainInfo = NULL;
+
+#ifdef __WIN32__
+
+/*
+ * Curses input event handling information.
+ */
+
+typedef struct {
+ HANDLE stdinHandle;
+ HWND hwnd;
+ HANDLE thread;
+ CkMainInfo *mainPtr;
+} InputInfo;
+
+static InputInfo inputInfo = {
+ INVALID_HANDLE_VALUE,
+ NULL,
+ INVALID_HANDLE_VALUE,
+ NULL
+};
+
+static void InputSetup _ANSI_ARGS_((InputInfo *inputInfo));
+static void InputExit _ANSI_ARGS_((ClientData clientData));
+static void InputThread _ANSI_ARGS_((void *arg));
+static LRESULT CALLBACK InputHandler _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam,
+ LPARAM lParam));
+static void InputHandler2 _ANSI_ARGS_((ClientData clientData));
+#endif
+
+/*
+ * The variables below hold several uid's that are used in many places
+ * in the toolkit.
+ */
+
+Ck_Uid ckDisabledUid = NULL;
+Ck_Uid ckActiveUid = NULL;
+Ck_Uid ckNormalUid = NULL;
+
+/*
+ * The following structure defines all of the commands supported by
+ * the toolkit, and the C procedures that execute them.
+ */
+
+typedef int (CkCmdProc) _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char **argv));
+
+typedef struct {
+ char *name; /* Name of command. */
+ CkCmdProc *cmdProc; /* Command procedure. */
+} CkCmd;
+
+CkCmd commands[] = {
+ /*
+ * Commands that are part of the intrinsics:
+ */
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ {"after", Tk_AfterCmd},
+#endif
+ {"bell", Ck_BellCmd},
+ {"bind", Ck_BindCmd},
+ {"bindtags", Ck_BindtagsCmd},
+ {"curses", Ck_CursesCmd},
+ {"destroy", Ck_DestroyCmd},
+ {"exit", Ck_ExitCmd},
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ {"fileevent", Tk_FileeventCmd},
+#endif
+ {"focus", Ck_FocusCmd},
+ {"grid", Ck_GridCmd},
+ {"lower", Ck_LowerCmd},
+ {"option", Ck_OptionCmd},
+ {"pack", Ck_PackCmd},
+ {"place", Ck_PlaceCmd},
+ {"raise", Ck_RaiseCmd},
+ {"recorder", Ck_RecorderCmd},
+ {"tkwait", Ck_TkwaitCmd},
+ {"update", Ck_UpdateCmd},
+ {"winfo", Ck_WinfoCmd},
+
+ /*
+ * Widget-creation commands.
+ */
+
+ {"button", Ck_ButtonCmd},
+ {"checkbutton", Ck_ButtonCmd},
+ {"entry", Ck_EntryCmd},
+ {"frame", Ck_FrameCmd},
+ {"label", Ck_ButtonCmd},
+ {"listbox", Ck_ListboxCmd},
+ {"menu", Ck_MenuCmd},
+ {"menubutton", Ck_MenubuttonCmd},
+ {"message", Ck_MessageCmd},
+ {"radiobutton", Ck_ButtonCmd},
+ {"scrollbar", Ck_ScrollbarCmd},
+ {"text", Ck_TextCmd},
+ {"toplevel", Ck_FrameCmd},
+ {"tree", Ck_TreeCmd},
+
+ {(char *) NULL, (CkCmdProc *) NULL}
+};
+
+/*
+ * Static procedures of this module.
+ */
+
+static void UnlinkWindow _ANSI_ARGS_((CkWindow *winPtr));
+static void UnlinkToplevel _ANSI_ARGS_((CkWindow *winPtr));
+static void ChangeToplevelFocus _ANSI_ARGS_((CkWindow *winPtr));
+static void DoRefresh _ANSI_ARGS_((ClientData clientData));
+static void RefreshToplevels _ANSI_ARGS_((CkWindow *winPtr));
+static void RefreshThem _ANSI_ARGS_((CkWindow *winPtr));
+static void UpdateHWCursor _ANSI_ARGS_((CkMainInfo *mainPtr));
+static CkWindow *GetWindowXY _ANSI_ARGS_((CkWindow *winPtr, int *xPtr,
+ int *yPtr));
+static int DeadAppCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int ExecCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int PutsCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int CloseCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int FlushCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int ReadCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int GetsCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+
+/*
+ * Some plain Tcl commands are handled specially.
+ */
+
+CkCmd redirCommands[] = {
+#ifndef __WIN32__
+ {"exec", ExecCmd},
+#endif
+ {"puts", PutsCmd},
+ {"close", CloseCmd},
+ {"flush", FlushCmd},
+ {"read", ReadCmd},
+ {"gets", GetsCmd},
+ {(char *) NULL, (CkCmdProc *) NULL}
+};
+
+/*
+ * The following structure is used as ClientData for redirected
+ * plain Tcl commands.
+ */
+
+typedef struct {
+ CkMainInfo *mainPtr;
+ Tcl_CmdInfo cmdInfo;
+} RedirInfo;
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * NewWindow --
+ *
+ * This procedure creates and initializes a CkWindow structure.
+ *
+ * Results:
+ * The return value is a pointer to the new window.
+ *
+ * Side effects:
+ * A new window structure is allocated and all its fields are
+ * initialized.
+ *
+ *--------------------------------------------------------------
+ */
+
+static CkWindow *
+NewWindow(parentPtr)
+ CkWindow *parentPtr;
+{
+ CkWindow *winPtr;
+
+ winPtr = (CkWindow *) ckalloc(sizeof (CkWindow));
+ winPtr->window = NULL;
+ winPtr->childList = NULL;
+ winPtr->lastChildPtr = NULL;
+ winPtr->parentPtr = NULL;
+ winPtr->nextPtr = NULL;
+ winPtr->topLevPtr = NULL;
+ winPtr->mainPtr = NULL;
+ winPtr->pathName = NULL;
+ winPtr->nameUid = NULL;
+ winPtr->classUid = NULL;
+ winPtr->handlerList = NULL;
+ winPtr->tagPtr = NULL;
+ winPtr->numTags = 0;
+ winPtr->focusPtr = NULL;
+ winPtr->geomMgrPtr = NULL;
+ winPtr->geomData = NULL;
+ winPtr->optionLevel = -1;
+ winPtr->reqWidth = winPtr->reqHeight = 1;
+ winPtr->x = winPtr->y = 0;
+ winPtr->width = winPtr->height = 1;
+ winPtr->fg = COLOR_WHITE;
+ winPtr->bg = COLOR_BLACK;
+ winPtr->attr = A_NORMAL;
+ winPtr->flags = 0;
+
+ return winPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameWindow --
+ *
+ * This procedure is invoked to give a window a name and insert
+ * the window into the hierarchy associated with a particular
+ * application.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * See above.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+NameWindow(interp, winPtr, parentPtr, name)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ CkWindow *winPtr; /* Window that is to be named and inserted. */
+ CkWindow *parentPtr; /* Pointer to logical parent for winPtr
+ * (used for naming, options, etc.). */
+ char *name; /* Name for winPtr; must be unique among
+ * parentPtr's children. */
+{
+#define FIXED_SIZE 200
+ char staticSpace[FIXED_SIZE];
+ char *pathName;
+ int new;
+ Tcl_HashEntry *hPtr;
+ int length1, length2;
+
+ /*
+ * Setup all the stuff except name right away, then do the name stuff
+ * last. This is so that if the name stuff fails, everything else
+ * will be properly initialized (needed to destroy the window cleanly
+ * after the naming failure).
+ */
+ winPtr->parentPtr = parentPtr;
+ winPtr->nextPtr = NULL;
+ if (parentPtr->childList == NULL) {
+ parentPtr->lastChildPtr = winPtr;
+ parentPtr->childList = winPtr;
+ } else {
+ parentPtr->lastChildPtr->nextPtr = winPtr;
+ parentPtr->lastChildPtr = winPtr;
+ }
+ winPtr->mainPtr = parentPtr->mainPtr;
+ winPtr->nameUid = Ck_GetUid(name);
+
+ /*
+ * Don't permit names that start with an upper-case letter: this
+ * will just cause confusion with class names in the option database.
+ */
+
+ if (isupper((unsigned char) name[0])) {
+ Tcl_AppendResult(interp,
+ "window name starts with an upper-case letter: \"",
+ name, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * To permit names of arbitrary length, must be prepared to malloc
+ * a buffer to hold the new path name. To run fast in the common
+ * case where names are short, use a fixed-size buffer on the
+ * stack.
+ */
+
+ length1 = strlen(parentPtr->pathName);
+ length2 = strlen(name);
+ if ((length1+length2+2) <= FIXED_SIZE) {
+ pathName = staticSpace;
+ } else {
+ pathName = (char *) ckalloc((unsigned) (length1+length2+2));
+ }
+ if (length1 == 1) {
+ pathName[0] = '.';
+ strcpy(pathName+1, name);
+ } else {
+ strcpy(pathName, parentPtr->pathName);
+ pathName[length1] = '.';
+ strcpy(pathName+length1+1, name);
+ }
+ hPtr = Tcl_CreateHashEntry(&parentPtr->mainPtr->nameTable, pathName, &new);
+ if (pathName != staticSpace) {
+ ckfree(pathName);
+ }
+ if (!new) {
+ Tcl_AppendResult(interp, "window name \"", name,
+ "\" already exists in parent", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetHashValue(hPtr, winPtr);
+ winPtr->pathName = Tcl_GetHashKey(&parentPtr->mainPtr->nameTable, hPtr);
+ Tcl_CreateHashEntry(&parentPtr->mainPtr->winTable, (char *) winPtr, &new);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_MainWindow --
+ *
+ * Returns the main window for an application.
+ *
+ * Results:
+ * If interp is associated with the main window, the main
+ * window is returned. Otherwise NULL is returned and an
+ * error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_MainWindow(interp)
+ Tcl_Interp *interp; /* Interpreter that embodies application,
+ * also used for error reporting. */
+{
+ if (ckMainInfo == NULL || ckMainInfo->interp != interp) {
+ if (interp != NULL)
+ interp->result = "no main window for application.";
+ return NULL;
+ }
+ return ckMainInfo->winPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_CreateMainWindow --
+ *
+ * Make the main window.
+ *
+ * Results:
+ * The return value is a token for the new window, or NULL if
+ * an error prevented the new window from being created. If
+ * NULL is returned, an error message will be left in
+ * interp->result.
+ *
+ * Side effects:
+ * A new window structure is allocated locally.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_CreateMainWindow(interp, className)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ char *className; /* Class name of the new main window. */
+{
+ int dummy;
+ Tcl_HashEntry *hPtr;
+ CkMainInfo *mainPtr;
+ CkWindow *winPtr;
+ CkCmd *cmdPtr;
+#ifdef SIGTSTP
+#ifdef HAVE_SIGACTION
+ struct sigaction oldsig, newsig;
+#else
+ Ck_SignalProc sigproc;
+#endif
+#endif
+#ifdef NCURSES_MOUSE_VERSION
+ MEVENT mEvent;
+#endif
+ char *term;
+ int isxterm = 0;
+
+ /*
+ * For now, only one main window may exists for the application.
+ */
+ if (ckMainInfo != NULL)
+ return NULL;
+
+ /*
+ * Create the basic CkWindow structure.
+ */
+
+ winPtr = NewWindow(NULL);
+
+ /*
+ * Create the CkMainInfo structure for this application, and set
+ * up name-related information for the new window.
+ */
+
+ mainPtr = (CkMainInfo *) ckalloc(sizeof(CkMainInfo));
+ mainPtr->winPtr = winPtr;
+ mainPtr->interp = interp;
+ Tcl_InitHashTable(&mainPtr->nameTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&mainPtr->winTable, TCL_ONE_WORD_KEYS);
+ mainPtr->topLevPtr = NULL;
+ mainPtr->focusPtr = winPtr;
+ mainPtr->bindingTable = Ck_CreateBindingTable(interp);
+ mainPtr->optionRootPtr = NULL;
+ mainPtr->refreshCount = 0;
+ mainPtr->refreshDelay = 0;
+ mainPtr->lastRefresh = 0;
+ mainPtr->refreshTimer = NULL;
+ mainPtr->flags = 0;
+ ckMainInfo = mainPtr;
+ winPtr->mainPtr = mainPtr;
+ winPtr->nameUid = Ck_GetUid(".");
+ winPtr->classUid = Ck_GetUid("Main"); /* ??? */
+ winPtr->flags |= CK_TOPLEVEL;
+ hPtr = Tcl_CreateHashEntry(&mainPtr->nameTable, (char *) winPtr->nameUid,
+ &dummy);
+ Tcl_SetHashValue(hPtr, winPtr);
+ winPtr->pathName = Tcl_GetHashKey(&mainPtr->nameTable, hPtr);
+ Tcl_CreateHashEntry(&mainPtr->winTable, (char *) winPtr, &dummy);
+
+ ckNormalUid = Ck_GetUid("normal");
+ ckDisabledUid = Ck_GetUid("disabled");
+ ckActiveUid = Ck_GetUid("active");
+
+#if CK_USE_UTF
+#ifdef __WIN32__
+ {
+ char enc[32], *envcp = getenv("CK_USE_ENCODING");
+ unsigned int cp = GetConsoleCP();
+
+ if (envcp && strncmp(envcp, "cp", 2) == 0) {
+ cp = atoi(envcp + 2);
+ SetConsoleCP(cp);
+ cp = GetConsoleCP();
+ }
+ if (GetConsoleOutputCP() != cp) {
+ SetConsoleOutputCP(cp);
+ }
+ sprintf(enc, "cp%d", cp);
+ mainPtr->isoEncoding = Tcl_GetEncoding(NULL, enc);
+ }
+#else
+ /*
+ * Use default system encoding as suggested by
+ * Anton Kovalenko <a_kovalenko@mtu-net.ru>.
+ * May be overriden by environment variable.
+ */
+ mainPtr->isoEncoding = Tcl_GetEncoding(NULL, getenv("CK_USE_ENCODING"));
+ if (mainPtr->isoEncoding == NULL) {
+ mainPtr->isoEncoding = Tcl_GetEncoding(NULL, NULL);
+ }
+#endif
+ if (mainPtr->isoEncoding == NULL) {
+ panic("standard encoding not found");
+ }
+ Tcl_DStringInit(&mainPtr->isoBuffer);
+#endif
+
+ /* Curses related initialization */
+
+#ifdef SIGTSTP
+ /* This is essential for ncurses-1.9.4 */
+#ifdef HAVE_SIGACTION
+ newsig.sa_handler = SIG_IGN;
+ sigfillset(&newsig.sa_mask);
+ newsig.sa_flags = 0;
+ sigaction(SIGTSTP, &newsig, &oldsig);
+#else
+ sigproc = (Ck_SignalProc) signal(SIGTSTP, SIG_IGN);
+#endif
+#endif
+ if (initscr() == (WINDOW *) ERR) {
+ ckfree((char *) winPtr);
+ return NULL;
+ }
+#ifdef SIGTSTP
+ /* This is essential for ncurses-1.9.4 */
+#ifdef HAVE_SIGACTION
+ sigaction(SIGTSTP, &oldsig, NULL);
+#else
+ signal(SIGTSTP, sigproc);
+#endif
+#endif
+ raw();
+ noecho();
+ idlok(stdscr, TRUE);
+ scrollok(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ nodelay(stdscr, TRUE);
+ meta(stdscr, TRUE);
+ nonl();
+ mainPtr->maxWidth = COLS;
+ mainPtr->maxHeight = LINES;
+ winPtr->width = mainPtr->maxWidth;
+ winPtr->height = mainPtr->maxHeight;
+ winPtr->window = newwin(winPtr->height, winPtr->width, 0, 0);
+ if (has_colors()) {
+ start_color();
+ mainPtr->flags |= CK_HAS_COLOR;
+ }
+#ifdef NCURSES_MOUSE_VERSION
+ mouseinterval(1);
+ mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED |
+ BUTTON2_PRESSED | BUTTON2_RELEASED |
+ BUTTON3_PRESSED | BUTTON3_RELEASED, NULL);
+ mainPtr->flags |= (getmouse(&mEvent) != ERR) ? CK_HAS_MOUSE : 0;
+#endif /* NCURSES_MOUSE_VERSION */
+
+#if defined(__WIN32__) || defined(__DJGPP__)
+ mouse_set(BUTTON1_PRESSED | BUTTON1_RELEASED |
+ BUTTON2_PRESSED | BUTTON2_RELEASED |
+ BUTTON3_PRESSED | BUTTON3_RELEASED);
+ mainPtr->flags |= CK_HAS_MOUSE;
+ term = "win32";
+#else
+ term = getenv("TERM");
+ isxterm = strncmp(term, "xterm", 5) == 0 ||
+ strncmp(term, "rxvt", 4) == 0 ||
+ strncmp(term, "kterm", 5) == 0 ||
+ strncmp(term, "color_xterm", 11) == 0 ||
+ (term[0] != '\0' && strncmp(term + 1, "xterm", 5) == 0);
+ if (!(mainPtr->flags & CK_HAS_MOUSE) && isxterm) {
+ mainPtr->flags |= CK_HAS_MOUSE | CK_MOUSE_XTERM;
+ fflush(stdout);
+ fputs("\033[?1000h", stdout);
+ fflush(stdout);
+ }
+#endif /* __WIN32__ */
+
+#ifdef HAVE_GPM
+ /*
+ * Some ncurses aren't compiled with GPM support built in,
+ * therefore by setting the following environment variable
+ * usage of GPM can be turned on.
+ */
+ if (!isxterm && (mainPtr->flags & CK_HAS_MOUSE)) {
+ char *forcegpm = getenv("CK_USE_GPM");
+
+ if (forcegpm && strchr("YyTt123456789", forcegpm[0])) {
+ mainPtr->flags &= ~CK_HAS_MOUSE;
+ }
+ }
+ if (!isxterm && !(mainPtr->flags & CK_HAS_MOUSE)) {
+ int fd;
+ Gpm_Connect conn;
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ EXTERN int CkHandleGPMInput _ANSI_ARGS_((ClientData clientData,
+ int mask, int flags));
+#else
+ EXTERN void CkHandleGPMInput _ANSI_ARGS_((ClientData clientData,
+ int mask));
+#endif
+
+ conn.eventMask = GPM_DOWN | GPM_UP | GPM_MOVE;
+ conn.defaultMask = 0;
+ conn.minMod = 0;
+ conn.maxMod = 0;
+ fd = Gpm_Open(&conn, 0);
+ if (fd >= 0) {
+ mainPtr->flags |= CK_HAS_MOUSE;
+#if (TCL_MAJOR_VERSION >= 8)
+ mainPtr->mouseData = (ClientData) fd;
+ Tcl_CreateFileHandler(fd, TCL_READABLE,
+ CkHandleGPMInput, (ClientData) mainPtr);
+#else
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ mainPtr->mouseData = (ClientData) fd;
+ Tk_CreateFileHandler2(fd, CkHandleGPMInput, (ClientData) mainPtr);
+#else
+ mainPtr->mouseData = (ClientData)
+ Tcl_GetFile((ClientData) fd, TCL_UNIX_FD);
+ Tcl_CreateFileHandler((Tcl_File) mainPtr->mouseData, TCL_READABLE,
+ CkHandleGPMInput, (ClientData) mainPtr);
+#endif
+#endif
+ }
+ }
+#endif /* HAVE_GPM */
+
+#ifdef __WIN32__
+ /* PDCurses specific !!! */
+ inputInfo.mainPtr = mainPtr;
+ inputInfo.stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
+ typeahead(-1);
+ SetConsoleMode(inputInfo.stdinHandle,
+ ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
+ InputSetup(&inputInfo);
+#else
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_CreateFileHandler(0,
+ TCL_READABLE, CkHandleInput, (ClientData) mainPtr);
+#else
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ Tk_CreateFileHandler2(0, CkHandleInput, (ClientData) mainPtr);
+#else
+ Tcl_CreateFileHandler(Tcl_GetFile((ClientData) 0, TCL_UNIX_FD),
+ TCL_READABLE, CkHandleInput, (ClientData) mainPtr);
+#endif
+#endif
+#endif
+
+ idlok(winPtr->window, TRUE);
+ scrollok(winPtr->window, FALSE);
+ keypad(winPtr->window, TRUE);
+ nodelay(winPtr->window, TRUE);
+ meta(winPtr->window, TRUE);
+ curs_set(0);
+ while (getch() != ERR) {
+ /* empty loop body. */
+ }
+ winPtr->flags |= CK_MAPPED;
+ Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
+ Ck_ClearToBot(winPtr, 0, 0);
+ Ck_EventuallyRefresh(winPtr);
+
+ /*
+ * Bind in Ck's commands.
+ */
+
+ for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
+ Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
+ (ClientData) winPtr, (Tcl_CmdDeleteProc *) NULL);
+ }
+
+ /*
+ * Redirect some critical Tcl commands to our own procedures
+ */
+ for (cmdPtr = redirCommands; cmdPtr->name != NULL; cmdPtr++) {
+ RedirInfo *redirInfo;
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_DString cmdName;
+ extern int TclRenameCommand _ANSI_ARGS_((Tcl_Interp *interp,
+ char *oldName, char *newName));
+#endif
+ redirInfo = (RedirInfo *) ckalloc(sizeof (RedirInfo));
+ redirInfo->mainPtr = mainPtr;
+ Tcl_GetCommandInfo(interp, cmdPtr->name, &redirInfo->cmdInfo);
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_DStringInit(&cmdName);
+ Tcl_DStringAppend(&cmdName, "____", -1);
+ Tcl_DStringAppend(&cmdName, cmdPtr->name, -1);
+ TclRenameCommand(interp, cmdPtr->name, Tcl_DStringValue(&cmdName));
+ Tcl_DStringFree(&cmdName);
+#endif
+ Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
+ (ClientData) redirInfo, (Tcl_CmdDeleteProc *) free);
+ }
+
+ /*
+ * Set variables for the intepreter.
+ */
+
+#if (TCL_MAJOR_VERSION < 8)
+ if (Tcl_GetVar(interp, "ck_library", TCL_GLOBAL_ONLY) == NULL) {
+ /*
+ * A library directory hasn't already been set, so figure out
+ * which one to use.
+ */
+
+ char *libDir = getenv("CK_LIBRARY");
+
+ if (libDir == NULL) {
+ libDir = CK_LIBRARY;
+ }
+ Tcl_SetVar(interp, "ck_library", libDir, TCL_GLOBAL_ONLY);
+ }
+#endif
+ Tcl_SetVar(interp, "ck_version", CK_VERSION, TCL_GLOBAL_ONLY);
+
+ /*
+ * Make main window into a frame widget.
+ */
+
+ Ck_SetClass(winPtr, className);
+ CkInitFrame(interp, winPtr, 0, NULL);
+ mainPtr->topLevPtr = winPtr;
+ winPtr->focusPtr = winPtr;
+ return winPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_Init --
+ *
+ * This procedure is invoked to add Ck to an interpreter. It
+ * incorporates all of Ck's commands into the interpreter and
+ * creates the main window for a new Ck application.
+ *
+ * Results:
+ * Returns a standard Tcl completion code and sets interp->result
+ * if there is an error.
+ *
+ * Side effects:
+ * Depends on what's in the ck.tcl script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_Init(interp)
+ Tcl_Interp *interp; /* Interpreter to initialize. */
+{
+ CkWindow *mainWindow;
+ char *p, *name, *class;
+ int code;
+ static char initCmd[] =
+#if (TCL_MAJOR_VERSION >= 8)
+"proc init {} {\n\
+ global ck_library ck_version\n\
+ rename init {}\n\
+ tcl_findLibrary ck $ck_version 0 ck.tcl CK_LIBRARY ck_library\n\
+}\n\
+init";
+#else
+"proc init {} {\n\
+ global ck_library ck_version env\n\
+ rename init {}\n\
+ set dirs {}\n\
+ if [info exists env(CK_LIBRARY)] {\n\
+ lappend dirs $env(CK_LIBRARY)\n\
+ }\n\
+ lappend dirs $ck_library\n\
+ lappend dirs [file dirname [info library]]/lib/ck$ck_version\n\
+ catch {lappend dirs [file dirname [file dirname \\\n\
+ [info nameofexecutable]]]/lib/ck$ck_version}\n\
+ set lib ck$ck_version\n\
+ lappend dirs [file dirname [file dirname [pwd]]]/$lib/library\n\
+ lappend dirs [file dirname [file dirname [info library]]]/$lib/library\n\
+ lappend dirs [file dirname [pwd]]/library\n\
+ foreach i $dirs {\n\
+ set ck_library $i\n\
+ if ![catch {uplevel #0 source $i/ck.tcl}] {\n\
+ return\n\
+ }\n\
+ }\n\
+ set msg \"Can't find a usable ck.tcl in the following directories: \n\"\n\
+ append msg \" $dirs\n\"\n\
+ append msg \"This probably means that Ck wasn't installed properly.\n\"\n\
+ error $msg\n\
+}\n\
+init";
+#endif
+
+ p = Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY);
+ if (p == NULL || *p == '\0')
+ p = "Ck";
+ name = strrchr(p, '/');
+ if (name != NULL)
+ name++;
+ else
+ name = p;
+ class = (char *) ckalloc((unsigned) (strlen(name) + 1));
+ strcpy(class, name);
+ class[0] = toupper((unsigned char) class[0]);
+ mainWindow = Ck_CreateMainWindow(interp, class);
+ ckfree(class);
+
+#if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
+ if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL)
+ return TCL_ERROR;
+ code = Tcl_PkgProvide(interp, "Ck", CK_VERSION);
+ if (code != TCL_OK)
+ return TCL_ERROR;
+#endif
+ return Tcl_Eval(interp, initCmd);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_CreateWindow --
+ *
+ * Create a new window as a child of an existing window.
+ *
+ * Results:
+ * The return value is the pointer to the new window.
+ * If an error occurred in creating the window, then an
+ * error message is left in interp->result and NULL is
+ * returned.
+ *
+ * Side effects:
+ * A new window structure is allocated locally. A curses
+ * window is not initially created, but will be created
+ * the first time the window is mapped.
+ *
+ *--------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_CreateWindow(interp, parentPtr, name, toplevel)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting.
+ * Interp->result is assumed to be
+ * initialized by the caller. */
+ CkWindow *parentPtr; /* Parent of new window. */
+ char *name; /* Name for new window. Must be unique
+ * among parent's children. */
+ int toplevel; /* If true, create toplevel window. */
+{
+ CkWindow *winPtr;
+
+ winPtr = NewWindow(parentPtr);
+ if (NameWindow(interp, winPtr, parentPtr, name) != TCL_OK) {
+ Ck_DestroyWindow(winPtr);
+ return NULL;
+ }
+ if (toplevel) {
+ CkWindow *wPtr;
+
+ winPtr->flags |= CK_TOPLEVEL;
+ winPtr->focusPtr = winPtr;
+ if (winPtr->mainPtr->topLevPtr == NULL) {
+ winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
+ winPtr->mainPtr->topLevPtr = winPtr;
+ } else {
+ for (wPtr = winPtr->mainPtr->topLevPtr; wPtr->topLevPtr != NULL;
+ wPtr = wPtr->topLevPtr) {
+ /* Empty loop body. */
+ }
+ winPtr->topLevPtr = wPtr->topLevPtr;
+ wPtr->topLevPtr = winPtr;
+ }
+ }
+ return winPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_CreateWindowFromPath --
+ *
+ * This procedure is similar to Ck_CreateWindow except that
+ * it uses a path name to create the window, rather than a
+ * parent and a child name.
+ *
+ * Results:
+ * The return value is the pointer to the new window.
+ * If an error occurred in creating the window, then an
+ * error message is left in interp->result and NULL is
+ * returned.
+ *
+ * Side effects:
+ * A new window structure is allocated locally. A curses
+ * window is not initially created, but will be created
+ * the first time the window is mapped.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_CreateWindowFromPath(interp, anywin, pathName, toplevel)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting.
+ * Interp->result is assumed to be
+ * initialized by the caller. */
+ CkWindow *anywin; /* Pointer to any window in application
+ * that is to contain new window. */
+ char *pathName; /* Path name for new window within the
+ * application of anywin. The parent of
+ * this window must already exist, but
+ * the window itself must not exist. */
+ int toplevel; /* If true, create toplevel window. */
+{
+#define FIXED_SPACE 5
+ char fixedSpace[FIXED_SPACE+1];
+ char *p;
+ CkWindow *parentPtr, *winPtr;
+ int numChars;
+
+ /*
+ * Strip the parent's name out of pathName (it's everything up
+ * to the last dot). There are two tricky parts: (a) must
+ * copy the parent's name somewhere else to avoid modifying
+ * the pathName string (for large names, space for the copy
+ * will have to be malloc'ed); (b) must special-case the
+ * situation where the parent is ".".
+ */
+
+ p = strrchr(pathName, '.');
+ if (p == NULL) {
+ Tcl_AppendResult(interp, "bad window path name \"", pathName,
+ "\"", (char *) NULL);
+ return NULL;
+ }
+ numChars = p - pathName;
+ if (numChars > FIXED_SPACE) {
+ p = (char *) ckalloc((unsigned) (numChars+1));
+ } else {
+ p = fixedSpace;
+ }
+ if (numChars == 0) {
+ *p = '.';
+ p[1] = '\0';
+ } else {
+ strncpy(p, pathName, numChars);
+ p[numChars] = '\0';
+ }
+
+ /*
+ * Find the parent window.
+ */
+
+ parentPtr = Ck_NameToWindow(interp, p, anywin);
+ if (p != fixedSpace) {
+ ckfree(p);
+ }
+ if (parentPtr == NULL)
+ return NULL;
+
+ /*
+ * Create the window.
+ */
+
+ winPtr = NewWindow(parentPtr);
+ if (NameWindow(interp, winPtr, parentPtr, pathName + numChars + 1)
+ != TCL_OK) {
+ Ck_DestroyWindow(winPtr);
+ return NULL;
+ }
+ if (toplevel) {
+ CkWindow *wPtr;
+
+ winPtr->flags |= CK_TOPLEVEL;
+ winPtr->focusPtr = winPtr;
+ if (winPtr->mainPtr->topLevPtr == NULL) {
+ winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
+ winPtr->mainPtr->topLevPtr = winPtr;
+ } else {
+ for (wPtr = winPtr->mainPtr->topLevPtr; wPtr->topLevPtr != NULL;
+ wPtr = wPtr->topLevPtr) {
+ /* Empty loop body. */
+ }
+ winPtr->topLevPtr = wPtr->topLevPtr;
+ wPtr->topLevPtr = winPtr;
+ }
+ }
+ return winPtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_DestroyWindow --
+ *
+ * Destroy an existing window. After this call, the caller
+ * should never again use the pointer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is deleted, along with all of its children.
+ * Relevant callback procedures are invoked.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_DestroyWindow(winPtr)
+ CkWindow *winPtr; /* Window to destroy. */
+{
+ CkWindowEvent event;
+ Tcl_HashEntry *hPtr;
+#ifdef NCURSES_MOUSE_VERSION
+ MEVENT mEvent;
+#endif
+
+ if (winPtr->flags & CK_ALREADY_DEAD)
+ return;
+ winPtr->flags |= CK_ALREADY_DEAD;
+
+ /*
+ * Recursively destroy children. The CK_RECURSIVE_DESTROY
+ * flags means that the child's window needn't be explicitly
+ * destroyed (the destroy of the parent already did it), nor
+ * does it need to be removed from its parent's child list,
+ * since the parent is being destroyed too.
+ */
+
+ while (winPtr->childList != NULL) {
+ winPtr->childList->flags |= CK_RECURSIVE_DESTROY;
+ Ck_DestroyWindow(winPtr->childList);
+ }
+ if (winPtr->mainPtr->focusPtr == winPtr) {
+ event.type = CK_EV_FOCUSOUT;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
+ }
+ if (winPtr->window != NULL) {
+ delwin(winPtr->window);
+ winPtr->window = NULL;
+ }
+ CkOptionDeadWindow(winPtr);
+ event.type = CK_EV_DESTROY;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
+ if (winPtr->tagPtr != NULL) {
+ CkFreeBindingTags(winPtr);
+ }
+ UnlinkWindow(winPtr);
+ CkEventDeadWindow(winPtr);
+ hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->winTable, (char *) winPtr);
+ if (hPtr != NULL)
+ Tcl_DeleteHashEntry(hPtr);
+ if (winPtr->pathName != NULL) {
+ CkMainInfo *mainPtr = winPtr->mainPtr;
+
+ Ck_DeleteAllBindings(mainPtr->bindingTable,
+ (ClientData) winPtr->pathName);
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&mainPtr->nameTable,
+ winPtr->pathName));
+ if (mainPtr->winPtr == winPtr) {
+ CkCmd *cmdPtr;
+
+ for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++)
+ if (cmdPtr->cmdProc != Ck_ExitCmd)
+ Tcl_CreateCommand(mainPtr->interp, cmdPtr->name,
+ DeadAppCmd, (ClientData) NULL,
+ (Tcl_CmdDeleteProc *) NULL);
+ Tcl_DeleteHashTable(&mainPtr->nameTable);
+ Ck_DeleteBindingTable(mainPtr->bindingTable);
+
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(0, NULL);
+ mainPtr->flags &= (getmouse(&mEvent) != ERR) ? ~CK_HAS_MOUSE : ~0;
+#endif /* NCURSES_MOUSE_VERSION */
+
+ if (mainPtr->flags & CK_HAS_MOUSE) {
+#if defined(__WIN32__) || defined(__DJGPP__)
+ mouse_set(0);
+#endif
+ if (mainPtr->flags & CK_MOUSE_XTERM) {
+ fflush(stdout);
+ fputs("\033[?1000l", stdout);
+ fflush(stdout);
+ } else {
+#ifdef HAVE_GPM
+#if (TCL_MAJOR_VERSION >= 8)
+ Tcl_DeleteFileHandler((int) mainPtr->mouseData);
+#else
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ Tk_DeleteFileHandler((int) mainPtr->mouseData);
+#else
+ Tcl_DeleteFileHandler((Tcl_File) mainPtr->mouseData);
+#endif
+#endif
+ Gpm_Close();
+#endif
+ }
+ }
+
+ curs_set(1);
+ if (mainPtr->flags & CK_NOCLR_ON_EXIT) {
+ wattrset(stdscr, A_NORMAL);
+ } else {
+ wclear(stdscr);
+ wrefresh(stdscr);
+ }
+ endwin();
+#if CK_USE_UTF
+ Tcl_DStringFree(&mainPtr->isoBuffer);
+ Tcl_FreeEncoding(mainPtr->isoEncoding);
+#endif
+ ckfree((char *) mainPtr);
+ ckMainInfo = NULL;
+ goto done;
+ }
+ }
+ if (winPtr->flags & CK_TOPLEVEL) {
+ UnlinkToplevel(winPtr);
+ ChangeToplevelFocus(winPtr->mainPtr->topLevPtr);
+ } else if (winPtr->mainPtr->focusPtr == winPtr) {
+ winPtr->mainPtr->focusPtr = winPtr->parentPtr;
+ if (winPtr->mainPtr->focusPtr != NULL &&
+ (winPtr->mainPtr->focusPtr->flags & CK_MAPPED)) {
+ event.type = CK_EV_FOCUSIN;
+ event.winPtr = winPtr->mainPtr->focusPtr;
+ Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
+ }
+ } else {
+ CkWindow *topPtr;
+
+ for (topPtr = winPtr; topPtr != NULL && !(topPtr->flags & CK_TOPLEVEL);
+ topPtr = topPtr->parentPtr) {
+ /* Empty loop body. */
+ }
+ if (topPtr->focusPtr == winPtr)
+ topPtr->focusPtr = winPtr->parentPtr;
+ }
+ Ck_EventuallyRefresh(winPtr);
+done:
+ ckfree((char *) winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MapWindow --
+ *
+ * Map a window within its parent. This may require the
+ * window and/or its parents to actually be created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The given window will be mapped. Windows may also
+ * be created.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_MapWindow(winPtr)
+ CkWindow *winPtr; /* Pointer to window to map. */
+{
+ if (winPtr == NULL || (winPtr->flags & CK_MAPPED))
+ return;
+ if (!(winPtr->parentPtr->flags & CK_MAPPED))
+ return;
+ if (winPtr->window == NULL)
+ Ck_MakeWindowExist(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MakeWindowExist --
+ *
+ * Ensure that a particular window actually exists.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the procedure returns, the curses window associated
+ * with winPtr is guaranteed to exist. This may require the
+ * window's ancestors to be created also.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_MakeWindowExist(winPtr)
+ CkWindow *winPtr; /* Pointer to window. */
+{
+ int x, y;
+ CkMainInfo *mainPtr;
+ CkWindow *parentPtr;
+ CkWindowEvent event;
+
+ if (winPtr == NULL || winPtr->window != NULL)
+ return;
+
+ mainPtr = winPtr->mainPtr;
+ if (winPtr->parentPtr->window == NULL)
+ Ck_MakeWindowExist(winPtr->parentPtr);
+
+ if (winPtr->x >= mainPtr->maxWidth)
+ winPtr->x = mainPtr->maxWidth - 1;
+ if (winPtr->x < 0)
+ winPtr->x = 0;
+ if (winPtr->y >= mainPtr->maxHeight)
+ winPtr->y = mainPtr->maxHeight - 1;
+ if (winPtr->y < 0)
+ winPtr->y = 0;
+
+ x = winPtr->x;
+ y = winPtr->y;
+
+ if (!(winPtr->flags & CK_TOPLEVEL)) {
+ parentPtr = winPtr->parentPtr;
+ if (x < 0)
+ x = winPtr->x = 0;
+ else if (x >= parentPtr->width)
+ x = winPtr->x = parentPtr->width - 1;
+ if (y < 0)
+ y = winPtr->y = 0;
+ else if (y >= parentPtr->height)
+ y = winPtr->y = parentPtr->height - 1;
+ if (x + winPtr->width >= parentPtr->width)
+ winPtr->width = parentPtr->width - x;
+ if (y + winPtr->height >= parentPtr->height)
+ winPtr->height = parentPtr->height - y;
+ parentPtr = winPtr;
+ while ((parentPtr = parentPtr->parentPtr) != NULL) {
+ x += parentPtr->x;
+ y += parentPtr->y;
+ if (parentPtr->flags & CK_TOPLEVEL)
+ break;
+ }
+ }
+ if (winPtr->width <= 0)
+ winPtr->width = 1;
+ if (winPtr->height <= 0)
+ winPtr->height = 1;
+
+ winPtr->window = newwin(winPtr->height, winPtr->width, y, x);
+ idlok(winPtr->window, TRUE);
+ scrollok(winPtr->window, FALSE);
+ keypad(winPtr->window, TRUE);
+ nodelay(winPtr->window, TRUE);
+ meta(winPtr->window, TRUE);
+ winPtr->flags |= CK_MAPPED;
+ Ck_ClearToBot(winPtr, 0, 0);
+ Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
+ Ck_EventuallyRefresh(winPtr);
+
+ event.type = CK_EV_MAP;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, (CkEvent *) &event);
+ event.type = CK_EV_EXPOSE;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, (CkEvent *) &event);
+ if (winPtr == mainPtr->focusPtr) {
+ event.type = CK_EV_FOCUSIN;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, (CkEvent *) &event);
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_MoveWindow --
+ *
+ * Move given window and its children.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_MoveWindow(winPtr, x, y)
+ CkWindow *winPtr; /* Window to move. */
+ int x, y; /* New location for window (within
+ * parent). */
+{
+ CkWindow *childPtr, *parentPtr;
+ int newx, newy;
+
+ if (winPtr == NULL)
+ return;
+
+ winPtr->x = x;
+ winPtr->y = y;
+ if (winPtr->window == NULL)
+ return;
+
+ newx = x;
+ newy = y;
+ if (!(winPtr->flags & CK_TOPLEVEL)) {
+ parentPtr = winPtr;
+ while ((parentPtr = parentPtr->parentPtr) != NULL) {
+ newx += parentPtr->x;
+ newy += parentPtr->y;
+ if (parentPtr->flags & CK_TOPLEVEL)
+ break;
+ }
+ }
+ if (newx + winPtr->width >= winPtr->mainPtr->maxWidth) {
+ winPtr->x -= newx - (winPtr->mainPtr->maxWidth - winPtr->width);
+ newx = winPtr->mainPtr->maxWidth - winPtr->width;
+ }
+ if (newy + winPtr->height >= winPtr->mainPtr->maxHeight) {
+ winPtr->y -= newy - (winPtr->mainPtr->maxHeight - winPtr->height);
+ newy = winPtr->mainPtr->maxHeight - winPtr->height;
+ }
+ if (newx < 0) {
+ winPtr->x -= newx;
+ newx = 0;
+ }
+ if (newy < 0) {
+ winPtr->y -= newy;
+ newy = 0;
+ }
+
+ mvwin(winPtr->window, newy, newx);
+
+ for (childPtr = winPtr->childList;
+ childPtr != NULL; childPtr = childPtr->nextPtr)
+ if (!(childPtr->flags & CK_TOPLEVEL))
+ Ck_MoveWindow(childPtr, childPtr->x, childPtr->y);
+ Ck_EventuallyRefresh(winPtr);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_ResizeWindow --
+ *
+ * Resize given window and eventually its children.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_ResizeWindow(winPtr, width, height)
+ CkWindow *winPtr; /* Window to resize. */
+ int width, height; /* New dimensions for window. */
+{
+ CkWindow *childPtr, *parentPtr;
+ CkWindow *mainWin = winPtr->mainPtr->winPtr;
+ CkWindowEvent event;
+ WINDOW *new;
+ int x, y, evMap = 0, doResize = 0;
+
+ if (winPtr == NULL || winPtr == mainWin)
+ return;
+
+ /*
+ * Special case: if both width/height set to -12345, adjust
+ * within parent window !
+ */
+
+ parentPtr = winPtr->parentPtr;
+ if (!(width == -12345 && height == -12345)) {
+ winPtr->width = width;
+ winPtr->height = height;
+ doResize++;
+ }
+
+ if (!(winPtr->flags & CK_TOPLEVEL)) {
+ if (winPtr->x + winPtr->width >= parentPtr->width) {
+ winPtr->width = parentPtr->width - winPtr->x;
+ doResize++;
+ }
+ if (winPtr->y + winPtr->height >= parentPtr->height) {
+ winPtr->height = parentPtr->height - winPtr->y;
+ doResize++;
+ }
+
+ if (!doResize)
+ return;
+
+ if (winPtr->window == NULL)
+ return;
+
+ parentPtr = winPtr;
+ x = winPtr->x;
+ y = winPtr->y;
+ while ((parentPtr = parentPtr->parentPtr) != NULL) {
+ x += parentPtr->x;
+ y += parentPtr->y;
+ if (parentPtr->flags & CK_TOPLEVEL)
+ break;
+ }
+ } else {
+ x = winPtr->x;
+ y = winPtr->y;
+ }
+
+ if (winPtr->width <= 0)
+ winPtr->width = 1;
+ if (winPtr->height <= 0)
+ winPtr->height = 1;
+
+ if (x + winPtr->width > winPtr->mainPtr->maxWidth)
+ winPtr->width = winPtr->mainPtr->maxWidth - x;
+ if (y + winPtr->height > winPtr->mainPtr->maxHeight)
+ winPtr->height = winPtr->mainPtr->maxHeight - y;
+
+ new = newwin(winPtr->height, winPtr->width, y, x);
+ if (winPtr->window == NULL) {
+ winPtr->flags |= CK_MAPPED;
+ evMap++;
+ } else {
+ delwin(winPtr->window);
+ }
+ winPtr->window = new;
+ idlok(winPtr->window, TRUE);
+ scrollok(winPtr->window, FALSE);
+ keypad(winPtr->window, TRUE);
+ nodelay(winPtr->window, TRUE);
+ meta(winPtr->window, TRUE);
+ Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
+ Ck_ClearToBot(winPtr, 0, 0);
+
+ for (childPtr = winPtr->childList;
+ childPtr != NULL; childPtr = childPtr->nextPtr) {
+ if (childPtr->flags & CK_TOPLEVEL)
+ continue;
+ Ck_ResizeWindow(childPtr, -12345, -12345);
+ }
+ Ck_EventuallyRefresh(winPtr);
+
+ event.type = CK_EV_MAP;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainWin->mainPtr, (CkEvent *) &event);
+ event.type = CK_EV_EXPOSE;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainWin->mainPtr, (CkEvent *) &event);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Ck_UnmapWindow, etc. --
+ *
+ * There are several procedures under here, each of which
+ * mirrors an existing X procedure. In addition to performing
+ * the functions of the corresponding procedure, each
+ * procedure also updates the local window structure and
+ * synthesizes an X event (if the window's structure is being
+ * managed internally).
+ *
+ * Results:
+ * See the manual entries.
+ *
+ * Side effects:
+ * See the manual entries.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Ck_UnmapWindow(winPtr)
+ CkWindow *winPtr; /* Pointer to window to unmap. */
+{
+ CkWindow *childPtr;
+ CkMainInfo *mainPtr = winPtr->mainPtr;
+ CkWindowEvent event;
+
+ for (childPtr = winPtr->childList;
+ childPtr != NULL; childPtr = childPtr->nextPtr) {
+ if (childPtr->flags & CK_TOPLEVEL)
+ continue;
+ Ck_UnmapWindow(childPtr);
+ }
+ if (!(winPtr->flags & CK_MAPPED))
+ return;
+ winPtr->flags &= ~CK_MAPPED;
+ delwin(winPtr->window);
+ winPtr->window = NULL;
+ Ck_EventuallyRefresh(winPtr);
+
+ if (mainPtr->focusPtr == winPtr) {
+ CkWindow *parentPtr;
+
+ parentPtr = winPtr->parentPtr;
+ while (parentPtr != NULL && !(parentPtr->flags & CK_TOPLEVEL))
+ parentPtr = parentPtr->parentPtr;
+ mainPtr->focusPtr = parentPtr;
+ event.type = CK_EV_FOCUSOUT;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, (CkEvent *) &event);
+ }
+ event.type = CK_EV_UNMAP;
+ event.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, (CkEvent *) &event);
+}
+
+void
+Ck_SetWindowAttr(winPtr, fg, bg, attr)
+ CkWindow *winPtr; /* Window to manipulate. */
+ int fg, bg; /* Foreground/background colors. */
+ int attr; /* Video attributes. */
+{
+ winPtr->fg = fg;
+ winPtr->bg = bg;
+ winPtr->attr = attr;
+ if (winPtr->window != NULL) {
+ if ((winPtr->mainPtr->flags & (CK_HAS_COLOR | CK_REVERSE_KLUDGE)) ==
+ (CK_HAS_COLOR | CK_REVERSE_KLUDGE)) {
+ if (attr & A_REVERSE) {
+ int tmp;
+
+ attr &= ~A_REVERSE;
+ tmp = bg;
+ bg = fg;
+ fg = tmp;
+ }
+ }
+ wattrset(winPtr->window, attr | Ck_GetPair(winPtr, fg, bg));
+ }
+}
+
+void
+Ck_GetRootGeometry(winPtr, xPtr, yPtr, widthPtr, heightPtr)
+ CkWindow *winPtr;
+ int *xPtr, *yPtr, *widthPtr, *heightPtr;
+{
+ int x, y;
+
+ if (widthPtr != NULL)
+ *widthPtr = winPtr->width;
+ if (heightPtr != NULL)
+ *heightPtr = winPtr->height;
+
+ x = y = 0;
+ do {
+ x += winPtr->x;
+ y += winPtr->y;
+ if (winPtr->flags & CK_TOPLEVEL)
+ break;
+ winPtr = winPtr->parentPtr;
+ } while (winPtr != NULL);
+ if (xPtr != NULL)
+ *xPtr = x;
+ if (yPtr != NULL)
+ *yPtr = y;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_NameToWindow --
+ *
+ * Given a string name for a window, this procedure
+ * returns the pointer to the window, if there exists a
+ * window corresponding to the given name.
+ *
+ * Results:
+ * The return result is either the pointer to the window corresponding
+ * to "name", or else NULL to indicate that there is no such
+ * window. In this case, an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_NameToWindow(interp, pathName, winPtr)
+ Tcl_Interp *interp; /* Where to report errors. */
+ char *pathName; /* Path name of window. */
+ CkWindow *winPtr; /* Pointer to window: name is assumed to
+ * belong to the same main window as winPtr. */
+{
+ Tcl_HashEntry *hPtr;
+
+ hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->nameTable, pathName);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "bad window path name \"",
+ pathName, "\"", (char *) NULL);
+ return NULL;
+ }
+ return (CkWindow *) Tcl_GetHashValue(hPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_SetClass --
+ *
+ * This procedure is used to give a window a class.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new class is stored for winPtr, replacing any existing
+ * class for it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_SetClass(winPtr, className)
+ CkWindow *winPtr; /* Window to assign class. */
+ char *className; /* New class for window. */
+{
+ winPtr->classUid = Ck_GetUid(className);
+ CkOptionClassChanged(winPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkWindow --
+ *
+ * This procedure removes a window from the childList of its
+ * parent.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is unlinked from its childList.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UnlinkWindow(winPtr)
+ CkWindow *winPtr; /* Child window to be unlinked. */
+{
+ CkWindow *prevPtr;
+
+ if (winPtr->parentPtr == NULL)
+ return;
+ prevPtr = winPtr->parentPtr->childList;
+ if (prevPtr == winPtr) {
+ winPtr->parentPtr->childList = winPtr->nextPtr;
+ if (winPtr->nextPtr == NULL)
+ winPtr->parentPtr->lastChildPtr = NULL;
+ } else {
+ while (prevPtr->nextPtr != winPtr) {
+ prevPtr = prevPtr->nextPtr;
+ if (prevPtr == NULL)
+ panic("UnlinkWindow couldn't find child in parent");
+ }
+ prevPtr->nextPtr = winPtr->nextPtr;
+ if (winPtr->nextPtr == NULL)
+ winPtr->parentPtr->lastChildPtr = prevPtr;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkToplevel --
+ *
+ * This procedure removes a window from the toplevel list.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is unlinked from the toplevel list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UnlinkToplevel(winPtr)
+ CkWindow *winPtr;
+{
+ CkWindow *prevPtr;
+
+ prevPtr = winPtr->mainPtr->topLevPtr;
+ if (prevPtr == winPtr) {
+ winPtr->mainPtr->topLevPtr = winPtr->topLevPtr;
+ } else {
+ while (prevPtr->topLevPtr != winPtr) {
+ prevPtr = prevPtr->topLevPtr;
+ if (prevPtr == NULL)
+ panic("UnlinkToplevel couldn't find toplevel");
+ }
+ prevPtr->topLevPtr = winPtr->topLevPtr;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_RestackWindow --
+ *
+ * Change a window's position in the stacking order.
+ *
+ * Results:
+ * TCL_OK is normally returned. If other is not a descendant
+ * of winPtr's parent then TCL_ERROR is returned and winPtr is
+ * not repositioned.
+ *
+ * Side effects:
+ * WinPtr is repositioned in the stacking order.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ck_RestackWindow(winPtr, aboveBelow, otherPtr)
+ CkWindow *winPtr; /* Pointer to window whose position in
+ * the stacking order is to change. */
+ int aboveBelow; /* Indicates new position of winPtr relative
+ * to other; must be Above or Below. */
+ CkWindow *otherPtr; /* WinPtr will be moved to a position that
+ * puts it just above or below this window.
+ * If NULL then winPtr goes above or below
+ * all windows in the same parent. */
+{
+ CkWindow *prevPtr;
+
+ if (winPtr->flags & CK_TOPLEVEL) {
+ if (otherPtr != NULL) {
+ while (otherPtr != NULL && !(otherPtr->flags & CK_TOPLEVEL))
+ otherPtr = otherPtr->parentPtr;
+ }
+ if (otherPtr == winPtr)
+ return TCL_OK;
+
+ UnlinkToplevel(winPtr);
+ if (aboveBelow == CK_ABOVE) {
+ if (otherPtr == NULL) {
+ winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
+ winPtr->mainPtr->topLevPtr = winPtr;
+ } else {
+ CkWindow *thisPtr = winPtr->mainPtr->topLevPtr;
+
+ prevPtr = NULL;
+ while (thisPtr != NULL && thisPtr != otherPtr) {
+ prevPtr = thisPtr;
+ thisPtr = thisPtr->topLevPtr;
+ }
+ if (prevPtr == NULL) {
+ winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
+ winPtr->mainPtr->topLevPtr = winPtr;
+ } else {
+ winPtr->topLevPtr = prevPtr->topLevPtr;
+ prevPtr->topLevPtr = winPtr;
+ }
+ }
+ } else {
+ CkWindow *thisPtr = winPtr->mainPtr->topLevPtr;
+
+ prevPtr = NULL;
+ while (thisPtr != NULL && thisPtr != otherPtr) {
+ prevPtr = thisPtr;
+ thisPtr = thisPtr->topLevPtr;
+ }
+ if (thisPtr == NULL) {
+ winPtr->topLevPtr = prevPtr->topLevPtr;
+ prevPtr->topLevPtr = winPtr;
+ } else {
+ winPtr->topLevPtr = thisPtr->topLevPtr;
+ thisPtr->topLevPtr = winPtr;
+ }
+ }
+ ChangeToplevelFocus(winPtr->mainPtr->topLevPtr);
+ goto done;
+ }
+
+ /*
+ * Find an ancestor of otherPtr that is a sibling of winPtr.
+ */
+
+ if (otherPtr == NULL) {
+ if (aboveBelow == CK_BELOW)
+ otherPtr = winPtr->parentPtr->lastChildPtr;
+ else
+ otherPtr = winPtr->parentPtr->childList;
+ } else {
+ while (winPtr->parentPtr != otherPtr->parentPtr) {
+ otherPtr = otherPtr->parentPtr;
+ if (otherPtr == NULL)
+ return TCL_ERROR;
+ }
+ }
+ if (otherPtr == winPtr)
+ return TCL_OK;
+
+ /*
+ * Reposition winPtr in the stacking order.
+ */
+
+ UnlinkWindow(winPtr);
+ if (aboveBelow == CK_BELOW) {
+ winPtr->nextPtr = otherPtr->nextPtr;
+ if (winPtr->nextPtr == NULL)
+ winPtr->parentPtr->lastChildPtr = winPtr;
+ otherPtr->nextPtr = winPtr;
+ } else {
+ prevPtr = winPtr->parentPtr->childList;
+ if (prevPtr == otherPtr)
+ winPtr->parentPtr->childList = winPtr;
+ else {
+ while (prevPtr->nextPtr != otherPtr)
+ prevPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = winPtr;
+ }
+ winPtr->nextPtr = otherPtr;
+ }
+
+done:
+ Ck_EventuallyRefresh(winPtr);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_SetFocus --
+ *
+ * This procedure is invoked to change the focus window for a
+ * given display in a given application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Event handlers may be invoked to process the change of
+ * focus.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_SetFocus(winPtr)
+ CkWindow *winPtr; /* Window that is to be the new focus. */
+{
+ CkMainInfo *mainPtr = winPtr->mainPtr;
+ CkEvent event;
+ CkWindow *oldTop = NULL, *newTop, *oldFocus;
+
+ if (winPtr == mainPtr->focusPtr)
+ return;
+
+ oldFocus = mainPtr->focusPtr;
+ if (oldFocus != NULL) {
+ for (oldTop = oldFocus; oldTop != NULL &&
+ !(oldTop->flags & CK_TOPLEVEL); oldTop = oldTop->parentPtr) {
+ /* Empty loop body. */
+ }
+ event.win.type = CK_EV_FOCUSOUT;
+ event.win.winPtr = oldFocus;
+ Ck_HandleEvent(mainPtr, &event);
+ }
+ mainPtr->focusPtr = winPtr;
+ for (newTop = winPtr; newTop != NULL &&
+ !(newTop->flags & CK_TOPLEVEL); newTop = newTop->parentPtr) {
+ /* Empty loop body. */
+ }
+ if (oldTop != newTop) {
+ if (oldTop != NULL)
+ oldTop->focusPtr = oldFocus;
+ Ck_RestackWindow(newTop, CK_ABOVE, NULL);
+ Ck_EventuallyRefresh(mainPtr->winPtr);
+ }
+ if (winPtr->flags & CK_MAPPED) {
+ event.win.type = CK_EV_FOCUSIN;
+ event.win.winPtr = winPtr;
+ Ck_HandleEvent(mainPtr, &event);
+ Ck_EventuallyRefresh(mainPtr->winPtr);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ChangeToplevelFocus --
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ChangeToplevelFocus(winPtr)
+ CkWindow *winPtr;
+{
+ CkWindow *winTop, *oldTop;
+ CkMainInfo *mainPtr;
+
+ if (winPtr == NULL)
+ return;
+
+ mainPtr = winPtr->mainPtr;
+ for (winTop = winPtr; winTop != NULL && !(winTop->flags & CK_TOPLEVEL);
+ winTop = winTop->parentPtr) {
+ /* Empty loop body. */
+ }
+ for (oldTop = mainPtr->focusPtr; oldTop != NULL &&
+ !(oldTop->flags & CK_TOPLEVEL); oldTop = oldTop->parentPtr) {
+ /* Empty loop body. */
+ }
+ if (winTop != oldTop) {
+ CkEvent event;
+
+ if (oldTop != NULL) {
+ oldTop->focusPtr = mainPtr->focusPtr;
+ event.win.type = CK_EV_FOCUSOUT;
+ event.win.winPtr = mainPtr->focusPtr;
+ Ck_HandleEvent(mainPtr, &event);
+ }
+ mainPtr->focusPtr = winTop->focusPtr;
+ event.win.type = CK_EV_FOCUSIN;
+ event.win.winPtr = mainPtr->focusPtr;
+ Ck_HandleEvent(mainPtr, &event);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_EventuallyRefresh --
+ *
+ * Dispatch refresh of entire screen.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_EventuallyRefresh(winPtr)
+ CkWindow *winPtr;
+{
+ if (++winPtr->mainPtr->refreshCount == 1)
+ Tcl_DoWhenIdle(DoRefresh, (ClientData) winPtr->mainPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DoRefresh --
+ *
+ * Refresh all curses windows. If the terminal is connected via
+ * a network connection (ie terminal server) the curses typeahead
+ * mechanism is not sufficient for delaying screen updates due to
+ * TCP buffering.
+ * Therefore the refreshDelay may be used in order to limit updates
+ * to happen not more often than 1000/refreshDelay times per second.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DoRefresh(clientData)
+ ClientData clientData;
+{
+ CkMainInfo *mainPtr = (CkMainInfo *) clientData;
+
+ if (mainPtr->flags & CK_REFRESH_TIMER) {
+ Tk_DeleteTimerHandler(mainPtr->refreshTimer);
+ mainPtr->flags &= ~CK_REFRESH_TIMER;
+ }
+ if (--mainPtr->refreshCount > 0) {
+ Tk_DoWhenIdle2(DoRefresh, clientData);
+ return;
+ }
+ mainPtr->refreshCount = 0;
+ if (mainPtr->refreshDelay > 0) {
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ struct timeval tv;
+ double t0;
+
+ gettimeofday(&tv, (struct timezone *) NULL);
+ t0 = (tv.tv_sec + 0.000001 * tv.tv_usec) * 1000;
+#else
+ Tcl_Time tv;
+ double t0;
+ extern void TclpGetTime _ANSI_ARGS_((Tcl_Time *timePtr));
+
+ TclpGetTime(&tv);
+ t0 = (tv.sec + 0.000001 * tv.usec) * 1000;
+#endif
+ if (t0 - mainPtr->lastRefresh < mainPtr->refreshDelay) {
+ mainPtr->refreshTimer = Tk_CreateTimerHandler(
+ mainPtr->refreshDelay - (int) (t0 - mainPtr->lastRefresh),
+ DoRefresh, clientData);
+ mainPtr->flags |= CK_REFRESH_TIMER;
+ return;
+ }
+ mainPtr->lastRefresh = t0;
+ }
+ curs_set(0);
+ RefreshToplevels(mainPtr->topLevPtr);
+ UpdateHWCursor(ckMainInfo);
+ doupdate();
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RefreshToplevels --
+ *
+ * Recursively refresh all toplevel windows starting at winPtr.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RefreshToplevels(winPtr)
+ CkWindow *winPtr;
+{
+ if (winPtr->topLevPtr != NULL)
+ RefreshToplevels(winPtr->topLevPtr);
+ if (winPtr->window != NULL) {
+ touchwin(winPtr->window);
+ wnoutrefresh(winPtr->window);
+ if (winPtr->childList != NULL)
+ RefreshThem(winPtr->childList);
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RefreshThem --
+ *
+ * Recursively refresh all curses windows starting at winPtr.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RefreshThem(winPtr)
+ CkWindow *winPtr;
+{
+ if (winPtr->nextPtr != NULL)
+ RefreshThem(winPtr->nextPtr);
+ if (winPtr->flags & CK_TOPLEVEL)
+ return;
+ if (winPtr->window != NULL) {
+ touchwin(winPtr->window);
+ wnoutrefresh(winPtr->window);
+ }
+ if (winPtr->childList != NULL)
+ RefreshThem(winPtr->childList);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateHWCursor --
+ *
+ * Make hardware cursor (in)visible for given window using curses.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateHWCursor(mainPtr)
+ CkMainInfo *mainPtr;
+{
+ int x, y;
+ CkWindow *wPtr, *stopAtWin, *winPtr = mainPtr->focusPtr;
+
+ if (winPtr == NULL || winPtr->window == NULL ||
+ (winPtr->flags & (CK_SHOW_CURSOR | CK_ALREADY_DEAD)) == 0) {
+invisible:
+ curs_set(0);
+ if (mainPtr->focusPtr != NULL && mainPtr->focusPtr->window != NULL)
+ wnoutrefresh(mainPtr->focusPtr->window);
+ return;
+ }
+
+ /*
+ * Get position of HW cursor in winPtr coordinates.
+ */
+
+ getyx(winPtr->window, y, x);
+
+ stopAtWin = NULL;
+ while (winPtr != NULL) {
+ for (wPtr = winPtr->childList;
+ wPtr != NULL && wPtr != stopAtWin; wPtr = wPtr->nextPtr) {
+ if ((wPtr->flags & CK_TOPLEVEL) || wPtr->window == NULL)
+ continue;
+ if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
+ y >= wPtr->y && y < wPtr->y + wPtr->height)
+ goto invisible;
+ }
+ x += winPtr->x;
+ y += winPtr->y;
+ stopAtWin = winPtr;
+ if (winPtr->parentPtr == NULL)
+ break;
+ winPtr = winPtr->parentPtr;
+ if (winPtr->flags & CK_TOPLEVEL)
+ break;
+ }
+ for (wPtr = mainPtr->topLevPtr; wPtr != NULL && wPtr != winPtr;
+ wPtr = wPtr->topLevPtr)
+ if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
+ y >= wPtr->y && y < wPtr->y + wPtr->height)
+ goto invisible;
+ curs_set(1);
+ wnoutrefresh(mainPtr->focusPtr->window);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowXY --
+ *
+ * Given X, Y coordinates, return topmost window.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static CkWindow *
+GetWindowXY(winPtr, xPtr, yPtr)
+ CkWindow *winPtr;
+ int *xPtr, *yPtr;
+{
+ int x, y;
+ CkWindow *wPtr;
+
+ x = *xPtr - winPtr->x; y = *yPtr - winPtr->y;
+ for (wPtr = winPtr->childList; wPtr != NULL; wPtr = wPtr->nextPtr) {
+ if (!(wPtr->flags & CK_MAPPED) || (wPtr->flags & CK_TOPLEVEL))
+ continue;
+ if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
+ y >= wPtr->y && y < wPtr->y + wPtr->height) {
+ wPtr = GetWindowXY(wPtr, &x, &y);
+ *xPtr = x;
+ *yPtr = y;
+ return wPtr;
+ }
+ }
+ *xPtr = x;
+ *yPtr = y;
+ return winPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_GetWindowXY --
+ *
+ * Given X, Y coordinates, return topmost window.
+ * If mode is zero, consider all toplevels, otherwise consider
+ * only topmost toplevel in search.
+ *
+ * Results:
+ * Window pointer or NULL. *xPtr, *yPtr are adjusted to window
+ * coordinate system if possible.
+ *
+ *----------------------------------------------------------------------
+ */
+
+CkWindow *
+Ck_GetWindowXY(mainPtr, xPtr, yPtr, mode)
+ CkMainInfo *mainPtr;
+ int *xPtr, *yPtr, mode;
+{
+ int x, y, x0, y0;
+ CkWindow *wPtr;
+
+ x0 = *xPtr; y0 = *yPtr;
+ wPtr = mainPtr->topLevPtr;
+nextToplevel:
+ x = x0; y = y0;
+ if (wPtr->flags & CK_MAPPED) {
+ if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
+ y >= wPtr->y && y < wPtr->y + wPtr->height) {
+ wPtr = GetWindowXY(wPtr, &x, &y);
+ *xPtr = x;
+ *yPtr = y;
+ return wPtr;
+ } else {
+ *xPtr = -1;
+ *yPtr = -1;
+ }
+ } else if (mode) {
+ return NULL;
+ }
+ if (mode == 0) {
+ wPtr = wPtr->topLevPtr;
+ if (wPtr != NULL)
+ goto nextToplevel;
+ }
+ return wPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_SetHWCursor --
+ *
+ * Make hardware cursor (in)visible for given window.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_SetHWCursor(winPtr, newState)
+ CkWindow *winPtr;
+ int newState;
+{
+ int oldState = winPtr->flags & CK_SHOW_CURSOR;
+
+ if (newState == oldState)
+ return;
+
+ if (newState)
+ winPtr->flags |= CK_SHOW_CURSOR;
+ else
+ winPtr->flags &= ~CK_SHOW_CURSOR;
+ if (winPtr == winPtr->mainPtr->focusPtr)
+ UpdateHWCursor(winPtr->mainPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_ClearToEol --
+ *
+ * Clear window starting from given position, til end of line.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_ClearToEol(winPtr, x, y)
+ CkWindow *winPtr;
+ int x, y;
+{
+ WINDOW *window = winPtr->window;
+
+ if (window == NULL)
+ return;
+
+ if (x == -1 && y == -1)
+ getyx(window, y, x);
+ else
+ wmove(window, y, x);
+ for (; x < winPtr->width; x++)
+ waddch(window, ' ');
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ck_ClearToBot --
+ *
+ * Clear window starting from given position, til end of screen.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Ck_ClearToBot(winPtr, x, y)
+ CkWindow *winPtr;
+ int x, y;
+{
+ WINDOW *window = winPtr->window;
+
+ if (window == NULL)
+ return;
+
+ wmove(window, y, x);
+ for (; x < winPtr->width; x++)
+ waddch(window, ' ');
+ for (++y; y < winPtr->height; y++) {
+ wmove(window, y, 0);
+ for (x = 0; x < winPtr->width; x++)
+ waddch(window, ' ');
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeadAppCmd --
+ *
+ * Report error since toolkit gone.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DeadAppCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ interp->result = "toolkit uninstalled";
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExecCmd --
+ *
+ * Own version of "exec" Tcl command which supports the -endwin
+ * option.
+ *
+ * Results:
+ * See documentation for "exec".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ExecCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+ int result, endWin = 0;
+ char *savedargv1;
+#ifdef SIGINT
+#ifdef HAVE_SIGACTION
+ struct sigaction oldsig, newsig;
+#else
+ Ck_SignalProc sigproc;
+#endif
+#endif
+
+ if (argc > 1 && strcmp(argv[1], "-endwin") == 0) {
+ endWin = 1;
+ savedargv1 = argv[1];
+ argv[1] = argv[0];
+ curs_set(1);
+ endwin();
+#ifdef SIGINT
+#ifdef HAVE_SIGACTION
+ newsig.sa_handler = SIG_IGN;
+ sigfillset(&newsig.sa_mask);
+ newsig.sa_flags = 0;
+ sigaction(SIGINT, &newsig, &oldsig);
+#else
+ sigproc = signal(SIGINT, SIG_IGN);
+#endif
+#endif
+ }
+ result = (*cmdInfo->proc)(cmdInfo->clientData, interp,
+ argc - endWin, argv + endWin);
+ if (endWin) {
+#ifdef SIGINT
+#ifdef HAVE_SIGACTION
+ sigaction(SIGINT, &oldsig, NULL);
+#else
+ signal(SIGINT, sigproc);
+#endif
+#endif
+ argv[0] = argv[1];
+ argv[1] = savedargv1;
+ Ck_EventuallyRefresh(redirInfo->mainPtr->winPtr);
+ }
+ return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * PutsCmd --
+ *
+ * Redirect "puts" Tcl command from "stdout" to "stderr" in the
+ * hope that it will not destroy our screen.
+ *
+ * Results:
+ * See documentation of "puts" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+PutsCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+ int index = 0;
+ char *newArgv[5];
+
+ newArgv[0] = argv[0];
+ if (argc > 1 && strcmp(argv[1], "-nonewline") == 0) {
+ newArgv[1] = argv[1];
+ index++;
+ }
+ if (argc == index + 2) {
+ newArgv[index + 2] = argv[index + 1];
+toStderr:
+ newArgv[index + 1] = "stderr";
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp,
+ index + 3, newArgv);
+ } else if (argc == index + 3 &&
+ (strcmp(argv[index + 1], "stdout") == 0 ||
+ strcmp(argv[index + 1], "file1") == 0)) {
+ newArgv[index + 2] = argv[index + 2];
+ goto toStderr;
+ }
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CloseCmd --
+ *
+ * Report error when attempt is made to close stdin or stdout.
+ *
+ * Results:
+ * See documentation of "close" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CloseCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+
+ if (argc == 2 &&
+ (strcmp(argv[1], "stdin") == 0 ||
+ strcmp(argv[1], "file0") == 0 ||
+ strcmp(argv[1], "stdout") == 0 ||
+ strcmp(argv[1], "file1") == 0)) {
+ Tcl_AppendResult(interp, "may not close fileId \"",
+ argv[1], "\" while in toolkit", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FlushCmd --
+ *
+ * Report error when attempt is made to flush stdin or stdout.
+ *
+ * Results:
+ * See documentation of "flush" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FlushCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+
+ if (argc == 2 &&
+ (strcmp(argv[1], "stdin") == 0 ||
+ strcmp(argv[1], "file0") == 0 ||
+ strcmp(argv[1], "stdout") == 0 ||
+ strcmp(argv[1], "file1") == 0)) {
+ Tcl_AppendResult(interp, "may not flush fileId \"",
+ argv[1], "\" while in toolkit", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadCmd --
+ *
+ * Report error when attempt is made to read from stdin.
+ *
+ * Results:
+ * See documentation of "read" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ReadCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+
+ if ((argc > 1 &&
+ (strcmp(argv[1], "stdin") == 0 ||
+ strcmp(argv[1], "file0") == 0)) ||
+ (argc > 2 &&
+ (strcmp(argv[2], "stdin") == 0 ||
+ strcmp(argv[2], "file0") == 0))) {
+ Tcl_AppendResult(interp, "may not read from fileId \"",
+ argv[1], "\" while in toolkit", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetsCmd --
+ *
+ * Report error when attempt is made to read from stdin.
+ *
+ * Results:
+ * See documentation of "gets" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetsCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ RedirInfo *redirInfo = (RedirInfo *) clientData;
+ Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
+
+ if (argc >= 2 &&
+ (strcmp(argv[1], "stdin") == 0 ||
+ strcmp(argv[1], "file0") == 0)) {
+ Tcl_AppendResult(interp, "may not gets from fileId \"",
+ argv[1], "\" while in toolkit", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
+}
+\f
+#ifdef __WIN32__
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * WIN32 specific curses input event handling.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InputSetup(inputInfo)
+ InputInfo *inputInfo;
+{
+ WNDCLASS class;
+ DWORD id;
+
+ /*
+ * Create the async notification window with a new class.
+ */
+ class.style = 0;
+ class.cbClsExtra = 0;
+ class.cbWndExtra = 0;
+ class.hInstance = GetModuleHandle(NULL);
+ class.hbrBackground = NULL;
+ class.lpszMenuName = NULL;
+ class.lpszClassName = "CursesInput";
+ class.lpfnWndProc = InputHandler;
+ class.hIcon = NULL;
+ class.hCursor = NULL;
+ if (RegisterClass(&class)) {
+ inputInfo->hwnd =
+ CreateWindow("CursesInput", "CursesInput", WS_TILED, 0, 0,
+ 0, 0, NULL, NULL, class.hInstance, NULL);
+ }
+ if (inputInfo->hwnd == NULL) {
+ panic("cannot create curses input window");
+ }
+ SetWindowLong(inputInfo->hwnd, GWL_USERDATA, (LONG) inputInfo);
+ inputInfo->thread = CreateThread(NULL, 4096,
+ (LPTHREAD_START_ROUTINE) InputThread,
+ (void *) inputInfo, 0, &id);
+ Tcl_CreateExitHandler(InputExit, (ClientData) inputInfo);
+}
+
+static void
+InputExit(clientData)
+ ClientData clientData;
+{
+ InputInfo *inputInfo = (InputInfo *) clientData;
+
+ if (inputInfo->hwnd != NULL) {
+ HWND hwnd = inputInfo->hwnd;
+
+ inputInfo->hwnd = NULL;
+ DestroyWindow(hwnd);
+ }
+ if (inputInfo->thread != INVALID_HANDLE_VALUE) {
+ WaitForSingleObject(inputInfo->thread, 1000);
+ }
+}
+
+static void
+InputThread(arg)
+ void *arg;
+{
+ InputInfo *inputInfo = (InputInfo *) arg;
+ INPUT_RECORD ip;
+ DWORD nRead;
+
+ while (inputInfo->hwnd != NULL) {
+ nRead = 0;
+ PeekConsoleInput(inputInfo->stdinHandle, &ip, 1, &nRead);
+ if (nRead > 0) {
+ PostMessage(inputInfo->hwnd, WM_USER + 42, 0, 0);
+ }
+ Sleep(10);
+ }
+ inputInfo->thread = INVALID_HANDLE_VALUE;
+ ExitThread(0);
+}
+
+static LRESULT CALLBACK
+InputHandler(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ InputInfo *inputInfo = (InputInfo *) GetWindowLong(hwnd, GWL_USERDATA);
+
+ if (message != WM_USER + 42) {
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ Tk_DoWhenIdle(InputHandler2, (ClientData) inputInfo);
+ return 0;
+}
+
+static void
+InputHandler2(clientData)
+ ClientData clientData;
+{
+ InputInfo *inputInfo = (InputInfo *) clientData;
+ INPUT_RECORD ip;
+ DWORD nRead;
+
+ do {
+ CkHandleInput((ClientData) inputInfo->mainPtr, TCL_READABLE);
+ nRead = 0;
+ PeekConsoleInput(inputInfo->stdinHandle, &ip, 1, &nRead);
+ } while (nRead != 0);
+}
+
+#endif
+
+
--- /dev/null
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+of the Rights in Technical Data and Computer Software Clause as DFARS
+252.227-7013 and FAR 52.227-19.
--- /dev/null
+/*
+ * stdlib.h --
+ *
+ * Declares facilities exported by the "stdlib" portion of
+ * the C library. This file isn't complete in the ANSI-C
+ * sense; it only declares things that are needed by Tcl.
+ * This file is needed even on many systems with their own
+ * stdlib.h (e.g. SunOS) because not all stdlib.h files
+ * declare all the procedures needed here (such as strtod).
+ *
+ * Copyright (c) 1991 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * @(#) stdlib.h 1.9 94/12/17 16:26:20
+ */
+
+#ifndef _STDLIB
+#define _STDLIB
+
+#include <tcl.h>
+
+extern void abort _ANSI_ARGS_((void));
+extern double atof _ANSI_ARGS_((CONST char *string));
+extern int atoi _ANSI_ARGS_((CONST char *string));
+extern long atol _ANSI_ARGS_((CONST char *string));
+extern char * calloc _ANSI_ARGS_((unsigned int numElements,
+ unsigned int size));
+extern void exit _ANSI_ARGS_((int status));
+extern int free _ANSI_ARGS_((char *blockPtr));
+extern char * getenv _ANSI_ARGS_((CONST char *name));
+extern char * malloc _ANSI_ARGS_((unsigned int numBytes));
+extern void qsort _ANSI_ARGS_((VOID *base, int n, int size,
+ int (*compar)(CONST VOID *element1, CONST VOID
+ *element2)));
+extern char * realloc _ANSI_ARGS_((char *ptr, unsigned int numBytes));
+extern double strtod _ANSI_ARGS_((CONST char *string, char **endPtr));
+extern long strtol _ANSI_ARGS_((CONST char *string, char **endPtr,
+ int base));
+extern unsigned long strtoul _ANSI_ARGS_((CONST char *string,
+ char **endPtr, int base));
+
+#endif /* _STDLIB */
--- /dev/null
+/*
+ * unistd.h --
+ *
+ * Macros, CONSTants and prototypes for Posix conformance.
+ *
+ * Copyright 1989 Regents of the University of California
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ *
+ * @(#) unistd.h 1.5 94/12/17 16:26:27
+ */
+
+#ifndef _UNISTD
+#define _UNISTD
+
+#include <sys/types.h>
+#ifndef _TCL
+# include "tcl.h"
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/*
+ * Strict POSIX stuff goes here. Extensions go down below, in the
+ * ifndef _POSIX_SOURCE section.
+ */
+
+extern void _exit _ANSI_ARGS_((int status));
+extern int access _ANSI_ARGS_((CONST char *path, int mode));
+extern int chdir _ANSI_ARGS_((CONST char *path));
+extern int chown _ANSI_ARGS_((CONST char *path, uid_t owner, gid_t group));
+extern int close _ANSI_ARGS_((int fd));
+extern int dup _ANSI_ARGS_((int oldfd));
+extern int dup2 _ANSI_ARGS_((int oldfd, int newfd));
+extern int execl _ANSI_ARGS_((CONST char *path, ...));
+extern int execle _ANSI_ARGS_((CONST char *path, ...));
+extern int execlp _ANSI_ARGS_((CONST char *file, ...));
+extern int execv _ANSI_ARGS_((CONST char *path, char **argv));
+extern int execve _ANSI_ARGS_((CONST char *path, char **argv, char **envp));
+extern int execvp _ANSI_ARGS_((CONST char *file, char **argv));
+extern pid_t fork _ANSI_ARGS_((void));
+extern char *getcwd _ANSI_ARGS_((char *buf, size_t size));
+extern gid_t getegid _ANSI_ARGS_((void));
+extern uid_t geteuid _ANSI_ARGS_((void));
+extern gid_t getgid _ANSI_ARGS_((void));
+extern int getgroups _ANSI_ARGS_((int bufSize, int *buffer));
+extern pid_t getpid _ANSI_ARGS_((void));
+extern uid_t getuid _ANSI_ARGS_((void));
+extern int isatty _ANSI_ARGS_((int fd));
+extern long lseek _ANSI_ARGS_((int fd, long offset, int whence));
+extern int pipe _ANSI_ARGS_((int *fildes));
+extern int read _ANSI_ARGS_((int fd, char *buf, size_t size));
+extern int setgid _ANSI_ARGS_((gid_t group));
+extern int setuid _ANSI_ARGS_((uid_t user));
+extern unsigned sleep _ANSI_ARGS_ ((unsigned seconds));
+extern char *ttyname _ANSI_ARGS_((int fd));
+extern int unlink _ANSI_ARGS_((CONST char *path));
+extern int write _ANSI_ARGS_((int fd, CONST char *buf, size_t size));
+
+#ifndef _POSIX_SOURCE
+extern char *crypt _ANSI_ARGS_((CONST char *, CONST char *));
+extern int fchown _ANSI_ARGS_((int fd, uid_t owner, gid_t group));
+extern int flock _ANSI_ARGS_((int fd, int operation));
+extern int ftruncate _ANSI_ARGS_((int fd, unsigned long length));
+extern int readlink _ANSI_ARGS_((CONST char *path, char *buf, int bufsize));
+extern int setegid _ANSI_ARGS_((gid_t group));
+extern int seteuid _ANSI_ARGS_((uid_t user));
+extern int setreuid _ANSI_ARGS_((int ruid, int euid));
+extern int symlink _ANSI_ARGS_((CONST char *, CONST char *));
+extern int ttyslot _ANSI_ARGS_((void));
+extern int truncate _ANSI_ARGS_((CONST char *path, unsigned long length));
+extern int vfork _ANSI_ARGS_((void));
+#endif /* _POSIX_SOURCE */
+
+#endif /* _UNISTD */
+
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-10-07'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ # GNU/KFreeBSD systems have a "k" prefix to indicate we are using
+ # FreeBSD's kernel, but not the complete OS.
+ case ${LIBC} in gnu) kernel_only='k' ;; esac
+ echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[DGKLNPTVWY]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+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`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-tcl=DIR use Tcl 8.X binaries from DIR"
+ac_help="$ac_help
+ --enable-shared build libck as a shared library"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+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'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+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
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # 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 ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$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" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) 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)
+ # 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 << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$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)
+ 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 ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ 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 "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# 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
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=ck.h
+
+# 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_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ 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 "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# 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 "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; 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
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# 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
+# 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 $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:561: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/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
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+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 "$ac_t""$INSTALL" 1>&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_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:616: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+CC=${CC-cc}
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:645: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 660 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:666: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 677 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:683: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 694 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:700: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+for ac_hdr in unistd.h limits.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:728: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 733 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:738: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+#--------------------------------------------------------------------
+# Supply a substitute for stdlib.h if it doesn't define strtol,
+# strtoul, or strtod (which it doesn't in some versions of SunOS).
+#--------------------------------------------------------------------
+
+echo $ac_n "checking stdlib.h""... $ac_c" 1>&6
+echo "configure:771: checking stdlib.h" >&5
+cat > conftest.$ac_ext <<EOF
+#line 773 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "strtol" >/dev/null 2>&1; then
+ rm -rf conftest*
+ tk_ok=yes
+else
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+
+cat > conftest.$ac_ext <<EOF
+#line 788 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "strtoul" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+
+cat > conftest.$ac_ext <<EOF
+#line 802 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "strtod" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+
+if test $tk_ok = no; then
+ cat >> confdefs.h <<\EOF
+#define NO_STDLIB_H 1
+EOF
+
+fi
+echo "$ac_t""$tk_ok" 1>&6
+
+#--------------------------------------------------------------------
+# Check for various typedefs and provide substitutes if
+# they don't exist.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:829: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 834 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:842: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 859 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ 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 <<EOF
+#line 877 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ 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 <<EOF
+#line 898 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#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); }
+
+EOF
+if { (eval echo configure:909: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for mode_t""... $ac_c" 1>&6
+echo "configure:933: checking for mode_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 938 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_mode_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_mode_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_mode_t" 1>&6
+if test $ac_cv_type_mode_t = no; then
+ cat >> confdefs.h <<\EOF
+#define mode_t int
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:966: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 971 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:999: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1004 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
+echo "configure:1032: checking for uid_t in sys/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1037 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "uid_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_uid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+
+#------------------------------------------------------------------------------
+# What type do signals return? Do we have sigaction ?
+#------------------------------------------------------------------------------
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1071: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1076 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1093: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+for ac_func in sigaction
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1114: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1119 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* 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();
+
+int main() {
+
+/* 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
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1142: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+#--------------------------------------------------------------------
+# See if there was a command-line option for where Tcl is; if
+# not, assume that its top-level directory is a sibling of ours.
+#--------------------------------------------------------------------
+
+# Check whether --with-tcl or --without-tcl was given.
+if test "${with_tcl+set}" = set; then
+ withval="$with_tcl"
+ TCL_BIN_DIR=$withval
+else
+ TCL_BIN_DIR=`cd ../tcl8.0/unix ; pwd`
+fi
+
+if test ! -d $TCL_BIN_DIR ; then
+ { echo "configure: error: Tcl directory $TCL_BIN_DIR doesn't exist" 1>&2; exit 1; }
+fi
+
+#--------------------------------------------------------------------
+# Read in configuration information generated by Tcl for shared
+# libraries, and arrange for it to be substituted into our
+# Makefile.
+#--------------------------------------------------------------------
+
+if test -r $TCL_BIN_DIR/tclConfig.sh ; then
+ file=$TCL_BIN_DIR/tclConfig.sh
+ if test -d $TCL_BIN_DIR/../generic ; then
+ TCL_DIR=`cd $TCL_BIN_DIR/../generic ; pwd`
+ elif test -d $TCL_BIN_DIR/../include ; then
+ TCL_DIR=`cd $TCL_BIN_DIR/../include ; pwd`
+ else
+ TCL_DIR=$TCL_BIN_DIR
+ fi
+ . $file
+ CC=$TCL_CC
+ SHLIB_CFLAGS=$TCL_SHLIB_CFLAGS
+ SHLIB_LD=$TCL_SHLIB_LD
+ SHLIB_LD_LIBS=$TCL_SHLIB_LD_LIBS
+ SHLIB_SUFFIX=$TCL_SHLIB_SUFFIX
+ SHLIB_VERSION=$TCL_SHLIB_VERSION
+ DL_LIBS=$TCL_DL_LIBS
+ LD_FLAGS=$TCL_LD_FLAGS
+ LD_SEARCH_FLAGS=$TCL_LD_SEARCH_FLAGS
+else
+ TCL_DIR=$TCL_BIN_DIR
+ TCL_LIB_FILE=libtcl.a
+ TCL_LIB_VERSIONS_OK=no
+ TCL_BUILD_LIB_SPEC="-L$TCL_BIN_DIR -ltcl"
+ TCL_INCLUDE_SPEC="-I$TCL_DIR"
+fi
+
+echo $ac_n "checking Ck version""... $ac_c" 1>&6
+echo "configure:1218: checking Ck version" >&5
+if test "${TCL_VERSION}" = "7.4"; then
+ VERSION=4.0
+ CK_VERSION=4.0
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=0
+elif test "${TCL_VERSION}" = "7.5"; then
+ VERSION=4.1
+ CK_VERSION=4.1
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=1
+elif test "${TCL_VERSION}" = "7.6"; then
+ VERSION=4.2
+ CK_VERSION=4.2
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=2
+else
+ # Assume Tcl8.0 or higher
+ VERSION=$TCL_VERSION
+ CK_VERSION=$VERSION
+ CK_MAJOR_VERSION=$TCL_MAJOR_VERSION
+ CK_MINOR_VERSION=$TCL_MINOR_VERSION
+fi
+echo "$ac_t""${CK_VERSION}" 1>&6
+
+#--------------------------------------------------------------------
+# Include sys/select.h if it exists and if it supplies things
+# that appear to be useful and aren't already in sys/types.h.
+# This appears to be true only on the RS/6000 under AIX. Some
+# systems like OSF/1 have a sys/select.h that's of no use, and
+# other systems like SCO UNIX have a sys/select.h that's
+# pernicious. If "fd_set" isn't defined anywhere then set a
+# special flag.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking fd_set and sys/select""... $ac_c" 1>&6
+echo "configure:1254: checking fd_set and sys/select" >&5
+cat > conftest.$ac_ext <<EOF
+#line 1256 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() {
+fd_set readMask, writeMask;
+; return 0; }
+EOF
+if { (eval echo configure:1263: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ tk_ok=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+if test $tk_ok = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 1275 "configure"
+#include "confdefs.h"
+#include <sys/select.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "fd_mask" >/dev/null 2>&1; then
+ rm -rf conftest*
+ tk_ok=yes
+fi
+rm -f conftest*
+
+ if test $tk_ok = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_SELECT_H 1
+EOF
+
+ fi
+fi
+echo "$ac_t""$tk_ok" 1>&6
+if test $tk_ok = no; then
+ cat >> confdefs.h <<\EOF
+#define NO_FD_SET 1
+EOF
+
+fi
+
+#---------------------------------------------------------------------
+# Locate the curses header files and the curses library archive.
+# The order is:
+# ../ncurses
+# /usr/include and /usr/lib
+# /opt/ncurses
+# /usr/local
+# /usr/local/ncurses
+#---------------------------------------------------------------------
+
+echo checking for curses/ncurses header files
+CURSESINCLUDES=nope
+USE_NCURSES=0
+dirs="../ncurses/include /usr/include /usr/include/ncurses /opt/ncurses/include /usr/local/include /usr/local/include/ncurses"
+for i in $dirs ; do
+ if test -r $i/ncurses.h; then
+ echo $ac_n "checking ncurses headers""... $ac_c" 1>&6
+echo "configure:1318: checking ncurses headers" >&5
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS -I$i"
+ cat > conftest.$ac_ext <<EOF
+#line 1322 "configure"
+#include "confdefs.h"
+#include <ncurses.h>
+int main() {
+int c; initscr(); c = ACS_ULCORNER; curs_set(1);
+; return 0; }
+EOF
+if { (eval echo configure:1329: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ CURSESINCLUDES="-I$i"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ CFLAGS=$tk_oldCFlags
+ if test "$CURSESINCLUDES" != nope; then
+ USE_NCURSES=1
+ break
+ fi
+ fi
+ if test -r $i/curses.h; then
+ echo $ac_n "checking curses headers""... $ac_c" 1>&6
+echo "configure:1348: checking curses headers" >&5
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS -I$i"
+ cat > conftest.$ac_ext <<EOF
+#line 1352 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() {
+int c; initscr(); c = ACS_ULCORNER; curs_set(1);
+; return 0; }
+EOF
+if { (eval echo configure:1359: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ CURSESINCLUDES="-I$i"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ CFLAGS=$tk_oldCFlags
+ if test "$CURSESINCLUDES" != nope; then
+ break
+ fi
+ fi
+done
+if test "$CURSESINCLUDES" = nope; then
+ echo "Warning: couldn't find any curses header file."
+ CURSESINCLUDES="# no header file found"
+else
+ if test $USE_NCURSES = 1 ; then
+ echo "using ncurses.h from $CURSESINCLUDES"
+ else
+ echo "using curses.h from $CURSESINCLUDES"
+ fi
+fi
+
+echo checking for curses/ncurses library files
+CURSESLIBSW=nope
+dirs="../ncurses/lib /usr/lib /usr/lib/ncurses /opt/ncurses/lib /usr/local/lib /usr/local/ncurses/lib"
+for i in $dirs ; do
+ if test $USE_NCURSES = 0 ; then
+ if test -r $i/libcurses.a || test -r $i/libcurses$SHLIB_SUFFIX ; then
+ echo $ac_n "checking curses library""... $ac_c" 1>&6
+echo "configure:1394: checking curses library" >&5
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ if test "$i" = "/usr/lib" ; then
+ LIBSW="-lcurses -ltermcap"
+ else
+ LIBSW="-L$i -lcurses -ltermcap"
+ fi
+ tk_oldLibs=$LIBS
+ LIBS="$LIBSW"
+ cat > conftest.$ac_ext <<EOF
+#line 1405 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() {
+int c; initscr(); c = ACS_ULCORNER; curs_set(1);
+; return 0; }
+EOF
+if { (eval echo configure:1412: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ CURSESLIBSW="$LIBSW"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+ if test "$CURSESLIBSW" != nope; then
+ break
+ fi
+ fi
+ else
+ if test -r $i/libncurses.a || test -r $i/libncurses$SHLIB_SUFFIX ; then
+ echo $ac_n "checking ncurses library""... $ac_c" 1>&6
+echo "configure:1432: checking ncurses library" >&5
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ if test "$i" = "/usr/lib" ; then
+ LIBSW="-lncurses"
+ else
+ LIBSW="-L$i -lncurses"
+ fi
+ tk_oldLibs=$LIBS
+ LIBS="$LIBSW"
+ cat > conftest.$ac_ext <<EOF
+#line 1443 "configure"
+#include "confdefs.h"
+#include <ncurses.h>
+int main() {
+int c; initscr(); c = ACS_ULCORNER; curs_set(1);
+; return 0; }
+EOF
+if { (eval echo configure:1450: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ CURSESLIBSW="$LIBSW"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+ if test "$CURSESLIBSW" != nope; then
+ break
+ fi
+ fi
+ fi
+done
+
+if test "$CURSESLIBSW" = nope ; then
+ echo "Warning: couldn't find the curses library archive. Using -lcurses."
+ CURSESLIBSW="-lcurses -ltermcap"
+else
+ echo "using curses library: $CURSESLIBSW"
+ echo $ac_n "checking curses scr_dump function""... $ac_c" 1>&6
+echo "configure:1476: checking curses scr_dump function" >&5
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ tk_oldLibs=$LIBS
+ LIBS="$CURSESLIBSW"
+ if test $USE_NCURSES = 1 ; then
+ cat > conftest.$ac_ext <<EOF
+#line 1483 "configure"
+#include "confdefs.h"
+#include <ncurses.h>
+int main() {
+int c; initscr(); scr_dump("xx");
+; return 0; }
+EOF
+if { (eval echo configure:1490: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_SCR_DUMP 1
+EOF
+
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ else
+ cat > conftest.$ac_ext <<EOF
+#line 1506 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() {
+int c; initscr(); scr_dump("xx");
+; return 0; }
+EOF
+if { (eval echo configure:1513: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_SCR_DUMP 1
+EOF
+
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+ fi
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+fi
+
+if test $USE_NCURSES = 1 ; then
+ USE_NCURSES="-DUSE_NCURSES"
+else
+ USE_NCURSES=""
+fi
+
+#---------------------------------------------------------------------
+# Check for GPM (General Purpose Mouse)
+#---------------------------------------------------------------------
+
+echo $ac_n "checking GPM library""... $ac_c" 1>&6
+echo "configure:1543: checking GPM library" >&5
+tk_oldLibs=$LIBS
+LIBS="-lgpm"
+cat > conftest.$ac_ext <<EOF
+#line 1547 "configure"
+#include "confdefs.h"
+#include <gpm.h>
+int main() {
+Gpm_Connect conn; Gpm_Open(&conn, 0);
+; return 0; }
+EOF
+if { (eval echo configure:1554: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_GPM 1
+EOF
+
+ CURSESLIBSW="$CURSESLIBSW -lgpm"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+LIBS=$tk_oldLibs
+
+#--------------------------------------------------------------------
+# Check for the existence of various libraries. The order here
+# is important, so that then end up in the right order in the
+# command line generated by make. The -lsocket and -lnsl libraries
+# require a couple of special tricks:
+# 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.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for main in -lXbsd""... $ac_c" 1>&6
+echo "configure:1590: checking for main in -lXbsd" >&5
+ac_lib_var=`echo Xbsd'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lXbsd $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1598 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1605: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -lXbsd"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+tk_checkBoth=0
+echo $ac_n "checking for connect""... $ac_c" 1>&6
+echo "configure:1628: checking for connect" >&5
+if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1633 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char connect(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect();
+
+int main() {
+
+/* 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_connect) || defined (__stub___connect)
+choke me
+#else
+connect();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1656: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_connect=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_connect=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ tk_checkSocket=0
+else
+ echo "$ac_t""no" 1>&6
+tk_checkSocket=1
+fi
+
+if test "$tk_checkSocket" = 1; then
+ echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6
+echo "configure:1678: checking for main in -lsocket" >&5
+ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1686 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1693: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -lsocket"
+else
+ echo "$ac_t""no" 1>&6
+tk_checkBoth=1
+fi
+
+fi
+if test "$tk_checkBoth" = 1; then
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lsocket -lnsl"
+ echo $ac_n "checking for accept""... $ac_c" 1>&6
+echo "configure:1719: checking for accept" >&5
+if eval "test \"`echo '$''{'ac_cv_func_accept'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1724 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char accept(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char accept();
+
+int main() {
+
+/* 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_accept) || defined (__stub___accept)
+choke me
+#else
+accept();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1747: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_accept=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_accept=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'accept`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ tk_checkNsl=0
+else
+ echo "$ac_t""no" 1>&6
+LIBS=$tk_oldLibs
+fi
+
+fi
+echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:1769: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1774 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+
+/* 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_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1797: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6
+echo "configure:1815: checking for main in -lnsl" >&5
+ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1823 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1830: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -lnsl"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+#--------------------------------------------------------------------
+# On Interactive the str(n)casecmp is burried in libinet.a
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for strncasecmp""... $ac_c" 1>&6
+echo "configure:1858: checking for strncasecmp" >&5
+if eval "test \"`echo '$''{'ac_cv_func_strncasecmp'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1863 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char strncasecmp(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strncasecmp();
+
+int main() {
+
+/* 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_strncasecmp) || defined (__stub___strncasecmp)
+choke me
+#else
+strncasecmp();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1886: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_strncasecmp=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_strncasecmp=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'strncasecmp`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for main in -linet""... $ac_c" 1>&6
+echo "configure:1904: checking for main in -linet" >&5
+ac_lib_var=`echo inet'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-linet $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1912 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1919: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -linet"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+#--------------------------------------------------------------------
+# Figure out how to find out whether a FILE structure contains
+# buffered readable data. Some known names for the count field:
+# _cnt: Most UNIX systems
+# __cnt: HPUX
+# _r: BSD
+# readCount: Sprite
+# Or, in GNU libc there are two fields, _gptr and _egptr, which
+# have to be compared.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking count field in FILE structures""... $ac_c" 1>&6
+echo "configure:1954: checking count field in FILE structures" >&5
+cat > conftest.$ac_ext <<EOF
+#line 1956 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->_cnt = 0;
+; return 0; }
+EOF
+if { (eval echo configure:1963: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ fcnt="_cnt"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test "$fcnt" = ""; then
+ cat > conftest.$ac_ext <<EOF
+#line 1973 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->__cnt = 0;
+; return 0; }
+EOF
+if { (eval echo configure:1980: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ fcnt="__cnt"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+fi
+if test "$fcnt" = ""; then
+ cat > conftest.$ac_ext <<EOF
+#line 1991 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->_r = 0;
+; return 0; }
+EOF
+if { (eval echo configure:1998: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ fcnt="_r"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+fi
+if test "$fcnt" = ""; then
+ cat > conftest.$ac_ext <<EOF
+#line 2009 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->readCount = 0;
+; return 0; }
+EOF
+if { (eval echo configure:2016: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ fcnt="readCount"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+fi
+if test "$fcnt" != ""; then
+ cat >> confdefs.h <<EOF
+#define TK_FILE_COUNT $fcnt
+EOF
+
+fi
+if test "$fcnt" = ""; then
+ cat > conftest.$ac_ext <<EOF
+#line 2033 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->_gptr = f->_egptr;
+; return 0; }
+EOF
+if { (eval echo configure:2040: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ tk_ok=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+ if test $tk_ok = yes; then
+ cat >> confdefs.h <<\EOF
+#define TK_FILE_GPTR 1
+EOF
+
+ fcnt="_gptr/_egptr"
+ fi
+fi
+if test "$fcnt" = ""; then
+ cat > conftest.$ac_ext <<EOF
+#line 2060 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+int main() {
+FILE *f = stdin; f->_IO_read_ptr = f->_IO_read_end;
+; return 0; }
+EOF
+if { (eval echo configure:2067: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ tk_ok=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ tk_ok=no
+fi
+rm -f conftest*
+ if test $tk_ok = yes; then
+ cat >> confdefs.h <<\EOF
+#define TK_FILE_READ_PTR 1
+EOF
+
+ fcnt="_IO_read_ptr/_IO_read_end"
+ fi
+fi
+if test "$fcnt" = ""; then
+ echo "$ac_t""not found; must supply TkReadDataPending procedure" 1>&6
+else
+ echo "$ac_t"""$fcnt"" 1>&6
+fi
+
+#--------------------------------------------------------------------
+# 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").
+#--------------------------------------------------------------------
+
+MATH_LIBS=""
+echo $ac_n "checking for sin""... $ac_c" 1>&6
+echo "configure:2100: checking for sin" >&5
+if eval "test \"`echo '$''{'ac_cv_func_sin'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2105 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char sin(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sin();
+
+int main() {
+
+/* 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_sin) || defined (__stub___sin)
+choke me
+#else
+sin();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2128: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_sin=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_sin=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'sin`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+MATH_LIBS="-lm"
+fi
+
+echo $ac_n "checking for main in -lieee""... $ac_c" 1>&6
+echo "configure:2149: checking for main in -lieee" >&5
+ac_lib_var=`echo ieee'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lieee $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2157 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2164: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ MATH_LIBS="-lieee $MATH_LIBS"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+#--------------------------------------------------------------------
+# If this system doesn't have a memmove procedure, use memcpy
+# instead.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for memmove""... $ac_c" 1>&6
+echo "configure:2191: checking for memmove" >&5
+if eval "test \"`echo '$''{'ac_cv_func_memmove'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2196 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char memmove(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char memmove();
+
+int main() {
+
+/* 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_memmove) || defined (__stub___memmove)
+choke me
+#else
+memmove();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2219: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_memmove=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_memmove=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'memmove`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+cat >> confdefs.h <<\EOF
+#define memmove memcpy
+EOF
+
+fi
+
+
+#--------------------------------------------------------------------
+# SGI systems don't use the BSD form of the gettimeofday function,
+# but they have a BSDgettimeofday function that can be used instead.
+#
+# Also, check for the existence of a gettimeofday declaration,
+# to tkPort.h can declare it if it isn't already declared.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for BSDgettimeofday""... $ac_c" 1>&6
+echo "configure:2252: checking for BSDgettimeofday" >&5
+if eval "test \"`echo '$''{'ac_cv_func_BSDgettimeofday'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2257 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char BSDgettimeofday(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char BSDgettimeofday();
+
+int main() {
+
+/* 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_BSDgettimeofday) || defined (__stub___BSDgettimeofday)
+choke me
+#else
+BSDgettimeofday();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2280: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_BSDgettimeofday=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_BSDgettimeofday=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'BSDgettimeofday`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_BSDGETTIMEOFDAY 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for gettimeofday declaration""... $ac_c" 1>&6
+echo "configure:2303: checking for gettimeofday declaration" >&5
+cat > conftest.$ac_ext <<EOF
+#line 2305 "configure"
+#include "confdefs.h"
+#include <sys/time.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "gettimeofday" >/dev/null 2>&1; then
+ rm -rf conftest*
+ echo "$ac_t""present" 1>&6
+else
+ rm -rf conftest*
+
+ echo "$ac_t""missing" 1>&6
+ cat >> confdefs.h <<\EOF
+#define GETTOD_NOT_DECLARED 1
+EOF
+
+
+fi
+rm -f conftest*
+
+
+#--------------------------------------------------------------------
+# 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.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for strtod""... $ac_c" 1>&6
+echo "configure:2334: checking for strtod" >&5
+if eval "test \"`echo '$''{'ac_cv_func_strtod'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2339 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char strtod(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strtod();
+
+int main() {
+
+/* 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_strtod) || defined (__stub___strtod)
+choke me
+#else
+strtod();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2362: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_strtod=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_strtod=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'strtod`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ tk_strtod=1
+else
+ echo "$ac_t""no" 1>&6
+tk_strtod=0
+fi
+
+if test "$tk_strtod" = 1; then
+ echo $ac_n "checking for Solaris 2.4 strtod bug""... $ac_c" 1>&6
+echo "configure:2384: checking for Solaris 2.4 strtod bug" >&5
+ if test "$cross_compiling" = yes; then
+ tk_ok=0
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2389 "configure"
+#include "confdefs.h"
+
+ extern double strtod();
+ int main()
+ {
+ char *string = "NaN";
+ char *term;
+ strtod(string, &term);
+ if ((term != string) && (term[-1] == 0)) {
+ exit(1);
+ }
+ exit(0);
+ }
+EOF
+if { (eval echo configure:2404: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ tk_ok=1
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ tk_ok=0
+fi
+rm -fr conftest*
+fi
+
+ if test "$tk_ok" = 1; then
+ echo "$ac_t""ok" 1>&6
+ else
+ echo "$ac_t""buggy" 1>&6
+ cat >> confdefs.h <<\EOF
+#define strtod fixstrtod
+EOF
+
+ fi
+fi
+
+#--------------------------------------------------------------------
+# The statements below define a collection of symbols related to
+# building libck as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+# Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+ enableval="$enable_shared"
+ ok=$enableval
+else
+ ok=no
+fi
+
+if test "$ok" = "yes" -a "${SHLIB_SUFFIX}" != ""; then
+ CK_SHLIB_CFLAGS="${SHLIB_CFLAGS}"
+ eval "CK_LIB_FILE=libck${TCL_SHARED_LIB_SUFFIX}"
+ MAKE_LIB="\${SHLIB_LD} -o ${CK_LIB_FILE} \${OBJS} ${SHLIB_LD_LIBS}"
+ RANLIB=":"
+else
+ CK_SHLIB_CFLAGS=""
+ eval "CK_LIB_FILE=libck${TCL_UNSHARED_LIB_SUFFIX}"
+ # Fixup if suffix missing
+ if test "$CK_LIB_FILE" = "libck" ; then
+ CK_LIB_FILE=libck.a
+ fi
+ MAKE_LIB="ar cr ${CK_LIB_FILE} \${OBJS}"
+fi
+
+# Note: in the following variable, it's important to use the absolute
+# path name of the Tcl directory rather than "..": this is because
+# AIX remembers this path and will attempt to use it at run-time to look
+# up the Tcl library.
+
+if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ CK_BUILD_LIB_SPEC="-L`pwd` -lck${VERSION}"
+ CK_LIB_SPEC="-L${exec_prefix}/lib -lck${VERSION}"
+else
+ CK_BUILD_LIB_SPEC="-L`pwd` -lck`echo ${VERSION} | tr -d .`"
+ CK_LIB_SPEC="-L${exec_prefix}/lib -lck`echo ${VERSION} | tr -d .`"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# 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. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# 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 \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile ckConfig.sh pkgIndex.tcl" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@CPP@%$CPP%g
+s%@CC@%$CC%g
+s%@DL_LIBS@%$DL_LIBS%g
+s%@LD_FLAGS@%$LD_FLAGS%g
+s%@MATH_LIBS@%$MATH_LIBS%g
+s%@MAKE_LIB@%$MAKE_LIB%g
+s%@SHLIB_CFLAGS@%$SHLIB_CFLAGS%g
+s%@SHLIB_LD@%$SHLIB_LD%g
+s%@SHLIB_LD_LIBS@%$SHLIB_LD_LIBS%g
+s%@SHLIB_SUFFIX@%$SHLIB_SUFFIX%g
+s%@SHLIB_VERSION@%$SHLIB_VERSION%g
+s%@TCL_DIR@%$TCL_DIR%g
+s%@TCL_BIN_DIR@%$TCL_BIN_DIR%g
+s%@TCL_LIB@%$TCL_LIB%g
+s%@TCL_INCLUDE_SPEC@%$TCL_INCLUDE_SPEC%g
+s%@TCL_VERSION@%$TCL_VERSION%g
+s%@TCL_BUILD_LIB_SPEC@%$TCL_BUILD_LIB_SPEC%g
+s%@CK_LD_SEARCH_FLAGS@%$CK_LD_SEARCH_FLAGS%g
+s%@CK_LIB_FILE@%$CK_LIB_FILE%g
+s%@CK_LIB_SPEC@%$CK_LIB_SPEC%g
+s%@CK_MAJOR_VERSION@%$CK_MAJOR_VERSION%g
+s%@CK_MINOR_VERSION@%$CK_MINOR_VERSION%g
+s%@CK_SHLIB_CFLAGS@%$CK_SHLIB_CFLAGS%g
+s%@CK_VERSION@%$CK_VERSION%g
+s%@CK_BUILD_LIB_SPEC@%$CK_BUILD_LIB_SPEC%g
+s%@USE_NCURSES@%$USE_NCURSES%g
+s%@CURSESINCLUDES@%$CURSESINCLUDES%g
+s%@CURSESLIBSW@%$CURSESLIBSW%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# 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_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # 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" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile ckConfig.sh pkgIndex.tcl"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+dnl This file is an input file used by the GNU "autoconf" program to
+dnl generate the file "configure", which is run during Ck installation
+dnl to configure the system for the local environment.
+AC_INIT(ck.h)
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+CC=${CC-cc}
+AC_HAVE_HEADERS(unistd.h limits.h)
+
+#--------------------------------------------------------------------
+# Supply a substitute for stdlib.h if it doesn't define strtol,
+# strtoul, or strtod (which it doesn't in some versions of SunOS).
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING(stdlib.h)
+AC_HEADER_EGREP(strtol, stdlib.h, tk_ok=yes, tk_ok=no)
+AC_HEADER_EGREP(strtoul, stdlib.h, , tk_ok=no)
+AC_HEADER_EGREP(strtod, stdlib.h, , tk_ok=no)
+if test $tk_ok = no; then
+ AC_DEFINE(NO_STDLIB_H)
+fi
+AC_MSG_RESULT($tk_ok)
+
+#--------------------------------------------------------------------
+# Check for various typedefs and provide substitutes if
+# they don't exist.
+#--------------------------------------------------------------------
+
+AC_MODE_T
+AC_PID_T
+AC_SIZE_T
+AC_UID_T
+
+#------------------------------------------------------------------------------
+# What type do signals return? Do we have sigaction ?
+#------------------------------------------------------------------------------
+
+AC_RETSIGTYPE
+AC_HAVE_FUNCS(sigaction)
+
+#--------------------------------------------------------------------
+# See if there was a command-line option for where Tcl is; if
+# not, assume that its top-level directory is a sibling of ours.
+#--------------------------------------------------------------------
+
+AC_ARG_WITH(tcl, [ --with-tcl=DIR use Tcl 8.X binaries from DIR],
+ TCL_BIN_DIR=$withval, TCL_BIN_DIR=`cd ../tcl8.0/unix ; pwd`)
+if test ! -d $TCL_BIN_DIR ; then
+ AC_MSG_ERROR(Tcl directory $TCL_BIN_DIR doesn't exist)
+fi
+
+#--------------------------------------------------------------------
+# Read in configuration information generated by Tcl for shared
+# libraries, and arrange for it to be substituted into our
+# Makefile.
+#--------------------------------------------------------------------
+
+if test -r $TCL_BIN_DIR/tclConfig.sh ; then
+ file=$TCL_BIN_DIR/tclConfig.sh
+ if test -d $TCL_BIN_DIR/../generic ; then
+ TCL_DIR=`cd $TCL_BIN_DIR/../generic ; pwd`
+ elif test -d $TCL_BIN_DIR/../include ; then
+ TCL_DIR=`cd $TCL_BIN_DIR/../include ; pwd`
+ else
+ TCL_DIR=$TCL_BIN_DIR
+ fi
+ . $file
+ CC=$TCL_CC
+ SHLIB_CFLAGS=$TCL_SHLIB_CFLAGS
+ SHLIB_LD=$TCL_SHLIB_LD
+ SHLIB_LD_LIBS=$TCL_SHLIB_LD_LIBS
+ SHLIB_SUFFIX=$TCL_SHLIB_SUFFIX
+ SHLIB_VERSION=$TCL_SHLIB_VERSION
+ DL_LIBS=$TCL_DL_LIBS
+ LD_FLAGS=$TCL_LD_FLAGS
+ LD_SEARCH_FLAGS=$TCL_LD_SEARCH_FLAGS
+else
+ TCL_DIR=$TCL_BIN_DIR
+ TCL_LIB_FILE=libtcl.a
+ TCL_LIB_VERSIONS_OK=no
+ TCL_BUILD_LIB_SPEC="-L$TCL_BIN_DIR -ltcl"
+ TCL_INCLUDE_SPEC="-I$TCL_DIR"
+fi
+
+AC_MSG_CHECKING([Ck version])
+if test "${TCL_VERSION}" = "7.4"; then
+ VERSION=4.0
+ CK_VERSION=4.0
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=0
+elif test "${TCL_VERSION}" = "7.5"; then
+ VERSION=4.1
+ CK_VERSION=4.1
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=1
+elif test "${TCL_VERSION}" = "7.6"; then
+ VERSION=4.2
+ CK_VERSION=4.2
+ CK_MAJOR_VERSION=4
+ CK_MINOR_VERSION=2
+else
+ # Assume Tcl8.0 or higher
+ VERSION=$TCL_VERSION
+ CK_VERSION=$VERSION
+ CK_MAJOR_VERSION=$TCL_MAJOR_VERSION
+ CK_MINOR_VERSION=$TCL_MINOR_VERSION
+fi
+AC_MSG_RESULT(${CK_VERSION})
+
+#--------------------------------------------------------------------
+# Include sys/select.h if it exists and if it supplies things
+# that appear to be useful and aren't already in sys/types.h.
+# This appears to be true only on the RS/6000 under AIX. Some
+# systems like OSF/1 have a sys/select.h that's of no use, and
+# other systems like SCO UNIX have a sys/select.h that's
+# pernicious. If "fd_set" isn't defined anywhere then set a
+# special flag.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([fd_set and sys/select])
+AC_TRY_COMPILE([#include <sys/types.h>],
+ [fd_set readMask, writeMask;], tk_ok=yes, tk_ok=no)
+if test $tk_ok = no; then
+ AC_HEADER_EGREP(fd_mask, sys/select.h, tk_ok=yes)
+ if test $tk_ok = yes; then
+ AC_DEFINE(HAVE_SYS_SELECT_H)
+ fi
+fi
+AC_MSG_RESULT($tk_ok)
+if test $tk_ok = no; then
+ AC_DEFINE(NO_FD_SET)
+fi
+
+#---------------------------------------------------------------------
+# Locate the curses header files and the curses library archive.
+# The order is:
+# ../ncurses
+# /usr/include and /usr/lib
+# /opt/ncurses
+# /usr/local
+# /usr/local/ncurses
+#---------------------------------------------------------------------
+
+echo checking for curses/ncurses header files
+CURSESINCLUDES=nope
+USE_NCURSES=0
+dirs="../ncurses/include /usr/include /usr/include/ncurses /opt/ncurses/include /usr/local/include /usr/local/include/ncurses"
+for i in $dirs ; do
+ if test -r $i/ncurses.h; then
+ AC_MSG_CHECKING([ncurses headers])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS -I$i"
+ AC_TRY_COMPILE([#include <ncurses.h>],
+ [int c; initscr(); c = ACS_ULCORNER; curs_set(1);],
+ [AC_MSG_RESULT(yes)
+ CURSESINCLUDES="-I$i"], AC_MSG_RESULT(no))
+ CFLAGS=$tk_oldCFlags
+ if test "$CURSESINCLUDES" != nope; then
+ USE_NCURSES=1
+ break
+ fi
+ fi
+ if test -r $i/curses.h; then
+ AC_MSG_CHECKING([curses headers])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS -I$i"
+ AC_TRY_COMPILE([#include <curses.h>],
+ [int c; initscr(); c = ACS_ULCORNER; curs_set(1);],
+ [AC_MSG_RESULT(yes)
+ CURSESINCLUDES="-I$i"], AC_MSG_RESULT(no))
+ CFLAGS=$tk_oldCFlags
+ if test "$CURSESINCLUDES" != nope; then
+ break
+ fi
+ fi
+done
+if test "$CURSESINCLUDES" = nope; then
+ echo "Warning: couldn't find any curses header file."
+ CURSESINCLUDES="# no header file found"
+else
+ if test $USE_NCURSES = 1 ; then
+ echo "using ncurses.h from $CURSESINCLUDES"
+ else
+ echo "using curses.h from $CURSESINCLUDES"
+ fi
+fi
+
+echo checking for curses/ncurses library files
+CURSESLIBSW=nope
+dirs="../ncurses/lib /usr/lib /usr/lib/ncurses /opt/ncurses/lib /usr/local/lib /usr/local/ncurses/lib"
+for i in $dirs ; do
+ if test $USE_NCURSES = 0 ; then
+ if test -r $i/libcurses.a || test -r $i/libcurses$SHLIB_SUFFIX ; then
+ AC_MSG_CHECKING([curses library])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ if test "$i" = "/usr/lib" ; then
+ LIBSW="-lcurses -ltermcap"
+ else
+ LIBSW="-L$i -lcurses -ltermcap"
+ fi
+ tk_oldLibs=$LIBS
+ LIBS="$LIBSW"
+ AC_TRY_LINK([#include <curses.h>],
+ [int c; initscr(); c = ACS_ULCORNER; curs_set(1);],
+ [AC_MSG_RESULT(yes)
+ CURSESLIBSW="$LIBSW"], AC_MSG_RESULT(no))
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+ if test "$CURSESLIBSW" != nope; then
+ break
+ fi
+ fi
+ else
+ if test -r $i/libncurses.a || test -r $i/libncurses$SHLIB_SUFFIX ; then
+ AC_MSG_CHECKING([ncurses library])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ if test "$i" = "/usr/lib" ; then
+ LIBSW="-lncurses"
+ else
+ LIBSW="-L$i -lncurses"
+ fi
+ tk_oldLibs=$LIBS
+ LIBS="$LIBSW"
+ AC_TRY_LINK([#include <ncurses.h>],
+ [int c; initscr(); c = ACS_ULCORNER; curs_set(1);],
+ [AC_MSG_RESULT(yes)
+ CURSESLIBSW="$LIBSW"], AC_MSG_RESULT(no))
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+ if test "$CURSESLIBSW" != nope; then
+ break
+ fi
+ fi
+ fi
+done
+
+if test "$CURSESLIBSW" = nope ; then
+ echo "Warning: couldn't find the curses library archive. Using -lcurses."
+ CURSESLIBSW="-lcurses -ltermcap"
+else
+ echo "using curses library: $CURSESLIBSW"
+ AC_MSG_CHECKING([curses scr_dump function])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS $CURSESINCLUDES"
+ tk_oldLibs=$LIBS
+ LIBS="$CURSESLIBSW"
+ if test $USE_NCURSES = 1 ; then
+ AC_TRY_LINK([#include <ncurses.h>],
+ [int c; initscr(); scr_dump("xx");],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_SCR_DUMP)], AC_MSG_RESULT(no))
+ else
+ AC_TRY_LINK([#include <curses.h>],
+ [int c; initscr(); scr_dump("xx");],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_SCR_DUMP)], AC_MSG_RESULT(no))
+ fi
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+fi
+
+if test $USE_NCURSES = 1 ; then
+ USE_NCURSES="-DUSE_NCURSES"
+else
+ USE_NCURSES=""
+fi
+
+#---------------------------------------------------------------------
+# Check for GPM (General Purpose Mouse)
+#---------------------------------------------------------------------
+
+AC_MSG_CHECKING([GPM library])
+tk_oldLibs=$LIBS
+LIBS="-lgpm"
+AC_TRY_LINK([#include <gpm.h>],
+ [Gpm_Connect conn; Gpm_Open(&conn, 0);],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_GPM)
+ CURSESLIBSW="$CURSESLIBSW -lgpm"],
+ [AC_MSG_RESULT(no)])
+LIBS=$tk_oldLibs
+
+#--------------------------------------------------------------------
+# Check for the existence of various libraries. The order here
+# is important, so that then end up in the right order in the
+# command line generated by make. The -lsocket and -lnsl libraries
+# require a couple of special tricks:
+# 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.
+#--------------------------------------------------------------------
+
+AC_CHECK_LIB(Xbsd, main, [LIBS="$LIBS -lXbsd"])
+
+tk_checkBoth=0
+AC_CHECK_FUNC(connect, tk_checkSocket=0, tk_checkSocket=1)
+if test "$tk_checkSocket" = 1; then
+ AC_CHECK_LIB(socket, main, LIBS="$LIBS -lsocket", tk_checkBoth=1)
+fi
+if test "$tk_checkBoth" = 1; then
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lsocket -lnsl"
+ AC_CHECK_FUNC(accept, tk_checkNsl=0, [LIBS=$tk_oldLibs])
+fi
+AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
+
+#--------------------------------------------------------------------
+# On Interactive the str(n)casecmp is burried in libinet.a
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strncasecmp, , AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"]))
+
+#--------------------------------------------------------------------
+# Figure out how to find out whether a FILE structure contains
+# buffered readable data. Some known names for the count field:
+# _cnt: Most UNIX systems
+# __cnt: HPUX
+# _r: BSD
+# readCount: Sprite
+# Or, in GNU libc there are two fields, _gptr and _egptr, which
+# have to be compared.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([count field in FILE structures])
+AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->_cnt = 0;], fcnt="_cnt", )
+if test "$fcnt" = ""; then
+ AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->__cnt = 0;], fcnt="__cnt", )
+fi
+if test "$fcnt" = ""; then
+ AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->_r = 0;], fcnt="_r", )
+fi
+if test "$fcnt" = ""; then
+ AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->readCount = 0;], fcnt="readCount", )
+fi
+if test "$fcnt" != ""; then
+ AC_DEFINE_UNQUOTED(TK_FILE_COUNT, $fcnt)
+fi
+if test "$fcnt" = ""; then
+ AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->_gptr = f->_egptr;],
+ tk_ok=yes, tk_ok=no)
+ if test $tk_ok = yes; then
+ AC_DEFINE(TK_FILE_GPTR)
+ fcnt="_gptr/_egptr"
+ fi
+fi
+if test "$fcnt" = ""; then
+ AC_TRY_COMPILE([#include <stdio.h>],
+ [FILE *f = stdin; f->_IO_read_ptr = f->_IO_read_end;],
+ tk_ok=yes, tk_ok=no)
+ if test $tk_ok = yes; then
+ AC_DEFINE(TK_FILE_READ_PTR)
+ fcnt="_IO_read_ptr/_IO_read_end"
+ fi
+fi
+if test "$fcnt" = ""; then
+ AC_MSG_RESULT([not found; must supply TkReadDataPending procedure])
+else
+ AC_MSG_RESULT("$fcnt")
+fi
+
+#--------------------------------------------------------------------
+# 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").
+#--------------------------------------------------------------------
+
+MATH_LIBS=""
+AC_CHECK_FUNC(sin, , MATH_LIBS="-lm")
+AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+#--------------------------------------------------------------------
+# If this system doesn't have a memmove procedure, use memcpy
+# instead.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(memmove, , [AC_DEFINE(memmove, memcpy)])
+
+#--------------------------------------------------------------------
+# SGI systems don't use the BSD form of the gettimeofday function,
+# but they have a BSDgettimeofday function that can be used instead.
+#
+# Also, check for the existence of a gettimeofday declaration,
+# to tkPort.h can declare it if it isn't already declared.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(BSDgettimeofday, AC_DEFINE(HAVE_BSDGETTIMEOFDAY))
+AC_MSG_CHECKING([for gettimeofday declaration])
+AC_EGREP_HEADER(gettimeofday, sys/time.h, AC_MSG_RESULT(present), [
+ AC_MSG_RESULT(missing)
+ AC_DEFINE(GETTOD_NOT_DECLARED)
+])
+
+#--------------------------------------------------------------------
+# 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.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strtod, tk_strtod=1, tk_strtod=0)
+if test "$tk_strtod" = 1; then
+ AC_MSG_CHECKING([for Solaris 2.4 strtod bug])
+ AC_TRY_RUN([
+ extern double strtod();
+ int main()
+ {
+ char *string = "NaN";
+ char *term;
+ strtod(string, &term);
+ if ((term != string) && (term[-1] == 0)) {
+ exit(1);
+ }
+ exit(0);
+ }], tk_ok=1, tk_ok=0, tk_ok=0)
+ if test "$tk_ok" = 1; then
+ AC_MSG_RESULT(ok)
+ else
+ AC_MSG_RESULT(buggy)
+ AC_DEFINE(strtod, fixstrtod)
+ fi
+fi
+
+#--------------------------------------------------------------------
+# The statements below define a collection of symbols related to
+# building libck as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+AC_ARG_ENABLE(shared,
+ [ --enable-shared build libck as a shared library],
+ [ok=$enableval], [ok=no])
+if test "$ok" = "yes" -a "${SHLIB_SUFFIX}" != ""; then
+ CK_SHLIB_CFLAGS="${SHLIB_CFLAGS}"
+ eval "CK_LIB_FILE=libck${TCL_SHARED_LIB_SUFFIX}"
+ MAKE_LIB="\${SHLIB_LD} -o ${CK_LIB_FILE} \${OBJS} ${SHLIB_LD_LIBS}"
+ RANLIB=":"
+else
+ CK_SHLIB_CFLAGS=""
+ eval "CK_LIB_FILE=libck${TCL_UNSHARED_LIB_SUFFIX}"
+ # Fixup if suffix missing
+ if test "$CK_LIB_FILE" = "libck" ; then
+ CK_LIB_FILE=libck.a
+ fi
+ MAKE_LIB="ar cr ${CK_LIB_FILE} \${OBJS}"
+fi
+
+# Note: in the following variable, it's important to use the absolute
+# path name of the Tcl directory rather than "..": this is because
+# AIX remembers this path and will attempt to use it at run-time to look
+# up the Tcl library.
+
+if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ CK_BUILD_LIB_SPEC="-L`pwd` -lck${VERSION}"
+ CK_LIB_SPEC="-L${exec_prefix}/lib -lck${VERSION}"
+else
+ CK_BUILD_LIB_SPEC="-L`pwd` -lck`echo ${VERSION} | tr -d .`"
+ CK_LIB_SPEC="-L${exec_prefix}/lib -lck`echo ${VERSION} | tr -d .`"
+fi
+
+AC_SUBST(CC)
+AC_SUBST(DL_LIBS)
+AC_SUBST(LD_FLAGS)
+AC_SUBST(MATH_LIBS)
+AC_SUBST(MAKE_LIB)
+AC_SUBST(SHLIB_CFLAGS)
+AC_SUBST(SHLIB_LD)
+AC_SUBST(SHLIB_LD_LIBS)
+AC_SUBST(SHLIB_SUFFIX)
+AC_SUBST(SHLIB_VERSION)
+AC_SUBST(TCL_DIR)
+AC_SUBST(TCL_BIN_DIR)
+AC_SUBST(TCL_LIB)
+AC_SUBST(TCL_INCLUDE_SPEC)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(TCL_BUILD_LIB_SPEC)
+AC_SUBST(CK_LD_SEARCH_FLAGS)
+AC_SUBST(CK_LIB_FILE)
+AC_SUBST(CK_LIB_SPEC)
+AC_SUBST(CK_MAJOR_VERSION)
+AC_SUBST(CK_MINOR_VERSION)
+AC_SUBST(CK_SHLIB_CFLAGS)
+AC_SUBST(CK_VERSION)
+AC_SUBST(CK_BUILD_LIB_SPEC)
+AC_SUBST(USE_NCURSES)
+AC_SUBST(CURSESINCLUDES)
+AC_SUBST(CURSESLIBSW)
+
+
+AC_OUTPUT(Makefile ckConfig.sh pkgIndex.tcl)
--- /dev/null
+#define RESOURCE_INCLUDED
+#include <ck.h>
+
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION CK_MAJOR_VERSION,CK_MINOR_VERSION,0,0
+ PRODUCTVERSION CK_MAJOR_VERSION,CK_MINOR_VERSION,0,0
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0x0L
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Curses based Windowing SHell\0"
+ VALUE "OriginalFilename", "cwsh.exe\0"
+ VALUE "CompanyName", "\0"
+ VALUE "FileVersion", CK_VERSION,
+ VALUE "LegalCopyright", "Copyright \251 2000-2001 Christian Werner\0"
+ VALUE "ProductName", "Ck " CK_VERSION " for Windows\0"
+ VALUE "ProductVersion", CK_VERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
--- /dev/null
+#
+# Write out list of default widget options
+#
+
+defscript1() {
+ cat >/tmp/d$$.tcl <<'EOD'
+set count 0
+foreach c [lsort -ascii [info commands]] {
+ if {$c == "puts" || $c == "vwait"} {
+ continue
+ }
+ if {[catch {$c .w$count} tmp]} {
+ continue
+ }
+ if {"$tmp" != ".w$count"} {
+ continue
+ }
+ if {[catch {.w$count configure} clist]} {
+ incr count
+ continue
+ }
+ puts stderr [format "\n\t\t\t%s\n" $c]
+ foreach i [lsort -ascii $clist] {
+ if {[llength $i] > 2} {
+ puts stderr [format "%-35s\t%s" [lindex $i 0] [lindex $i 3]]
+ }
+ }
+ incr count
+}
+exit 0
+EOD
+echo /tmp/d$$.tcl
+}
+
+#
+# Write out list of class bindings
+#
+
+defscript2() {
+ cat >/tmp/d$$.tcl <<'EOD'
+proc all w { return $w }
+set count 0
+foreach c [lsort -ascii [info commands]] {
+ if {$c == "puts" || $c == "vwait"} {
+ continue
+ }
+ if {[catch {$c .w$count} tmp]} {
+ continue
+ }
+ if {"$tmp" != ".w$count"} {
+ continue
+ }
+ set class $c
+ if {$class != "all" && [catch {winfo class .w$count} class]} {
+ incr count
+ continue
+ }
+ puts stderr [format "\n\t\t\t%s\n" $class]
+ set out ""
+ set icnt 0
+ foreach i [lsort -ascii [bind $class]] {
+ append out [format "%-19.19s" $i]
+ incr icnt
+ if {$icnt % 4 == 0} {
+ append out "\n"
+ } else {
+ append out " "
+ }
+ }
+ if {$out == ""} {
+ set out "*** no events bound to class ***"
+ }
+ puts stderr [string trimright $out "\n"]
+ incr count
+}
+exit 0
+EOD
+echo /tmp/d$$.tcl
+}
+
+
+SCRIPT=`defscript1`
+
+rm -f def.list
+exec 2>def.list
+echo "Terminals w/ color" >&2
+echo "------------------" >&2
+TERM=color_xterm ./cwsh $SCRIPT
+echo -e "\f" >&2
+echo "Terminals w/o color" >&2
+echo "-------------------" >&2
+TERM=vt100 ./cwsh $SCRIPT
+rm -f $SCRIPT
+
+SCRIPT=`defscript2`
+
+echo -e "\f" >&2
+echo "Events bound to classes" >&2
+echo "-----------------------" >&2
+TERM=vt100 ./cwsh $SCRIPT
+rm -f $SCRIPT
+
--- /dev/null
+/*
+ * default.h --
+ *
+ * This file defines the defaults for all options for all widgets.
+ *
+ * Copyright (c) 1995 Christian Werner.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+#define DEF_FRAME_ATTRIB "normal"
+#define DEF_FRAME_BG_COLOR "black"
+#define DEF_FRAME_BG_MONO "black"
+#define DEF_FRAME_FG_COLOR "white"
+#define DEF_FRAME_FG_MONO "white"
+#define DEF_FRAME_BORDER NULL
+#define DEF_FRAME_HEIGHT "0"
+#define DEF_FRAME_TAKE_FOCUS "0"
+#define DEF_FRAME_WIDTH "0"
+
+
+#define DEF_BUTTON_ACTIVE_ATTR_COLOR "normal"
+#define DEF_BUTTON_ACTIVE_ATTR_MONO "reverse"
+#define DEF_BUTTON_ACTIVE_BG_COLOR "white"
+#define DEF_BUTTON_ACTIVE_BG_MONO "black"
+#define DEF_BUTTON_ACTIVE_FG_COLOR "black"
+#define DEF_BUTTON_ACTIVE_FG_MONO "white"
+#define DEF_BUTTON_ATTR "normal"
+#define DEF_LABEL_ATTR "normal"
+#define DEF_BUTTON_ANCHOR "center"
+#define DEF_BUTTON_BG_COLOR "black"
+#define DEF_BUTTON_BG_MONO "black"
+#define DEF_BUTTON_COMMAND NULL
+#define DEF_BUTTON_DISABLED_ATTR "dim"
+#define DEF_BUTTON_DISABLED_BG_COLOR "black"
+#define DEF_BUTTON_DISABLED_BG_MONO "black"
+#define DEF_BUTTON_DISABLED_FG_COLOR "white"
+#define DEF_BUTTON_DISABLED_FG_MONO "white"
+#define DEF_BUTTON_FG "white"
+#define DEF_BUTTON_HEIGHT "0"
+#define DEF_BUTTON_OFF_VALUE "0"
+#define DEF_BUTTON_ON_VALUE "1"
+#define DEF_BUTTON_SELECT_COLOR "red"
+#define DEF_BUTTON_SELECT_MONO "white"
+#define DEF_BUTTON_STATE "normal"
+#define DEF_BUTTON_TAKE_FOCUS "1"
+#define DEF_LABEL_TAKE_FOCUS "0"
+#define DEF_BUTTON_TEXT NULL
+#define DEF_BUTTON_TEXT_VARIABLE NULL
+#define DEF_BUTTON_UNDERLINE "-1"
+#define DEF_BUTTON_UNDERLINE_ATTR "bold"
+#define DEF_BUTTON_UNDERLINE_FG_COLOR "white"
+#define DEF_BUTTON_UNDERLINE_FG_MONO "white"
+#define DEF_BUTTON_VALUE NULL
+#define DEF_RADIOBUTTON_VARIABLE NULL
+#define DEF_CHECKBUTTON_VARIABLE NULL
+#define DEF_BUTTON_WIDTH "0"
+
+
+#define DEF_ENTRY_BG_COLOR "black"
+#define DEF_ENTRY_BG_MONO "black"
+#define DEF_ENTRY_ATTR "normal"
+#define DEF_ENTRY_FG "white"
+#define DEF_ENTRY_JUSTIFY "left"
+#define DEF_ENTRY_SELECT_ATTR_COLOR "normal"
+#define DEF_ENTRY_SELECT_ATTR_MONO "reverse"
+#define DEF_ENTRY_SELECT_FG_COLOR "black"
+#define DEF_ENTRY_SELECT_FG_MONO "black"
+#define DEF_ENTRY_SELECT_BG_COLOR "white"
+#define DEF_ENTRY_SELECT_BG_MONO "black"
+#define DEF_ENTRY_SHOW NULL
+#define DEF_ENTRY_STATE "normal"
+#define DEF_ENTRY_TAKE_FOCUS "1"
+#define DEF_ENTRY_TEXT_VARIABLE NULL
+#define DEF_ENTRY_WIDTH "16"
+#define DEF_ENTRY_SCROLL_COMMAND NULL
+
+
+#define DEF_LISTBOX_ACTIVE_ATTR_COLOR "normal"
+#define DEF_LISTBOX_ACTIVE_ATTR_MONO "reverse"
+#define DEF_LISTBOX_ACTIVE_BG_COLOR "white"
+#define DEF_LISTBOX_ACTIVE_BG_MONO "black"
+#define DEF_LISTBOX_ACTIVE_FG_COLOR "black"
+#define DEF_LISTBOX_ACTIVE_FG_MONO "white"
+#define DEF_LISTBOX_BG_COLOR "black"
+#define DEF_LISTBOX_BG_MONO "black"
+#define DEF_LISTBOX_FG "white"
+#define DEF_LISTBOX_ATTR "normal"
+#define DEF_LISTBOX_HEIGHT "10"
+#define DEF_LISTBOX_SELECT_ATTR_COLOR "bold"
+#define DEF_LISTBOX_SELECT_ATTR_MONO "bold"
+#define DEF_LISTBOX_SELECT_BG_COLOR "black"
+#define DEF_LISTBOX_SELECT_BG_MONO "black"
+#define DEF_LISTBOX_SELECT_FG_COLOR "white"
+#define DEF_LISTBOX_SELECT_FG_MONO "white"
+#define DEF_LISTBOX_SELECT_MODE "browse"
+#define DEF_LISTBOX_TAKE_FOCUS "1"
+#define DEF_LISTBOX_WIDTH "20"
+#define DEF_LISTBOX_SCROLL_COMMAND NULL
+#define DEF_LISTBOX_SCROLL_COMMAND NULL
+
+
+#define DEF_SCROLLBAR_ACTIVE_ATTR_COLOR "normal"
+#define DEF_SCROLLBAR_ACTIVE_ATTR_MONO "reverse"
+#define DEF_SCROLLBAR_ACTIVE_BG_COLOR "white"
+#define DEF_SCROLLBAR_ACTIVE_BG_MONO "black"
+#define DEF_SCROLLBAR_ACTIVE_FG_COLOR "black"
+#define DEF_SCROLLBAR_ACTIVE_FG_MONO "white"
+#define DEF_SCROLLBAR_ATTR "normal"
+#define DEF_SCROLLBAR_BG_COLOR "black"
+#define DEF_SCROLLBAR_BG_MONO "black"
+#define DEF_SCROLLBAR_COMMAND NULL
+#define DEF_SCROLLBAR_FG_COLOR "white"
+#define DEF_SCROLLBAR_FG_MONO "white"
+#define DEF_SCROLLBAR_ORIENT "vertical"
+#define DEF_SCROLLBAR_TAKE_FOCUS "1"
+
+
+#define DEF_MESSAGE_ANCHOR "center"
+#define DEF_MESSAGE_ASPECT "320"
+#define DEF_MESSAGE_ATTR "normal"
+#define DEF_MESSAGE_BG_COLOR "black"
+#define DEF_MESSAGE_BG_MONO "black"
+#define DEF_MESSAGE_FG_COLOR "white"
+#define DEF_MESSAGE_FG_MONO "white"
+#define DEF_MESSAGE_JUSTIFY "left"
+#define DEF_MESSAGE_TAKE_FOCUS "0"
+#define DEF_MESSAGE_TEXT NULL
+#define DEF_MESSAGE_TEXT_VARIABLE NULL
+#define DEF_MESSAGE_WIDTH "0"
+
+
+#define DEF_TEXT_ATTR "normal"
+#define DEF_TEXT_BG_COLOR "black"
+#define DEF_TEXT_BG_MONO "black"
+#define DEF_TEXT_FG "white"
+#define DEF_TEXT_HEIGHT "10"
+#define DEF_TEXT_SELECT_ATTR_COLOR "normal"
+#define DEF_TEXT_SELECT_ATTR_MONO "reverse"
+#define DEF_TEXT_SELECT_BG_COLOR "white"
+#define DEF_TEXT_SELECT_BG_MONO "black"
+#define DEF_TEXT_SELECT_FG_COLOR "black"
+#define DEF_TEXT_SELECT_FG_MONO "white"
+#define DEF_TEXT_STATE "normal"
+#define DEF_TEXT_TABS ""
+#define DEF_TEXT_TAKE_FOCUS "1"
+#define DEF_TEXT_WIDTH "40"
+#define DEF_TEXT_WRAP "char"
+#define DEF_TEXT_XSCROLL_COMMAND NULL
+#define DEF_TEXT_YSCROLL_COMMAND NULL
+
+
+#define DEF_MENUBUTTON_ATTR "normal"
+#define DEF_MENUBUTTON_ACTIVE_ATTR_COLOR "normal"
+#define DEF_MENUBUTTON_ACTIVE_ATTR_MONO "reverse"
+#define DEF_MENUBUTTON_ACTIVE_BG_COLOR "white"
+#define DEF_MENUBUTTON_ACTIVE_BG_MONO "black"
+#define DEF_MENUBUTTON_ACTIVE_FG_COLOR "black"
+#define DEF_MENUBUTTON_ACTIVE_FG_MONO "white"
+#define DEF_MENUBUTTON_ANCHOR "center"
+#define DEF_MENUBUTTON_BG_COLOR "black"
+#define DEF_MENUBUTTON_BG_MONO "black"
+#define DEF_MENUBUTTON_DISABLED_ATTR "dim"
+#define DEF_MENUBUTTON_DISABLED_BG_COLOR "black"
+#define DEF_MENUBUTTON_DISABLED_BG_MONO "black"
+#define DEF_MENUBUTTON_DISABLED_FG_COLOR "white"
+#define DEF_MENUBUTTON_DISABLED_FG_MONO "white"
+#define DEF_MENUBUTTON_FG "white"
+#define DEF_MENUBUTTON_HEIGHT "0"
+#define DEF_MENUBUTTON_INDICATOR "0"
+#define DEF_MENUBUTTON_INDICATOR_FG_COLOR "red"
+#define DEF_MENUBUTTON_INDICATOR_FG_MONO "white"
+#define DEF_MENUBUTTON_MENU NULL
+#define DEF_MENUBUTTON_STATE "normal"
+#define DEF_MENUBUTTON_TAKE_FOCUS "0"
+#define DEF_MENUBUTTON_TEXT NULL
+#define DEF_MENUBUTTON_TEXT_VARIABLE NULL
+#define DEF_MENUBUTTON_UNDERLINE "-1"
+#define DEF_MENUBUTTON_UNDERLINE_ATTR "reverse"
+#define DEF_MENUBUTTON_UNDERLINE_FG_COLOR "white"
+#define DEF_MENUBUTTON_UNDERLINE_FG_MONO "white"
+#define DEF_MENUBUTTON_WIDTH "0"
+
+
+#define DEF_MENU_ENTRY_ACTIVE_ATTR NULL
+#define DEF_MENU_ENTRY_ACTIVE_BG NULL
+#define DEF_MENU_ENTRY_ACTIVE_FG NULL
+#define DEF_MENU_ENTRY_ACCELERATOR NULL
+#define DEF_MENU_ENTRY_ATTR NULL
+#define DEF_MENU_ENTRY_BG NULL
+#define DEF_MENU_ENTRY_COMMAND NULL
+#define DEF_MENU_ENTRY_FG NULL
+#define DEF_MENU_ENTRY_INDICATOR "1"
+#define DEF_MENU_ENTRY_LABEL NULL
+#define DEF_MENU_ENTRY_MENU NULL
+#define DEF_MENU_ENTRY_OFF_VALUE "0"
+#define DEF_MENU_ENTRY_ON_VALUE "1"
+#define DEF_MENU_ENTRY_SELECT NULL
+#define DEF_MENU_ENTRY_STATE "normal"
+#define DEF_MENU_ENTRY_VALUE NULL
+#define DEF_MENU_ENTRY_CHECK_VARIABLE NULL
+#define DEF_MENU_ENTRY_RADIO_VARIABLE "selectedButton"
+#define DEF_MENU_ENTRY_UNDERLINE "-1"
+
+
+#define DEF_MENU_ACTIVE_ATTR_COLOR "normal"
+#define DEF_MENU_ACTIVE_ATTR_MONO "reverse"
+#define DEF_MENU_ACTIVE_BG_COLOR "white"
+#define DEF_MENU_ACTIVE_BG_MONO "black"
+#define DEF_MENU_ACTIVE_FG_COLOR "black"
+#define DEF_MENU_ACTIVE_FG_MONO "white"
+#define DEF_MENU_ATTR "normal"
+#define DEF_MENU_BG_COLOR "black"
+#define DEF_MENU_BG_MONO "black"
+#define DEF_MENU_BORDER NULL
+#define DEF_MENU_DISABLED_ATTR "dim"
+#define DEF_MENU_DISABLED_BG_COLOR "black"
+#define DEF_MENU_DISABLED_BG_MONO "black"
+#define DEF_MENU_DISABLED_FG_COLOR "white"
+#define DEF_MENU_DISABLED_FG_MONO "white"
+#define DEF_MENU_FG "white"
+#define DEF_MENU_POST_COMMAND ""
+#define DEF_MENU_SELECT_COLOR "red"
+#define DEF_MENU_SELECT_MONO "white"
+#define DEF_MENU_TAKE_FOCUS "0"
+#define DEF_MENU_UNDERLINE_ATTR "reverse"
+#define DEF_MENU_UNDERLINE_FG_COLOR "white"
+#define DEF_MENU_UNDERLINE_FG_MONO "white"
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH after n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+after \- Execute a command after a time delay
+.SH SYNOPSIS
+\fBafter \fIms\fR
+.br
+\fBafter \fIms \fR?\fIscript script script ...\fR?
+.br
+\fBafter cancel \fIid\fR
+.br
+\fBafter cancel \fIscript script script ...\fR
+.br
+\fBafter idle \fR?\fIscript script script ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This command is used to delay execution of the program or to execute
+a command in background after a delay. It has several forms,
+depending on the first argument to the command:
+.TP
+\fBafter \fIms\fR
+\fIMs\fR must be an integer giving a time in milliseconds.
+The command sleeps for \fIms\fR milliseconds and then returns.
+While the command is sleeping the application does not respond to
+keypresses or any other events.
+.TP
+\fBafter \fIms \fR?\fIscript script script ...\fR?
+In this form the command returns immediately, but it arranges
+for a Tcl command to be executed \fIms\fR milliseconds later as a
+background event handler.
+The delayed command is formed by concatenating all the \fIscript\fR
+arguments in the same fashion as the \fBconcat\fR command.
+The command will be executed at global level (outside the context
+of any Tcl procedure).
+If an error occurs while executing the delayed command then the
+\fBtkerror\fR mechanism is used to report the error.
+The \fBafter\fR command returns an identifier that can be used
+to cancel the delayed command using \fBafter cancel\fR.
+.TP
+\fBafter cancel \fIid\fR
+Cancels the execution of a delayed command that
+was previously scheduled.
+\fIId\fR indicates which command should be canceled; it must have
+been the return value from a previous \fBafter\fR command.
+If the command given by \fIid\fR has already been executed then
+the \fBafter cancel\fR command has no effect.
+.TP
+\fBafter cancel \fIscript script ...\fR
+This command also cancels the execution of a delayed command.
+The \fIscript\fR arguments are concatenated together with space
+separators (just as in the \fBconcat\fR command).
+If there is a pending command that matches the string, it is
+cancelled and will never be executed; if no such command is
+currently pending then the \fBafter cancel\fR command has no effect.
+.TP
+\fBafter idle \fIscript \fR?\fIscript script ...\fR?
+Concatenates the \fIscript\fR arguments together with space
+separators (just as in the \fBconcat\fR command), and arranges
+for the resulting script to be evaluated later as an idle handler
+(the script runs the next time the Tk event loop is entered
+and there are no events to process).
+The command returns an identifier that can be used
+to cancel the delayed command using \fBafter cancel\fR.
+If an error occurs while executing the script then the
+\fBtkerror\fR mechanism is used to report the error.
+
+.SH "SEE ALSO"
+tkerror
+
+.SH KEYWORDS
+cancel, delay, sleep, time
--- /dev/null
+'\"
+'\" Copyright (c) 1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH bell n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bell \- Ring a terminal's bell
+.SH SYNOPSIS
+\fBbell\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+This command rings the bell on the terminal if supported, otherwise the
+terminal's screen is flashed. An empty string is returned as result of
+this command. \fBBell\fR is carried out immediately, i.e. not deferred
+until the application becomes idle.
+
+.SH KEYWORDS
+beep, bell, ring
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH bind n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bind \- Arrange for events to invoke Tcl scripts
+.SH SYNOPSIS
+\fBbind\fI tag\fR
+.br
+\fBbind\fI tag sequence\fR
+.br
+\fBbind\fI tag sequence script\fR
+.br
+\fBbind\fI tag sequence \fB+\fIscript\fR
+.BE
+
+.SH INTRODUCTION
+.PP
+The \fBbind\fR command associates Tcl scripts with events.
+If all three arguments are specified, \fBbind\fR will
+arrange for \fIscript\fR (a Tcl script) to be evaluated whenever
+the event(s) given by \fIsequence\fR occur in the window(s)
+identified by \fItag\fR.
+If \fIscript\fR is prefixed with a ``+'', then it is appended to
+any existing binding for \fIsequence\fR; otherwise \fIscript\fR replaces
+any existing binding.
+If \fIscript\fR is an empty string then the current binding for
+\fIsequence\fR is destroyed, leaving \fIsequence\fR unbound.
+In all of the cases where a \fIscript\fR argument is provided,
+\fBbind\fR returns an empty string.
+.PP
+If \fIsequence\fR is specified without a \fIscript\fR, then the
+script currently bound to \fIsequence\fR is returned, or
+an empty string is returned if there is no binding for \fIsequence\fR.
+If neither \fIsequence\fR nor \fIscript\fR is specified, then the
+return value is a list whose elements are all the sequences
+for which there exist bindings for \fItag\fR.
+.PP
+The \fItag\fR argument determines which window(s) the binding applies to.
+If \fItag\fR begins with a dot, as in \fB.a.b.c\fR, then it must
+be the path name for a window; otherwise it may be an arbitrary
+string.
+Each window has an associated list of tags, and a binding applies
+to a particular window if its tag is among those specified for
+the window.
+Although the \fBbindtags\fR command may be used to assign an
+arbitrary set of binding tags to a window, the default binding
+tags provide the following behavior:
+.IP
+If a tag is the name of an internal window the binding applies
+to that window.
+.IP
+If the tag is the name of a toplevel window the binding applies
+to the toplevel window and all its internal windows.
+.IP
+If the tag is the name of a class of widgets, such as \fBButton\fR,
+the binding applies to all widgets in that class;
+.IP
+If \fItag\fR has the value \fBall\fR,
+the binding applies to all windows in the application.
+
+.SH "EVENT PATTERNS"
+.PP
+The \fIsequence\fR argument specifies a sequence of one or more
+event patterns, with optional white space between the patterns. Each
+event pattern may
+take either of two forms. In the simplest case it is a single
+printing ASCII character, such as \fBa\fR or \fB[\fR. The character
+may not be a space character or the character \fB<\fR. This form of
+pattern matches a \fBKeyPress\fR event for the particular
+character. The second form of pattern is longer but more general.
+It has the following syntax:
+.DS C
+\fB<\fItype-detail\fB>\fR
+.DE
+The entire event pattern is surrounded by angle brackets.
+Inside the angle brackets are an event
+type, and an extra piece of information (\fIdetail\fR) identifying
+a particular button or keysym. Any of the fields may be omitted,
+as long as at least one of \fItype\fR and \fIdetail\fR is present.
+The fields must be separated by white space or dashes.
+
+.SH "EVENT TYPES"
+.LP
+The \fItype\fR field may be any of the following list.
+Where two names appear together, they are synonyms.
+.DS C
+.ta 5c 10c
+\fB
+BarCode Expose Map
+ButtonPress, Button FocusIn Unmap
+ButtonRelease FocusOut
+Destroy KeyPress, Key, Control\fR
+.DE
+.LP
+The last part of a long event specification is \fIdetail\fR. In the
+case of a \fBButtonPress\fR or \fBButtonRelease\fR event, it is the
+number of a button (1-5). If a button number is given, then only an
+event on that particular button will match; if no button number is
+given, then an event on any button will match. Note: giving a
+specific button number is different than specifying a button modifier;
+in the first case, it refers to a button being pressed or released,
+while in the second it refers to some other button that is already
+depressed when the matching event occurs. If a button
+number is given then \fItype\fR may be omitted: if will default
+to \fBButtonPress\fR. For example, the specifier \fB<1>\fR
+is equivalent to \fB<ButtonPress-1>\fR.
+.LP
+If the event type is \fBKeyPress\fR, \fBKey\fR or \fBControl\fR, then
+\fIdetail\fR may be specified in the form of a keysym. Keysyms
+are textual specifications for particular keys on the keyboard;
+they include all the alphanumeric ASCII characters (e.g. ``a'' is
+the keysym for the ASCII character ``a''), plus descriptions for
+non-alphanumeric characters (``comma'' is the keysym for the comma
+character), plus descriptions for some of the non-ASCII keys on the
+keyboard (e.g. ``F1'' is the keysym for the F1 function key, if it exists).
+The complete list of keysyms is not presented here; it is
+available by invoking the \fBcurses haskey\fR Tcl command and may vary
+from system to system.
+If necessary, you can use the \fB%K\fR notation described below
+to print out the keysym name for a particular key.
+If a keysym \fIdetail\fR is given, then the
+\fItype\fR field may be omitted; it will default to \fBKeyPress\fR.
+For example, \fB<KeyPress-comma>\fR is equivalent to
+\fB<comma>\fR.
+
+.SH "BINDING SCRIPTS AND SUBSTITUTIONS"
+.LP
+The \fIscript\fR argument to \fBbind\fR is a Tcl script,
+which will be executed whenever the given event sequence occurs.
+\fICommand\fR will be executed in the same interpreter that the
+\fBbind\fR command was executed in, and it will run at global
+level (only global variables will be accessible).
+If \fIscript\fR contains
+any \fB%\fR characters, then the script will not be
+executed directly. Instead, a new script will be
+generated by replacing each \fB%\fR, and the character following
+it, with information from the current event. The replacement
+depends on the character following the \fB%\fR, as defined in the
+list below. Unless otherwise indicated, the
+replacement string is the decimal value of the given field from
+the current event.
+Some of the substitutions are only valid for
+certain types of events; if they are used for other types of events
+the value substituted is undefined.
+.TP
+\fB%%\fR
+Replaced with a single percent.
+.TP
+\fB%b\fR
+The number of the button that was pressed or released. Valid only
+for \fBButtonPress\fR and \fBButtonRelease\fR events.
+.TP
+\fB%k\fR
+The \fIkeycode\fR field from the event. Valid only for \fBKeyPress\fR
+and \fBKeyRelease\fR events.
+.TP
+\fB%x\fR
+The \fIx\fR coordinate (window coordinate system)
+from \fBButtonPress\fR and \fBButtonRelease\fR events.
+.TP
+\fB%y\fR
+The \fIy\fR coordinate (window coordinate system)
+from \fBButtonPress\fR and \fBButtonRelease\fR events.
+.TP
+\fB%A\fR
+For \fBKeyPress\fR events, substitutes the ASCII character corresponding to
+the event, or the empty string if the event doesn't correspond to an ASCII
+character (e.g. the shift key was pressed).
+For \fBBarCode\fR events, substitutes the entire barcode data packet.
+.TP
+\fB%K\fR
+The keysym corresponding to the event, substituted as a textual
+string. Valid only for \fBKeyPress\fR events.
+.TP
+\fB%N\fR
+The keysym corresponding to the event, substituted as
+a decimal number. Valid only for \fBKeyPress\fR events.
+.TP
+\fB%W\fR
+The path name of the window to which the event was reported (the
+\fIwindow\fR field from the event). Valid for all event types.
+.TP
+\fB%X\fR
+The \fIx\fR coordinate (screen coordinate system)
+from \fBButtonPress\fR and \fBButtonRelease\fR events.
+.TP
+\fB%Y\fR
+The \fIy\fR coordinate (screen coordinate system)
+from \fBButtonPress\fR and \fBButtonRelease\fR events.
+.LP
+The replacement string for a %-replacement is formatted as a proper
+Tcl list element.
+This means that it will be surrounded with braces
+if it contains spaces, or special characters such as \fB$\fR and
+\fB{\fR may be preceded by backslashes.
+This guarantees that the string will be passed through the Tcl
+parser when the binding script is evaluated.
+Most replacements are numbers or well-defined strings such
+as \fBcomma\fR; for these replacements no special formatting
+is ever necessary.
+The most common case where reformatting occurs is for the \fB%A\fR
+substitution. For example, if \fIscript\fR is
+.DS
+\fBinsert\0%A\fR
+.DE
+and the character typed is an open square bracket, then the script
+actually executed will be
+.DS
+\fBinsert\0\e[\fR
+.DE
+This will cause the \fBinsert\fR to receive the original replacement
+string (open square bracket) as its first argument.
+If the extra backslash hadn't been added, Tcl would not have been
+able to parse the script correctly.
+
+.SH MULTIPLE MATCHES
+.LP
+It is possible for several bindings to match a given event.
+If the bindings are associated with different \fItag\fR's,
+then each of the bindings will be executed, in order.
+By default, a class binding will be executed first, followed
+by a binding for the widget, a binding for its toplevel, and
+an \fBall\fR binding.
+The \fBbindtags\fR command may be used to change this order for
+a particular window or to associate additional binding tags with
+the window.
+.LP
+The \fBcontinue\fR and \fBbreak\fR commands may be used inside a
+binding script to control the processing of matching scripts.
+If \fBcontinue\fR is invoked, then the current binding script
+is terminated but Tk will continue processing binding scripts
+associated with other \fItag\fR's.
+If the \fBbreak\fR command is invoked within a binding script,
+then that script terminates and no other scripts will be invoked
+for the event.
+.LP
+If more than one binding matches a particular event and they
+have the same \fItag\fR, then the most specific binding
+is chosen and its script is evaluated.
+The following tests are applied, in order, to determine which of
+several matching sequences is more specific:
+(a) a longer sequence (in terms of number
+of events matched) is more specific than a shorter sequence;
+(b) an event pattern that specifies a specific button or key is more specific
+than one that doesn't.
+.LP
+If an event does not match any of the existing bindings, then the
+event is ignored.
+An unbound event is not considered to be an error.
+
+.SH ERRORS
+.LP
+If an error occurs in executing the script for a binding then the
+\fBtkerror\fR mechanism is used to report the error.
+The \fBtkerror\fR command will be executed at global level
+(outside the context of any Tcl procedure).
+
+.SH "SEE ALSO"
+tkerror
+
+.SH KEYWORDS
+event, binding
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH bindtags n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+bindtags \- Determine which bindings apply to a window, and order of evaluation
+.SH SYNOPSIS
+\fBbindtags \fIwindow \fR?\fItagList\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+When a binding is created with the \fBbind\fR command, it is
+associated either with a particular window such as \fB.a.b.c\fR,
+a class name such as \fBButton\fR, the keyword \fBall\fR, or any
+other string.
+All of these forms are called \fIbinding tags\fR.
+Each window contains a list of binding tags that determine how
+events are processed for the window.
+When an event occurs in a window, it is applied to each of the
+window's tags in order: for each tag, the most specific binding
+that matches the given tag and event is executed.
+See the \fBbind\fR command for more information on the matching
+process.
+.PP
+By default, each window has four binding tags consisting of the
+name of the window, the window's class name, the name of the window's
+nearest toplevel ancestor, and \fBall\fR, in that order.
+Toplevel windows have only three tags by default, since the toplevel
+name is the same as that of the window.
+The \fBbindtags\fR command allows the binding tags for a window to be
+read and modified.
+.PP
+If \fBbindtags\fR is invoked with only one argument, then the
+current set of binding tags for \fIwindow\fR is returned as a list.
+If the \fItagList\fR argument is specified to \fBbindtags\fR,
+then it must be a proper list; the tags for \fIwindow\fR are changed
+to the elements of the list.
+The elements of \fItagList\fR may be arbitrary strings; however,
+any tag starting with a dot is treated as the name of a window; if
+no window by that name exists at the time an event is processed,
+then the tag is ignored for that event.
+The order of the elements in \fItagList\fR determines the order in
+which binding scripts are executed in response to events.
+For example, the command
+.DS
+\fBbindtags .b {all . Button .b}\fR
+.DE
+reverses the order in which binding scripts will be evaluated for
+a button named \fB.b\fR so that \fBall\fR bindings are invoked
+first, following by bindings for \fB.b\fR's toplevel (``.''), followed by
+class bindings, followed by bindings for \fB.b\fR.
+.PP
+The \fBbindtags\fR command may be used to introduce arbitrary
+additional binding tags for a window, or to remove standard tags.
+For example, the command
+.DS
+\fBbindtags .b {.b TrickyButton . all}\fR
+.DE
+replaces the \fBButton\fR tag for \fB.b\fR with \fBTrickyButton\fR.
+This means that the default widget bindings for buttons, which are
+associated with the \fBButton\fR tag, will no longer apply to \fB.b\fR,
+but any bindings associated with \fBTrickyButton\fR (perhaps some
+new button behavior) will apply.
+
+.SH "SEE ALSO"
+bind
+
+.SH KEYWORDS
+binding, event, tag
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH button n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+button \- Create and manipulate button widgets
+.SH SYNOPSIS
+\fBbutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBactiveAttributes\fR \fBattributes\fR \fBdisabledForeground\fR \fBtextVariable\fR
+\fBactiveBackground\fR \fBbackground\fR \fBforeground\fR \fBunderline\fR
+\fBactiveForeground\fR \fBdisabledAttributes\fR \fBtakeFocus\fR \fBunderlineAttributes\fR
+\fBanchor\fR \fBdisabledBackground\fR \fBtext\fR \fBunderlineForeground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBcommand\fR
+Class: \fBCommand\fR
+Command-Line Switch: \fB\-command\fR
+.fi
+.IP
+Specifies a Tcl command to associate with the button. This command
+is typically invoked when mouse button 1 is pressed over the button
+window.
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies a desired height for the button in screen lines.
+If this option isn't specified, the button's desired height is 1 line.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of three states for the button: \fBnormal\fR, \fBactive\fR,
+or \fBdisabled\fR. In normal state the button is displayed using the
+\fBforeground\fR and \fBbackground\fR options. The active state is
+typically used when the input focus is in the button. In active state
+the button is displayed using the \fBactiveAttributes\fR,
+\fBactiveForeground\fR and \fBactiveBackground\fR options.
+Disabled state means that the button should be insensitive:
+the default bindings will refuse to activate the widget and will ignore
+mouse button presses. In this state the \fBdisabledAttributes\fR,
+\fBdisabledForeground\fR and \fBdisabledBackground\fR options
+determine how the button is displayed.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies a desired width for the button in screen columns.
+If this option isn't specified, the button's desired width is computed
+from the size of the text being displayed in it.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBbutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a button widget.
+Additional options, described above, may be specified on the command line
+or in the option database to configure aspects of the button such as its
+colors, attributes, and text. The \fBbutton\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A button is a widget that displays a textual string, bitmap or image.
+One of the characters may optionally be underlined using the
+\fBunderline\fR, \fBunderlineAttributes\fR, and \fBunderlineForeground\fR
+options. It can display itself in either of three different ways, according
+to the \fBstate\fR option.
+When a user invokes the button (e.g. by pressing mouse button 1 with the cursor
+over the button), then the Tcl command specified in the \fB\-command\fR
+option is invoked.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBbutton\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for button widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBbutton\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBbutton\fR
+command.
+.TP
+\fIpathName \fBinvoke\fR
+Invoke the Tcl command associated with the button, if there is one.
+The return value is the return value from the Tcl command, or an
+empty string if there is no command associated with the button.
+This command is ignored if the button's state is \fBdisabled\fR.
+
+.SH "DEFAULT BINDINGS"
+.PP
+Ck automatically creates class bindings for buttons that give them
+the following default behavior:
+.IP [1]
+A button activates whenever it gets the input focus and deactivates
+whenever it loses the input focus.
+.IP [2]
+If mouse button 1 is pressed over a button, the button is invoked.
+.IP [3]
+When a button has the input focus, the space or return key cause the
+button to be invoked.
+.PP
+If the button's state is \fBdisabled\fR then none of the above
+actions occur: the button is completely non-responsive.
+.PP
+The behavior of buttons can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+button, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH checkbutton n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+checkbutton \- Create and manipulate checkbutton widgets
+.SH SYNOPSIS
+\fBcheckbutton\fI pathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBactiveAttributes\fR \fBattributes\fR \fBdisabledForeground\fR \fBtextVariable\fR
+\fBactiveBackground\fR \fBbackground\fR \fBforeground\fR \fBunderline\fR
+\fBactiveForeground\fR \fBdisabledAttributes\fR \fBtakeFocus\fR \fBunderlineAttributes\fR
+\fBanchor\fR \fBdisabledBackground\fR \fBtext\fR \fBunderlineForeground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBcommand\fR
+Class: \fBCommand\fR
+Command-Line Switch: \fB\-command\fR
+.fi
+.IP
+Specifies a Tcl command to associate with the button. This command
+is typically invoked when mouse button 1 is pressed on the button
+window. The button's global variable (\fB\-variable\fR option) will
+be updated before the command is invoked.
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies a desired height for the button in screen lines.
+If this option isn't specified, the button's desired height is 1 line.
+.LP
+.nf
+Name: \fBoffValue\fR
+Class: \fBValue\fR
+Command-Line Switch: \fB\-offvalue\fR
+.fi
+.IP
+Specifies value to store in the button's associated variable whenever
+this button is deselected. Defaults to ``0''.
+.LP
+.nf
+Name: \fBonValue\fR
+Class: \fBValue\fR
+Command-Line Switch: \fB\-onvalue\fR
+.fi
+.IP
+Specifies value to store in the button's associated variable whenever
+this button is selected. Defaults to ``1''.
+.LP
+.nf
+Name: \fBselectColor\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-selectcolor\fR
+.fi
+.IP
+Specifies a background color to use when the button is selected.
+If \fBindicatorOn\fR is true then the color applicies to the indicator.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of three states for the checkbutton: \fBnormal\fR, \fBactive\fR,
+or \fBdisabled\fR. In normal state the checkbutton is displayed using the
+\fBattributes\fR, \fBforeground\fR and \fBbackground\fR options.
+The active state is used when the input focus is in the checkbutton.
+In active state the checkbutton is displayed using the
+\fBactiveAttributes\fR, \fBactiveForeground\fR, and
+\fBactiveBackground\fR options. Disabled state means that the checkbutton
+should be insensitive: the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \fBdisabledAttributes\fR, \fBdisabledForeground\fR, and
+\fBdisabledBackground\fR options determine how the checkbutton is displayed.
+.LP
+.nf
+Name: \fBvariable\fR
+Class: \fBVariable\fR
+Command-Line Switch: \fB\-variable\fR
+.fi
+.IP
+Specifies name of global variable to set to indicate whether
+or not this button is selected. Defaults to the name of the
+button within its parent (i.e. the last element of the button
+window's path name).
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies a desired width for the button in screen columns.
+If this option isn't specified, the button's desired width is computed
+from the size of the text being displayed in it.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBcheckbutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a checkbutton widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the checkbutton such as its colors, font,
+text, and initial relief. The \fBcheckbutton\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A checkbutton is a widget that displays a textual string
+and a square called an \fIindicator\fR. One of the characters of the
+string may optionally be underlined using the
+\fBunderline\fR, \fBunderlineAttributes\fR, and \fBunderlineForeground\fR
+options. A checkbutton has all of the behavior of a simple button,
+including the following: it can display itself in either of three different
+ways, according to the \fBstate\fR option, and it invokes
+a Tcl command whenever mouse button 1 is clicked over the
+checkbutton.
+.PP
+In addition, checkbuttons can be \fIselected\fR. If a checkbutton is
+selected then the indicator is drawn with a special color, and
+a Tcl variable associated with the checkbutton is set to a particular
+value (normally 1).
+If the checkbutton is not selected, then the indicator is drawn with no
+special color, and the associated variable is set to a different value
+(typically 0).
+By default, the name of the variable associated with a checkbutton is the
+same as the \fIname\fR used to create the checkbutton.
+The variable name, and the ``on'' and ``off'' values stored in it,
+may be modified with options on the command line or in the option
+database. By default a checkbutton is configured to select and deselect
+itself on alternate button clicks.
+In addition, each checkbutton monitors its associated variable and
+automatically selects and deselects itself when the variables value
+changes to and from the button's ``on'' value.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBcheckbutton\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for checkbutton widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBcheckbutton\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBcheckbutton\fR
+command.
+.TP
+\fIpathName \fBdeselect\fR
+Deselects the checkbutton and sets the associated variable to its ``off''
+value.
+.TP
+\fIpathName \fBinvoke\fR
+Does just what would have happened if the user invoked the checkbutton
+with the mouse: toggle the selection state of the button and invoke
+the Tcl command associated with the checkbutton, if there is one.
+The return value is the return value from the Tcl command, or an
+empty string if there is no command associated with the checkbutton.
+This command is ignored if the checkbutton's state is \fBdisabled\fR.
+.TP
+\fIpathName \fBselect\fR
+Selects the checkbutton and sets the associated variable to its ``on''
+value.
+.TP
+\fIpathName \fBtoggle\fR
+Toggles the selection state of the button, redisplaying it and
+modifying its associated variable to reflect the new state.
+
+.SH BINDINGS
+.PP
+Ck automatically creates class bindings for checkbuttons that give them
+the following default behavior:
+.IP [1]
+A checkbutton activates whenever it gets the input focus and deactivates
+whenever it loses the input focus.
+.IP [2]
+When mouse button 1 is pressed over a checkbutton it is invoked (its
+selection state toggles and the command associated with the button is
+invoked, if there is one).
+.IP [3]
+When a checkbutton has the input focus, the space or return keys cause
+the checkbutton to be invoked.
+.PP
+If the checkbutton's state is \fBdisabled\fR then none of the above
+actions occur: the checkbutton is completely non-responsive.
+.PP
+The behavior of checkbuttons can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+checkbutton, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_chooseColor.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_chooseColor.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_chooseColor n 4.2 Tk "Tk Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_chooseColor \- pops up a dialog box for the user to select a color.
+.PP
+.SH SYNOPSIS
+\fBck_chooseColor \fR?\fIoption value ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The procedure \fBck_chooseColor\fR pops up a dialog box for the
+user to select a color. The following \fIoption\-value\fR pairs are
+possible as command line arguments:
+.TP
+\fB\-initialcolor\fR \fIcolor\fR
+Specifies the color to display in the color dialog when it pops
+up. \fIcolor\fR must be in a form acceptable to the \fBTk_GetColor\fR
+function.
+.TP
+\fB\-parent\fR \fIwindow\fR
+Makes \fIwindow\fR the logical parent of the color dialog. The color
+dialog is displayed on top of its parent window.
+.TP
+\fB\-title\fR \fItitleString\fR
+Specifies a string to display as the title of the dialog box. If this
+option is not specified, then a default title will be displayed.
+.LP
+If the user selects a color, \fBck_chooseColor\fR will return the
+name of the color in a form acceptable to \fBTk_GetColor\fR. If the
+user cancels the operation, both commands will return the empty
+string.
+.SH EXAMPLE
+.CS
+button .b \-bg [ck_chooseColor \-initialcolor gray \-title "Choose color"]
+.CE
+
+.SH KEYWORDS
+color selection dialog
--- /dev/null
+'\"
+'\" Copyright (c) 1992 The Regents of the University of California.
+'\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_dialog.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_dialog.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_dialog n 4.1 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_dialog \- Create modal dialog and wait for response
+.SH SYNOPSIS
+\fBck_dialog \fIwindow title text default string string ...\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure is part of the Ck script library.
+Its arguments describe a dialog box:
+.TP
+\fIwindow\fR
+Name of top-level window to use for dialog. Any existing window
+by this name is destroyed.
+.TP
+\fItitle\fR
+Text to display in dialog's decorative frame.
+.TP
+\fItext\fR
+Message to appear in the top portion of the dialog box.
+.TP
+\fIdefault\fR
+If this is an integer greater than or equal to zero, then it gives
+the index of the button that is to be the default button for the dialog
+(0 for the leftmost button, and so on).
+If less than zero or an empty string then first button (number zero) is
+focuced by default.
+.TP
+\fIstring\fR
+There will be one button for each of these arguments.
+Each \fIstring\fR specifies text to display in a button,
+in order from left to right.
+.PP
+After creating a dialog box, \fBck_dialog\fR waits for the user to
+select one of the buttons either by clicking on the button with the
+mouse or by typing return to invoke the default button (if any).
+Then it returns the index of the selected button: 0 for the leftmost
+button, 1 for the button next to it, and so on.
+If the dialog's window is destroyed before the user selects one
+of the buttons, then -1 is returned.
+.PP
+While waiting for the user to respond, \fBck_dialog\fR sets a local
+grab. This prevents the user from interacting with the application
+in any way except to invoke the dialog box.
+.SH RESOURCES
+
+This dialog uses Dialog class.
+
+.SH KEYWORDS
+ dialog, modal
--- /dev/null
+'\"
+'\" Copyright (c) 1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_getOpenFile.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_getOpenFile.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_getOpenFile n 4.2 Tk "Tk Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tk_getOpenFile, tk_getSaveFile \- pop up a dialog box for the user to select a file to open or save.
+.SH SYNOPSIS
+\fBck_getOpenFile \fR?\fIoption value ...\fR?
+.br
+\fBck_getSaveFile \fR?\fIoption value ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The procedures \fBck_getOpenFile\fR and \fBck_getSaveFile\fR pop up a
+dialog box for the user to select a file to open or save. The
+\fBck_getOpenFile\fR command is usually associated with the \fBOpen\fR
+command in the \fBFile\fR menu. Its purpose is for the user to select an
+existing file \fIonly\fR. If the user enters an non-existent file, the
+dialog box gives the user an error prompt and requires the user to give
+an alternative selection. If an application allows the user to create
+new files, it should do so by providing a separate \fBNew\fR menu command.
+.PP
+The \fBck_getSaveFile\fR command is usually associated with the \fBSave
+as\fR command in the \fBFile\fR menu. If the user enters a file that
+already exists, the dialog box prompts the user for confirmation
+whether the existing file should be overwritten or not.
+.PP
+The following \fIoption\-value\fR pairs are possible as command line
+arguments to these two commands:
+.TP
+\fB\-defaultextension\fR \fIextension\fR
+Specifies a string that will be appended to the filename if the user
+enters a filename without an extension. The defaut value is the empty
+string, which means no extension will be appended to the filename in
+any case. This option is ignored on the Macintosh platform, which
+does not require extensions to filenames,
+.VS 8.4
+and the UNIX implementation guesses reasonable values for this from
+the \fB\-filetypes\fR option when this is not supplied.
+.VE 8.4
+.TP
+\fB\-filetypes\fR \fIfilePatternList\fR
+If a \fBFile types\fR listbox exists in the file dialog on the particular
+platform, this option gives the \fIfiletype\fRs in this listbox. When
+the user choose a filetype in the listbox, only the files of that type
+are listed. If this option is unspecified, or if it is set to the
+empty list, or if the \fBFile types\fR listbox is not supported by the
+particular platform then all files are listed regardless of their
+types. See the section SPECIFYING FILE PATTERNS below for a
+discussion on the contents of \fIfilePatternList\fR.
+.TP
+\fB\-initialdir\fR \fIdirectory\fR
+Specifies that the files in \fIdirectory\fR should be displayed
+when the dialog pops up. If this parameter is not specified, then
+the files in the current working directory are displayed. If the
+parameter specifies a relative path, the return value will convert the
+relative path to an absolute path. This option may not always work on
+the Macintosh. This is not a bug. Rather, the \fIGeneral Controls\fR
+control panel on the Mac allows the end user to override the
+application default directory.
+.TP
+\fB\-initialfile\fR \fIfilename\fR
+Specifies a filename to be displayed in the dialog when it pops up. This
+option is ignored on the Macintosh platform.
+.TP
+\fB\-parent\fR \fIwindow\fR
+Makes \fIwindow\fR the logical parent of the file dialog. The file
+dialog is displayed on top of its parent window.
+.TP
+\fB\-title\fR \fItitleString\fR
+Specifies a string to display as the title of the dialog box. If this
+option is not specified, then a default title is displayed.
+.PP
+If the user selects a file, both \fBck_getOpenFile\fR and
+\fBck_getSaveFile\fR return the full pathname of this file. If the
+user cancels the operation, both commands return the empty string.
+.SH "SPECIFYING FILE PATTERNS"
+
+The \fIfilePatternList\fR value given by the \fB\-filetypes\fR option
+is a list of file patterns. Each file pattern is a list of the
+form
+.CS
+\fItypeName\fR {\fIextension\fR ?\fIextension ...\fR?} ?{\fImacType\fR ?\fImacType ...\fR?}?
+.CE
+\fItypeName\fR is the name of the file type described by this
+file pattern and is the text string that appears in the \fBFile types\fR
+listbox. \fIextension\fR is a file extension for this file pattern.
+\fImacType\fR is a four-character Macintosh file type. The list of
+\fImacType\fRs is optional and may be omitted for applications that do
+not need to execute on the Macintosh platform.
+.PP
+Several file patterns may have the same \fItypeName,\fR in which case
+they refer to the same file type and share the same entry in the
+listbox. When the user selects an entry in the listbox, all the files
+that match at least one of the file patterns corresponding
+to that entry are listed. Usually, each file pattern corresponds to a
+distinct type of file. The use of more than one file patterns for one
+type of file is necessary on the Macintosh platform only.
+.PP
+On the Macintosh platform, a file matches a file pattern if its
+name matches at least one of the \fIextension\fR(s) AND it
+belongs to at least one of the \fImacType\fR(s) of the
+file pattern. For example, the \fBC Source Files\fR file pattern in the
+sample code matches with files that have a \fB\.c\fR extension AND
+belong to the \fImacType\fR \fBTEXT\fR. To use the OR rule instead,
+you can use two file patterns, one with the \fIextensions\fR only and
+the other with the \fImacType\fR only. The \fBGIF Files\fR file type
+in the sample code matches files that EITHER have a \fB\.gif\fR
+extension OR belong to the \fImacType\fR \fBGIFF\fR.
+.PP
+On the Unix and Windows platforms, a file matches a file pattern
+if its name matches at at least one of the \fIextension\fR(s) of
+the file pattern. The \fImacType\fRs are ignored.
+.SH "SPECIFYING EXTENSIONS"
+.PP
+On the Unix and Macintosh platforms, extensions are matched using
+glob-style pattern matching. On the Windows platforms, extensions are
+matched by the underlying operating system. The types of possible
+extensions are: (1) the special extension * matches any
+file; (2) the special extension "" matches any files that
+do not have an extension (i.e., the filename contains no full stop
+character); (3) any character string that does not contain any wild
+card characters (* and ?).
+.PP
+Due to the different pattern matching rules on the various platforms,
+to ensure portability, wild card characters are not allowed in the
+extensions, except as in the special extension *. Extensions
+without a full stop character (e.g, ~) are allowed but may not
+work on all platforms.
+
+.SH EXAMPLE
+.CS
+set types {
+ {{Text Files} {.txt} }
+ {{TCL Scripts} {.tcl} }
+ {{C Source Files} {.c} TEXT}
+ {{GIF Files} {.gif} }
+ {{GIF Files} {} GIFF}
+ {{All Files} * }
+}
+set filename [ck_getOpenFile -filetypes $types]
+
+if {$filename != ""} {
+ # Open the file ...
+}
+.CE
+
+.SH CUSTOMIZATION
+
+Ck file dialog uses class \fBCkFdialog\fR for its toplevel wiget. Use
+\fBoption add\fR command to change default colors for it.
+directory menu is inside frame with class \fBDir\fR and filename input
+line in the frame with class \fBFilename\fR
+
+.SH KEYWORDS
+file selection dialog
--- /dev/null
+'\"
+'\" Copyright (c) 1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_messsageBox.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_messsageBox.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_messageBox n 4.2 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_messageBox \- pops up a message window and waits for user response.
+.SH SYNOPSIS
+\fBck_messageBox \fR?\fIoption value ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure creates and displays a message window with an
+application-specified message, an icon and a set of buttons. Each of
+the buttons in the message window is identified by a unique symbolic
+name (see the \fB\-type\fR options). After the message window is
+popped up, \fBtk_messageBox\fR waits for the user to select one of the
+buttons. Then it returns the symbolic name of the selected button.
+
+The following option-value pairs are supported:
+.TP
+\fB\-default\fR \fIname\fR
+\fIName\fR gives the symbolic name of the default button for
+this message window ('ok', 'cancel', and so on). See \fB\-type\fR
+for a list of the symbolic names. If this option is not specified,
+the first button in the dialog will be made the default.
+.TP
+\fB\-icon\fR \fIiconImage\fR
+Specifies an icon to display. \fIIconImage\fR must be one of the
+following: \fBerror\fR, \fBinfo\fR, \fBquestion\fR or
+\fBwarning\fR. If this option is not specified, then the info icon will be
+displayed.
+.TP
+\fB\-message\fR \fIstring\fR
+Specifies the message to display in this message box.
+.TP
+\fB\-parent\fR \fIwindow\fR
+Makes \fIwindow\fR the logical parent of the message box. The message
+box is displayed on top of its parent window.
+.TP
+\fB\-title\fR \fItitleString\fR
+Specifies a string to display as the title of the message box. The
+default value is an empty string.
+.TP
+\fB\-type\fR \fIpredefinedType\fR
+Arranges for a predefined set of buttons to be displayed. The
+following values are possible for \fIpredefinedType\fR:
+.RS
+.TP 18
+\fBabortretryignore\fR
+Displays three buttons whose symbolic names are \fBabort\fR,
+\fBretry\fR and \fBignore\fR.
+.TP 18
+\fBok\fR
+Displays one button whose symbolic name is \fBok\fR.
+.TP 18
+\fBokcancel\fR
+Displays two buttons whose symbolic names are \fBok\fR and \fBcancel\fR.
+.TP 18
+\fBretrycancel\fR
+Displays two buttons whose symbolic names are \fBretry\fR and \fBcancel\fR.
+.TP 18
+\fByesno\fR
+Displays two buttons whose symbolic names are \fByes\fR and \fBno\fR.
+.TP 18
+\fByesnocancel\fR
+Displays three buttons whose symbolic names are \fByes\fR, \fBno\fR
+and \fBcancel\fR.
+.RE
+.PP
+.SH EXAMPLE
+.CS
+set answer [ck_messageBox \-message "Really quit?" \-type yesno \-icon question]
+switch -- $answer {
+ yes exit
+ no {ck_messageBox \-message "I know you like this application!" \-type ok}
+}
+.CE
+
+.SH KEYWORDS
+message box
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_optionMenu.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_optionMenu.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_optionMenu n 4.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_optionMenu \- Create an option menubutton and its menu
+.SH SYNOPSIS
+\fBck_optionMenu \fIw varName value \fR?\fIvalue value ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure creates an option menubutton whose name is \fIw\fR,
+plus an associated menu.
+Together they allow the user to select one of the values
+given by the \fIvalue\fR arguments.
+The current value will be stored in the global variable whose
+name is given by \fIvarName\fR and it will also be displayed as the label
+in the option menubutton.
+The user can click on the menubutton to display a menu containing
+all of the \fIvalue\fRs and thereby select a new value.
+Once a new value is selected, it will be stored in the variable
+and appear in the option menubutton.
+The current value can also be changed by setting the variable.
+.PP
+The return value from \fBck_optionMenu\fR is the name of the menu
+associated with \fIw\fR, so that the caller can change its configuration
+options or manipulate it in other ways.
+
+.SH KEYWORDS
+option menu
--- /dev/null
+'\"
+'\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ck_popup.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" RCS: @(#) $Id: ck_popup.n,v 1.1 2006-02-24 18:59:53 vitus Exp $
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ta \\n()Au \\n()Bu
+.ie !"\\$3"" \{\
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 5.5c 11c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
+.TH ck_popup n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_popup \- Post a popup menu
+.SH SYNOPSIS
+\fBtk_popup \fImenu x y \fR?\fIentry\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure posts a menu at a given position on the screen and
+configures Tk so that the menu and its cascaded children can be
+traversed with the mouse or the keyboard.
+\fIMenu\fR is the name of a menu widget and \fIx\fR and \fIy\fR
+are the root coordinates at which to display the menu.
+If \fIentry\fR is omitted or an empty string, the
+menu's upper left corner is positioned at the given point.
+Otherwise \fIentry\fR gives the index of an entry in \fImenu\fR and
+the menu will be positioned so that the entry is positioned over
+the given point.
+
+.SH KEYWORDS
+menu, popup
--- /dev/null
+'\"
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH curses n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+curses \- Retrieve/modify curses based information
+.SH SYNOPSIS
+\fBcurses\fR \fIoption \fR?\fIarg arg ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBcurses\fR command is used to retrieve or modify information
+which is related to the \fBcurses(3)\fR library providing the
+input/output mechanisms used by Ck.
+It can take any of a number of different forms,
+depending on the \fIoption\fR argument. The legal forms are:
+.TP
+\fBcurses barcode\fR \fIstartChar endChar ?timeout?\fR
+Enables or modifies barcode reader support with delivery of \fBBarCode\fR
+events. \fIStartChar\fR and \fIendChar\fR are the start and end characters
+which delimit the barcode data packet without being delivered to the
+application. They must be specified as decimal numbers.
+The optional \fItimeout\fR argument is the maximum time between reception
+of start and end characters in millisecond for receiving the data packet;
+the default value is 1000.
+.TP
+\fBcurses barcode\fR \fI?off?\fR
+If \fIoff\fR is present, barcode reader support is disabled. Otherwise,
+the current start/end characters and the timeout are returned as a list
+of three decimal numbers.
+.TP
+\fBcurses baudrate\fR
+Returns the baud rate of the terminal as decimal string.
+.TP
+\fBcurses encoding \fR\fI?ISO8859|IBM437?\fR
+Sets or returns the character encoding being or to be used for
+displaying text. This affects for example the output of
+the text widget for the character values 0x80..0x9f.
+.TP
+\fBcurses gchar \fR\fI?charName? ?value?\fR
+Sets or returns the mappings of ``Alternate Character Set'' characters
+used to display the arrows of scrollbars, the indicators for checkbuttons
+and radiobuttons etc. \fICharName\fR must be a valid name of an ACS
+character (see list below), and \fIvalue\fR must be an integer, i.e.
+the value of the \fBcurses(3)\fR character which shall be output for the
+ACS character. By default the \fBterminfo(5)\fR entry for the terminal
+provides these mappings and there's rarely a need to modify them.
+.sp 1
+.ta 3c
+.nf
+\fBCk name description\fR
+ulcorner upper left corner
+urcorner upper right corner
+llcorner lower left corner
+lrcorner lower right corner
+rtee tee pointing right
+ltee tee pointing left
+btee tee pointing up
+ttee tee pointing down
+hline horizontal line
+vline vertical line
+plus large plus or crossover
+s1 scan line #1
+s9 scan line #9
+diamond diamond
+ckboard checker board (stipple)
+degree degree symbol
+plminus plus/minus
+bullet bullet
+larrow arrow pointing left
+rarrow arrow pointing right
+uarrow arrow pointing up
+darrow arrow pointing down
+board board of squares
+lantern lantern symbol
+block solid square block
+.fi
+.TP
+\fBcurses haskey\fR \fI?keyName?\fR
+If \fIkeyName\fR is omitted this command returns a list of all valid
+symbolic names of keyboard keys.
+If \fIkeyName\fR is given, a boolean is returned indicating if the
+terminal can generate that key.
+.TP
+\fBcurses purgeinput\fR
+Removes all characters typed so far from the keyboard input queue. This
+command should be used with great caution, since \fBxterm(1)\fR
+mouse events and barcode events are reported through the keyboard
+input queue as a character stream which can be interrupted
+by this command.
+.TP
+\fBcurses refreshdelay \fR\fI?milliseconds?\fR
+Sets or returns a time value which is used to limit the number of
+\fBcurses(3)\fR screen updates. By default the delay is zero, which
+does not impose any limits. Setting the refresh delay to a positive
+number can be useful in environments where the terminal is connected
+via terminal servers or \fBrlogin(1)\fR sessions.
+.TP
+\fBcurses reversekludge \fR\fI?boolean?\fR
+Queries or modifies special code for treatment of the reverse video
+attribute in conjunction with colors. On some terminals (e.g. the
+infamous AT386 Interactive console), the reverse attribute overrides
+the colors in effect. If the special code is enabled, the reverse
+attribute is emulated by swapping the foreground and background colors.
+.TP
+\fBcurses screendump \fR\fIfileName\fR
+Dumps the current screen contents to the file \fIfileName\fR if the
+curses library supports the \fBscr_dmp(3)\fR function. Otherwise an
+error is reported. The screen dump file is per se not useful, since
+it contains some binary representation internal to curses. However,
+there may exist an external utility program which transforms the screen
+dump file to ASCII in order to print it on paper.
+.TP
+\fBcurses suspend\fR
+Takes appropriate actions for job control, such as saving \fBcurses(3)\fR
+terminal state, sending the stop signal to the process and restoring
+the terminal state when the process is continued.
+
+.SH "SEE ALSO"
+curses(3)
+
+.SH KEYWORDS
+screen, terminal, curses
+
--- /dev/null
+'\"
+'\" Copyright (c) 1991-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH cwsh 1 8.0 Ck "Ck Applications"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+cwsh \- Simple curses windowing shell
+.SH SYNOPSIS
+\fBcwsh\fR ?\fIfileName arg arg ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+\fBCwsh\fR is a simple program consisting of the Tcl command
+language, the Ck toolkit, and a main program that eventually reads
+commands from a file.
+It creates a main window and then processes Tcl commands.
+If \fBcwsh\fR is invoked with no arguments,
+then it reads Tcl commands interactively from a command window.
+It will continue processing commands until all windows have been
+deleted or until the \fBexit\fR Tcl command is evaluated.
+If there exists a file \fB.cwshrc\fR in the home directory of
+the user, \fBcwsh\fR evaluates the file as a Tcl script
+just before presenting the command window.
+.PP
+If \fBcwsh\fR is invoked with an initial \fIfileName\fR argument, then
+\fIfileName\fR is treated as the name of a script file.
+\fBCwsh\fR will evaluate the script in \fIfileName\fR (which
+presumably creates a user interface), then it will respond to events
+until all windows have been deleted. The command window will not
+be created.
+There is no automatic evaluation of \fB.cwshrc\fR in this
+case, but the script file can always \fBsource\fR it if desired.
+
+.SH "APPLICATION NAME AND CLASS"
+.PP
+The name of the application, which is used for processing the
+option data base is taken from \fIfileName\fR, if it is specified,
+or from the command name by which \fBcwsh\fR was invoked.
+If this name contains a ``/''
+character, then only the characters after the last slash are used
+as the application name.
+.PP
+The class of the application, which is used for purposes such as
+specifying options, is the same as its name except that the first letter is
+capitalized.
+
+.SH "VARIABLES"
+.PP
+\fBCwsh\fR sets the following Tcl variables:
+.TP 15
+\fBargc\fR
+Contains a count of the number of \fIarg\fR arguments (0 if none).
+.TP 15
+\fBargv\fR
+Contains a Tcl list whose elements are the \fIarg\fR arguments
+that follow \fIfileName\fR, in order, or an empty string
+if there are no such arguments.
+.TP 15
+\fBargv0\fR
+Contains \fIfileName\fR if it was specified.
+Otherwise, contains the name by which \fBcwsh\fR was invoked.
+.TP 15
+\fBtcl_interactive\fR
+Contains 1 if \fBcwsh\fR was started without \fIfileName\fR
+argument, 0 otherwise.
+
+.SH "SCRIPT FILES"
+.PP
+If you create a Tcl script in a file whose first line is
+.DS
+\fB#!/usr/local/bin/cwsh\fR
+.DE
+then you can invoke the script file directly from your shell if
+you mark it as executable.
+This assumes that \fBcwsh\fR has been installed in the default
+location in /usr/local/bin; if it's installed somewhere else
+then you'll have to modify the above line to match.
+Many UNIX systems do not allow the \fB#!\fR line to exceed about
+30 characters in length, so be sure that the \fBcwsh\fR executable
+can be accessed with a short file name.
+.PP
+An even better approach is to start your script files with the
+following three lines:
+.DS
+\fB#!/bin/sh
+# the next line restarts using cwsh \e
+exec cwsh "$0" "$@"\fR
+.DE
+This approach has three advantages over the approach in the previous
+paragraph. First, the location of the \fBcwsh\fR binary doesn't have
+to be hard-wired into the script: it can be anywhere in your shell
+search path. Second, it gets around the 30-character file name limit
+in the previous approach.
+Third, this approach will work even if \fBcwsh\fR is
+itself a shell script (this is done on some systems in order to
+handle multiple architectures or operating systems: the \fBcwsh\fR
+script selects one of several binaries to run). The three lines
+cause both \fBsh\fR and \fBcwsh\fR to process the script, but the
+\fBexec\fR is only executed by \fBsh\fR.
+\fBsh\fR processes the script first; it treats the second
+line as a comment and executes the third line.
+The \fBexec\fR statement cause the shell to stop processing and
+instead to start up \fBcwsh\fR to reprocess the entire script.
+When \fBcwsh\fR starts up, it treats all three lines as comments,
+since the backslash at the end of the second line causes the third
+line to be treated as part of the comment on the second line.
+
+.SH KEYWORDS
+shell, toolkit
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH destroy n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+destroy \- Destroy one or more windows
+.SH SYNOPSIS
+\fBdestroy \fR?\fIwindow window ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This command deletes the windows given by the
+\fIwindow\fR arguments, plus all of their descendants.
+If a \fIwindow\fR ``.'' is deleted then the entire application
+will be destroyed and the actions of the \fBexit\fR command are
+taken. The \fIwindow\fRs are destroyed in order, and if an error occurs
+in destroying a window the command aborts without destroying the
+remaining windows.
+
+.SH KEYWORDS
+application, destroy, window
--- /dev/null
+'\"
+'\" Copyright (c) 1992 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH ck_dialog n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_dialog \- Create dialog and wait for response
+.SH SYNOPSIS
+\fBck_dialog \fIwindow title text string string ...\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure is part of the Ck script library.
+Its arguments describe a dialog box:
+.TP
+\fIwindow\fR
+Name of top-level window to use for dialog. Any existing window
+by this name is destroyed.
+.TP
+\fItitle\fR
+Text to appear in the window's top line as title for the dialog.
+.TP
+\fItext\fR
+Message to appear in the top portion of the dialog box.
+.TP
+\fIstring\fR
+There will be one button for each of these arguments.
+Each \fIstring\fR specifies text to display in a button,
+in order from left to right.
+.PP
+After creating a dialog box, \fBck_dialog\fR waits for the user to
+select one of the buttons either by clicking on the button with the
+mouse or by typing return or space to invoke the focus button (if any).
+Then it returns the index of the selected button: 0 for the leftmost
+button, 1 for the button next to it, and so on.
+
+.SH KEYWORDS
+bitmap, dialog
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH entry n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+entry \- Create and manipulate entry widgets
+.SH SYNOPSIS
+\fBentry\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBattributes\fR \fBjustify\fR \fBselectForeground\fR \fBxScrollCommand\fR
+\fBbackground\fR \fBselectAttributes\fR \fBtakeFocus\fR
+\fBforeground\fR \fBselectBackground\fR \fBtextVariable\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBshow\fR
+Class: \fBShow\fR
+Command-Line Switch: \fB\-show\fR
+.fi
+.IP
+If this option is specified, then the true contents of the entry
+are not displayed in the window.
+Instead, each character in the entry's value will be displayed as
+the first character in the value of this option, such as ``*''.
+This is useful, for example, if the entry is to be used to enter
+a password.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of two states for the entry: \fBnormal\fR or \fBdisabled\fR.
+If the entry is disabled then the value may not be changed using widget
+commands and no insertion cursor will be displayed, even if the input focus is
+in the widget.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies an integer value indicating the desired width of the entry window,
+in screen columns. If the value is less than or equal to zero, the widget
+picks a size just large enough to hold its current text. The default width
+is 16.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBentry\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into an entry widget.
+Additional options, described above, may be specified on the
+command line or in the option database
+to configure aspects of the entry such as its colors and attributes.
+The \fBentry\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+An entry is a widget that displays a one-line text string and
+allows that string to be edited using widget commands described below, which
+are typically bound to keystrokes and mouse actions.
+When first created, an entry's string is empty.
+A portion of the entry may be selected as described below.
+Entries also observe the standard Ck rules for dealing with the
+input focus. When an entry has the input focus it displays an
+\fIinsertion cursor\fR to indicate where new characters will be
+inserted.
+.PP
+Entries are capable of displaying strings that are too long to
+fit entirely within the widget's window. In this case, only a
+portion of the string will be displayed; commands described below
+may be used to change the view in the window. Entries use
+the standard \fBxScrollCommand\fR mechanism for interacting with
+scrollbars (see the description of the \fBxScrollCommand\fR option
+for details).
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBentry\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command.
+.PP
+Many of the widget commands for entries take one or more indices as
+arguments. An index specifies a particular character in the entry's
+string, in any of the following ways:
+.TP 12
+\fInumber\fR
+Specifies the character as a numerical index, where 0 corresponds
+to the first character in the string.
+.TP 12
+\fBanchor\fR
+Indicates the anchor point for the selection, which is set with the
+\fBselect from\fR and \fBselect adjust\fR widget commands.
+.TP 12
+\fBend\fR
+Indicates the character just after the last one in the entry's string.
+This is equivalent to specifying a numerical index equal to the length
+of the entry's string.
+.TP 12
+\fBinsert\fR
+Indicates the character adjacent to and immediately following the
+insertion cursor.
+.TP 12
+\fBsel.first\fR
+Indicates the first character in the selection. It is an error to
+use this form if the selection isn't in the entry window.
+.TP 12
+\fBsel.last\fR
+Indicates the character just after the last one in the selection.
+It is an error to use this form if the selection isn't in the
+entry window.
+.TP 12
+\fB@\fInumber\fR
+In this form, \fInumber\fR is treated as an x-coordinate in the
+entry's window; the character spanning that x-coordinate is used.
+For example, ``\fB@0\fR'' indicates the left-most character in the
+window.
+.LP
+Abbreviations may be used for any of the forms above, e.g. ``\fBe\fR''
+or ``\fBsel.f\fR''. In general, out-of-range indices are automatically
+rounded to the nearest legal value.
+.PP
+The following commands are possible for entry widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBentry\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBentry\fR
+command.
+.TP
+\fIpathName \fBdelete \fIfirst \fR?\fIlast\fR?
+Delete one or more elements of the entry.
+\fIFirst\fR is the index of the first character to delete, and
+\fIlast\fR is the index of the character just after the last
+one to delete.
+If \fIlast\fR isn't specified it defaults to \fIfirst\fR+1,
+i.e. a single character is deleted.
+This command returns an empty string.
+.TP
+\fIpathName \fBget\fR
+Returns the entry's string.
+.TP
+\fIpathName \fBicursor \fIindex\fR
+Arrange for the insertion cursor to be displayed just before the character
+given by \fIindex\fR. Returns an empty string.
+.TP
+\fIpathName \fBindex\fI index\fR
+Returns the numerical index corresponding to \fIindex\fR.
+.TP
+\fIpathName \fBinsert \fIindex string\fR
+Insert the characters of \fIstring\fR just before the character
+indicated by \fIindex\fR. Returns an empty string.
+.TP
+\fIpathName \fBselection \fIoption arg\fR
+This command is used to adjust the selection within an entry. It
+has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBselection adjust \fIindex\fR
+Locate the end of the selection nearest to the character given by
+\fIindex\fR, and adjust that end of the selection to be at \fIindex\fR
+(i.e including but not going beyond \fIindex\fR). The other
+end of the selection is made the anchor point for future
+\fBselect to\fR commands. If the selection
+isn't currently in the entry, then a new selection is created to
+include the characters between \fIindex\fR and the most recent
+selection anchor point, inclusive.
+Returns an empty string.
+.TP
+\fIpathName \fBselection clear\fR
+Clear the selection if it is currently in this widget. If the
+selection isn't in this widget then the command has no effect.
+Returns an empty string.
+.TP
+\fIpathName \fBselection from \fIindex\fR
+Set the selection anchor point to just before the character
+given by \fIindex\fR. Doesn't change the selection.
+Returns an empty string.
+.TP
+\fIpathName \fBselection present\fR
+Returns 1 if there is are characters selected in the entry,
+0 if nothing is selected.
+.TP
+\fIpathName \fBselection range \fIstart\fR \fIend\fR
+Sets the selection to include the characters starting with
+the one indexed by \fIstart\fR and ending with the one just
+before \fIend\fR.
+If \fIend\fR refers to the same character as \fIstart\fR or an
+earlier one, then the entry's selection is cleared.
+.TP
+\fIpathName \fBselection to \fIindex\fR
+If \fIindex\fR is before the anchor point, set the selection
+to the characters from \fIindex\fR up to but not including
+the anchor point.
+If \fIindex\fR is the same as the anchor point, do nothing.
+If \fIindex\fR is after the anchor point, set the selection
+to the characters from the anchor point up to but not including
+\fIindex\fR.
+The anchor point is determined by the most recent \fBselect from\fR
+or \fBselect adjust\fR command in this widget.
+If the selection isn't in this widget then a new selection is
+created using the most recent anchor point specified for the widget.
+Returns an empty string.
+.RE
+.TP
+\fIpathName \fBxview \fIargs\fR
+This command is used to query and change the horizontal position of the
+text in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the entry's text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview\fR \fIindex\fR
+Adjusts the view in the window so that the character given by \fIindex\fR
+is displayed at the left edge of the window.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that the character \fIfraction\fR of the
+way through the text appears at the left edge of the window.
+\fIFraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR average-width characters on the display; if it is
+\fBpages\fR then the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+
+.SH "DEFAULT BINDINGS"
+.PP
+Ck automatically creates class bindings for entries that give them
+the following default behavior.
+.IP [1]
+Clicking mouse button 1 positions the insertion cursor
+just before the character underneath the mouse cursor, sets the
+input focus to this widget, and clears any selection in the widget.
+.IP [2]
+If any normal printing characters are typed in an entry, they are
+inserted at the point of the insertion cursor.
+.IP [3]
+The Left and Right keys move the insertion cursor one character to the
+left or right; they also clear any selection in the entry and set
+the selection anchor.
+Control-b and Control-f behave the same as Left and Right, respectively.
+.IP [4]
+The Home key, or Control-a, will move the insertion cursor to the
+beginning of the entry and clear any selection in the entry.
+.IP [5]
+The End key, or Control-e, will move the insertion cursor to the
+end of the entry and clear any selection in the entry.
+.IP [6]
+The Select key sets the selection anchor to the position
+of the insertion cursor. It doesn't affect the current selection.
+.IP [7]
+The Delete key deletes the selection, if there is one in the entry.
+If there is no selection, it deletes the character to the right of
+the insertion cursor.
+.IP [8]
+The BackSpace key and Control-h delete the selection, if there is one
+in the entry.
+If there is no selection, it deletes the character to the left of
+the insertion cursor.
+.IP [9]
+Control-d deletes the character to the right of the insertion cursor.
+.IP [10]
+Control-k deletes all the characters to the right of the insertion
+cursor.
+.IP [11]
+Control-t reverses the order of the two characters to the right of
+the insertion cursor.
+.PP
+If the entry is disabled using the \fB\-state\fR option, then the entry's
+view can still be adjusted and text in the entry can still be selected,
+but no insertion cursor will be displayed and no text modifications will
+take place.
+.PP
+The behavior of entries can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+entry, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH entryx n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+entryx \- Create extended entry widgets
+.SH SYNOPSIS
+\fBentryx \fIpathName ?options?\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+This procedure is part of the Ck script library. It is a slightly extended
+version of the Tcl \fBentry\fR command which provides the following
+additional command line options:
+.TP
+\fB\-default \fIvalue\fR
+Default value for \fBinteger\fR, \fBunsigned\fR, and \fBfloat\fR modes,
+which is stored in entry widget on FocusOut, if the value in the widget
+is not a legal number. Defaults to an empty string.
+.TP
+\fB\-fieldwidth \fInumber\fR
+Limits the string in the entry widget to at most \fInumber\fR characters.
+Defaults to some ten thousand characters.
+.TP
+\fB\-initial \fIvalue\fR
+Initial value for the entry's string. If this option is omitted, no
+initial value is set.
+.TP
+\fB\-mode \fImodeName\fR
+Determines the additional bindings for input checking which will be
+bound to the entry widget. \fIModeName\fR must be one of \fBinteger\fR
+(for integer numbers including optional sign), \fBunsigned\fR (for
+integer numbers without sign), \fBfloat\fR (for
+floating point numbers including optional sign and fractional part,
+but without exponent part), \fBnormal\fR (for entries without input
+checking at all), \fBregexp\fR (for checking the entry's string
+against a regular expression), and \fBboolean\fR (for boolean values,
+e.g. 0 or 1, Y or N and so on).
+If the \fB\-mode\fR option is omitted, \fBnormal\fR is chosen as default.
+.TP
+\fB\-offvalue \fIchar\fR
+For mode \fBboolean\fR entry widgets only: \fIchar\fR is used for the
+``false'' state of the entry. \fIChar\fR defaults to ``0''. It is always
+converted to upper case.
+.TP
+\fB\-onvalue \fIchar\fR
+For mode \fBboolean\fR entry widgets only: \fIchar\fR is used for the
+``true'' state of the entry. \fIChar\fR defaults to ``1''. It is always
+converted to upper case.
+.TP
+\fB\-regexp \fIregExp\fR
+For all modes this is the regular expression which provides input filtering.
+This option is ignored for \fBboolean\fR mode.
+.TP
+\fB\-touchvariable \fIvarName\fR
+The global variable \fIvarName\fR is set to 1 whenever the user
+changes the entry's string. The user may reset this variable to 0
+at any time.
+.PP
+These options must be given at creation time of the entry. They cannot
+be modified later using the \fBconfigure\fR widget command.
+.PP
+After creating the entry widget, \fBentryx\fR binds procedures to
+do input checking using the \fBbindtags\fR mechanism to the entry widget.
+These procedures provide for overtype rather than insert mode and give
+the following behaviour:
+.IP [1]
+If mouse button 1 is pressed on the entry and the entry accepts the input
+focus, the input focus is set on the entry and the entry's insertion cursor
+is placed on the very first character.
+.IP [2]
+The Left and Right keys move the insertion cursor one character to the
+left or right. In \fBboolean\fR mode these keys are used for keyboard
+traversal, i.e. the Left key moves the focus to the previous widget in
+focus order, the Right key to the next widget.
+.IP [3]
+The return key moves the input focus to the next widget in focus order.
+.IP [4]
+The Home key moves the insertion cursor to the
+beginning of the entry. In \fBboolean\fR mode this key is ignored.
+.IP [5]
+The End key moves the insertion cursor to the
+end of the entry. In \fBboolean\fR mode this key is ignored.
+.IP [6]
+The Delete key deletes the character to the right of the insertion cursor.
+In \fBboolean\fR mode this key is ignored.
+.IP [7]
+The BackSpace key and Control-h delete the character to the left of
+the insertion cursor. In \fBboolean\fR mode this key is ignored.
+.IP [8]
+The space key deletes from the insertion cursor until the end of the entry,
+if the mode is \fBinteger\fR, \fBunsigned\fR, \fBfloat\fR or \fBregexp\fR.
+For \fBregexp\fR mode, the space character must not be part of the regular
+expression to achieve this behaviour. Otherwise it is treated as all other
+printable keys. In \fBboolean\fR mode this key toggles the entry's value.
+.IP [9]
+All other printable keys are checked according to the entry's mode.
+If allowed they overtype the character under the insertion cursor, otherwise
+they are ignored and the terminal's bell is rung.
+Lower case characters are automatically converted to upper case, if the
+regular expression filters denies lower case characters but allows upper
+case characters.
+.IP [10]
+FocusIn is bound to display the entry with the \fIreverse\fR attribute for
+monochrome screens or with swapped foreground and background colors on color
+screens; additionally, the insertion cursor is placed on the very first
+character in the entry.
+.IP [11]
+FocusOut is bound to restore the visual effects of FocusIn, i.e. on
+mononochrome screens, the \fIreverse\fR attribute is removed,
+on color screens, the foreground and background colors are restored to
+their original values. For \fBinteger\fR, \fBunsigned\fR, and \fIfloat\fR
+modes, the entry's value is finally checked using the \fBscan\fR Tcl command.
+If the value is legal it is restored into the entry as the return from the
+\fBscan\fR, thus giving the Tcl canonical form for the value, i.e. no leading
+zeros for integral values (which otherwise could be interpreted as octal
+numbers) and a decimal point with at least one fractional
+digit for floating point values (which otherwise could be interpreted as
+integral numbers). If the \fBscan\fR conversion fails, the value specified
+in the \fB\-default\fR option is stored into the entry.
+
+.SH KEYWORDS
+entry, input
--- /dev/null
+'\"
+'\" Copyright (c) 1993 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH exit n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+exit \- Exit the process
+.SH SYNOPSIS
+\fBexit \fR?\fI\-noclear\fR? \fR?\fIreturnCode\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+Terminate the process, returning \fIreturnCode\fR (an integer) to the
+system as the exit status.
+If \fIreturnCode\fR isn't specified then it defaults
+to 0.
+This command replaces the Tcl command by the same name.
+It is identical to Tcl's \fBexit\fR command except that
+before exiting it destroys all the windows managed by
+the process.
+This allows various cleanup operations to be performed, such
+as restoring the terminal's state and clearing the terminal's screen.
+If the \fI\-noclear\fR switch is given, no screen clear takes place.
+
+.SH KEYWORDS
+exit, process
--- /dev/null
+'\"
+'\" Copyright (c) 1994 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH fileevent n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+fileevent \- Execute a script when a file becomes readable or writable
+.SH SYNOPSIS
+\fBfileevent \fIfileId \fBreadable \fR?\fIscript\fR?
+.br
+\fBfileevent \fIfileId \fBwritable \fR?\fIscript\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This command is used to create \fIfile event handlers\fR.
+A file event handler is a binding between a file and a script,
+such that the script is evaluated whenever the file becomes
+readable or writable.
+File event handlers are most commonly used to allow data to be
+received from a child process on an event-driven basis, so that
+the receiver can continue to interact with the user while
+waiting for the data to arrive.
+If an application invokes \fBgets\fR or \fBread\fR when there
+is no input data available, the process will block; until the
+input data arrives, it will not be able to service other events,
+so it will appear to the user to ``freeze up''.
+With \fBfileevent\fR, the process can tell when data is present
+and only invoke \fBgets\fR or \fBread\fR when they won't block.
+.PP
+The \fIfileId\fR argument to \fBfileevent\fR refers to an open file;
+it must be \fBstdin\fR, \fBstdout\fR, \fBstderr\fR, or the return value
+from some previous \fBopen\fR command.
+If the \fIscript\fR argument is specified, then \fBfileevent\fR
+creates a new event handler: \fIscript\fR will be evaluated
+whenever the file becomes readable or writable (depending on the
+second argument to \fBfileevent\fR).
+In this case \fBfileevent\fR returns an empty string.
+The \fBreadable\fR and \fBwritable\fR event handlers for a file
+are independent, and may be created and deleted separately.
+However, there may be at most one \fBreadable\fR and one \fBwritable\fR
+handler for a file at a given time.
+If \fBfileevent\fR is called when the specified handler already
+exists, the new script replaces the old one.
+.PP
+If the \fIscript\fR argument is not specified, \fBfileevent\fR
+returns the current script for \fIfileId\fR, or an empty string
+if there is none.
+If the \fIscript\fR argument is specified as an empty string
+then the event handler is deleted, so that no script will be invoked.
+A file event handler is also deleted automatically whenever
+its file is closed or its interpreter is deleted.
+.PP
+A file is considered to be readable whenever the \fBgets\fR
+and \fBread\fR commands can return without blocking.
+A file is also considered to be readable if an end-of-file or
+error condition is present.
+It is important for \fIscript\fR to check for these conditions
+and handle them appropriately; for example, if there is no special
+check for end-of-file, an infinite loop may occur where \fIscript\fR
+reads no data, returns, and is immediately invoked again.
+.PP
+When using \fBfileevent\fR for event-driven I/O, it's important
+to read the file in the same units that are written
+from the other end.
+For example, suppose that you are using \fBfileevent\fR to
+read data generated by a child process.
+If the child process is writing whole lines, then you should use
+\fBgets\fR to read those lines.
+If the child generates one line at a time then you shouldn't
+make more than a single call to \fBgets\fR in \fIscript\fR: the first call
+will consume all the available data, so the second call may block.
+You can also use \fBread\fR to read the child's data, but only
+if you know how many bytes the child is writing at a time: if
+you try to read more bytes than the child has written, the
+\fBread\fR call will block.
+.PP
+A file is considered to be writable if at least one byte of data
+can be written to the file without blocking, or if an error condition
+is present.
+Write handlers are probably not very useful without additional command
+support.
+The \fBputs\fR command is dangerous since it write more than
+one byte at a time and may thus block.
+What is really needed is a new non-blocking form of write that
+saves any data that couldn't be written to the file.
+.PP
+The script for a file event is executed at global level (outside the
+context of any Tcl procedure).
+If an error occurs while executing the script then the
+\fBtkerror\fR mechanism is used to report the error.
+In addition, the file event handler is deleted if it ever returns
+an error; this is done in order to prevent infinite loops due to
+buggy handlers.
+
+.SH CREDITS
+.PP
+\fBfileevent\fR is based on the \fBaddinput\fR command created
+by Mark Diekhans.
+
+.SH "SEE ALSO"
+tkerror
+
+.SH KEYWORDS
+asynchronous I/O, event handler, file, readable, script, writable
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH focus n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+focus \- Manage the input focus
+.SH SYNOPSIS
+\fBfocus\fR
+.br
+\fBfocus \fIwindow\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBfocus\fR command is used to manage the Ck input focus.
+At any given time, one window on the terminal's screen is designated as
+the \fIfocus window\fR; any key press events are sent to that window.
+The Tcl procedures \fBck_focusNext\fR and \fBck_focusPrev\fR
+implement a focus order among the windows of a top-level; they
+are used in the default bindings for Tab and Shift-Tab, among other
+things. Switching the focus among different top-levels is up
+to the user.
+.PP
+The \fBfocus\fR command can take any of the following forms:
+.TP
+\fBfocus\fR
+Returns the path name of the focus window or an empty string if no window
+in the application has the focus.
+.TP
+\fBfocus \fIwindow\fR
+This command sets the input focus to \fIwindow\fR and returns an
+empty string. If \fIwindow\fR is in a different top-level than
+the current input focus window, then \fIwindow's\fR top-level
+is automatically raised just as if the \fBraise\fR Tcl command
+had been invoked.
+If \fIwindow\fR is an empty string then the command does nothing.
+
+.SH KEYWORDS
+events, focus, keyboard, top-level
--- /dev/null
+'\"
+'\" Copyright (c) 1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH ck_focusNext n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ck_focusNext, ck_focusPrev \- Utility procedures for managing the input focus.
+.SH SYNOPSIS
+\fBck_focusNext \fIwindow\fR
+.br
+\fBck_focusPrev \fIwindow\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+\fBck_focusNext\fR is a utility procedure used for keyboard traversal.
+It returns the ``next'' window after \fIwindow\fR in focus order.
+The focus order is determined by
+the stacking order of windows and the structure of the window hierarchy.
+Among siblings, the focus order is the same as the stacking order, with the
+lowest window being first.
+If a window has children, the window is visited first, followed by
+its children (recursively), followed by its next sibling.
+Top-level windows other than \fIwindow\fR are skipped, so that
+\fBck_focusNext\fR never returns a window in a different top-level
+from \fIwindow\fR.
+.PP
+After computing the next window, \fBck_focusNext\fR examines the
+window's \fB\-takefocus\fR option to see whether it should be skipped.
+If so, \fBck_focusNext\fR continues on to the next window in the focus
+order, until it eventually finds a window that will accept the focus
+or returns back to \fIwindow\fR.
+.PP
+\fBck_focusPrev\fR is similar to \fBck_focusNext\fR except that it
+returns the window just before \fIwindow\fR in the focus order.
+
+.SH KEYWORDS
+focus, keyboard traversal, toplevel
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH frame n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+frame \- Create and manipulate frame widgets
+.SH SYNOPSIS
+\fBframe\fI \fIpathName ?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBattributes\fR \fBborder\fR \fBforeground\fR \fBtakefocus\fR
+\fBbackground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBclass\fR
+Class: \fBClass\fR
+Command-Line Switch: \fB\-class\fR
+.fi
+.IP
+Specifies a class for the window.
+This class will be used when querying the option database for
+the window's other options, and it will also be used later for
+other purposes such as bindings.
+The \fBclass\fR option may not be changed with the \fBconfigure\fR
+widget command.
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies the desired height for the window in screen lines.
+If this option is equal to zero then the window will
+not request any size at all.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies the desired width for the window in screen columns.
+If this option is equal to zero then the window will
+not request any size at all.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBframe\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a frame widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the frame such as its background color
+and attributes. The \fBframe\fR command returns the
+path name of the new window.
+.PP
+A frame is a simple widget. Its primary purpose is to act as a
+spacer or container for complex window layouts. The only features
+of a frame are its background color, attributes and border.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBframe\fR command creates a new Tcl command whose
+name is the same as the path name of the frame's window. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIPathName\fR is the name of the command, which is the same as
+the frame widget's path name. \fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for frame widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBframe\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? \fI?value option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBframe\fR
+command.
+
+.SH BINDINGS
+.PP
+When a new frame is created, it has no default event bindings:
+frames are not intended to be interactive.
+
+.SH KEYWORDS
+frame, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1996 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\"
+.so man.macros
+.TH grid n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+grid \- Geometry manager that arranges widgets in a grid
+.SH SYNOPSIS
+\fBgrid \fIoption arg \fR?\fIarg ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBgrid\fR command is used to communicate with the grid
+geometry manager that arranges widgets in rows and columns inside
+of another window, called the geometry master (or master window).
+The \fBgrid\fR command can have any of several forms, depending
+on the \fIoption\fR argument:
+.TP
+\fBgrid \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+If the first argument to \fBgrid\fR is a window name (any value
+starting with ``.''), then the command is processed in the same
+way as \fBgrid configure\fR.
+.TP
+\fBgrid bbox \fImaster column row\fR
+The bounding box (in rows or columns) is returned for the space occupied by
+the grid position indicated by \fIcolumn\fP and \fIrow\fP. The
+return value consists of 4 integers. The first two are the column/row offset
+from the master window (x then y) of the top-left corner of the grid
+cell, and the second two are the width and height of the cell.
+.TP
+\fBgrid columnconfigure \fImaster index \fR?\fI\-option value...\fR?
+Query or set the column properties of the \fIindex\fP column of the
+geometry master, \fImaster\fP.
+The valid options are \fB\-minsize\fP and \fB\-weight\fP.
+The \fB\-minsize\fP option sets the minimum column size,
+and the \fB\-weight\fP option (a floating point value)
+sets the relative weight for apportioning
+any extra spaces among
+columns. If no value is specified, the current value is returned.
+.TP
+\fBgrid configure \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+The arguments consist of the names of one or more slave windows
+followed by pairs of arguments that specify how
+to manage the slaves.
+The characters \fB\-\fP, \fBx\fP and \fB^\fP,
+can be specified instead of a window name to alter the default
+location of a \fIslave\fP, as described in the ``RELATIVE PLACEMENT''
+section, below.
+The following options are supported:
+.RS
+.TP
+\fB\-column \fIn\fR
+Insert the slave so that it occupies the \fIn\fPth column in the grid.
+Column numbers start with 0. If this option is not supplied, then the
+slave is arranged just to the right of previous slave specified on this
+call to \fIgrid\fP, or column "0" if it is the first slave. For each
+\fBx\fP that immediately precedes the \fIslave\fP, the column position
+is incremented by one. Thus the \fBx\fP represents a blank column
+for this row in the grid.
+.TP
+\fB\-columnspan \fIn\fR
+Insert the slave so that it occupies \fIn\fP columns in the grid.
+The default is one column, unless the window name is followed by a
+\fB\-\fP, in which case the columnspan is incremented once for each immediately
+following \fB\-\fP.
+.TP
+\fB\-ipadx \fIamount\fR
+The \fIamount\fR specifies how much horizontal internal padding to
+leave on each side of the slave(s).
+\fIAmount\fR is specified in terminal columns. It defaults to 0.
+.TP
+\fB\-ipady \fIamount\fR
+The \fIamount\fR specifies how much vertical internal padding to
+leave on on the top and bottom of the slave(s).
+\fIAmount\fR is specified in terminal rows. It defaults to 0.
+.TP
+\fB\-padx \fIamount\fR
+The \fIamount\fR specifies how much horizontal external padding to
+leave on each side of the slave(s). The \fIamount\fR defaults to 0.
+.TP
+\fB\-pady \fIamount\fR
+The \fIamount\fR specifies how much vertical external padding to
+leave on the top and bottom of the slave(s). The \fIamount\fR defaults to 0.
+.TP
+\fB\-row \fIn\fR
+Insert the slave so that it occupies the \fIn\fPth row in the grid.
+Row numbers start with 0. If this option is not supplied, then the
+slave is arranged on the same row as the previous slave specified on this
+call to \fBgrid\fP, or the first unoccupied row if this is the first slave.
+.TP
+\fB\-rowspan \fIn\fR
+Insert the slave so that it occupies \fIn\fP rows in the grid.
+The default is one row. If the next \fBgrid\fP command contains
+\fB^\fP characters instead of \fIslaves\fP that line up with the columns
+of this \fIslave\fP, then the \fBrowspan\fP of this \fIslave\fP is
+extended by one.
+.TP
+\fB\-sticky \fIstyle\fR
+If a slave's parcel is larger than its requested dimensions, this
+option may be used to position (or stretch) the slave within its cavity.
+\fIStyle\fR is a string that contains zero or more of the characters
+\fBn\fP, \fBs\fP, \fBe\fP or \fBw\fP.
+The string can optionally contains spaces or
+commas, but they are ignored. Each letter refers to a side (north, south,
+east, or west) that the slave will "stick" to. If both \fBn\fP and \fBs\fP (or
+\fBe\fP and \fBw\fP) are specified, the slave will be stretched to fill the entire
+height (or width) of its cavity. The \fBsticky\fP option subsumes the
+combination of \fB\-anchor\fP and \fB\-fill\fP that is used by \fBpack\fP.
+The default is \fB{}\fP, which causes the slave to be centered in its cavity,
+at its requested size.
+.LP
+If any of the slaves are already managed by the geometry manager
+then any unspecified options for them retain their previous values rather
+than receiving default values.
+.RE
+.TP
+\fBgrid forget \fIslave \fR?\fIslave ...\fR?
+Removes each of the \fIslave\fRs from grid for its
+master and unmaps their windows.
+The slaves will no longer be managed by the grid geometry manager.
+.TP
+\fBgrid info \fIslave\fR
+Returns a list whose elements are the current configuration state of
+the slave given by \fIslave\fR in the same option-value form that
+might be specified to \fBgrid configure\fR.
+.TP
+\fBgrid location \fImaster x y\fR
+Given \fIx\fP and \fIy\fP values in terminal columns/rows relative to the
+master window, the column and row number at that \fIx\fP and \fIy\fP
+location is returned.
+For locations that are above or to the left of the grid, \fB-1\fP is returned.
+.TP
+\fBgrid propagate \fImaster\fR ?\fIboolean\fR?
+If \fIboolean\fR has a true boolean value such as \fB1\fR or \fBon\fR
+then propagation is enabled for \fImaster\fR, which must be a window
+name (see ``GEOMETRY PROPAGATION'' below).
+If \fIboolean\fR has a false boolean value then propagation is
+disabled for \fImaster\fR.
+In either of these cases an empty string is returned.
+If \fIboolean\fR is omitted then the command returns \fB0\fR or
+\fB1\fR to indicate whether propagation is currently enabled
+for \fImaster\fR.
+Propagation is enabled by default.
+.TP
+\fBgrid \fRrowconfigure \fImaster index \fR?\fI\-option value...\fR?
+Query or set the row properties of the \fIindex\fP row of the
+geometry master, \fImaster\fP.
+The valid options are \fB\-minsize\fP and \fB\-weight\fP.
+\fBMinsize\fP sets the minimum row size, in screen units, and \fBweight\fP
+sets the relative weight for apportioning any extra spaces among
+rows. If no value is specified, the current value is returned.
+.TP
+\fBgrid size \fImaster\fR
+Returns the size of the grid (in columns then rows) for \fImaster\fP.
+The size is determined either by the \fIslave\fP occupying the largest
+row or column, or the largest column or row with a \fBminsize\fP or
+\fBweight\fP.
+.TP
+\fBgrid slaves \fImaster\fR ?\fI\-option value\fR?
+If no options are supplied, a list of all of the slaves in \fImaster\fR
+are returned. \fIOption\fP can be either \fB\-row\fP or \fB\-column\fP which
+causes only the slaves in the row (or column) specified by \fIvalue\fP
+to be returned.
+.SH "RELATIVE PLACEMENT"
+.PP
+The \fBgrid\fP command contains a limited set of capabilities that
+permit layouts to be created without specifying the row and column
+information for each slave. This permits slaves to be rearranged,
+added, or removed without the need to explicitly specify row and
+column information.
+When no column or row information is specified for a \fIslave\fP,
+default values are chosen for
+\fBcolumn\fP, \fBrow\fP, \fPcolumnspan\fP and \fProwspan\fP
+at the time the \fIslave\fP is managed. The values are chosen
+based upon the current layout of the grid, the position of the \fIslave\fP
+relative to other \fIslave\fPs in the same grid command, and the presence
+of the characters \fB\-\fP, \fB^\fP, and \fB^\fP in \fBgrid\fP
+command where \fIslave\fP names are normally expected.
+.RS
+.TP
+\fB\-\fP
+This increases the columnspan of the \fIslave\fP to the left. Several
+\fB\-\fP's in a row will successively increase the columnspan. S \fB\-\fP
+may not follow a \fB^\fP or a \fBx\fP.
+.TP
+\fBx\fP
+This leaves an empty column between the \fIslave\fP on the left and
+the \fIslave\fP on the right.
+.TP
+\fB^\fP
+This extends the \fBrowspan\fP of the \fIslave\fP above the \fB^\fP's
+in the grid. The number of \fB^\fP's in a row must match the number of
+columns spanned by the \fIslave\fP above it.
+.RE
+.SH "GEOMETRY PROPAGATION"
+.PP
+Grid normally computes how large a master must be to
+just exactly meet the needs of its slaves, and it sets the
+requested width and height of the master to these dimensions.
+This causes geometry information to propagate up through a
+window hierarchy to a top-level window so that the entire
+sub-tree sizes itself to fit the needs of the leaf windows.
+However, the \fBgrid propagate\fR command may be used to
+turn off propagation for one or more masters.
+If propagation is disabled then grid will not set
+the requested width and height of the master window.
+This may be useful if, for example, you wish for a master
+window to have a fixed size that you specify.
+
+.SH "RESTRICTIONS ON MASTER WINDOWS"
+.PP
+The master for each slave must be the slave's parent.
+This restriction is necessary to guarantee that the
+slave can be placed over any part of its master that is
+visible without danger of the slave being clipped by its parent.
+
+.SH CREDITS
+.PP
+The \fBgrid\fP command is based on the \fIGridBag\fP geometry manager
+written by D. Stein.
+
+.SH KEYWORDS
+geometry manager, location, grid, parcel, propagation, size, pack
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH label n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+label \- Create and manipulate label widgets
+.SH SYNOPSIS
+\fBlabel\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBanchor\fR \fBforeground\fR \fBtextVariable\fR \fBunderlineForeground\fR
+\fBattributes\fR \fBtakeFocus\fR \fBunderline\fR
+\fBbackground\fR \fBtext\fR \fBunderlineAttributes\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies a desired height for the label in screen lines.
+If this option isn't specified, the label's desired height is 1 line.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies a desired width for the label in screen columns.
+If this option isn't specified, the label's desired width is computed
+from the size of the text being displayed in it.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBlabel\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a label widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the label such as its colors, font,
+text, and initial relief. The \fBlabel\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A label is a widget that displays a textual string.
+The label can be manipulated in a few simple ways, such as
+changing its attributes or text, using the commands described below.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBlabel\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for label widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBlabel\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBlabel\fR
+command.
+
+.SH BINDINGS
+.PP
+When a new label is created, it has no default event bindings:
+labels are not intended to be interactive.
+
+.SH KEYWORDS
+label, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH listbox n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+listbox \- Create and manipulate listbox widgets
+.SH SYNOPSIS
+\fBlistbox\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBactiveAttributes\fR \fBattributes\fR \fBselectAttributes\fR \fBtakeFocus\fR
+\fBactiveBackground\fR \fBbackground\fR \fBselectBackground\fR \fBxScrollCommand\fR
+\fBactiveForeground\fR \fBforeground\fR \fBselectForeground\fR \fByScrollCommand\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies the desired height for the window, in lines.
+If zero or less, then the desired height for the window is made just
+large enough to hold all the elements in the listbox.
+.LP
+.nf
+Name: \fBselectMode\fR
+Class: \fBSelectMode\fR
+Command-Line Switch: \fB\-selectmode\fR
+.fi
+.IP
+Specifies one of several styles for manipulating the selection.
+The value of the option may be arbitrary, but the default bindings
+expect it to be either \fBsingle\fR, \fBbrowse\fR or \fBmultiple\fR;
+the default value is \fBbrowse\fR.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies the desired width for the window in characters.
+If zero or less, then the desired width for the window is made just
+large enough to hold all the elements in the listbox.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBlistbox\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a listbox widget.
+Additional options, described above, may be specified on the command line
+or in the option database to configure aspects of the listbox such as its
+colors, attributes and text. The \fBlistbox\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A listbox is a widget that displays a list of strings, one per line.
+When first created, a new listbox has no elements.
+Elements may be added or deleted using widget commands described
+below. In addition, one or more elements may be selected as described
+below.
+.PP
+It is not necessary for all the elements to be
+displayed in the listbox window at once; commands described below
+may be used to change the view in the window. Listboxes allow
+scrolling in both directions using the standard \fBxScrollCommand\fR
+and \fByScrollCommand\fR options.
+
+.SH "INDICES"
+.PP
+Many of the widget commands for listboxes take one or more indices
+as arguments.
+An index specifies a particular element of the listbox, in any of
+the following ways:
+.TP 12
+\fInumber\fR
+Specifies the element as a numerical index, where 0 corresponds
+to the first element in the listbox.
+.TP 12
+\fBactive\fR
+Indicates the element that has the location cursor. This element
+will be displayed with the \fBactiveAttributes\fR, \fBactiveBackground\fR,
+and \fBactiveForeground\fR options if the keyboard focus is in the
+listbox. The element is specified with the \fBactivate\fR
+widget command.
+.TP 12
+\fBanchor\fR
+Indicates the anchor point for the selection, which is set with the
+\fBselection anchor\fR widget command.
+.TP 12
+\fBend\fR
+Indicates the end of the listbox.
+For some commands this means just after the last element;
+for other commands it means the last element.
+.TP 12
+\fB@\fIx\fB,\fIy\fR
+Indicates the element that covers the point in the listbox window
+specified by \fIx\fR and \fIy\fR (in screen coordinates). If no
+element covers that point, then the closest element to that
+point is used.
+.LP
+In the widget command descriptions below, arguments named \fIindex\fR,
+\fIfirst\fR, and \fIlast\fR always contain text indices in one of
+the above forms.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBlistbox\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for listbox widgets:
+.TP
+\fIpathName \fBactivate\fR \fIindex\fR
+Sets the active element to the one indicated by \fIindex\fR.
+The active element is drawn with the \fBactiveAttributes\fR,
+\fBactiveBackground\fR, and \fBactiveForeground\fR options
+when the widget has the input focus, and its index may be retrieved
+with the index \fBactive\fR.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBlistbox\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBlistbox\fR
+command.
+.TP
+\fIpathName \fBcurselection\fR
+Returns a list containing the numerical indices of
+all of the elements in the listbox that are currently selected.
+If there are no elements selected in the listbox then an empty
+string is returned.
+.TP
+\fIpathName \fBdelete \fIfirst \fR?\fIlast\fR?
+Deletes one or more elements of the listbox. \fIFirst\fR and \fIlast\fR
+are indices specifying the first and last elements in the range
+to delete. If \fIlast\fR isn't specified it defaults to
+\fIfirst\fR, i.e. a single element is deleted.
+.TP
+\fIpathName \fBget \fIfirst\fR ?\fIlast\fR?
+If \fIlast\fR is omitted, returns the contents of the listbox
+element indicated by \fIfirst\fR.
+If \fIlast\fR is specified, the command returns a list whose elements
+are all of the listbox elements between \fIfirst\fR and \fIlast\fR,
+inclusive.
+Both \fIfirst\fR and \fIlast\fR may have any of the standard
+forms for indices.
+.TP
+\fIpathName \fBindex \fIindex\fR
+Returns a decimal string giving the integer index value that
+corresponds to \fIindex\fR.
+.TP
+\fIpathName \fBinsert \fIindex \fR?\fIelement element ...\fR?
+Inserts zero or more new elements in the list just before the
+element given by \fIindex\fR. If \fIindex\fR is specified as
+\fBend\fR then the new elements are added to the end of the
+list. Returns an empty string.
+.TP
+\fIpathName \fBnearest \fIy\fR
+Given a y-coordinate within the listbox window, this command returns
+the index of the (visible) listbox element nearest to that y-coordinate.
+.TP
+\fIpathName \fBsee \fIindex\fR
+Adjust the view in the listbox so that the element given by \fIindex\fR
+is visible.
+If the element is already visible then the command has no effect;
+if the element is near one edge of the window then the listbox
+scrolls to bring the element into view at the edge; otherwise
+the listbox scrolls to center the element.
+.TP
+\fIpathName \fBselection \fIoption arg\fR
+This command is used to adjust the selection within a listbox. It
+has several forms, depending on \fIoption\fR:
+.RS
+.TP
+\fIpathName \fBselection anchor \fIindex\fR
+Sets the selection anchor to the element given by \fIindex\fR.
+The selection anchor is the end of the selection that is fixed
+while dragging out a selection with the mouse.
+The index \fBanchor\fR may be used to refer to the anchor
+element.
+.TP
+\fIpathName \fBselection clear \fIfirst \fR?\fIlast\fR?
+If any of the elements between \fIfirst\fR and \fIlast\fR
+(inclusive) are selected, they are deselected.
+The selection state is not changed for elements outside
+this range.
+.TP
+\fIpathName \fBselection includes \fIindex\fR
+Returns 1 if the element indicated by \fIindex\fR is currently
+selected, 0 if it isn't.
+.TP
+\fIpathName \fBselection set \fIfirst \fR?\fIlast\fR?
+Selects all of the elements in the range between
+\fIfirst\fR and \fIlast\fR, inclusive, without affecting
+the selection state of elements outside that range.
+.RE
+.TP
+\fIpathName \fBsize\fR
+Returns a decimal string indicating the total number of elements
+in the listbox.
+.TP
+\fIpathName \fBxview \fIargs\fR
+This command is used to query and change the horizontal position of the
+information in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the horizontal span that is visible in the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the listbox's text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview\fR \fIindex\fR
+Adjusts the view in the window so that the character position given by
+\fIindex\fR is displayed at the left edge of the window.
+Character positions are defined by the width of the character \fB0\fR.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the
+total width of the listbox text is off-screen to the left.
+\fIfraction\fR must be a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR character units (the width of the \fB0\fR character)
+on the display; if it is \fBpages\fR then the view adjusts by
+\fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \fByview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \fByview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the listbox element at the
+top of the window, relative to the listbox as a whole (0.5 means
+it is halfway through the listbox, for example).
+The second element gives the position of the listbox element just after
+the last one in the window, relative to the listbox as a whole.
+These are the same values passed to scrollbars via the \fB\-yscrollcommand\fR
+option.
+.TP
+\fIpathName \fByview\fR \fIindex\fR
+Adjusts the view in the window so that the element given by
+\fIindex\fR is displayed at the top of the window.
+.TP
+\fIpathName \fByview moveto\fI fraction\fR
+Adjusts the view in the window so that the element given by \fIfraction\fR
+appears at the top of the window.
+\fIFraction\fR is a fraction between 0 and 1; 0 indicates the first
+element in the listbox, 0.33 indicates the element one-third the
+way through the listbox, and so on.
+.TP
+\fIpathName \fByview scroll \fInumber what\fR
+This command adjusts the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR.
+If \fIwhat\fR is \fBunits\fR, the view adjusts up or down by
+\fInumber\fR lines; if it is \fBpages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier elements
+become visible; if it is positive then later elements
+become visible.
+.RE
+
+.SH "DEFAULT BINDINGS"
+.PP
+Ck automatically creates class bindings for listboxes. Much of the
+behavior of a listbox is determined by its \fBselectMode\fR option,
+which selects one of three ways of dealing with the selection.
+.PP
+If the selection mode is \fBsingle\fR or \fBbrowse\fR, at most one
+element can be selected in the listbox at once.
+In both modes, clicking button 1 on an element selects
+it and deselects any other selected item.
+.PP
+If the selection mode is \fBmultiple\fR, any number of elements may
+be selected at once, including discontiguous ranges.
+Clicking button 1 on an element toggles its selection state without
+affecting any other elements.
+.PP
+Most people will probably want to use \fBbrowse\fR mode for
+single selections and \fBmultiple\fR mode for multiple selections.
+.PP
+In addition to the above behavior, the following additional behavior
+is defined by the default bindings:
+.IP [1]
+If the Up or Down key is pressed, the location cursor (active
+element) moves up or down one element.
+If the selection mode is \fBbrowse\fR then the
+new active element is also selected and all other elements are
+deselected.
+.IP [2]
+The Left and Right keys scroll the listbox view left and right
+by the one column.
+.IP [3]
+The Prior and Next keys scroll the listbox view up and down
+by one page (the height of the window).
+.IP [4]
+The Home and End keys scroll the listbox horizontally to
+the left and right edges, respectively.
+.IP [5]
+The space and Select keys make a selection at the location cursor
+(active element) just as if mouse button 1 had been pressed over
+this element.
+.PP
+The behavior of listboxes can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+listbox, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH lower n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+lower \- Change a window's position in the stacking order
+.SH SYNOPSIS
+\fBlower \fIwindow \fR?\fIbelowThis\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+If the \fIbelowThis\fR argument is omitted then the command lowers
+\fIwindow\fR so that it is below all of its siblings in the stacking
+order (it will be obscured by any siblings that overlap it and
+will not obscure any siblings).
+If \fIbelowThis\fR is specified then it must be the path name of
+a window that is either a sibling of \fIwindow\fR or the descendant
+of a sibling of \fIwindow\fR.
+In this case the \fBlower\fR command will insert
+\fIwindow\fR into the stacking order just below \fIbelowThis\fR
+(or the ancestor of \fIbelowThis\fR that is a sibling of \fIwindow\fR);
+this could end up either raising or lowering \fIwindow\fR.
+
+.SH KEYWORDS
+lower, obscure, stacking order
--- /dev/null
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out [indent]
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS [type [name]]
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .VS
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" @(#) man.macros 1.3 95/05/06 15:19:04
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ie !"\\$3"" \{\
+.ta \\n()Au \\n()Bu
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH menu n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+menu \- Create and manipulate menu widgets
+.SH SYNOPSIS
+\fBmenu\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBactiveAttributes\fR \fBbackground\fR \fBdisabledForeground\fR \fBunderlineForeground\fR
+\fBactiveBackground\fR \fBborder\fR \fBforeground\fR
+\fBactiveForeground\fR \fBdisabledAttributes\fR \fBtakeFocus\fR
+\fBattributes\fR \fBdisabledBackground\fR \fBunderlineAttributes\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBpostCommand\fR
+Class: \fBCommand\fR
+Command-Line Switch: \fB\-postcommand\fR
+.fi
+.IP
+If this option is specified then it provides a Tcl command to execute
+each time the menu is posted. The command is invoked by the \fBpost\fR
+widget command before posting the menu.
+.LP
+.nf
+Name: \fBselectColor\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-selectcolor\fR
+.fi
+.IP
+For menu entries that are check buttons or radio buttons, this option
+specifies the color to display in the indicator when the check button
+or radio button is selected. On color terminals this defaults to red,
+on monochrome terminals to white.
+.BE
+
+.SH INTRODUCTION
+.PP
+The \fBmenu\fR command creates a new top-level window (given
+by the \fIpathName\fR argument) and makes it into a menu widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the menu such as its colors and font.
+The \fBmenu\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A menu is a widget that displays a collection of one-line entries arranged
+in a column. There exist several different types of entries,
+each with different properties. Entries of different types may be
+combined in a single menu. Menu entries are not the same as
+entry widgets. In fact, menu entries are not even distinct widgets;
+the entire menu is one widget.
+.PP
+Menu entries are displayed with up to three separate fields.
+The main field is a label in the form of a text string.
+If the \fB\-accelerator\fR option is specified for an entry then a second
+textual field is displayed to the right of the label. The accelerator
+typically describes a keystroke sequence that may be typed in the
+application to cause the same result as invoking the menu entry.
+The third field is an \fIindicator\fR. The indicator is present only for
+checkbutton or radiobutton entries. It indicates whether the entry
+is selected or not, and is displayed to the left of the entry's
+string.
+.PP
+In normal use, an entry becomes active (displays itself differently)
+whenever the input focus is over the entry. If a mouse
+button is pressed over the entry then the entry is \fIinvoked\fR.
+The effect of invocation is different for each type of entry;
+these effects are described below in the sections on individual
+entries.
+.PP
+Entries may be \fIdisabled\fR, which causes their labels
+and accelerators to be displayed with other colors.
+The default menu bindings will not allow
+a disabled entry to be activated or invoked.
+Disabled entries may be re-enabled, at which point it becomes
+possible to activate and invoke them again.
+
+.SH "COMMAND ENTRIES"
+.PP
+The most common kind of menu entry is a command entry, which
+behaves much like a button widget. When a command entry is
+invoked, a Tcl command is executed. The Tcl
+command is specified with the \fB\-command\fR option.
+
+.SH "SEPARATOR ENTRIES"
+.PP
+A separator is an entry that is displayed as a horizontal dividing
+line. A separator may not be activated or invoked, and it has
+no behavior other than its display appearance.
+
+.SH "CHECKBUTTON ENTRIES"
+.PP
+A checkbutton menu entry behaves much like a checkbutton widget.
+When it is invoked it toggles back and forth between the selected
+and deselected states. When the entry is selected, a particular
+value is stored in a particular global variable (as determined by
+the \fB\-onvalue\fR and \fB\-variable\fR options for the entry); when
+the entry is deselected another value (determined by the
+\fB\-offvalue\fR option) is stored in the global variable.
+An indicator box is displayed to the left of the label in a checkbutton
+entry. If the entry is selected then the indicator's center is displayed
+in the color given by the \fB-selectcolor\fR option for the entry;
+otherwise the indicator's center is displayed in the background color for
+the menu or menu entry.
+If a \fB\-command\fR option is specified for a checkbutton
+entry, then its value is evaluated as a Tcl command each time the entry
+is invoked; this happens after toggling the entry's
+selected state.
+
+.SH "RADIOBUTTON ENTRIES"
+.PP
+A radiobutton menu entry behaves much like a radiobutton widget.
+Radiobutton entries are organized in groups of which only one
+entry may be selected at a time. Whenever a particular entry
+becomes selected it stores a particular value into a particular
+global variable (as determined by the \fB\-value\fR and
+\fB\-variable\fR options for the entry). This action
+causes any previously-selected entry in the same group
+to deselect itself.
+Once an entry has become selected, any change to the entry's
+associated variable will cause the entry to deselect itself.
+Grouping of radiobutton entries is determined by their
+associated variables: if two entries have the same associated
+variable then they are in the same group.
+An indicator diamond is displayed to the left of the label in each
+radiobutton entry. If the entry is selected then the indicator's
+center is displayed in the color given by the \fB\-selectcolor\fR option
+for the entry;
+otherwise the indicator's center is displayed in the background color for
+the menu or menu entry.
+If a \fB\-command\fR option is specified for a radiobutton
+entry, then its value is evaluated as a Tcl command each time the entry
+is invoked; this happens after selecting the entry.
+
+.SH "CASCADE ENTRIES"
+.PP
+A cascade entry is one with an associated menu (determined
+by the \fB\-menu\fR option). Cascade entries allow the construction
+of cascading menus.
+The \fBpostcascade\fR widget command can be used to post and unpost
+the associated menu just to the right of the cascade entry.
+The associated menu must be a child of the menu containing
+the cascade entry (this is needed in order for menu traversal to
+work correctly).
+.PP
+A cascade entry posts its associated menu by invoking a
+Tcl command of the form
+.RS
+.IP
+\fImenu\fB post \fIx y\fR
+.RE
+.LP
+where \fImenu\fR is the path name of the associated menu, and \fIx\fR
+and \fIy\fR are the root-window coordinates of the upper-right
+corner of the cascade entry.
+The lower-level menu is unposted by executing a Tcl command with
+the form
+.RS
+.IP
+\fImenu\fB unpost\fR
+.RE
+.LP
+where \fImenu\fR is the name of the associated menu.
+.LP
+If a \fB\-command\fR option is specified for a cascade entry then it is
+evaluated as a Tcl command whenever the entry is invoked.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBmenu\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command.
+.PP
+Many of the widget commands for a menu take as one argument an
+indicator of which entry of the menu to operate on. These
+indicators are called \fIindex\fRes and may be specified in
+any of the following forms:
+.TP 12
+\fInumber\fR
+Specifies the entry numerically, where 0 corresponds
+to the top-most entry of the menu, 1 to the entry below it, and
+so on.
+.TP 12
+\fBactive\fR
+Indicates the entry that is currently active. If no entry is
+active then this form is equivalent to \fBnone\fR. This form may
+not be abbreviated.
+.TP 12
+\fBend\fR
+Indicates the bottommost entry in the menu. If there are no
+entries in the menu then this form is equivalent to \fBnone\fR.
+This form may not be abbreviated.
+.TP 12
+\fBlast\fR
+Same as \fBend\fR.
+.TP 12
+\fBnone\fR
+Indicates ``no entry at all''; this is used most commonly with
+the \fBactivate\fR option to deactivate all the entries in the
+menu. In most cases the specification of \fBnone\fR causes
+nothing to happen in the widget command.
+This form may not be abbreviated.
+.TP 12
+\fB@\fInumber\fR
+In this form, \fInumber\fR is treated as a y-coordinate in the
+menu's window; the entry closest to that y-coordinate is used.
+For example, ``\fB@0\fR'' indicates the top-most entry in the
+window.
+.TP 12
+\fIpattern\fR
+If the index doesn't satisfy one of the above forms then this
+form is used. \fIPattern\fR is pattern-matched against the label of
+each entry in the menu, in order from the top down, until a
+matching entry is found. The rules of \fBTcl_StringMatch\fR
+are used.
+.PP
+The following widget commands are possible for menu widgets:
+.TP
+\fIpathName \fBactivate \fIindex\fR
+Change the state of the entry indicated by \fIindex\fR to \fBactive\fR
+and redisplay it using its active colors.
+Any previously-active entry is deactivated. If \fIindex\fR
+is specified as \fBnone\fR, or if the specified entry is
+disabled, then the menu ends up with no active entry.
+Returns an empty string.
+.TP
+\fIpathName \fBadd \fItype \fR?\fIoption value option value ...\fR?
+Add a new entry to the bottom of the menu. The new entry's type
+is given by \fItype\fR and must be one of \fBcascade\fR,
+\fBcheckbutton\fR, \fBcommand\fR, \fBradiobutton\fR, or \fBseparator\fR,
+or a unique abbreviation of one of the above. If additional arguments
+are present, they specify any of the following options:
+.RS
+.TP
+\fB\-activeattributes \fIvalue\fR
+Specifies video attributes to use for displaying this entry when it
+is active.
+If this option is specified as an empty string (the default), then the
+\fBactiveAttributes\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-activebackground \fIvalue\fR
+Specifies a background color to use for displaying this entry when it
+is active.
+If this option is specified as an empty string (the default), then the
+\fBactiveBackground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-activeforeground \fIvalue\fR
+Specifies a foreground color to use for displaying this entry when it
+is active.
+If this option is specified as an empty string (the default), then the
+\fBactiveForeground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-accelerator \fIvalue\fR
+Specifies a string to display at the right side of the menu entry.
+Normally describes an accelerator keystroke sequence that may be
+typed to invoke the same function as the menu entry. This option
+is not available for separator entries.
+.TP
+\fB\-attributes \fIvalue\fR
+Specifies video attributes to use for displaying this entry when it
+is in the normal state (neither active nor disabled).
+If this option is specified as an empty string (the default), then the
+\fBattributes\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-background \fIvalue\fR
+Specifies a background color to use for displaying this entry when it
+is in the normal state (neither active nor disabled).
+If this option is specified as an empty string (the default), then the
+\fBbackground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-command \fIvalue\fR
+For command, checkbutton, and radiobutton entries, specifies a
+Tcl command to execute when the menu entry is invoked.
+For cascade entries, specifies a Tcl command to execute
+when the entry is activated (i.e. just before its submenu is
+posted).
+Not available for separator entries.
+.TP
+\fB\-foreground \fIvalue\fR
+Specifies a foreground color to use for displaying this entry when it
+is in the normal state (neither active nor disabled).
+If this option is specified as an empty string (the default), then the
+\fBforeground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-indicatoron \fIvalue\fR
+Available only for checkbutton and radiobutton entries.
+\fIValue\fR is a boolean that determines whether or not the
+indicator should be displayed.
+.TP
+\fB\-label \fIvalue\fR
+Specifies a string to display as an identifying label in the menu
+entry. Not available for separator entries.
+.TP
+\fB\-menu \fIvalue\fR
+Available only for cascade entries. Specifies the path name of
+the submenu associated with this entry.
+The submenu must be a child of the menu.
+.TP
+\fB\-offvalue \fIvalue\fR
+Available only for checkbutton entries. Specifies the value to
+store in the entry's associated variable when the entry is
+deselected.
+.TP
+\fB\-onvalue \fIvalue\fR
+Available only for checkbutton entries. Specifies the value to
+store in the entry's associated variable when the entry is selected.
+.TP
+\fB\-selectcolor \fIvalue\fR
+Available only for checkbutton and radiobutton entries.
+Specifies the color to display in the indicator when the entry is
+selected.
+If the value is an empty string (the default) then the \fBselectColor\fR
+option for the menu determines the indicator color.
+.TP
+\fB\-state \fIvalue\fR
+Specifies one of three states for the entry: \fBnormal\fR, \fBactive\fR,
+or \fBdisabled\fR. In normal state the entry is displayed using the
+\fBattributes\fR, \fBforeground\fR, and \fBbackground\fR options
+for the entry or for the menu.
+The active state is typically used when the input focus is in the entry.
+In active state the entry is displayed using the
+\fBactiveAttributes\fR, \fBactiveForeground\fR, and \fBactiveBackground\fR
+options for the entry or for the menu.
+Disabled state means that the entry should be insensitive:
+the default bindings will refuse to activate or invoke the entry.
+In this state the entry is displayed according to the
+\fBdisabledAttributes\fR, \fBdisabledForeground\fR, and
+\fBdisabledBackground\fR options for the menu.
+This option is not available for separator entries.
+.TP
+\fB\-underline \fIvalue\fR
+Specifies the integer index of a character to underline in the entry.
+This option is also queried by the default bindings and used to
+implement keyboard traversal.
+0 corresponds to the first character of the text displayed in the entry,
+1 to the next character, and so on.
+This option is not available for separator entries.
+.TP
+\fB\-underlineAttributes \fIvalue\fR
+Specifies video attributes to use for displaying the underlined
+character in this entry when it
+is in the normal state (neither active nor disabled).
+If this option is specified as an empty string (the default), then the
+\fBunderlineAttributes\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-underlineForeground \fIvalue\fR
+Specifies a foreground color to use for displaying the underlined
+character in this entry when it
+is in the normal state (neither active nor disabled).
+If this option is specified as an empty string (the default), then the
+\fBunderlineForeground\fR option for the overall menu is used.
+This option is not available for separator entries.
+.TP
+\fB\-value \fIvalue\fR
+Available only for radiobutton entries. Specifies the value to
+store in the entry's associated variable when the entry is selected.
+.TP
+\fB\-variable \fIvalue\fR
+Available only for checkbutton and radiobutton entries. Specifies
+the name of a global value to set when the entry is selected.
+For checkbutton entries the variable is also set when the entry
+is deselected. For radiobutton entries, changing the variable
+causes the currently-selected entry to deselect itself.
+.LP
+The \fBadd\fR widget command returns an empty string.
+.RE
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBmenu\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBmenu\fR
+command.
+.TP
+\fIpathName \fBdelete \fIindex1\fR ?\fIindex2\fR?
+Delete all of the menu entries between \fIindex1\fR and
+\fIindex2\fR inclusive.
+If \fIindex2\fR is omitted then it defaults to \fIindex1\fR.
+.TP
+\fIpathName \fBentrycget\fR \fIindex option\fR
+Returns the current value of a configuration option for
+the entry given by \fIindex\fR.
+\fIOption\fR may have any of the values accepted by the \fBadd\fR
+widget command.
+.TP
+\fIpathName \fBentryconfigure \fIindex \fR?\fIoptions\fR?
+This command is similar to the \fBconfigure\fR command, except that
+it applies to the options for an individual entry, whereas \fBconfigure\fR
+applies to the options for the menu as a whole.
+\fIOptions\fR may have any of the values accepted by the \fBadd\fR
+widget command. If \fIoptions\fR are specified, options are modified
+as indicated
+in the command and the command returns an empty string.
+If no \fIoptions\fR are specified, returns a list describing
+the current options for entry \fIindex\fR.
+.TP
+\fIpathName \fBindex \fIindex\fR
+Returns the numerical index corresponding to \fIindex\fR, or
+\fBnone\fR if \fIindex\fR was specified as \fBnone\fR.
+.TP
+\fIpathName \fBinsert \fIindex\fR \fItype \fR?\fIoption value option value ...\fR?
+Same as the \fBadd\fR widget command except that it inserts the new
+entry just before the entry given by \fIindex\fR, instead of appending
+to the end of the menu. The \fItype\fR, \fIoption\fR, and \fIvalue\fR
+arguments have the same interpretation as for the \fBadd\fR widget
+command. It is not possible to insert new menu entries before the
+tear-off entry, if the menu has one.
+.TP
+\fIpathName \fBinvoke \fIindex\fR
+Invoke the action of the menu entry. See the sections on the
+individual entries above for details on what happens. If the
+menu entry is disabled then nothing happens. If the
+entry has a command associated with it then the result of that
+command is returned as the result of the \fBinvoke\fR widget
+command. Otherwise the result is an empty string. Note: invoking
+a menu entry does not automatically unpost the menu; the default
+bindings normally take care of this before invoking the \fBinvoke\fR
+widget command.
+.TP
+\fIpathName \fBpost \fIx y\fR
+Arrange for the menu to be displayed on the screen at the root-window
+coordinates given by \fIx\fR and \fIy\fR. These coordinates are
+adjusted if necessary to guarantee that the entire menu is visible on
+the screen. This command normally returns an empty string.
+If the \fBpostCommand\fR option has been specified, then its value is
+executed as a Tcl script before posting the menu and the result of
+that script is returned as the result of the \fBpost\fR widget
+command.
+If an error returns while executing the command, then the error is
+returned without posting the menu.
+.TP
+\fIpathName \fBpostcascade \fIindex\fR
+Posts the submenu associated with the cascade entry given by
+\fIindex\fR, and unposts any previously posted submenu.
+If \fIindex\fR doesn't correspond to a cascade entry,
+or if \fIpathName\fR isn't posted,
+the command has no effect except to unpost any currently posted
+submenu.
+.TP
+\fIpathName \fBtype \fIindex\fR
+Returns the type of the menu entry given by \fIindex\fR.
+This is the \fItype\fR argument passed to the \fBadd\fR widget
+command when the entry was created, such as \fBcommand\fR
+or \fBseparator\fR.
+.TP
+\fIpathName \fBunpost\fR
+Unmap the window so that it is no longer displayed. If a
+lower-level cascaded menu is posted, unpost that menu. Returns an
+empty string.
+.TP
+\fIpathName \fByposition \fIindex\fR
+Returns a decimal string giving the y-coordinate within the menu
+window of the line in the entry specified by \fIindex\fR.
+
+.SH "MENU CONFIGURATIONS"
+.PP
+The default bindings support two different ways of using menus:
+.TP
+\fBPulldown Menus\fR
+This is the most common case. You create one menubutton widget for
+each top-level menu, and typically you arrange a series of menubuttons
+in a row in a menubar window. You also create the top-level menus
+and any cascaded submenus, and tie them together with \fB\-menu\fR
+options in menubuttons and cascade menu entries. The top-level menu must
+be a child of the menubutton, and each submenu must be a child of the
+menu that refers to it. Once you have done this, the default bindings
+will allow users to traverse and invoke the tree of menus via its
+menubutton; see the \fBmenubutton\fR manual entry for details.
+.TP
+\fBOption Menus\fR
+An option menu consists of a menubutton with an associated menu
+that allows you to select one of several values. The current value
+is displayed in the menubutton and is also stored in a global
+variable. Use the \fBck_optionMenu\fR procedure to create option
+menubuttons and their menus.
+
+.SH "DEFAULT BINDINGS"
+.PP
+Ck automatically creates class bindings for menus that give them
+the following default behavior:
+.IP [1]
+When button 1 is pressed on a menu, the active entry (if any) is invoked.
+The menu also unposts.
+.IP [2]
+The Space and Return keys invoke the active entry and
+unpost the menu.
+.IP [3]
+If any of the entries in a menu have letters underlined with
+with \fB\-underline\fR option, then pressing one of the underlined
+letters (or its upper-case or lower-case equivalent) invokes that
+entry and unposts the menu.
+.IP [4]
+The Escape key aborts a menu selection in progress without invoking any
+entry. It also unposts the menu.
+.IP [5]
+The Up and Down keys activate the next higher or lower entry
+in the menu. When one end of the menu is reached, the active
+entry wraps around to the other end.
+.IP [6]
+The Left key moves to the next menu to the left.
+If the current menu is a cascaded submenu, then the submenu is
+unposted and the current menu entry becomes the cascade entry
+in the parent.
+If the current menu is a top-level menu posted from a
+menubutton, then the current menubutton is unposted and the
+next menubutton to the left is posted.
+Otherwise the key has no effect.
+The left-right order of menubuttons is determined by their stacking
+order: Ck assumes that the lowest menubutton (which by default
+is the first one created) is on the left.
+.IP [7]
+The Right key moves to the next menu to the right.
+If the current entry is a cascade entry, then the submenu is
+posted and the current menu entry becomes the first entry
+in the submenu.
+Otherwise, if the current menu was posted from a
+menubutton, then the current menubutton is unposted and the
+next menubutton to the right is posted.
+.PP
+Disabled menu entries are non-responsive: they don't activate and
+they ignore mouse button presses and releases.
+.PP
+The behavior of menus can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH BUGS
+.PP
+At present it isn't possible to use the
+option database to specify values for the options to individual
+entries.
+
+.SH KEYWORDS
+menu, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH menubutton n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+menubutton \- Create and manipulate menubutton widgets
+.SH SYNOPSIS
+\fBmenubutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBactiveAttributes\fR \fBattributes\fR \fBdisabledForeground\fR \fBtextVariable\fR
+\fBactiveBackground\fR \fBbackground\fR \fBforeground\fR \fBunderline\fR
+\fBactiveForeground\fR \fBdisabledAttributes\fR \fBtakeFocus\fR \fBunderlineAttributes\fR
+\fBanchor\fR \fBdisabledBackground\fR \fBtext\fR \fBunderlineForeground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies a desired height for the menubutton in screen lines.
+If this option isn't specified, the menubutton's desired height is 1 line.
+.LP
+.nf
+Name: \fBindicatorForeground\fR
+Class: \fBIndicatorForeground\fR
+Command-Line Switch: \fB\-indicatorforeground\fR
+.fi
+.IP
+Color in which the indicator rectangle, if any, is drawn.
+On color terminals this defaults to red, on monochrome terminals
+to white.
+.LP
+.nf
+Name: \fBindicatorOn\fR
+Class: \fBIndicatorOn\fR
+Command-Line Switch: \fB\-indicatoron\fR
+.fi
+.IP
+The value must be a proper boolean value. If it is true then
+a small indicator rectangle will be displayed on the right side
+of the menubutton and the default menu bindings will treat this
+as an option menubutton. If false then no indicator will be
+displayed.
+.LP
+.nf
+Name: \fBmenu\fR
+Class: \fBMenuName\fR
+Command-Line Switch: \fB\-menu\fR
+.fi
+.IP
+Specifies the path name of the menu associated with this menubutton.
+The menu must be a child of the menubutton.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of three states for the menubutton: \fBnormal\fR, \fBactive\fR,
+or \fBdisabled\fR. In normal state the menubutton is displayed using the
+\fBattributes\fR, \fBforeground\fR, and \fBbackground\fR options.
+The active state is typically used when the input focus is in the menubutton.
+In active state the menubutton is displayed using the
+\fBactiveAttributes\fR, \fBactiveForeground\fR, and
+\fBactiveBackground\fR options.
+Disabled state means that the menubutton should be insensitive:
+the default bindings will refuse to activate
+the widget and will ignore mouse button presses.
+In this state the \fBdisabledAttributes\fR, \fBdisabledForeground\fR, and
+\fBdisabledBackground\fR options determine how the button is displayed.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies a desired width for the menubutton in screen columns.
+If this option isn't specified, the menubutton's desired width is computed
+from the size of the text being displayed in it.
+.BE
+
+.SH INTRODUCTION
+.PP
+The \fBmenubutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a menubutton widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the menubutton such as its colors, attributes,
+and text. The \fBmenubutton\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A menubutton is a widget that displays a textual string
+and is associated with a menu widget.
+One of the characters may optionally be underlined using the
+\fBunderline\fR, \fBunderlineAttributes\fR, and
+\fBunderlineForeground\fR options.
+In normal usage, pressing mouse button 1 over the menubutton causes
+the associated menu to be posted just underneath the menubutton.
+.PP
+There are several interactions between menubuttons and menus; see
+the \fBmenu\fR manual entry for information on various menu configurations,
+such as pulldown menus and option menus.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBmenubutton\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for menubutton widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBmenubutton\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBmenubutton\fR
+command.
+
+.SH "DEFAULT BINDINGS"
+.PP
+Ck automatically creates class bindings for menubuttons that give them
+the following default behavior:
+.IP [1]
+A menubutton activates whenever it gets the input focus and deactivates
+whenever it loses the input focus.
+.IP [2]
+Pressing mouse button 1 over a menubutton posts the menubutton:
+its associated menu is posted under the menubutton.
+Once a menu entry has been invoked, the menubutton unposts itself.
+.IP [3]
+When a menubutton is posted, its associated menu claims the input
+focus to allow keyboard traversal of the menu and its submenus.
+See the \fBmenu\fR manual entry for details on these bindings.
+.IP [4]
+The F10 key may be typed in any window to post the first menubutton
+under its toplevel window that isn't disabled.
+.IP [5]
+If a menubutton has the input focus, the space and return keys
+post the menubutton.
+.PP
+If the menubutton's state is \fBdisabled\fR then none of the above
+actions occur: the menubutton is completely non-responsive.
+.PP
+The behavior of menubuttons can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+menubutton, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH message n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+message \- Create and manipulate message widgets
+.SH SYNOPSIS
+\fBmessage\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBanchor\fR \fBbackground\fR \fBtakeFocus\fR \fBtextVariable\fR
+\fBattributes\fR \fBforeground\fR \fBtext\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBaspect\fR
+Class: \fBAspect\fR
+Command-Line Switch: \fB\-aspect\fR
+.fi
+.IP
+Specifies a non-negative integer value indicating desired
+aspect ratio for the text. The aspect ratio is specified as
+100*width/height. 100 means the text should
+be as wide as it is tall, 200 means the text should
+be twice as wide as it is tall, 50 means the text should
+be twice as tall as it is wide, and so on.
+Used to choose line length for text if \fBwidth\fR option
+isn't specified. Defaults to 320.
+.LP
+.nf
+Name: \fBjustify\fR
+Class: \fBJustify\fR
+Command-Line Switch: \fB\-justify\fR
+.fi
+.IP
+Specifies how to justify lines of text.
+Must be one of \fBleft\fR, \fBcenter\fR, or \fBright\fR. Defaults
+to \fBleft\fR.
+This option works together with the \fBanchor\fR, \fBaspect\fR,
+and \fBwidth\fR options to provide a variety
+of arrangements of the text within the window.
+The \fBaspect\fR and \fBwidth\fR options determine the amount of
+screen space needed to display the text.
+The \fBanchor\fR option determines where this
+rectangular area is displayed within the widget's window, and the
+\fBjustify\fR option determines how each line is displayed within that
+rectangular region.
+For example, suppose \fBanchor\fR is \fBe\fR and \fBjustify\fR is
+\fBleft\fR, and that the message window is much larger than needed
+for the text.
+The the text will displayed so that the left edges of all the lines
+line up; the entire text block will be centered in the vertical span
+of the window.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies the length of lines in the window in screen columns.
+If this option has a value greater than zero then the \fBaspect\fR
+option is ignored and the \fBwidth\fR option determines the line
+length.
+If this option has a value equal to zero, then the \fBaspect\fR option
+determines the line length.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBmessage\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a message widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the message such as its colors, attributes,
+and text. The \fBmessage\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A message is a widget that displays a textual string. A message
+widget has three special features. First, it breaks up
+its string into lines in order to produce a given aspect ratio
+for the window. The line breaks are chosen at word boundaries
+wherever possible (if not even a single word would fit on a
+line, then the word will be split across lines). Newline characters
+in the string will force line breaks; they can be used, for example,
+to leave blank lines in the display.
+.PP
+The second feature of a message widget is justification. The text
+may be displayed left-justified (each line starts at the left side of
+the window), centered on a line-by-line basis, or right-justified
+(each line ends at the right side of the window).
+.PP
+The third feature of a message widget is that it handles control
+characters and non-printing characters specially. Tab characters
+are replaced with enough blank space to line up on the next
+8-character boundary. Newlines cause line breaks. Other control
+characters (ASCII code less than 0x20) and characters not defined
+in the font are displayed as a four-character sequence \fB\ex\fIhh\fR where
+\fIhh\fR is the two-digit hexadecimal number corresponding to
+the character.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBmessage\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for message widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBmessage\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBmessage\fR
+command.
+
+.SH "DEFAULT BINDINGS"
+.PP
+When a new message is created, it has no default event bindings:
+messages are intended for output purposes only.
+
+.SH BUGS
+.PP
+Tabs don't work very well with text that is centered or right-justified.
+The most common result is that the line is justified wrong.
+
+.SH KEYWORDS
+message, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH option n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+option \- Add/retrieve window options to/from the option database
+.SH SYNOPSIS
+\fBoption add \fIpattern value \fR?\fIpriority\fR?
+.sp
+\fBoption clear\fR
+.sp
+\fBoption get \fIwindow name class\fR
+.sp
+\fBoption readfile \fIfileName \fR?\fIpriority\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBoption\fR command allows you to add entries to the Ck option
+database or to retrieve options from the database. The \fBadd\fR
+form of the command adds a new option to the database.
+\fIPattern\fR contains
+the option being specified, and consists of names and/or classes
+separated by asterisks or dots, in the usual X format. \fIValue\fR
+contains a text string to associate with \fIpattern\fR; this is the
+value that will be returned in invocations of the \fBoption get\fR
+command. If \fIpriority\fR
+is specified, it indicates the priority level for this option (see
+below for legal values); it defaults to \fBinteractive\fR.
+This command always returns an empty string.
+.PP
+The \fBoption clear\fR command clears the option database. This command
+always returns an empty string.
+.PP
+The \fBoption get\fR command returns the value of the option
+specified for \fIwindow\fR
+under \fIname\fR and \fIclass\fR. If several entries in the option
+database match \fIwindow\fR, \fIname\fR, and \fIclass\fR, then
+the command returns whichever was created with highest
+\fIpriority\fR level. If there are several matching
+entries at the same priority level, then it returns whichever entry
+was most recently entered into the option database. If there are
+no matching entries, then the empty string is returned.
+.PP
+The \fBreadfile\fR form of the command reads \fIfileName\fR,
+which should have the standard format for an
+X resource database such as \fB.Xdefaults\fR, and adds all the
+options specified in that file to the option database. If \fIpriority\fR
+is specified, it indicates the priority level at which to enter the
+options; \fIpriority\fR defaults to \fBinteractive\fR.
+.PP
+The \fIpriority\fR arguments to the \fBoption\fR command are
+normally specified symbolically using one of the following values:
+.TP
+\fBwidgetDefault\fR
+Level 20. Used for default values hard-coded into widgets.
+.TP
+\fBstartupFile\fR
+Level 40. Used for options specified in application-specific
+startup files.
+.TP
+\fBuserDefault\fR
+Level 60. Used for options specified in user-specific defaults
+files, such as \fB.Xdefaults\fR, resource databases loaded into
+the X server, or user-specific startup files.
+.TP
+\fBinteractive\fR
+Level 80. Used for options specified interactively after the application
+starts running. If \fIpriority\fR isn't specified, it defaults to
+this level.
+.LP
+Any of the above keywords may be abbreviated. In addition, priorities
+may be specified numerically using integers between 0 and 100,
+inclusive. The numeric form is probably a bad idea except for new priority
+levels other than the ones given above.
+
+.SH KEYWORDS
+database, option, priority, retrieve
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH options n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+options \- Standard options supported by widgets
+.BE
+
+.SH DESCRIPTION
+This manual entry describes the common configuration options supported
+by widgets in the Ck toolkit. Every widget does not necessarily support
+every option (see the manual entries for individual widgets for a list
+of the standard options supported by that widget), but if a widget does
+support an option with one of the names listed below, then the option
+has exactly the effect described below.
+.PP
+In the descriptions below,
+``Name'' refers to the option's name in the option database
+``Class'' refers to the option's class value
+in the option database. ``Command-Line Switch'' refers to the
+switch used in widget-creation and \fBconfigure\fR widget commands to
+set this value. For example, if an option's command-line switch is
+\fB\-foreground\fR and there exists a widget \fB.a.b.c\fR, then the
+command
+.DS
+\&\fB.a.b.c\0\0configure\0\0\-foreground black\fR
+.DE
+may be used to specify the value \fBblack\fR for the option in the
+the widget \fB.a.b.c\fR. Command-line switches may be abbreviated,
+as long as the abbreviation is unambiguous.
+.ta 4c
+.LP
+.nf
+Name: \fBactiveAttributes\fR
+Class: \fBAttributes\fR
+Command-Line Switch: \fB\-activeattributes\fR
+.fi
+.IP
+Specifies video attributes to use when drawing active elements of
+widgets. This option must be a proper Tcl list which may contain
+the elements:
+.PP
+.ta 4c 8c
+.nf
+ \fBblink\fR \fBreverse\fR
+ \fBbold\fR \fBstandout\fR
+ \fBdim\fR \fBunderline\fR
+ \fBnormal\fR
+.fi
+.ta 4c
+.IP
+If the list is empty, the \fBnormal\fR attribute is automatically present.
+.LP
+.nf
+Name: \fBactiveBackground\fR
+Class: \fBForeground\fR
+Command-Line Switch: \fB\-activebackground\fR
+.fi
+.IP
+Specifies background color to use when drawing active elements of
+widgets. Color specifications are always symbolic; valid color names are:
+.PP
+.ta 4c 8c
+.nf
+ \fBblack\fR \fBmagenta\fR
+ \fBblue\fR \fBred\fR
+ \fBcyan\fR \fByellow\fR
+ \fBgreen\fR \fBwhite\fR
+.fi
+.ta 4c
+.PP
+.LP
+.nf
+Name: \fBactiveForeground\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-activeforeground\fR
+.fi
+.IP
+Specifies foreground color to use when drawing active elements.
+See above for possible colors.
+.LP
+.nf
+Name: \fBanchor\fR
+Class: \fBAnchor\fR
+Command-Line Switch: \fB\-anchor\fR
+.fi
+.IP
+Specifies how the text in a widget is to be displayed in the widget.
+Must be one of the values \fBn\fR, \fBne\fR, \fBe\fR, \fBse\fR,
+\fBs\fR, \fBsw\fR, \fBw\fR, \fBnw\fR, or \fBcenter\fR.
+For example, \fBnw\fR means display the text such that its
+top-left corner is at the top-left corner of the widget.
+.LP
+.nf
+Name: \fBattributes\fR
+Class: \fBAttributes\fR
+Command-Line Switch: \fB\-attributes\fR
+.fi
+.IP
+Specifies video attributes to use when displaying the widget.
+See \fBactiveAttributes\fR for possible values.
+.LP
+.nf
+Name: \fBbackground\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-background or \-bg\fR
+.fi
+.IP
+Specifies the normal background color to use when displaying the
+widget. See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBborder\fR
+Class: \fBBorder\fR
+Command-Line Switch: \fB\-border\fR
+.fi
+.IP
+Specifies the characters used for drawing a border around a widget.
+This options must be a proper Tcl list with exactly zero, one, three, six,
+or eight elements:
+.RS
+.TP 12
+0 elements
+No extra space for the border is allocated by the widget.
+.TP 12
+1 element
+All four sides of the border's rectangle plus the corners are made from
+the sole element.
+.TP 12
+3 elements
+The first element is used for the rectangle's corners, the second for
+the horizontal sides, and the third for the vertical sides.
+.TP 12
+6 elements
+The order of elements in the rectangle is: upper left corner, horizontal
+side, upper right corner, vertical side, lower right corner, lower left
+corner.
+.TP 12
+8 elements
+Each element gives corner and side, alternating, starting at the upper
+left corner of the square, clockwise.
+.RE
+.IP
+The list elements must be either a single character or a symbolic name
+of a graphical character. For valid names of graphical characters refer
+to the \fBcurses gchar\fR command.
+.LP
+.nf
+Name: \fBdisabledAttributes\fR
+Class: \fBDisabledAttributes\fR
+Command-Line Switch: \fB\-disabledattributes\fR
+.fi
+.IP
+Specifies video attributes to use when drawing a disabled element.
+See \fBactiveAttributes\fR for possible values.
+.LP
+.nf
+Name: \fBdisabledBackground\fR
+Class: \fBDisabledBackground\fR
+Command-Line Switch: \fB\-disabledbackground\fR
+.fi
+.IP
+Specifies background color to use when drawing a disabled element.
+See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBdisabledForeground\fR
+Class: \fBDisabledForeground\fR
+Command-Line Switch: \fB\-disabledforeground\fR
+.fi
+.IP
+Specifies foreground color to use when drawing a disabled element.
+See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBforeground\fR
+Class: \fBForeground\fR
+Command-Line Switch: \fB\-foreground or \-fg\fR
+.fi
+.IP
+Specifies the normal foreground color to use when displaying the widget.
+See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBjustify\fR
+Class: \fBJustify\fR
+Command-Line Switch: \fB\-justify\fR
+.fi
+.IP
+When there are multiple lines of text displayed in a widget, this
+option determines how the lines line up with each other.
+Must be one of \fBleft\fR, \fBcenter\fR, or \fBright\fR.
+\fBLeft\fR means that the lines' left edges all line up, \fBcenter\fR
+means that the lines' centers are aligned, and \fBright\fR means
+that the lines' right edges line up.
+.LP
+.nf
+Name: \fBorient\fR
+Class: \fBOrient\fR
+Command-Line Switch: \fB\-orient\fR
+.fi
+.IP
+For widgets that can lay themselves out with either a horizontal
+or vertical orientation, such as scrollbars, this option specifies
+which orientation should be used. Must be either \fBhorizontal\fR
+or \fBvertical\fR or an abbreviation of one of these.
+.LP
+.nf
+Name: \fBselectAttributes\fR
+Class: \fBSelectAttributes\fR
+Command-Line Switch: \fB\-selectattributes\fR
+.fi
+.IP
+Specifies video attributes to use when displaying selected items.
+See \fBactiveAttributes\fR for possible values.
+.LP
+.nf
+Name: \fBselectBackground\fR
+Class: \fBForeground\fR
+Command-Line Switch: \fB\-selectbackground\fR
+.fi
+.IP
+Specifies the background color to use when displaying selected
+items. See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBselectForeground\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-selectforeground\fR
+.fi
+.IP
+Specifies the foreground color to use when displaying selected
+items. See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBtakeFocus\fR
+Class: \fBTakeFocus\fR
+Command-Line Switch: \fB\-takefocus\fR
+.fi
+.IP
+Provides information used when moving the focus from window to window
+via keyboard traversal (e.g., Tab and BackTab).
+Before setting the focus to a window, the traversal scripts first
+check whether the window is viewable (it and all its ancestors are mapped);
+if not, the window is skipped.
+Next, the scripts consult the value of the \fBtakeFocus\fR option.
+A value of \fB0\fR means that this window should be skipped entirely
+during keyboard traversal.
+\fB1\fR means that the this window should always receive the input
+focus.
+An empty value means that the traversal scripts make the decision
+about whether or not to focus on the window: the current
+algorithm is to skip the window if it is
+disabled or if it has no key bindings.
+If the value has any other form, then the traversal scripts take
+the value, append the name of the window to it (with a separator space),
+and evaluate the resulting string as a Tcl script.
+The script must return 0, 1, or an empty string; this value is used
+just as if the option had that value in the first place.
+Note: this interpretation of the option is defined entirely by
+the Tcl scripts that implement traversal: the widget implementations
+ignore the option entirely, so you can change its meaning if you
+redefine the keyboard traversal scripts.
+.LP
+.nf
+Name: \fBtext\fR
+Class: \fBText\fR
+Command-Line Switch: \fB\-text\fR
+.fi
+.IP
+Specifies a string to be displayed inside the widget. The way in which
+the string is displayed depends on the particular widget and may be
+determined by other options, such as \fBanchor\fR or \fBjustify\fR.
+.LP
+.nf
+Name: \fBtextVariable\fR
+Class: \fBVariable\fR
+Command-Line Switch: \fB\-textvariable\fR
+.fi
+.IP
+Specifies the name of a variable. The value of the variable is a text
+string to be displayed inside the widget; if the variable value changes
+then the widget will automatically update itself to reflect the new value.
+The way in which the string is displayed in the widget depends on the
+particular widget and may be determined by other options, such as
+\fBanchor\fR or \fBjustify\fR.
+.LP
+.nf
+Name: \fBunderline\fR
+Class: \fBUnderline\fR
+Command-Line Switch: \fB\-underline\fR
+.fi
+.IP
+Specifies the integer index of a character to underline in the widget.
+This option is used by the default bindings to implement keyboard
+traversal for menu buttons and menu entries.
+0 corresponds to the first character of the text displayed in the
+widget, 1 to the next character, and so on.
+.LP
+.nf
+Name: \fBunderlineAttributes\fR
+Class: \fBUnderlineAttributes\fR
+Command-Line Switch: \fB\-underlineattributes\fR
+.fi
+.IP
+
+.LP
+.nf
+Name: \fBunderlineForeground\fR
+Class: \fBUnderlineForeground\fR
+Command-Line Switch: \fB\-underlineforeground\fR
+.fi
+.IP
+Specifies the foreground color to use when displaying an underlined
+character. See \fBactiveBackground\fR for possible colors.
+.LP
+.nf
+Name: \fBxScrollCommand\fR
+Class: \fBScrollCommand\fR
+Command-Line Switch: \fB\-xscrollcommand\fR
+.fi
+.IP
+Specifies the prefix for a command used to communicate with horizontal
+scrollbars.
+When the view in the widget's window changes (or
+whenever anything else occurs that could change the display in a
+scrollbar, such as a change in the total size of the widget's
+contents), the widget will
+generate a Tcl command by concatenating the scroll command and
+two numbers.
+Each of the numbers is a fraction between 0 and 1, which indicates
+a position in the document. 0 indicates the beginning of the document,
+1 indicates the end, .333 indicates a position one third the way through
+the document, and so on.
+The first fraction indicates the first information in the document
+that is visible in the window, and the second fraction indicates
+the information just after the last portion that is visible.
+The command is
+then passed to the Tcl interpreter for execution. Typically the
+\fBxScrollCommand\fR option consists of the path name of a scrollbar
+widget followed by ``set'', e.g. ``.x.scrollbar set'': this will cause
+the scrollbar to be updated whenever the view in the window changes.
+If this option is not specified, then no command will be executed.
+.LP
+.nf
+Name: \fByScrollCommand\fR
+Class: \fBScrollCommand\fR
+Command-Line Switch: \fB\-yscrollcommand\fR
+.fi
+.IP
+Specifies the prefix for a command used to communicate with vertical
+scrollbars. This option is treated in the same way as the
+\fBxScrollCommand\fR option, except that it is used for vertical
+scrollbars and is provided by widgets that support vertical scrolling.
+See the description of \fBxScrollCommand\fR for details
+on how this option is used.
+
+.SH KEYWORDS
+class, name, standard option, switch
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH pack n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+pack \- Geometry manager that packs around edges of cavity
+.SH SYNOPSIS
+\fBpack \fIoption arg \fR?\fIarg ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBpack\fR command is used to communicate with the packer,
+a geometry manager that arranges the children of a parent by
+packing them in order around the edges of the parent.
+The \fBpack\fR command can have any of several forms, depending
+on the \fIoption\fR argument:
+.TP
+\fBpack \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+If the first argument to \fBpack\fR is a window name (any value
+starting with ``.''), then the command is processed in the same
+way as \fBpack configure\fR.
+.TP
+\fBpack configure \fIslave \fR?\fIslave ...\fR? ?\fIoptions\fR?
+The arguments consist of the names of one or more slave windows
+followed by pairs of arguments that specify how
+to manage the slaves.
+See ``THE PACKER ALGORITHM'' below for details on how the options
+are used by the packer.
+The following options are supported:
+.RS
+.TP
+\fB\-after \fIother\fR
+\fIOther\fR must the name of another window.
+Use its master as the master for the slaves, and insert
+the slaves just after \fIother\fR in the packing order.
+.TP
+\fB\-anchor \fIanchor\fR
+\fIAnchor\fR must be a valid anchor position such as \fBn\fR
+or \fBsw\fR; it specifies where to position each slave in its
+parcel.
+Defaults to \fBcenter\fR.
+.TP
+\fB\-before \fIother\fR
+\fIOther\fR must the name of another window.
+Use its master as the master for the slaves, and insert
+the slaves just before \fIother\fR in the packing order.
+.TP
+\fB\-expand \fIboolean\fR
+Specifies whether the slaves should be expanded to consume
+extra space in their master.
+\fIBoolean\fR may have any proper boolean value, such as \fB1\fR
+or \fBno\fR.
+Defaults to 0.
+.TP
+\fB\-fill \fIstyle\fR
+If a slave's parcel is larger than its requested dimensions, this
+option may be used to stretch the slave.
+\fIStyle\fR must have one of the following values:
+.RS
+.TP
+\fBnone\fR
+Give the slave its requested dimensions plus any internal padding
+requested with \fB\-ipadx\fR or \fB\-ipady\fR. This is the default.
+.TP
+\fBx\fR
+Stretch the slave horizontally to fill the entire width of its
+parcel (except leave external padding as specified by \fB\-padx\fR).
+.TP
+\fBy\fR
+Stretch the slave vertically to fill the entire height of its
+parcel (except leave external padding as specified by \fB\-pady\fR).
+.TP
+\fBboth\fR
+Stretch the slave both horizontally and vertically.
+.RE
+.TP
+\fB\-in \fIother\fR
+Insert the slave(s) at the end of the packing order for the master
+window given by \fIother\fR.
+.TP
+\fB\-ipadx \fIamount\fR
+\fIAmount\fR specifies how much horizontal internal padding to
+leave on each side of the slave(s).
+\fIAmount\fR must be a valid screen distance, such as \fB2\fR or \fB.5c\fR.
+It defaults to 0.
+.TP
+\fB\-ipady \fIamount\fR
+\fIAmount\fR specifies how much vertical internal padding to
+leave on each side of the slave(s).
+\fIAmount\fR defaults to 0.
+.TP
+\fB\-padx \fIamount\fR
+\fIAmount\fR specifies how much horizontal external padding to
+leave on each side of the slave(s).
+\fIAmount\fR defaults to 0.
+.TP
+\fB\-pady \fIamount\fR
+\fIAmount\fR specifies how much vertical external padding to
+leave on each side of the slave(s).
+\fIAmount\fR defaults to 0.
+.TP
+\fB\-side \fIside\fR
+Specifies which side of the master the slave(s) will be packed against.
+Must be \fBleft\fR, \fBright\fR, \fBtop\fR, or \fBbottom\fR.
+Defaults to \fBtop\fR.
+.LP
+If no \fB\-in\fR, \fB\-after\fR or \fB\-before\fR option is specified
+then each of the slaves will be inserted at the end of the packing list
+for its parent unless it is already managed by the packer (in which
+case it will be left where it is).
+If one of these options is specified then all the slaves will be
+inserted at the specified point.
+If any of the slaves are already managed by the geometry manager
+then any unspecified options for them retain their previous values rather
+than receiving default values.
+.RE
+.TP
+\fBpack forget \fIslave \fR?\fIslave ...\fR?
+Removes each of the \fIslave\fRs from the packing order for its
+master and unmaps their windows.
+The slaves will no longer be managed by the packer.
+.TP
+\fBpack info \fIslave\fR
+Returns a list whose elements are the current configuration state of
+the slave given by \fIslave\fR in the same option-value form that
+might be specified to \fBpack configure\fR.
+The first two elements of the list are ``\fB\-in \fImaster\fR'' where
+\fImaster\fR is the slave's master.
+.TP
+\fBpack propagate \fImaster\fR ?\fIboolean\fR?
+If \fIboolean\fR has a true boolean value such as \fB1\fR or \fBon\fR
+then propagation is enabled for \fImaster\fR, which must be a window
+name (see ``GEOMETRY PROPAGATION'' below).
+If \fIboolean\fR has a false boolean value then propagation is
+disabled for \fImaster\fR.
+In either of these cases an empty string is returned.
+If \fIboolean\fR is omitted then the command returns \fB0\fR or
+\fB1\fR to indicate whether propagation is currently enabled
+for \fImaster\fR.
+Propagation is enabled by default.
+.TP
+\fBpack slaves \fImaster\fR
+Returns a list of all of the slaves in the packing order for \fImaster\fR.
+The order of the slaves in the list is the same as their order in
+the packing order.
+If \fImaster\fR has no slaves then an empty string is returned.
+
+.SH "THE PACKER ALGORITHM"
+.PP
+For each master the packer maintains an ordered list of slaves
+called the \fIpacking list\fR.
+The \fB\-in\fR, \fB\-after\fR, and \fB\-before\fR configuration
+options are used to specify the master for each slave and the slave's
+position in the packing list.
+If none of these options is given for a slave then the slave
+is added to the end of the packing list for its parent.
+.PP
+The packer arranges the slaves for a master by scanning the
+packing list in order.
+At the time it processes each slave, a rectangular area within
+the master is still unallocated.
+This area is called the \fIcavity\fR; for the first slave it
+is the entire area of the master.
+.PP
+For each slave the packer carries out the following steps:
+.IP [1]
+The packer allocates a rectangular \fIparcel\fR for the slave
+along the side of the cavity given by the slave's \fB\-side\fR option.
+If the side is top or bottom then the width of the parcel is
+the width of the cavity and its height is the requested height
+of the slave plus the \fB\-ipady\fR and \fB\-pady\fR options.
+For the left or right side the height of the parcel is
+the height of the cavity and the width is the requested width
+of the slave plus the \fB\-ipadx\fR and \fB\-padx\fR options.
+The parcel may be enlarged further because of the \fB\-expand\fR
+option (see ``EXPANSION'' below)
+.IP [2]
+The packer chooses the dimensions of the slave.
+The width will normally be the slave's requested width plus
+twice its \fB\-ipadx\fR option and the height will normally be
+the slave's requested height plus twice its \fB\-ipady\fR
+option.
+However, if the \fB\-fill\fR option is \fBx\fR or \fBboth\fR
+then the width of the slave is expanded to fill the width of the parcel,
+minus twice the \fB\-padx\fR option.
+If the \fB\-fill\fR option is \fBy\fR or \fBboth\fR
+then the height of the slave is expanded to fill the width of the parcel,
+minus twice the \fB\-pady\fR option.
+.IP [3]
+The packer positions the slave over its parcel.
+If the slave is smaller than the parcel then the \fB\-anchor\fR
+option determines where in the parcel the slave will be placed.
+If \fB\-padx\fR or \fB\-pady\fR is non-zero, then the given
+amount of external padding will always be left between the
+slave and the edges of the parcel.
+.PP
+Once a given slave has been packed, the area of its parcel
+is subtracted from the cavity, leaving a smaller rectangular
+cavity for the next slave.
+If a slave doesn't use all of its parcel, the unused space
+in the parcel will not be used by subsequent slaves.
+If the cavity should become too small to meet the needs of
+a slave then the slave will be given whatever space is
+left in the cavity.
+If the cavity shrinks to zero size, then all remaining slaves
+on the packing list will be unmapped from the screen until
+the master window becomes large enough to hold them again.
+
+.SH "EXPANSION"
+.PP
+If a master window is so large that there will be extra space
+left over after all of its slaves have been packed, then the
+extra space is distributed uniformly among all of the slaves
+for which the \fB\-expand\fR option is set.
+Extra horizontal space is distributed among the expandable
+slaves whose \fB\-side\fR is \fBleft\fR or \fBright\fR,
+and extra vertical space is distributed among the expandable
+slaves whose \fB\-side\fR is \fBtop\fR or \fBbottom\fR.
+
+.SH "GEOMETRY PROPAGATION"
+.PP
+The packer normally computes how large a master must be to
+just exactly meet the needs of its slaves, and it sets the
+requested width and height of the master to these dimensions.
+This causes geometry information to propagate up through a
+window hierarchy to a top-level window so that the entire
+sub-tree sizes itself to fit the needs of the leaf windows.
+However, the \fBpack propagate\fR command may be used to
+turn off propagation for one or more masters.
+If propagation is disabled then the packer will not set
+the requested width and height of the packer.
+This may be useful if, for example, you wish for a master
+window to have a fixed size that you specify.
+
+.SH "RESTRICTIONS ON MASTER WINDOWS"
+.PP
+The master for each slave must be the slave's parent
+This restriction is necessary to guarantee that the
+slave can be placed over any part of its master that is
+visible without danger of the slave being clipped by its parent.
+
+.SH KEYWORDS
+geometry manager, location, packer, parcel, propagation, size
--- /dev/null
+'\"
+'\" Copyright (c) 1992 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH place n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+place \- Geometry manager for fixed or rubber-sheet placement
+.SH SYNOPSIS
+\fBplace \fIwindow option value \fR?\fIoption value ...\fR?
+.sp
+\fBplace configure \fIwindow option value \fR?\fIoption value ...\fR?
+.sp
+\fBplace forget \fIwindow\fR
+.sp
+\fBplace info \fIwindow\fR
+.sp
+\fBplace slaves \fIwindow\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+The placer is a geometry manager for Ck.
+It provides simple fixed placement of windows, where you specify
+the exact size and location of one window, called the \fIslave\fR,
+within another window, called the \fImaster\fR.
+The placer also provides rubber-sheet placement, where you specify the
+size and location of the slave in terms of the dimensions of
+the master, so that the slave changes size and location
+in response to changes in the size of the master.
+Lastly, the placer allows you to mix these styles of placement so
+that, for example, the slave has a fixed width and height but is
+centered inside the master.
+.PP
+If the first argument to the \fBplace\fR command is a window path
+name or \fBconfigure\fR then the command arranges for the placer
+to manage the geometry of a slave whose path name is \fIwindow\fR.
+The remaining arguments consist of one or more \fIoption\-value\fR
+pairs that specify the way in which \fIwindow\fR's
+geometry is managed.
+If the placer is already managing \fIwindow\fR, then the
+\fIoption\-value\fR pairs modify the configuration for \fIwindow\fR.
+In this form the \fBplace\fR command returns an empty string as result.
+The following \fIoption\-value\fR pairs are supported:
+.TP
+\fB\-x \fIlocation\fR
+\fILocation\fR specifies the x-coordinate within the master window
+of the anchor point for \fIwindow\fR.
+The location is specified in screen columns and need not lie within
+the bounds of the master window.
+.TP
+\fB\-relx \fIlocation\fR
+\fILocation\fR specifies the x-coordinate within the master window
+of the anchor point for \fIwindow\fR.
+In this case the location is specified in a relative fashion
+as a floating-point number: 0.0 corresponds to the left edge
+of the master and 1.0 corresponds to the right edge of the master.
+\fILocation\fR need not be in the range 0.0\-1.0.
+If both \fB\-x\fR and \fB\-relx\fR are specified for a slave
+then their values are summed. For example, \fB\-relx 0.5 \-x \-2\fR
+positions the left edge of the slave 2 columns to the left of the
+center of its master.
+.TP
+\fB\-y \fIlocation\fR
+\fILocation\fR specifies the y-coordinate within the master window
+of the anchor point for \fIwindow\fR.
+The location is specified in screen lines and need not lie within
+the bounds of the master window.
+.TP
+\fB\-rely \fIlocation\fR
+\fILocation\fR specifies the y-coordinate within the master window
+of the anchor point for \fIwindow\fR.
+In this case the value is specified in a relative fashion
+as a floating-point number: 0.0 corresponds to the top edge
+of the master and 1.0 corresponds to the bottom edge of the master.
+\fILocation\fR need not be in the range 0.0\-1.0.
+If both \fB\-y\fR and \fB\-rely\fR are specified for a slave
+then their values are summed. For example, \fB\-rely 0.5 \-x 3\fR
+positions the top edge of the slave 3 lines below the center of its master.
+.TP
+\fB\-anchor \fIwhere\fR
+\fIWhere\fR specifies which point of \fIwindow\fR is to be positioned
+at the (x,y) location selected by the \fB\-x\fR, \fB\-y\fR,
+\fB\-relx\fR, and \fB\-rely\fR options.
+The anchor point is in terms of the outer area of \fIwindow\fR
+including its border, if any.
+Thus if \fIwhere\fR is \fBse\fR then the lower-right corner of
+\fIwindow\fR's border will appear at the given (x,y) location
+in the master.
+The anchor position defaults to \fBnw\fR.
+.TP
+\fB\-width \fIsize\fR
+\fISize\fR specifies the width for \fIwindow\fR in screen columns.
+The width will be the outer width of \fIwindow\fR including its
+border, if any.
+If \fIsize\fR is an empty string, or if no \fB\-width\fR
+or \fB\-relwidth\fR option is specified, then the width requested
+internally by the window will be used.
+.TP
+\fB\-relwidth \fIsize\fR
+\fISize\fR specifies the width for \fIwindow\fR.
+In this case the width is specified as a floating-point number
+relative to the width of the master: 0.5 means \fIwindow\fR will
+be half as wide as the master, 1.0 means \fIwindow\fR will have
+the same width as the master, and so on.
+If both \fB\-width\fR and \fB\-relwidth\fR are specified for a slave,
+their values are summed. For example, \fB\-relwidth 1.0 \-width 5\fR
+makes the slave 5 columns wider than the master.
+.TP
+\fB\-height \fIsize\fR
+\fISize\fR specifies the height for \fIwindow\fR in screen lines.
+The height will be the outer dimension of \fIwindow\fR including its
+border, if any.
+If \fIsize\fR is an empty string, or if no \fB\-height\fR or
+\fB\-relheight\fR option is specified, then the height requested
+internally by the window will be used.
+.TP
+\fB\-relheight \fIsize\fR
+\fISize\fR specifies the height for \fIwindow\fR.
+In this case the height is specified as a floating-point number
+relative to the height of the master: 0.5 means \fIwindow\fR will
+be half as high as the master, 1.0 means \fIwindow\fR will have
+the same height as the master, and so on.
+If both \fB\-height\fR and \fB\-relheight\fR are specified for a slave,
+their values are summed. For example, \fB\-relheight 1.0 \-height \-2\fR
+makes the slave 2 lines shorter than the master.
+.TP
+\fB\-bordermode \fImode\fR
+\fIMode\fR determines the degree to which borders within the
+master are used in determining the placement of the slave.
+The default and most common value is \fBinside\fR.
+In this case the placer considers the area of the master to
+be the innermost area of the master, inside any border:
+an option of \fB\-x 0\fR corresponds to an x-coordinate just
+inside the border and an option of \fB\-relwidth 1.0\fR
+means \fIwindow\fR will fill the area inside the master's
+border.
+If \fImode\fR is \fBignore\fR, borders are ignored:
+the area of the master is considered to be its official area, which
+includes any internal border.
+.PP
+If the same value is specified separately with
+two different options, such as \fB\-x\fR and \fB\-relx\fR, then
+the most recent option is used and the older one is ignored.
+.PP
+The \fBplace slaves\fR command returns a list of all the slave
+windows for which \fIwindow\fR is the master.
+If there are no slaves for \fIwindow\fR then an empty string is
+returned.
+.PP
+The \fBplace forget\fR command causes the placer to stop managing
+the geometry of \fIwindow\fR. As a side effect of this command
+\fIwindow\fR will be unmapped so that it doesn't appear on the
+screen.
+If \fIwindow\fR isn't currently managed by the placer then the
+command has no effect.
+\fBPlace forget\fR returns an empty string as result.
+.PP
+The \fBplace info\fR command returns a list giving the current
+configuration of \fIwindow\fR.
+The list consists of \fIoption\-value\fR pairs in exactly the
+same form as might be specified to the \fBplace configure\fR
+command.
+If the configuration of a window has been retrieved with
+\fBplace info\fR, that configuration can be restored later by
+first using \fBplace forget\fR to erase any existing information
+for the window and then invoking \fBplace configure\fR with
+the saved information.
+
+.SH "FINE POINTS"
+.PP
+Unlike many other geometry managers (such as the packer)
+the placer does not make any attempt to manipulate the geometry of
+the master windows or the parents of slave windows (i.e. it doesn't
+set their requested sizes).
+To control the sizes of these windows, make them windows like
+frames and canvases that provide configuration options for this purpose.
+.PP
+The \fBplace\fR command is the only way to position toplevel windows
+on the screen. In this special case, the master of a toplevel window
+is assumed to be the entire screen area and the toplevel's location and
+area is computed based on the screen's area.
+
+.SH KEYWORDS
+geometry manager, height, location, master, place, rubber sheet, slave, width,
+toplevel
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH radiobutton n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+radiobutton \- Create and manipulate radiobutton widgets
+.SH SYNOPSIS
+\fBradiobutton\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 3.8c 7.6c 11.4c
+\fBactiveAttributes\fR \fBattributes\fR \fBdisabledForeground\fR \fBtextVariable\fR
+\fBactiveBackground\fR \fBbackground\fR \fBforeground\fR \fBunderline\fR
+\fBactiveForeground\fR \fBdisabledAttributes\fR \fBtakeFocus\fR \fBunderlineAttributes\fR
+\fBanchor\fR \fBdisabledBackground\fR \fBtext\fR \fBunderlineForeground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBcommand\fR
+Class: \fBCommand\fR
+Command-Line Switch: \fB\-command\fR
+.fi
+.IP
+Specifies a Tcl command to associate with the button. This command
+is typically invoked when mouse button 1 is pressed in the button
+window. The button's global variable (\fB\-variable\fR option) will
+be updated before the command is invoked.
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies a desired height for the button in screen lines.
+If this option isn't specified, the button's desired height is 1 line.
+.LP
+.nf
+Name: \fBselectColor\fR
+Class: \fBBackground\fR
+Command-Line Switch: \fB\-selectcolor\fR
+.fi
+.IP
+Specifies a background color to use when the button is selected.
+If \fBindicatorOn\fR is true, the color applicies to the indicator.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of three states for the radiobutton: \fBnormal\fR, \fBactive\fR,
+or \fBdisabled\fR. In normal state the radiobutton is displayed using the
+\fBattributes\fR, \fBforeground\fR, and \fBbackground\fR options.
+The active state is used when the input focus is in the radiobutton.
+In active state the radiobutton is displayed using the
+\fBactiveAttributes\fR, \fBactiveForeground\fR, and
+\fBactiveBackground\fR options.
+Disabled state means that the radiobutton should be insensitive:
+the default bindings will refuse to activate the widget and will ignore mouse
+button presses.
+In this state the \fBdisabledAttributes\fR, \fBdisabledForeground\fR and
+\fBdisabledBackground\fR options determine how the radiobutton is displayed.
+.LP
+.nf
+Name: \fBvalue\fR
+Class: \fBValue\fR
+Command-Line Switch: \fB\-value\fR
+.fi
+.IP
+Specifies value to store in the button's associated variable whenever
+this button is selected.
+.LP
+.nf
+Name: \fBvariable\fR
+Class: \fBVariable\fR
+Command-Line Switch: \fB\-variable\fR
+.fi
+.IP
+Specifies name of global variable to set whenever this button is
+selected. Changes in this variable also cause the button to select
+or deselect itself. Defaults to the value \fBselectedButton\fR.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies a desired width for the button in screen columns.
+If this option isn't specified, the button's desired width is computed
+from the size of the text being displayed in it.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBradiobutton\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a radiobutton widget.
+Additional options, described above, may be specified on the command line
+or in the option database to configure aspects of the radiobutton such as
+its colors, attributes, and text. The \fBradiobutton\fR command returns its
+\fIpathName\fR argument. At the time this command is invoked,
+there must not exist a window named \fIpathName\fR, but
+\fIpathName\fR's parent must exist.
+.PP
+A radiobutton is a widget that displays a textual string
+and a circle called an \fIindicator\fR.
+One of the characters of the string may optionally be underlined
+using the \fBunderline\fR, \fBunderlineAttributes\fR, and
+\fBunderlineForeground\fR options.
+A radiobutton has all of the behavior of a simple button:
+it can display itself in either of three different ways,
+according to the \fBstate\fR option, and it invokes
+a Tcl command whenever mouse button 1 is clicked over the
+check button.
+.PP
+In addition, radiobuttons can be \fIselected\fR.
+If a radiobutton is selected, the indicator is normally
+drawn with a special color, and a Tcl variable associated with the
+radiobutton is set to a particular value.
+If the radiobutton is not selected, the indicator is drawn with no
+special color. Typically, several radiobuttons share a single variable
+and the value of the variable indicates which radiobutton is to be selected.
+When a radiobutton is selected it sets the value of the variable to
+indicate that fact; each radiobutton also monitors the value of
+the variable and automatically selects and deselects itself when the
+variable's value changes.
+By default the variable \fBselectedButton\fR
+is used; its contents give the name of the button that is
+selected, or the empty string if no button associated with that
+variable is selected.
+The name of the variable for a radiobutton,
+plus the variable to be stored into it, may be modified with options
+on the command line or in the option database.
+Configuration options may also be used to modify the way the
+indicator is displayed.
+By default a radio button is configured to select itself on button clicks.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBradiobutton\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for radiobutton widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBradiobutton\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBradiobutton\fR
+command.
+.TP
+\fIpathName \fBdeselect\fR
+Deselects the radiobutton and sets the associated variable to an
+empty string.
+If this radiobutton was not currently selected, the command has
+no effect.
+.TP
+\fIpathName \fBinvoke\fR
+Does just what would have happened if the user invoked the radiobutton
+with the mouse: selects the button and invokes
+its associated Tcl command, if there is one.
+The return value is the return value from the Tcl command, or an
+empty string if there is no command associated with the radiobutton.
+This command is ignored if the radiobutton's state is \fBdisabled\fR.
+.TP
+\fIpathName \fBselect\fR
+Selects the radiobutton and sets the associated variable to the
+value corresponding to this widget.
+
+.SH BINDINGS
+.PP
+Ck automatically creates class bindings for radiobuttons that give them
+the following default behavior:
+.IP [1]
+The radiobutton activates whenever it gets the input focus and deactivates
+whenever it loses the input focus.
+.IP [2]
+When mouse button 1 is pressed over a radiobutton it is invoked (it
+becomes selected and the command associated with the button is
+invoked, if there is one).
+.IP [3]
+When a radiobutton has the input focus, the space or return keys cause
+the radiobutton to be invoked.
+.PP
+If the radiobutton's state is \fBdisabled\fR then none of the above
+actions occur: the radiobutton is completely non-responsive.
+.PP
+The behavior of radiobuttons can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH KEYWORDS
+radiobutton, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH raise n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+raise \- Change a window's position in the stacking order
+.SH SYNOPSIS
+\fBraise \fIwindow \fR?\fIaboveThis\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+If the \fIaboveThis\fR argument is omitted then the command raises
+\fIwindow\fR so that it is above all of its siblings in the stacking
+order (it will not be obscured by any siblings and will obscure
+any siblings that overlap it).
+If \fIaboveThis\fR is specified then it must be the path name of
+a window that is either a sibling of \fIwindow\fR or the descendant
+of a sibling of \fIwindow\fR.
+In this case the \fBraise\fR command will insert
+\fIwindow\fR into the stacking order just above \fIaboveThis\fR
+(or the ancestor of \fIaboveThis\fR that is a sibling of \fIwindow\fR);
+this could end up either raising or lowering \fIwindow\fR.
+
+.SH KEYWORDS
+obscure, raise, stacking order
+
--- /dev/null
+'\"
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH recorder n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+recorder \- Simple event recorder/player
+.SH SYNOPSIS
+\fBrecorder replay \fIfileName\fR
+.br
+\fBrecorder start \fR?\fI\-withdelay\fR? \fIfileName\fR
+.br
+\fBrecorder stop\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+This command provides a simple recorder/player for certain kinds
+of events. The \fBrecorder start\fR form arranges for recording
+events to the event log file \fIfileName\fR. If the \fI\-withdelay\fR
+switch is specified, the delays between events are also recorded.
+The event log file may be replayed using the \fBrecorder replay\fR
+command form. With \fBrecorder stop\fR all recording/playing
+activity is stopped and all event log files are closed.
+.PP
+Each event takes up one line in an event log file. Event types are the
+first word in angle brackets in the line. They are followed by parameters
+for the event:
+.TP
+\fB<ButtonPress> \fIwindow button x y rootX rooty\fR
+Mouse button \fBbutton\fR (1, 2, or 3) pressed in window \fIwindow\fR at
+window coordinate \fIx\fR, \fIy\fR. Root coordinates are in \fIrootX\fR,
+\fIrootY\fR.
+.TP
+\fB<ButtonRelease> \fIwindow button x y rootX rooty\fR
+Mouse button released, analogous to \fB<ButtonPress>\fR.
+.TP
+\fB<Delay> \fImilliseconds\fR
+Delay replay for \fImilliseconds\fR.
+.TP
+\fB<Key> \fIwindow keysym\fR
+Key pressed in \fIwindow\fR. \fIKeysym\fR is the symbolic name of the
+key, e.g. ``Linefeed'', ``Return'', ``Control-A'', or a hexadecimal
+key code like 0xc3.
+Note that hexadecimal key codes greater than 0x7f are not portable
+accross different systems.
+.PP
+Lines starting with a hash are treated as comments. All other lines
+whose first word does not start with an open angle bracket are
+evaluated as normal Tcl commands. As in Tcl source files, newline-backslash
+sequences are treated as continuation lines.
+.PP
+Errors occuring during replay are reported using the background error
+mechanism. Upon error, the replay event log file is closed.
+
+.SH KEYWORDS
+event, recorder
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH scrollbar n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+scrollbar \- Create and manipulate scrollbar widgets
+.SH SYNOPSIS
+\fBscrollbar\fI pathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBactiveAttributes\fR \fBactiveForeground\fR \fBbackground\fR \fBorient\fR
+\fBactiveBackground\fR \fBattributes\fR \fBforeground\fR \fBtakeFocus\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBcommand\fR
+Class: \fBCommand\fR
+Command-Line Switch: \fB\-command\fR
+.fi
+.IP
+Specifies the prefix of a Tcl command to invoke to change the view
+in the widget associated with the scrollbar. When a user requests
+a view change by manipulating the scrollbar, a Tcl command is
+invoked. The actual command consists of this option followed by
+additional information as described later.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBscrollbar\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a scrollbar widget.
+Additional options, described above, may be specified on the command
+line or in the option database to configure aspects of the scrollbar
+such as its colors, orientation, and relief.
+The \fBscrollbar\fR command returns its \fIpathName\fR argument.
+At the time this command is invoked, there must not exist a window
+named \fIpathName\fR, but \fIpathName\fR's parent must exist.
+.PP
+A scrollbar is a widget that displays two arrows, one at each end of
+the scrollbar, and a \fIslider\fR in the middle portion of the
+scrollbar.
+It provides information about what is visible in an \fIassociated window\fR
+that displays an document of some sort (such as a file being edited).
+The position and size of the slider indicate which portion of the
+document is visible in the associated window. For example, if the
+slider in a vertical scrollbar covers the top third of the area
+between the two arrows, it means that the associated window displays
+the top third of its document.
+.PP
+Scrollbars can be used to adjust the view in the associated window
+by clicking or dragging with the mouse. See the BINDINGS section
+below for details.
+
+.SH "ELEMENTS"
+A scrollbar displays five elements, which are referred to in the
+widget commands for the scrollbar:
+.TP 10
+\fBarrow1\fR
+The top or left arrow in the scrollbar.
+.TP 10
+\fBtrough1\fR
+The region between the slider and \fBarrow1\fR.
+.TP 10
+\fBslider\fR
+The rectangle that indicates what is visible in the associated widget.
+.TP 10
+\fBtrough2\fR
+The region between the slider and \fBarrow2\fR.
+.TP 10
+\fBarrow2\fR
+The bottom or right arrow in the scrollbar.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBscrollbar\fR command creates a new Tcl command whose
+name is \fIpathName\fR. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for scrollbar widgets:
+.TP
+\fIpathName \fBactivate\fR
+Marks the scrollbar as active, which
+causes it to be displayed as specified by the
+\fBactiveAttributes\fR, \fBactiveBackground\fR and \fBactiveForeground\fR
+options.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBscrollbar\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBscrollbar\fR
+command.
+.TP
+\fIpathName \fBdeactivate\fR
+Marks the scrollbar as normal, which
+causes it to be displayed as specified by the
+\fBattributes\fR, \fBbackground\fR and \fBforeground\fR options.
+.TP
+\fIpathName \fBfraction \fIx y\fR
+Returns a real number between 0 and 1 indicating where the point
+given by \fIx\fR and \fIy\fR lies in the trough area of the scrollbar.
+The value 0 corresponds to the top or left of the trough, the
+value 1 corresponds to the bottom or right, 0.5 corresponds to
+the middle, and so on.
+\fIX\fR and \fIy\fR must be screen coordinates relative to the scrollbar
+widget.
+If \fIx\fR and \fIy\fR refer to a point outside the trough, the closest
+point in the trough is used.
+.TP
+\fIpathName \fBget\fR
+Returns the scrollbar settings in the form of a list whose
+elements are the arguments to the most recent \fBset\fR widget command.
+.TP
+\fIpathName \fBidentify\fR \fIx y\fR
+Returns the name of the element under the point given by \fIx\fR and
+\fIy\fR (such as \fBarrow1\fR), or an empty string if the point does
+not lie in any element of the scrollbar.
+\fIX\fR and \fIy\fR must be screen coordinates relative to the scrollbar
+widget.
+.TP
+\fIpathName \fBset\fR \fIfirst last\fR
+This command is invoked by the scrollbar's associated widget to
+tell the scrollbar about the current view in the widget.
+The command takes two arguments, each of which is a real fraction
+between 0 and 1.
+The fractions describe the range of the document that is visible in
+the associated widget.
+For example, if \fIfirst\fR is 0.2 and \fIlast\fR is 0.4, it means
+that the first part of the document visible in the window is 20%
+of the way through the document, and the last visible part is 40%
+of the way through.
+
+.SH "SCROLLING COMMANDS"
+.PP
+When the user interacts with the scrollbar, for example by dragging
+the slider, the scrollbar notifies the associated widget that it
+must change its view.
+The scrollbar makes the notification by evaluating a Tcl command
+generated from the scrollbar's \fB\-command\fR option.
+The command may take any of the following forms.
+In each case, \fIprefix\fR is the contents of the
+\fB\-command\fR option, which usually has a form like \fB.t yview\fR
+.TP
+\fIprefix \fBmoveto \fIfraction\fR
+\fIFraction\fR is a real number between 0 and 1.
+The widget should adjust its view so that the point given
+by \fIfraction\fR appears at the beginning of the widget.
+If \fIfraction\fR is 0 it refers to the beginning of the
+document. 1.0 refers to the end of the document, 0.333
+refers to a point one-third of the way through the document,
+and so on.
+.TP
+\fIprefix \fBscroll \fInumber \fBunit\fR
+The widget should adjust its view by \fInumber\fR units.
+The units are defined in whatever way makes sense for the widget,
+such as characters or lines in a text widget.
+\fINumber\fR is either 1, which means one unit should scroll off
+the top or left of the window, or \-1, which means that one unit
+should scroll off the bottom or right of the window.
+.TP
+\fIprefix \fBscroll \fInumber \fBpage\fR
+The widget should adjust its view by \fInumber\fR pages.
+It is up to the widget to define the meaning of a page; typically
+it is slightly less than what fits in the window, so that there
+is a slight overlap between the old and new views.
+\fINumber\fR is either 1, which means the next page should
+become visible, or \-1, which means that the previous page should
+become visible.
+
+.SH BINDINGS
+Ck automatically creates class bindings for scrollbars that give them
+the following default behavior.
+If the behavior is different for vertical and horizontal scrollbars,
+the horizontal behavior is described in parentheses.
+
+.IP [1]
+Pressing button 1 over \fBarrow1\fR causes the view in the
+associated widget to shift up (left) by one unit so that the
+document appears to move down (right) one unit.
+.IP [2]
+Pressing button 1 over \fBtrough1\fR causes the view in the
+associated widget to shift up (left) by one screenful so that the
+document appears to move down (right) one screenful.
+.IP [3]
+Pressing button 1 over \fBtrough2\fR causes the view in the
+associated widget to shift down (right) by one screenful so that the
+document appears to move up (left) one screenful.
+.IP [4]
+Pressing button 1 over \fBarrow2\fR causes the view in the
+associated widget to shift down (right) by one unit so that the
+document appears to move up (left) one unit.
+.IP [5]
+In vertical scrollbars the Up and Down keys have the same behavior
+as mouse clicks over \fBarrow1\fR and \fBarrow2\fR, respectively.
+In horizontal scrollbars these keys have no effect.
+.IP [6]
+In horizontal scrollbars the Left and Right keys have the same behavior
+as mouse clicks over \fBarrow1\fR and \fBarrow2\fR, respectively.
+In vertical scrollbars these keys have no effect.
+.IP [7]
+The Prior and Next keys have the same behavior
+as mouse clicks over \fBtrough1\fR and \fBtrough2\fR, respectively.
+.IP [8]
+The Home key adjusts the view to the top (left edge) of the document.
+.IP [9]
+The End key adjusts the view to the bottom (right edge) of the document.
+.IP [10]
+FocusIn and FocusOut events activate and deactive the scrollbars, respectively.
+
+.SH KEYWORDS
+scrollbar, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1992 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH text n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+text \- Create and manipulate text widgets
+.SH SYNOPSIS
+\fBtext\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBattributes\fR \fBselectAttributes\fR \fBselectForeground\fR \fBxScrollCommand\fR
+\fBbackground\fR \fBselectBackground\fR \fBtakeFocus\fR \fByScrollCommand\fR
+\fBforeground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies the desired height for the window, in screen lines.
+Must be at least one.
+.LP
+.nf
+Name: \fBstate\fR
+Class: \fBState\fR
+Command-Line Switch: \fB\-state\fR
+.fi
+.IP
+Specifies one of two states for the text: \fBnormal\fR or \fBdisabled\fR.
+If the text is disabled then characters may not be inserted or deleted
+and no insertion cursor will be displayed, even if the input focus is
+in the widget.
+.LP
+.nf
+Name: \fBtabs\fR
+Class: \fBTabs\fR
+Command-Line Switch: \fB\-tabs\fR
+.fi
+.IP
+Specifies a set of tab stops for the window. The option's value consists
+of a list of screen distances giving the positions of the tab stops. Each
+position may optionally be followed in the next list element
+by one of the keywords \fBleft\fR, \fBright\fR, \fBcenter\fR,
+or \fBnumeric\fR, which specifies how to justify
+text relative to the tab stop. \fBLeft\fR is the default; it causes
+the text following the tab character to be positioned with its left edge
+at the tab position. \fBRight\fR means that the right edge of the text
+following the tab character is positioned at the tab position, and
+\fBcenter\fR means that the text is centered at the tab position.
+\fBNumeric\fR means that the decimal point in the text is positioned
+at the tab position; if there is no decimal point then the least
+significant digit of the number is positioned just to the left of the
+tab position; if there is no number in the text then the text is
+right-justified at the tab position.
+For example, \fB\-tabs {2 left 4 6 center}\fR creates three
+tab stops at two-column intervals; the first two use left
+justification and the third uses center justification.
+If the list of tab stops does not have enough elements to cover all
+of the tabs in a text line, then Ck extrapolates new tab stops using
+the spacing and alignment from the last tab stop in the list.
+The value of the \fBtabs\fR option may be overridden by \fB\-tabs\fR
+options in tags.
+If no \fB\-tabs\fR option is specified, or if it is specified as
+an empty list, then Ck uses default tabs spaced every eight columns.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies the desired width for the window in screen columns.
+.LP
+.nf
+Name: \fBwrap\fR
+Class: \fBWrap\fR
+Command-Line Switch: \fB\-wrap\fR
+.fi
+.IP
+Specifies how to handle lines in the text that are too long to be
+displayed in a single line of the text's window.
+The value must be \fBnone\fR or \fBchar\fR or \fBword\fR.
+A wrap mode of \fBnone\fR means that each line of text appears as
+exactly one line on the screen; extra characters that don't fit
+on the screen are not displayed.
+In the other modes each line of text will be broken up into several
+screen lines if necessary to keep all the characters visible.
+In \fBchar\fR mode a screen line break may occur after any character;
+in \fBword\fR mode a line break will only be made at word boundaries.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBtext\fR command creates a new window (given by the
+\fIpathName\fR argument) and makes it into a text widget.
+Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the text such as its colors and attributes.
+The \fBtext\fR command returns the path name of the new window.
+.PP
+A text widget displays one or more lines of text and allows that
+text to be edited.
+Text widgets support two different kinds of annotations on the
+text, called tags and marks.
+Tags allow different portions of the text
+to be displayed with different attributes and colors.
+.\" In addition, Tcl commands can be associated with tags so
+.\" that scripts are invoked when particular actions such as keystrokes
+.\" and mouse button presses occur in particular ranges of the text.
+See TAGS below for more details.
+.PP
+The second form of annotation consists of marks, which are floating
+markers in the text.
+Marks are used to keep track of various interesting positions in the
+text as it is edited.
+See MARKS below for more details.
+
+.SH INDICES
+.PP
+Many of the widget commands for texts take one or more indices
+as arguments.
+An index is a string used to indicate a particular place within
+a text, such as a place to insert characters or one endpoint of a
+range of characters to delete.
+Indices have the syntax
+.IP
+\fIbase modifier modifier modifier ...\fR
+.LP
+Where \fIbase\fR gives a starting point and the \fImodifier\fRs
+adjust the index from the starting point (e.g. move forward or
+backward one character). Every index must contain a \fIbase\fR,
+but the \fImodifier\fRs are optional.
+.LP
+The \fIbase\fR for an index must have one of the following forms:
+.TP 12
+\fIline\fB.\fIchar\fR
+Indicates \fIchar\fR'th character on line \fIline\fR.
+Lines are numbered from 1 for consistency with other UNIX programs
+that use this numbering scheme.
+Within a line, characters are numbered from 0.
+.TP 12
+\fB@\fIx\fB,\fIy\fR
+Indicates the character that covers the place whose x and y coordinates
+within the text's window are \fIx\fR and \fIy\fR.
+.TP 12
+\fBend\fR
+Indicates the end of the text (the character just after the last
+newline).
+.TP 12
+\fImark\fR
+Indicates the character just after the mark whose name is \fImark\fR.
+.TP 12
+\fItag\fB.first\fR
+Indicates the first character in the text that has been tagged with
+\fItag\fR.
+This form generates an error if no characters are currently tagged
+with \fItag\fR.
+.TP 12
+\fItag\fB.last\fR
+Indicates the character just after the last one in the text that has
+been tagged with \fItag\fR.
+This form generates an error if no characters are currently tagged
+with \fItag\fR.
+.LP
+If modifiers follow the base index, each one of them must have one
+of the forms listed below. Keywords such as \fBchars\fR and \fBwordend\fR
+may be abbreviated as long as the abbreviation is unambiguous.
+.TP
+\fB+ \fIcount\fB chars\fR
+Adjust the index forward by \fIcount\fR characters, moving to later
+lines in the text if necessary. If there are fewer than \fIcount\fR
+characters in the text after the current index, then set the index
+to the last character in the text.
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\fB\- \fIcount\fB chars\fR
+Adjust the index backward by \fIcount\fR characters, moving to earlier
+lines in the text if necessary. If there are fewer than \fIcount\fR
+characters in the text before the current index, then set the index
+to the first character in the text.
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\fB+ \fIcount\fB lines\fR
+Adjust the index forward by \fIcount\fR lines, retaining the same
+character position within the line. If there are fewer than \fIcount\fR
+lines after the line containing the current index, then set the index
+to refer to the same character position on the last line of the text.
+Then, if the line is not long enough to contain a character at the indicated
+character position, adjust the character position to refer to the last
+character of the line (the newline).
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\fB\- \fIcount\fB lines\fR
+Adjust the index backward by \fIcount\fR lines, retaining the same
+character position within the line. If there are fewer than \fIcount\fR
+lines before the line containing the current index, then set the index
+to refer to the same character position on the first line of the text.
+Then, if the line is not long enough to contain a character at the indicated
+character position, adjust the character position to refer to the last
+character of the line (the newline).
+Spaces on either side of \fIcount\fR are optional.
+.TP
+\fBlinestart\fR
+Adjust the index to refer to the first character on the line.
+.TP
+\fBlineend\fR
+Adjust the index to refer to the last character on the line (the newline).
+.TP
+\fBwordstart\fR
+Adjust the index to refer to the first character of the word containing
+the current index. A word consists of any number of adjacent characters
+that are letters, digits, or underscores, or a single character that
+is not one of these.
+.TP
+\fBwordend\fR
+Adjust the index to refer to the character just after the last one of the
+word containing the current index. If the current index refers to the last
+character of the text then it is not modified.
+.LP
+If more than one modifier is present then they are applied in
+left-to-right order. For example, the index ``\fBend \- 1 chars\fR''
+refers to the next-to-last character in the text and
+``\fBinsert wordstart \- 1 c\fR'' refers to the character just before
+the first one in the word containing the insertion cursor.
+
+.SH TAGS
+.PP
+The first form of annotation in text widgets is a tag.
+A tag is a textual string that is associated with some of the characters
+in a text.
+Tags may contain arbitrary characters, but it is probably best to
+avoid using the the characters `` '' (space), \fB+\fR, or \fB\-\fR:
+these characters have special meaning in indices, so tags containing
+them can't be used as indices.
+There may be any number of tags associated with characters in a
+text.
+Each tag may refer to a single character, a range of characters, or
+several ranges of characters.
+An individual character may have any number of tags associated with it.
+.PP
+A priority order is defined among tags, and this order is used in
+implementing some of the tag-related functions described below.
+When a tag is defined (by associating it with characters or setting
+its display options
+.\" or binding commands
+to it), it is given
+a priority higher than any existing tag.
+The priority order of tags may be redefined using the
+``\fIpathName \fBtag raise\fR'' and ``\fIpathName \fBtag lower\fR''
+widget commands.
+.PP
+.\" Tags serve three purposes in text widgets.
+Tags serve two purposes in text widgets.
+First, they control the way information is displayed on the screen.
+By default, characters are displayed as determined by the
+\fBbackground\fR, \fBattributes\fR, and \fBforeground\fR options for the
+text widget.
+However, display options may be associated with individual tags
+using the ``\fIpathName \fBtag configure\fR'' widget command.
+If a character has been tagged, then the display options associated
+with the tag override the default display style.
+The following options are currently supported for tags:
+.TP
+\fB\-attributes \fIattrList\fR
+\fIAttrList\fR specifies the attributes to use for characters associated
+with the tag.
+.TP
+\fB\-background \fIcolor\fR
+\fIColor\fR specifies the background color to use for characters
+associated with the tag.
+.TP
+\fB\-foreground \fIcolor\fR
+\fIColor\fR specifies the color to use when drawing text and other
+foreground information such as underlines.
+It may have any of the forms accepted by \fBTk_GetColor\fR.
+.TP
+\fB\-justify \fIjustify\fR
+If the first character of a display line has a tag for which this
+option has been specified, then \fIjustify\fR determines how to
+justify the line.
+It must be one of \fBleft\fR, \fBright\fR, or \fBcenter\fR.
+If a line wraps, then the justification for each line on the
+display is determined by the first character of that display line.
+.TP
+\fB\-lmargin1 \fIcolumns\fR
+If the first character of a text line has a tag for which this
+option has been specified, then \fIcolumns\fR specifies how
+much the line should be indented from the left edge of the
+window.
+If a line of text wraps, this option only applies to the
+first line on the display; the \fB\-lmargin2\fR option controls
+the indentation for subsequent lines.
+.TP
+\fB\-lmargin2 \fIcolumns\fR
+If the first character of a display line has a tag for which this
+option has been specified, and if the display line is not the
+first for its text line (i.e., the text line has wrapped), then
+\fIcolumns\fR specifies how much the line should be indented from
+the left edge of the window.
+This option is only used when wrapping is enabled, and it only
+applies to the second and later display lines for a text line.
+.TP
+\fB\-rmargin \fIcolumns\fR
+If the first character of a display line has a tag for which this
+option has been specified, then \fIcolumns\fR specifies how wide
+a margin to leave between the end of the line and the right
+edge of the window.
+This option is only used when wrapping is enabled.
+If a text line wraps, the right margin for each line on the
+display is determined by the first character of that display
+line.
+.TP
+\fB\-tabs \fItabList\fR
+\fITabList\fR specifies a set of tab stops in the same form
+as for the \fB\-tabs\fR option for the text widget. This
+option only applies to a display line if it applies to the
+first character on that display line.
+If this option is specified as an empty string, it cancels
+the option, leaving it unspecified for the tag (the default).
+If the option is specified as a non-empty string that is
+an empty list, such as \fB\-tags\0{\0}\fR, then it requests
+default 8-character tabs as described for the \fBtags\fR
+widget option.
+.TP
+\fB\-wrap \fImode\fR
+\fIMode\fR specifies how to handle lines that are wider than the
+text's window.
+It has the same legal values as the \fB\-wrap\fR option
+for the text widget: \fBnone\fR, \fBchar\fR, or \fBword\fR.
+If this tag option is specified, it overrides the \fB\-wrap\fR option
+for the text widget.
+.PP
+If a character has several tags associated with it, and if their
+display options conflict, then the options of the highest priority
+tag are used.
+If a particular display option hasn't been specified for a
+particular tag, or if it is specified as an empty string, then
+that option will never be used; the next-highest-priority
+tag's option will used instead.
+If no tag specifies a particular display option, then the default
+style for the widget will be used.
+.\" .PP
+.\" The second purpose for tags is event bindings.
+.\" You can associate bindings with a tag in much the same way you can
+.\" associate bindings with a widget class: whenever particular X
+.\" events occur on characters with the given tag, a given
+.\" Tcl command will be executed.
+.\" Tag bindings can be used to give behaviors to ranges of characters;
+.\" among other things, this allows hypertext-like
+.\" features to be implemented.
+.\" For details, see the description of the \fBtag bind\fR widget
+.\" command below.
+.PP
+.\" The third use for tags is in managing the selection.
+The second use for tags is in managing the selection.
+See THE SELECTION below.
+
+.SH MARKS
+.PP
+The second form of annotation in text widgets is a mark.
+Marks are used for remembering particular places in a text.
+They are something like tags, in that they have names and
+they refer to places in the file, but a mark isn't associated
+with particular characters.
+Instead, a mark is associated with the gap between two characters.
+Only a single position may be associated with a mark at any given
+time.
+If the characters around a mark are deleted the mark will still
+remain; it will just have new neighbor characters.
+In contrast, if the characters containing a tag are deleted then
+the tag will no longer have an association with characters in
+the file.
+Marks may be manipulated with the ``\fIpathName \fBmark\fR'' widget
+command, and their current locations may be determined by using the
+mark name as an index in widget commands.
+.PP
+Each mark also has a \fIgravity\fR, which is either \fBleft\fR or
+\fBright\fR.
+The gravity for a mark specifies what happens to the mark when
+text is inserted at the point of the mark.
+If a mark has left gravity, then the mark is treated as if it
+were attached to the character on its left, so the mark will
+remain to the left of any text inserted at the mark position.
+If the mark has right gravity, new text inserted at the mark
+position will appear to the right of the mark. The gravity
+for a mark defaults to \fBright\fR.
+.PP
+The name space for marks is different from that for tags: the
+same name may be used for both a mark and a tag, but they will refer
+to different things.
+.PP
+Two marks have special significance.
+First, the mark \fBinsert\fR is associated with the insertion cursor,
+as described under THE INSERTION CURSOR below.
+Second, the mark \fBcurrent\fR is associated with the character
+closest to the mouse and is adjusted automatically to track the
+mouse position and any changes to the text in the widget (one
+exception: \fBcurrent\fR is not updated in response to mouse
+motions if a mouse button is down; the update will be deferred
+until all mouse buttons have been released).
+Neither of these special marks may be deleted.
+
+.SH THE SELECTION
+.PP
+Selection support is implemented via tags.
+The \fBsel\fR tag is automatically defined when a text widget is
+created, and it may not be deleted with the ``\fIpathName \fBtag delete\fR''
+widget command. Furthermore, the \fBselectBackground\fR,
+\fBselectAttributes\fR, and \fBselectForeground\fR options for
+the text widget are tied to the \fB\-background\fR,
+\fB\-attributes\fR, and \fB\-foreground\fR options for the \fBsel\fR
+tag: changes in either will automatically be reflected in the
+other.
+
+.SH THE INSERTION CURSOR
+.PP
+The mark named \fBinsert\fR has special significance in text widgets.
+It is defined automatically when a text widget is created and it
+may not be unset with the ``\fIpathName \fBmark unset\fR'' widget
+command.
+The \fBinsert\fR mark represents the position of the insertion
+cursor, and the insertion cursor will automatically be moved to
+this point whenever the text widget has the input focus.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBtext\fR command creates a new Tcl command whose
+name is the same as the path name of the text's window. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIPathName\fR is the name of the command, which is the same as
+the text widget's path name. \fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for text widgets:
+.TP
+\fIpathName \fBbbox \fIindex\fR
+Returns a list of four elements describing the screen area
+of the character given by \fIindex\fR.
+The first two elements of the list give the x and y coordinates
+of the upper-left corner of the area occupied by the
+character, and the last two elements give the width and height
+of the area.
+If the character is not visible on the screen then the return
+value is an empty list.
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBtext\fR
+command.
+.TP
+\fIpathName \fBcompare\fR \fIindex1 op index2\fR
+Compares the indices given by \fIindex1\fR and \fIindex2\fR according
+to the relational operator given by \fIop\fR, and returns 1 if
+the relationship is satisfied and 0 if it isn't.
+\fIOp\fR must be one of the operators <, <=, ==, >=, >, or !=.
+If \fIop\fR is == then 1 is returned if the two indices refer to
+the same character, if \fIop\fR is < then 1 is returned if \fIindex1\fR
+refers to an earlier character in the text than \fIindex2\fR, and
+so on.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? \fI?value option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBtext\fR
+command.
+.TP
+\fIpathName \fBdebug \fR?\fIboolean\fR?
+If \fIboolean\fR is specified, then it must have one of the true or
+false values accepted by Tcl_GetBoolean.
+If the value is a true one then internal consistency checks will be
+turned on in the B-tree code associated with text widgets.
+If \fIboolean\fR has a false value then the debugging checks will
+be turned off.
+In either case the command returns an empty string.
+If \fIboolean\fR is not specified then the command returns \fBon\fR
+or \fBoff\fR to indicate whether or not debugging is turned on.
+There is a single debugging switch shared by all text widgets: turning
+debugging on or off in any widget turns it on or off for all widgets.
+For widgets with large amounts of text, the consistency checks may
+cause a noticeable slow-down.
+.TP
+\fIpathName \fBdelete \fIindex1 \fR?\fIindex2\fR?
+Delete a range of characters from the text.
+If both \fIindex1\fR and \fIindex2\fR are specified, then delete
+all the characters starting with the one given by \fIindex1\fR
+and stopping just before \fIindex2\fR (i.e. the character at
+\fIindex2\fR is not deleted).
+If \fIindex2\fR doesn't specify a position later in the text
+than \fIindex1\fR then no characters are deleted.
+If \fIindex2\fR isn't specified then the single character at
+\fIindex1\fR is deleted.
+It is not allowable to delete characters in a way that would leave
+the text without a newline as the last character.
+The command returns an empty string.
+.TP
+\fIpathName \fBdlineinfo \fIindex\fR
+Returns a list with five elements describing the area occupied
+by the display line containing \fIindex\fR.
+The first two elements of the list give the x and y coordinates
+of the upper-left corner of the area occupied by the
+line, the third and fourth elements give the width and height
+of the area, and the fifth element gives the position of the baseline
+for the line (always zero).
+All of this information is measured in screen coordinates.
+If the current wrap mode is \fBnone\fR and the line extends beyond
+the boundaries of the window,
+the area returned reflects the entire area of the line, including the
+portions that are out of the window.
+If the line is shorter than the full width of the window then the
+area returned reflects just the portion of the line that is occupied
+by characters.
+If the display line containing \fIindex\fR is not visible on
+the screen then the return value is an empty list.
+.TP
+\fIpathName \fBget \fIindex1 \fR?\fIindex2\fR?
+Return a range of characters from the text.
+The return value will be all the characters in the text starting
+with the one whose index is \fIindex1\fR and ending just before
+the one whose index is \fIindex2\fR (the character at \fIindex2\fR
+will not be returned).
+If \fIindex2\fR is omitted then the single character at \fIindex1\fR
+is returned.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then an empty string is returned.
+.TP
+\fIpathName \fBindex \fIindex\fR
+Returns the position corresponding to \fIindex\fR in the form
+\fIline.char\fR where \fIline\fR is the line number and \fIchar\fR
+is the character number.
+\fIIndex\fR may have any of the forms described under INDICES above.
+.TP
+\fIpathName \fBinsert \fIindex chars \fR?\fItagList chars tagList ...\fR?
+Inserts all of the \fIchars\fR arguments just before the character at
+\fIindex\fR.
+If \fIindex\fR refers to the end of the text (the character after
+the last newline) then the new text is inserted just before the
+last newline instead.
+If there is a single \fIchars\fR argument and no \fItagList\fR, then
+the new text will receive any tags that are present on both the
+character before and the character after the insertion point; if a tag
+is present on only one of these characters then it will not be
+applied to the new text.
+If \fItagList\fR is specified then it consists of a list of
+tag names; the new characters will receive all of the tags in
+this list and no others, regardless of the tags present around
+the insertion point.
+If multiple \fIchars\fR\-\fItagList\fR argument pairs are present,
+they produce the same effect as if a separate \fBinsert\fR widget
+command had been issued for each pair, in order.
+The last \fItagList\fR argument may be omitted.
+.TP
+\fIpathName \fBmark \fIoption \fR?\fIarg arg ...\fR?
+This command is used to manipulate marks. The exact behavior of
+the command depends on the \fIoption\fR argument that follows
+the \fBmark\fR argument. The following forms of the command
+are currently supported:
+.RS
+.TP
+\fIpathName \fBmark gravity \fImarkName\fR ?\fIdirection\fR?
+If \fIdirection\fR is not specified, returns \fBleft\fR or \fBright\fR
+to indicate which of its adjacent characters \fImarkName\fR is attached
+to.
+If \fIdirection\fR is specified, it must be \fBleft\fR or \fBright\fR;
+the gravity of \fImarkName\fR is set to the given value.
+.TP
+\fIpathName \fBmark names\fR
+Returns a list whose elements are the names of all the marks that
+are currently set.
+.TP
+\fIpathName \fBmark set \fImarkName index\fR
+Sets the mark named \fImarkName\fR to a position just before the
+character at \fIindex\fR.
+If \fImarkName\fR already exists, it is moved from its old position;
+if it doesn't exist, a new mark is created.
+This command returns an empty string.
+.TP
+\fIpathName \fBmark unset \fImarkName \fR?\fImarkName markName ...\fR?
+Remove the mark corresponding to each of the \fImarkName\fR arguments.
+The removed marks will not be usable in indices and will not be
+returned by future calls to ``\fIpathName \fBmark names\fR''.
+This command returns an empty string.
+.RE
+.TP
+\fIpathName \fBsearch \fR?\fIswitches\fR? \fIpattern index \fR?\fIstopIndex\fR?
+Searches the text in \fIpathName\fR starting at \fIindex\fR for a range
+of characters that matches \fIpattern\fR.
+If a match is found, the index of the first character in the match is
+returned as result; otherwise an empty string is returned.
+One or more of the following switches (or abbreviations thereof)
+may be specified to control the search:
+.RS
+.TP
+\fB\-forwards\fR
+The search will proceed forward through the text, finding the first
+matching range starting at a position later than \fIindex\fR.
+This is the default.
+.TP
+\fB\-backwards\fR
+The search will proceed backward through the text, finding the
+matching range closest to \fIindex\fR whose first character
+is before \fIindex\fR.
+.TP
+\fB\-exact\fR
+Use exact matching: the characters in the matching range must be
+identical to those in \fIpattern\fR.
+This is the default.
+.TP
+\fB\-regexp\fR
+Treat \fIpattern\fR as a regular expression and match it against
+the text using the rules for regular expressions (see the \fBregexp\fR
+command for details).
+.TP
+\fB\-nocase\fR
+Ignore case differences between the pattern and the text.
+.TP
+\fB\-count\fI varName\fR
+The argument following \fB\-count\fR gives the name of a variable;
+if a match is found, the number of characters in the matching
+range will be stored in the variable.
+.TP
+\fB\-\-\fR
+This switch has no effect except to terminate the list of switches:
+the next argument will be treated as \fIpattern\fR even if it starts
+with \fB\-\fR.
+.LP
+The matching range must be entirely within a single line of text.
+For regular expression matching the newlines are removed from the ends
+of the lines before matching: use the \fB$\fR feature in regular
+expressions to match the end of a line.
+For exact matching the newlines are retained.
+If \fIstopIndex\fR is specified, the search stops at that index:
+for forward searches, no match at or after \fIstopIndex\fR will
+be considered; for backward searches, no match earlier in the
+text than \fIstopIndex\fR will be considered.
+If \fIstopIndex\fR is omitted, the entire text will be searched:
+when the beginning or end of the text is reached, the search
+continues at the other end until the starting location is reached
+again; if \fIstopIndex\fR is specified, no wrap-around will occur.
+.RE
+.TP
+\fIpathName \fBsee \fIindex\fR
+Adjusts the view in the window so that the character given by \fIindex\fR
+is visible.
+If \fIindex\fR is already visible then the command does nothing.
+If \fIindex\fR is a short distance out of view, the command
+adjusts the view just enough to make \fIindex\fR visible at the
+edge of the window.
+If \fIindex\fR is far out of view, then the command centers
+\fIindex\fR in the window.
+.TP
+\fIpathName \fBtag \fIoption \fR?\fIarg arg ...\fR?
+This command is used to manipulate tags. The exact behavior of the
+command depends on the \fIoption\fR argument that follows the
+\fBtag\fR argument. The following forms of the command are currently
+supported:
+.RS
+.TP
+\fIpathName \fBtag add \fItagName index1 \fR?\fIindex2 index1 index2 ...\fR?
+Associate the tag \fItagName\fR with all of the characters starting
+with \fIindex1\fR and ending just before
+\fIindex2\fR (the character at \fIindex2\fR isn't tagged).
+A single command may contain any number of \fIindex1\fR\-\fIindex2\fR
+pairs.
+If the last \fIindex2\fR is omitted then the single character at
+\fIindex1\fR is tagged.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then the command has no effect.
+.\" .TP
+.\" \fIpathName \fBtag bind \fItagName\fR ?\fIsequence\fR? ?\fIscript\fR?
+.\" This command associates \fIscript\fR with the tag given by
+.\" \fItagName\fR.
+.\" Whenever the event sequence given by \fIsequence\fR occurs for a
+.\" character that has been tagged with \fItagName\fR,
+.\" the script will be invoked.
+.\" This widget command is similar to the \fBbind\fR command except that
+.\" it operates on characters in a text rather than entire widgets.
+.\" See the \fBbind\fR manual entry for complete details
+.\" on the syntax of \fIsequence\fR and the substitutions performed
+.\" on \fIscript\fR before invoking it.
+.\" If all arguments are specified then a new binding is created, replacing
+.\" any existing binding for the same \fIsequence\fR and \fItagName\fR
+.\" (if the first character of \fIscript\fR is ``+'' then \fIscript\fR
+.\" augments an existing binding rather than replacing it).
+.\" In this case the return value is an empty string.
+.\" If \fIscript\fR is omitted then the command returns the \fIscript\fR
+.\" associated with \fItagName\fR and \fIsequence\fR (an error occurs
+.\" if there is no such binding).
+.\" If both \fIscript\fR and \fIsequence\fR are omitted then the command
+.\" returns a list of all the sequences for which bindings have been
+.\" defined for \fItagName\fR.
+.\" .RS
+.\" .LP
+.\" The only events for which bindings may be specified are those related
+.\" to the mouse and keyboard, such as \fBEnter\fR, \fBLeave\fR,
+.\" \fBButtonPress\fR, \fBMotion\fR, and \fBKeyPress\fR.
+.\" Event bindings for a text widget use the \fBcurrent\fR mark
+.\" described under MARKS above.
+.\" An \fBEnter\fR event triggers for a tag when the tag first
+.\" becomes present on the current character, and a \fBLeave\fR
+.\" event triggers for a tag when it ceases to be present on
+.\" the current character.
+.\" \fBEnter\fR and \fBLeave\fR events can happen either because the
+.\" \fBcurrent\fR mark moved or because the character at that
+.\" position changed.
+.\" Note that these events are different than \fBEnter\fR and \fBLeave\fR
+.\" events for windows.
+.\" Mouse and keyboard events are directed to the current character.
+.\" .LP
+.\" It is possible for the current character to have multiple tags,
+.\" and for each of them to have a binding for a particular event
+.\" sequence.
+.\" When this occurs, one binding is invoked for each tag, in order
+.\" from lowest-priority to highest priority.
+.\" If there are multiple matching bindings for a single tag, then
+.\" the most specific binding is chosen (see the manual entry for
+.\" the \fBbind\fR command for details).
+.\" \fBcontinue\fR and \fBbreak\fR commands within binding scripts
+.\" are processed in the same way as for bindings created with
+.\" the \fBbind\fR command.
+.\" .LP
+.\" If bindings are created for the widget as a whole using the
+.\" \fBbind\fR command, then those bindings will supplement the
+.\" tag bindings.
+.\" The tag bindings will be invoked first, followed by bindings
+.\" for the window as a whole.
+.\" .RE
+.TP
+\fIpathName \fBtag cget\fR \fItagName option\fR
+This command returns the current value of the option named \fIoption\fR
+associated with the tag given by \fItagName\fR.
+\fIOption\fR may have any of the values accepted by the \fBtag configure\fR
+widget command.
+.TP
+\fIpathName \fBtag configure \fItagName\fR ?\fIoption\fR? ?\fIvalue\fR? ?\fIoption value ...\fR?
+This command is similar to the \fBconfigure\fR widget command except
+that it modifies options associated with the tag given by \fItagName\fR
+instead of modifying options for the overall text widget.
+If no \fIoption\fR is specified, the command returns a list describing
+all of the available options for \fItagName\fR.
+If \fIoption\fR is specified with no \fIvalue\fR, then the command returns
+a list describing the one named option (this list will be identical to
+the corresponding sublist of the value returned if no \fIoption\fR
+is specified).
+If one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given option(s) to have the given value(s) in \fItagName\fR;
+in this case the command returns an empty string.
+See TAGS above for details on the options available for tags.
+.TP
+\fIpathName \fBtag delete \fItagName \fR?\fItagName ...\fR?
+Deletes all tag information for each of the \fItagName\fR
+arguments.
+The command removes the tags from all characters in the file
+and also deletes any other information associated with the tags,
+such as bindings and display information.
+The command returns an empty string.
+.TP
+\fIpathName\fB tag lower \fItagName \fR?\fIbelowThis\fR?
+Changes the priority of tag \fItagName\fR so that it is just lower
+in priority than the tag whose name is \fIbelowThis\fR.
+If \fIbelowThis\fR is omitted, then \fItagName\fR's priority
+is changed to make it lowest priority of all tags.
+.TP
+\fIpathName \fBtag names \fR?\fIindex\fR?
+Returns a list whose elements are the names of all the tags that
+are active at the character position given by \fIindex\fR.
+If \fIindex\fR is omitted, then the return value will describe
+all of the tags that exist for the text (this includes all tags
+that have been named in a ``\fIpathName \fBtag\fR'' widget
+command but haven't been deleted by a ``\fIpathName \fBtag delete\fR''
+widget command, even if no characters are currently marked with
+the tag).
+The list will be sorted in order from lowest priority to highest
+priority.
+.TP
+\fIpathName \fBtag nextrange \fItagName index1 \fR?\fIindex2\fR?
+This command searches the text for a range of characters tagged
+with \fItagName\fR where the first character of the range is
+no earlier than the character at \fIindex1\fR and no later than
+the character just before \fIindex2\fR (a range starting at
+\fIindex2\fR will not be considered).
+If several matching ranges exist, the first one is chosen.
+The command's return value is a list containing
+two elements, which are the index of the first character of the
+range and the index of the character just after the last one in
+the range.
+If no matching range is found then the return value is an
+empty string.
+If \fIindex2\fR is not given then it defaults to the end of the text.
+.TP
+\fIpathName\fB tag raise \fItagName \fR?\fIaboveThis\fR?
+Changes the priority of tag \fItagName\fR so that it is just higher
+in priority than the tag whose name is \fIaboveThis\fR.
+If \fIaboveThis\fR is omitted, then \fItagName\fR's priority
+is changed to make it highest priority of all tags.
+.TP
+\fIpathName \fBtag ranges \fItagName\fR
+Returns a list describing all of the ranges of text that have been
+tagged with \fItagName\fR.
+The first two elements of the list describe the first tagged range
+in the text, the next two elements describe the second range, and
+so on.
+The first element of each pair contains the index of the first
+character of the range, and the second element of the pair contains
+the index of the character just after the last one in the
+range.
+If there are no characters tagged with \fItag\fR then an
+empty string is returned.
+.TP
+\fIpathName \fBtag remove \fItagName index1 \fR?\fIindex2 index1 index2 ...\fR?
+Remove the tag \fItagName\fR from all of the characters starting
+at \fIindex1\fR and ending just before
+\fIindex2\fR (the character at \fIindex2\fR isn't affected).
+A single command may contain any number of \fIindex1\fR\-\fIindex2\fR
+pairs.
+If the last \fIindex2\fR is omitted then the single character at
+\fIindex1\fR is tagged.
+If there are no characters in the specified range (e.g. \fIindex1\fR
+is past the end of the file or \fIindex2\fR is less than or equal
+to \fIindex1\fR) then the command has no effect.
+This command returns an empty string.
+.RE
+.TP
+\fIpathName \fBxview \fIoption args\fR
+This command is used to query and change the horizontal position of the
+text in the widget's window. It can take any of the following
+forms:
+.RS
+.TP
+\fIpathName \fBxview\fR
+Returns a list containing two elements.
+Each element is a real fraction between 0 and 1; together they describe
+the portion of the document's horizontal span that is visible in
+the window.
+For example, if the first element is .2 and the second element is .6,
+20% of the text is off-screen to the left, the middle 40% is visible
+in the window, and 40% of the text is off-screen to the right.
+The fractions refer only to the lines that are actually visible in the
+window: if the lines in the window are all very short, so that they
+are entirely visible, the returned fractions will be 0 and 1,
+even if there are other lines in the text that are
+much wider than the window.
+These are the same values passed to scrollbars via the \fB\-xscrollcommand\fR
+option.
+.TP
+\fIpathName \fBxview moveto\fI fraction\fR
+Adjusts the view in the window so that \fIfraction\fR of the horizontal
+span of the text is off-screen to the left.
+\fIFraction\fR is a fraction between 0 and 1.
+.TP
+\fIpathName \fBxview scroll \fInumber what\fR
+This command shifts the view in the window left or right according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR or an abbreviation
+of one of these.
+If \fIwhat\fR is \fBunits\fR, the view adjusts left or right by
+\fInumber\fR average-width characters on the display; if it is
+\fBpages\fR then the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then characters farther to the left
+become visible; if it is positive then characters farther to the right
+become visible.
+.RE
+.TP
+\fIpathName \fByview \fI?args\fR?
+This command is used to query and change the vertical position of the
+text in the widget's window.
+It can take any of the following forms:
+.RS
+.TP
+\fIpathName \fByview\fR
+Returns a list containing two elements, both of which are real fractions
+between 0 and 1.
+The first element gives the position of the first character in the
+top line in the window, relative to the text as a whole (0.5 means
+it is halfway through the text, for example).
+The second element gives the position of the character just after
+the last one in the bottom line of the window,
+relative to the text as a whole.
+These are the same values passed to scrollbars via the \fB\-yscrollcommand\fR
+option.
+.TP
+\fIpathName \fByview moveto\fI fraction\fR
+Adjusts the view in the window so that the character given by \fIfraction\fR
+appears on the top line of the window.
+\fIFraction\fR is a fraction between 0 and 1; 0 indicates the first
+character in the text, 0.33 indicates the character one-third the
+way through the text, and so on.
+.TP
+\fIpathName \fByview scroll \fInumber what\fR
+This command adjust the view in the window up or down according to
+\fInumber\fR and \fIwhat\fR.
+\fINumber\fR must be an integer.
+\fIWhat\fR must be either \fBunits\fR or \fBpages\fR.
+If \fIwhat\fR is \fBunits\fR, the view adjusts up or down by
+\fInumber\fR lines on the display; if it is \fBpages\fR then
+the view adjusts by \fInumber\fR screenfuls.
+If \fInumber\fR is negative then earlier positions in the text
+become visible; if it is positive then later positions in the text
+become visible.
+.TP
+\fIpathName \fByview \fR?\fB\-pickplace\fR? \fIindex\fR
+Changes the view in the widget's window to make \fIindex\fR visible.
+If the \fB\-pickplace\fR option isn't specified then \fIindex\fR will
+appear at the top of the window.
+If \fB\-pickplace\fR is specified then the widget chooses where
+\fIindex\fR appears in the window:
+.RS
+.IP [1]
+If \fIindex\fR is already visible somewhere in the window then the
+command does nothing.
+.IP [2]
+If \fIindex\fR is only a few lines off-screen above the window then
+it will be positioned at the top of the window.
+.IP [3]
+If \fIindex\fR is only a few lines off-screen below the window then
+it will be positioned at the bottom of the window.
+.IP [4]
+Otherwise, \fIindex\fR will be centered in the window.
+.LP
+The \fB\-pickplace\fR option has been obsoleted by the \fBsee\fR widget
+command (\fBsee\fR handles both x- and y-motion to make a location
+visible, whereas \fB\-pickplace\fR only handles motion in y).
+.RE
+.TP
+\fIpathName \fByview \fInumber\fR
+This command makes the first character on the line after
+the one given by \fInumber\fR visible at the top of the window.
+\fINumber\fR must be an integer.
+This command used to be used for scrolling, but now it is obsolete.
+.RE
+
+.SH BINDINGS
+.PP
+Ck automatically creates class bindings for texts that give them
+the following default behavior.
+In the descriptions below, ``word'' refers to a contiguous group
+of letters, digits, or ``_'' characters, or any single character
+other than these.
+.IP [1]
+Clicking mouse button 1 positions the insertion cursor
+just before the character underneath the mouse cursor, sets the
+input focus to this widget, and clears any selection in the widget.
+.IP [2]
+If any normal printing characters are typed, they are
+inserted at the point of the insertion cursor.
+.IP [3]
+The Left and Right keys move the insertion cursor one character to the
+left or right; they also clear any selection in the text.
+Control-b and Control-f behave the same as Left and Right, respectively.
+.IP [4]
+The Up and Down keys move the insertion cursor one line up or
+down and clear any selection in the text.
+Control-p and Control-n behave the same as Up and Down, respectively.
+.IP [5]
+The Next and Prior keys move the insertion cursor forward or backwards
+by one screenful and clear any selection in the text.
+Control-v moves the view down one screenful without moving the
+insertion cursor or adjusting the selection.
+.IP [6]
+Home and Control-a move the insertion cursor to the
+beginning of its line and clear any selection in the widget.
+.IP [7]
+End and Control-e move the insertion cursor to the
+end of the line and clear any selection in the widget.
+.IP [8]
+The Delete key deletes the selection, if there is one in the widget.
+If there is no selection, it deletes the character to the right of
+the insertion cursor.
+.IP [9]
+Backspace and Control-h delete the selection, if there is one
+in the widget.
+If there is no selection, they delete the character to the left of
+the insertion cursor.
+.IP [10]
+Control-d deletes the character to the right of the insertion cursor.
+.IP [11]
+Control-k deletes from the insertion cursor to the end of its line;
+if the insertion cursor is already at the end of a line, then
+Control-k deletes the newline character.
+.IP [12]
+Control-o opens a new line by inserting a newline character in
+front of the insertion cursor without moving the insertion cursor.
+.IP [13]
+Control-x moves the input focus to the next widget in focus order.
+.IP [14]
+Control-t reverses the order of the two characters to the right of
+the insertion cursor.
+.PP
+If the widget is disabled using the \fB\-state\fR option, then its
+view can still be adjusted and text can still be selected,
+but no insertion cursor will be displayed and no text modifications will
+take place.
+.PP
+The behavior of texts can be changed by defining new bindings for
+individual widgets or by redefining the class bindings.
+
+.SH "PERFORMANCE ISSUES"
+.PP
+Text widgets should run efficiently under a variety
+of conditions. The text widget uses about 2-3 bytes of
+main memory for each byte of text, so texts containing a megabyte
+or more should be practical on most workstations.
+Text is represented internally with a modified B-tree structure
+that makes operations relatively efficient even with large texts.
+Tags are included in the B-tree structure in a way that allows
+tags to span large ranges or have many disjoint smaller ranges
+without loss of efficiency.
+Marks are also implemented in a way that allows large numbers of
+marks.
+The only known mode of operation where a text widget may not run
+efficiently is if it has a very large number of different tags.
+Hundreds of tags should be fine, or even a thousand,
+but tens of thousands of tags will make texts consume a lot of
+memory and run slowly.
+
+.SH KEYWORDS
+text, widget
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH tkerror n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tkerror \- Command invoked to process background errors
+.SH SYNOPSIS
+\fBtkerror \fImessage\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBtkerror\fR command doesn't exist as built-in part of Ck. Instead,
+individual applications or users can define a \fBtkerror\fR
+command (e.g. as a Tcl procedure) if they wish to handle background
+errors.
+.PP
+A background error is one that occurs in a command that didn't
+originate with the application. For example, if an error occurs
+while executing a command specified with a \fBbind\fR or \fBafter\fR
+command, then it is a background error. For a non-background error,
+the error can simply be returned up through nested Tcl command
+evaluations until it reaches the top-level code in the application;
+then the application can report the error in whatever way it
+wishes. When a background error occurs, the unwinding ends in
+the Ck library and there is no obvious way for Ck to report
+the error.
+.PP
+When Ck detects a background error, it saves information about the
+error and invokes the \fBtkerror\fR command later when Ck is idle.
+Before invoking \fBtkerror\fR, Ck restores the \fBerrorInfo\fR
+and \fBerrorCode\fR variables to their values at the time the
+error occurred, then it invokes \fBtkerror\fR with
+the error message as its only argument.
+Ck assumes that the application has implemented the \fBtkerror\fR
+command, and that the command will report the error in a way that
+makes sense for the application. Ck will ignore any result returned
+by the \fBtkerror\fR command.
+.PP
+If another Tcl error occurs within the \fBtkerror\fR command
+(for example, because no \fBtkerror\fR command has been defined)
+then Ck reports the error itself by writing a message to stderr.
+.PP
+If several background errors accumulate before \fBtkerror\fR
+is invoked to process them, \fBtkerror\fR will be invoked once
+for each error, in the order they occurred.
+However, if \fBtkerror\fR returns with a break exception, then
+any remaining errors are skipped without calling \fBtkerror\fR.
+.PP
+The Ck script library includes a default \fBtkerror\fR procedure
+that posts a dialog box containing the error message and offers
+the user a chance to see a stack trace showing where the
+error occurred.
+
+.SH KEYWORDS
+background error, reporting
--- /dev/null
+'\"
+'\" Copyright (c) 1992 The Regents of the University of California.
+'\" Copyright (c) 1994 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH tkwait n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+tkwait \- Wait for variable to change or window to be destroyed
+.SH SYNOPSIS
+\fBtkwait variable \fIname\fR
+.br
+\fBtkwait visibility \fIname\fR
+.br
+\fBtkwait window \fIname\fR
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBtkwait\fR command waits for one of several things to happen,
+then it returns without taking any other actions.
+The return value is always an empty string.
+If the first argument is \fBvariable\fR (or any abbreviation of
+it) then the second argument is the name of a global variable and the
+command waits for that variable to be modified.
+If the first argument is \fBvisibility\fR (or any abbreviation
+of it) then the second argument is the name of a window and the
+\fBtkwait\fR command waits for a change in its
+visibility state. This form is typically used to wait for a newly-created
+window to appear on the screen before taking some action.
+At the time of this writing, visibility state changes are unreliable.
+Thus this form of the \fBtkwait\fR command is strongly discouraged.
+If the first argument is \fBwindow\fR (or any abbreviation
+of it) then the second argument is the name of a window and the
+\fBtkwait\fR command waits for that window to be destroyed.
+This form is typically used to wait for a user to finish interacting
+with a dialog box before using the result of that interaction.
+.PP
+While the \fBtkwait\fR command is waiting it processes events in
+the normal fashion, so the application will continue to respond
+to user interactions.
+
+.SH KEYWORDS
+variable, visibility, wait, window
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH toplevel n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+toplevel \- Create and manipulate toplevel widgets
+.SH SYNOPSIS
+\fBtoplevel\fI \fIpathName \fR?\fIoptions\fR?
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+\fBattributes\fR \fBborder\fR \fBforeground\fR \fBtakefocus\fR
+\fBbackground\fR
+.fi
+.LP
+See the ``options'' manual entry for details on the standard options.
+.SH "WIDGET-SPECIFIC OPTIONS"
+.ta 4c
+.LP
+.nf
+Name: \fBclass\fR
+Class: \fBClass\fR
+Command-Line Switch: \fB\-class\fR
+.fi
+.IP
+Specifies a class for the window.
+This class will be used when querying the option database for
+the window's other options, and it will also be used later for
+other purposes such as bindings.
+The \fBclass\fR option may not be changed with the \fBconfigure\fR
+widget command.
+.LP
+.nf
+Name: \fBheight\fR
+Class: \fBHeight\fR
+Command-Line Switch: \fB\-height\fR
+.fi
+.IP
+Specifies the desired height for the window in screen lines.
+If this option is equal to zero then the window will
+not request any size at all.
+.LP
+.nf
+Name: \fBwidth\fR
+Class: \fBWidth\fR
+Command-Line Switch: \fB\-width\fR
+.fi
+.IP
+Specifies the desired width for the window in screen columns.
+If this option is equal to zero then the window will
+not request any size at all.
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBtoplevel\fR command creates a new toplevel widget (given
+by the \fIpathName\fR argument). Additional
+options, described above, may be specified on the command line
+or in the option database
+to configure aspects of the toplevel such as its background color
+and relief. The \fBtoplevel\fR command returns the
+path name of the new window.
+.PP
+A toplevel is similar to a frame except that it is created as a
+top-level window: its parent with respect to screen real estate
+is the terminal's screen rather than the logical parent from its
+path name. The primary
+purpose of a toplevel is to serve as a container for dialog boxes
+and other collections of widgets. The only visible features
+of a toplevel are its background color, attributes and border.
+
+.SH "WIDGET COMMAND"
+.PP
+The \fBtoplevel\fR command creates a new Tcl command whose
+name is the same as the path name of the toplevel's window. This
+command may be used to invoke various
+operations on the widget. It has the following general form:
+.DS C
+\fIpathName option \fR?\fIarg arg ...\fR?
+.DE
+\fIPathName\fR is the name of the command, which is the same as
+the toplevel widget's path name. \fIOption\fR and the \fIarg\fRs
+determine the exact behavior of the command. The following
+commands are possible for toplevel widgets:
+.TP
+\fIpathName \fBcget\fR \fIoption\fR
+Returns the current value of the configuration option given
+by \fIoption\fR.
+\fIOption\fR may have any of the values accepted by the \fBtoplevel\fR
+command.
+.TP
+\fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
+Query or modify the configuration options of the widget.
+If no \fIoption\fR is specified, returns a list describing all of
+the available options for \fIpathName\fR. If \fIoption\fR is specified
+with no \fIvalue\fR, then the command returns a list describing the
+one named option (this list will be identical to the corresponding
+sublist of the value returned if no \fIoption\fR is specified). If
+one or more \fIoption\-value\fR pairs are specified, then the command
+modifies the given widget option(s) to have the given value(s); in
+this case the command returns an empty string.
+\fIOption\fR may have any of the values accepted by the \fBtoplevel\fR
+command.
+
+.SH PLACEMENT
+The only means to place a toplevel widget on the screen is the
+\fBplace\fR geometry manager.
+
+.SH BINDINGS
+.PP
+When a new toplevel is created, it has no default event bindings:
+toplevels are not intended to be interactive.
+
+.SH KEYWORDS
+toplevel, widget, place
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1992 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH update n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+update \- Process pending events and/or when-idle handlers
+.SH SYNOPSIS
+\fBupdate\fR ?\fBidletasks|screen\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+This command is used to bring the entire application world
+``up to date.''
+It flushes all pending output to the display, waits for the
+server to process that output and return errors or events,
+handles all pending events of any sort (including when-idle handlers),
+and repeats this set of operations until there are no pending
+events, no pending when-idle handlers, no pending output to the server,
+and no operations still outstanding at the server.
+.PP
+If the \fBidletasks\fR keyword is specified as an argument to the
+command, then no new events or errors are processed; only when-idle
+idlers are invoked.
+This causes operations that are normally deferred, such as display
+updates and window layout calculations, to be performed immediately.
+.PP
+The \fBupdate idletasks\fR command is useful in scripts where
+changes have been made to the application's state and you want those
+changes to appear on the display immediately, rather than waiting
+for the script to complete. Most display updates are performed as
+idle handlers, so \fBupdate idletasks\fR will cause them to run.
+However, there are some kinds of updates that only happen in
+response to events, such as those triggered by window size changes;
+these updates will not occur in \fBupdate idletasks\fR.
+.PP
+If the \fBscreen\fR keyword is specified as an argument to the command,
+then the entire screen is repainted from scratch without handling any other
+events. This is useful if the terminal's screen has been garbled by
+another process.
+.PP
+The \fBupdate\fR command with no options is useful in scripts where
+you are performing a long-running computation but you still want
+the application to respond to user interactions; if you occasionally
+call \fBupdate\fR then user input will be processed during the
+next call to \fBupdate\fR.
+
+.SH KEYWORDS
+event, flush, handler, idle, update
--- /dev/null
+'\"
+'\" Copyright (c) 1990-1994 The Regents of the University of California.
+'\" Copyright (c) 1994-1995 Sun Microsystems, Inc.
+'\" Copyright (c) 1996-1999 Christian Werner
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH winfo n 8.0 Ck "Ck Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+winfo \- Return window-related information
+.SH SYNOPSIS
+\fBwinfo\fR \fIoption \fR?\fIarg arg ...\fR?
+.BE
+
+.SH DESCRIPTION
+.PP
+The \fBwinfo\fR command is used to retrieve information about windows
+managed by Ck. It can take any of a number of different forms,
+depending on the \fIoption\fR argument. The legal forms are:
+.TP
+\fBwinfo children \fIwindow\fR
+Returns a list containing the path names of all the children
+of \fIwindow\fR. Top-level windows are returned as children
+of their logical parents.
+.TP
+\fBwinfo class \fIwindow\fR
+Returns the class name for \fIwindow\fR.
+.TP
+\fBwinfo containing \fIrootX rootY\fR
+Returns the path name for the window containing the point given
+by \fIrootX\fR and \fIrootY\fR.
+\fIRootX\fR and \fIrootY\fR are specified as cursor position
+in the coordinate system of the terminal.
+If no window in this application contains the point then an empty
+string is returned.
+In selecting the containing window, children are given higher priority
+than parents and among siblings the highest one in the stacking order is
+chosen.
+.TP
+\fBwinfo depth \fIwindow\fR
+Returns a decimal string giving the depth of \fIwindow\fR. 1 means the
+terminal's screen is monochrome. Any number higher than 1 means that
+the terminal supports colors.
+.TP
+\fBwinfo exists \fIwindow\fR
+Returns 1 if there exists a window named \fIwindow\fR, 0 if no such
+window exists.
+.TP
+\fBwinfo geometry \fIwindow\fR
+Returns the geometry for \fIwindow\fR, in the form
+\fIwidth\fBx\fIheight\fB+\fIx\fB+\fIy\fR. All dimensions are
+in terminal coordinates.
+.TP
+\fBwinfo height \fIwindow\fR
+Returns a decimal string giving \fIwindow\fR's height in terminal lines.
+When a window is first created its height will be 1; the
+height will eventually be changed by a geometry manager to fulfill
+the window's needs.
+If you need the true height immediately after creating a widget,
+invoke \fBupdate\fR to force the geometry manager to arrange it,
+or use \fBwinfo reqheight\fR to get the window's requested height
+instead of its actual height.
+.TP
+\fBwinfo ismapped \fIwindow\fR
+Returns \fB1\fR if \fIwindow\fR is currently mapped, \fB0\fR otherwise.
+.TP
+\fBwinfo manager \fIwindow\fR
+Returns the name of the geometry manager currently
+responsible for \fIwindow\fR, or an empty string if \fIwindow\fR
+isn't managed by any geometry manager.
+The name is usually the name of the Tcl command for the geometry
+manager, such as \fBpack\fR or \fBplace\fR.
+.TP
+\fBwinfo name \fIwindow\fR
+Returns \fIwindow\fR's name (i.e. its name within its parent, as opposed
+to its full path name).
+The command \fBwinfo name .\fR will return the name of the application.
+.TP
+\fBwinfo parent \fIwindow\fR
+Returns the path name of \fIwindow\fR's parent, or an empty string
+if \fIwindow\fR is the main window of the application.
+.TP
+\fBwinfo reqheight \fIwindow\fR
+Returns a decimal string giving \fIwindow\fR's requested height,
+in lines. This is the value used by \fIwindow\fR's geometry
+manager to compute its geometry.
+.TP
+\fBwinfo reqwidth \fIwindow\fR
+Returns a decimal string giving \fIwindow\fR's requested width,
+in columns. This is the value used by \fIwindow\fR's geometry
+manager to compute its geometry.
+.TP
+\fBwinfo rootx \fIwindow\fR
+Returns a decimal string giving the x-coordinate, in the root
+window of the screen, of the
+upper-left corner of \fIwindow\fR's border (or \fIwindow\fR if it
+has no border).
+.TP
+\fBwinfo rooty \fIwindow\fR
+Returns a decimal string giving the y-coordinate, in the root
+window of the screen, of the
+upper-left corner of \fIwindow\fR's border (or \fIwindow\fR if it
+has no border).
+.TP
+\fBwinfo screenheight \fIwindow\fR
+Returns a decimal string giving the height of \fIwindow\fR's terminal
+screen, in lines.
+.TP
+\fBwinfo screenwidth \fIwindow\fR
+Returns a decimal string giving the width of \fIwindow\fR's terminal screen,
+in columns.
+.TP
+\fBwinfo toplevel \fIwindow\fR
+Returns the path name of the top-level window containing \fIwindow\fR.
+.TP
+\fBwinfo width \fIwindow\fR
+Returns a decimal string giving \fIwindow\fR's width in columns.
+When a window is first created its width will be 1; the
+width will eventually be changed by a geometry manager to fulfill
+the window's needs.
+If you need the true width immediately after creating a widget,
+invoke \fBupdate\fR to force the geometry manager to arrange it,
+or use \fBwinfo reqwidth\fR to get the window's requested width
+instead of its actual width.
+.TP
+\fBwinfo x \fIwindow\fR
+Returns a decimal string giving the x-coordinate, in \fIwindow\fR's
+parent, of the
+upper-left corner of \fIwindow\fR's border (or \fIwindow\fR if it
+has no border).
+.TP
+\fBwinfo y \fIwindow\fR
+Returns a decimal string giving the y-coordinate, in \fIwindow\fR's
+parent, of the
+upper-left corner of \fIwindow\fR's border (or \fIwindow\fR if it
+has no border).
+
+.SH KEYWORDS
+children, class, geometry, height, identifier, information,
+mapped, parent, path name, screen, terminal, width, window
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * ks_names.h --
+ *
+ * Key symbols, associated values and terminfo names.
+ *
+ * Copyright (c) 1995 Christian Werner.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#ifdef KEY_BACKSPACE
+{ "BackSpace", KEY_BACKSPACE, "kbs" },
+#else
+{ "BackSpace", 0x008, NULL },
+#endif
+#ifdef KEY_DC
+{ "Delete", KEY_DC, "kdch1" },
+#else
+{ "Delete", 0x07f, NULL },
+#endif
+{ "Tab", 0x009, NULL },
+{ "Linefeed", 0x00a, NULL },
+{ "Return", 0x00d, NULL },
+{ "Escape", 0x01b, NULL },
+{ "ASCIIDelete", 0x07f, NULL },
+#ifdef KEY_HOME
+{ "Home", KEY_HOME, "khome" },
+#endif
+#ifdef KEY_LEFT
+{ "Left", KEY_LEFT, "kcub1" },
+#endif
+#ifdef KEY_UP
+{ "Up", KEY_UP, "kcuu1" },
+#endif
+#ifdef KEY_RIGHT
+{ "Right", KEY_RIGHT, "kcuf1" },
+#endif
+#ifdef KEY_DOWN
+{ "Down", KEY_DOWN, "kcud1" },
+#endif
+#ifdef KEY_PPAGE
+{ "Prior", KEY_PPAGE, "kpp" },
+#endif
+#ifdef KEY_NPAGE
+{ "Next", KEY_NPAGE, "knp" },
+#endif
+#ifdef KEY_END
+{ "End", KEY_END, "kend" },
+#endif
+#ifdef KEY_BEG
+{ "Begin", KEY_BEG, "kbeg" },
+#endif
+#ifdef KEY_SELECT
+{ "Select", KEY_SELECT, "kslt" },
+#endif
+#ifdef KEY_PRINT
+{ "Print", KEY_PRINT, "kprt" },
+#endif
+#ifdef KEY_COMMAND
+{ "Execute", KEY_COMMAND, "kcmd" },
+#endif
+#ifdef KEY_IC
+{ "Insert", KEY_IC, "kich1" },
+#endif
+#ifdef KEY_UNDO
+{ "Undo", KEY_UNDO, "kund" },
+#endif
+#ifdef KEY_REDO
+{ "Redo", KEY_REDO, "krdo" },
+#endif
+#ifdef KEY_OPTIONS
+{ "Menu", KEY_OPTIONS, "kopt" },
+#endif
+#ifdef KEY_REFERENCE
+{ "Find", KEY_REFERENCE, "kref" },
+#endif
+#ifdef KEY_BTAB
+{ "BackTab", KEY_BTAB, "kcbt" },
+#endif
+#ifdef KEY_CANCEL
+{ "Cancel", KEY_CANCEL, "kcan" },
+#endif
+#ifdef KEY_HELP
+{ "Help", KEY_HELP, "khlp" },
+#endif
+#ifdef KEY_F
+{ "F1", KEY_F(1), "kf1" },
+{ "F2", KEY_F(2), "kf2" },
+{ "F3", KEY_F(3), "kf3" },
+{ "F4", KEY_F(4), "kf4" },
+{ "F5", KEY_F(5), "kf5" },
+{ "F6", KEY_F(6), "kf6" },
+{ "F7", KEY_F(7), "kf7" },
+{ "F8", KEY_F(8), "kf8" },
+{ "F9", KEY_F(9), "kf9" },
+{ "F10", KEY_F(10), "kf10" },
+{ "L1", KEY_F(11), "kf11" },
+{ "F11", KEY_F(11), "kf11" },
+{ "L2", KEY_F(12), "kf12" },
+{ "F12", KEY_F(12), "kf12" },
+{ "L3", KEY_F(13), "kf13" },
+{ "F13", KEY_F(13), "kf13" },
+{ "L4", KEY_F(14), "kf14" },
+{ "F14", KEY_F(14), "kf14" },
+{ "L5", KEY_F(15), "kf15" },
+{ "F15", KEY_F(15), "kf15" },
+{ "L6", KEY_F(16), "kf16" },
+{ "F16", KEY_F(16), "kf16" },
+{ "L7", KEY_F(17), "kf17" },
+{ "F17", KEY_F(17), "kf17" },
+{ "L8", KEY_F(18), "kf18" },
+{ "F18", KEY_F(18), "kf18" },
+{ "L9", KEY_F(19), "kf19" },
+{ "F19", KEY_F(19), "kf19" },
+{ "L10", KEY_F(20), "kf20" },
+{ "F20", KEY_F(20), "kf20" },
+{ "R1", KEY_F(21), "kf21" },
+{ "F21", KEY_F(21), "kf21" },
+{ "R2", KEY_F(22), "kf22" },
+{ "F22", KEY_F(22), "kf22" },
+{ "R3", KEY_F(23), "kf23" },
+{ "F23", KEY_F(23), "kf23" },
+{ "R4", KEY_F(24), "kf24" },
+{ "F24", KEY_F(24), "kf24" },
+{ "R5", KEY_F(25), "kf25" },
+{ "F25", KEY_F(25), "kf25" },
+{ "R6", KEY_F(26), "kf26" },
+{ "F26", KEY_F(26), "kf26" },
+{ "R7", KEY_F(27), "kf27" },
+{ "F27", KEY_F(27), "kf27" },
+{ "R8", KEY_F(28), "kf28" },
+{ "F28", KEY_F(28), "kf28" },
+{ "R9", KEY_F(29), "kf29" },
+{ "F29", KEY_F(29), "kf29" },
+{ "R10", KEY_F(30), "kf30" },
+{ "F30", KEY_F(30), "kf30" },
+{ "R11", KEY_F(31), "kf31" },
+{ "F31", KEY_F(31), "kf31" },
+{ "R12", KEY_F(32), "kf32" },
+{ "F32", KEY_F(32), "kf32" },
+{ "R13", KEY_F(33), "kf33" },
+{ "F33", KEY_F(33), "kf33" },
+{ "R14", KEY_F(34), "kf34" },
+{ "F34", KEY_F(34), "kf34" },
+{ "R15", KEY_F(35), "kf35" },
+{ "F35", KEY_F(35), "kf35" },
+#endif
+#ifdef KEY_SUSPEND
+{ "Suspend", KEY_SUSPEND, "kspd" },
+#endif
+{ "space", 0x020, NULL },
+{ "exclam", 0x021, NULL },
+{ "quotedbl", 0x022, NULL },
+{ "numbersign", 0x023, NULL },
+{ "dollar", 0x024, NULL },
+{ "percent", 0x025, NULL },
+{ "ampersand", 0x026, NULL },
+{ "quoteright", 0x027, NULL },
+{ "parenleft", 0x028, NULL },
+{ "parenright", 0x029, NULL },
+{ "asterisk", 0x02a, NULL },
+{ "plus", 0x02b, NULL },
+{ "comma", 0x02c, NULL },
+{ "minus", 0x02d, NULL },
+{ "period", 0x02e, NULL },
+{ "slash", 0x02f, NULL },
+{ "0", 0x030, NULL },
+{ "1", 0x031, NULL },
+{ "2", 0x032, NULL },
+{ "3", 0x033, NULL },
+{ "4", 0x034, NULL },
+{ "5", 0x035, NULL },
+{ "6", 0x036, NULL },
+{ "7", 0x037, NULL },
+{ "8", 0x038, NULL },
+{ "9", 0x039, NULL },
+{ "colon", 0x03a, NULL },
+{ "semicolon", 0x03b, NULL },
+{ "less", 0x03c, NULL },
+{ "equal", 0x03d, NULL },
+{ "greater", 0x03e, NULL },
+{ "question", 0x03f, NULL },
+{ "at", 0x040, NULL },
+{ "A", 0x041, NULL },
+{ "B", 0x042, NULL },
+{ "C", 0x043, NULL },
+{ "D", 0x044, NULL },
+{ "E", 0x045, NULL },
+{ "F", 0x046, NULL },
+{ "G", 0x047, NULL },
+{ "H", 0x048, NULL },
+{ "I", 0x049, NULL },
+{ "J", 0x04a, NULL },
+{ "K", 0x04b, NULL },
+{ "L", 0x04c, NULL },
+{ "M", 0x04d, NULL },
+{ "N", 0x04e, NULL },
+{ "O", 0x04f, NULL },
+{ "P", 0x050, NULL },
+{ "Q", 0x051, NULL },
+{ "R", 0x052, NULL },
+{ "S", 0x053, NULL },
+{ "T", 0x054, NULL },
+{ "U", 0x055, NULL },
+{ "V", 0x056, NULL },
+{ "W", 0x057, NULL },
+{ "X", 0x058, NULL },
+{ "Y", 0x059, NULL },
+{ "Z", 0x05a, NULL },
+{ "bracketleft", 0x05b, NULL },
+{ "backslash", 0x05c, NULL },
+{ "bracketright", 0x05d, NULL },
+{ "asciicircum", 0x05e, NULL },
+{ "underscore", 0x05f, NULL },
+{ "quoteleft", 0x060, NULL },
+{ "a", 0x061, NULL },
+{ "b", 0x062, NULL },
+{ "c", 0x063, NULL },
+{ "d", 0x064, NULL },
+{ "e", 0x065, NULL },
+{ "f", 0x066, NULL },
+{ "g", 0x067, NULL },
+{ "h", 0x068, NULL },
+{ "i", 0x069, NULL },
+{ "j", 0x06a, NULL },
+{ "k", 0x06b, NULL },
+{ "l", 0x06c, NULL },
+{ "m", 0x06d, NULL },
+{ "n", 0x06e, NULL },
+{ "o", 0x06f, NULL },
+{ "p", 0x070, NULL },
+{ "q", 0x071, NULL },
+{ "r", 0x072, NULL },
+{ "s", 0x073, NULL },
+{ "t", 0x074, NULL },
+{ "u", 0x075, NULL },
+{ "v", 0x076, NULL },
+{ "w", 0x077, NULL },
+{ "x", 0x078, NULL },
+{ "y", 0x079, NULL },
+{ "z", 0x07a, NULL },
+{ "braceleft", 0x07b, NULL },
+{ "bar", 0x07c, NULL },
+{ "braceright", 0x07d, NULL },
+{ "asciitilde", 0x07e, NULL },
+
--- /dev/null
+# tkerror.tcl --
+#
+# This file contains a default version of the tkError procedure. It
+# posts a dialog box with the error message and gives the user a chance
+# to see a more detailed stack trace.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+# Copyright (c) 1999 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+if {[winfo depth .] > 1} {
+ option add *ckerrorDialog*background red
+ option add *ErrorTrace*background red
+}
+
+# Fake the auto_mkindex procedure for Tcl 7.4 \
+proc tkerror err {}
+
+# Fake the auto_mkindex procedure for Tcl 7.5 and above \
+proc bgerror err {}
+
+# tkerror --
+# This is the default version of tkerror. It posts a dialog box containing
+# the error message and gives the user a chance to ask to see a stack
+# trace.
+#
+# Arguments:
+# err - The error message.
+
+if {$tcl_version > 7.4} {
+ set ckPriv(bgErrProc) bgerror
+} else {
+ set ckPriv(bgErrProc) tkerror
+}
+ proc $ckPriv(bgErrProc) err {
+ global errorInfo
+ set info $errorInfo
+ set button [ck_dialog .ckerrorDialog "Error in Tcl Script" \
+ "Error: $err" Okay Skip Trace]
+ if {$button == 0} {
+ return
+ } elseif {$button == 1} {
+ return -code break
+ }
+ set w .ckerrorTrace
+ catch {destroy $w}
+ toplevel $w -class ErrorTrace \
+ -border { ulcorner hline urcorner vline lrcorner hline llcorner vline }
+ place $w -relx 0.5 -rely 0.5 -anchor center
+ label $w.title -text "Stack Trace for Error"
+ place $w.title -y 0 -relx 0.5 -anchor center -bordermode ignore
+ button $w.ok -text OK -command "destroy $w"
+ scrollbar $w.scroll -command "$w.text yview" -takefocus 0
+ text $w.text -yscrollcommand "$w.scroll set"
+ frame $w.sep -border hline
+ pack $w.ok -side bottom -ipadx 1
+ pack $w.sep -side bottom -fill x
+ pack $w.scroll -side right -fill y
+ pack $w.text -side left -expand 1 -fill both
+ $w.text insert 0.0 $info
+ $w.text mark set insert 0.0
+ bind $w.text <Tab> {focus [ck_focusNext %W] ; break}
+ focus $w.ok
+ tkwait window $w
+}
+
--- /dev/null
+# button.tcl --
+#
+# This file defines the default bindings for Ck label, button,
+# checkbutton, and radiobutton widgets and provides procedures
+# that help in implementing those bindings.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+set ckPriv(buttonWindow) ""
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for buttons.
+#-------------------------------------------------------------------------
+
+bind Button <FocusIn> {ckButtonFocus %W 1}
+bind Button <FocusOut> {ckButtonFocus %W 0}
+bind Button <space> {ckButtonInvoke %W}
+bind Button <Return> {ckButtonInvoke %W}
+bind Button <Linefeed> {ckButtonInvoke %W}
+bind Button <Button-1> {ckButtonInvoke %W}
+
+bind Checkbutton <FocusIn> {ckButtonFocus %W 1}
+bind Checkbutton <FocusOut> {ckButtonFocus %W 0}
+bind Checkbutton <space> {ckButtonInvoke %W}
+bind Checkbutton <Return> {ckButtonInvoke %W}
+bind Checkbutton <Linefeed> {ckButtonInvoke %W}
+bind Checkbutton <Button-1> {ckButtonInvoke %W}
+
+bind Radiobutton <FocusIn> {ckButtonFocus %W 1}
+bind Radiobutton <FocusOut> {ckButtonFocus %W 0}
+bind Radiobutton <space> {ckButtonInvoke %W}
+bind Radiobutton <Return> {ckButtonInvoke %W}
+bind Radiobutton <Linefeed> {ckButtonInvoke %W}
+bind Radiobutton <Button-1> {ckButtonInvoke %W}
+
+# ckButtonFocus --
+# The procedure below is called when a button is invoked through
+# the keyboard.
+#
+# Arguments:
+# w - The name of the widget.
+
+proc ckButtonFocus {w flag} {
+ global ckPriv
+ if {[$w cget -state] == "disabled"} return
+ if {$flag} {
+ set ckPriv(buttonWindow) $w
+ set ckPriv(buttonState) [$w cget -state]
+ $w configure -state active
+ return
+ }
+ if {$w == $ckPriv(buttonWindow)} {
+ set ckPriv(buttonWindow) ""
+ $w configure -state $ckPriv(buttonState)
+ set ckPriv(buttonState) ""
+ }
+}
+
+# ckButtonInvoke --
+# The procedure below is called when a button is invoked through
+# the keyboard.
+#
+# Arguments:
+# w - The name of the widget.
+
+proc ckButtonInvoke w {
+ if {[$w cget -state] != "disabled"} {
+ uplevel #0 [list $w invoke]
+ }
+}
--- /dev/null
+# ck.tcl --
+#
+# Initialization script normally executed in the interpreter for each
+# curses wish-based application. Arranges class bindings for widgets.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+# Copyright (c) 1995-2000 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+# Insist on running with compatible versions of Tcl and Ck.
+
+scan [info tclversion] "%d.%d" a b
+scan $ck_version "%d.%d" c d
+if {$a == 7} {
+ if {$c != 4} {
+ error "wrong version of Ck loaded ($c.$d): need 4.X"
+ }
+ if {$b != $d+4 } {
+ error "wrong version of Ck loaded ($c.$d): need 4.[expr $b-4]"
+ }
+} elseif {$a == 8} {
+ if {$c != 8} {
+ error "wrong version of Ck loaded ($c.$d): need 8.X"
+ }
+ if {$d != $b} {
+ error "wrong version of Ck loaded ($c.$d): need 8.$b"
+ }
+}
+
+unset a b c d
+
+if {[string compare $tcl_platform(platform) windows] == 0 } {
+ curses encoding IBM437
+ set env(TERM) win32
+} elseif {[string compare $tcl_platform(platform) dos]==0} {
+ curses encoding IBM437
+}
+
+# Inhibit exec of unknown commands
+
+set auto_noexec 1
+
+# Add this directory to the begin of the auto-load search path:
+
+if {[info exists auto_path]} {
+ set auto_path [concat $ck_library $auto_path]
+}
+
+# ----------------------------------------------------------------------
+# Read in files that define all of the class bindings.
+# ----------------------------------------------------------------------
+
+source $ck_library/button.tcl
+source $ck_library/entry.tcl
+source $ck_library/listbox.tcl
+source $ck_library/scrollbar.tcl
+source $ck_library/text.tcl
+source $ck_library/menu.tcl
+
+# ----------------------------------------------------------------------
+# Default bindings for keyboard traversal.
+# ----------------------------------------------------------------------
+
+bind all <Tab> {focus [ck_focusNext %W]}
+bind all <BackTab> {focus [ck_focusPrev %W]}
+if {$tcl_interactive} {
+ bind all <Control-c> ckCommand
+ ckCommand
+}
+
--- /dev/null
+# ckfbox.tcl --
+#
+# Implements the "CK" standard file selection dialog box.
+#
+# Copyright (c) 1994-1996 Sun Microsystems, Inc.
+# Copyright (c) 1999-2000 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+proc ck_getOpenFile args {
+ eval ckFDialog open $args
+}
+
+proc ck_getSaveFile args {
+ eval ckFDialog save $args
+}
+
+# ckFDialog --
+#
+# Implements the file selection dialog.
+
+proc ckFDialog {type args} {
+ global ckPriv
+ set w __ck_filedialog
+ upvar #0 $w data
+ ckFDialog_Config $w $type $args
+ if {![string compare $data(-parent) .]} {
+ set w .$w
+ } else {
+ set w $data(-parent).$w
+ }
+ # (re)create the dialog box if necessary
+ if {![winfo exists $w]} {
+ ckFDialog_Create $w
+ } elseif {[string compare [winfo class $w] CkFDialog]} {
+ destroy $w
+ ckFDialog_Create $w
+ } else {
+ set data(dirMenuBtn) $w.f1.menu
+ set data(dirMenu) $w.f1.menu.menu
+ set data(upBtn) $w.f1.up
+ set data(list) $w.list
+ set data(ent) $w.f2.ent
+ set data(typeMenuLab) $w.f3.lab
+ set data(typeMenuBtn) $w.f3.menu
+ set data(typeMenu) $data(typeMenuBtn).m
+ set data(okBtn) $w.f2.ok
+ set data(cancelBtn) $w.f3.cancel
+ }
+ # Initialize the file types menu
+ if {$data(-filetypes) != {}} {
+ $data(typeMenu) delete 0 end
+ foreach type $data(-filetypes) {
+ set title [lindex $type 0]
+ set filter [lindex $type 1]
+ $data(typeMenu) add command -label $title \
+ -command [list ckFDialog_SetFilter $w $type]
+ }
+ ckFDialog_SetFilter $w [lindex $data(-filetypes) 0]
+ $data(typeMenuBtn) config -state normal -takefocus 1
+ $data(typeMenuLab) config -state normal
+ } else {
+ set data(filter) "*"
+ $data(typeMenuBtn) config -state disabled -takefocus 0
+ $data(typeMenuLab) config -state disabled
+ }
+ ckFDialog_UpdateWhenIdle $w
+ place forget $w
+ place $w -relx 0.5 -rely 0.5 -anchor center
+ set oldFocus [focus]
+ focus $data(ent)
+ $data(ent) delete 0 end
+ $data(ent) insert 0 $data(selectFile)
+ $data(ent) select from 0
+ $data(ent) select to end
+ $data(ent) icursor end
+ tkwait variable ckPriv(selectFilePath)
+ catch {focus $oldFocus}
+ destroy $w
+ return $ckPriv(selectFilePath)
+}
+
+# ckFDialog_Config --
+#
+# Configures the filedialog according to the argument list
+#
+proc ckFDialog_Config {w type argList} {
+ upvar #0 $w data
+ set data(type) $type
+ # 1. the configuration specs
+ set specs {
+ {-defaultextension "" "" ""}
+ {-filetypes "" "" ""}
+ {-initialdir "" "" ""}
+ {-initialfile "" "" ""}
+ {-parent "" "" "."}
+ {-title "" "" ""}
+ }
+ # 2. default values depending on the type of the dialog
+ if {![info exists data(selectPath)]} {
+ # first time the dialog has been popped up
+ set data(selectPath) [pwd]
+ set data(selectFile) ""
+ }
+ # 3. parse the arguments
+ tclParseConfigSpec $w $specs "" $argList
+ if {![string compare $data(-title) ""]} {
+ if {![string compare $type "open"]} {
+ set data(-title) "Open"
+ } else {
+ set data(-title) "Save As"
+ }
+ }
+ # 4. set the default directory and selection according to the -initial
+ # settings
+ if {[string compare $data(-initialdir) ""]} {
+ if {[file isdirectory $data(-initialdir)]} {
+ set data(selectPath) [glob $data(-initialdir)]
+ } else {
+ set data(selectPath) [pwd]
+ }
+ # Convert the initialdir to an absolute path name.
+ set old [pwd]
+ cd $data(selectPath)
+ set data(selectPath) [pwd]
+ cd $old
+ }
+ set data(selectFile) $data(-initialfile)
+ # 5. Parse the -filetypes option
+ set data(-filetypes) [ckFDGetFileTypes $data(-filetypes)]
+ if {![winfo exists $data(-parent)]} {
+ error "bad window path name \"$data(-parent)\""
+ }
+}
+
+proc ckFDialog_Create {w} {
+ set dataName [lindex [split $w .] end]
+ upvar #0 $dataName data
+ toplevel $w -class CkFDialog -border {
+ ulcorner hline urcorner vline lrcorner hline llcorner vline
+ }
+ # f1: the frame with the directory option menu
+ set f1 [frame $w.f1 -class Dir]
+ label $f1.lab -text "Directory:" -underline 0
+ set data(dirMenuBtn) $f1.menu
+ set data(dirMenu) [ck_optionMenu $f1.menu [format %s(selectPath) $dataName] ""]
+ set data(upBtn) [button $f1.up -text Up -width 4 -underline 0]
+ pack $data(upBtn) -side right -padx 1 -fill both
+ pack $f1.lab -side left -padx 4 -fill both
+ pack $f1.menu -expand yes -fill both -padx 1
+ frame $w.sep0 -border hline -height 1
+ set data(list) [listbox $w.list -selectmode browse -height 8]
+ bindtags $data(list) [list Listbox $data(list) $w all]
+ bind $data(list) <Button-1> [list ckFDialog_ListBrowse $w]
+ bind $data(list) <KeyPress> [list ckFDialog_ListBrowse $w]
+ bind $data(list) <space> [list ckFDialog_ListBrowse $w]
+ bind $data(list) <Return> [list ckFDialog_ListInvoke $w]
+ bind $data(list) <Linefeed> [list ckFDialog_ListInvoke $w]
+ frame $w.sep1 -border hline -height 1
+ # f2: the frame with the OK button and the "file name" field
+ set f2 [frame $w.f2 -class Filename]
+ label $f2.lab -text "File name:" -anchor e -width 14 -underline 5
+ set data(ent) [entry $f2.ent]
+ # f3: the frame with the cancel button and the file types field
+ set f3 [frame $w.f3 -class Filetype]
+ # The "File of types:" label needs to be grayed-out when
+ # -filetypes are not specified. The label widget does not support
+ # grayed-out text on monochrome displays. Therefore, we have to
+ # use a button widget to emulate a label widget (by setting its
+ # bindtags)
+ set data(typeMenuLab) [button $f3.lab -text "Files of type:" \
+ -anchor e -width 14 -underline 9 -takefocus 0]
+ bindtags $data(typeMenuLab) [list $data(typeMenuLab) Label \
+ [winfo toplevel $data(typeMenuLab)] all]
+ set data(typeMenuBtn) [menubutton $f3.menu -menu $f3.menu.m]
+ $f3.menu config -takefocus 1 \
+ -disabledbackground [$f3.menu cget -background] \
+ -disabledforeground [$f3.menu cget -foreground]
+ bind $f3.menu <FocusIn> {
+ if {[%W cget -state] != "disabled"} {
+ %W configure -state active
+ }
+ }
+ bind $f3.menu <FocusOut> {
+ if {[%W cget -state] != "disabled"} {
+ %W configure -state normal
+ }
+ }
+ set data(typeMenu) [menu $data(typeMenuBtn).m -border {
+
+ ulcorner hline urcorner vline lrcorner hline llcorner vline}]
+ $data(typeMenuBtn) config -takefocus 1 -anchor w
+ # the okBtn is created after the typeMenu so that the keyboard traversal
+ # is in the right order
+ set data(okBtn) [button $f2.ok -text OK -underline 0 -width 6]
+ set data(cancelBtn) [button $f3.cancel -text Cancel -underline 0 -width 6]
+ # pack the widgets in f2 and f3
+ pack $data(okBtn) -side right -padx 1 -anchor e
+ pack $f2.lab -side left -padx 1
+ pack $f2.ent -expand 1 -fill x
+ pack $data(cancelBtn) -side right -padx 1 -anchor w
+ pack $data(typeMenuLab) -side left -padx 1
+ pack $data(typeMenuBtn) -expand 1 -fill x -side right
+ # Pack all the frames together. We are done with widget construction.
+ pack $f1 -side top -fill x
+ pack $w.sep0 -side top -fill x
+ pack $f3 -side bottom -fill x
+ pack $f2 -side bottom -fill x
+ pack $w.sep1 -side bottom -fill x
+ pack $data(list) -expand 1 -fill both -padx 1
+ # Set up the event handlers
+ bind $data(ent) <Return> "ckFDialog_ActivateEnt $w"
+ bind $data(ent) <Linefeed> "ckFDialog_ActivateEnt $w"
+ $data(upBtn) config -command "ckFDialog_UpDirCmd $w"
+ $data(okBtn) config -command "ckFDialog_OkCmd $w"
+ $data(cancelBtn) config -command "ckFDialog_CancelCmd $w"
+ trace variable data(selectPath) w "ckFDialog_SetPath $w"
+ bind $w <Control-d> "focus $data(dirMenuBtn) ; break"
+ bind $w <Control-t> [format {
+ if {"[%s cget -state]" == "normal"} {
+ focus %s
+ }
+ } $data(typeMenuBtn) $data(typeMenuBtn)]
+ bind $w <Control-n> "focus $data(ent) ; break"
+ bind $w <Escape> "ckButtonInvoke $data(cancelBtn)"
+ bind $w <Control-c> "ckButtonInvoke $data(cancelBtn) ; break"
+ bind $w <Control-o> "ckFDialog_InvokeBtn $w Open ; break"
+ bind $w <Control-s> "ckFDialog_InvokeBtn $w Save ; break"
+ bind $w <Control-u> "ckFDialog_UpDirCmd $w ; break"
+}
+
+# ckFDialog_UpdateWhenIdle --
+#
+# Creates an idle event handler which updates the dialog in idle
+# time. This is important because loading the directory may take a long
+# time and we don't want to load the same directory for multiple times
+# due to multiple concurrent events.
+
+proc ckFDialog_UpdateWhenIdle {w} {
+ upvar #0 [winfo name $w] data
+ if {[info exists data(updateId)]} {
+ return
+ } else {
+ set data(updateId) [after idle ckFDialog_Update $w]
+ }
+}
+
+# ckFDialog_Update --
+#
+# Loads the files and directories into listbox. Also
+# sets up the directory option menu for quick access to parent
+# directories.
+
+proc ckFDialog_Update {w} {
+ global tcl_version
+ # This proc may be called within an idle handler. Make sure that the
+ # window has not been destroyed before this proc is called
+ if {![winfo exists $w] || [string compare [winfo class $w] CkFDialog]} {
+ return
+ }
+ set dataName [winfo name $w]
+ upvar #0 $dataName data
+ global ckPriv
+ catch {unset data(updateId)}
+ set appPWD [pwd]
+ if {[catch {
+ cd $data(selectPath)
+ }]} {
+ # We cannot change directory to $data(selectPath). $data(selectPath)
+ # should have been checked before ckFDialog_Update is called, so
+ # we normally won't come to here. Anyways, give an error and abort
+ # action.
+ ck_messageBox -type ok -parent $data(-parent) -message \
+ "Cannot change to the directory \"$data(selectPath)\".\nPermission denied."
+ cd $appPWD
+ return
+ }
+ update idletasks
+ $data(list) delete 0 end
+ # Make the dir list
+ if {$tcl_version >= 8.0} {
+ set sortmode -dictionary
+ } else {
+ set sortmode -ascii
+ }
+ foreach f [lsort $sortmode [glob -nocomplain .* *]] {
+ if {![string compare $f .]} {
+ continue
+ }
+ if {![string compare $f ..]} {
+ continue
+ }
+ if {[file isdir ./$f]} {
+ if {![info exists hasDoneDir($f)]} {
+ $data(list) insert end [format "(dir) %s" $f]
+ set hasDoneDir($f) 1
+ }
+ }
+ }
+ # Make the file list
+ #
+ if {![string compare $data(filter) *]} {
+ set files [lsort $sortmode \
+ [glob -nocomplain .* *]]
+ } else {
+ set files [lsort $sortmode \
+ [eval glob -nocomplain $data(filter)]]
+ }
+
+ set top 0
+ foreach f $files {
+ if {![file isdir ./$f]} {
+ if {![info exists hasDoneFile($f)]} {
+ $data(list) insert end [format " %s" $f]
+ set hasDoneFile($f) 1
+ }
+ }
+ }
+ $data(list) selection clear 0 end
+ $data(list) selection set 0
+ $data(list) activate 0
+ $data(list) yview 0
+ # Update the Directory: option menu
+ set list ""
+ set dir ""
+ foreach subdir [file split $data(selectPath)] {
+ set dir [file join $dir $subdir]
+ lappend list $dir
+ }
+ $data(dirMenu) delete 0 end
+ set var [format %s(selectPath) $dataName]
+ foreach path $list {
+ $data(dirMenu) add command -label $path -command [list set $var $path]
+ }
+ # Restore the PWD to the application's PWD
+ cd $appPWD
+}
+
+# ckFDialog_SetPathSilently --
+#
+# Sets data(selectPath) without invoking the trace procedure
+
+proc ckFDialog_SetPathSilently {w path} {
+ upvar #0 [winfo name $w] data
+ trace vdelete data(selectPath) w "ckFDialog_SetPath $w"
+ set data(selectPath) $path
+ trace variable data(selectPath) w "ckFDialog_SetPath $w"
+}
+
+# This proc gets called whenever data(selectPath) is set
+
+proc ckFDialog_SetPath {w name1 name2 op} {
+ if {[winfo exists $w]} {
+ upvar #0 [winfo name $w] data
+ ckFDialog_UpdateWhenIdle $w
+ }
+}
+
+# This proc gets called whenever data(filter) is set
+
+proc ckFDialog_SetFilter {w type} {
+ upvar #0 [winfo name $w] data
+ set data(filter) [lindex $type 1]
+ $data(typeMenuBtn) config -text [lindex $type 0] -indicatoron 0
+ ckFDialog_UpdateWhenIdle $w
+}
+
+# ckFDialogResolveFile --
+#
+# Interpret the user's text input in a file selection dialog.
+# Performs:
+#
+# (1) ~ substitution
+# (2) resolve all instances of . and ..
+# (3) check for non-existent files/directories
+# (4) check for chdir permissions
+#
+# Arguments:
+# context: the current directory you are in
+# text: the text entered by the user
+# defaultext: the default extension to add to files with no extension
+#
+# Return vaue:
+# [list $flag $directory $file]
+#
+# flag = OK : valid input
+# = PATTERN : valid directory/pattern
+# = PATH : the directory does not exist
+# = FILE : the directory exists by the file doesn't
+# exist
+# = CHDIR : Cannot change to the directory
+# = ERROR : Invalid entry
+#
+# directory : valid only if flag = OK or PATTERN or FILE
+# file : valid only if flag = OK or PATTERN
+#
+# directory may not be the same as context, because text may contain
+# a subdirectory name
+
+proc ckFDialogResolveFile {context text defaultext} {
+ set appPWD [pwd]
+ set path [ckFDialog_JoinFile $context $text]
+ if {[file ext $path] == ""} {
+ set path "$path$defaultext"
+ }
+ if {[catch {file exists $path}]} {
+ # This "if" block can be safely removed if the following code
+ # stop generating errors.
+ #
+ # file exists ~nonsuchuser
+ #
+ return [list ERROR $path ""]
+ }
+ if {[file exists $path]} {
+ if {[file isdirectory $path]} {
+ if {[catch {
+ cd $path
+ }]} {
+ return [list CHDIR $path ""]
+ }
+ set directory [pwd]
+ set file ""
+ set flag OK
+ cd $appPWD
+ } else {
+ if {[catch {
+ cd [file dirname $path]
+ }]} {
+ return [list CHDIR [file dirname $path] ""]
+ }
+ set directory [pwd]
+ set file [file tail $path]
+ set flag OK
+ cd $appPWD
+ }
+ } else {
+ set dirname [file dirname $path]
+ if {[file exists $dirname]} {
+ if {[catch {
+ cd $dirname
+ }]} {
+ return [list CHDIR $dirname ""]
+ }
+ set directory [pwd]
+ set file [file tail $path]
+ if {[regexp {[*]|[?]} $file]} {
+ set flag PATTERN
+ } else {
+ set flag FILE
+ }
+ cd $appPWD
+ } else {
+ set directory $dirname
+ set file [file tail $path]
+ set flag PATH
+ }
+ }
+ return [list $flag $directory $file]
+}
+
+# Gets called when the entry box gets keyboard focus. We clear the selection
+# from the icon list . This way the user can be certain that the input in the
+# entry box is the selection.
+
+proc ckFDialog_EntFocusIn {w} {
+ upvar #0 [winfo name $w] data
+ if {[string compare [$data(ent) get] ""]} {
+ $data(ent) selection from 0
+ $data(ent) selection to end
+ $data(ent) icursor end
+ } else {
+ $data(ent) selection clear
+ }
+ $data(list) selection clear 0 end
+ if {![string compare $data(type) open]} {
+ $data(okBtn) config -text "Open"
+ } else {
+ $data(okBtn) config -text "Save"
+ }
+}
+
+proc ckFDialog_EntFocusOut {w} {
+ upvar #0 [winfo name $w] data
+ $data(ent) selection clear
+}
+
+# Gets called when user presses Return in the "File name" entry.
+
+proc ckFDialog_ActivateEnt {w} {
+ upvar #0 [winfo name $w] data
+ set text [string trim [$data(ent) get]]
+ set list [ckFDialogResolveFile $data(selectPath) $text \
+ $data(-defaultextension)]
+ set flag [lindex $list 0]
+ set path [lindex $list 1]
+ set file [lindex $list 2]
+ switch -- $flag {
+ OK {
+ if {![string compare $file ""]} {
+ # user has entered an existing (sub)directory
+ set data(selectPath) $path
+ $data(ent) delete 0 end
+ } else {
+ ckFDialog_SetPathSilently $w $path
+ set data(selectFile) $file
+ ckFDialog_Done $w
+ }
+ }
+ PATTERN {
+ set data(selectPath) $path
+ set data(filter) $file
+ }
+ FILE {
+ if {![string compare $data(type) open]} {
+ ck_messageBox -type ok -parent $data(-parent) \
+ -message "File \"[file join $path $file]\" does not exist."
+ $data(ent) select from 0
+ $data(ent) select to end
+ $data(ent) icursor end
+ } else {
+ ckFDialog_SetPathSilently $w $path
+ set data(selectFile) $file
+ ckFDialog_Done $w
+ }
+ }
+ PATH {
+ ck_messageBox -type ok -parent $data(-parent) \
+ -message "Directory \"$path\" does not exist."
+ $data(ent) select from 0
+ $data(ent) select to end
+ $data(ent) icursor end
+ }
+ CHDIR {
+ ck_messageBox -type ok -parent $data(-parent) -message \
+ "Cannot change to the directory \"$path\".\nPermission denied."
+ $data(ent) select from 0
+ $data(ent) select to end
+ $data(ent) icursor end
+ }
+ ERROR {
+ ck_messageBox -type ok -parent $data(-parent) -message \
+ "Invalid file name \"$path\"."
+ $data(ent) select from 0
+ $data(ent) select to end
+ $data(ent) icursor end
+ }
+ }
+}
+
+# Gets called when user presses the Alt-s or Alt-o keys.
+
+proc ckFDialog_InvokeBtn {w key} {
+ upvar #0 [winfo name $w] data
+ if {![string compare [$data(okBtn) cget -text] $key]} {
+ ckButtonInvoke $data(okBtn)
+ }
+}
+
+# Gets called when user presses the "parent directory" button
+
+proc ckFDialog_UpDirCmd {w} {
+ upvar #0 [winfo name $w] data
+ if {[string compare $data(selectPath) "/"]} {
+ set data(selectPath) [file dirname $data(selectPath)]
+ }
+}
+
+# Join a file name to a path name. The "file join" command will break
+# if the filename begins with ~
+
+proc ckFDialog_JoinFile {path file} {
+ if {[string match {~*} $file] && [file exists $path/$file]} {
+ return [file join $path ./$file]
+ } else {
+ return [file join $path $file]
+ }
+}
+
+# Gets called when user presses the "OK" button
+
+proc ckFDialog_OkCmd {w} {
+ upvar #0 [winfo name $w] data
+ set text ""
+ set index [$data(list) curselection]
+ if {"$index" != ""} {
+ set text [string range [$data(list) get $index] 6 end]
+ }
+ if {[string compare $text ""]} {
+ set file [ckFDialog_JoinFile $data(selectPath) $text]
+ if {[file isdirectory $file]} {
+ ckFDialog_ListInvoke $w $text
+ return
+ }
+ }
+ ckFDialog_ActivateEnt $w
+}
+
+# Gets called when user presses the "Cancel" button
+
+proc ckFDialog_CancelCmd {w} {
+ upvar #0 [winfo name $w] data
+ global ckPriv
+ set ckPriv(selectFilePath) ""
+}
+
+# Gets called when user browses the listbox.
+
+proc ckFDialog_ListBrowse w {
+ upvar #0 [winfo name $w] data
+ set index [$data(list) curselection]
+ set text ""
+ if {[string length $index]} {
+ set text [string range [$data(list) get $index] 6 end]
+ }
+ if {[string length $text] == 0} {
+ return
+ }
+ set file [ckFDialog_JoinFile $data(selectPath) $text]
+ if {![file isdirectory $file]} {
+ $data(ent) delete 0 end
+ $data(ent) insert 0 $text
+ if {![string compare $data(type) open]} {
+ $data(okBtn) config -text "Open"
+ } else {
+ $data(okBtn) config -text "Save"
+ }
+ } else {
+ $data(okBtn) config -text "Open"
+ }
+}
+
+# Gets called when user invokes the lisbox.
+
+proc ckFDialog_ListInvoke {w {text {}}} {
+ upvar #0 [winfo name $w] data
+ if {[string length $text] == 0} {
+ set index [$data(list) curselection]
+ if {[string length $index]} {
+ set text [string range [$data(list) get $index] 6 end]
+ }
+ }
+ if {[string length $text] == 0} {
+ return
+ }
+ set file [ckFDialog_JoinFile $data(selectPath) $text]
+ if {[file isdirectory $file]} {
+ set appPWD [pwd]
+ if {[catch {cd $file}]} {
+ ck_messageBox -type ok -parent $data(-parent) -message \
+ "Cannot change to the directory \"$file\".\nPermission denied."
+ } else {
+ cd $appPWD
+ set data(selectPath) $file
+ }
+ } else {
+ set data(selectFile) $file
+ ckFDialog_Done $w
+ }
+}
+
+# ckFDialog_Done --
+#
+# Gets called when user has input a valid filename. Pops up a
+# dialog box to confirm selection when necessary. Sets the
+# ckPriv(selectFilePath) variable, which will break the "tkwait"
+# loop in ckFDialog and return the selected filename to the
+# script that calls ck_getOpenFile or ck_getSaveFile
+
+proc ckFDialog_Done {w {selectFilePath ""}} {
+ upvar #0 [winfo name $w] data
+ global ckPriv
+ if {![string compare $selectFilePath ""]} {
+ set selectFilePath [ckFDialog_JoinFile $data(selectPath) \
+ $data(selectFile)]
+ set ckPriv(selectFile) $data(selectFile)
+ set ckPriv(selectPath) $data(selectPath)
+ if {[file exists $selectFilePath] &&
+ ![string compare $data(type) save]} {
+ set reply [ck_messageBox -icon warning -type yesno\
+ -parent $data(-parent) -message "File\
+ \"$selectFilePath\" already exists.\nDo\
+ you want to overwrite it?"]
+ if {![string compare $reply "no"]} {
+ return
+ }
+ }
+ }
+ set ckPriv(selectFilePath) $selectFilePath
+}
+
--- /dev/null
+# clrpick.tcl --
+#
+# Color selection dialog.
+# standard color selection dialog.
+#
+# Copyright (c) 1996 Sun Microsystems, Inc.
+# Copyright (c) 1999 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+# ck_chooseColor --
+#
+# Create a color dialog and let the user choose a color. This function
+# should not be called directly. It is called by the tk_chooseColor
+# function when a native color selector widget does not exist
+
+proc ck_chooseColor args {
+ global ckPriv
+ set w .__ck__color
+ upvar #0 $w data
+ if {[winfo depth .] == 1} {
+ set data(colors) {black white}
+ set data(rcolors) {white black}
+ } else {
+ set data(colors) {black blue cyan green magenta red white yellow}
+ set data(rcolors) {white white white white white white black black}
+ }
+ ckColorDialog_Config $w $args
+
+ if {![winfo exists $w]} {
+ toplevel $w -class CkColorDialog -border {
+ ulcorner hline urcorner vline lrcorner hline llcorner vline
+ }
+ ckColorDialog_BuildDialog $w
+ }
+ place $w -relx 0.5 -rely 0.5 -anchor center
+
+ # Set the focus.
+
+ set oldFocus [focus]
+ focus $w.bot.ok
+
+ # Wait for the user to respond, then restore the focus and
+ # return the index of the selected button. Restore the focus
+ # before deleting the window, since otherwise the window manager
+ # may take the focus away so we can't redirect it. Finally,
+ # restore any grab that was in effect.
+
+ tkwait variable ckPriv(selectColor)
+ catch {focus $oldFocus}
+ destroy $w
+ unset data
+ return $ckPriv(selectColor)
+}
+
+# ckColorDialog_Config --
+#
+# Parses the command line arguments to tk_chooseColor
+#
+proc ckColorDialog_Config {w argList} {
+ global ckPriv
+ upvar #0 $w data
+ set specs {
+ {-initialcolor "" "" ""}
+ {-parent "" "" "."}
+ {-title "" "" "Color"}
+ }
+ tclParseConfigSpec $w $specs "" $argList
+ if {![string compare $data(-initialcolor) ""]} {
+ if {[info exists ckPriv(selectColor)] && \
+ [string compare $ckPriv(selectColor) ""]} {
+ set data(-initialcolor) $ckPriv(selectColor)
+ } else {
+ set data(-initialcolor) [. cget -background]
+ }
+ } elseif {[lsearch -exact $data(colors) $data(-initialcolor)] <= 0} {
+ error "illegal -initialcolor"
+ }
+ if {![winfo exists $data(-parent)]} {
+ error "bad window path name \"$data(-parent)\""
+ }
+}
+
+# ckColorDialog_BuildDialog --
+#
+# Build the dialog.
+#
+proc ckColorDialog_BuildDialog w {
+ upvar #0 $w data
+ label $w.title -text "Select Color"
+ pack $w.title -side top -fill x -pady 1
+ frame $w.top
+ pack $w.top -side top -fill x -padx 1
+ set count 0
+ foreach i $data(colors) {
+ radiobutton $w.top.$i -background $i -text $i -value $i \
+ -variable ${w}(finalColor) \
+ -foreground [lindex $data(rcolors) $count] \
+ -selectcolor [lindex $data(rcolors) $count]
+ if {[winfo depth .] > 1} {
+ $w.top.$i configure -activeforeground \
+ [$w.top.$i cget -background] -activeattributes bold \
+ -activebackground [$w.top.$i cget -foreground]
+ }
+ pack $w.top.$i -side top -fill x
+ incr count
+ }
+ frame $w.bot
+ pack $w.bot -side top -fill x -padx 1 -pady 1
+ button $w.bot.ok -text OK -width 8 -underline 0 \
+ -command [list ckColorDialog_OkCmd $w]
+ button $w.bot.cancel -text Cancel -width 8 -underline 0 \
+ -command [list ckColorDialog_CancelCmd $w]
+ pack $w.bot.ok $w.bot.cancel -side left -expand 1
+ # Accelerator bindings
+ bind $w <Escape> [list ckButtonInvoke $w.bot.cancel]
+ bind $w <c> [list ckButtonInvoke $w.bot.cancel]
+ bind $w <C> [list ckButtonInvoke $w.bot.cancel]
+ bind $w <o> [list ckButtonInvoke $w.bot.ok]
+ bind $w <O> [list ckButtonInvoke $w.bot.ok]
+ set data(finalColor) $data(-initialcolor)
+}
+
+proc ckColorDialog_OkCmd {w} {
+ global ckPriv
+ upvar #0 $w data
+ set ckPriv(selectColor) $data(finalColor)
+}
+
+proc ckColorDialog_CancelCmd {w} {
+ global ckPriv
+ set ckPriv(selectColor) ""
+}
+
--- /dev/null
+# comdlg.tcl --
+#
+# Some functions needed for the common dialog boxes. Probably need to go
+# in a different file.
+#
+# RCS: @(#) $Id: comdlg.tcl,v 1.1 2006-02-24 18:59:53 vitus Exp $
+#
+# Copyright (c) 1996 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+# tclParseConfigSpec --
+#
+# Parses a list of "-option value" pairs. If all options and
+# values are legal, the values are stored in
+# $data($option). Otherwise an error message is returned. When
+# an error happens, the data() array may have been partially
+# modified, but all the modified members of the data(0 array are
+# guaranteed to have valid values. This is different than
+# Tk_ConfigureWidget() which does not modify the value of a
+# widget record if any error occurs.
+#
+# Arguments:
+#
+# w = widget record to modify. Must be the pathname of a widget.
+#
+# specs = {
+# {-commandlineswitch resourceName ResourceClass defaultValue verifier}
+# {....}
+# }
+#
+# flags = currently unused.
+#
+# argList = The list of "-option value" pairs.
+#
+proc tclParseConfigSpec {w specs flags argList} {
+ upvar #0 $w data
+
+ # 1: Put the specs in associative arrays for faster access
+ #
+ foreach spec $specs {
+ if {[llength $spec] < 4} {
+ error "\"spec\" should contain 5 or 4 elements"
+ }
+ set cmdsw [lindex $spec 0]
+ set cmd($cmdsw) ""
+ set rname($cmdsw) [lindex $spec 1]
+ set rclass($cmdsw) [lindex $spec 2]
+ set def($cmdsw) [lindex $spec 3]
+ set verproc($cmdsw) [lindex $spec 4]
+ }
+
+ if {([llength $argList]%2) != 0} {
+ foreach {cmdsw value} $argList {
+ if {![info exists cmd($cmdsw)]} {
+ error "unknown option \"$cmdsw\", must be [tclListValidFlags cmd]"
+ }
+ }
+ error "value for \"[lindex $argList end]\" missing"
+ }
+
+ # 2: set the default values
+ #
+ foreach cmdsw [array names cmd] {
+ set data($cmdsw) $def($cmdsw)
+ }
+
+ # 3: parse the argument list
+ #
+ foreach {cmdsw value} $argList {
+ if {![info exists cmd($cmdsw)]} {
+ error "unknown option \"$cmdsw\", must be [tclListValidFlags cmd]"
+ }
+ set data($cmdsw) $value
+ }
+
+ # Done!
+}
+
+proc tclListValidFlags {v} {
+ upvar $v cmd
+
+ set len [llength [array names cmd]]
+ set i 1
+ set separator ""
+ set errormsg ""
+ foreach cmdsw [lsort [array names cmd]] {
+ append errormsg "$separator$cmdsw"
+ incr i
+ if {$i == $len} {
+ set separator " or "
+ } else {
+ set separator ", "
+ }
+ }
+ return $errormsg
+}
+
+# This procedure is used to sort strings in a case-insenstive mode.
+#
+proc tclSortNoCase {str1 str2} {
+ return [string compare [string toupper $str1] [string toupper $str2]]
+}
+
+
+# Gives an error if the string does not contain a valid integer
+# number
+#
+proc tclVerifyInteger {string} {
+ lindex {1 2 3} $string
+}
+
+# ckFDGetFileTypes --
+#
+# Process the string given by the -filetypes option of the file
+# dialogs. Similar to the C function TkGetFileFilters() on the Mac
+# and Windows platform.
+#
+proc ckFDGetFileTypes {string} {
+ foreach t $string {
+ if {[llength $t] < 2 || [llength $t] > 3} {
+ error "bad file type \"$t\", should be \"typeName {extension ?extensions ...?} ?{macType ?macTypes ...?}?\""
+ }
+ eval lappend [list fileTypes([lindex $t 0])] [lindex $t 1]
+ }
+
+ set types {}
+ foreach t $string {
+ set label [lindex $t 0]
+ set exts {}
+
+ if {[info exists hasDoneType($label)]} {
+ continue
+ }
+
+ set name "$label ("
+ set sep ""
+ foreach ext $fileTypes($label) {
+ if {![string compare $ext ""]} {
+ continue
+ }
+ regsub {^[.]} $ext "*." ext
+ if {![info exists hasGotExt($label,$ext)]} {
+ append name $sep$ext
+ lappend exts $ext
+ set hasGotExt($label,$ext) 1
+ }
+ set sep ,
+ }
+ append name ")"
+ lappend types [list $name $exts]
+
+ set hasDoneType($label) 1
+ }
+
+ return $types
+}
--- /dev/null
+#
+# command.tcl --
+#
+# This file defines the command dialog procedure.
+#
+# Copyright (c) 1995-1996 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+# ckCommand --
+# Create command window e.g. for interactive use of cwsh
+
+proc ckCommand {{w .ckCommand}} {
+ global ckPriv
+ if [winfo exists $w] {
+ raise $w
+ return
+ }
+ toplevel $w -class CommandDialog \
+ -border { ulcorner hline urcorner vline lrcorner hline llcorner vline }
+ place $w -relx 0.5 -rely 0.5 -relwidth 0.5 -relheight 0.5 -anchor center
+
+ label $w.title -text "Command dialog"
+ place $w.title -y 0 -relx 0.5 -bordermode ignore -anchor center
+
+ entry $w.entry
+ frame $w.sep0 -border hline -height 1
+ scrollbar $w.scroll -command "$w.output yview" -takefocus 0
+ text $w.output -yscrollcommand "$w.scroll set"
+ frame $w.sep1 -border hline -height 1
+ button $w.close -command "lower $w" -text Dismiss
+
+ pack $w.entry -side top -fill x
+ pack $w.sep0 -side top -fill x
+ pack $w.close -side bottom -ipadx 1
+ pack $w.sep1 -side bottom -fill x
+ pack $w.scroll -side right -fill y
+ pack $w.output -side left -fill both -expand 1
+
+ bind $w.entry <Return> "ckCommandRun $w"
+ bind $w.entry <Linefeed> "ckCommandRun $w"
+ bind $w.entry <Up> "ckCmdHist $w 1"
+ bind $w.entry <Down> "ckCmdHist $w -1"
+ bind $w.output <Tab> {focus [ck_focusNext %W] ; break}
+ bind $w.output <Control-X> "ckCommandRun $w \[$w.output get 1.0 end\]"
+ bind $w <Escape> "lower $w ; break"
+ bind $w <Control-U> "ckCmdToggleSize $w"
+ bind $w <Control-L> {update screen}
+
+ focus $w.entry
+
+ set ckPriv(cmdHistory) {}
+ set ckPriv(cmdHistCnt) -1
+ set ckPriv(cmdHistMax) 32
+}
+
+proc ckCmdToggleSize w {
+ if {[string first "-relwidth 1" [place info $w]] >= 0} {
+ place $w -relx 0.5 -rely 0.5 -relwidth 0.5 -relheight 0.5 \
+ -anchor center
+ } else {
+ place $w -relx 0.5 -rely 0.5 -relwidth 1.0 -relheight 1.0 \
+ -anchor center
+ }
+}
+
+proc ckCmdHist {w dir} {
+ global ckPriv
+ incr ckPriv(cmdHistCnt) $dir
+ if {$ckPriv(cmdHistCnt) < 0} {
+ set cmd ""
+ set ckPriv(cmdHistCnt) -1
+ } else {
+ if {$ckPriv(cmdHistCnt) >= [llength $ckPriv(cmdHistory)]} {
+ set ckPriv(cmdHistCnt) [expr [llength $ckPriv(cmdHistory)] - 1]
+ return
+ }
+ set cmd [lindex $ckPriv(cmdHistory) $ckPriv(cmdHistCnt)]
+ }
+ $w.entry delete 0 end
+ $w.entry insert end $cmd
+}
+
+proc ckCommandRun {w {cmd {}}} {
+ global errorInfo ckPriv
+ if {$cmd == ""} {
+ set cmd [string trim [$w.entry get]]
+ if {$cmd == ""} {
+ return
+ }
+ }
+ set code [catch {uplevel #0 $cmd} result]
+ if {$code == 0} {
+ set ckPriv(cmdHistory) [lrange [concat [list $cmd] \
+ $ckPriv(cmdHistory)] 0 $ckPriv(cmdHistMax)]
+ set ckPriv(cmdHistCnt) -1
+ }
+ $w.output delete 1.0 end
+ $w.output insert 1.0 $result
+ if $code { $w.output insert end "\n----\n$errorInfo" }
+ $w.output mark set insert 1.0
+ if {$code == 0} {
+ $w.entry delete 0 end
+ }
+}
--- /dev/null
+# dialog.tcl --
+#
+# This file defines the procedure ck_dialog, which creates a dialog
+# box containing a bitmap, a message, and one or more buttons.
+#
+# Copyright (c) 1992-1993 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#
+# ck_dialog:
+#
+# This procedure displays a dialog box, waits for a button in the dialog
+# to be invoked, then returns the index of the selected button.
+#
+# Arguments:
+# w - Window to use for dialog top-level.
+# title - Title to display in dialog's decorative frame.
+# text - Message to display in dialog.
+# default -
+# args - One or more strings to display in buttons across the
+# bottom of the dialog box.
+
+proc ck_dialog {w title text default args} {
+ global ckPriv
+ if {[llength $args] <= 0} {
+ return -1
+ }
+ catch {destroy $w}
+ toplevel $w -class Dialog \
+ -border {ulcorner hline urcorner vline lrcorner hline llcorner vline}
+ place $w -relx 0.5 -rely 0.5 -anchor center
+ if {[string length $title] > 0} {
+ label $w.title -text $title
+ pack $w.title -side top -fill x
+ frame $w.sep0 -border hline -height 1
+ pack $w.sep0 -side top -fill x
+ }
+ message $w.msg -text $text
+ pack $w.msg -side top
+ frame $w.sep1 -border hline -height 1
+ pack $w.sep1 -side top -fill x
+ frame $w.b
+ pack $w.b -side top -fill x
+ set i 0
+ foreach but $args {
+ button $w.b.b$i -text $but -command \
+ "set ckPriv(button) $i ; destroy $w"
+ pack $w.b.b$i -side left -ipadx 1 -expand 1
+ incr i
+ }
+ if {catch {set default [expr $default+0]} {
+ set default 0
+ }
+ if {[string length $default]&&$default >=0&& $default <$i} {
+ focus $w.b.b$default
+ } else {
+ focus $w.b.b0
+ }
+ tkwait window $w
+ return $ckPriv(button)
+}
--- /dev/null
+# entry.tcl --
+#
+# This file defines the default bindings for entry widgets and provides
+# procedures that help in implementing those bindings.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for entries.
+#-------------------------------------------------------------------------
+
+bind Entry <Left> {
+ ckEntrySetCursor %W [expr [%W index insert] - 1]
+}
+bind Entry <Right> {
+ ckEntrySetCursor %W [expr [%W index insert] + 1]
+}
+bind Entry <Home> {
+ ckEntrySetCursor %W 0
+}
+bind Entry <End> {
+ ckEntrySetCursor %W end
+}
+bind Entry <Delete> {
+ if [%W selection present] {
+ %W delete sel.first sel.last
+ } else {
+ %W delete insert
+ }
+}
+bind Entry <ASCIIDelete> {
+ if [%W selection present] {
+ %W delete sel.first sel.last
+ } else {
+ %W delete insert
+ }
+}
+bind Entry <BackSpace> {
+ ckEntryBackspace %W
+}
+bind Entry <Select> {
+ %W selection from insert
+}
+bind Entry <KeyPress> {
+ ckEntryInsert %W %A
+}
+bind Entry <Control> {# nothing}
+bind Entry <Escape> {# nothing}
+bind Entry <Return> {# nothing}
+bind Entry <Linefeed> {# nothing}
+bind Entry <Tab> {# nothing}
+bind Entry <BackTab> {# nothing}
+
+bind Entry <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+
+# Additional emacs-like bindings:
+
+bind Entry <Control-a> {
+ ckEntrySetCursor %W 0
+}
+bind Entry <Control-b> {
+ ckEntrySetCursor %W [expr [%W index insert] - 1]
+}
+bind Entry <Control-d> {
+ %W delete insert
+}
+bind Entry <Control-e> {
+ ckEntrySetCursor %W end
+}
+bind Entry <Control-f> {
+ ckEntrySetCursor %W [expr [%W index insert] + 1]
+}
+bind Entry <Control-h> {
+ ckEntryBackspace %W
+}
+bind Entry <Control-k> {
+ %W delete insert end
+}
+bind Entry <Control-t> {
+ ckEntryTranspose %W
+}
+
+# ckEntryKeySelect --
+# This procedure is invoked when stroking out selections using the
+# keyboard. It moves the cursor to a new position, then extends
+# the selection to that position.
+#
+# Arguments:
+# w - The entry window.
+# new - A new position for the insertion cursor (the cursor hasn't
+# actually been moved to this position yet).
+
+proc ckEntryKeySelect {w new} {
+ if ![$w selection present] {
+ $w selection from insert
+ $w selection to $new
+ } else {
+ $w selection adjust $new
+ }
+ $w icursor $new
+}
+
+# ckEntryInsert --
+# Insert a string into an entry at the point of the insertion cursor.
+# If there is a selection in the entry, and it covers the point of the
+# insertion cursor, then delete the selection before inserting.
+#
+# Arguments:
+# w - The entry window in which to insert the string
+# s - The string to insert (usually just a single character)
+
+proc ckEntryInsert {w s} {
+ if {$s == ""} return
+ catch {
+ set insert [$w index insert]
+ if {([$w index sel.first] <= $insert)
+ && ([$w index sel.last] >= $insert)} {
+ $w delete sel.first sel.last
+ }
+ }
+ $w insert insert $s
+ ckEntrySeeInsert $w
+}
+
+# ckEntryBackspace --
+# Backspace over the character just before the insertion cursor.
+# If backspacing would move the cursor off the left edge of the
+# window, reposition the cursor at about the middle of the window.
+#
+# Arguments:
+# w - The entry window in which to backspace.
+
+proc ckEntryBackspace w {
+ if [$w selection present] {
+ $w delete sel.first sel.last
+ } else {
+ set x [expr {[$w index insert] - 1}]
+ if {$x >= 0} {$w delete $x}
+ if {[$w index @0] >= [$w index insert]} {
+ set range [$w xview]
+ set left [lindex $range 0]
+ set right [lindex $range 1]
+ $w xview moveto [expr $left - ($right - $left)/2.0]
+ }
+ }
+}
+
+# ckEntrySeeInsert --
+# Make sure that the insertion cursor is visible in the entry window.
+# If not, adjust the view so that it is.
+#
+# Arguments:
+# w - The entry window.
+
+proc ckEntrySeeInsert w {
+ set c [$w index insert]
+ set left [$w index @0]
+ if {$left > $c} {
+ $w xview $c
+ return
+ }
+ set x [winfo width $w]
+ while {([$w index @$x] <= $c) && ($left < $c)} {
+ incr left
+ $w xview $left
+ }
+}
+
+# ckEntrySetCursor -
+# Move the insertion cursor to a given position in an entry. Also
+# clears the selection, if there is one in the entry, and makes sure
+# that the insertion cursor is visible.
+#
+# Arguments:
+# w - The entry window.
+# pos - The desired new position for the cursor in the window.
+
+proc ckEntrySetCursor {w pos} {
+ $w icursor $pos
+ $w selection clear
+ ckEntrySeeInsert $w
+}
+
+# ckEntryTranspose -
+# This procedure implements the "transpose" function for entry widgets.
+# It tranposes the characters on either side of the insertion cursor,
+# unless the cursor is at the end of the line. In this case it
+# transposes the two characters to the left of the cursor. In either
+# case, the cursor ends up to the right of the transposed characters.
+#
+# Arguments:
+# w - The entry window.
+
+proc ckEntryTranspose w {
+ set i [$w index insert]
+ if {$i < [$w index end]} {
+ incr i
+ }
+ set first [expr $i-2]
+ if {$first < 0} {
+ return
+ }
+ set new [string index [$w get] [expr $i-1]][string index [$w get] $first]
+ $w delete $first $i
+ $w insert insert $new
+ ckEntrySeeInsert $w
+}
--- /dev/null
+# entryx.tcl --
+#
+# This file defines the additional bindings for entry widgets and provides
+# procedures that help in implementing those bindings.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+# Copyright (c) 1995 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#-------------------------------------------------------------------------
+# Create extended entry widget.
+#-------------------------------------------------------------------------
+
+proc entryx {w args} {
+ global ckPriv errorInfo
+
+ set mode EntryNormal
+ set index [lsearch -glob $args -mod*]
+ set regexp ".*"
+ if {$index >= 0} {
+ set mode [lindex $args [expr $index + 1]]
+ switch -glob -- $mode {
+ in* {
+ set mode EntryInteger
+ set regexp {^[-+]?[0-9]*$}
+ }
+ un* {
+ set mode EntryUnsigned
+ set regexp {^[0-9]*$}
+ }
+ fl* {
+ set mode EntryFloat
+ set regexp {^[-+]?(([0-9]*(\.)[0-9]*)|([0-9]*))$}
+ }
+ re* {
+ set mode EntryRegexp
+ }
+ no* {
+ set mode EntryNormal
+ }
+ bo* {
+ set mode EntryBoolean
+ }
+ default {
+ return -code error -errorinfo \
+"bad mode \"$mode\": should be boolean, integer, unsigned, float, regexp or normal"
+ }
+ }
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+
+ set index [lsearch -glob $args -reg*]
+ if {$index >= 0} {
+ set regexp [lindex $args [expr $index + 1]]
+ if [catch {regexp -- $regexp foo}] {
+ return -code error -errorinfo "bad regexp \"$regexp\""
+ }
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+ set initial ""
+ set index [lsearch -glob $args -ini*]
+ if {$index >= 0} {
+ set initial [lindex $args [expr $index + 1]]
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+ set touchvar ""
+ set index [lsearch -glob $args -to*]
+ if {$index >= 0} {
+ set touchvar [lindex $args [expr $index + 1]]
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+ set default ""
+ set index [lsearch -glob $args -de*]
+ if {$index >= 0} {
+ set default [lindex $args [expr $index + 1]]
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+ if {$mode == "EntryBoolean"} {
+ set onval 1
+ set offval 0
+
+ set index [lsearch -glob $args -onv*]
+ if {$index >= 0} {
+ set onval [lindex $args [expr $index + 1]]
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+ set index [lsearch -glob $args -offv*]
+ if {$index >= 0} {
+ set offval [lindex $args [expr $index + 1]]
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+ }
+
+ set fieldwidth 100000
+ set index [lsearch -glob $args -fie*]
+ if {$index >= 0} {
+ set value [lindex $args [expr $index + 1]]
+ if {[catch {expr int($value)} fieldwidth] || $fieldwidth <= 0} {
+ return -code error -errorinfo "bad fieldwidth \"$value\""
+ }
+ set args [lreplace $args $index [expr $index + 1]]
+ }
+
+ if [catch {eval entry $w $args} ret] {
+ return -code error -errorinfo $errorInfo
+ }
+
+ set ckPriv(entryx$ret,fw) $fieldwidth
+ if {$touchvar != ""} {
+ upvar #0 $touchvar tv
+ if ![catch {set tv 0}] {
+ set ckPriv(entryx$ret,tv) $touchvar
+ }
+ }
+ set ckPriv(entryx$ret,re) $regexp
+ set ckPriv(entryx$ret,de) $default
+
+ if {$mode == "EntryBoolean"} {
+ set ckPriv(entryx$ret,t) [string toupper [string index $onval 0]]
+ set ckPriv(entryx$ret,f) [string toupper [string index $offval 0]]
+ }
+
+ bindtags $ret [list $ret $mode [winfo toplevel $ret] all]
+
+ if {$initial != ""} {
+ $ret delete 0 end
+ $ret insert end $initial
+ }
+
+ return $ret
+}
+
+#-------------------------------------------------------------------------
+# The code below creates the class bindings for extended entries.
+#-------------------------------------------------------------------------
+
+bind EntryInteger <Destroy> {ckEntryDestroy %W}
+bind EntryUnsigned <Destroy> {ckEntryDestroy %W}
+bind EntryFloat <Destroy> {ckEntryDestroy %W}
+bind EntryRegexp <Destroy> {ckEntryDestroy %W}
+bind EntryNormal <Destroy> {ckEntryDestroy %W}
+bind EntryBoolean <Destroy> {ckEntryDestroy %W}
+
+bind EntryInteger <FocusIn> {ckEntryFocus %W 1 Integer}
+bind EntryUnsigned <FocusIn> {ckEntryFocus %W 1 Unsigned}
+bind EntryFloat <FocusIn> {ckEntryFocus %W 1 Float}
+bind EntryRegexp <FocusIn> {ckEntryFocus %W 1 Regexp}
+bind EntryNormal <FocusIn> {ckEntryFocus %W 1 Normal}
+bind EntryBoolean <FocusIn> {ckEntryFocus %W 1 Boolean}
+
+bind EntryInteger <FocusOut> {ckEntryFocus %W 0 Integer}
+bind EntryUnsigned <FocusOut> {ckEntryFocus %W 0 Unsigned}
+bind EntryFloat <FocusOut> {ckEntryFocus %W 0 Float}
+bind EntryRegexp <FocusOut> {ckEntryFocus %W 0 Regexp}
+bind EntryNormal <FocusOut> {ckEntryFocus %W 0 Normal}
+bind EntryBoolean <FocusOut> {ckEntryFocus %W 0 Boolean}
+
+bind EntryInteger <Left> {ckEntryXSetCursor %W [expr [%W index insert] - 1]}
+bind EntryUnsigned <Left> {ckEntryXSetCursor %W [expr [%W index insert] - 1]}
+bind EntryFloat <Left> {ckEntryXSetCursor %W [expr [%W index insert] - 1]}
+bind EntryRegexp <Left> {ckEntryXSetCursor %W [expr [%W index insert] - 1]}
+bind EntryNormal <Left> {ckEntryXSetCursor %W [expr [%W index insert] - 1]}
+bind EntryBoolean <Left> {focus [ck_focusPrev %W]}
+
+bind EntryInteger <Right> {ckEntryXSetCursor %W [expr [%W index insert] + 1]}
+bind EntryUnsigned <Right> {ckEntryXSetCursor %W [expr [%W index insert] + 1]}
+bind EntryFloat <Right> {ckEntryXSetCursor %W [expr [%W index insert] + 1]}
+bind EntryRegexp <Right> {ckEntryXSetCursor %W [expr [%W index insert] + 1]}
+bind EntryNormal <Right> {ckEntryXSetCursor %W [expr [%W index insert] + 1]}
+bind EntryBoolean <Right> {focus [ck_focusNext %W]}
+
+bind EntryInteger <BackSpace> {ckEntryXBackspace %W}
+bind EntryUnsigned <BackSpace> {ckEntryXBackspace %W}
+bind EntryFloat <BackSpace> {ckEntryXBackspace %W}
+bind EntryRegexp <BackSpace> {ckEntryXBackspace %W}
+bind EntryNormal <BackSpace> {ckEntryXBackspace %W}
+bind EntryBoolean <BackSpace> {# nothing}
+
+bind EntryInteger <Control-h> {ckEntryXBackspace %W}
+bind EntryUnsigned <Control-h> {ckEntryXBackspace %W}
+bind EntryFloat <Control-h> {ckEntryXBackspace %W}
+bind EntryRegexp <Control-h> {ckEntryXBackspace %W}
+bind EntryNormal <Control-h> {ckEntryXBackspace %W}
+bind EntryBoolean <Control-h> {# nothing}
+
+bind EntryInteger <Delete> {ckEntryXDelete %W}
+bind EntryUnsigned <Delete> {ckEntryXDelete %W}
+bind EntryFloat <Delete> {ckEntryXDelete %W}
+bind EntryRegexp <Delete> {ckEntryXDelete %W}
+bind EntryNormal <Delete> {ckEntryXDelete %W}
+bind EntryBoolean <Delete> {# nothing}
+
+bind EntryInteger <ASCIIDelete> {ckEntryXDelete %W}
+bind EntryUnsigned <ASCIIDelete> {ckEntryXDelete %W}
+bind EntryFloat <ASCIIDelete> {ckEntryXDelete %W}
+bind EntryRegexp <ASCIIDelete> {ckEntryXDelete %W}
+bind EntryNormal <ASCIIDelete> {ckEntryXDelete %W}
+bind EntryBoolean <ASCIIDelete> {# nothing}
+
+bind EntryInteger <Home> {ckEntryXSetCursor %W 0}
+bind EntryUnsigned <Home> {ckEntryXSetCursor %W 0}
+bind EntryFloat <Home> {ckEntryXSetCursor %W 0}
+bind EntryRegexp <Home> {ckEntryXSetCursor %W 0}
+bind EntryNormal <Home> {ckEntryXSetCursor %W 0}
+bind EntryBoolean <Home> {# nothing}
+
+bind EntryInteger <End> {ckEntryXSetCursor %W [expr [%W index end] - 1]}
+bind EntryUnsigned <End> {ckEntryXSetCursor %W [expr [%W index end] - 1]}
+bind EntryFloat <End> {ckEntryXSetCursor %W [expr [%W index end] - 1]}
+bind EntryRegexp <End> {ckEntryXSetCursor %W [expr [%W index end] - 1]}
+bind EntryNormal <End> {ckEntryXSetCursor %W [expr [%W index end] - 1]}
+bind EntryBoolean <End> {# nothing}
+
+bind EntryInteger <Return> {focus [ck_focusNext %W]}
+bind EntryUnsigned <Return> {focus [ck_focusNext %W]}
+bind EntryFloat <Return> {focus [ck_focusNext %W]}
+bind EntryRegexp <Return> {focus [ck_focusNext %W]}
+bind EntryNormal <Return> {focus [ck_focusNext %W]}
+bind EntryBoolean <Return> {focus [ck_focusNext %W]}
+
+bind EntryInteger <Linefeed> {focus [ck_focusNext %W]}
+bind EntryUnsigned <Linefeed> {focus [ck_focusNext %W]}
+bind EntryFloat <Linefeed> {focus [ck_focusNext %W]}
+bind EntryRegexp <Linefeed> {focus [ck_focusNext %W]}
+bind EntryNormal <Linefeed> {focus [ck_focusNext %W]}
+bind EntryBoolean <Linefeed> {focus [ck_focusNext %W]}
+
+bind EntryInteger <Tab> {# nothing}
+bind EntryUnsigned <Tab> {# nothing}
+bind EntryFloat <Tab> {# nothing}
+bind EntryRegexp <Tab> {# nothing}
+bind EntryNormal <Tab> {# nothing}
+bind EntryBoolean <Tab> {# nothing}
+
+bind EntryInteger <BackTab> {# nothing}
+bind EntryUnsigned <BackTab> {# nothing}
+bind EntryFloat <BackTab> {# nothing}
+bind EntryRegexp <BackTab> {# nothing}
+bind EntryNormal <BackTab> {# nothing}
+bind EntryBoolean <BackTab> {# nothing}
+
+bind EntryInteger <Escape> {# nothing}
+bind EntryUnsigned <Escape> {# nothing}
+bind EntryFloat <Escape> {# nothing}
+bind EntryRegexp <Escape> {# nothing}
+bind EntryNormal <Escape> {# nothing}
+bind EntryBoolean <Escape> {# nothing}
+
+bind EntryInteger <Control> {# nothing}
+bind EntryUnsigned <Control> {# nothing}
+bind EntryFloat <Control> {# nothing}
+bind EntryRegexp <Control> {# nothing}
+bind EntryNormal <Control> {# nothing}
+bind EntryBoolean <Control> {# nothing}
+
+bind EntryInteger <KeyPress> {ckEntryInput %W %A}
+bind EntryUnsigned <KeyPress> {ckEntryInput %W %A}
+bind EntryFloat <KeyPress> {ckEntryInput %W %A}
+bind EntryRegexp <KeyPress> {ckEntryInput %W %A}
+bind EntryNormal <KeyPress> {ckEntryInput %W %A}
+bind EntryBoolean <KeyPress> {ckEntryBooleanInput %W %A}
+
+bind EntryInteger <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+bind EntryUnsigned <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+bind EntryFloat <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+bind EntryRegexp <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+bind EntryNormal <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+bind EntryBoolean <Button-1> {
+ if [ckFocusOK %W] {%W icursor @%x ; focus %W}
+}
+
+# ckEntryDestroy --
+# If entry has been destroyed, cleanup parts of global ckPriv array
+#
+# Arguments:
+# w - The entry window
+
+proc ckEntryDestroy w {
+ global ckPriv
+ unset ckPriv(entryx$w,fw)
+ unset ckPriv(entryx$w,re)
+ unset ckPriv(entryx$w,de)
+ catch {unset ckPriv(entryx%W,tv)}
+ catch {unset ckPriv(entryx%W,t)}
+ catch {unset ckPriv(entryx%W,f)}
+}
+
+# ckEntryTouched --
+# If entry has a touch variable assigned, this variable is asserted here.
+#
+# Arguments:
+# w - The entry window
+
+proc ckEntryTouched w {
+ global ckPriv
+ if [info exists ckPriv(entryx$w,tv)] {
+ upvar #0 $ckPriv(entryx$w,tv) var
+ set var 1
+ }
+}
+
+# ckEntryFocus --
+# For FocusIn set reverse on mono screens or swap foreground/background
+# on color screens and position insertion cursor in first column of entry.
+# For FocusOut restore attributes or colors.
+#
+# Arguments:
+# w - The entry window
+# focus - 1=FocusIn or 0=FocusOut
+# mode - Type of entryx, e.g. Integer etc.
+
+proc ckEntryFocus {w focus mode} {
+ global ckPriv
+ if {[winfo depth $w] == 1} {
+ if $focus {
+ set ckPriv(entryxAttr) [$w cget -attributes]
+ $w configure -attributes reverse
+ } else {
+ $w configure -attributes $ckPriv(entryxAttr)
+ }
+ } else {
+ if $focus {
+ set ckPriv(entryxFg) [$w cget -foreground]
+ set ckPriv(entryxBg) [$w cget -background]
+ $w configure -foreground $ckPriv(entryxBg) \
+ -background $ckPriv(entryxFg)
+ } else {
+ $w configure -foreground $ckPriv(entryxFg) \
+ -background $ckPriv(entryxBg)
+ }
+ }
+ if $focus {
+ $w icursor 0
+ ckEntryXSeeInsert $w
+ } else {
+ switch -glob -- $mode {
+ Integer - Unsigned {
+ set val [$w get]
+ $w delete 0 end
+ if [scan $val %d val] {
+ $w insert end $val
+ } else {
+ $w insert end $ckPriv(entryx$w,de)
+ }
+ }
+ Float {
+ set val [$w get]
+ $w delete 0 end
+ if [scan $val %f val] {
+ $w insert end $val
+ } else {
+ $w insert end $ckPriv(entryx$w,de)
+ }
+ }
+ }
+ }
+}
+
+# ckEntryXSetCursor -
+# Move the insertion cursor to a given position in an entry.
+# Makes sure that the insertion cursor is visible.
+#
+# Arguments:
+# w - The entry window.
+# pos - The desired new position for the cursor in the window.
+
+proc ckEntryXSetCursor {w pos} {
+ $w icursor $pos
+ ckEntryXSeeInsert $w
+}
+
+# ckEntryXSeeInsert --
+# Make sure that the insertion cursor is visible in the entry window.
+# If not, adjust the view so that it is. If the cursor is at the very
+# end of the fieldwidth, advance focus to next window.
+#
+# Arguments:
+# w - The entry window.
+
+proc ckEntryXSeeInsert w {
+ global ckPriv
+ set c [$w index insert]
+ set left [$w index @0]
+ if {$left > $c} {
+ $w xview $c
+ return
+ }
+ set x [winfo width $w]
+ while {([$w index @$x] <= $c) && ($left < $c)} {
+ incr left
+ $w xview $left
+ }
+ if {$c >= $ckPriv(entryx$w,fw)} {
+ $w icursor [expr $ckPriv(entryx$w,fw) - 1]
+ }
+ if {$c >= $ckPriv(entryx$w,fw)} {
+ set c [expr $ckPriv(entryx$w,fw) - 1]
+ $w icursor $c
+ $w xview [expr $c - $x]
+ }
+}
+
+# ckEntryXBackspace --
+# Backspace over the character just before the insertion cursor.
+# If backspacing would move the cursor off the left edge of the
+# window, reposition the cursor at about the middle of the window.
+#
+# Arguments:
+# w - The entry window in which to backspace.
+
+proc ckEntryXBackspace w {
+ set x [expr {[$w index insert] - 1}]
+ if {$x >= 0} {
+ $w delete $x
+ ckEntryTouched $w
+ }
+ if {[$w index @0] >= [$w index insert]} {
+ set range [$w xview]
+ set left [lindex $range 0]
+ set right [lindex $range 1]
+ $w xview moveto [expr $left - ($right - $left)/2.0]
+ }
+}
+
+# ckEntryXDelete --
+# Delete the character at the insertion cursor.
+#
+# Arguments:
+# w - The entry window in which to backspace.
+
+proc ckEntryXDelete w {
+ set a [$w index insert]
+ set b [$w index end]
+ if {$a != $b && $b != 0} {
+ $w delete $a
+ ckEntryTouched $w
+ }
+}
+
+# ckEntryBooleanInput --
+#
+# Arguments:
+# w - The entry window in which to insert the string
+# s - The string to insert
+
+proc ckEntryBooleanInput {w s} {
+ global ckPriv
+ set old [$w get]
+ set s [string toupper $s]
+ if {[string compare " " $s] == 0} {
+ if {[string compare $ckPriv(entryx$w,t) $old] == 0} {
+ set s $ckPriv(entryx$w,f)
+ } else {
+ set s $ckPriv(entryx$w,t)
+ }
+ } elseif {[string compare $ckPriv(entryx$w,t) $s] != 0 && \
+ [string compare $ckPriv(entryx$w,f) $s] != 0} {
+ return
+ }
+ if {[string compare $s $old] != 0} {
+ $w delete 0 end
+ $w insert 0 $s
+ $w icursor 0
+ ckEntryTouched $w
+ }
+}
+
+# ckEntryInput --
+#
+# Input string into entry (types Integer, Unsigned, Float, Regexp, Normal).
+# Handling of blanks is as follows:
+# 1. try to enter blank into the string, if result is a valid regexp
+# 2. otherwise try to delete rest of field, if result is a valid regexp
+# 3. ignore the blank input
+# Lower case characters are converted to upper case, if regexp denies
+# lower case characters.
+#
+# Arguments:
+# w - The entry window in which to insert the string
+# s - The string to insert
+
+proc ckEntryInput {w s} {
+ global ckPriv
+ if {$s == ""} return
+ set insert [$w index insert]
+ if {$insert >= $ckPriv(entryx$w,fw)} return
+ set save [$w get]
+ $w insert insert $s
+ $w delete insert
+ if {![regexp $ckPriv(entryx$w,re) [$w get]]} {
+ set ok 1
+ if {$s == " "} {
+ $w icursor [expr [$w index insert] - 1]
+ $w delete insert end
+ ckEntryTouched $w
+ ckEntryXSeeInsert $w
+ return
+ } elseif {[string match {[a-z]} $s]} {
+ $w icursor $insert
+ $w insert insert [string toupper $s]
+ $w delete insert
+ } else {
+ set ok 0
+ }
+ if {$ok} {
+ set ok [regexp $ckPriv(entryx$w,re) [$w get]]
+ }
+ if {!$ok} {
+ $w delete 0 end
+ $w insert end $save
+ $w icursor $insert
+ ckEntryXSeeInsert $w
+ bell
+ return
+ }
+ }
+ ckEntryTouched $w
+ ckEntryXSeeInsert $w
+}
--- /dev/null
+# focus.tcl --
+#
+# This file defines several procedures for managing the input
+# focus.
+#
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+set ckPriv(restrictWindow) {}
+
+# ck_focusNext --
+# This procedure returns the name of the next window after "w" in
+# "focus order" (the window that should receive the focus next if
+# Tab is typed in w). "Next" is defined by a pre-order search
+# of the current window and its descendants, with the stacking
+# order determining the order of siblings. The "-takefocus" options
+# on windows determine whether or not they should be skipped.
+#
+# Arguments:
+# w - Name of a window.
+
+proc ck_focusNext w {
+ global ckPriv
+ set cur $w
+ while 1 {
+
+ # Descend to just before the first child of the current widget.
+
+ set parent $cur
+ set children [winfo children $cur]
+ set i -1
+
+ # Look for the next sibling that isn't a top-level.
+
+ while 1 {
+ incr i
+ if {$i < [llength $children]} {
+ set cur [lindex $children $i]
+ if {[winfo toplevel $cur] == $cur} {
+ continue
+ } else {
+ break
+ }
+ }
+
+ # No more siblings, so go to the current widget's parent.
+ # If it's a top-level, break out of the loop, otherwise
+ # look for its next sibling.
+
+ set cur $parent
+ if {$parent == $ckPriv(restrictWindow)} break
+ if {[winfo toplevel $cur] == $cur} {
+ break
+ }
+ set parent [winfo parent $parent]
+ set children [winfo children $parent]
+ set i [lsearch -exact $children $cur]
+ }
+ if {($cur == $w) || [ckFocusOK $cur]} {
+ return $cur
+ }
+ }
+}
+
+# ck_focusPrev --
+# This procedure returns the name of the previous window before "w" in
+# "focus order" (the window that should receive the focus next if
+# Shift-Tab is typed in w). "Next" is defined by a pre-order search
+# of the current and its descendants, with the stacking
+# order determining the order of siblings. The "-takefocus" options
+# on windows determine whether or not they should be skipped.
+#
+# Arguments:
+# w - Name of a window.
+
+proc ck_focusPrev w {
+ global ckPriv
+ set cur $w
+ while 1 {
+
+ # Collect information about the current window's position
+ # among its siblings.
+
+ # Collect information about the current window's position
+ # among its siblings. Also, if the window is a top-level,
+ # then reposition to just after the last child of the window.
+
+ if {[winfo toplevel $cur] == $cur || \
+ $cur == $ckPriv(restrictWindow)} {
+ set parent $cur
+ set children [winfo children $cur]
+ set i [llength $children]
+ } else {
+ set parent [winfo parent $cur]
+ set children [winfo children $parent]
+ set i [lsearch -exact $children $cur]
+ }
+
+ # Go to the previous sibling, then descend to its last descendant
+ # (highest in stacking order. While doing this, ignore top-levels
+ # and their descendants. When we run out of descendants, go up
+ # one level to the parent.
+
+ while {$i > 0} {
+ incr i -1
+ set cur [lindex $children $i]
+ if {[winfo toplevel $cur] == $cur} {
+ continue
+ }
+ set parent $cur
+ set children [winfo children $parent]
+ set i [llength $children]
+ }
+ set cur $parent
+ if {($cur == $w) || [ckFocusOK $cur]} {
+ return $cur
+ }
+ }
+}
+
+# ckFocusOK --
+#
+# This procedure is invoked to decide whether or not to focus on
+# a given window. It returns 1 if it's OK to focus on the window,
+# 0 if it's not OK. The code first checks whether the window is
+# viewable. If not, then it never focuses on the window. Then it
+# checks the -takefocus option for the window and uses it if it's
+# set. If there's no -takefocus option, the procedure checks to
+# see if (a) the widget isn't disabled, and (b) it has some key
+# bindings. If all of these are true, then 1 is returned.
+#
+# Arguments:
+# w - Name of a window.
+
+proc ckFocusOK w {
+ global ckPriv
+ if {![winfo ismapped $w]} {return 0}
+ if {$ckPriv(restrictWindow) != ""} {
+ if {[winfo toplevel $w] == [winfo toplevel $ckPriv(restrictWindow)]} {
+ if {[string first $ckPriv(restrictWindow) $w] != 0} {return 0}
+ }
+ }
+ set code [catch {$w cget -takefocus} value]
+ if {($code == 0) && ($value != "")} {
+ if {$value == 0} {
+ return 0
+ } elseif {$value == 1} {
+ # For listboxes: don't take focus if nothing selectable
+ if {[winfo class $w] == "Listbox" && [$w size] == 0} {
+ return 0
+ }
+ return 1
+ } else {
+ set value [uplevel #0 $value $w]
+ if {$value != ""} {
+ return $value
+ }
+ }
+ }
+ set code [catch {$w cget -state} value]
+ if {($code == 0) && ($value == "disabled")} {
+ return 0
+ }
+ regexp Key|Focus "[bind $w] [bind [winfo class $w]]"
+}
+
+# ck_RestrictFocus --
+#
+# This procedure implements restriction of keyboard focus on a
+# subtree of the widget hierarchy not including toplevels within
+# that subtree.
+#
+# Argument formats:
+#
+# w - Name of a window on which the restriction is placed.
+# current - Returns the current restrict window or an empty
+# string, if there's no restriction active.
+# release w - If w is the current restrict window, the restriction is
+# released.
+
+proc ck_RestrictFocus args {
+ global ckPriv
+ set len [llength $args]
+ set opt [lindex $args 0]
+ switch -glob -- $opt {
+ .* {
+ if {$len != 1} {
+ error "bad # arguments: must be \"ck_RestrictFocus window\""
+ }
+ if {![winfo exists $opt]} {
+ error "bad window pathname \"$opt\""
+ }
+ if {![winfo ismapped $opt]} {
+ error "window \"$opt\" not viewable"
+ }
+ set ckPriv(restrictWindow) $opt
+ bind $opt <Destroy> {ck_Unrestrict %W}
+ bind $opt <Unmap> {ck_Unrestrict %W}
+ }
+ c* {
+ if {$len != 1} {
+ error "bad # arguments: must be \"ck_RestrictFocus current\""
+ }
+ return $ckPriv(restrictWindow)
+ }
+ r* {
+ if {$len != 2} {
+ error \
+ "bad # arguments: must be \"ck_RestrictFocus release window\""
+ }
+ set w [lindex $args 1]
+ ck_Unrestrict $w
+ }
+ default {
+ error "bad option \"$opt\": must be current or release"
+ }
+ }
+}
+
+# ck_Unrestrict --
+#
+# This procedure is invoked on Destroy or Unmap events in order
+# to release a restriction on a window.
+#
+# Arguments:
+# w - Name of a window.
+
+proc ck_Unrestrict w {
+ global ckPriv
+ if {$w == $ckPriv(restrictWindow)} {
+ set ckPriv(restrictWindow) {}
+ }
+ bind $w <Destroy> {# nothing}
+ bind $w <Unmap> {# nothing}
+}
--- /dev/null
+# keylpr --
+# Pretty print the contents of a keyed list
+#
+# Copyright (c) 1996 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+
+# Internal function which recursively collects the keys in a keyed list.
+# Don't remove the blank before "proc", it prevents "auto_mkindex" from
+# creating an entry for this procedure.
+
+ proc keylpr__ {list keynames prefix} {
+ upvar 1 $keynames names
+ upvar 1 $list l
+ set m [keylkeys l $prefix]
+ set x [string length $prefix]
+ foreach k $m {
+ if $x {
+ set p ${prefix}.$k
+ } else {
+ set p $k
+ }
+ if ![keylpr__ l names $p] {
+ lappend names $p
+ }
+ }
+ return [llength $m]
+}
+
+# Keyed list pretty printer, returns string with pretty printed keyed
+# list. Parameters
+#
+# keylname name of keyed list variable to be formatted
+# prefix (opt) component of keyed list to be formatted or empty
+# to format entire keyed list
+
+proc keylpr {keylname {prefix {}}} {
+ upvar 1 $keylname keylist
+ set keys {}
+ keylpr__ keylist keys $prefix
+ set maxlength 0
+ set keys [lsort -ascii $keys]
+ foreach k $keys {
+ set l [string length $k]
+ if {$l > $maxlength} {
+ set maxlength $l
+ }
+ }
+ set fmt "%-${maxlength}s %s"
+ set r {}
+ set q {}
+ foreach k $keys {
+ if ![catch {format $fmt $k [keylget keylist $k]} l] {
+ append r $q $l
+ set q "\n"
+ }
+ }
+ return $r
+}
--- /dev/null
+# listbox.tcl --
+#
+# This file defines the default bindings for Tk listbox widgets
+# and provides procedures that help in implementing those bindings.
+#
+# Copyright (c) 1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+#--------------------------------------------------------------------------
+# ckPriv elements used in this file:
+#
+# listboxPrev - The last element to be selected or deselected
+# during a selection operation.
+# listboxSelection - All of the items that were selected before the
+# current selection operation (such as a mouse
+# drag) started; used to cancel an operation.
+#--------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for listboxes.
+#-------------------------------------------------------------------------
+
+bind Listbox <Up> {
+ ckListboxUpDown %W -1
+}
+bind Listbox <Down> {
+ ckListboxUpDown %W 1
+}
+bind Listbox <Left> {
+ %W xview scroll -1 units
+}
+bind Listbox <Right> {
+ %W xview scroll 1 units
+}
+bind Listbox <Prior> {
+ %W yview scroll -1 pages
+ %W activate @0,0
+}
+bind Listbox <Next> {
+ %W yview scroll 1 pages
+ %W activate @0,0
+}
+bind Listbox <Home> {
+ %W xview moveto 0
+}
+bind Listbox <End> {
+ %W xview moveto 1
+}
+bind Listbox <space> {
+ ckListboxBeginSelect %W [%W index active]
+}
+bind Listbox <Select> {
+ ckListboxBeginSelect %W [%W index active]
+}
+bind Listbox <Escape> {
+ ckListboxCancel %W
+}
+bind Listbox <Button-1> {
+ focus %W
+ ckListboxBeginSelect %W [%W index @0,%y]
+}
+
+# ckListboxBeginSelect --
+#
+# This procedure is typically invoked on space 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 ckListboxBeginSelect {w el} {
+ global ckPriv
+ if {[$w cget -selectmode] == "multiple"} {
+ if [$w selection includes $el] {
+ $w selection clear $el
+ } else {
+ $w selection set $el
+ }
+ } else {
+ $w activate $el
+ $w selection clear 0 end
+ $w selection set $el
+ $w selection anchor $el
+ set ckPriv(listboxSelection) {}
+ set ckPriv(listboxPrev) $el
+ }
+}
+
+# ckListboxUpDown --
+#
+# 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 ckListboxUpDown {w amount} {
+ global ckPriv
+ $w activate [expr [$w index active] + $amount]
+ $w see active
+ switch [$w cget -selectmode] {
+ browse {
+ $w selection clear 0 end
+ $w selection set active
+ }
+ extended {
+ $w selection clear 0 end
+ $w selection set active
+ $w selection anchor active
+ set ckPriv(listboxPrev) [$w index active]
+ set ckPriv(listboxSelection) {}
+ }
+ }
+}
+
+# ckListboxCancel
+#
+# 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 ckListboxCancel w {
+ global ckPriv
+ if {[$w cget -selectmode] != "extended"} {
+ return
+ }
+ set first [$w index anchor]
+ set last $ckPriv(listboxPrev)
+ if {$first > $last} {
+ set tmp $first
+ set first $last
+ set last $tmp
+ }
+ $w selection clear $first $last
+ while {$first <= $last} {
+ if {[lsearch $ckPriv(listboxSelection) $first] >= 0} {
+ $w selection set $first
+ }
+ incr first
+ }
+}
--- /dev/null
+# menu.tcl --
+#
+# This file defines the default bindings for Tk menus and menubuttons.
+# It also implements keyboard traversal of menus and implements a few
+# other utility procedures related to menus.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#-------------------------------------------------------------------------
+# Elements of ckPriv that are used in this file:
+#
+# focus - Saves the focus during a menu selection operation.
+# Focus gets restored here when the menu is unposted.
+# postedMb - Name of the menubutton whose menu is currently
+# posted, or an empty string if nothing is posted
+# popup - If a menu has been popped up via ck_popup, this
+# gives the name of the menu. Otherwise this value
+# is empty.
+#-------------------------------------------------------------------------
+
+set ckPriv(postedMb) ""
+set ckPriv(popup) ""
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for menus
+# and menubuttons.
+#-------------------------------------------------------------------------
+
+bind Menubutton <FocusIn> {}
+bind Menubutton <space> {
+ if {[ckMbPost %W]} {
+ ckMenuFirstEntry [%W cget -menu]
+ }
+}
+bind Menubutton <Return> {
+ if {[ckMbPost %W]} {
+ ckMenuFirstEntry [%W cget -menu]
+ }
+}
+bind Menubutton <Linefeed> {
+ if {[ckMbPost %W]} {
+ ckMenuFirstEntry [%W cget -menu]
+ }
+}
+bind Menubutton <Button-1> {
+ if {[ckMbPost %W]} {
+ ckMenuFirstEntry [%W cget -menu]
+ }
+}
+
+bind Menu <space> {
+ ckMenuInvoke %W
+}
+bind Menu <Return> {
+ ckMenuInvoke %W
+}
+bind Menu <Linefeed> {
+ ckMenuInvoke %W
+}
+bind Menu <Escape> {
+ ckMenuEscape %W ; break
+}
+bind Menu <Left> {
+ ckMenuLeftRight %W left
+}
+bind Menu <Right> {
+ ckMenuLeftRight %W right
+}
+bind Menu <Up> {
+ ckMenuNextEntry %W -1
+}
+bind Menu <Down> {
+ ckMenuNextEntry %W +1
+}
+bind Menu <KeyPress> {
+ ckTraverseWithinMenu %W %A
+}
+bind Menu <Button-1> {
+ %W activate @%y
+ ckMenuInvoke %W
+}
+
+bind all <F10> {
+ ckFirstMenu %W
+}
+
+# ckMbPost --
+# Given a menubutton, this procedure does all the work of posting
+# its associated menu and unposting any other menu that is currently
+# posted.
+#
+# Arguments:
+# w - The name of the menubutton widget whose menu
+# is to be posted.
+# x, y - Root coordinates of cursor, used for positioning
+# option menus. If not specified, then the center
+# of the menubutton is used for an option menu.
+
+proc ckMbPost {w {x {}} {y {}}} {
+ global ckPriv
+ if {([$w cget -state] == "disabled") || ($w == $ckPriv(postedMb))} {
+ return 0
+ }
+ set menu [$w cget -menu]
+ if {$menu == ""} {
+ return 0
+ }
+ if ![string match $w.* $menu] {
+ error "can't post $menu: it isn't a descendant of $w"
+ }
+ set cur $ckPriv(postedMb)
+ if {$cur != ""} {
+ ckMenuUnpost {}
+ }
+ set ckPriv(postedMb) $w
+ set ckPriv(focus) [focus]
+ $menu activate none
+
+ # If this looks like an option menubutton then post the menu so
+ # that the current entry is on top of the mouse. Otherwise post
+ # the menu just below the menubutton, as for a pull-down.
+
+ if {([$w cget -indicatoron] == 1) && ([$w cget -textvariable] != "")} {
+ if {$y == ""} {
+ set x [expr [winfo rootx $w] + [winfo width $w]/2]
+ set y [expr [winfo rooty $w] + [winfo height $w]/2]
+ }
+ ckPostOverPoint $menu $x $y [ckMenuFindName $menu [$w cget -text]]
+ } else {
+ $menu post [winfo rootx $w] [expr [winfo rooty $w]+[winfo height $w]]
+ }
+ focus $menu
+ $w configure -state active
+ return 1
+}
+
+# ckMenuUnpost --
+# This procedure unposts a given menu, plus all of its ancestors up
+# to (and including) a menubutton, if any. It also restores various
+# values to what they were before the menu was posted, and releases
+# a grab if there's a menubutton involved. Special notes:
+# Be sure to enclose various groups of commands in "catch" so that
+# the procedure will complete even if the menubutton or the menu
+# has been deleted.
+#
+# Arguments:
+# menu - Name of a menu to unpost. Ignored if there
+# is a posted menubutton.
+
+proc ckMenuUnpost menu {
+ global ckPriv
+ set mb $ckPriv(postedMb)
+ catch {
+ if {$mb != ""} {
+ $mb configure -state normal
+ catch {focus $ckPriv(focus)}
+ set ckPriv(focus) ""
+ set menu [$mb cget -menu]
+ $menu unpost
+ set ckPriv(postedMb) {}
+ } elseif {[string length $ckPriv(popup)]} {
+ catch {focus $ckPriv(focus)}
+ set ckPriv(focus) ""
+ $ckPriv(popup) unpost
+ set ckPriv(popup) ""
+ }
+ }
+}
+
+# ckMenuInvoke --
+# This procedure is invoked when button 1 is released over a menu.
+# It invokes the appropriate menu action and unposts the menu if
+# it came from a menubutton.
+#
+# Arguments:
+# w - Name of the menu widget.
+
+proc ckMenuInvoke w {
+ if {[$w type active] == "cascade"} {
+ $w postcascade active
+ set menu [$w entrycget active -menu]
+ ckMenuFirstEntry $menu
+ } else {
+ ckMenuUnpost $w
+ uplevel #0 [list $w invoke active]
+ }
+}
+
+# ckMenuEscape --
+# This procedure is invoked for the Cancel (or Escape) key. It unposts
+# the given menu and, if it is the top-level menu for a menu button,
+# unposts the menu button as well.
+#
+# Arguments:
+# menu - Name of the menu window.
+
+proc ckMenuEscape menu {
+ if {[winfo class [winfo parent $menu]] != "Menu"} {
+ ckMenuUnpost $menu
+ } else {
+ ckMenuLeftRight $menu -1
+ }
+}
+
+# ckMenuLeftRight --
+# This procedure is invoked to handle "left" and "right" traversal
+# motions in menus. It traverses to the next menu in a menu bar,
+# or into or out of a cascaded menu.
+#
+# Arguments:
+# menu - The menu that received the keyboard
+# event.
+# direction - Direction in which to move: "left" or "right"
+
+proc ckMenuLeftRight {menu direction} {
+ global ckPriv
+
+ # First handle traversals into and out of cascaded menus.
+
+ if {$direction == "right"} {
+ set count 1
+ if {[$menu type active] == "cascade"} {
+ $menu postcascade active
+ set m2 [$menu entrycget active -menu]
+ if {$m2 != ""} {
+ ckMenuFirstEntry $m2
+ }
+ return
+ }
+ } else {
+ set count -1
+ set m2 [winfo parent $menu]
+ if {[winfo class $m2] == "Menu"} {
+ focus $m2
+ $m2 postcascade none
+ return
+ }
+ }
+
+ # Can't traverse into or out of a cascaded menu. Go to the next
+ # or previous menubutton, if that makes sense.
+
+ set w $ckPriv(postedMb)
+ if {$w == ""} {
+ return
+ }
+ set buttons [winfo children [winfo parent $w]]
+ set length [llength $buttons]
+ set i [expr [lsearch -exact $buttons $w] + $count]
+ while 1 {
+ while {$i < 0} {
+ incr i $length
+ }
+ while {$i >= $length} {
+ incr i -$length
+ }
+ set mb [lindex $buttons $i]
+ if {([winfo class $mb] == "Menubutton")
+ && ([$mb cget -state] != "disabled")
+ && ([$mb cget -menu] != "")
+ && ([[$mb cget -menu] index last] != "none")} {
+ break
+ }
+ if {$mb == $w} {
+ return
+ }
+ incr i $count
+ }
+ if {[ckMbPost $mb]} {
+ ckMenuFirstEntry [$mb cget -menu]
+ }
+}
+
+# ckMenuNextEntry --
+# Activate the next higher or lower entry in the posted menu,
+# wrapping around at the ends. Disabled entries are skipped.
+#
+# Arguments:
+# menu - Menu window that received the keystroke.
+# count - 1 means go to the next lower entry,
+# -1 means go to the next higher entry.
+
+proc ckMenuNextEntry {menu count} {
+ global ckPriv
+ if {[$menu index last] == "none"} {
+ return
+ }
+ set length [expr [$menu index last]+1]
+ set active [$menu index active]
+ if {$active == "none"} {
+ set i 0
+ } else {
+ set i [expr $active + $count]
+ }
+ while 1 {
+ while {$i < 0} {
+ incr i $length
+ }
+ while {$i >= $length} {
+ incr i -$length
+ }
+ if {[catch {$menu entrycget $i -state} state] == 0} {
+ if {$state != "disabled"} {
+ break
+ }
+ }
+ if {$i == $active} {
+ return
+ }
+ incr i $count
+ }
+ $menu activate $i
+}
+
+# ckMenuFind --
+# This procedure searches the entire window hierarchy under w for
+# a menubutton that isn't disabled and whose underlined character
+# is "char". It returns the name of that window, if found, or an
+# empty string if no matching window was found. If "char" is an
+# empty string then the procedure returns the name of the first
+# menubutton found that isn't disabled.
+#
+# Arguments:
+# w - Name of window where key was typed.
+# char - Underlined character to search for;
+# may be either upper or lower case, and
+# will match either upper or lower case.
+
+proc ckMenuFind {w char} {
+ global ckPriv
+ set char [string tolower $char]
+
+ foreach child [winfo child $w] {
+ switch [winfo class $child] {
+ Menubutton {
+ set char2 [string index [$child cget -text] \
+ [$child cget -underline]]
+ if {([string compare $char [string tolower $char2]] == 0)
+ || ($char == "")} {
+ if {[$child cget -state] != "disabled"} {
+ return $child
+ }
+ }
+ }
+ Frame {
+ set match [ckMenuFind $child $char]
+ if {$match != ""} {
+ return $match
+ }
+ }
+ }
+ }
+ return {}
+}
+
+# ckFirstMenu --
+# This procedure traverses to the first menubutton in the toplevel
+# for a given window, and posts that menubutton's menu.
+#
+# Arguments:
+# w - Name of a window. Selects which toplevel
+# to search for menubuttons.
+
+proc ckFirstMenu w {
+ set w [ckMenuFind [winfo toplevel $w] ""]
+ if {$w != ""} {
+ if {[ckMbPost $w]} {
+ ckMenuFirstEntry [$w cget -menu]
+ }
+ }
+}
+
+# ckTraverseWithinMenu
+# This procedure implements keyboard traversal within a menu. It
+# searches for an entry in the menu that has "char" underlined. If
+# such an entry is found, it is invoked and the menu is unposted.
+#
+# Arguments:
+# w - The name of the menu widget.
+# char - The character to look for; case is
+# ignored. If the string is empty then
+# nothing happens.
+
+proc ckTraverseWithinMenu {w char} {
+ if {$char == ""} {
+ return
+ }
+ set char [string tolower $char]
+ set last [$w index last]
+ if {$last == "none"} {
+ return
+ }
+ for {set i 0} {$i <= $last} {incr i} {
+ if [catch {set char2 [string index \
+ [$w entrycget $i -label] \
+ [$w entrycget $i -underline]]}] {
+ continue
+ }
+ if {[string compare $char [string tolower $char2]] == 0} {
+ if {[$w type $i] == "cascade"} {
+ $w postcascade $i
+ $w activate $i
+ set m2 [$w entrycget $i -menu]
+ if {$m2 != ""} {
+ tkMenuFirstEntry $m2
+ }
+ } else {
+ ckMenuUnpost $w
+ uplevel #0 [list $w invoke $i]
+ }
+ return
+ }
+ }
+}
+
+# ckMenuFirstEntry --
+# Given a menu, this procedure finds the first entry that isn't
+# disabled or a tear-off or separator, and activates that entry.
+# However, if there is already an active entry in the menu (e.g.,
+# because of a previous call to tkPostOverPoint) then the active
+# entry isn't changed. This procedure also sets the input focus
+# to the menu.
+#
+# Arguments:
+# menu - Name of the menu window (possibly empty).
+
+proc ckMenuFirstEntry menu {
+ if {$menu == ""} {
+ return
+ }
+ focus $menu
+ if {[$menu index active] != "none"} {
+ return
+ }
+ set last [$menu index last]
+ if {$last == "none"} {
+ return
+ }
+ for {set i 0} {$i <= $last} {incr i} {
+ if {([catch {set state [$menu entrycget $i -state]}] == 0)
+ && ($state != "disabled")} {
+ $menu activate $i
+ return
+ }
+ }
+}
+
+# ckMenuFindName --
+# Given a menu and a text string, return the index of the menu entry
+# that displays the string as its label. If there is no such entry,
+# return an empty string. This procedure is tricky because some names
+# like "active" have a special meaning in menu commands, so we can't
+# always use the "index" widget command.
+#
+# Arguments:
+# menu - Name of the menu widget.
+# s - String to look for.
+
+proc ckMenuFindName {menu s} {
+ set i ""
+ if {![regexp {^active$|^last$|^none$|^[0-9]|^@} $s]} {
+ catch {set i [$menu index $s]}
+ return $i
+ }
+ set last [$menu index last]
+ if {$last == "none"} {
+ return
+ }
+ for {set i 0} {$i <= $last} {incr i} {
+ if ![catch {$menu entrycget $i -label} label] {
+ if {$label == $s} {
+ return $i
+ }
+ }
+ }
+ return ""
+}
+
+# ckPostOverPoint --
+# This procedure posts a given menu such that a given entry in the
+# menu is centered over a given point in the root window. It also
+# activates the given entry.
+#
+# Arguments:
+# menu - Menu to post.
+# x, y - Root coordinates of point.
+# entry - Index of entry within menu to center over (x,y).
+# If omitted or specified as {}, then the menu's
+# upper-left corner goes at (x,y).
+
+proc ckPostOverPoint {menu x y {entry {}}} {
+ if {$entry != {}} {
+ if {$entry == [$menu index last]} {
+ incr y [expr -([$menu yposition $entry] \
+ + [winfo reqheight $menu])/2]
+ } else {
+ incr y [expr -([$menu yposition $entry] \
+ + [$menu yposition [expr $entry+1]])/2]
+ }
+ incr x [expr -[winfo reqwidth $menu]/2]
+ }
+ $menu post $x $y
+ if {($entry != {}) && ([$menu entrycget $entry -state] != "disabled")} {
+ $menu activate $entry
+ }
+}
+
+# ck_popup --
+# This procedure pops up a menu and sets things up for traversing
+# the menu and its submenus.
+#
+# Arguments:
+# menu - Name of the menu to be popped up.
+# x, y - Root coordinates at which to pop up the
+# menu.
+# entry - Index of a menu entry to center over (x,y).
+# If omitted or specified as {}, then menu's
+# upper-left corner goes at (x,y).
+
+proc ck_popup {menu x y {entry {}}} {
+ global ckPriv
+ if {($ckPriv(popup) != "") || ($ckPriv(postedMb) != "")} {
+ ckMenuUnpost {}
+ }
+ ckPostOverPoint $menu $x $y $entry
+ set ckPriv(focus) [focus]
+ set ckPriv(popup) $menu
+ focus $menu
+}
--- /dev/null
+# msgbox.tcl --
+#
+# Implements messageboxes.
+#
+# Copyright (c) 1994-1997 Sun Microsystems, Inc.
+# Copyright (c) 1999 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+
+# ck_messageBox --
+#
+# Pops up a messagebox with an application-supplied message with
+# an icon and a list of buttons.
+# See the user documentation for details on what ck_messageBox does.
+
+proc ck_messageBox args {
+ global ckPriv
+ set w ckPrivMsgBox
+ upvar #0 $w data
+ set specs {
+ {-default "" "" ""}
+ {-icon "" "" "info"}
+ {-message "" "" ""}
+ {-parent "" "" .}
+ {-title "" "" ""}
+ {-type "" "" "ok"}
+ }
+ tclParseConfigSpec $w $specs "" $args
+ if {[lsearch {info warning error question} $data(-icon)] == -1} {
+ error "invalid icon \"$data(-icon)\", must be error, info, question or warning"
+ }
+ if {![winfo exists $data(-parent)]} {
+ error "bad window path name \"$data(-parent)\""
+ }
+ switch -- $data(-type) {
+ abortretryignore {
+ set buttons {
+ {abort -width 6 -text Abort -underline 0}
+ {retry -width 6 -text Retry -underline 0}
+ {ignore -width 6 -text Ignore -underline 0}
+ }
+ }
+ ok {
+ set buttons {
+ {ok -width 6 -text OK -underline 0}
+ }
+ if {$data(-default) == ""} {
+ set data(-default) "ok"
+ }
+ }
+ okcancel {
+ set buttons {
+ {ok -width 6 -text OK -underline 0}
+ {cancel -width 6 -text Cancel -underline 0}
+ }
+ }
+ retrycancel {
+ set buttons {
+ {retry -width 6 -text Retry -underline 0}
+ {cancel -width 6 -text Cancel -underline 0}
+ }
+ }
+ yesno {
+ set buttons {
+ {yes -width 6 -text Yes -underline 0}
+ {no -width 6 -text No -underline 0}
+ }
+ }
+ yesnocancel {
+ set buttons {
+ {yes -width 6 -text Yes -underline 0}
+ {no -width 6 -text No -underline 0}
+ {cancel -width 6 -text Cancel -underline 0}
+ }
+ }
+ default {
+ error "invalid message box type \"$data(-type)\", must be abortretryignore, ok, okcancel, retrycancel, yesno or yesnocancel"
+ }
+ }
+ if {[string compare $data(-default) ""]} {
+ set valid 0
+ foreach btn $buttons {
+ if {![string compare [lindex $btn 0] $data(-default)]} {
+ set valid 1
+ break
+ }
+ }
+ if {!$valid} {
+ error "invalid default button \"$data(-default)\""
+ }
+ }
+ # 2. Set the dialog to be a child window of $parent
+ if {[string compare $data(-parent) .]} {
+ set w $data(-parent).__ck__messagebox
+ } else {
+ set w .__ck__messagebox
+ }
+
+ # 3. Create the top-level window and divide it into top
+ # and bottom parts.
+ catch {destroy $w}
+ toplevel $w -class Dialog \
+ -border { ulcorner hline urcorner vline lrcorner hline llcorner vline }
+ place $w -relx 0.5 -rely 0.5 -anchor center
+ label $w.title -text $data(-title)
+ pack $w.title -side top -fill x
+ frame $w.bot
+ pack $w.bot -side bottom -fill both
+ frame $w.top
+ pack $w.top -side top -fill both -expand 1
+ # 4. Fill the top part with bitmap and message (use the option
+ # database for -wraplength so that it can be overridden by
+ # the caller).
+ message $w.top.msg -text $data(-message) -aspect 1000
+ pack $w.top.msg -side right -expand 1 -fill both -padx 1 -pady 1
+ # 5. Create a row of buttons at the bottom of the dialog.
+ set i 0
+ foreach but $buttons {
+ set name [lindex $but 0]
+ set opts [lrange $but 1 end]
+ if {![string compare $opts {}]} {
+ # Capitalize the first letter of $name
+ set capName \
+ [string toupper \
+ [string index $name 0]][string range $name 1 end]
+ set opts [list -text $capName]
+ }
+ eval button $w.bot.$name $opts \
+ -command [list [list set ckPriv(button) $name]]
+ pack $w.bot.$name -side left -expand 1 -padx 1 -pady 1
+ # create the binding for the key accelerator, based on the underline
+ set underIdx [$w.bot.$name cget -underline]
+ if {$underIdx >= 0} {
+ set key [string index [$w.bot.$name cget -text] $underIdx]
+ bind $w [string tolower $key] [list $w.bot.$name invoke]
+ bind $w [string toupper $key] [list $w.bot.$name invoke]
+ }
+ incr i
+ }
+ # 6. Create a binding for <Return> on the dialog if there is a
+ # default button.
+ if {[string compare $data(-default) ""]} {
+ bind $w <Return> "ckButtonInvoke $w.bot.$data(-default) ; break"
+ bind $w <Linefeed> "ckButtonInvoke $w.bot.$data(-default) ; break"
+ }
+ # 7. Claim the focus.
+ set oldFocus [focus]
+ if {[string compare $data(-default) ""]} {
+ focus $w.bot.$data(-default)
+ } else {
+ focus [lindex [winfo children $w.bot] 0]
+ }
+ # 8. Wait for the user to respond, then restore the focus and
+ # return the index of the selected button. Restore the focus
+ # before deleting the window, since otherwise the window manager
+ # may take the focus away so we can't redirect it. Finally,
+ # restore any grab that was in effect.
+ tkwait variable ckPriv(button)
+ catch {focus $oldFocus}
+ destroy $w
+ return $ckPriv(button)
+}
--- /dev/null
+# optMenu.tcl --
+#
+# This file defines the procedure ck_optionMenu, which creates
+# an option button and its associated menu.
+#
+# Copyright (c) 1994 The Regents of the University of California.
+# Copyright (c) 1994 Sun Microsystems, Inc.
+# Copyright (c) 1999 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+# ck_optionMenu --
+# This procedure creates an option button named $w and an associated
+# menu. Together they provide the functionality of Motif option menus:
+# they can be used to select one of many values, and the current value
+# appears in the global variable varName, as well as in the text of
+# the option menubutton. The name of the menu is returned as the
+# procedure's result, so that the caller can use it to change configuration
+# options on the menu or otherwise manipulate it.
+#
+# Arguments:
+# w - The name to use for the menubutton.
+# varName - Global variable to hold the currently selected value.
+# firstValue - First of legal values for option (must be >= 1).
+# args - Any number of additional values.
+
+proc ck_optionMenu {w varName firstValue args} {
+ upvar #0 $varName var
+ if {![info exists var]} {
+ set var $firstValue
+ }
+ set width [string length $firstValue]
+ foreach i $args {
+ set l [string length $i]
+ if {$l > $width} {
+ set width $l
+ }
+ }
+ incr width 2
+ menubutton $w -textvariable $varName -menu $w.menu \
+ -anchor c -takefocus 1 -width $width
+ bind $w <FocusIn> {
+ if {[%W cget -state] != "disabled"} {
+ %W configure -state active
+ update idletasks
+ }
+ }
+ bind $w <FocusOut> {
+ if {[%W cget -state] != "disabled"} {
+ %W configure -state normal
+ update idletasks
+ }
+ }
+ menu $w.menu \
+ -border { ulcorner hline urcorner vline lrcorner hline llcorner vline }
+ $w.menu add radiobutton -label $firstValue -variable $varName
+ foreach i $args {
+ $w.menu add radiobutton -label $i -variable $varName
+ }
+ return $w.menu
+}
--- /dev/null
+# parray:
+# Print the contents of a global array in command window.
+#
+# Copyright (c) 1991-1993 The Regents of the University of California.
+# Copyright (c) 1994 Sun Microsystems, Inc.
+# Copyright (c) 1995 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+proc parray {a {pattern *}} {
+ upvar 1 $a array
+ if ![array exists array] {
+ error "\"$a\" isn't an array"
+ }
+ set maxl 0
+ foreach name [lsort [array names array $pattern]] {
+ if {[string length $name] > $maxl} {
+ set maxl [string length $name]
+ }
+ }
+ set result ""
+ set maxl [expr {$maxl + [string length $a] + 2}]
+ foreach name [lsort [array names array $pattern]] {
+ set nameString [format %s(%s) $a $name]
+ append result \
+ [format "set %-*s %s\n" $maxl $nameString [list $array($name)]]
+ }
+ return $result
+}
--- /dev/null
+# scrollbar.tcl --
+#
+# This file defines the default bindings for Tk scrollbar widgets.
+# It also provides procedures that help in implementing the bindings.
+#
+# Copyright (c) 1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for scrollbars.
+#-------------------------------------------------------------------------
+
+bind Scrollbar <Up> {
+ ckScrollByUnits %W v -1
+}
+bind Scrollbar <Down> {
+ ckScrollByUnits %W v 1
+}
+bind Scrollbar <Left> {
+ ckScrollByUnits %W h -1
+}
+bind Scrollbar <Right> {
+ ckScrollByUnits %W h 1
+}
+bind Scrollbar <Prior> {
+ ckScrollByPages %W hv -1
+}
+bind Scrollbar <Next> {
+ ckScrollByPages %W hv 1
+}
+bind Scrollbar <Home> {
+ ckScrollToPos %W 0
+}
+bind Scrollbar <End> {
+ ckScrollToPos %W 1
+}
+bind Scrollbar <Button-1> {
+ ckScrollByButton %W %x %y
+}
+
+bind Scrollbar <FocusIn> {%W activate}
+bind Scrollbar <FocusOut> {%W deactivate}
+
+# ckScrollByUnits --
+# This procedure tells the scrollbar's associated widget to scroll up
+# or down by a given number of units. It notifies the associated widget
+# in different ways for old and new command syntaxes.
+#
+# Arguments:
+# w - The scrollbar widget.
+# orient - Which kinds of scrollbars this applies to: "h" for
+# horizontal, "v" for vertical, "hv" for both.
+# amount - How many units to scroll: typically 1 or -1.
+
+proc ckScrollByUnits {w orient amount} {
+ set cmd [$w cget -command]
+ if {($cmd == "") || ([string first \
+ [string index [$w cget -orient] 0] $orient] < 0)} {
+ return
+ }
+ set info [$w get]
+ if {[llength $info] == 2} {
+ uplevel #0 $cmd scroll $amount units
+ } else {
+ uplevel #0 $cmd [expr [lindex $info 2] + $amount]
+ }
+}
+
+# ckScrollByPages --
+# This procedure tells the scrollbar's associated widget to scroll up
+# or down by a given number of screenfuls. It notifies the associated
+# widget in different ways for old and new command syntaxes.
+#
+# Arguments:
+# w - The scrollbar widget.
+# orient - Which kinds of scrollbars this applies to: "h" for
+# horizontal, "v" for vertical, "hv" for both.
+# amount - How many screens to scroll: typically 1 or -1.
+
+proc ckScrollByPages {w orient amount} {
+ set cmd [$w cget -command]
+ if {($cmd == "") || ([string first \
+ [string index [$w cget -orient] 0] $orient] < 0)} {
+ return
+ }
+ set info [$w get]
+ if {[llength $info] == 2} {
+ uplevel #0 $cmd scroll $amount pages
+ } else {
+ uplevel #0 $cmd [expr [lindex $info 2] + $amount*([lindex $info 1] - 1)]
+ }
+}
+
+# ckScrollToPos --
+# This procedure tells the scrollbar's associated widget to scroll to
+# a particular location, given by a fraction between 0 and 1. It notifies
+# the associated widget in different ways for old and new command syntaxes.
+#
+# Arguments:
+# w - The scrollbar widget.
+# pos - A fraction between 0 and 1 indicating a desired position
+# in the document.
+
+proc ckScrollToPos {w pos} {
+ set cmd [$w cget -command]
+ if {($cmd == "")} {
+ return
+ }
+ set info [$w get]
+ if {[llength $info] == 2} {
+ uplevel #0 $cmd moveto $pos
+ } else {
+ uplevel #0 $cmd [expr round([lindex $info 0]*$pos)]
+ }
+}
+
+# ckScrollByButton --
+# This procedure is invoked for button presses on any element of the
+# scrollbar.
+#
+# Arguments:
+# w - The scrollbar widget.
+# x, y - Mouse coordinates of button press.
+
+proc ckScrollByButton {w x y} {
+ set element [$w identify $x $y]
+ if {$element == "arrow1"} {
+ ckScrollByUnits $w hv -1
+ } elseif {$element == "trough1"} {
+ ckScrollByPages $w hv -1
+ } elseif {$element == "trough2"} {
+ ckScrollByPages $w hv 1
+ } elseif {$element == "arrow2"} {
+ ckScrollByUnits $w hv 1
+ } else {
+ return
+ }
+}
+
--- /dev/null
+#
+# showglob.tcl --
+#
+# This file defines a command for retrieving Tcl global variables.
+#
+# Copyright (c) 1996 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+proc showglob args {
+ set result {}
+ if {[llength $args] == 0} {
+ set args *
+ }
+ foreach i $args {
+ foreach k [info globals $i] {
+ set glob($k) {}
+ }
+ }
+ foreach i [lsort -ascii [array names glob]] {
+ upvar #0 $i var
+ if [array exists var] {
+ foreach k [lsort -ascii [array names var]] {
+ lappend result set [list $i]($k) $var($k)
+ append result "\n"
+ }
+ } else {
+ catch {lappend result set $i $var}
+ append result "\n"
+ }
+ }
+ return $result
+}
+
+
--- /dev/null
+#
+# showproc.tcl --
+#
+# This file defines a command for retrieving Tcl procedures.
+#
+# Copyright (c) 1996 Christian Werner
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+proc showproc args {
+ set result {}
+ if {[llength $args] == 0} {
+ set args *
+ }
+ foreach i $args {
+ foreach k [info procs $i] {
+ set procs($k) {}
+ }
+ }
+ foreach i [lsort -ascii [array names procs]] {
+ set proc proc
+ lappend proc $i
+ set args {}
+ foreach k [info args $i] {
+ if [info default $i $k value] {
+ lappend args [list $k $value]
+ } else {
+ lappend args $k
+ }
+ }
+ lappend proc $args
+ lappend proc [info body $i]
+ lappend result $proc
+ }
+ return [join $result "\n\n"]
+}
+
+
--- /dev/null
+# Tcl autoload index file, version 2.0
+# This file is generated by the "auto_mkindex" command
+# and sourced to set up indexing information for one or
+# more commands. Typically each line is a command that
+# sets an element in the auto_index array, where the
+# element name is the name of a command and the value is
+# a script that loads the command.
+
+set auto_index(ckButtonFocus) [list source [file join $dir button.tcl]]
+set auto_index(ckButtonInvoke) [list source [file join $dir button.tcl]]
+set auto_index(showproc) [list source [file join $dir showproc.tcl]]
+set auto_index(ckListboxBeginSelect) [list source [file join $dir listbox.tcl]]
+set auto_index(ckListboxUpDown) [list source [file join $dir listbox.tcl]]
+set auto_index(ckListboxCancel) [list source [file join $dir listbox.tcl]]
+set auto_index(ckScrollByUnits) [list source [file join $dir scrollbar.tcl]]
+set auto_index(ckScrollByPages) [list source [file join $dir scrollbar.tcl]]
+set auto_index(ckScrollToPos) [list source [file join $dir scrollbar.tcl]]
+set auto_index(ckScrollByButton) [list source [file join $dir scrollbar.tcl]]
+set auto_index(ck_dialog) [list source [file join $dir dialog.tcl]]
+set auto_index(keylpr) [list source [file join $dir keylpr.tcl]]
+set auto_index(ckTextSetCursor) [list source [file join $dir text.tcl]]
+set auto_index(ckTextInsert) [list source [file join $dir text.tcl]]
+set auto_index(ckTextUpDownLine) [list source [file join $dir text.tcl]]
+set auto_index(ckTextScrollPages) [list source [file join $dir text.tcl]]
+set auto_index(ckCommand) [list source [file join $dir command.tcl]]
+set auto_index(ckCmdToggleSize) [list source [file join $dir command.tcl]]
+set auto_index(ckCmdHist) [list source [file join $dir command.tcl]]
+set auto_index(ckCommandRun) [list source [file join $dir command.tcl]]
+set auto_index(ckMbPost) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuUnpost) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuInvoke) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuEscape) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuLeftRight) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuNextEntry) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuFind) [list source [file join $dir menu.tcl]]
+set auto_index(ckFirstMenu) [list source [file join $dir menu.tcl]]
+set auto_index(ckTraverseWithinMenu) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuFirstEntry) [list source [file join $dir menu.tcl]]
+set auto_index(ckMenuFindName) [list source [file join $dir menu.tcl]]
+set auto_index(ckPostOverPoint) [list source [file join $dir menu.tcl]]
+set auto_index(ck_popup) [list source [file join $dir menu.tcl]]
+set auto_index(ck_getOpenFile) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ck_getSaveFile) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_Config) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_Create) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_UpdateWhenIdle) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_Update) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_SetPathSilently) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_SetPath) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_SetFilter) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialogResolveFile) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_EntFocusIn) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_EntFocusOut) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_ActivateEnt) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_InvokeBtn) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_UpDirCmd) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_JoinFile) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_OkCmd) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_CancelCmd) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_ListBrowse) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_ListInvoke) [list source [file join $dir ckfbox.tcl]]
+set auto_index(ckFDialog_Done) [list source [file join $dir ckfbox.tcl]]
+set auto_index(parray) [list source [file join $dir parray.tcl]]
+set auto_index(showglob) [list source [file join $dir showglob.tcl]]
+set auto_index(ck_focusNext) [list source [file join $dir focus.tcl]]
+set auto_index(ck_focusPrev) [list source [file join $dir focus.tcl]]
+set auto_index(ckFocusOK) [list source [file join $dir focus.tcl]]
+set auto_index(ck_RestrictFocus) [list source [file join $dir focus.tcl]]
+set auto_index(ck_Unrestrict) [list source [file join $dir focus.tcl]]
+set auto_index(ckEntryKeySelect) [list source [file join $dir entry.tcl]]
+set auto_index(ckEntryInsert) [list source [file join $dir entry.tcl]]
+set auto_index(ckEntryBackspace) [list source [file join $dir entry.tcl]]
+set auto_index(ckEntrySeeInsert) [list source [file join $dir entry.tcl]]
+set auto_index(ckEntrySetCursor) [list source [file join $dir entry.tcl]]
+set auto_index(ckEntryTranspose) [list source [file join $dir entry.tcl]]
+set auto_index(entryx) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryDestroy) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryTouched) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryFocus) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryXSetCursor) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryXSeeInsert) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryXBackspace) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryXDelete) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryBooleanInput) [list source [file join $dir entryx.tcl]]
+set auto_index(ckEntryInput) [list source [file join $dir entryx.tcl]]
+set auto_index(ck_messageBox) [list source [file join $dir msgbox.tcl]]
+set auto_index(tclParseConfigSpec) [list source [file join $dir comdlg.tcl]]
+set auto_index(tclListValidFlags) [list source [file join $dir comdlg.tcl]]
+set auto_index(tclSortNoCase) [list source [file join $dir comdlg.tcl]]
+set auto_index(tclVerifyInteger) [list source [file join $dir comdlg.tcl]]
+set auto_index(ckFDGetFileTypes) [list source [file join $dir comdlg.tcl]]
+set auto_index(ck_optionMenu) [list source [file join $dir optMenu.tcl]]
+set auto_index(ck_chooseColor) [list source [file join $dir clrpick.tcl]]
+set auto_index(ckColorDialog_Config) [list source [file join $dir clrpick.tcl]]
+set auto_index(ckColorDialog_BuildDialog) [list source [file join $dir clrpick.tcl]]
+set auto_index(ckColorDialog_OkCmd) [list source [file join $dir clrpick.tcl]]
+set auto_index(ckColorDialog_CancelCmd) [list source [file join $dir clrpick.tcl]]
+set auto_index(tkerror) [list source [file join $dir bgerror.tcl]]
+set auto_index(bgerror) [list source [file join $dir bgerror.tcl]]
--- /dev/null
+# text.tcl --
+#
+# This file defines the default bindings for text widgets and provides
+# procedures that help in implementing the bindings.
+#
+# Copyright (c) 1992-1994 The Regents of the University of California.
+# Copyright (c) 1994-1995 Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+#-------------------------------------------------------------------------
+# The code below creates the default class bindings for texts.
+#-------------------------------------------------------------------------
+
+bind Text <Left> {
+ ckTextSetCursor %W [%W index {insert - 1c}]
+}
+bind Text <Right> {
+ ckTextSetCursor %W [%W index {insert + 1c}]
+}
+bind Text <Up> {
+ ckTextSetCursor %W [ckTextUpDownLine %W -1]
+}
+bind Text <Down> {
+ ckTextSetCursor %W [ckTextUpDownLine %W 1]
+}
+bind Text <Prior> {
+ ckTextSetCursor %W [ckTextScrollPages %W -1]
+}
+bind Text <Next> {
+ ckTextSetCursor %W [ckTextScrollPages %W 1]
+}
+bind Text <Home> {
+ ckTextSetCursor %W {insert linestart}
+}
+bind Text <End> {
+ ckTextSetCursor %W {insert lineend}
+}
+bind Text <Tab> {
+ ckTextInsert %W \t
+ focus %W
+ break
+}
+bind Text <Return> {
+ ckTextInsert %W \n
+}
+bind Text <Linefeed> {
+ ckTextInsert %W \n
+}
+bind Text <Delete> {
+ if {[%W tag nextrange sel 1.0 end] != ""} {
+ %W delete sel.first sel.last
+ } else {
+ %W delete insert
+ %W see insert
+ }
+}
+bind Text <ASCIIDelete> {
+ if {[%W tag nextrange sel 1.0 end] != ""} {
+ %W delete sel.first sel.last
+ } else {
+ %W delete insert
+ %W see insert
+ }
+}
+bind Text <BackSpace> {
+ if {[%W tag nextrange sel 1.0 end] != ""} {
+ %W delete sel.first sel.last
+ } elseif [%W compare insert != 1.0] {
+ %W delete insert-1c
+ %W see insert
+ }
+}
+bind Text <KeyPress> {
+ ckTextInsert %W %A
+}
+bind Text <Button-1> {
+ if [ckFocusOK %W] {
+ ckTextSetCursor %W @%x,%y
+ focus %W
+ }
+}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong. Ditto for <Escape>.
+
+bind Text <Control> {# nothing}
+
+bind Text <Control-x> {focus [ck_focusNext %W]}
+
+bind Text <Control-a> {
+ ckTextSetCursor %W {insert linestart}
+}
+bind Text <Control-b> {
+ ckTextSetCursor %W insert-1c
+}
+bind Text <Control-d> {
+ %W delete insert
+}
+bind Text <Control-e> {
+ ckTextSetCursor %W {insert lineend}
+}
+bind Text <Control-f> {
+ ckTextSetCursor %W insert+1c
+}
+bind Text <Control-k> {
+ if [%W compare insert == {insert lineend}] {
+ %W delete insert
+ } else {
+ %W delete insert {insert lineend}
+ }
+}
+bind Text <Control-n> {
+ ckTextSetCursor %W [ckTextUpDownLine %W 1]
+}
+bind Text <Control-o> {
+ %W insert insert \n
+ %W mark set insert insert-1c
+}
+bind Text <Control-p> {
+ ckTextSetCursor %W [ckTextUpDownLine %W -1]
+}
+bind Text <Control-v> {
+ ckTextScrollPages %W 1
+}
+bind Text <Control-h> {
+ if [%W compare insert != 1.0] {
+ %W delete insert-1c
+ %W see insert
+ }
+}
+bind Text <FocusIn> {%W see insert}
+
+set ckPriv(prevPos) {}
+
+# ckTextSetCursor
+# Move the insertion cursor to a given position in a text. Also
+# clears the selection, if there is one in the text, and makes sure
+# that the insertion cursor is visible. Also, don't let the insertion
+# cursor appear on the dummy last line of the text.
+#
+# Arguments:
+# w - The text window.
+# pos - The desired new position for the cursor in the window.
+
+proc ckTextSetCursor {w pos} {
+ global ckPriv
+
+ if [$w compare $pos == end] {
+ set pos {end - 1 chars}
+ }
+ $w mark set insert $pos
+ $w tag remove sel 1.0 end
+ $w see insert
+}
+
+# ckTextInsert --
+# Insert a string into a text at the point of the insertion cursor.
+# If there is a selection in the text, and it covers the point of the
+# insertion cursor, then delete the selection before inserting.
+#
+# Arguments:
+# w - The text window in which to insert the string
+# s - The string to insert (usually just a single character)
+
+proc ckTextInsert {w s} {
+ if {([string length $s] == 0) || ([$w cget -state] == "disabled")} {
+ return
+ }
+ catch {
+ if {[$w compare sel.first <= insert]
+ && [$w compare sel.last >= insert]} {
+ $w delete sel.first sel.last
+ }
+ }
+ $w insert insert $s
+ $w see insert
+}
+
+# ckTextUpDownLine --
+# Returns the index of the character one line above or below the
+# insertion cursor. There are two tricky things here. First,
+# we want to maintain the original column across repeated operations,
+# even though some lines that will get passed through don't have
+# enough characters to cover the original column. Second, don't
+# try to scroll past the beginning or end of the text.
+#
+# Arguments:
+# w - The text window in which the cursor is to move.
+# n - The number of lines to move: -1 for up one line,
+# +1 for down one line.
+
+proc ckTextUpDownLine {w n} {
+ global ckPriv
+
+ set i [$w index insert]
+ scan $i "%d.%d" line char
+ if {[string compare $ckPriv(prevPos) $i] != 0} {
+ set ckPriv(char) $char
+ }
+ set new [$w index [expr $line + $n].$ckPriv(char)]
+ if {[$w compare $new == end] || [$w compare $new == "insert linestart"]} {
+ set new $i
+ }
+ set ckPriv(prevPos) $new
+ return $new
+}
+
+# ckTextScrollPages --
+# This is a utility procedure used in bindings for moving up and down
+# pages and possibly extending the selection along the way. It scrolls
+# the view in the widget by the number of pages, and it returns the
+# index of the character that is at the same position in the new view
+# as the insertion cursor used to be in the old view.
+#
+# Arguments:
+# w - The text window in which the cursor is to move.
+# count - Number of pages forward to scroll; may be negative
+# to scroll backwards.
+
+proc ckTextScrollPages {w count} {
+ set bbox [$w bbox insert]
+ $w yview scroll $count pages
+ if {$bbox == ""} {
+ return [$w index @[expr [winfo height $w]/2],0]
+ }
+ return [$w index @[lindex $bbox 0],[lindex $bbox 1]]
+}
--- /dev/null
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+of the Rights in Technical Data and Computer Software Clause as DFARS
+252.227-7013 and FAR 52.227-19.
--- /dev/null
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script. It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands. When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded Ck [list load [file join [file dirname $dir] libck.so]]
--- /dev/null
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script. It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands. When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded Ck @CK_VERSION@ [list load [file join [file dirname $dir] libck@CK_VERSION@.so]]
--- /dev/null
+
+lappend auto_path .
+package require Ck
+set frame {ulcorner hline urcorner vline lrcorner llcorner}
+option add *CkFDialog*Background blue
+option add *CkChooseColor*Background green
+option add *MenuBar*foreground black
+option add *MenuBar*background white
+option add *MenuBar*activeBackground black
+option add *MenuBar*activeForeground white
+option add *MenuBar*selectedBackground black
+option add *MenuBar*selectedForeground white
+option add *MenuBar*underlineAttributes {underline bold}
+option add *MenuBar*underlineForeground yellow
+frame .menu -class MenuBar
+menubutton .menu.file -text File -underline 0 -menu .menu.file.m
+menu .menu.file.m -border $frame
+.menu.file.m add command -label "New" -command {fileNew}
+.menu.file.m add command -label "Open..." -command {fileOpen}
+.menu.file.m add command -label "Save..." -command {fileSave}
+.menu.file.m add separator
+.menu.file.m add command -label "Exit" -command {destroy .}
+menubutton .menu.dialog -text Dialogs -underline 0 -menu .menu.dialog.m
+menu .menu.dialog.m -border $frame
+.menu.dialog.m add command -label "Message box.." -command {ck_messageBox -title "MessageBox" -message "This is simple message box" -type ok}
+.menu.dialog.m add command -label "Color Picker.." -command {ck_chooseColor}
+.menu.dialog.m add command -label "Command window.." -command {ckCommand}
+label .menu.hint -text "Press <F10> for menu" -foreground black -background white
+pack .menu.hint -side right
+pack .menu.file .menu.dialog -side left -padx 1
+pack .menu -side top -fill x
+frame .f
+text .f.t -yscrollcommand ".f.y set" -foreground yellow -attributes bold -background blue
+scrollbar .f.y -orient vert -command ".f.t yview"
+pack .f.t -side left -expand y -fill both
+pack .f.y -side right -expand n -fill y
+pack .f -side top -expand y -fill both
+focus .f.t
+bind .f.t <F10> {focus .menu.file;.menu.file configure -state active}
+proc fileOpen {} {
+ global fileName
+ set filename [ck_getOpenFile]
+ if {![string length filename]} return;
+ set f [open $filename]
+ .f.t delete 0.0 end
+ .f.t insert 0.0 [read $f]
+ close $f
+ set fileName $filename
+ .f.t mark set insert 0.0
+ focus .f.t
+}
+
+proc fileSave {} {
+ global fileName
+ if {[info exists fileName]} {
+ set filename [ck_getSaveFile -initialfile $filename]
+ } else {
+ set filename [ck_getSaveFile]
+ }
+ if {![string length filename]} return;
+ set f [open $filename w]
+ puts -nonewline $f [.f.t get 0.0 end]
+ close $f
+ set fileName $filename
+ focus .f.t
+}
+
+proc fileNew {} {
+ global fileName
+ catch {unset fileName}
+ .f.t delete 0.0 end
+ focus .f.t
+}
+tkwait window .
+
--- /dev/null
+/*
+ * tkEvent.c --
+ *
+ * This file provides basic event-managing facilities, whereby
+ * procedure callbacks may be attached to certain events. It
+ * also contains the command procedures for the commands "after"
+ * and "fileevent", plus abridged versions of "tkwait" and
+ * "update", for use with Tk_EventInit.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "ckPort.h"
+#include "ck.h"
+
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+
+/*
+ * For each timer callback that's pending, there is one record
+ * of the following type, chained together in a list sorted by
+ * time (earliest event first).
+ */
+
+typedef struct TimerEvent {
+ struct timeval time; /* When timer is to fire. */
+ void (*proc) _ANSI_ARGS_((ClientData clientData));
+ /* Procedure to call. */
+ ClientData clientData; /* Argument to pass to proc. */
+ Tk_TimerToken token; /* Identifies event so it can be
+ * deleted. */
+ struct TimerEvent *nextPtr; /* Next event in queue, or NULL for
+ * end of queue. */
+} TimerEvent;
+
+static TimerEvent *firstTimerHandlerPtr;
+ /* First event in queue. */
+
+/*
+ * The information below is used to provide read, write, and
+ * exception masks to select during calls to Tk_DoOneEvent.
+ */
+
+static fd_mask ready[3*MASK_SIZE];
+ /* Masks passed to select and modified
+ * by kernel to indicate which files are
+ * actually ready. */
+static fd_mask check[3*MASK_SIZE];
+ /* Temporary set of masks, built up during
+ * Tk_DoOneEvent, that reflects what files
+ * we should wait for in the next select
+ * (doesn't include things that we've been
+ * asked to ignore in this call). */
+static int numFds = 0; /* Number of valid bits in mask
+ * arrays (this value is passed
+ * to select). */
+
+/*
+ * For each file registered in a call to Tk_CreateFileHandler,
+ * and for each display that's currently active, there is one
+ * record of the following type. All of these records are
+ * chained together into a single list.
+ */
+
+typedef struct FileHandler {
+ int fd; /* POSIX file descriptor for file. */
+ fd_mask *readPtr; /* Pointer to word in ready array
+ * for this file's read mask bit. */
+ fd_mask *writePtr; /* Same for write mask bit. */
+ fd_mask *exceptPtr; /* Same for except mask bit. */
+ fd_mask *checkReadPtr; /* Pointer to word in check array for
+ * this file's read mask bit. */
+ fd_mask *checkWritePtr; /* Same for write mask bit. */
+ fd_mask *checkExceptPtr; /* Same for except mask bit. */
+ fd_mask bitSelect; /* Value to AND with *readPtr etc. to
+ * select just this file's bit. */
+ int mask; /* Mask of desired events: TK_READABLE, etc. */
+ Tk_FileProc *proc; /* Procedure to call, in the style of
+ * Tk_CreateFileHandler. This is NULL
+ * if the handler was created by
+ * Tk_CreateFileHandler2. */
+ Tk_FileProc2 *proc2; /* Procedure to call, in the style of
+ * Tk_CreateFileHandler2. NULL means that
+ * the handler was created by
+ * Tk_CreateFileHandler. */
+ ClientData clientData; /* Argument to pass to proc. */
+ struct FileHandler *nextPtr;/* Next in list of all files we
+ * care about (NULL for end of
+ * list). */
+} FileHandler;
+
+static FileHandler *firstFileHandlerPtr;
+ /* List of all file events. */
+
+/*
+ * There is one of the following structures for each of the
+ * handlers declared in a call to Tk_DoWhenIdle. All of the
+ * currently-active handlers are linked together into a list.
+ */
+
+typedef struct IdleHandler {
+ void (*proc) _ANSI_ARGS_((ClientData clientData));
+ /* Procedure to call. */
+ ClientData clientData; /* Value to pass to proc. */
+ int generation; /* Used to distinguish older handlers from
+ * recently-created ones. */
+ struct IdleHandler *nextPtr;/* Next in list of active handlers. */
+} IdleHandler;
+
+static IdleHandler *idleList = NULL;
+ /* First in list of all idle handlers. */
+static IdleHandler *lastIdlePtr = NULL;
+ /* Last in list (or NULL for empty list). */
+static int idleGeneration = 0; /* Used to fill in the "generation" fields
+ * of IdleHandler structures. Increments
+ * each time Tk_DoOneEvent starts calling
+ * idle handlers, so that all old handlers
+ * can be called without calling any of the
+ * new ones created by old ones. */
+static int oldGeneration = 0; /* "generation" currently being handled. */
+
+/*
+ * The following procedure provides a secret hook for tkXEvent.c so that
+ * it can handle delayed mouse motion events at the right time.
+ */
+
+void (*tkDelayedEventProc) _ANSI_ARGS_((void)) = NULL;
+
+/*
+ * One of the following structures exists for each file with a handler
+ * created by the "fileevent" command. Several of the fields are
+ * two-element arrays, in which the first element is used for read
+ * events and the second for write events.
+ */
+
+typedef struct FileEvent {
+ FILE *f; /* Stdio handle for file. */
+ Tcl_Interp *interps[2]; /* Interpreters in which to execute
+ * scripts. NULL means no handler
+ * for event. */
+ char *scripts[2]; /* Scripts to evaluate in response to
+ * events (malloc'ed). NULL means no
+ * handler for event. */
+ struct FileEvent *nextPtr; /* Next in list of all file events
+ * currently defined. */
+} FileEvent;
+
+static FileEvent *firstFileEventPtr = NULL;
+ /* First in list of all existing
+ * file events. */
+
+/*
+ * The data structure below is used by the "after" command to remember
+ * the command to be executed later.
+ */
+
+typedef struct AfterInfo {
+ Tcl_Interp *interp; /* Interpreter in which to execute command. */
+ char *command; /* Command to execute. Malloc'ed, so must
+ * be freed when structure is deallocated. */
+ int id; /* Integer identifier for command; used to
+ * cancel it. */
+ Tk_TimerToken token; /* Used to cancel the "after" command. NULL
+ * means that the command is run as an
+ * idle handler rather than as a timer
+ * handler. */
+ struct AfterInfo *nextPtr; /* Next in list of all "after" commands for
+ * the application. */
+} AfterInfo;
+
+static AfterInfo *firstAfterPtr = NULL;
+ /* First in list of all pending "after"
+ * commands. */
+
+/*
+ * The data structure below is used to report background errors. One
+ * such structure is allocated for each error; it holds information
+ * about the interpreter and the error until tkerror can be invoked
+ * later as an idle handler.
+ */
+
+typedef struct BgError {
+ Tcl_Interp *interp; /* Interpreter in which error occurred. NULL
+ * means this error report has been cancelled
+ * (a previous report generated a break). */
+ char *errorMsg; /* The error message (interp->result when
+ * the error occurred). Malloc-ed. */
+ char *errorInfo; /* Value of the errorInfo variable
+ * (malloc-ed). */
+ char *errorCode; /* Value of the errorCode variable
+ * (malloc-ed). */
+ struct BgError *nextPtr; /* Next in list of all pending error
+ * reports. */
+} BgError;
+
+static BgError *firstBgPtr = NULL;
+ /* First in list of all background errors
+ * waiting to be processed (NULL if none). */
+static BgError *lastBgPtr = NULL;
+ /* First in list of all background errors
+ * waiting to be processed (NULL if none). */
+
+/*
+ * Prototypes for procedures referenced only in this file:
+ */
+
+static void AfterProc _ANSI_ARGS_((ClientData clientData));
+static void DeleteFileEvent _ANSI_ARGS_((FILE *f));
+static int FileEventProc _ANSI_ARGS_((ClientData clientData,
+ int mask, int flags));
+static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
+static void HandleBgErrors _ANSI_ARGS_((ClientData clientData));
+static int TkwaitCmd2 _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static int UpdateCmd2 _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv));
+static char * WaitVariableProc2 _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, char *name1, char *name2,
+ int flags));
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_CreateFileHandler --
+ *
+ * Arrange for a given procedure to be invoked whenever
+ * a given file becomes readable or writable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * From now on, whenever the I/O channel given by fd becomes
+ * ready in the way indicated by mask, proc will be invoked.
+ * See the manual entry for details on the calling sequence
+ * to proc. If fd is already registered then the old mask
+ * and proc and clientData values will be replaced with
+ * new ones.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_CreateFileHandler(fd, mask, proc, clientData)
+ int fd; /* Integer identifier for stream. */
+ int mask; /* OR'ed combination of TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION:
+ * indicates conditions under which
+ * proc should be called. TK_IS_DISPLAY
+ * indicates that this is a display and that
+ * clientData is the (Display *) for it,
+ * and that events should be handled
+ * automatically.*/
+ Tk_FileProc *proc; /* Procedure to call for each
+ * selected event. */
+ ClientData clientData; /* Arbitrary data to pass to proc. */
+{
+ register FileHandler *filePtr;
+ int index;
+
+ if (fd >= FD_SETSIZE) {
+ panic("Tk_CreatefileHandler can't handle file id %d", fd);
+ }
+
+ /*
+ * Make sure the file isn't already registered. Create a
+ * new record in the normal case where there's no existing
+ * record.
+ */
+
+ for (filePtr = firstFileHandlerPtr; filePtr != NULL;
+ filePtr = filePtr->nextPtr) {
+ if (filePtr->fd == fd) {
+ break;
+ }
+ }
+ index = fd/(NBBY*sizeof(fd_mask));
+ if (filePtr == NULL) {
+ filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
+ filePtr->fd = fd;
+ filePtr->readPtr = &ready[index];
+ filePtr->writePtr = &ready[index+MASK_SIZE];
+ filePtr->exceptPtr = &ready[index+2*MASK_SIZE];
+ filePtr->checkReadPtr = &check[index];
+ filePtr->checkWritePtr = &check[index+MASK_SIZE];
+ filePtr->checkExceptPtr = &check[index+2*MASK_SIZE];
+ filePtr->bitSelect = 1 << (fd%(NBBY*sizeof(fd_mask)));
+ filePtr->nextPtr = firstFileHandlerPtr;
+ firstFileHandlerPtr = filePtr;
+ }
+
+ /*
+ * The remainder of the initialization below is done
+ * regardless of whether or not this is a new record
+ * or a modification of an old one.
+ */
+
+ filePtr->mask = mask;
+ filePtr->proc = proc;
+ filePtr->proc2 = NULL;
+ filePtr->clientData = clientData;
+
+ if (numFds <= fd) {
+ numFds = fd+1;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_CreateFileHandler2 --
+ *
+ * Arrange for a given procedure to be invoked during the
+ * event loop to handle a particular file.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * In each pass through Tk_DoOneEvent, proc will be invoked to
+ * decide whether fd is "ready" and take appropriate action if
+ * it is. See the manual entry for details on the calling
+ * sequence to proc. If a handler for fd has already been
+ * registered then it is superseded by the new one.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_CreateFileHandler2(fd, proc, clientData)
+ int fd; /* Integer identifier for stream. */
+ Tk_FileProc2 *proc; /* Procedure to call from the event
+ * dispatcher. */
+ ClientData clientData; /* Arbitrary data to pass to proc. */
+{
+ register FileHandler *filePtr;
+
+ /*
+ * Let Tk_CreateFileHandler do all of the work of setting up
+ * the handler, then just modify things a bit after it returns.
+ */
+
+ Tk_CreateFileHandler(fd, 0, (Tk_FileProc *) NULL, clientData);
+ for (filePtr = firstFileHandlerPtr; filePtr->fd != fd;
+ filePtr = filePtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ filePtr->proc = NULL;
+ filePtr->proc2 = proc;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_DeleteFileHandler --
+ *
+ * Cancel a previously-arranged callback arrangement for
+ * a file.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a callback was previously registered on fd, remove it.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_DeleteFileHandler(fd)
+ int fd; /* Stream id for which to remove
+ * callback procedure. */
+{
+ register FileHandler *filePtr;
+ FileHandler *prevPtr;
+
+ /*
+ * Find the entry for the given file (and return if there
+ * isn't one).
+ */
+
+ for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ;
+ prevPtr = filePtr, filePtr = filePtr->nextPtr) {
+ if (filePtr == NULL) {
+ return;
+ }
+ if (filePtr->fd == fd) {
+ break;
+ }
+ }
+
+ /*
+ * Clean up information in the callback record.
+ */
+
+ if (prevPtr == NULL) {
+ firstFileHandlerPtr = filePtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = filePtr->nextPtr;
+ }
+ ckfree((char *) filePtr);
+
+ /*
+ * Recompute numFds.
+ */
+
+ numFds = 0;
+ for (filePtr = firstFileHandlerPtr; filePtr != NULL;
+ filePtr = filePtr->nextPtr) {
+ if (numFds <= filePtr->fd) {
+ numFds = filePtr->fd+1;
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_CreateTimerHandler --
+ *
+ * Arrange for a given procedure to be invoked at a particular
+ * time in the future.
+ *
+ * Results:
+ * The return value is a token for the timer event, which
+ * may be used to delete the event before it fires.
+ *
+ * Side effects:
+ * When milliseconds have elapsed, proc will be invoked
+ * exactly once.
+ *
+ *--------------------------------------------------------------
+ */
+
+Tk_TimerToken
+Tk_CreateTimerHandler(milliseconds, proc, clientData)
+ int milliseconds; /* How many milliseconds to wait
+ * before invoking proc. */
+ Tk_TimerProc *proc; /* Procedure to invoke. */
+ ClientData clientData; /* Arbitrary data to pass to proc. */
+{
+ register TimerEvent *timerPtr, *tPtr2, *prevPtr;
+ static int id = 0;
+
+ timerPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));
+
+ /*
+ * Compute when the event should fire.
+ */
+
+ (void) gettimeofday(&timerPtr->time, (struct timezone *) NULL);
+ timerPtr->time.tv_sec += milliseconds/1000;
+ timerPtr->time.tv_usec += (milliseconds%1000)*1000;
+ if (timerPtr->time.tv_usec >= 1000000) {
+ timerPtr->time.tv_usec -= 1000000;
+ timerPtr->time.tv_sec += 1;
+ }
+
+ /*
+ * Fill in other fields for the event.
+ */
+
+ timerPtr->proc = proc;
+ timerPtr->clientData = clientData;
+ id++;
+ timerPtr->token = (Tk_TimerToken) id;
+
+ /*
+ * Add the event to the queue in the correct position
+ * (ordered by event firing time).
+ */
+
+ for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
+ prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
+ if ((tPtr2->time.tv_sec > timerPtr->time.tv_sec)
+ || ((tPtr2->time.tv_sec == timerPtr->time.tv_sec)
+ && (tPtr2->time.tv_usec > timerPtr->time.tv_usec))) {
+ break;
+ }
+ }
+ if (prevPtr == NULL) {
+ timerPtr->nextPtr = firstTimerHandlerPtr;
+ firstTimerHandlerPtr = timerPtr;
+ } else {
+ timerPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = timerPtr;
+ }
+ return timerPtr->token;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_DeleteTimerHandler --
+ *
+ * Delete a previously-registered timer handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroy the timer callback identified by TimerToken,
+ * so that its associated procedure will not be called.
+ * If the callback has already fired, or if the given
+ * token doesn't exist, then nothing happens.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_DeleteTimerHandler(token)
+ Tk_TimerToken token; /* Result previously returned by
+ * Tk_DeleteTimerHandler. */
+{
+ register TimerEvent *timerPtr, *prevPtr;
+
+ for (timerPtr = firstTimerHandlerPtr, prevPtr = NULL; timerPtr != NULL;
+ prevPtr = timerPtr, timerPtr = timerPtr->nextPtr) {
+ if (timerPtr->token != token) {
+ continue;
+ }
+ if (prevPtr == NULL) {
+ firstTimerHandlerPtr = timerPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = timerPtr->nextPtr;
+ }
+ ckfree((char *) timerPtr);
+ return;
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_DoWhenIdle --
+ *
+ * Arrange for proc to be invoked the next time the
+ * system is idle (i.e., just before the next time
+ * that Tk_DoOneEvent would have to wait for something
+ * to happen).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Proc will eventually be called, with clientData
+ * as argument. See the manual entry for details.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_DoWhenIdle(proc, clientData)
+ Tk_IdleProc *proc; /* Procedure to invoke. */
+ ClientData clientData; /* Arbitrary value to pass to proc. */
+{
+ register IdleHandler *idlePtr;
+
+ idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
+ idlePtr->proc = proc;
+ idlePtr->clientData = clientData;
+ idlePtr->generation = idleGeneration;
+ idlePtr->nextPtr = NULL;
+ if (lastIdlePtr == NULL) {
+ idleList = idlePtr;
+ } else {
+ lastIdlePtr->nextPtr = idlePtr;
+ }
+ lastIdlePtr = idlePtr;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_DoWhenIdle2 --
+ *
+ * Arrange for proc to be invoked when the system is idle
+ * (i.e., if currently idle or just before the next time
+ * that Tk_DoOneEvent would have to wait for something
+ * to happen).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Proc will eventually be called, with clientData
+ * as argument. See the manual entry for details.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_DoWhenIdle2(proc, clientData)
+ Tk_IdleProc *proc; /* Procedure to invoke. */
+ ClientData clientData; /* Arbitrary value to pass to proc. */
+{
+ register IdleHandler *idlePtr;
+
+ idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
+ idlePtr->proc = proc;
+ idlePtr->clientData = clientData;
+ idlePtr->generation = idleList == NULL ? oldGeneration :
+ idleList->generation;
+ idlePtr->nextPtr = NULL;
+ if (lastIdlePtr == NULL) {
+ idleList = idlePtr;
+ } else {
+ lastIdlePtr->nextPtr = idlePtr;
+ }
+ lastIdlePtr = idlePtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_CancelIdleCall --
+ *
+ * If there are any when-idle calls requested to a given procedure
+ * with given clientData, cancel all of them.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the proc/clientData combination were on the when-idle list,
+ * they are removed so that they will never be called.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_CancelIdleCall(proc, clientData)
+ Tk_IdleProc *proc; /* Procedure that was previously registered. */
+ ClientData clientData; /* Arbitrary value to pass to proc. */
+{
+ register IdleHandler *idlePtr, *prevPtr;
+ IdleHandler *nextPtr;
+
+ for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
+ prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
+ while ((idlePtr->proc == proc)
+ && (idlePtr->clientData == clientData)) {
+ nextPtr = idlePtr->nextPtr;
+ ckfree((char *) idlePtr);
+ idlePtr = nextPtr;
+ if (prevPtr == NULL) {
+ idleList = idlePtr;
+ } else {
+ prevPtr->nextPtr = idlePtr;
+ }
+ if (idlePtr == NULL) {
+ lastIdlePtr = prevPtr;
+ return;
+ }
+ }
+ }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_DoOneEvent --
+ *
+ * Process a single event of some sort. If there's no
+ * work to do, wait for an event to occur, then process
+ * it.
+ *
+ * Results:
+ * The return value is 1 if the procedure actually found
+ * an event to process. If no event was found then 0 is
+ * returned.
+ *
+ * Side effects:
+ * May delay execution of process while waiting for an
+ * X event, X error, file-ready event, or timer event.
+ * The handling of the event could cause additional
+ * side effects. Collapses sequences of mouse-motion
+ * events for the same window into a single event by
+ * delaying motion event processing.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tk_DoOneEvent(flags)
+ int flags; /* Miscellaneous flag values: may be any
+ * combination of TK_DONT_WAIT, TK_X_EVENTS,
+ * TK_FILE_EVENTS, TK_TIMER_EVENTS, and
+ * TK_IDLE_EVENTS. */
+{
+ register FileHandler *filePtr;
+ struct timeval curTime, timeoutVal, *timeoutPtr;
+ int numFound, mask, anyFilesToWaitFor;
+
+ if ((flags & TK_ALL_EVENTS) == 0) {
+ flags |= TK_ALL_EVENTS;
+ }
+
+ /*
+ * Phase One: see if there's a ready file that was left over
+ * from before (i.e don't do a select, just check the bits from
+ * the last select).
+ */
+
+ checkFiles:
+ if (tcl_AsyncReady) {
+ (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
+ return 1;
+ }
+ memset((VOID *) check, 0, 3*MASK_SIZE*sizeof(fd_mask));
+ anyFilesToWaitFor = 0;
+ for (filePtr = firstFileHandlerPtr; filePtr != NULL;
+ filePtr = filePtr->nextPtr) {
+ mask = 0;
+ if (*filePtr->readPtr & filePtr->bitSelect) {
+ mask |= TK_READABLE;
+ *filePtr->readPtr &= ~filePtr->bitSelect;
+ }
+ if (*filePtr->writePtr & filePtr->bitSelect) {
+ mask |= TK_WRITABLE;
+ *filePtr->writePtr &= ~filePtr->bitSelect;
+ }
+ if (*filePtr->exceptPtr & filePtr->bitSelect) {
+ mask |= TK_EXCEPTION;
+ *filePtr->exceptPtr &= ~filePtr->bitSelect;
+ }
+ if (filePtr->proc2 != NULL) {
+ /*
+ * Handler created by Tk_CreateFileHandler2.
+ */
+
+ mask = (*filePtr->proc2)(filePtr->clientData, mask, flags);
+ if (mask == TK_FILE_HANDLED) {
+ return 1;
+ }
+ } else {
+ /*
+ * Handler created by Tk_CreateFileHandler.
+ */
+
+ if (!(flags & TK_FILE_EVENTS)) {
+ continue;
+ }
+ if (mask != 0) {
+ (*filePtr->proc)(filePtr->clientData, mask);
+ return 1;
+ }
+ mask = filePtr->mask;
+ }
+ if (mask != 0) {
+ anyFilesToWaitFor = 1;
+ if (mask & TK_READABLE) {
+ *filePtr->checkReadPtr |= filePtr->bitSelect;
+ }
+ if (mask & TK_WRITABLE) {
+ *filePtr->checkWritePtr |= filePtr->bitSelect;
+ }
+ if (mask & TK_EXCEPTION) {
+ *filePtr->checkExceptPtr |= filePtr->bitSelect;
+ }
+ }
+ }
+
+ /*
+ * Phase Two: get the current time and see if any timer
+ * events are ready to fire. If so, fire one and return.
+ */
+
+ checkTime:
+ if ((firstTimerHandlerPtr != NULL) && (flags & TK_TIMER_EVENTS)) {
+ register TimerEvent *timerPtr = firstTimerHandlerPtr;
+
+ (void) gettimeofday(&curTime, (struct timezone *) NULL);
+ if ((timerPtr->time.tv_sec < curTime.tv_sec)
+ || ((timerPtr->time.tv_sec == curTime.tv_sec)
+ && (timerPtr->time.tv_usec < curTime.tv_usec))) {
+ firstTimerHandlerPtr = timerPtr->nextPtr;
+ (*timerPtr->proc)(timerPtr->clientData);
+ ckfree((char *) timerPtr);
+ return 1;
+ }
+ }
+
+ /*
+ * Phase Three: if there are DoWhenIdle requests pending (or
+ * if we're not allowed to block), then do a select with an
+ * instantaneous timeout. If a ready file is found, then go
+ * back to process it.
+ */
+
+ if (((idleList != NULL) && (flags & TK_IDLE_EVENTS))
+ || (flags & TK_DONT_WAIT)) {
+ if (flags & (TK_X_EVENTS|TK_FILE_EVENTS)) {
+ memcpy((VOID *) ready, (VOID *) check,
+ 3*MASK_SIZE*sizeof(fd_mask));
+ timeoutVal.tv_sec = timeoutVal.tv_usec = 0;
+ numFound = select(numFds, (SELECT_MASK *) &ready[0],
+ (SELECT_MASK *) &ready[MASK_SIZE],
+ (SELECT_MASK *) &ready[2*MASK_SIZE], &timeoutVal);
+ if (numFound <= 0) {
+ /*
+ * Some systems don't clear the masks after an error, so
+ * we have to do it here.
+ */
+
+ memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
+ }
+ if ((numFound > 0) || ((numFound == -1) && (errno == EINTR))) {
+ goto checkFiles;
+ }
+ }
+ }
+
+ /*
+ * Phase Four: if there is a delayed motion event then call a procedure
+ * to handle it. Do it now, before calling any DoWhenIdle handlers,
+ * since the goal of idle handlers is to delay until after all pending
+ * events have been processed.
+ *
+ * The particular implementation of this (a procedure variable shared
+ * with tkXEvent.c) is a bit kludgy, but it allows this file to be used
+ * separately without any of the rest of Tk.
+ */
+
+ if ((tkDelayedEventProc != NULL) && (flags & TK_X_EVENTS)) {
+ (*tkDelayedEventProc)();
+ return 1;
+ }
+
+ /*
+ * Phase Five: process all pending DoWhenIdle requests.
+ */
+
+ if ((idleList != NULL) && (flags & TK_IDLE_EVENTS)) {
+ register IdleHandler *idlePtr;
+ int myGeneration;
+
+ oldGeneration = myGeneration = idleList->generation;
+ idleGeneration++;
+
+ /*
+ * The code below is trickier than it may look, for the following
+ * reasons:
+ *
+ * 1. New handlers can get added to the list while the current
+ * one is being processed. If new ones get added, we don't
+ * want to process them during this pass through the list (want
+ * to check for other work to do first). This is implemented
+ * using the generation number in the handler: new handlers
+ * will have a different generation than any of the ones currently
+ * on the list.
+ * 2. The handler can call Tk_DoOneEvent, so we have to remove
+ * the hander from the list before calling it. Otherwise an
+ * infinite loop could result.
+ * 3. Tk_CancelIdleCall can be called to remove an element from
+ * the list while a handler is executing, so the list could
+ * change structure during the call.
+ */
+
+ for (idlePtr = idleList;
+ ((idlePtr != NULL) && (idlePtr->generation == myGeneration));
+ idlePtr = idleList) {
+ idleList = idlePtr->nextPtr;
+ if (idleList == NULL) {
+ lastIdlePtr = NULL;
+ }
+ (*idlePtr->proc)(idlePtr->clientData);
+ ckfree((char *) idlePtr);
+ }
+ return 1;
+ }
+
+ /*
+ * Phase Six: do a select to wait for either one of the
+ * files to become ready or for the first timer event to
+ * fire. Then go back to process the event.
+ */
+
+ if ((flags & TK_DONT_WAIT)
+ || !(flags & (TK_TIMER_EVENTS|TK_FILE_EVENTS|TK_X_EVENTS))) {
+ return 0;
+ }
+ if ((firstTimerHandlerPtr == NULL) || !(flags & TK_TIMER_EVENTS)) {
+ timeoutPtr = NULL;
+ } else {
+ timeoutPtr = &timeoutVal;
+ timeoutVal.tv_sec = firstTimerHandlerPtr->time.tv_sec
+ - curTime.tv_sec;
+ timeoutVal.tv_usec = firstTimerHandlerPtr->time.tv_usec
+ - curTime.tv_usec;
+ if (timeoutVal.tv_usec < 0) {
+ timeoutVal.tv_sec -= 1;
+ timeoutVal.tv_usec += 1000000;
+ }
+ }
+ if ((timeoutPtr == NULL) && !anyFilesToWaitFor) {
+ return 0;
+ }
+ memcpy((VOID *) ready, (VOID *) check, 3*MASK_SIZE*sizeof(fd_mask));
+ numFound = select(numFds, (SELECT_MASK *) &ready[0],
+ (SELECT_MASK *) &ready[MASK_SIZE],
+ (SELECT_MASK *) &ready[2*MASK_SIZE], timeoutPtr);
+ if (numFound == -1) {
+ /*
+ * Some systems don't clear the masks after an error, so
+ * we have to do it here.
+ */
+
+ memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
+ }
+ if (numFound == 0) {
+ goto checkTime;
+ }
+ goto checkFiles;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_Sleep --
+ *
+ * Delay execution for the specified number of milliseconds.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Time passes.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_Sleep(ms)
+ int ms; /* Number of milliseconds to sleep. */
+{
+ static struct timeval delay;
+
+ delay.tv_sec = ms/1000;
+ delay.tv_usec = (ms%1000)*1000;
+ (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
+ (SELECT_MASK *) 0, &delay);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_BackgroundError --
+ *
+ * This procedure is invoked to handle errors that occur in Tcl
+ * commands that are invoked in "background" (e.g. from event or
+ * timer bindings).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The command "tkerror" is invoked later as an idle handler to
+ * process the error, passing it the error message. If that fails,
+ * then an error message is output on stderr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_BackgroundError(interp)
+ Tcl_Interp *interp; /* Interpreter in which an error has
+ * occurred. */
+{
+ BgError *errPtr;
+ char *varValue;
+
+ /*
+ * The Tcl_AddErrorInfo call below (with an empty string) ensures that
+ * errorInfo gets properly set. It's needed in cases where the error
+ * came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
+ * in these cases errorInfo still won't have been set when this
+ * procedure is called.
+ */
+
+ Tcl_AddErrorInfo(interp, "");
+ errPtr = (BgError *) ckalloc(sizeof(BgError));
+ errPtr->interp = interp;
+ errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)
+ + 1));
+ strcpy(errPtr->errorMsg, interp->result);
+ varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+ if (varValue == NULL) {
+ varValue = errPtr->errorMsg;
+ }
+ errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
+ strcpy(errPtr->errorInfo, varValue);
+ varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
+ if (varValue == NULL) {
+ varValue = "";
+ }
+ errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
+ strcpy(errPtr->errorCode, varValue);
+ errPtr->nextPtr = NULL;
+ if (firstBgPtr == NULL) {
+ firstBgPtr = errPtr;
+ Tk_DoWhenIdle(HandleBgErrors, (ClientData) NULL);
+ } else {
+ lastBgPtr->nextPtr = errPtr;
+ }
+ lastBgPtr = errPtr;
+ Tcl_ResetResult(interp);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * HandleBgErrors --
+ *
+ * This procedure is invoked as an idle handler to process all of
+ * the accumulated background errors.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on what actions "tkerror" takes for the errors.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HandleBgErrors(clientData)
+ ClientData clientData; /* Not used. */
+{
+ Tcl_Interp *interp;
+ char *command;
+ char *argv[2];
+ int code;
+ BgError *errPtr;
+
+ while (firstBgPtr != NULL) {
+ interp = firstBgPtr->interp;
+ if (interp == NULL) {
+ goto doneWithReport;
+ }
+
+ /*
+ * Restore important state variables to what they were at
+ * the time the error occurred.
+ */
+
+ Tcl_SetVar(interp, "errorInfo", firstBgPtr->errorInfo,
+ TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "errorCode", firstBgPtr->errorCode,
+ TCL_GLOBAL_ONLY);
+
+ /*
+ * Create and invoke the tkerror command.
+ */
+
+ argv[0] = "tkerror";
+ argv[1] = firstBgPtr->errorMsg;
+ command = Tcl_Merge(2, argv);
+ Tcl_AllowExceptions(interp);
+ code = Tcl_GlobalEval(interp, command);
+ ckfree(command);
+ if (code == TCL_ERROR) {
+ if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
+ fprintf(stderr, "%s\n", firstBgPtr->errorInfo);
+ } else {
+ fprintf(stderr, "tkerror failed to handle background error.\n");
+ fprintf(stderr, " Original error: %s\n",
+ firstBgPtr->errorMsg);
+ fprintf(stderr, " Error in tkerror: %s\n", interp->result);
+ }
+ } else if (code == TCL_BREAK) {
+ /*
+ * Break means cancel any remaining error reports for this
+ * interpreter.
+ */
+
+ for (errPtr = firstBgPtr; errPtr != NULL;
+ errPtr = errPtr->nextPtr) {
+ if (errPtr->interp == interp) {
+ errPtr->interp = NULL;
+ }
+ }
+ }
+
+ /*
+ * Discard the command and the information about the error report.
+ */
+
+ doneWithReport:
+ ckfree(firstBgPtr->errorMsg);
+ ckfree(firstBgPtr->errorInfo);
+ ckfree(firstBgPtr->errorCode);
+ errPtr = firstBgPtr->nextPtr;
+ ckfree((char *) firstBgPtr);
+ firstBgPtr = errPtr;
+ }
+ lastBgPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AfterCmd --
+ *
+ * This procedure is invoked to process the "after" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+Tk_AfterCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. Not used.*/
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ /*
+ * The variable below is used to generate unique identifiers for
+ * after commands. This id can wrap around, which can potentially
+ * cause problems. However, there are not likely to be problems
+ * in practice, because after commands can only be requested to
+ * about a month in the future, and wrap-around is unlikely to
+ * occur in less than about 1-10 years. Thus it's unlikely that
+ * any old ids will still be around when wrap-around occurs.
+ */
+
+ static int nextId = 1;
+ int ms, id;
+ AfterInfo *afterPtr;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " milliseconds ?command? ?arg arg ...?\" or \"",
+ argv[0], " cancel id|command\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ if (isdigit((unsigned char) argv[1][0])) {
+ if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ms < 0) {
+ ms = 0;
+ }
+ if (argc == 2) {
+ Tk_Sleep(ms);
+ return TCL_OK;
+ }
+ afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
+ afterPtr->interp = interp;
+ if (argc == 3) {
+ afterPtr->command = (char *) ckalloc((unsigned)
+ (strlen(argv[2]) + 1));
+ strcpy(afterPtr->command, argv[2]);
+ } else {
+ afterPtr->command = Tcl_Concat(argc-2, argv+2);
+ }
+ afterPtr->id = nextId;
+ nextId += 1;
+ afterPtr->token = Tk_CreateTimerHandler(ms, AfterProc,
+ (ClientData) afterPtr);
+ afterPtr->nextPtr = firstAfterPtr;
+ firstAfterPtr = afterPtr;
+ sprintf(interp->result, "after#%d", afterPtr->id);
+ } else if (strncmp(argv[1], "cancel", strlen(argv[1])) == 0) {
+ char *arg;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " cancel id|command\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ arg = argv[2];
+ } else {
+ arg = Tcl_Concat(argc-2, argv+2);
+ }
+ if (strncmp(arg, "after#", 6) == 0) {
+ if (Tcl_GetInt(interp, arg+6, &id) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (afterPtr = firstAfterPtr; afterPtr != NULL;
+ afterPtr = afterPtr->nextPtr) {
+ if (afterPtr->id == id) {
+ break;
+ }
+ }
+ } else {
+ for (afterPtr = firstAfterPtr; afterPtr != NULL;
+ afterPtr = afterPtr->nextPtr) {
+ if (strcmp(afterPtr->command, arg) == 0) {
+ break;
+ }
+ }
+ }
+ if (arg != argv[2]) {
+ ckfree(arg);
+ }
+ if (afterPtr != NULL) {
+ if (afterPtr->token != NULL) {
+ Tk_DeleteTimerHandler(afterPtr->token);
+ } else {
+ Tk_CancelIdleCall(AfterProc, (ClientData) afterPtr);
+ }
+ FreeAfterPtr(afterPtr);
+ }
+ } else if (strncmp(argv[1], "idle", strlen(argv[1])) == 0) {
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " idle script script ...\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
+ afterPtr->interp = interp;
+ if (argc == 3) {
+ afterPtr->command = (char *) ckalloc((unsigned)
+ (strlen(argv[2]) + 1));
+ strcpy(afterPtr->command, argv[2]);
+ } else {
+ afterPtr->command = Tcl_Concat(argc-2, argv+2);
+ }
+ afterPtr->id = nextId;
+ nextId += 1;
+ afterPtr->token = NULL;
+ afterPtr->nextPtr = firstAfterPtr;
+ firstAfterPtr = afterPtr;
+ Tk_DoWhenIdle(AfterProc, (ClientData) afterPtr);
+ sprintf(interp->result, "after#%d", afterPtr->id);
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[1],
+ "\": must be cancel, idle, or a number", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AfterProc --
+ *
+ * Timer callback to execute commands registered with the
+ * "after" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Executes whatever command was specified. If the command
+ * returns an error, then the command "tkerror" is invoked
+ * to process the error; if tkerror fails then information
+ * about the error is output on stderr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+AfterProc(clientData)
+ ClientData clientData; /* Describes command to execute. */
+{
+ AfterInfo *afterPtr = (AfterInfo *) clientData;
+ AfterInfo *prevPtr;
+ int result;
+
+ /*
+ * First remove the callback from our list of callbacks; otherwise
+ * someone could delete the callback while it's being executed, which
+ * could cause a core dump.
+ */
+
+ if (firstAfterPtr == afterPtr) {
+ firstAfterPtr = afterPtr->nextPtr;
+ } else {
+ for (prevPtr = firstAfterPtr; prevPtr->nextPtr != afterPtr;
+ prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ prevPtr->nextPtr = afterPtr->nextPtr;
+ }
+
+ /*
+ * Execute the callback.
+ */
+
+ result = Tcl_GlobalEval(afterPtr->interp, afterPtr->command);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(afterPtr->interp, "\n (\"after\" script)");
+ Tk_BackgroundError(afterPtr->interp);
+ }
+
+ /*
+ * Free the memory for the callback.
+ */
+
+ ckfree(afterPtr->command);
+ ckfree((char *) afterPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeAfterPtr --
+ *
+ * This procedure removes an "after" command from the list of
+ * those that are pending and frees its resources. This procedure
+ * does *not* cancel the timer handler; if that's needed, the
+ * caller must do it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The memory associated with afterPtr is released.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeAfterPtr(afterPtr)
+ AfterInfo *afterPtr; /* Command to be deleted. */
+{
+ AfterInfo *prevPtr;
+ if (firstAfterPtr == afterPtr) {
+ firstAfterPtr = afterPtr->nextPtr;
+ } else {
+ for (prevPtr = firstAfterPtr; prevPtr->nextPtr != afterPtr;
+ prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ prevPtr->nextPtr = afterPtr->nextPtr;
+ }
+ ckfree(afterPtr->command);
+ ckfree((char *) afterPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_FileeventCmd --
+ *
+ * This procedure is invoked to process the "fileevent" Tcl
+ * command. See the user documentation for details on what it does.
+ * This command is based on Mark Diekhans' "addinput" command.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+Tk_FileeventCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter.
+ * Not used.*/
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ FILE *f;
+ int index, fd, c;
+ size_t length;
+ FileEvent *fevPtr, *prevPtr;
+
+ /*
+ * Parse arguments.
+ */
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
+ " fileId event ?script?", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'r') && (strncmp(argv[2], "readable", length) == 0)) {
+ index = 0;
+ } else if ((c == 'w') && (strncmp(argv[2], "writable", length) == 0)) {
+ index = 1;
+ } else {
+ Tcl_AppendResult(interp, "bad event name \"", argv[2],
+ "\": must be readable or writable", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetOpenFile(interp, argv[1], index, 1, &f) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fd = fileno(f);
+
+ /*
+ * Locate an existing file handler for this file, if one exists,
+ * and make a new one if none currently exists.
+ */
+
+ for (fevPtr = firstFileEventPtr; ; fevPtr = fevPtr->nextPtr) {
+ if (fevPtr == NULL) {
+ if ((argc == 3) || (argv[3][0] == 0)) {
+ return TCL_OK;
+ }
+ fevPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
+ fevPtr->f = f;
+ fevPtr->interps[0] = NULL;
+ fevPtr->interps[1] = NULL;
+ fevPtr->scripts[0] = NULL;
+ fevPtr->scripts[1] = NULL;
+ fevPtr->nextPtr = firstFileEventPtr;
+ firstFileEventPtr = fevPtr;
+ Tk_CreateFileHandler2(fileno(f), FileEventProc,
+ (ClientData) fevPtr);
+ tcl_FileCloseProc = DeleteFileEvent;
+ break;
+ }
+ if (fevPtr->f == f) {
+ break;
+ }
+ }
+
+ /*
+ * If we're just supposed to return the current script, do so.
+ */
+
+ if (argc == 3) {
+ if (fevPtr->scripts[index] != NULL) {
+ interp->result = fevPtr->scripts[index];
+ }
+ return TCL_OK;
+ }
+
+ /*
+ * If we're supposed to delete the event handler, do so.
+ */
+
+ if (argv[3][0] == 0) {
+ if (fevPtr->scripts[index] != NULL) {
+ fevPtr->interps[index] = NULL;
+ ckfree(fevPtr->scripts[index]);
+ fevPtr->scripts[index] = NULL;
+ }
+ if ((fevPtr->scripts[0] == NULL) && (fevPtr->scripts[1] == NULL)) {
+ if (firstFileEventPtr == fevPtr) {
+ firstFileEventPtr = fevPtr->nextPtr;
+ } else {
+ for (prevPtr = firstFileEventPtr; prevPtr->nextPtr != fevPtr;
+ prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ prevPtr->nextPtr = fevPtr->nextPtr;
+ }
+ Tk_DeleteFileHandler(fileno(fevPtr->f));
+ ckfree((char *) fevPtr);
+ }
+ return TCL_OK;
+ }
+
+ /*
+ * This is a new handler being created. Save its script.
+ */
+
+ fevPtr->interps[index] = interp;
+ if (fevPtr->scripts[index] != NULL) {
+ ckfree(fevPtr->scripts[index]);
+ }
+ fevPtr->scripts[index] = ckalloc((unsigned) (strlen(argv[3]) + 1));
+ strcpy(fevPtr->scripts[index], argv[3]);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FileEventProc --
+ *
+ * This procedure is invoked by Tk's event loop to deal with file
+ * event bindings created by the "fileevent" command.
+ *
+ * Results:
+ * The return value is TK_FILE_HANDLED if the file was ready and
+ * a script was invoked to handle it. Otherwise an OR-ed combination
+ * of TK_READABLE and TK_WRITABLE is returned, indicating the events
+ * that should be checked in future calls to select.
+ *
+ * Side effects:
+ * Whatever the event script does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FileEventProc(clientData, mask, flags)
+ ClientData clientData; /* Pointer to FileEvent structure for file. */
+ int mask; /* OR-ed combination of the bits TK_READABLE,
+ * TK_WRITABLE, and TK_EXCEPTION, indicating
+ * current state of file. */
+ int flags; /* Flag bits passed to Tk_DoOneEvent;
+ * contains bits such as TK_DONT_WAIT,
+ * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
+{
+ FileEvent *fevPtr = (FileEvent *) clientData;
+ Tcl_DString script;
+ Tcl_Interp *interp;
+ FILE *f;
+ int code, checkMask;
+
+ if (!(flags & TK_FILE_EVENTS)) {
+ return 0;
+ }
+
+ /*
+ * The code here is a little tricky, because the script for an
+ * event could delete the event handler. Thus, after we call
+ * Tcl_GlobalEval we can't use fevPtr anymore. We also have to
+ * copy the script to make sure that it doesn't get freed while
+ * being evaluated.
+ */
+
+ checkMask = 0;
+ f = fevPtr->f;
+ if (fevPtr->scripts[1] != NULL) {
+ if (mask & TK_WRITABLE) {
+ Tcl_DStringInit(&script);
+ Tcl_DStringAppend(&script, fevPtr->scripts[1], -1);
+ interp = fevPtr->interps[1];
+ code = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
+ Tcl_DStringFree(&script);
+ if (code != TCL_OK) {
+ goto error;
+ }
+ return TK_FILE_HANDLED;
+ } else {
+ checkMask |= TK_WRITABLE;
+ }
+ }
+ if (fevPtr->scripts[0] != NULL) {
+ if ((mask & TK_READABLE) || TK_READ_DATA_PENDING(f)) {
+ Tcl_DStringInit(&script);
+ Tcl_DStringAppend(&script, fevPtr->scripts[0], -1);
+ interp = fevPtr->interps[0];
+ code = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
+ Tcl_DStringFree(&script);
+ if (code != TCL_OK) {
+ goto error;
+ }
+ return TK_FILE_HANDLED;
+ } else {
+ checkMask |= TK_READABLE;
+ }
+ }
+ return checkMask;
+
+ /*
+ * An error occurred in the script, so we have to call
+ * Tk_BackgroundError. However, it's possible that the file ready
+ * condition didn't get cleared for the file, so we could end
+ * up in an infinite loop if we're not careful. To be safe,
+ * delete the event handler.
+ */
+
+ error:
+ DeleteFileEvent(f);
+ Tcl_AddErrorInfo(interp,
+ "\n (script bound to file event - binding deleted)");
+ Tk_BackgroundError(interp);
+ return TK_FILE_HANDLED;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteFileEvent --
+ *
+ * This procedure is invoked to delete all file event handlers
+ * for a file. For example, this is necessary if a file is closed,
+ * or if an error occurs in a handler for a file.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The file event handler is removed, so it will never be invoked
+ * again.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteFileEvent(f)
+ FILE *f; /* Stdio structure describing open file. */
+{
+ register FileEvent *fevPtr;
+ FileEvent *prevPtr;
+
+ /*
+ * See if there exists a file handler for the given file.
+ */
+
+ for (prevPtr = NULL, fevPtr = firstFileEventPtr; ;
+ prevPtr = fevPtr, fevPtr = fevPtr->nextPtr) {
+ if (fevPtr == NULL) {
+ return;
+ }
+ if (fevPtr->f == f) {
+ break;
+ }
+ }
+
+ /*
+ * Unlink it from the list, then free it.
+ */
+
+ if (prevPtr == NULL) {
+ firstFileEventPtr = fevPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = fevPtr->nextPtr;
+ }
+ Tk_DeleteFileHandler(fileno(fevPtr->f));
+ if (fevPtr->scripts[0] != NULL) {
+ ckfree(fevPtr->scripts[0]);
+ }
+ if (fevPtr->scripts[1] != NULL) {
+ ckfree(fevPtr->scripts[1]);
+ }
+ ckfree((char *) fevPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkEventCleanupProc --
+ *
+ * This procedure is invoked whenever an interpreter is deleted.
+ * It deletes any file events and after commands that refer to
+ * that interpreter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * File event handlers and after commands are removed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+TkEventCleanupProc(clientData, interp)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter that is being deleted. */
+{
+ FileEvent *fevPtr, *prevPtr, *nextPtr;
+ AfterInfo *afterPtr, *prevAfterPtr, *nextAfterPtr;
+ int i;
+
+ prevPtr = NULL;
+ fevPtr = firstFileEventPtr;
+ while (fevPtr != NULL) {
+ for (i = 0; i < 2; i++) {
+ if (fevPtr->interps[i] == interp) {
+ fevPtr->interps[i] = NULL;
+ ckfree((char *) fevPtr->scripts[i]);
+ fevPtr->scripts[i] = NULL;
+ }
+ }
+ if ((fevPtr->scripts[0] != NULL) || (fevPtr->scripts[1] != NULL)) {
+ prevPtr = fevPtr;
+ fevPtr = fevPtr->nextPtr;
+ continue;
+ }
+ nextPtr = fevPtr->nextPtr;
+ if (prevPtr == NULL) {
+ firstFileEventPtr = nextPtr;
+ } else {
+ prevPtr->nextPtr = nextPtr;
+ }
+ Tk_DeleteFileHandler(fileno(fevPtr->f));
+ ckfree((char *) fevPtr);
+ fevPtr = nextPtr;
+ }
+
+ prevAfterPtr = NULL;
+ afterPtr = firstAfterPtr;
+ while (afterPtr != NULL) {
+ if (afterPtr->interp != interp) {
+ prevAfterPtr = afterPtr;
+ afterPtr = afterPtr->nextPtr;
+ continue;
+ }
+ nextAfterPtr = afterPtr->nextPtr;
+ if (prevAfterPtr == NULL) {
+ firstAfterPtr = nextAfterPtr;
+ } else {
+ prevAfterPtr->nextPtr = nextAfterPtr;
+ }
+ if (afterPtr->token != NULL) {
+ Tk_DeleteTimerHandler(afterPtr->token);
+ } else {
+ Tk_CancelIdleCall(AfterProc, (ClientData) afterPtr);
+ }
+ ckfree(afterPtr->command);
+ ckfree((char *) afterPtr);
+ afterPtr = nextAfterPtr;
+ }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkwaitCmd2 --
+ *
+ * This procedure is invoked to process the "tkwait" Tcl command.
+ * See the user documentation for details on what it does. This
+ * is a modified version of tkwait with only the "variable"
+ * option, suitable for use in stand-alone mode without the rest
+ * of Tk. It's only used when Tk_EventInit has been called.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+TkwaitCmd2(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int c, done;
+ size_t length;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " variable name\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'v') && (strncmp(argv[1], "variable", length) == 0)
+ && (length >= 2)) {
+ Tcl_TraceVar(interp, argv[2],
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ WaitVariableProc2, (ClientData) &done);
+ done = 0;
+ while (!done) {
+ Tk_DoOneEvent(0);
+ }
+ Tcl_UntraceVar(interp, argv[2],
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ WaitVariableProc2, (ClientData) &done);
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be variable", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Clear out the interpreter's result, since it may have been set
+ * by event handlers.
+ */
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+ /* ARGSUSED */
+static char *
+WaitVariableProc2(clientData, interp, name1, name2, flags)
+ ClientData clientData; /* Pointer to integer to set to 1. */
+ Tcl_Interp *interp; /* Interpreter containing variable. */
+ char *name1; /* Name of variable. */
+ char *name2; /* Second part of variable name. */
+ int flags; /* Information about what happened. */
+{
+ int *donePtr = (int *) clientData;
+
+ *donePtr = 1;
+ return (char *) NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateCmd2 --
+ *
+ * This procedure is invoked to process the "update" Tcl command.
+ * See the user documentation for details on what it does. This
+ * is a modified version of the command that doesn't deal with
+ * windows, suitable for use in stand-alone mode without the rest
+ * of Tk. It's only used when Tk_EventInit has been called.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+UpdateCmd2(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int flags;
+
+ if (argc == 1) {
+ flags = TK_DONT_WAIT|TK_FILE_EVENTS|TK_TIMER_EVENTS|TK_IDLE_EVENTS;
+ } else if (argc == 2) {
+ if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
+ Tcl_AppendResult(interp, "bad argument \"", argv[1],
+ "\": must be idletasks", (char *) NULL);
+ return TCL_ERROR;
+ }
+ flags = TK_IDLE_EVENTS;
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?idletasks?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Handle all pending events.
+ */
+
+ while (Tk_DoOneEvent(flags) != 0) {
+ /* Empty loop body */
+ }
+
+ /*
+ * Must clear the interpreter's result because event handlers could
+ * have executed commands.
+ */
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_EventInit --
+ *
+ * This procedure is invoked from Tcl_AppInit if the Tk event stuff
+ * is being used by itself (without the rest of Tk) in an application.
+ * It creates the "after" and "fileevent" commands.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side effects:
+ * New commands get added to interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_EventInit(interp)
+ Tcl_Interp *interp; /* Interpreter in which to set up
+ * event-handling. */
+{
+ Tcl_CreateCommand(interp, "after", Tk_AfterCmd, (ClientData) NULL,
+ (void (*)()) NULL);
+ Tcl_CreateCommand(interp, "fileevent", Tk_FileeventCmd, (ClientData) NULL,
+ (void (*)()) NULL);
+ Tcl_CreateCommand(interp, "tkwait", TkwaitCmd2, (ClientData) NULL,
+ (void (*)()) NULL);
+ Tcl_CreateCommand(interp, "update", UpdateCmd2, (ClientData) NULL,
+ (void (*)()) NULL);
+ Tcl_CallWhenDeleted(interp, TkEventCleanupProc, (ClientData) NULL);
+ return TCL_OK;
+}
+
+#endif /* TCL_MINOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
--- /dev/null
+##############################################################################\r
+# Ck for Win32 using Microsoft Visual C++ 6.0\r
+##############################################################################\r
+\r
+#\r
+# TCL_DIR must be set to the installation directory of Tcl8.0\r
+#\r
+TCL_DIR = C:\progra~1\Tcl\r
+\r
+#\r
+# CURSES_INCLUDES must point to the directory where PDCURSES include files are\r
+#\r
+CURSES_INCLUDES = -IE:\pdcurses\r
+\r
+#\r
+# CURSES_LIB must point to the PDCURSES link library\r
+#\r
+CURSES_LIB = E:\pdcurses\win32\pdcurses.lib\r
+\r
+#\r
+# Installation directory of MS VC 6\r
+#\r
+MSVC = "C:\progra~1\microsoft visual studio\vc98"\r
+\r
+#-----------------------------------------------------------------------------\r
+# The information below should be usable as is.\r
+\r
+CC = cl\r
+LN = link\r
+RC = rc\r
+\r
+LIBS = $(TCL_DIR)\lib\tcl80.lib libcmt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+DLLLIBS = $(TCL_DIR)\lib\tcl80.lib msvcrt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+CFLAGS = -Zi -Gs -GD -c -W3 -nologo -D_MT -DWIN32 -I$(MSVC)\include \\r
+ -I$(TCL_DIR)\include $(CURSES_INCLUDES)\r
+\r
+LFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:WinMainCRTStartup\r
+\r
+DLLLFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:_DllMainCRTStartup@12 /DLL\r
+\r
+WIDGOBJS = ckButton.obj ckEntry.obj ckFrame.obj ckListbox.obj \\r
+ ckMenu.obj ckMenubutton.obj ckMessage.obj ckScrollbar.obj ckTree.obj\r
+\r
+TEXTOBJS = ckText.obj ckTextBTree.obj ckTextDisp.obj ckTextIndex.obj \\r
+ ckTextMark.obj ckTextTag.obj\r
+\r
+OBJS = ckBind.obj ckBorder.obj ckCmds.obj ckConfig.obj ckEvent.obj \\r
+ ckFocus.obj \\r
+ ckGeometry.obj ckGet.obj ckGrid.obj ckMain.obj ckOption.obj \\r
+ ckPack.obj ckPlace.obj \\r
+ ckPreserve.obj ckRecorder.obj ckUtil.obj ckWindow.obj tkEvent.obj \\r
+ ckAppInit.obj $(WIDGOBJS) $(TEXTOBJS)\r
+\r
+HDRS = default.h ks_names.h ck.h ckPort.h ckText.h\r
+\r
+all: ck80.dll cwsh.exe\r
+\r
+ck80.dll: $(OBJS)\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(DLLLFLAGS) -out:$@ $(DLLLIBS) @<<\r
+$(OBJS)\r
+<<\r
+\r
+cwsh.exe: winMain.obj cwsh.res ck80.dll\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(LFLAGS) winMain.obj cwsh.res -out:$@ $(DLLLIBS) ck80.lib\r
+\r
+clean:\r
+ del *.obj\r
+ del *.lib\r
+ del *.exp\r
+ del *.exe\r
+ del *.res\r
+ del *.pdb\r
+ del ck80.dll\r
+\r
+.c.obj:\r
+ $(CC) -DBUILD_ck -D_DLL $(CFLAGS) $<\r
+\r
+.rc.res:\r
+ $(RC) -I$(MSVC)\include -I$(TCL_DIR)\include -fo $@ -r $<\r
+\r
+winMain.obj: winMain.c\r
+ $(CC) $(CFLAGS) -D_DLL winMain.c\r
+\r
+\1a\r
--- /dev/null
+##############################################################################\r
+# Ck for Win32 using Microsoft Visual C++ 6.0\r
+##############################################################################\r
+\r
+#\r
+# TCL_DIR must be set to the installation directory of Tcl8.2\r
+#\r
+TCL_DIR = C:\progra~1\Tcl\r
+\r
+#\r
+# CURSES_INCLUDES must point to the directory where PDCURSES include files are\r
+#\r
+CURSES_INCLUDES = -IE:\pdcurses\r
+\r
+#\r
+# CURSES_LIB must point to the PDCURSES link library\r
+#\r
+CURSES_LIB = E:\pdcurses\win32\pdcurses.lib\r
+\r
+#\r
+# Installation directory of MS VC 6\r
+#\r
+MSVC = "C:\progra~1\microsoft visual studio\vc98"\r
+\r
+#-----------------------------------------------------------------------------\r
+# The information below should be usable as is.\r
+\r
+CC = cl\r
+LN = link\r
+RC = rc\r
+\r
+LIBS = $(TCL_DIR)\lib\tcl82.lib libcmt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+DLLLIBS = $(TCL_DIR)\lib\tcl82.lib msvcrt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+CFLAGS = -Zi -Gs -GD -c -W3 -nologo -D_MT -DWIN32 -I$(MSVC)\include \\r
+ -I$(TCL_DIR)\include $(CURSES_INCLUDES)\r
+\r
+LFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:WinMainCRTStartup\r
+\r
+DLLLFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:_DllMainCRTStartup@12 /DLL\r
+\r
+WIDGOBJS = ckButton.obj ckEntry.obj ckFrame.obj ckListbox.obj \\r
+ ckMenu.obj ckMenubutton.obj ckMessage.obj ckScrollbar.obj ckTree.obj\r
+\r
+TEXTOBJS = ckText.obj ckTextBTree.obj ckTextDisp.obj ckTextIndex.obj \\r
+ ckTextMark.obj ckTextTag.obj\r
+\r
+OBJS = ckBind.obj ckBorder.obj ckCmds.obj ckConfig.obj ckEvent.obj \\r
+ ckFocus.obj \\r
+ ckGeometry.obj ckGet.obj ckGrid.obj ckMain.obj ckOption.obj \\r
+ ckPack.obj ckPlace.obj \\r
+ ckPreserve.obj ckRecorder.obj ckUtil.obj ckWindow.obj tkEvent.obj \\r
+ ckAppInit.obj $(WIDGOBJS) $(TEXTOBJS)\r
+\r
+HDRS = default.h ks_names.h ck.h ckPort.h ckText.h\r
+\r
+all: ck82.dll cwsh.exe\r
+\r
+ck82.dll: $(OBJS)\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(DLLLFLAGS) -out:$@ $(DLLLIBS) @<<\r
+$(OBJS)\r
+<<\r
+\r
+cwsh.exe: winMain.obj cwsh.res ck82.dll\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(LFLAGS) winMain.obj cwsh.res -out:$@ $(DLLLIBS) ck82.lib\r
+\r
+clean:\r
+ del *.obj\r
+ del *.lib\r
+ del *.exp\r
+ del *.exe\r
+ del *.res\r
+ del *.pdb\r
+ del ck82.dll\r
+\r
+.c.obj:\r
+ $(CC) -DBUILD_ck -D_DLL $(CFLAGS) $<\r
+\r
+.rc.res:\r
+ $(RC) -I$(MSVC)\include -I$(TCL_DIR)\include -fo $@ -r $<\r
+\r
+winMain.obj: winMain.c\r
+ $(CC) $(CFLAGS) -D_DLL winMain.c\r
+\r
+\1a\r
--- /dev/null
+##############################################################################\r
+# Ck for Win32 using Microsoft Visual C++ 6.0\r
+##############################################################################\r
+\r
+#\r
+# TCL_DIR must be set to the installation directory of Tcl8.3\r
+#\r
+TCL_DIR = C:\progra~1\Tcl\r
+\r
+#\r
+# CURSES_INCLUDES must point to the directory where PDCURSES include files are\r
+#\r
+CURSES_INCLUDES = -IE:\pdcurses\r
+\r
+#\r
+# CURSES_LIB must point to the PDCURSES link library\r
+#\r
+CURSES_LIB = E:\pdcurses\win32\pdcurses.lib\r
+\r
+#\r
+# Installation directory of MS VC 6\r
+#\r
+MSVC = "C:\progra~1\microsoft visual studio\vc98"\r
+\r
+#-----------------------------------------------------------------------------\r
+# The information below should be usable as is.\r
+\r
+CC = cl\r
+LN = link\r
+RC = rc\r
+\r
+LIBS = $(TCL_DIR)\lib\tcl83.lib libcmt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+DLLLIBS = $(TCL_DIR)\lib\tcl83.lib msvcrt.lib kernel32.lib user32.lib \\r
+ $(CURSES_LIB)\r
+\r
+CFLAGS = -Zi -Gs -GD -c -W3 -nologo -D_MT -DWIN32 -I$(MSVC)\include \\r
+ -I$(TCL_DIR)\include $(CURSES_INCLUDES)\r
+\r
+LFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:WinMainCRTStartup\r
+\r
+DLLLFLAGS = /NODEFAULTLIB /RELEASE /NOLOGO /MACHINE:IX86 /SUBSYSTEM:WINDOWS \\r
+ /ENTRY:_DllMainCRTStartup@12 /DLL\r
+\r
+WIDGOBJS = ckButton.obj ckEntry.obj ckFrame.obj ckListbox.obj \\r
+ ckMenu.obj ckMenubutton.obj ckMessage.obj ckScrollbar.obj ckTree.obj\r
+\r
+TEXTOBJS = ckText.obj ckTextBTree.obj ckTextDisp.obj ckTextIndex.obj \\r
+ ckTextMark.obj ckTextTag.obj\r
+\r
+OBJS = ckBind.obj ckBorder.obj ckCmds.obj ckConfig.obj ckEvent.obj \\r
+ ckFocus.obj \\r
+ ckGeometry.obj ckGet.obj ckGrid.obj ckMain.obj ckOption.obj \\r
+ ckPack.obj ckPlace.obj \\r
+ ckPreserve.obj ckRecorder.obj ckUtil.obj ckWindow.obj tkEvent.obj \\r
+ ckAppInit.obj $(WIDGOBJS) $(TEXTOBJS)\r
+\r
+HDRS = default.h ks_names.h ck.h ckPort.h ckText.h\r
+\r
+all: ck83.dll cwsh.exe\r
+\r
+ck83.dll: $(OBJS)\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(DLLLFLAGS) -out:$@ $(DLLLIBS) @<<\r
+$(OBJS)\r
+<<\r
+\r
+cwsh.exe: winMain.obj cwsh.res ck83.dll\r
+ set LIB=$(MSVC)\lib\r
+ $(LN) $(LFLAGS) winMain.obj cwsh.res -out:$@ $(DLLLIBS) ck83.lib\r
+\r
+clean:\r
+ del *.obj\r
+ del *.lib\r
+ del *.exp\r
+ del *.exe\r
+ del *.res\r
+ del *.pdb\r
+ del ck83.dll\r
+\r
+.c.obj:\r
+ $(CC) -DBUILD_ck -D_DLL $(CFLAGS) $<\r
+\r
+.rc.res:\r
+ $(RC) -I$(MSVC)\include -I$(TCL_DIR)\include -fo $@ -r $<\r
+\r
+winMain.obj: winMain.c\r
+ $(CC) $(CFLAGS) -D_DLL winMain.c\r
+\r
+\1a\r
--- /dev/null
+/*
+ * winMain.c --
+ *
+ * Main entry point for cwsh.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ * Copyright (c) 1999 Christian Werner
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: winMain.c,v 1.1 2006-02-24 18:59:53 vitus Exp $
+ */
+
+#include "ck.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <malloc.h>
+#include <locale.h>
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
+static void CwshPanic _ANSI_ARGS_(TCL_VARARGS(char *,format));
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * WinMain --
+ *
+ * Main entry point from Windows.
+ *
+ * Results:
+ * Returns false if initialization fails, otherwise it never
+ * returns.
+ *
+ * Side effects:
+ * Just about anything, since from here we call arbitrary Tcl code.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int APIENTRY
+WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
+ HINSTANCE hInstance;
+ HINSTANCE hPrevInstance;
+ LPSTR lpszCmdLine;
+ int nCmdShow;
+{
+ char **argv, *p;
+ int argc;
+ char buffer[MAX_PATH];
+
+ Tcl_SetPanicProc(CwshPanic);
+
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+ FreeConsole();
+ if (!AllocConsole()) {
+ CwshPanic("Error allocating console");
+ }
+ freopen("CONIN$", "r", stdin);
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+
+ /*
+ * Set up the default locale to be standard "C" locale so parsing
+ * is performed correctly.
+ */
+
+ setlocale(LC_ALL, "C");
+
+ /*
+ * Increase the application queue size from default value of 8.
+ * At the default value, cross application SendMessage of WM_KILLFOCUS
+ * will fail because the handler will not be able to do a PostMessage!
+ * This is only needed for Windows 3.x, since NT dynamically expands
+ * the queue.
+ */
+ SetMessageQueue(64);
+
+ setargv(&argc, &argv);
+
+ /*
+ * Replace argv[0] with full pathname of executable, and forward
+ * slashes substituted for backslashes.
+ */
+
+ GetModuleFileName(NULL, buffer, sizeof(buffer));
+ argv[0] = buffer;
+ for (p = buffer; *p != '\0'; p++) {
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+
+ Ck_Main(argc, argv, Tcl_AppInit);
+ return 1;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ if (Ck_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+#if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
+ tcl_RcFileName = "~/.cwshrc";
+#else
+ Tcl_StaticPackage(interp, "Ck", Ck_Init, (Tcl_PackageInitProc *) NULL);
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/.cwshrc", TCL_GLOBAL_ONLY);
+#endif
+ return TCL_OK;
+
+error:
+ CwshPanic(interp->result);
+ return TCL_ERROR;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CwshPanic --
+ *
+ * Display a message and exit.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Exits the program.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+CwshPanic TCL_VARARGS_DEF(char *,arg1)
+{
+ va_list argList;
+ char buf[1024];
+ char *format;
+
+ format = TCL_VARARGS_START(char *,arg1,argList);
+ vsprintf(buf, format, argList);
+
+ MessageBeep(MB_ICONEXCLAMATION);
+ MessageBox(NULL, buf, "Fatal Error in CWSH",
+ MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
+#ifdef _MSC_VER
+ DebugBreak();
+#endif
+ ExitProcess(1);
+}
+/*
+ *-------------------------------------------------------------------------
+ *
+ * setargv --
+ *
+ * Parse the Windows command line string into argc/argv. Done here
+ * because we don't trust the builtin argument parser in crt0.
+ * Windows applications are responsible for breaking their command
+ * line into arguments.
+ *
+ * 2N backslashes + quote -> N backslashes + begin quoted string
+ * 2N + 1 backslashes + quote -> literal
+ * N backslashes + non-quote -> literal
+ * quote + quote in a quoted string -> single quote
+ * quote + quote not in quoted string -> empty string
+ * quote -> begin quoted string
+ *
+ * Results:
+ * Fills argcPtr with the number of arguments and argvPtr with the
+ * array of arguments.
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *--------------------------------------------------------------------------
+ */
+
+static void
+setargv(argcPtr, argvPtr)
+ int *argcPtr; /* Filled with number of argument strings. */
+ char ***argvPtr; /* Filled with argument strings (malloc'd). */
+{
+ char *cmdLine, *p, *arg, *argSpace;
+ char **argv;
+ int argc, size, inquote, copy, slashes;
+
+ cmdLine = GetCommandLine();
+
+ /*
+ * Precompute an overly pessimistic guess at the number of arguments
+ * in the command line by counting non-space spans.
+ */
+
+ size = 2;
+ for (p = cmdLine; *p != '\0'; p++) {
+ if (isspace(*p)) {
+ size++;
+ while (isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ }
+ }
+ argSpace = (char *) ckalloc((unsigned) (size * sizeof(char *)
+ + strlen(cmdLine) + 1));
+ argv = (char **) argSpace;
+ argSpace += size * sizeof(char *);
+ size--;
+
+ p = cmdLine;
+ for (argc = 0; argc < size; argc++) {
+ argv[argc] = arg = argSpace;
+ while (isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+
+ inquote = 0;
+ slashes = 0;
+ while (1) {
+ copy = 1;
+ while (*p == '\\') {
+ slashes++;
+ p++;
+ }
+ if (*p == '"') {
+ if ((slashes & 1) == 0) {
+ copy = 0;
+ if ((inquote) && (p[1] == '"')) {
+ p++;
+ copy = 1;
+ } else {
+ inquote = !inquote;
+ }
+ }
+ slashes >>= 1;
+ }
+
+ while (slashes) {
+ *arg = '\\';
+ arg++;
+ slashes--;
+ }
+
+ if ((*p == '\0') || (!inquote && isspace(*p))) {
+ break;
+ }
+ if (copy != 0) {
+ *arg = *p;
+ arg++;
+ }
+ p++;
+ }
+ *arg = '\0';
+ argSpace = arg + 1;
+ }
+ argv[argc] = NULL;
+
+ *argcPtr = argc;
+ *argvPtr = argv;
+}
+